计算机视觉----opencv(图像轮毂绘制(大小选择,排序,外接图形绘制),轮廓的近似,模板的匹配)
一、准备工作
图片准备
phone
flower
kele
template
二、图像轮毂绘制
cv2.drawContours(image, contours, contourIdx, color, thickness=None,lineType=None, hierarchy=None, maxLevel=None, offset=None)
参数含义如下:
image:要在其上绘制轮廓的输入图像。
contours:轮廓列表, 通常由cv2.findContours()函数返回。
contourIdx:要绘制的轮廓的索引。如果为负数, 则绘制所有轮廓, -1
color:轮廓的颜色, 以BGR格式表示。例如, (0, 255, 0) 表示绿色。
thickness:轮廓线的粗细, 默认值为1。
lineType:轮廓线的类型, 默认值为cv2.LINE_8。
hierarchy:轮廓层次结构, 通常由cv2.findContours()函数返回。
maxLevel:绘制的最大轮廓层级。默认值为None, 表示绘制所有层级。
offset:轮廓点的偏移量。默认值为None。
phone_color = cv2.imread('phone.png')
# 如果需要灰度图进行处理,可以单独创建灰度版本
phone_gray = cv2.cvtColor(phone_color, cv2.COLOR_BGR2GRAY)
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)#阈值处理为二值
_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
计算轮廓面积,contour 是轮廓点构成的二维向量(如轮廓列表 contours 中的一个轮廓)
oriented 是方向标记,默认 False 返回面积绝对值,True 时根据轮廓方向返回带符号数值
cv2.contourArea(contour[, oriented]) -> retval
area_0 = cv2.contourArea(contours[0])
print(area_0)
area_1 = cv2.contourArea(contours[1])
print(area_1)
计算轮廓周长,curve 是输入的二维点集(轮廓点),可为 vector 或 Mat 类型
closed 用于指示曲线是否封闭
arcLength(InputArray curve, bool closed)
length = cv2.arcLength(contours[0], closed=True)
print(length)
根据面积筛选轮廓并显示
a_list = []
for i in contours:# 筛选面积大于 10000 的轮廓if cv2.contourArea(i) > 10000:a_list.append(i)
image_copy = phone_gray.copy()
# 在拷贝图像上绘制筛选出的轮廓,contourIdx=-1 表示绘制所有轮廓,颜色 (0,255,0) 是绿色,线宽 3
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)
根据轮廓面积排序筛选(轮廓定位方法:选最大面积轮廓)
# 对轮廓按面积降序排序,key 用 cv2.contourArea,reverse=True 实现降序,取排序后第一个(最大面积)
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[0]
# 绘制最大面积轮廓,contourIdx=-1 绘制全部,颜色 (0,255,255) 是黄,线宽 3
image_contours = cv2.drawContours(phone_gray.copy(), contours=[sortcnt], contourIdx=-1, color=(0,255,255), thickness=3)
cv2.imshow(winname='image_contours', mat=image_contours)
cv2.waitKey(0)
外接圆、外接矩形绘制
计算轮廓的最小外接圆
cnt = contours[6]
# 计算轮廓的最小外接圆,返回圆心 (x,y) 和半径 r
(x, y), r = cv2.minEnclosingCircle(cnt)
# 在图像上绘制外接圆,圆心 (int(x),int(y)),半径 int(r),颜色 (0,255,0) 绿色,线宽 2
phone_circle = cv2.circle(phone_gray, 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)
计算轮廓的最小外接矩形
x, y, w, h = cv2.boundingRect(cnt)
# 在图像上绘制矩形,左上角 (x,y),右下角 (x+w,y+h),颜色 (0,255,0) 绿色,线宽 2
phone_rectangle = cv2.rectangle(phone_color, pt1=(x, y), pt2=(x + w, y + h), color=(255,0,0), thickness=2)
cv2.imshow(winname='phone_rectangle', mat=phone_rectangle)
cv2.waitKey(0)
三、轮廓的近似
approx = cv2.approxPolyDP(curve, epsilon, closed)
参数说明:
curve:输入轮廓。
epsilon:近似精度,即两个轮廓之间最大的欧式距离。该参数越小,得到的近似结果越接近实际轮廓;反之,得到的近似结果会更加粗略。
closed:布尔类型的参数,表示是否封闭轮廓。如果是 True,表示输入轮廓是封闭的,近似结果也会是封闭的;否则表示输入轮廓不是封闭的,近似结果也
返回值:approx,近似结果,是一个ndarray数组,为一个近似后的轮廓,包含了被近似出来的轮廓上的点的坐标
# 读取手机图片(加载用于处理的图像)
phone = cv2.imread('phone.png')
# 将彩色图像转换为灰度图(为后续二值化等操作做准备,灰度图更便于处理轮廓等)
phone_gray = cv2.cvtColor(phone, cv2.COLOR_BGR2GRAY)
# 对灰度图进行二值化处理(将图像转换为黑白二值形式,突出轮廓等特征,120 是阈值,255 是最大值,超过阈值设为 255,否则设为 0 )
ret, phone_thresh = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)# 获取轮廓(原写法注释,实际代码做了调整,通过指定索引 [-2] 来获取合适轮廓,cv2.RETR_TREE 是轮廓检索模式,cv2.CHAIN_APPROX_NONE 是逼近方法 )
# image, 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
epsilon = 0.01 * cv2.arcLength(contours[0], True)
cv2.arcLength(contours[0], True)
计算第一个轮廓的周长- 参数
True
表示该轮廓是闭合的 epsilon
是轮廓近似的精度参数,值越小,近似结果越接近原始轮廓- 这里使用轮廓周长的 1% 作为近似精度
轮廓近似处理
approx = cv2.approxPolyDP(contours[0], epsilon, True) print(approx.shape)
cv2.approxPolyDP()
是 OpenCV 中用于轮廓近似的函数- 第一个参数是要近似的原始轮廓(这里取 contours [0] 即第一个轮廓)
- 第二个参数是上面计算的精度 epsilon
- 第三个参数
True
表示近似后的轮廓仍然是闭合的 - 返回值
approx
是近似后的轮廓点集合
绘制近似后的轮廓
phone_new = phone.copy() image_contours = cv2.drawContours(phone_new, [approx], contourIdx=-1, color=(0, 255, 0), thickness=3)
cv2.drawContours()
用于在图像上绘制轮廓- 第一个参数是要绘制的图像(原图副本)
- 第二个参数是要绘制的轮廓(注意需要用列表包裹)
contourIdx=-1
表示绘制所有轮廓(这里只有一个)color=(0, 255, 0)
表示绘制颜色为绿色(BGR 格式)thickness=3
表示线条粗细为 3 像素
四、模板匹配
cv2.matchTemplate 函数用于在输入图像中匹配模板图像,找到最相似的位置
函数参数说明:
image:待搜索图像(即包含要查找模板的大图像 )
templ:模板图像(要在待搜索图像中查找的小图像 )
method:计算匹配程度的方法,可选以下几种:
TM_SQDIFF 平方差匹配法,该方法采用平方差来进行匹配;匹配越好,值越小;匹配越差,值越大。
TM_CCORR 相关匹配法;该方法采用乘法操作;数值越大表明匹配程度越好。
TM_CCOEFF 相关系数匹配法;数值越大表明匹配程度越好。
TM_SQDIFF_NORMED 归一化平方差匹配法,匹配越好,值越小;匹配越差,值越大。
TM_CCORR_NORMED 归一化相关匹配法,数值越大表明匹配程度越好。
TM_CCOEFF_NORMED 归一化相关系数匹配法,数值越大表明匹配程度越好。(这里代码中使用的就是这种 )
result:匹配结果输出,一般可不显式指定,让函数返回结果矩阵 ;mask:掩膜,可选参数,这里未使用
# 读取待搜索图像(假设名为 kele.png ,实际需保证文件存在且路径正确 )
# 读取模板图像(假设名为 template.png ,实际需保证文件存在且路径正确 )
kele = cv2.imread('kele.png')
template = cv2.imread('template.png')# 显示待搜索图像,窗口名为 'kele'
# 显示模板图像,窗口名为 'template'
# 等待按键输入,参数 0 表示一直等待,直到有按键按下
cv2.imshow(winname='kele', mat=kele)
cv2.imshow(winname='template', mat=template)
cv2.waitKey(0)
获取模板图像的高度(h )和宽度(w ),shape[0] 是高度,shape[1] 是宽度
进行模板匹配,使用归一化相关系数匹配法(TM_CCOEFF_NORMED )
返回匹配结果的矩阵,其中每个元素表示该位置与模板的匹配程度
h, w = template.shape[:2]
res = cv2.matchTemplate(image=kele, templ=template, method=cv2.TM_CCOEFF_NORMED)
cv2.minMaxLoc 可以获取矩阵中的最小值和最大值,以及最小值的索引坐标和最大值的索引坐标
min_val:匹配结果矩阵中的最小值;max_val:最大值;min_loc:最小值位置坐标;max_loc:最大值位置坐标
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
因为使用的 TM_CCOEFF_NORMED 方法,值越大匹配越好,所以取最大值对应的位置作为模板匹配到的左上角坐标
计算模板匹配区域的右下角坐标,根据模板宽高(w、h )和左上角坐标得到
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
在待搜索图像 kele 上绘制矩形,标记出模板匹配到的区域
矩形左上角坐标是 top_left ,右下角坐标是 bottom_right ,颜色为 (0, 255, 0) 即绿色,线条粗细为 2
kele_template = cv2.rectangle(img=kele, pt1=top_left, pt2=bottom_right, color=(0, 255, 0), thickness=2)# 显示标记了模板匹配区域的图像,窗口名为 'kele_template'
# 等待按键输入,参数 0 表示一直等待,直到有按键按下
cv2.imshow(winname='kele_template', mat=kele_template)
cv2.waitKey(0)
五、作业
对花朵图片绘制轮毂,自行调节阈值,并在同一个图上绘制近似轮毂,进准度为0.005
import cv2flower = cv2.imread('flower.png')
flower_gray = cv2.cvtColor(flower, cv2.COLOR_BGR2GRAY)
cv2.imshow('flower_gray',flower_gray)
ret, phone_thresh = cv2.threshold(flower_gray, 235, 255, cv2.THRESH_BINARY)
cv2.imshow('phone_thresh',phone_thresh)
contours = cv2.findContours(phone_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2]
combined_image = flower.copy()
sortcnt = sorted(contours, key=cv2.contourArea, reverse=True)[1]
cv2.drawContours(combined_image, [sortcnt], -1, color=(0, 255, 0), thickness=3)
epsilon = 0.005 * cv2.arcLength(sortcnt, True)
approx = cv2.approxPolyDP(sortcnt, epsilon, True)
cv2.drawContours(combined_image, [approx], -1, color=(255, 0, 0), thickness=3)
cv2.imshow('最大轮廓(绿)和近似轮廓(蓝)', combined_image)
cv2.waitKey(0)
解读:
首先导入 OpenCV 库,然后通过
cv2.imread('flower.png')
读取名为 'flower.png' 的图像文件,将其存储在变量flower
中。使用
cv2.cvtColor(flower, cv2.COLOR_BGR2GRAY)
将彩色图像转换为灰度图像,存储在flower_gray
中,这是很多图像处理的预处理步骤,便于后续分析。接着用cv2.imshow('flower_gray', flower_gray)
显示灰度图像。进行阈值分割处理,
cv2.threshold(flower_gray, 235, 255, cv2.THRESH_BINARY)
将灰度图像中灰度值大于 235 的像素设为 255(白色),其余设为 0(黑色),得到二值化图像,结果存储在phone_thresh
中(这里变量名可能是笔误,应为flower_thresh
更合适),并通过cv2.imshow
显示该阈值图像。调用
cv2.findContours(phone_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2]
从阈值处理后的图像中寻找轮廓,cv2.RETR_TREE
表示检索所有轮廓并建立层次关系,cv2.CHAIN_APPROX_NONE
表示存储所有轮廓点,[-2]
用于兼容不同 OpenCV 版本以获取轮廓列表,结果存在contours
中。复制原图到
combined_image
用于后续绘制操作,通过sorted(contours, key=cv2.contourArea, reverse=True)[1]
按轮廓面积降序排序轮廓,并取第二个最大的轮廓(通常[0]
是整个图像的外轮廓),存储在sortcnt
中。用
cv2.drawContours(combined_image, [sortcnt], -1, color=(0, 255, 0), thickness=3)
在原图副本上绘制该最大轮廓,使用绿色(BGR 格式的 (0,255,0)),线宽为 3。计算轮廓近似的精度
epsilon
,其值为轮廓周长的 0.5%(通过0.005 * cv2.arcLength(sortcnt, True)
计算),然后用cv2.approxPolyDP(sortcnt, epsilon, True)
对轮廓进行多边形近似,减少轮廓点数量,结果存在approx
中。再用cv2.drawContours
以蓝色绘制近似后的轮廓。最后通过
cv2.imshow('最大轮廓(绿)和近似轮廓(蓝)', combined_image)
显示包含两种轮廓的最终图像,并使用cv2.waitKey(0)
等待用户按键,参数 0 表示无限等待,直到用户操作后关闭窗口。
效果: