机器视觉学习-day13-凸包特征检测
1 凸包
假如在图像中有一些点:

经过凸包检测并绘制之后,应该得到的结果:

2 基础凸包检测算法
1.先找到最左边和最右边的点

2.连接上面的两个点,将点集分为上半区和下半区,以上半区为例:

3. 找到上半区中离直线最远的点。
假设直线的方程为:,则点(x0,y0)到直线的公式为:

4. 把最远的像素点与之前最左和最右的像素点连接,把这两条直线明明为y1和y2。

5. 求出y1和y2的直线方程,把上半区所有的点带入计算与y1和y2的距离(分正负):

d=0时,表示点在y1或y2上,可以忽略
d>0时,表示点在y1或y2上方,这表示需要更新凸包点
d<0时,表示点在y1和y2下方,忽略
6. 当上一步出现d>0的情况时,需要更新凸包点,重新连接,形成新的y2和y3:

7. 在新的y2和y3中重新反复计算新凸包点,直到上半区计算完成。
8. 下半区执行跟上半区相似的操作,但是判断条件改为寻找d<0的点。
上面的过程都是基于已知坐标点进行的处理,实际上对于未处理的图像,可能并不能直接获取点的坐标,特别是彩色图像,需要转换为二值化后的图像,并通过轮廓检测算法获取边界点的坐标。
3 其他检测凸包方法
以下方法,了解即可。
3.1 Graham扫描法
Graham扫描法是一种用于寻找二维平面上点集凸包的算法,也是OpenCV凸包检测的基本算法。其基本思想是先找一个基点,从这个基点出发,按照逆时针方向逐个寻找凸包点。
3.2 QuickHull法
在二维空间中,QuickHull用于计算凸包,采用分而治之的策略,通过递归将点集划分为更小的子集,并构建这些子集的凸包,用于OpenCV的大量计算加速。
3.3 Andrew扫描链法
4 代码实践
代码步骤:图片输入→灰度化→二值化→寻找轮廓→查找凸包→绘制轮廓→图像输出
4.1 寻找轮廓
轮廓指的是前景的轮廓,参数如下:

轮廓的查找方式:先使用默认参数,细节下一章讲解。
轮廓的近似办法:先使用默认参数,细节下一章讲解。
4.2 查找凸包
寻找凸包节点不提供参数
4.3 绘制轮廓


使用图片:tubao1.jpg

import cv2
if __name__ == '__main__':
# 1. 图片输入
path = 'tubao1.jpg'
image_np = cv2.imread(path)
# 2. 灰度化
image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)
# 3. 二值化
ret, image_np_thresh = cv2.threshold(
image_np_gray, # 输入的灰度图像
127, # 阈值(127),但使用OTSU方法时会自动计算最佳阈值
255, # 最大值(255),当像素值超过阈值时赋予的值
cv2.THRESH_BINARY + cv2.THRESH_OTSU
)
# cv2.THRESH_BINARY:二值化,大于阈值的设为最大值,其余设为0
# cv2.THRESH_OTSU:使用大津算法自动确定最佳阈值
print('thresh:', ret)
# ret:实际使用的阈值
# image_np_thresh:二值化后的图像
# 4.寻找轮廓
# 返回值1:轮廓的点坐标,使用array存储n个轮廓的信息
# 返回值2:多个轮廓之间的层级关系
contours, hierarchy = cv2.findContours(
image_np_thresh, # 二值化图像
cv2.RETR_EXTERNAL, # 轮廓的查找方式,cv2.RETR_EXTERNAL:只检测最外层轮廓
cv2.CHAIN_APPROX_SIMPLE # 轮廓近似办法,cv2.CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角方向的元素,只保留端点
)
print(len(contours)) # 轮廓的数量,contours:轮廓的列表,每个轮廓是一个点集
# print(contours)
# print(hierarchy)
# 拿到第一个轮廓的数据
cnt = contours[0] # 获取第一个轮廓
# print(cnt)
# 1394表示前景轮廓的像素点,1表示占位,2表示二维点
print(cnt.shape) # (1394, 1, 2)
# 5. 查找凸包
# 返回轮廓凸包点
hull = cv2.convexHull(cnt) # 参数也可以替换为其他的轮廓,cnt是第一个轮廓
print(hull)
print(hull.shape) # 包含凸包点数量
"""
cv2.convexHull()函数计算轮廓的凸包
凸包是包含轮廓所有点的最小凸多边形
参数:轮廓点集
返回值:凸包的点集
"""
# 6. 绘制轮廓
# 绘制多条线
cv2.polylines(
image_np, # 在哪个图上画线
[hull], # 绘制的轮廓列表
isClosed=True, # 轮廓是否封闭
color=(0, 0, 255), # 颜色
thickness=2 # 粗细
)
"""
cv2.polylines()函数在图像上绘制多边形
参数1:要绘制的图像
参数2:要绘制的轮廓列表(需要是列表形式)
参数3:是否闭合轮廓(True表示连接首尾点)
参数4:颜色(BGR格式,这里是红色)
参数5:线宽
"""
# 7. 图片输出
cv2.imshow('image_np', image_np)
cv2.waitKey(0)
cv2.imwrite('tubao1-1.png', image_np)
"""
代码逻辑总结:
读取图像文件
转换为灰度图像
二值化处理,分离前景和背景
在二值图像中查找轮廓
计算第一个轮廓的凸包
在原图上绘制凸包
显示结果图像
"""
代码运行后结果:tubao1-1.png

练习:同时绘制tubao2的两个凸包轮廓,分别使用不同的颜色。
图片为:tubao2.jpg

"""同时绘制tubao2的两个凸包轮廓,分别使用不同的颜色。"""
import cv2
if __name__ == '__main__':
# 1. 图片输入
path = 'tubao2.jpg'
image_np = cv2.imread(path)
# 2. 灰度化
image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)
# 3. 二值化
ret, image_np_thresh = cv2.threshold(
image_np_gray, # 输入的灰度图像
127, # 阈值(127),但使用OTSU方法时会自动计算最佳阈值
255, # 最大值(255),当像素值超过阈值时赋予的值
cv2.THRESH_BINARY + cv2.THRESH_OTSU
)
# cv2.THRESH_BINARY:二值化,大于阈值的设为最大值,其余设为0
# cv2.THRESH_OTSU:使用大津算法自动确定最佳阈值
print('thresh:', ret)
# 4.寻找轮廓
# 返回值1:轮廓的点坐标,使用array存储n个轮廓的信息
# 返回值2:多个轮廓之间的层级关系
contours, hierarchy = cv2.findContours(
image_np_thresh, # 二值化图像
cv2.RETR_EXTERNAL, # 轮廓的查找方式,cv2.RETR_EXTERNAL:只检测最外层轮廓
cv2.CHAIN_APPROX_SIMPLE # 轮廓近似办法,cv2.CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角方向的元素,只保留端点
)
print('找到的轮廓数量:', len(contours)) # 打印轮廓的数量
# 轮廓的数量,contours:轮廓的列表,每个轮廓是一个点集
# 5. 处理每个轮廓并绘制凸包
# 定义不同颜色用于绘制不同轮廓的凸包
colors = [
(0, 0, 255), # 红色 (BGR格式)
(0, 255, 255), # 黄色
(0, 255, 0), # 绿色
(255, 0, 0), # 蓝色
(255, 0, 255), # 粉色
(255, 255, 0) # 青色
]
# 遍历所有找到的轮廓
for i, cnt in enumerate(contours):
# # 计算当前轮廓的面积,过滤掉太小的轮廓(可能是噪声)
# area = cv2.contourArea(cnt)
# if area < 100: # 忽略面积小于100像素的轮廓
# continue
"""
for循环:遍历contours列表中的所有轮廓
enumerate(contours):同时获取轮廓的索引i和轮廓数据cnt
i:当前轮廓在列表中的索引(从0开始)
cnt:当前轮廓的点集数据(NumPy数组)
cv2.contourArea(cnt):计算当前轮廓的面积
这个函数接收一个轮廓点集作为参数,返回该轮廓所包围区域的面积
面积单位是像素,表示轮廓内部区域的大小
"""
# 计算当前轮廓的凸包
hull = cv2.convexHull(cnt)
"""
cv2.convexHull()函数计算轮廓的凸包
凸包是包含轮廓所有点的最小凸多边形
参数:轮廓点集
返回值:凸包的点集
"""
# 选择颜色(循环使用预定义的颜色)
# color = colors[i] # 当i≥6时会报IndexError
color = colors[i % len(colors)] # 0%6=0...7%6=1(循环)
# 6.绘制当前轮廓的凸包
cv2.polylines(
image_np, # 在原图上绘制
[hull], # 要绘制的凸包点集
isClosed=True, # 闭合轮廓
color=color, # 使用选择的颜色
thickness=2 # 线宽为2像素
)
"""
cv2.polylines()函数在图像上绘制多边形
参数1:要绘制的图像
参数2:要绘制的轮廓列表(需要是列表形式)
参数3:是否闭合轮廓(True表示连接首尾点)
参数4:颜色(BGR格式,这里是红色)
参数5:线宽
"""
# 7. 图片输出
cv2.imshow('轮廓凸包检测结果', image_np) # 显示处理后的图像
cv2.waitKey(0) # 等待按键,0表示无限等待
cv2.destroyAllWindows() # 关闭所有OpenCV窗口
cv2.imwrite('tubao2-2.jpg', image_np)
运行结果:tubao2-2.jpg

