一、轮廓检测
1.轮廓检测是什么?
轮廓检测是计算机视觉中的一种技术,用于检测图像中物体的边界。它通过分析图像的像素强度变化,找出连续的点构成的曲线,这些曲线代表了图像中物体的形状轮廓。
2.核心函数:findContours()
在opencv3.x版本中的具体用法
_,contours,hierarchy=cv2.findContours(close2.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
参数分析
1)返回值
-
_(下划线):通常用于忽略不需要的返回值-
在OpenCV 3.x中,
findContours()返回三个值 -
第一个返回值是修改后的图像(通常被忽略)
-
-
contours:检测到的轮廓集合-
类型:Python列表
-
每个元素是一个轮廓,轮廓本身是点坐标的数组
-
例如:
cnts[0]是第一个轮廓的所有点
-
-
hierarchy:轮廓的层次结构信息
-
类型:numpy数组
-
描述轮廓之间的父子关系
-
2)close2.copy()
-
close2:输入的二值图像,即想要进行轮廓检测的图片 -
.copy():创建图像的副本-
要求:必须是8位单通道二值图像(黑白图)
-
注意:函数会修改原始图像,建议使用副本
-
预处理:通常需要先进行阈值处理或边缘检测
-
3)cv2.RETR_EXTERNAL
-
轮廓检索模式:只检索最外层的轮廓,也可以用其他的索引模式
-
特点:
-
只检测物体的外部边界
-
忽略所有内部轮廓(孔洞)
-
适用于物体计数、简单形状检测
-
4)cv2.CHAIN_APPROX_SIMPLE
-
轮廓近似方法:压缩冗余点,节省内存
-
效果:
-
只保留轮廓的关键转折点
-
例如:矩形只存储4个角点,而不是所有边界点
-
减少内存占用,提高处理速度
-
5)轮廓索引方式
| 模式 | 说明 | |
|---|---|---|
| RETR_EXTERNAL | 只检测最外层轮廓 | |
| RETR_LIST | 检测所有轮廓,不建立层次关系 | |
| RETR_CCOMP | 检测所有轮廓,组织为两层层次 | |
| RETR_TREE | 检测所有轮廓,建立完整的层次树 |
6)轮廓近似方法
| 方法 | 说明 | |
|---|---|---|
| CHAIN_APPROX_NONE | 存储所有轮廓点 | |
| CHAIN_APPROX_SIMPLE | 压缩水平、垂直、对角线方向,只保留端点 | |
| CHAIN_APPROX_TC89_L1 | 使用Teh-Chin链近似算法L1 | |
| CHAIN_APPROX_TC89_KCOS | 使用Teh-Chin链近似算法KCOS |
3、具体用法
python
import cv2
phone =cv2.imread('phone.png')#读取原图
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)#灰度图的处理
cv2.imshow( 'phone',phone_gray)
cv2.waitKey(0)
#phone_gray=cv2.imread('phone.png',0) #读取灰度图
ret, people_binary = cv2.threshold(phone_gray, 120, 255,cv2.THRESH_BINARY)#國值处理为二值
cv2.imshow( 'phone_binary',people_binary)
cv2.waitKey(0)
contours = cv2.findContours(people_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2]#通用
print(len(contours))
image_copy = phone.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1,color=(0,0,255),thickness=2)
cv2.imshow('contours_show',image_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()

1)轮廓面积计算
cv2.contourArea(contour,oriented=False) 轮廓面积
相关参数:
contour: 顶点构成的二维向量组(如轮廓列表contours中的一个轮廓)
oriented: 定向区域标志,默认值为 False,返回面积的绝对值。True 时则根据轮廓方向返回带符号的数值
python
area_0 = cv2.contourArea(contours[0]) # 计算轮廓面积
print(area_0)
area_1 = cv2.contourArea(contours[1])
print(area_1)
代码解释:
-
函数功能:计算轮廓包围的面积
-
算法原理:基于格林公式的数值积分方法
-
oriented参数的作用:
-
False(默认):返回面积的绝对值 -
True:根据轮廓方向(顺时针/逆时针)返回带符号的面积 -
正值表示逆时针方向,负值表示顺时针方向
-
2)轮廓周长计算
cv2.arcLength(curve, closed)
curve: 输入的二维点集(轮廓顶点),可以是 vector 或 Mat 类型。
closed: 用于指示曲线是否封闭。
python
length = cv2.arcLength(contours[0], closed=True)
print(length)
3)根据面积筛选轮廓
python
# 根据面积显示特定轮廓
a_list = []
for i in contours:
if cv2.contourArea(i) > 10000: # 筛选面积大于10000的轮廓
a_list.append(i)
image_copy = phone.copy()
image_copy = cv2.drawContours(image=image_copy, contours=a_list,
contourIdx=-1, color=(0, 255, 0), thickness=3)
cv2.imshow(winname='contours_show_10000', mat=image_copy)
cv2.waitKey(0)

根据面积选择了绿色全是圈中的轮廓
代码解释:
-
筛选逻辑:只保留面积大于10000像素的轮廓
-
实际意义:过滤掉小噪声或不需要的小物体
-
drawContours()参数:-
contourIdx=-1:绘制所有轮廓(-1表示全部) -
color=(0, 255, 0):绿色轮廓线 -
thickness=3:线宽为3像素
-
4)按面积排序轮廓
python
# 轮廓定位好方法:根据轮廓面积进行排序
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0] # 选取最大面积的轮廓
image_contours = cv2.drawContours(image_copy, contours=[sortcnt],
contourIdx=-1, color=(0, 0, 255), thickness=3)
cv2.imshow(winname='image_contours', mat=image_contours)
cv2.waitKey(0)

