轮廓是图像中具有相同颜色或强度的连续点组成的曲线。轮廓通常是闭合的,用来表示一个物体的完整边界。简单来说,轮廓就是物体的“外框”或者“边界线”。
在图像处理中,轮廓也差不多是这个意思。它是一个物体或者形状的边缘线,用来区分物体和背景,或者物体之间的边界。简单来说,轮廓就是物体的“外框”或者“边界线”。 例如: 1. 一张照片里有一只猫:猫的身体边缘就是轮廓,它把猫和周围的背景分开了。 2. 一幅画里有一个三角形:三角形的三条边就是它的轮廓。 3. 一张黑白照片里有一些文字:文字的边缘就是轮廓,它把文字和空白的背景区分开。
在计算机视觉和图像处理中,我们通过一些算法(比如OpenCV中的findContours函数)来自动找到这些轮廓,然后可以用它来识别物体的形状、大小,或者对物体进行分析和处理。
图像轮廓和图像边缘是图像处理中两个非常重要的概念,它们虽然密切相关,但并不完全相同。下面用通俗的方式来解释它们的区别:
概念上的区别: - 边缘是图像中亮度或颜色变化的地方,通常是单个像素级别的特征。 - 轮廓是由多个边缘点组成的闭合曲线,表示一个物体的完整边界。
直观上的区别: - 边缘更像是“线条”,可能不完整,也可能不闭合。 - 轮廓更像是“框”,是一个完整的边界,通常可以用来表示一个物体的形状。
用途上的区别: - 边缘检测主要用于提取图像中的纹理、形状变化等信息,常用于图像分割、特征提取等。 - 轮廓检测主要用于识别物体的形状、计算物体的面积、周长等,常用于目标检测和形状分析。
简单类比:边缘就像是一幅画中的“线条”,用来勾勒形状。轮廓就像是一幅画中的“框”,用来完整地表示一个物体的边界。
实际例子:假设你有一张照片,照片里有一个杯子:边缘可能是杯子的把手、杯口、杯身和桌面之间的分界线。这些是亮度变化的地方。轮廓则是杯子的外边界,是一个完整的曲线,用来区分杯子和背景。
在OpenCV中使用Python处理图像轮廓,通常涉及以下几个步骤:读取图像、转换为灰度图、应用边缘检测、寻找轮廓,最后绘制轮廓。下面是一个完整的例子,展示了如何使用OpenCV库在Python中检测图像的轮廓。
import cv2
import numpy as np
# 1. 读取图像
image = cv2.imread('./images/jpg/apples01.jpg') # 替换为你的图像路径
if image is None:
print("无法加载图像,请检查路径!")
exit()
# 2. 转换为灰度图像
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 3. 应用高斯模糊(可选,用于减少噪声)
blurred_image = cv2.GaussianBlur(gray_image, (5, 5), 0)
# 4. 二值化处理
_, binary_image = cv2.threshold(blurred_image, 20, 255, cv2.THRESH_BINARY)
# 5. 查找轮廓
# 使用cv2.RETR_EXTERNAL提取最外层轮廓,cv2.CHAIN_APPROX_SIMPLE压缩轮廓点
contours, hierarchy = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 6. 绘制轮廓
# 参数说明:
# - image: 原始图像
# - contours: 轮廓列表
# - -1: 绘制所有轮廓
# - (0, 255, 0): 轮廓颜色(BGR格式,这里是绿色)
# - 2: 轮廓线条粗细
cv2.drawContours(image, contours, -1, (0, 255, 0), 2)
# 7. 显示结果
cv2.imshow('Original Image', image)
cv2.imshow('Gray Image', gray_image)
cv2.imshow('Binary Image', binary_image)
cv2.imshow('Contours', image)
# 8. 等待用户按键并关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont
font = cv2.FONT_HERSHEY_SIMPLEX
def putText_Chinese(img, text, pt, textColor=(0, 255, 0), textSize=20):
left, top = pt[0], pt[1]
if (isinstance(img, np.ndarray)):
# 判断是否OpenCV图片类型
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img)
fontText = ImageFont.truetype("simhei.ttf", textSize, encoding="utf-8")
draw.text((left, top), text, textColor, font=fontText)
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
img = cv2.imread('./images/png/china.png')
cv2.imshow('src', img)
temp = img.copy()
gray = cv2.cvtColor(temp, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 130, 250, cv2.THRESH_BINARY)
cv2.imshow("thres", thresh)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
(x, y, w, h) = cv2.boundingRect(cnt)
if (w > 300 and h > 300):
left_most = tuple(cnt[cnt[:, :, 0].argmin()][0])
right_most = tuple(cnt[cnt[:, :, 0].argmax()][0])
top_most = tuple(cnt[cnt[:, :, 1].argmin()][0])
bottom_most = tuple(cnt[cnt[:, :, 1].argmax()][0])
cv2.drawContours(img, cnt, -1, (255, 0, 0), 2)
cv2.circle(img, left_most, 5, (0, 255, 0), -1, cv2.LINE_AA)
cv2.circle(img, right_most, 5, (0, 255, 0), -1, cv2.LINE_AA)
cv2.circle(img, top_most, 5, (0, 255, 0), -1, cv2.LINE_AA)
cv2.circle(img, bottom_most, 5, (0, 255, 0), -1, cv2.LINE_AA)
img = putText_Chinese(img, "西", left_most, (0, 255, 255), 30)
img = putText_Chinese(img, "东", right_most, (0, 255, 255), 30)
img = putText_Chinese(img, "北", top_most, (0, 255, 255), 30)
img = putText_Chinese(img, "南", bottom_most, (0, 255, 255), 30)
img = putText_Chinese(img, "中 国", (int(x + w / 2), int(y + h / 2)), (255, 255, 0), 70)
cv2.imshow('contours', img)
cv2.imwrite('result.bmp', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
原图如下:

阈值处理分割出地图轮廓如下:

计算对应轮廓的极点坐标并标注,效果如下:
