机器视觉学习-day15-图像轮廓特征查找
1 图像轮廓特征查找
代码步骤:图片输入→灰度化→二值化→寻找轮廓→绘制轮廓→外接轮廓→图片输出
contour_type 轮廓类型,包括:
boundingRect 外接矩形
minAreaRect 最小外接矩形
minEnclosingCircle 最小外接圆
color :线段颜色
thickness :线段粗细
1.1 外接矩形
下图中绿的是外接矩形,表示不考虑旋转并且能包含整个轮廓的矩形,外接矩形可根据获得的轮廓坐标中最上、最下、最左和最右四个坐标值绘制。
原始图像:4.jpg
import cv2
import numpy as npif __name__ == '__main__':# 1. 图片输入path = '4.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, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)print(ret)# 4. 寻找轮廓contours, hierarchy = cv2.findContours(image_np_thresh, # 二值化之后的图像cv2.RETR_EXTERNAL, # 查找方式cv2.CHAIN_APPROX_SIMPLE # 近似办法)print(len(contours))for i in contours:print(i.shape)# 5. 绘制轮廓image_np = cv2.drawContours(image_np, # 在哪个图上绘制contours, # 轮廓数据列表contourIdx=-1, # 绘制轮廓的id,-1表示全绘制color=(0, 0, 255), # 绘制的颜色thickness=2 # 线宽)# 6. 外界轮廓contour_image = image_np.copy()for i in contours:# 计算轮廓的外接矩形# 返回值:矩形的左上角xy坐标和矩形的宽高x, y, w, h = cv2.boundingRect(i)print('外接矩形的左上角坐标:', x, y)print('外接矩形的宽高:', w, h)# 绘制外接矩形cv2.rectangle(contour_image,(x, y),(x + w, y + h),(0, 255, 0),2)# 6. 图片输出cv2.imshow('image_np', image_np) # 轮廓cv2.imshow('contour_image', contour_image) # 外接矩形cv2.waitKey(0)cv2.imwrite('image_np1.png', image_np)cv2.imwrite('waijiejuxing.png', contour_image)
运行结果,轮廓:image_np1.png
外接矩形:waijiejuxing.png
1.2 最小外接矩形
最小外接矩形就是上图中蓝色的矩形,寻找最小外接矩形的算法叫做旋转卡壳法,计算过程如下:
旋转卡壳法有一个重要的前提条件:对于多边形P的一个外接矩形(最小外接矩形)存在一条边与多边形P共线。
假设某轮廓的凸包图如下所示:
步骤1:起始边共线:找高度
根据前提条件,凸多边形的最小外接矩形与凸多边形的某条边是共线的,因此选择凸多边形的一条边为起始边,然后按照逆时针方向计算每个凸包点与起始边的距离,并将距离最大的点记录下来。
如上图所示,首先以ab为起始边,计算出e点离起始边的距离最远,那么e到起始边的距离就是一个矩形的高度。
步骤2:投影找宽度
对于矩形的最右边,以向量为基准,分别计算凸包点在向量上的投影长度,投影长度最长的凸包点所在的垂直与起始边的直线就是矩形最右边的直线。
如上图所示,d点就是在向量上投影长度最长的点,那么通过d点垂直与直线ab的直线就是外接矩形右边界所在的直线。
矩形的左边界也是这样计算的,不同的是使用的向量是而不是,如下图所示。
步骤3:更换起始边反复计算
矩形的高就是e点到直线ab的距离,矩形的宽就是是h点在向量的投影加上d点在向量上的投影减去ab的长度:
这样就构成了外接矩形,计算其面积。然后更换起始边,计算新的起始边对应的外接矩形面积,即凸多边形有几个边就计算几次外接矩形,然后找到所有面积中最小的矩形作为凸多边形的最小外接矩形。
图片同上:4.jpg
import cv2 # OpenCV库,用于计算机视觉任务
import numpy as np # Python科学计算库,OpenCV使用numpy数组存储图像数据if __name__ == '__main__':# 1. 图片输入path = '4.jpg'image_np = cv2.imread(path) # 读取图像文件,返回一个numpy数组表示的图像# 默认读取为BGR格式(OpenCV的标准格式,非RGB)# 2. 灰度化image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)# cv2.COLOR_BGR2GRAY:将BGR彩色图像转换为灰度图像# 3. 二值化ret, image_np_thresh = cv2.threshold( # cv2.threshold():图像二值化函数,将灰度图转换为只有黑白两种颜色的图像image_np_gray, # 输入的灰度图像127, # 阈值(但使用OTSU时会自动计算,此值被忽略)255, # 最大值(当像素值超过阈值时赋予的值)cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU # 反向二值化(大于阈值变为0,否则变为最大值)) # ret:实际使用的阈值(OTSU算法计算出的最佳阈值)print(ret) # 打印实际使用的阈值值# 4. 寻找轮廓contours, hierarchy = cv2.findContours(image_np_thresh, # 二值化之后的图像cv2.RETR_EXTERNAL, # 查找方式:cv2.RETR_EXTERNAL:只检测最外层轮廓cv2.CHAIN_APPROX_SIMPLE, # 近似办法: 压缩水平、垂直和对角线段# cv2.CHAIN_APPROX_NONE # 存储所有轮廓点)"""需要完整轮廓信息(如精确测量周长):使用CHAIN_APPROX_NONE只需要轮廓形状(如物体检测、识别):使用CHAIN_APPROX_SIMPLECHAIN_APPROX_SIMPLE:仅保存轮廓的关键点(端点)压缩水平、垂直和对角方向的冗余点,产生的轮廓数据量较小,对于大多数应用足够使用处理内存敏感的应用:使用CHAIN_APPROX_SIMPLE处理简单几何形状:使用CHAIN_APPROX_SIMPLE足够""""""返回值:contours:找到的轮廓列表,每个轮廓是一个点集hierarchy:轮廓的层次信息(此代码中未使用)"""print(len(contours)) # 打印找到的轮廓数量for i in contours:print(i.shape) # 循环打印每个轮廓的形状信息# 5. 绘制轮廓image_np = cv2.drawContours( # cv2.drawContours():在图像上绘制轮廓image_np, # 要绘制轮廓的目标图像,即在哪个图上绘制contours, # 轮廓数据列表contourIdx=-1, # 绘制轮廓的id,-1表示全绘制color=(0, 0, 255), # 绘制的颜色thickness=2 # 线宽)# 6. 外界轮廓contour_image = image_np.copy() # 创建原图的副本,避免修改原图for i in contours:# 计算最小外接矩形# 返回值:包含最小外接矩形参数的对象rect = cv2.minAreaRect(i) # cv2.minAreaRect():计算轮廓的最小外接矩形(可能是旋转的)# 包含矩形对角线角点的坐标和矩形的角度print(rect)# 把矩形数据转换为四个角点坐标points = cv2.boxPoints(rect)print(points) # 浮点型# 浮点型转换为整型box = np.int32(points) # OpenCV绘制需要整数坐标print(box)# 绘制轮廓cv2.drawContours(contour_image, # 要在其上绘制轮廓的目标图像[box], # 要绘制的轮廓数据,注意这里传入的是包含单个轮廓的列表-1, # 绘制所有轮廓,如果为0或正整数,则只绘制指定索引的轮廓(0, 255, 255), # 黄色2) # 虽然我们只传入了一个轮廓[box],但使用-1表示绘制这个列表中的所有轮廓(即这个矩形)"""box是通过cv2.boxPoints(rect)和np.int32(points)转换得到的最小外接矩形的四个角点必须用方括号括起来,因为drawContours函数期望一个轮廓列表,即使只绘制一个轮廓 示例:如果box = [[x1,y1], [x2,y2], [x3,y3], [x4,y4]],那么应该传入[box]而不是box虽然我们只传入了一个轮廓[box],但使用-1表示绘制这个列表中的所有轮廓(即这个矩形)代码逻辑流程:在之前的代码中,已经计算出了最小外接矩形的四个角点box,现在要将这个矩形绘制到图像上使用drawContours函数,传入:目标图像contour_image 轮廓数据[box](注意用方括号包装) 绘制所有轮廓(索引为-1) 使用黄色(0, 255, 255) 线宽为2像素"""# 7. 图片输出cv2.imshow('image_np', image_np) # 显示带原始轮廓的图像cv2.imshow('contour_image', contour_image) # 显示带外接矩形的图像cv2.waitKey(0)cv2.imshow('min_waijiejuxing.png', contour_image)"""
代码执行流程总结读取输入图像: cv2.imread() 转换为灰度图像: cv2.cvtColor(),cv2.COLOR_BGR2GRAY 二值化处理(使用OTSU算法自动确定阈值):cv2.threshold() 查找图像中的轮廓:cv2.findContours() 在原图上绘制找到的轮廓:cv2.drawContours() 为每个轮廓计算最小外接矩形: cv2.minAreaRect() ,cv2.boxPoints()矩形转换为四个角点坐标在图像副本上绘制这些外接矩形:cv2.drawContours() 显示两种结果图像
"""import cv2 # OpenCV库,用于计算机视觉任务
import numpy as np # Python科学计算库,OpenCV使用numpy数组存储图像数据if __name__ == '__main__':# 1. 图片输入path = '4.jpg'image_np = cv2.imread(path) # 读取图像文件,返回一个numpy数组表示的图像# 默认读取为BGR格式(OpenCV的标准格式,非RGB)# 2. 灰度化image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)# cv2.COLOR_BGR2GRAY:将BGR彩色图像转换为灰度图像# 3. 二值化ret, image_np_thresh = cv2.threshold( # cv2.threshold():图像二值化函数,将灰度图转换为只有黑白两种颜色的图像image_np_gray, # 输入的灰度图像127, # 阈值(但使用OTSU时会自动计算,此值被忽略)255, # 最大值(当像素值超过阈值时赋予的值)cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU # 反向二值化(大于阈值变为0,否则变为最大值)) # ret:实际使用的阈值(OTSU算法计算出的最佳阈值)print(ret) # 打印实际使用的阈值值# 4. 寻找轮廓contours, hierarchy = cv2.findContours(image_np_thresh, # 二值化之后的图像cv2.RETR_EXTERNAL, # 查找方式:cv2.RETR_EXTERNAL:只检测最外层轮廓cv2.CHAIN_APPROX_SIMPLE, # 近似办法: 压缩水平、垂直和对角线段# cv2.CHAIN_APPROX_NONE # 存储所有轮廓点)"""需要完整轮廓信息(如精确测量周长):使用CHAIN_APPROX_NONE只需要轮廓形状(如物体检测、识别):使用CHAIN_APPROX_SIMPLECHAIN_APPROX_SIMPLE:仅保存轮廓的关键点(端点)压缩水平、垂直和对角方向的冗余点,产生的轮廓数据量较小,对于大多数应用足够使用处理内存敏感的应用:使用CHAIN_APPROX_SIMPLE处理简单几何形状:使用CHAIN_APPROX_SIMPLE足够""""""返回值:contours:找到的轮廓列表,每个轮廓是一个点集hierarchy:轮廓的层次信息(此代码中未使用)"""print(len(contours)) # 打印找到的轮廓数量for i in contours:print(i.shape) # 循环打印每个轮廓的形状信息# 5. 绘制轮廓image_np = cv2.drawContours( # cv2.drawContours():在图像上绘制轮廓image_np, # 要绘制轮廓的目标图像,即在哪个图上绘制contours, # 轮廓数据列表contourIdx=-1, # 绘制轮廓的id,-1表示全绘制color=(0, 0, 255), # 绘制的颜色thickness=2 # 线宽)# 6. 外界轮廓contour_image = image_np.copy() # 创建原图的副本,避免修改原图for i in contours:# 计算最小外接矩形# 返回值:包含最小外接矩形参数的对象rect = cv2.minAreaRect(i) # cv2.minAreaRect():计算轮廓的最小外接矩形(可能是旋转的)# 包含矩形对角线角点的坐标和矩形的角度print(rect)# 把矩形数据转换为四个角点坐标points = cv2.boxPoints(rect)print(points) # 浮点型# 浮点型转换为整型box = np.int32(points) # OpenCV绘制需要整数坐标print(box)# 绘制轮廓cv2.drawContours(contour_image, # 要在其上绘制轮廓的目标图像[box], # 要绘制的轮廓数据,注意这里传入的是包含单个轮廓的列表-1, # 绘制所有轮廓,如果为0或正整数,则只绘制指定索引的轮廓(0, 255, 255), # 黄色2) # 虽然我们只传入了一个轮廓[box],但使用-1表示绘制这个列表中的所有轮廓(即这个矩形)"""box是通过cv2.boxPoints(rect)和np.int32(points)转换得到的最小外接矩形的四个角点必须用方括号括起来,因为drawContours函数期望一个轮廓列表,即使只绘制一个轮廓 示例:如果box = [[x1,y1], [x2,y2], [x3,y3], [x4,y4]],那么应该传入[box]而不是box虽然我们只传入了一个轮廓[box],但使用-1表示绘制这个列表中的所有轮廓(即这个矩形)代码逻辑流程:在之前的代码中,已经计算出了最小外接矩形的四个角点box,现在要将这个矩形绘制到图像上使用drawContours函数,传入:目标图像contour_image 轮廓数据[box](注意用方括号包装) 绘制所有轮廓(索引为-1) 使用黄色(0, 255, 255) 线宽为2像素"""# 7. 图片输出cv2.imshow('image_np', image_np) # 显示带原始轮廓的图像cv2.imshow('contour_image', contour_image) # 显示带外接矩形的图像cv2.waitKey(0)cv2.imwrite('min_waijiejuxing.png', contour_image)"""
代码执行流程总结读取输入图像: cv2.imread() 转换为灰度图像: cv2.cvtColor(),cv2.COLOR_BGR2GRAY 二值化处理(使用OTSU算法自动确定阈值):cv2.threshold() 查找图像中的轮廓:cv2.findContours() 在原图上绘制找到的轮廓:cv2.drawContours() 为每个轮廓计算最小外接矩形: cv2.minAreaRect() ,cv2.boxPoints()矩形转换为四个角点坐标在图像副本上绘制这些外接矩形:cv2.drawContours() 显示两种结果图像
"""
运行后图片为:min_waijiejuxing.png
1.3 外接圆
寻找最小外接圆的算法是随机增量算法(Welzl算法)。
给定n个点Sn={P0, P1,..., Pn-1}的最小外接圆,该算法基于下面的前提:如果点集Si={P0, P1,..., Pi-1}的最小外接圆为Di,那么Di需要包含原外位于圆Di的另一个点Pi,则新的最小外接圆Di+1一定在圆上包含Pi点。
先取3个点建立一个圆(不共线的三个点即可确定一个圆,如果共线就取距离最远的两个点作为直径建立圆),然后遍历剩下的所有点,对于遍历到的点P来说:
如果该点在圆内,那么最小覆盖圆不变。
如果该点在圆外,根据上述定理,该点一定在想要求得的最小覆盖圆的圆周上,又因为三个点才能确定一个圆,所以需要枚举P点之前的点来找其余的两个点。当找到与P点组成的圆能够将所有点都包含在圆内或圆上,该圆就是这些点的最小外接圆。
图片同上:4.jpg
import cv2 # 导入OpenCV库,用于图像处理
import numpy as np # 导入NumPy库,用于数值计算和数组操作if __name__ == '__main__': # Python主程序入口# 1. 图片输入path = '4.jpg' # 定义图像文件的路径image_np = cv2.imread(path) # 读取图像文件,返回一个NumPy数组表示的图像# 2. 灰度化image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) # 将彩色图像转换为灰度图像# 3. 二值化# 使用OTSU算法自动确定最佳阈值,并进行反向二值化处理ret, image_np_thresh = cv2.threshold(image_np_gray,127,255,cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)print(ret) # 打印OTSU算法计算出的最佳阈值# 4. 寻找轮廓# 在二值图像中查找轮廓contours, hierarchy = cv2.findContours(image_np_thresh, # 输入的二值化图像cv2.RETR_EXTERNAL, # 只检测最外层轮廓cv2.CHAIN_APPROX_SIMPLE # 压缩水平、垂直和对角方向的元素,只保留它们的端点)print(len(contours)) # 打印找到的轮廓数量for i in contours:print(i.shape) # 打印每个轮廓的形状(包含的点数)# 5. 绘制轮廓# 在原图上绘制所有找到的轮廓image_np = cv2.drawContours(image_np, # 要在其上绘制的图像contours, # 轮廓数据列表contourIdx=-1, # 要绘制的轮廓索引,-1表示绘制所有轮廓color=(0, 0, 255), # 轮廓颜色(BGR格式,这里是红色)thickness=2 # 轮廓线宽度)# 6. 外接轮廓(最小外接圆)contour_image = image_np.copy() # 创建原图的副本for i in contours:# 计算最小外接圆# 返回值:圆心坐标和半径(x, y), radius = cv2.minEnclosingCircle(i)print((x, y), radius) # 打印圆心坐标和半径# 转换为整数(像素坐标必须是整数)x, y, radius = int(x), int(y), int(radius)# 画圆cv2.circle(contour_image, # 要绘制圆的图像(x, y), # 圆心坐标radius, # 圆的半径(185, 100, 0), # 圆的颜色(BGR格式)2 # 圆的线宽)# 7. 图片输出cv2.imshow('image_np', image_np) # 显示带有轮廓的图像cv2.imshow('contour_image', contour_image) # 显示带有外接圆的图像cv2.waitKey(0) # 等待键盘输入,0表示无限等待cv2.imwrite('R.png', contour_image)"""
cv2.imread(path)功能:读取图像文件参数:图像文件路径 返回:NumPy数组表示的图像cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)功能:转换图像颜色空间 参数:源图像和颜色转换代码 返回:转换后的图像cv2.threshold(src, thresh, maxval, type)功能:对图像进行阈值处理参数:src:源图像(灰度图)thresh:阈值maxval:最大值(当像素值超过阈值时赋予的值)type:阈值类型返回:阈值和阈值化后的图像cv2.findContours(image, mode, method)功能:查找图像中的轮廓 参数:image:二值输入图像 mode:轮廓检索模式 method:轮廓近似方法 返回:轮廓和层次结构cv2.drawContours(image, contours, contourIdx, color, thickness)功能:绘制轮廓 参数: image:目标图像 contours:所有输入轮廓 contourIdx:要绘制的轮廓索引 color:轮廓颜色 thickness:轮廓线粗细 cv2.minEnclosingCircle(points) 功能:计算最小外接圆 参数:点的集合(轮廓) 返回:圆心坐标和半径cv2.circle(image, center, radius, color, thickness)功能:绘制圆 参数: image:目标图像 center:圆心坐标 radius:半径 color:圆颜色 thickness:线宽cv2.imshow(winname, mat)功能:显示图像 参数: winname:窗口名称 mat:要显示的图像cv2.waitKey(delay) 功能:等待键盘输入 参数:延迟时间(毫秒),0表示无限等待代码逻辑总结
这段代码完成了以下图像处理流程:读取输入图像 将彩色图像转换为灰度图像 对灰度图像进行二值化处理(使用OTSU算法自动确定最佳阈值) 在二值图像中查找轮廓 在原图上绘制找到的轮廓 为每个轮廓计算并绘制最小外接圆 显示处理结果 这种处理流程常用于对象检测、图像分割和形状分析等计算机视觉任务。
"""
运行结果:R.png
练习:
1.绘制星星的最小外接矩形,要求矩形的颜色不同,越美观越好。
图片:11.jpg
"""绘制星星的最小外接矩形,要求矩形的颜色不同,越美观越好。"""
import cv2
import numpy as npcv2.namedWindow('image_np_thresh', cv2.WINDOW_NORMAL)
cv2.resizeWindow('image_np_thresh', 700, 400)if __name__ == '__main__':# 1.图片输入image_np = cv2.imread('11.jpg')# 2.灰度化image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)# 3.二值化ret, image_np_thresh = cv2.threshold(image_np_gray,1,255,cv2.THRESH_BINARY + cv2.THRESH_OTSU)print(ret) # ret:实际使用的阈值(OTSU算法计算出的最佳阈值)cv2.imshow('image_np_thresh', image_np_thresh)cv2.imwrite('image_np_thresh.png', image_np_thresh) # 二值化图像# 4.寻找轮廓contours, hierarchy = cv2.findContours(image_np_thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE,)print(len(contours))for i in contours:print(i.shape) # 循环打印每个轮廓的形状信息# 5.绘制轮廓(挨着图像的)image_np = cv2.drawContours(image_np, # 要绘制轮廓的目标图像,即在哪个图上绘制contours, # 轮廓数据列表contourIdx=-1, # 绘制轮廓的id,-1表示全绘制color=(0, 0, 0), # 绘制的颜色:黑色thickness=2 # 线宽)# 6.绘制外接(轮廓)矩形(使用不同颜色)contour_image = image_np.copy() # 创建原图的副本,避免修改原图# 定义不同颜色colors = [(0, 0, 255), # 红色(0, 255, 0), # 绿色(0, 255, 255), # 黄色(255, 0, 255), # 粉色(255, 255, 0), # 青色(255, 255, 255), # 白色(0, 0, 0) # 黑色]# 为每个轮廓绘制最小外接矩形for i, cnt in enumerate(contours): # 使用enumerate()函数同时获取轮廓索引和轮廓数据rect = cv2.minAreaRect(cnt) # 计算轮廓的最小外接矩形(可能是旋转的)print(rect) # 包含矩形对角线角点的坐标和矩形的角度# 把矩形数据转换为四个角点坐标points = cv2.boxPoints(rect)print(points)# 浮点型转换为整型box = np.int32(points) # OpenCV绘制需要整数坐标print(box)# 选择颜色(循环使用颜色列表)color_idx = i % len(colors)color = colors[color_idx]# 绘制轮廓(外接的)cv2.drawContours(contour_image,[box],0, # 绘制轮廓的id,-1表示全绘制,从0开始代表下标color, # 使用选择的颜色,2)cv2.namedWindow('image_np', cv2.WINDOW_NORMAL)cv2.resizeWindow('image_np', 700, 400)cv2.namedWindow('contour_image', cv2.WINDOW_NORMAL)cv2.resizeWindow('contour_image', 700, 400)# 7.图片输出cv2.imshow('image_np', image_np) # 显示带原始轮廓的图像cv2.imshow('contour_image', contour_image) # 显示带外接矩形的图像cv2.waitKey(0)cv2.imwrite('Black.png', contour_image)"""
enumerate(iterable, start=0)
iterable:任何可迭代对象(列表、元组、字符串等)
start:索引的起始值,默认为 0
"""
代码生成的二值化图像为:image_np_thresh.png
生成轮廓图片:image_np.png(黑色轮廓)
最终生成的最小外接矩形:Black.png
2.绘制六芒星的内外轮廓(图像为透明背景)
图片:6.jpg
"""绘制六芒星的内外轮廓"""
import cv2 # 导入OpenCV库,用于计算机视觉和图像处理
import numpy as npcv2.namedWindow('image_np', cv2.WINDOW_NORMAL)
cv2.resizeWindow('image_np', 500, 500)if __name__ == '__main__':# 1.图片输入image_np = cv2.imread("6.jpg")# # 2. 灰度化# image_np = cv2.cvtColor(# image_np,# cv2.COLOR_BGR2GRAY# )# cv2.imshow('2', image_np)## # 3.二值化# ret, image_np_thresh = cv2.threshold(# image_np,# 1,# 255,# cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU# )# cv2.imshow('1', image_np)# 使用灰度化、二值化后,图片透明底色无法选中# 2.HSV空间转换hsv_image_np = cv2.cvtColor(image_np, cv2.COLOR_BGR2HSV) # BGR转HSV# 3. 制作掩膜# 定义黄色范围yellow_low = np.array([26, 43, 46]) # 红色下限(Hmin, Smin, Vmin)yellow_high = np.array([34, 255, 255]) # 红色上限(Hmax, Smax, Vmax)mask1 = cv2.inRange( # 创建掩膜hsv_image_np, # 基于哪个图像, 输入HSV图像yellow_low, # 颜色下限yellow_high # 颜色上限)# 定义橙色范围orange_low = np.array([11, 43, 46]) # 红色下限(Hmin, Smin, Vmin)orange_high = np.array([25, 255, 255]) # 红色上限(Hmax, Smax, Vmax)mask2 = cv2.inRange( # 创建掩膜hsv_image_np, # 基于哪个图像, 输入HSV图像orange_low, # 颜色下限orange_high # 颜色上限)# 合并掩膜mask_image_np = cv2.bitwise_or(mask1, mask2) # 黄和橙cv2.imshow('mask_image_np', mask_image_np)cv2.imwrite('mask_image_np.png', mask_image_np) # 掩膜图像# 4. 寻找轮廓contours, hierarchy = cv2.findContours(mask_image_np,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)print(len(contours)) # 打印找到的轮廓数量for i in contours: # 遍历所有轮廓print(i.shape) # 打印每个轮廓的形状(点数,1,坐标维度)# 定义不同颜色用于绘制不同轮廓colors = [(255, 255, 255), # 白色(255, 0, 255), # 粉色]# 遍历所有找到的轮廓for i, cnt in enumerate(contours):# enumerate()函数返回的是(index, value)元组对,所以不能直接将循环变量用于索引# 计算当前轮廓的面积,过滤掉太小的轮廓(可能是噪声)area = cv2.contourArea(cnt)if area < 6500: # 忽略面积小于100像素的轮廓continue# 选择颜色(循环使用预定义的颜色)# color = colors[i] # 当i≥4时会报IndexErrorcolor = colors[i % len(colors)] # 0%4=0...5%4=1(循环)# 5. 绘制轮廓image_np = cv2.drawContours(image_np, # 在哪个图上绘制contours, # 轮廓数据列表contourIdx=i, # 绘制轮廓的id,-1表示全绘制color=color, # 使用选择的颜色thickness=5 # 线宽)# 6. 图片输出cv2.imshow('image_np', image_np)cv2.waitKey(0)cv2.imwrite('image_np.png', image_np)
代码使用灰度化二值化不适用此图片,使用掩膜,改变背景。代码中 <6500: 忽略面积小于6500像素的轮廓,即小三角,若要想加上小三角轮廓,改小像素数值即可。
掩膜图像:mask_image_np.png
最终结果:image_np.png