计算机视觉----opencv(图像轮毂绘制(大小选择,排序,外接图形绘制),轮廓的近似,模板的匹配)

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

一、准备工作

图片准备

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]
  1. 计算近似精度 epsilon

    epsilon = 0.01 * cv2.arcLength(contours[0], True)
    
     
    • cv2.arcLength(contours[0], True) 计算第一个轮廓的周长
    • 参数 True 表示该轮廓是闭合的
    • epsilon 是轮廓近似的精度参数,值越小,近似结果越接近原始轮廓
    • 这里使用轮廓周长的 1% 作为近似精度
  2. 轮廓近似处理

    approx = cv2.approxPolyDP(contours[0], epsilon, True)
    print(approx.shape)
    
     
    • cv2.approxPolyDP() 是 OpenCV 中用于轮廓近似的函数
    • 第一个参数是要近似的原始轮廓(这里取 contours [0] 即第一个轮廓)
    • 第二个参数是上面计算的精度 epsilon
    • 第三个参数 True 表示近似后的轮廓仍然是闭合的
    • 返回值 approx 是近似后的轮廓点集合
  3. 绘制近似后的轮廓

    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 cv2

flower = 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)

解读:

  1. 首先导入 OpenCV 库,然后通过cv2.imread('flower.png')读取名为 'flower.png' 的图像文件,将其存储在变量flower中。

  2. 使用cv2.cvtColor(flower, cv2.COLOR_BGR2GRAY)将彩色图像转换为灰度图像,存储在flower_gray中,这是很多图像处理的预处理步骤,便于后续分析。接着用cv2.imshow('flower_gray', flower_gray)显示灰度图像。

  3. 进行阈值分割处理,cv2.threshold(flower_gray, 235, 255, cv2.THRESH_BINARY)将灰度图像中灰度值大于 235 的像素设为 255(白色),其余设为 0(黑色),得到二值化图像,结果存储在phone_thresh中(这里变量名可能是笔误,应为flower_thresh更合适),并通过cv2.imshow显示该阈值图像。

  4. 调用cv2.findContours(phone_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2]从阈值处理后的图像中寻找轮廓,cv2.RETR_TREE表示检索所有轮廓并建立层次关系,cv2.CHAIN_APPROX_NONE表示存储所有轮廓点,[-2]用于兼容不同 OpenCV 版本以获取轮廓列表,结果存在contours中。

  5. 复制原图到combined_image用于后续绘制操作,通过sorted(contours, key=cv2.contourArea, reverse=True)[1]按轮廓面积降序排序轮廓,并取第二个最大的轮廓(通常[0]是整个图像的外轮廓),存储在sortcnt中。

  6. cv2.drawContours(combined_image, [sortcnt], -1, color=(0, 255, 0), thickness=3)在原图副本上绘制该最大轮廓,使用绿色(BGR 格式的 (0,255,0)),线宽为 3。

  7. 计算轮廓近似的精度epsilon,其值为轮廓周长的 0.5%(通过0.005 * cv2.arcLength(sortcnt, True)计算),然后用cv2.approxPolyDP(sortcnt, epsilon, True)对轮廓进行多边形近似,减少轮廓点数量,结果存在approx中。再用cv2.drawContours以蓝色绘制近似后的轮廓。

  8. 最后通过cv2.imshow('最大轮廓(绿)和近似轮廓(蓝)', combined_image)显示包含两种轮廓的最终图像,并使用cv2.waitKey(0)等待用户按键,参数 0 表示无限等待,直到用户操作后关闭窗口。

效果: