在计算机视觉领域,轮廓检测是物体识别和形状分析的基础操作。与常见的边缘检测不同,轮廓更强调边界的闭合性和整体性。想象一下用铅笔描摹物体外形时,边缘检测得到的会是断断续续的铅笔痕迹,而轮廓检测则像是一笔完成的连贯线条。
OpenCV处理轮廓有个重要前提:必须使用二值图像。这是因为:
典型处理流程如下:
python复制图像读取 → 灰度转换 → 二值化 → 轮廓检测 → 特征分析 → 轮廓近似
首先确保安装OpenCV库:
bash复制pip install opencv-python
基础预处理代码示例:
python复制import cv2
# 读取图像
img = cv2.imread('object.jpg')
# 转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化处理
_, binary = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)
注意:阈值120需要根据实际图像调整。太低的阈值会保留过多背景噪声,太高的阈值可能导致目标轮廓断裂。
OpenCV提供cv2.findContours()函数进行轮廓检测:
python复制contours, hierarchy = cv2.findContours(
binary,
cv2.RETR_TREE,
cv2.CHAIN_APPROX_NONE
)
参数解析:
RETR_TREE:检索所有轮廓并建立完整层级关系CHAIN_APPROX_NONE:存储轮廓上所有点的坐标CHAIN_APPROX_SIMPLE:仅存储轮廓关键点(如直线的端点)绘制轮廓使用cv2.drawContours():
python复制result = img.copy()
cv2.drawContours(
image=result,
contours=contours,
contourIdx=-1, # -1表示绘制所有轮廓
color=(0,255,0), # BGR格式的绿色
thickness=2
)
轮廓面积计算:
python复制area = cv2.contourArea(contour)
轮廓周长计算:
python复制perimeter = cv2.arcLength(contour, closed=True)
按面积筛选轮廓:
python复制large_contours = [c for c in contours if cv2.contourArea(c) > 1000]
按面积排序获取最大轮廓:
python复制largest_contour = sorted(contours, key=cv2.contourArea, reverse=True)[0]
最小外接矩形:
python复制x,y,w,h = cv2.boundingRect(contour)
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
最小外接圆:
python复制(x,y),radius = cv2.minEnclosingCircle(contour)
cv2.circle(img, (int(x),int(y)), int(radius), (0,255,0), 2)
原始轮廓可能包含数百甚至上千个点,轮廓近似通过Douglas-Peucker算法用更少的点来近似轮廓形状。算法原理:
python复制epsilon = 0.01 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
ε值的选择经验:
通过近似后的顶点数可以初步判断形状:
python复制vertices = len(approx)
if vertices == 3:
shape = "triangle"
elif vertices == 4:
# 进一步判断是否为正方形
x,y,w,h = cv2.boundingRect(approx)
aspect_ratio = w / float(h)
shape = "square" if 0.95 <= aspect_ratio <= 1.05 else "rectangle"
elif vertices == 5:
shape = "pentagon"
else:
shape = "circle"
轮廓检测不到
轮廓断裂不连续
python复制kernel = np.ones((3,3), np.uint8)
dilated = cv2.dilate(binary, kernel, iterations=1)
轮廓包含过多噪声
python复制blurred = cv2.GaussianBlur(gray, (5,5), 0)
对于实时应用:
CHAIN_APPROX_SIMPLE减少数据量对于精度要求高的场景:
python复制edges = cv2.Canny(gray, 50, 150)
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
多轮廓处理时:
文档扫描仪实现步骤:
python复制# 获取近似四边形
epsilon = 0.02 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
# 透视变换
if len(approx) == 4:
pts = np.float32([approx[0][0], approx[1][0], approx[2][0], approx[3][0]])
dst = np.float32([[0,0], [w,0], [w,h], [0,h]])
M = cv2.getPerspectiveTransform(pts, dst)
warped = cv2.warpPerspective(img, M, (w,h))
工业零件检测技巧: