【youcans 的 OpenCV 例程200篇】200.轮廓的基本属性

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),是指对象垂直边界矩形的宽度与高度的比值,是对象/轮廓的重要特征。

通过对象/轮廓的垂直矩形边界框,可以计算宽高比:

A s p e c t R a t i o = W i d t h ( B o u n d i n g R e c t ) H e i g h t ( B o u n d i n g R e c t ) AspectRatio = \frac{Width_{(BoundingRect)}}{Height_{(BoundingRect)}} AspectRatio=Height(BoundingRect)Width(BoundingRect)

例程:

    # 轮廓的宽高比 (Aspect Ratio)
    x, y, wv, hv = cv2.boundingRect(cnt)  # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 h
    aspect_ratio = round(wv/hv, 2)  # 轮廓外接垂直矩形的宽高比

注意:

  1. 在 OpenCV 中使用垂直边界矩形计算宽高比,而不是用最小边界矩形计算。
  2. 有的资料中用“长宽比”描述,但从 OpenCV 的定义应为“宽高比”。

2.4.2 轮廓的面积比(Extent)

对象的面积(Extent),是指对象面积与垂直边界矩形面积的比值。

通过轮廓面积和垂直边界矩形面积,可以计算面积比:
E x t e n t = A r e a ( O b j e c t ) A r e a ( B o u n d i n g R e c t ) Extent = \frac{Area_{(Object)}}{Area_{(BoundingRect)}} Extent=Area(BoundingRect)Area(Object)
例程:

    # 轮廓的面积比 (Extent)
    x, y, wv, hv = cv2.boundingRect(cnt)  # 轮廓外接垂直矩形的左上顶点 (x,y), 宽度 w, 高度 h
    rect_area = wv * hv  # 轮廓外接垂直矩形的面积
    cnt_area = cv2.contourArea(cnt)  # 轮廓的面积
    extent = round(cnt_area/rect_area, 2)  # 轮廓的面积比

注意:

  1. 在 OpenCV 中的使用外接垂直边界矩形计算面积比,而不是用最小矩形边界计算。
  2. 有的资料中将 Extent 译为“占空比”、“范围”,皆供参考。

2.4.3 轮廓的坚实度(Solidity)

对象的坚实度(Solidity),是指对象面积与其凸包面积的比值。

通过轮廓面积和凸包面积,可以计算坚实度:
S o l i d i t y = A r e a ( O b j e c t ) A r e a ( C o n v e x H u l l ) 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), 是指与轮廓面积相等的圆形的直径 。

通过轮廓面积,可以计算轮廓的等效直径:
D e q u = 4 ∗ A r e a ( O b j e c t ) / π D_{equ} = \sqrt{4 * Area_{(Object)} / {\pi}} Dequ=4Area(Object)/π

例程:

    # 轮廓的等效直径 (Equivalent diameter)
    cnt_area = cv2.contourArea(cnt)  # 轮廓的面积
    dEqu = round(np.sqrt(4*cnt_area/np.pi), 2)  # 轮廓的等效直径

注意:

  1. 轮廓的等效直径,也称为当量直径,注意不是轮廓外接圆/内接圆的直径。

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), 旋转角度 ang
    angle = round(ellipse[2], 1)  # 轮廓的方向, 指椭圆长轴 a 与水平方向的夹角

注意:

  1. 旋转角度 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)

注意:

  1. 通过函数 np.nonzero() 获得的像素点集形状为 (len,2),像素点的坐标格式为行-列:(y, x)。
  2. 通过函数 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)

注意:

  1. 最大值、最小值是针对灰度图像的像素值而言的,对于多通道的彩色图像是无所谓最大值最小值的。
  2. 最大值、最小值像素点坐标的格式为列-行:(x, y)。
  3. 如果存在多个最大值/最小值的像素,只返回其中一个最大值/最小值像素点的位置。

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. 函数允许 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)  # OpenCV3
    contours, 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, 高度 h
    print("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, 高度 h
    aspect_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, 高度 h
    rect_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), 旋转角度 ang
    angle = 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 的 OpenCV 例程200篇】200.轮廓的基本属性_第1张图片



(本节完)


版权声明:
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.轮廓的基本属性

你可能感兴趣的:(opencv,python,图像处理,计算机视觉,图像分割)