代码解释:
-
排序方法 :
sorted()函数配合key=cv2.contourArea -
reverse=True:降序排列(从大到小) -
[0]:取第一个元素,即面积最大的轮廓
5)最小外接圆
python
# 外接圆
cnt = contours[6] # 取第7个轮廓
(x, y), r = cv2.minEnclosingCircle(cnt) # 计算轮廓的外接圆
phone_circle = cv2.circle(phone, center=(int(x), int(y)),
radius=int(r), color=(0, 255, 0), thickness=2)
cv2.imshow(winname='phone_circle', mat=phone_circle)
cv2.waitKey(0)

代码解释:
-
minEnclosingCircle():-
返回:圆心坐标(x, y)和半径r
-
算法:寻找能完全包围轮廓的最小圆
-
-
应用:圆形物体检测、目标大小估计
6)外接矩阵
python
# 外接矩形
x, y, w, h = cv2.boundingRect(cnt) # 计算轮廓的最小外接矩形
phone_rectangle = cv2.rectangle(phone, pt1=(x, y), pt2=(x + w, y + h),
color=(0, 0, 255), thickness=2)
cv2.imshow(winname='phone_rectangle', mat=phone_rectangle)
cv2.waitKey(0)
cv2.destroyAllWindows()

代码解释:
-
boundingRect():-
返回:矩形左上角坐标(x, y)和宽度w、高度h
-
特点:矩形与坐标轴平行(非旋转矩形)
-
-
rectangle()参数:-
pt1=(x, y):矩形左上角 -
pt2=(x+w, y+h):矩形右下角
-
7)轮廓近似
approx = cv2.approxPolyDP(curve, epsilon, closed)
参数说明:
curve: 输入轮廓。
epsilon: 近似精度,即两个轮廓之间最大的欧式距离。该参数越小,得到的近似结果越接近实际轮廓;反之,得到的近似结果会更加粗略。
closed: 布尔类型的参数。表示是否封闭轮廓。如果是 True,表示输入轮廓是封闭的。近似结果也会是封闭的;否则表示输入轮廓不是封闭的。近似结果也不会是封闭的。
返回值:approx: 近似结果,是一个nda=req数据。为这个近似后的轮廓,包含了被近似出来的轮廓上的点的坐标
python
phone = cv2.imread('phone.png')
phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
ret, phone_thresh = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
# 对灰度图进行二值化处理:
# - 阈值设为120,大于120的像素设为255(白色),小于等于120的设为0(黑色)
# image, contours, hierarchy = cv2.findContours(phone_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# _, contours, hierarchy = cv2.findContours(phone_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
contours = cv2.findContours(phone_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2]
epsilon = 0.01 * cv2.arcLength(contours[0], True)
#多边形近似轮廓
approx = cv2.approxPolyDP(contours[0], epsilon, True)
print(contours[0].shape)
print(approx.shape)
phone_new = phone.copy()
image_contours = cv2.drawContours(phone_new, [approx], -1, (0, 0 ,255), 3)
cv2.imshow('phone', phone)
cv2.waitKey(0)
cv2.imshow('image_contours', image_contours)
cv2.waitKey(0)
cv2.destroyAllWindows()

4.注意事项
-
图像预处理:必须先转换为二值图像
-
内存管理:轮廓数据可能很大,注意内存使用
-
坐标顺序:轮廓点通常按顺时针或逆时针顺序存储
-
性能考虑 :
CHAIN_APPROX_SIMPLE比CHAIN_APPROX_NONE更节省内存
二、模板匹配
cv2.matchTemplate(image, templ, method)
image:待搜索图像
templ:模板图像
method:计算匹配程度的方法:
TM_SQDIFF平方差匹配法:该方法采用平方差来进行匹配;匹配越好,值越小;匹配越差,值越大。
TM_CCORR相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。
TM_CCOEFF相关系数匹配法:数值越大表明匹配程度越好。
TM_SQDIFF_NORMED归一化平方差匹配法,匹配越好,值越小;匹配越差,值越大。
TM_CCORR_NORMED归一化相关匹配法,数值越大表明匹配程度越好。
TM_CCOEFF_NORMED 归一化相关系数匹配法,数值越大表明匹配程度越好。
相关代码
python
import cv2
# 读取图像
kele = cv2.imread('kele.png')
kele=cv2.resize(kele,dsize=None,fx=1.5,fy=1.5)
tubiao = cv2.imread('tubiao.png')
tubiao=cv2.resize(tubiao,dsize=None,fx=1.5,fy=1.5)
cv2.imshow('kele', kele)
cv2.waitKey(0)
cv2.imshow('tubiao', tubiao)
cv2.waitKey(0)
h, w = tubiao.shape[:2] # 获取模板图像的高度和宽度
# 使用模板匹配方法
res = cv2.matchTemplate(kele, tubiao, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# min_val:最小匹配值
# max_val:最大匹配值(最匹配的位置)
# min_loc:最小匹配值的位置坐标
# max_loc:最大匹配值的位置坐标(最佳匹配位置)
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
kele_tubiao = cv2.rectangle(kele, top_left, bottom_right, (0, 255, 0), 2)
cv2.imshow('kele_tubiao', kele_tubiao)
cv2.waitKey(0)
cv2.destroyAllWindows()

注:模板的大小,要和图形中的所对应模板大小相同