OpenCV 例程200篇 总目录-202206更新
文章目录
- 【youcans 的 OpenCV 例程200篇】200.轮廓的基本属性
- 2.4 轮廓的基本属性
- 2.4.1 轮廓的宽高比(Aspect Ratio)
- 2.4.2 轮廓的面积比(Extent)
- 2.4.3 轮廓的坚实度(Solidity)
- 2.4.4 轮廓的等效直径(Equivalent diameter)
- 2.4.5 轮廓的方向(Orientation)
- 2.4.6 轮廓的掩模(Mask)
- 2.4.7 最大值/最小值及其位置
- 2.4.8 灰度均值和颜色均值
- 2.4.9 极端点位置
- 例程 12.6:轮廓的基本属性
【youcans 的 OpenCV 例程200篇】200.轮廓的基本属性
在对实际图像进行轮廓查找时,得到的轮廓数量很多。获取轮廓后,通常基于轮廓的特征进行筛选、识别和处理。例如,基于轮廓的周长和面积对轮廓进行筛选,然后绘制筛选的目标轮廓或其最小外接矩形。
2.4 轮廓的基本属性
2.4.1 轮廓的宽高比(Aspect Ratio)
对象的宽高比(Aspect Ratio),是指对象垂直边界矩形的宽度与高度的比值,是对象/轮廓的重要特征。
通过对象/轮廓的垂直矩形边界框,可以计算宽高比:
AspectRatio=Width(BoundingRect)Height(BoundingRect)AspectRatio = \frac{Width_{(BoundingRect)}}{Height_{(BoundingRect)}} AspectRatio=Height(BoundingRect)Width(BoundingRect)
例程:
# 轮廓的宽高比 (Aspect Ratio)x, y, wv, hv = cv2.boundingRect(cnt) # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 haspect_ratio = round(wv/hv, 2) # 轮廓外接垂直矩形的宽高比
注意:
- 在 OpenCV 中使用垂直边界矩形计算宽高比,而不是用最小边界矩形计算。
- 有的资料中用“长宽比”描述,但从 OpenCV 的定义应为“宽高比”。
2.4.2 轮廓的面积比(Extent)
对象的面积(Extent),是指对象面积与垂直边界矩形面积的比值。
通过轮廓面积和垂直边界矩形面积,可以计算面积比:
Extent=Area(Object)Area(BoundingRect)Extent = \frac{Area_{(Object)}}{Area_{(BoundingRect)}} Extent=Area(BoundingRect)Area(Object)
例程:
# 轮廓的面积比 (Extent)x, y, wv, hv = cv2.boundingRect(cnt) # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 hrect_area = wv * hv # 轮廓外接垂直矩形的面积cnt_area = cv2.contourArea(cnt) # 轮廓的面积extent = round(cnt_area/rect_area, 2) # 轮廓的面积比
注意:
- 在 OpenCV 中的使用外接垂直边界矩形计算面积比,而不是用最小矩形边界计算。
- 有的资料中将 Extent 译为“占空比”、“范围”,皆供参考。
2.4.3 轮廓的坚实度(Solidity)
对象的坚实度(Solidity),是指对象面积与其凸包面积的比值。
通过轮廓面积和凸包面积,可以计算坚实度:
Solidity=Area(Object)Area(ConvexHull)Solidity = \frac{Area_{(Object)}}{Area_{(ConvexHull)}} Solidity=Area(ConvexHull)Area(Object)
例程:
# 轮廓的坚实度 (Solidity)cnt_area = cv2.contourArea(cnt) # 轮廓的面积hull = cv2.convexHull(cnt) # 轮廓的凸包,返回凸包顶点集hull_area = cv2.contourArea(hull) # 凸包的面积solidity = round(cnt_area/hull_area, 2) # 轮廓的坚实度
2.4.4 轮廓的等效直径(Equivalent diameter)
轮廓的等效直径(Equivalent diameter), 是指与轮廓面积相等的圆形的直径 。
通过轮廓面积,可以计算轮廓的等效直径:
Dequ=4∗Area(Object)/πD_{equ} = \sqrt{4 * Area_{(Object)} / {\pi}} Dequ=4∗Area(Object)/π
例程:
# 轮廓的等效直径 (Equivalent diameter)cnt_area = cv2.contourArea(cnt) # 轮廓的面积dEqu = round(np.sqrt(4*cnt_area/np.pi), 2) # 轮廓的等效直径
注意:
- 轮廓的等效直径,也称为当量直径,注意不是轮廓外接圆/内接圆的直径。
2.4.5 轮廓的方向(Orientation)
轮廓的方向(Orientation), 是指物体指向的角度。
通过函数 cv2.fitEllipse() 可以得到轮廓的最优拟合椭圆,并返回椭圆的中心点、轴长和旋转角度:
- retval = [(x,y), (a,b), ang],椭圆中心点坐标 (x,y),椭圆长轴、短轴长度 (a,b),旋转角度 angle
例程:
# 轮廓的方向 (Orientation)ellipse = cv2.fitEllipse(cnt) # 椭圆中心点 (x,y), 长轴短轴长度 (a,b), 旋转角度 angangle = round(ellipse[2], 1) # 轮廓的方向, 指椭圆长轴 a 与水平方向的夹角
注意:
- 旋转角度 angle 表示轮廓的方向,指椭圆长轴与水平方向的夹角,顺时针为正。
2.4.6 轮廓的掩模(Mask)
图像掩模(Mask)也常被写成“图像掩膜”,或称为掩码、掩像、模板、遮罩,有的资料中甚至译成“面具”。图像处理时,可以只对掩模区域进行处理,或者只对非掩模区域进行处理。
轮廓的掩模是轮廓区域的掩模图像,背景为黑色、轮廓区域为白色。
轮廓的掩模可以通过绘制轮廓 cv2.drawContours() 函数的内部填充功能(“CV_FILLED”)实现。
轮廓的像素点(Pixelpoints),是指轮廓区域内的所有像素点。
轮廓的像素点可以由轮廓掩模的非 0 筛选(切片)获得,如通过 Numpy 函数 np.nonzero() 或 OpenCV 函数 cv2.findNonZero() 实现。
例程:
# 轮廓的掩模和像素点 (Mask)maskCnt = np.zeros(gray.shape, np.uint8) # 背景区域置为黑色cv2.drawContours(maskCnt, [cnt], 0, 255, -1) # 轮廓区域置为白色pixelsNP = np.transpose(np.nonzero(maskCnt)) # (15859, 2)pixelsCV = cv2.findNonZero(maskCnt) # (15859, 1, 2)
注意:
- 通过函数 np.nonzero() 获得的像素点集形状为 (len,2),像素点的坐标格式为行-列:(y, x)。
- 通过函数 cv.findNonZero() 获得的像素点集形状为 (len,1,2),像素点的坐标格式为列-行:(x, y)。
2.4.7 最大值/最小值及其位置
轮廓中像素点的灰度值的最大值、最小值及其位置,可以由函数 **cv.minMaxLoc() ** 通过掩模图像获得。
函数说明:
cv.minMaxLoc(src[, mask]) → minVal, maxVal, minLoc, maxLoc
对于灰度图像或二维数组,查找最大值、最小值及其位置。
参数说明:
- src:单通道图像或二维数组
- mask:掩模图像,可选项,只对掩模区域查找最大值、最小值
- minVal: 最小值,浮点数
- maxVal: 最大值,浮点数
- minLoc: 最小值像素的位置,像素坐标 (x, y)
- maxLoc: 最大值像素的位置,像素坐标 (x, y)
例程:
# 轮廓的掩模maskCnt = np.zeros(gray.shape, np.uint8) # 背景区域置为黑色cv2.drawContours(maskCnt, [cnt], 0, 255, -1) # 轮廓区域置为白色# 轮廓的最大值/最小值及其位置min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(gray, mask=maskCnt) # 必须用灰度图像print(min_val, max_val, min_loc, max_loc) # 0.0 255.0 (468, 291) (379, 357)
注意:
- 最大值、最小值是针对灰度图像的像素值而言的,对于多通道的彩色图像是无所谓最大值最小值的。
- 最大值、最小值像素点坐标的格式为列-行:(x, y)。
- 如果存在多个最大值/最小值的像素,只返回其中一个最大值/最小值像素点的位置。
2.4.8 灰度均值和颜色均值
轮廓中像素点的颜色均值或灰度值均值,可以由函数 **cv.mean() ** 对掩模图像计算。
函数说明:
cv.mean(src[, mask]) → retval
对于单通道或多通道图像,计算整个图像或掩模区域的像素值的均值。
参数说明:
- src:单通道或多通道图像
- mask:掩模图像,可选项
- retval:4 个元素的元组,元素值为浮点数
例程:
# 轮廓的掩模maskCnt = np.zeros(gray.shape, np.uint8) # 背景区域置为黑色cv2.drawContours(maskCnt, [cnt], 0, 255, -1) # 轮廓区域置为白色# 轮廓的灰度均值和颜色均值gray_mean = cv2.mean(gray, maskCnt) # (mg, 0.0, 0.0, 0.0)img_mean = cv2.mean(img, maskCnt) # (mR, mG, mB, 0.0)
注意:
- 函数允许 1~4 通道的图像输入,返回值是一个元组,包含 4个元素,是图像对应通道的均值。如果该通道不存在,则输出的均值为 0.0。例如输入为灰度图像时输出为 (mg, 0.0, 0.0, 0.0)。
2.4.9 极端点位置
极点是指对象的最左侧、最右侧、最顶部、最底部的点。
查找轮廓的极端点,可以通过轮廓边界点集 contours[i],对所有边界点的横坐标、纵坐标分别查找最大值、最小值及其位置。
由轮廓上下左右的极端点位置,就可以得到轮廓的垂直矩形边界框的顶点坐标和宽度高度。
例程:
# 轮廓的极端点位置leftmost = tuple(cnt[cnt[:, :, 0].argmin()][0]) # cnt[:,:,0], 所有边界点的横坐标rightmost = tuple(cnt[cnt[:, :, 0].argmax()][0])topmost = tuple(cnt[cnt[:, :, 1].argmin()][0]) # cnt[:,:,1], 所有边界点的纵坐标bottommost = tuple(cnt[cnt[:, :, 1].argmax()][0])
例程 12.6:轮廓的基本属性
# 12.6 轮廓的基本属性img = cv2.imread("../images/seagull01.png", flags=1)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 灰度图像# HSV 色彩空间图像分割hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 将图片转换到 HSV 色彩空间lowerBlue, upperBlue = np.array([100, 43, 46]), np.array([124, 255, 255]) # 蓝色阈值segment = cv2.inRange(hsv, lowerBlue, upperBlue) # 背景色彩图像分割kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # (5, 5) 结构元binary = cv2.dilate(cv2.bitwise_not(segment), kernel=kernel, iterations=3) # 图像膨胀# 寻找二值化图中的轮廓# binary, contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # OpenCV3contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # OpenCV4~# 绘制全部轮廓,contourIdx=-1 绘制全部轮廓imgCnts = img.copy()imgCnts = cv2.drawContours(imgCnts, contours, -1, (255,255,255), -1) # 绘制全部轮廓, 内部填充print("len(contours) =", len(contours)) # 所有轮廓的列表cnt = contours[1]# 轮廓的垂直矩形边界框# boundingBoxes = [cv2.boundingRect(cnt) for cnt in contours] # 所有轮廓的外接垂直矩形xv, yv, wv, hv = cv2.boundingRect(cnt) # 矩形左上顶点的坐标 x, y, 矩形宽度 w, 高度 hprint("Vertical rectangle: (x,y)={}, (w,h)={}".format((xv, yv), (wv, hv)))cv2.rectangle(imgCnts, (xv,yv), (xv+wv,yv+hv), (0, 0, 255), 2) # 绘制垂直矩形边界框# 轮廓的宽高比 (Aspect Ratio)xv, yv, wv, hv = cv2.boundingRect(cnt) # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 haspect_ratio = round(wv/hv, 2) # 轮廓外接垂直矩形的宽高比print("Vertical rectangle: w={}, h={}".format(wv,hv))print("Aspect ratio:", aspect_ratio)# 轮廓的面积比 (Extent)xv, yv, wv, hv = cv2.boundingRect(cnt) # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 hrect_area = wv * hv # 轮廓外接垂直矩形的面积cnt_area = cv2.contourArea(cnt) # 轮廓的面积extent = round(cnt_area/rect_area, 2) # 轮廓的面积比print("Area of cnt:", cnt_area)print("Area of VertRect:", rect_area)print("Extent(area ratio):", extent)# 轮廓的坚实度 (Solidity)cnt_area = cv2.contourArea(cnt) # 轮廓的面积hull = cv2.convexHull(cnt) # 轮廓的凸包,返回凸包顶点集hull_area = cv2.contourArea(hull) # 凸包的面积solidity = round(cnt_area/hull_area, 2) # 轮廓的坚实度print("Area of cnt:", cnt_area)print("Area of convex hull:", hull_area)print("Solidity(area ratio):", solidity)# 轮廓的等效直径 (Equivalent diameter)cnt_area = cv2.contourArea(cnt) # 轮廓的面积dEqu = round(np.sqrt(4*cnt_area/np.pi), 2) # 轮廓的等效直径print("Area of cnt:", cnt_area)print("Equivalent diameter:", dEqu)# 轮廓的方向 (Orientation)ellipse = cv2.fitEllipse(cnt) # 椭圆中心点 (x,y), 长轴短轴长度 (a,b), 旋转角度 angangle = round(ellipse[2], 1) # 轮廓的方向, 指椭圆长轴与水平方向的夹角print("Orientation of cnt: {}".format(angle))# 轮廓的掩模和像素点 (Mask)maskCnt = np.zeros(gray.shape, np.uint8) # 背景区域置为黑色cv2.drawContours(maskCnt, [cnt], 0, 255, -1) # 轮廓区域置为白色pixelsNP = np.transpose(np.nonzero(maskCnt)) # (15859, 2): (y, x)pixelsCV = cv2.findNonZero(maskCnt) # (15859, 1, 2): (x, y)print("pixelsNP: {}, pixelsCV: {}".format(pixelsNP.shape, pixelsCV.shape))# 轮廓的最大值/最小值及其位置min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(gray, mask=maskCnt) # 必须用灰度图像print("Minimum value is {} at Pos{}".format(min_val, min_loc))print("Maximum value is {} at Pos{}".format(max_val, max_loc))# 轮廓的灰度均值和颜色均值gray_mean = cv2.mean(gray, maskCnt) # (mg, 0, 0, 0)img_mean = cv2.mean(img, maskCnt) # (mR, mG, mB, 0)print("gray_mean: {:.1f} \nimg_mean: ({:.1f}, {:.1f}, {:.1f})".format(gray_mean[0], img_mean[0], img_mean[1], img_mean[2]))# 轮廓的极端点位置leftmost = tuple(cnt[cnt[:, :, 0].argmin()][0]) # cnt[:,:,0], 所有边界点的横坐标rightmost = tuple(cnt[cnt[:, :, 0].argmax()][0])topmost = tuple(cnt[cnt[:, :, 1].argmin()][0]) # cnt[:,:,1], 所有边界点的纵坐标bottommost = tuple(cnt[cnt[:, :, 1].argmax()][0])print("Left most is {} at Pos{}".format(leftmost[0], leftmost))print("Right most is {} at Pos{}".format(rightmost[0], rightmost))print("Top most is {} at Pos{}".format(topmost[1], topmost))print("Bottom most is {} at Pos{}".format(bottommost[1], bottommost))for point in [leftmost, rightmost, topmost, bottommost]:cv2.circle(imgCnts, point, 5, (0, 0, 255), -1) # 在轮廓的极端点上绘制圆点plt.figure(figsize=(9, 6))plt.subplot(131), plt.axis('off'), plt.title("Origin")plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))plt.subplot(132), plt.axis('off'), plt.title("Tree contour")plt.imshow(cv2.cvtColor(imgCnts, cv2.COLOR_BGR2RGB))plt.subplot(133), plt.axis('off'), plt.title("Contour mask")plt.imshow(cv2.cvtColor(maskCnt, cv2.COLOR_BGR2RGB))plt.tight_layout()plt.show()
运行结果:
len(contours) = 6
Vertical rectangle: (x,y)=(168, 173), (w,h)=(132, 156)
Vertical rectangle: w=132, h=156
Aspect ratio: 0.85
Area of cnt: 9302.5
Area of VertRect: 20592
Extent(area ratio): 0.45
Area of cnt: 9302.5
Area of convex hull: 12546.5
Solidity(area ratio): 0.74
Area of cnt: 9302.5
Equivalent diameter: 108.83
Orientation of cnt: 155.4
pixelsNP: (9551, 2), pixelsCV: (9551, 1, 2)
Minimum value is 0.0 at Pos(204, 179)
Maximum value is 255.0 at Pos(233, 220)
gray_mean: 111.2
img_mean: (127.8, 113.6, 100.1)
Left most is 168 at Pos(168, 256)
Right most is 299 at Pos(299, 322)
Top most is 173 at Pos(203, 173)
Bottom most is 328 at Pos(288, 328)
(本节完)
版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/125112262)
Copyright 2022 youcans, XUPT
Crated:2022-5-28
欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中
欢迎关注 『youcans 的 OpenCV学习课』 系列,持续更新中
更多内容,请见:
【OpenCV 例程200篇 总目录-202206更新】
194.寻找图像轮廓(cv.findContours)
195.绘制图像轮廓(cv.drawContours)
196.图像的矩和不变矩(cv.moments)
197.轮廓的基本特征
198.基于不变矩的形状相似性检测
199.轮廓的外接边界框
200.轮廓的基本属性