练习:
给下面的图像绘制凸包轮廓,灰色是背景。
图片:2.jpg (# 使用灰度化、二值化后,颜色浅的 无法选中)

"""给下面的图像绘制凸包轮廓,灰色是背景。""" # 凸包特征检测
# 1.读取图像 → 2. 转换为灰度图 → 3. 二值化处理 (图像操作必要步骤)
# → 4. 查找轮廓 → 5. 绘制轮廓 → 6. 显示结果
import cv2
import numpy as np
if __name__ == '__main__':
# 1.图片输入
image_np = cv2.imread('2.jpg')
# 使用灰度化、二值化后,颜色浅的 无法选中
# 2.HSV空间转换
hsv_image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2HSV) # BGR转HSV
# 3. 制作掩膜
# 定义红色范围1
red_low = np.array([0, 43, 46]) # 红色下限(Hmin, Smin, Vmin)
red_high = np.array([10, 255, 255]) # 红色上限(Hmax, Smax, Vmax)
mask1 = cv2.inRange( # 创建掩膜
hsv_image_np, # 基于哪个图像, 输入HSV图像
red_low, # 颜色下限
red_high # 颜色上限
)
# 定义红色范围2
red_low = np.array([156, 43, 46]) # 红色第二范围下限(深红色)
red_high = np.array([180, 255, 255]) # 第二范围上限
# 制作掩膜
mask2 = cv2.inRange( # 创建第二个掩膜
hsv_image_np,
red_low,
red_high
)
# 定义绿色范围
red_low = np.array([35, 43, 46]) # 红色第二范围下限(深红色)
red_high = np.array([77, 255, 255]) # 第二范围上限
# 制作掩膜
mask3 = cv2.inRange( # 创建第二个掩膜
hsv_image_np,
red_low,
red_high
)
# 定义黄色范围
red_low = np.array([26, 43, 46]) # 红色第二范围下限(深红色)
red_high = np.array([34, 255, 255]) # 第二范围上限
# 制作掩膜
mask4 = cv2.inRange( # 创建第二个掩膜
hsv_image_np,
red_low,
red_high
)
# 定义紫色范围
red_low = np.array([125, 43, 46]) # 红色第二范围下限(深红色)
red_high = np.array([155, 255, 255]) # 第二范围上限
# 制作掩膜
mask5 = cv2.inRange( # 创建第二个掩膜
hsv_image_np,
red_low,
red_high
)
# 合并掩膜
mask_image_np1 = cv2.bitwise_or(mask1, mask2) # 红
mask_image_np2 = cv2.bitwise_or(mask_image_np1, mask3) # 红+绿
mask_image_np3 = cv2.bitwise_or(mask_image_np2, mask4) # 红+绿+黄
mask_image_np4 = cv2.bitwise_or(mask_image_np3, mask5) # 红+绿+黄+紫
cv2.imshow('mask_image_np4', mask_image_np4)
cv2.imwrite('mask_image_np4', mask_image_np4)
# 4.寻找轮廓
contours, hierarchy = cv2.findContours( # cv2.findContours(): 查找图像中的轮廓
mask_image_np4,
cv2.RETR_EXTERNAL, # 轮廓检索模式,只检测最外层轮廓
cv2.CHAIN_APPROX_SIMPLE, # 轮廓近似方法,压缩水平、垂直和对角线段,只保留它们的端点
)
print(len(contours)) # print(len(contours)):打印找到的轮廓数量
# 5. 处理每个轮廓并绘制凸包
# 定义不同颜色用于绘制不同轮廓的凸包
colors = [ # 在cv2中,BGR格式
(255, 255, 0), # 青色
(0, 0, 255), # 红色
(255, 0, 0), # 蓝色
(0, 255, 255), # 黄色
(0, 255, 0), # 绿色
]
# 遍历所有找到的轮廓
for i, cnt in enumerate(contours):
# 计算当前轮廓的面积,过滤掉太小的轮廓(可能是噪声)
area = cv2.contourArea(cnt)
if area < 100: # 忽略面积小于100像素的轮廓
continue
# 计算当前轮廓的凸包
hull = cv2.convexHull(cnt)
# 选择颜色(循环使用预定义的颜色)
color = colors[i] # 当i≥6时会报IndexError
# color = colors[i % len(colors)] # 0%6=0...7%6=1(循环)
# 6.绘制当前轮廓的凸包
cv2.polylines(
image_np, # 在原图上绘制
[hull], # 要绘制的凸包点集
isClosed=True, # 闭合轮廓
color=color, # 使用选择的颜色
thickness=2 # 线宽为2像素
)
# 7. 图片输出
cv2.imshow('image_np', image_np) # 显示处理后的图像
cv2.waitKey(0) # 等待按键,0表示无限等待
cv2.destroyAllWindows() # 关闭所有OpenCV窗口
cv2.imwrite('color.png', image_np)
运行掩膜图像:mask_image_np4.png

运行结果:color.png

更多推荐



所有评论(0)