计算机视觉第一课opencv(四)保姆级教学

发布于:2025-08-30 ⋅ 阅读:(13) ⋅ 点赞:(0)

目录

简介

一、轮廓检测

1.查找轮廓的API

2.代码分析

2.1.图像二值化处理

2.2轮廓检测

2.3轮廓绘制

2.4轮廓面积计算

2.5轮廓周长计算

2.6筛选特定面积的轮廓

2.7查找最大面积的轮廓

2.8绘制轮廓的外接圆

2.9绘制轮廓的外接矩形

二、轮廓的近似

三、模板匹配 


简介

        今天继续学习opencv,今天学习轮廓检测、轮廓的近似

计算机视觉第一课opencv(三)保姆级教学

计算机视觉第一课opencv(一)保姆级教学

计算机视觉第一课opencv(二)保姆级教

一、轮廓检测

1.查找轮廓的API

image, contours, hierarchy = cv2.findContours(img, mode, method)
参数:img: 需要实现轮廓检测的原图
mode: 轮廓的检索模式,主要有四种方式:
  cv2.RETR_EXTERNAL:只检测外轮廓,所有子轮廓被忽略
  cv2.RETR_LIST:检测的轮廓不建立等级关系,所有轮廓属于同一等级
  cv2.RETR_CCOMP:返回所有的轮廓,只建立两个等级的轮廓,一个对象的外轮廓为第1级组织结构,
                  而对象内中空的轮廓为第2级组织结构,空间中的任何对象的轮廓又是第 1 级组织结构。
-> cv2.RETR_TREE:返回所有的轮廓,建立一个完整的组织结构的轮廓。
method: 轮廓的近似方法,主要有以下两种:
-> cv2.CHAIN_APPROX_NONE:存储所有的轮廓点。
-> cv2.CHAIN_APPROX_SIMPLE:压缩模式,只保留该方向的终点坐标,例如一个矩形轮廓只画4个点来保存轮廓信息。
返回:image: 返回类型的原图
 contours:包含图像中所有轮廓的list对象,其中每一个独立的轮廓信息以边界点坐标(x,y)的形式储存在numpy数组中。
 hierarchy:轮廓的层次结构,一个包含4个值的数组:[Next, Previous, First Child, Parent]
 Next:与当前轮廓处于同一层级的下一条轮廓
 Previous:与当前轮廓处于同一层级的上一条轮廓
 First Child:当前轮廓的第一条子轮廓
 Parent:当前轮廓的父轮廓
 注意:做轮廓检测前需要将图片读取为二值数据,即像素值只为0和255。

2.代码分析

图像读取与预处理

import cv2
phone = cv2.imread('phone.png')  # 读取原图,默认以BGR格式加载
phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)  # 转换为灰度图
cv2.imshow('phone_0', phone_gray)  # 显示灰度图
cv2.waitKey(0)  # 等待用户按键,0表示无限等待
  • 这部分代码首先读取图像,然后将彩色图像转为灰度图(减少计算量,便于后续处理)
  • cv2.imshow()用于显示图像,cv2.waitKey(0)确保图像窗口不会立即关闭

2.1.图像二值化处理

ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)  # 二值化处理
cv2.imshow('phone_binary', phone_binary)
cv2.waitKey(0)
  • 二值化是轮廓检测的前提,将灰度图转换为只有黑白两种颜色的图像
  • 参数解释:
    • 120:阈值,像素值高于此值会被处理
    • 255:最大值,满足条件的像素会被设置为此值
    • cv2.THRESH_BINARY:二值化类型,大于阈值的设为 255,否则设为 0

2.2轮廓检测

contours = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2]  # 检测轮廓
print(len(contours))  # 打印检测到的轮廓数量
  • cv2.findContours()是轮廓检测的核心函数
  • 参数解释:
    • cv2.RETR_TREE:轮廓检索模式,检测所有轮廓并建立完整的层次结构
    • cv2.CHAIN_APPROX_NONE:轮廓近似方法,存储所有轮廓点
    • [-2]:兼容不同 OpenCV 版本的写法,确保获取到轮廓列表

2.3轮廓绘制

image_copy = phone.copy()  # 复制原图,避免在原图上直接绘制
cv2.drawContours(
    image=image_copy, 
    contours=contours, 
    contourIdx=-1,  # -1表示绘制所有轮廓
    color=(0,0,255),  # 红色(BGR格式)
    thickness=2  # 线条粗细
)
cv2.imshow('Contours_show', image_copy)
cv2.waitKey(0)
  • cv2.drawContours()用于在图像上绘制轮廓
  • 这里创建原图副本是为了保留原图,方便后续其他处

