速通机器学习 12|轮廓检测、轮廓特征与轮廓近似
目录
前言
1 、轮廓检测
1.1 轮廓与边缘的区别
1.2 核心 API
cv2.findContours () 查找轮廓
cv2.drawContours () 绘制轮廓
1.3 轮廓检测基础代码
2、轮廓特征
1.轮廓面积 contourArea
2.轮廓周长 arcLength
3.外接矩形 boundingRect(正外接矩形,不旋转)
4.最小外接旋转矩形 minAreaRect
5.轮廓重心(矩 Moments)
3、轮廓近似
3.1 原理
3.2 API
3.3 完整综合代码
编辑
4、本章小结
前言
承接第 8 章边缘检测。边缘检测只能得到零散的边界像素,轮廓检测可以把同一物体的边界像素组合成一条完整闭合轮廓,常用于物体计数、形状识别、目标分割,是传统计算机视觉高频模块。
1 、轮廓检测
1.1 轮廓与边缘的区别
边缘:图像灰度发生突变的像素点,线条零散,不一定闭合。
轮廓:经过算法分组之后,属于同一个物体的闭合边界点集合,可以做后续计算。
重要前提:
cv2.findContours()输入优先使用二值图像,黑色为背景,白色为待检测目标。 标准处理流水线:彩色图 → 灰度图 → 二值化threshold→ 查找轮廓。
1.2 核心 API
cv2.findContours () 查找轮廓
contours, hierarchy = cv2.findContours(binary_img, mode, method)contours:轮廓列表,存储图像中所有检测到的轮廓。整体为元组嵌套列表结构,每个子列表对应一条独立轮廓,由一组物体边界的(x,y)坐标点组成,可单独提取、处理单条轮廓。hierarchy:轮廓层级,用于处理嵌套轮廓(物体内部存在孔洞)。
检索模式 mode
cv2.RETR_EXTERNAL:只提取最外层轮廓,忽略内部孔洞,日常最常用。cv2.RETR_TREE:输出完整的嵌套层级关系,处理孔洞、内外轮廓。
轮廓点存储方式 method
cv2.CHAIN_APPROX_SIMPLE:压缩轮廓点,去掉重复冗余点,节省内存,推荐使用。cv2.CHAIN_APPROX_NONE:保存边界上全部像素点,点数量大。
cv2.drawContours () 绘制轮廓
cv2.drawContours(dst_img, contours, index, color, thickness)dst_img:绘制轮廓的目标图像,必须传入原图副本,避免修改原始图像。contours:需要绘制的轮廓列表,为findContours检测得到的轮廓数组。index:轮廓索引,-1代表绘制所有轮廓;传入具体数字(如0、1)仅绘制对应序号的单条轮廓。color:轮廓绘制颜色,遵循 OpenCV 的 BGR 通道格式,例如(0,0,255)为红色、(0,255,0)为绿色。thickness:轮廓线条宽度,单位为像素;设置为-1可填充整个轮廓内部区域
1.3 轮廓检测基础代码
import cv2 读取原图 phone = cv2.imread(r'D:\pythoncode2\bigdata_ai40\Image\phone.png') 转换灰度图 phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY) cv2.imshow('phone_gray', phone_gray) cv2.waitKey(0) 二值化处理:分割前景与背景,生成黑白二值图 ret, phone_binary = cv2.threshold(phone_gray, 127, 255, cv2.THRESH_BINARY) cv2.imshow('phone_binary', phone_binary) cv2.waitKey(0) 查找轮廓 [-2] 兼容所有OpenCV版本,稳定获取轮廓列表 RETR_TREE:提取完整层级轮廓(包含内部孔洞) CHAIN_APPROX_NONE:保留轮廓全部像素点 contours = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2] print(f"检测到轮廓数量:{len(contours)}") 绘制所有轮廓 img_copy = phone.copy() cv2.drawContours(image=img_copy, contours=contours, contourIdx=-1, color=(0, 0, 255), thickness=3) cv2.imshow('phone_contour', img_copy) cv2.waitKey(0) 释放所有窗口 cv2.destroyAllWindows()2、轮廓特征
拿到单条轮廓cnt = contours[i],可以计算物体各类几何特征,用于筛选目标物体。
1.轮廓面积 contourArea
area = cv2.contourArea(cnt)作用:过滤噪声小轮廓,设置面积阈值,把过小干扰轮廓剔除。
2.轮廓周长 arcLength
peri = cv2.arcLength(cnt, closed=True)closed=True代表轮廓是闭合图形;周长是轮廓近似的重要输入参数。
3.外接矩形 boundingRect(正外接矩形,不旋转)
x, y, w, h = cv2.boundingRect(cnt)- x,y:矩形左上角坐标;w,h:宽、高。
可以画矩形:cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2)
4.最小外接旋转矩形 minAreaRect
rect = cv2.minAreaRect(cnt) box = cv2.boxPoints(rect) box = np.intp(box) cv2.drawContours(img, [box], 0, (255, 0, 0), 2)可以得到旋转角度,适合倾斜物体。
5.轮廓重心(矩 Moments)
M = cv2.moments(cnt) cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"])获取物体中心点坐标。
使用场景:通过面积、宽高比筛选,只保留我们感兴趣的物体轮廓。
3、轮廓近似
3.1 原理
原始轮廓包含大量点,图像噪声会带来很多毛刺、抖动。
轮廓近似approxPolyDP使用道格拉斯‑普克算法,用少量顶点多边形去拟合原始复杂轮廓。
3.2 API
approx = cv2.approxPolyDP(curve, epsilon, closed)- curve:输入单条轮廓,不能直接传入全部 contours 列表。
- epsilon:近似精度,最核心超参数,通常取轮廓周长的比例:
epsilon = 0.04 * cv2.arcLength(cnt, True)
- epsilon 越大:顶点越少,轮廓越粗糙;
- epsilon 越小:顶点越多,越贴近原始轮廓。
- closed=True:表示处理闭合轮廓。
根据返回approx顶点数量,做形状识别: |
- 3 个顶点 → 三角形
- 4 个顶点 → 矩形、正方形
- 大量顶点 → 圆形或者不规则图形
3.3 完整综合代码
import cv2 import numpy as np 读取图像 img = cv2.imread(r'D:\pythoncode2\bigdata_ai40\Image\shape.png') origin_img = img.copy() 图像预处理:灰度转换 + 二值化 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) 查找外层轮廓,兼容所有OpenCV版本 contours = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2] 绘制原始完整轮廓 cv2.drawContours(origin_img, contours, -1, (0, 255, 0), 2) 遍历所有轮廓,计算特征、绘制矩形并执行轮廓近似 for cnt in contours: # 1. 计算轮廓面积,过滤微小噪声轮廓 area = cv2.contourArea(cnt) if area < 50: continue # 2. 计算闭合轮廓周长 peri = cv2.arcLength(cnt, closed=True) # 3. 轮廓近似:通过周长比例设置精度 epsilon = 0.04 * peri approx = cv2.approxPolyDP(cnt, epsilon, closed=True) # 绘制近似后的多边形轮廓 cv2.drawContours(img, [approx], -1, (255, 0, 0), 2) # 4. 绘制正外接矩形(不旋转) x, y, w, h = cv2.boundingRect(approx) cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 255), 2) # 5. 绘制最小外接旋转矩形(适配倾斜物体) min_rect = cv2.minAreaRect(cnt) min_box = cv2.boxPoints(min_rect) min_box = np.intp(min_box) cv2.drawContours(img, [min_box], 0, (255, 255, 0), 2) # 识别轮廓顶点数量并标注 point_num = len(approx) cv2.putText(img, f"pts:{point_num}", (x, y-8), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) 展示结果 cv2.imshow("raw_contour", origin_img) cv2.imshow("approx_feature_result", img) cv2.waitKey(0) 释放所有窗口 cv2.destroyAllWindows()4、本章小结
轮廓是闭合物体点集,由二值图像提取,区别于零散边缘。
轮廓特征包含面积、周长、外接矩形、重心,用于筛选目标物体。
轮廓近似
approxPolyDP依靠 epsilon 控制精度,依靠顶点数目实现简单形状识别。典型场景:零件检测、答题卡识别、图形分类、物体计数。