2.4轮廓面积计算

area_0 = cv2.contourArea(contours[0])  # 计算第一个轮廓的面积
print(area_0)
area_1 = cv2.contourArea(contours[1])  # 计算第二个轮廓的面积
print(area_1)
  • cv2.contourArea()用于计算轮廓所包围的面积
  • 面积单位是像素,值越大表示轮廓包围的区域越大

2.5轮廓周长计算

length = cv2.arcLength(contours[0], closed=True)  # 计算第一个轮廓的周长
print(length)
  • cv2.arcLength()用于计算轮廓的周长
  • closed=True表示轮廓是封闭的

2.6筛选特定面积的轮廓

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_copy, a_list, -1, (0, 255, 0), 3)  # 用绿色绘制筛选后的轮廓
cv2.imshow('Contours_show_10000', image_copy)
cv2.waitKey(0)
  • 这部分实现了根据面积筛选轮廓的功能
  • 只保留面积大于 10000 的轮廓,并用绿色绘制,便于观察较大的物体轮廓

2.7查找最大面积的轮廓

# 按面积降序排序,取第一个(面积最大的轮廓)
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0]
image_contours = cv2.drawContours(phone.copy(), [sortcnt], -1, (0, 0, 255), 3)
cv2.imshow('image_contours', image_contours)
cv2.waitKey(0)
  • 使用sorted()函数和cv2.contourArea作为排序键,找到面积最大的轮廓
  • 这种方法常用于定位图像中最主要的物体

2.8绘制轮廓的外接圆

cnt = contours[6]  # 选择第7个轮廓(索引从0开始)
(x, y), r = cv2.minEnclosingCircle(cnt)  # 计算最小外接圆
phone_circle = cv2.circle(phone, (int(x), int(y)), int(r), (0, 255, 0), 2)  # 绘制外接圆
cv2.imshow('phone_circle', phone_circle)
cv2.waitKey(0)
  • cv2.minEnclosingCircle()计算能包围轮廓的最小圆
  • 返回值包括圆心坐标 (x,y) 和半径 r
  • cv2.circle()用于绘制圆形,这里用绿色线条

2.9绘制轮廓的外接矩形

x, y, w, h = cv2.boundingRect(cnt)  # 计算最小外接矩形
phone_rectangle = cv2.rectangle(phone, (x, y), (x + w, y + h), (255, 0, 0), 2)  # 绘制矩形
cv2.imshow('phone_rectangle', phone_rectangle)
cv2.waitKey(0)
  • cv2.boundingRect()计算能包围轮廓的最小矩形(轴对齐矩形)
  • 返回值包括矩形左上角坐标 (x,y)、宽度 w 和高度 h
  • cv2.rectangle()用于绘制矩形,这里用蓝色线条

二、轮廓的近似

approx = cv2.approxPolyDP(curve, epsilon, closed)
参数说明:
    curve:输入轮廓。
    epsilon:近似精度,即两个轮廓之间最大的欧式距离。该参数越小,得到的近似结果越接近实际轮廓;反之,得到的近似结果会更加粗略。
    closed:布尔类型的参数,表示是否封闭轮廓。如果是 True,表示输入轮廓是封闭的,近似结果也会是封闭的;否则表示输入轮廓不是封闭的,近似结果也
    返回值:approx:近似结果,是一个ndarray数组,为1个近似后的轮廓,包含了被近似出来的轮廓上的点的坐标

图像读取与预处理

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(黑色)

轮廓检测

contours=cv2.findContours(phone_thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)[-2]
  • 使用cv2.findContours函数检测轮廓
  • 参数cv2.RETR_TREE表示检测所有轮廓并建立完整的层次结构
  • 参数cv2.CHAIN_APPROX_NONE表示存储所有轮廓点
  • [-2]是为了兼容不同 OpenCV 版本,确保获取到轮廓列表

轮廓近似处理

# 设置近似精度,epsilon值越小,近似结果越接近原始轮廓
epsilon = 0.002 * cv2.arcLength(contours[0], True)        

# 对轮廓进行近似
approx = cv2.approxPolyDP(contours[0], epsilon, True)   
  • cv2.arcLength计算轮廓周长,True表示轮廓是封闭的
  • epsilon是近似精度,这里设置为轮廓周长的 0.002 倍
  • cv2.approxPolyDP函数实现轮廓近似,将轮廓用更少的点来表示,保留主要形状

结果对比与显示

# 打印原始轮廓和近似轮廓的点数量
print(contours[0].shape)
print(approx.shape)

# 绘制近似后的轮廓
phone_new = phone.copy()
image_contours = cv2.drawContours(phone_new, [approx], contourIdx=-1, color=(0,255,0), thickness=3)

# 显示原图和处理后的图像
cv2.imshow('phone', phone)
cv2.waitKey(0)
cv2.imshow('image_contours', image_contours)
cv2.waitKey(0)
  • 通过打印形状可以看到,近似后的轮廓点数量明显减少
  • 用绿色线条绘制近似后的轮廓,并与原图进行对比显示
  • cv2.waitKey(0)表示等待用户按键后再继续执行

        通过调整epsilon的值,可以控制近似程度,值越大,轮廓简化越明显,保留的点越少;值越小,越接近原始轮廓。
 

三、模板匹配 

模板匹配是一种用于查找与模板图像(补丁)匹配(相似)的图像区域的技术

为了识别匹配区域,我们必须通过滑动来将模板图像与源图像进行比较

一次移动一个像素(从左到右,从上到下)。在每个位置,都会计算一个度量(度量计算公式),以便它表示该位置的匹配“好”或“坏”程度

 cv2.matchTemplate(image, templ, method, result=None, mask=None) 

image :待搜索图像

templ:模板图像

method:计算匹配程度的方法,可以有:

        TM_SQDIFF 平方差匹配法,该方法采用平方差进行匹配;匹配越好,值越小;匹配越差,值越大。

        TM_CCORR 相关匹配法,该方法采用乘积操作;数值越大表明匹配程度越好。

        TM_CCOEFF 相关系数匹配法,数值越大表明匹配程度越好。

        TM_SQDIFF_NORMED 归一化平方差匹配法,匹配越好,值越小;匹配越差,值越大。         TM_CCORR_NORMED 归一化相关匹配法,数值越大表明匹配程度越好。         TM_CCOEFF_NORMED 归一化相关系数匹配法,数值越大表明匹配程度越好。

图像读取与显示

kele = cv2.imread('kele.png')
template = cv2.imread('template.png')
cv2.imshow(winname='kele', mat=kele)
cv2.imshow(winname='template', mat=template)
cv2.waitKey(0)
  • cv2.imread:从本地加载图像,kele 存大图像、template 存模板小图像,默认按 BGR 色彩空间 读入(和日常 RGB 有区别,OpenCV 历史原因导致 )。
  • cv2.imshow:创建窗口显示图像,第一个参数是窗口名称(winname ),第二个是要显示的图像数据(mat )。
  • cv2.waitKey(0):暂停程序,等待用户按下任意键再继续执行,0 表示 “无限等待”,保证图像窗口能停留让我们看到内容。

获取模板尺寸与执行模板匹配

h, w = template.shape[:2]
res = cv2.matchTemplate(kele, template, cv2.TM_CCOEFF_NORMED)  
  • template.shape[:2]:获取模板图像的 高度(h)、宽度(w) ,shape 返回 (高,宽,通道数) ,[:2] 取前两个值,后续标记匹配区域会用到尺寸。
  • cv2.matchTemplate(...):执行模板匹配核心操作!把 template 当 “模板”,在 kele 里逐像素 / 逐区域滑动比较 。cv2.TM_CCOEFF_NORMED 是选的 归一化相关系数匹配法 ,结果存在 res 里:res 是个二维矩阵,每个元素值表示对应位置与模板的匹配程度(越接近 1 ,匹配越好 )。

解析匹配结果,标记目标区域

min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)  
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
kele_template = cv2.rectangle(kele, top_left, bottom_right, color=(0, 255, 0), thickness=2)  
  • cv2.minMaxLoc(res):从 res(匹配结果矩阵 )里找 最小值(min_val )、最大值(max_val ),以及它们对应的坐标(min_locmax_loc ) 。因为用了 TM_CCOEFF_NORMED ,值越大匹配越好,所以关注 max_loc(最佳匹配区域的左上角坐标 )。
  • bottom_right = (top_left[0] + w, top_left[1] + h):根据模板宽高,算出匹配区域 右下角坐标 ,这样就能确定一个矩形范围。
  • cv2.rectangle(...):在 kele 图像上画矩形!参数分别是:要画的图像(kele )、左上角坐标(top_left )、右下角坐标(bottom_right )、矩形颜色((0, 255, 0) ,即绿色,BGR 格式 )、线条粗细(thickness=2 ),结果存在 kele_template 里。

显示最终结果

cv2.imshow(winname='kele_template', mat=kele_template)
cv2.waitKey(0)
  • 用 cv2.imshow 显示标记了匹配区域的图像 kele_template ,再用 cv2.waitKey(0) 等待按键,方便我们查看匹配效果,程序最后才结束。


网站公告

今日签到

点亮在社区的每一天
去签到