likes
comments
collection
share

OpenCV 17: 轮廓特征

作者站长头像
站长
· 阅读数 20

目标

在本文中,将学习

  • 如何找到轮廓的不同特征,例如面积,周长,质心,边界框等。
  • 将看到大量与轮廓有关的函数

图像矩

图像矩是指图像的某些特定像素灰度的加权平均值(矩),或者是图像具有类似功能或意义的属性, 可以帮助计算一些特征,例如物体的质心,物体的面积等。函数cv2.moments()提供了所有计算出的矩值。见下文:

import cv2
import numpy as np

img = cv2.imread('start.png', 0)
ret, thresh = cv2.threshold(img, 127, 255, 0)
contours, hierarchy = cv2.findContours(thres, 1, 2)

cnt = contours[0]
M = cv2.moments(cnt)
print(M)

{'m00': 13724.5, 'm10': 1797933.3333333333, 'm01': 1873571.8333333333, 'm20': 246067666.25, 'm11': 245442859.54166666, 'm02': 279142772.0833333, 'm30': 34995570121.700005, 'm21': 33592122871.399998, 'm12': 36568730961.566666, 'm03': 44488834889.950005, 'mu20': 10535277.37527883, 'mu11': 1695.8166170418262, 'mu02': 23375974.410556376, 'mu30': -729.2795791625977, 'mu21': 258212.5407886505, 'mu12': 80070.37276697159, 'mu03': -11313.452514648438, 'nu20': 0.055931042112529486, 'nu11': 9.002970424438718e-06, 'nu02': 0.12410139406924131, 'nu30': -3.304857392468866e-08, 'nu21': 1.170135087607162e-05, 'nu12': 3.628528357540433e-06, 'nu03': -5.126887992711116e-07}

此时,可以提取有用的数据,例如面积,质心等。 质心由关系给出 Cx=M10M00C_x = \frac{M_{10}}{M_{00}}Cx=M00M10 Cy=M01M00C_y = \frac{M_{01}}{M_{00}}Cy=M00M01

可以按照以下步骤进行:

cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])

轮廓面积

轮廓区域由函数cv2.contourArea()或从矩M['m00']中给出。

area = cv2.contourArea(cnt)
area = int(M['m00'])

轮廓周长

周长也被称为弧长。可以使用cv2.arcLength()函数找到它。第二个参数指定形状是闭合轮廓(True)还是曲线。

perimeter = cv2.arcLength(cnt, True)

轮廓近似

根据指定的精度,可以将轮廓形状近似为顶点数量较少的其他形状。它是Douglas-Peucker算法(是一种将线段组成的曲线降采样为点数较少的类似曲线的算法)的实现。

为了理解这一点,假设试图在图像中找到一个正方形,但是由于图像中的某些问题,没有得到一个完美的正方形,而是一个“坏形状”(如下图所示)。现在,可以使用此函数来近似形状。 在这种情况下,第二个参数称为epsilon,它是从轮廓到近似轮廓的最大距离, 是一个精度参数。需要正确选择epsilon才能获得正确的输出。

epsilon = 0.1*cv.arcLength(cnt,True) 
approx = cv.approxPolyDP(cnt,epsilon,True)

下面,在第二张图片中,绿线显示了ε=弧长*0.1时的近似曲线。第三幅图显示了ε=弧长*0.01时的情况。第三个参数指定曲线是否闭合。OpenCV 17: 轮廓特征

轮廓凸包

凸包外观看起来与轮廓逼近相似,但不相似(在某些情况下两者可能提供相同的结果)。cv2.convexHull()函数检查曲线是否存在凸凹缺陷并对其进行校正。一般而言,凸曲线是始终凸出或至少平坦的曲线。如果在内部凸出,则称为凸度缺陷。例如,检查下面的手的图像。红线显示手的凸包。双向箭头标记显示凸度缺陷,这是凸包与轮廓线之间的局部最大偏差。 OpenCV 17: 轮廓特征

函数参数:

hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]] 参数详细信息: *points:传递到的轮廓。

  • hull: 输出
  • clockwise:方向标记。如果为True,则输出凸包为顺时针方向。否则,其方向为逆时针方向。
  • returnPoints:默认情况下为True。然后返回凸包的坐标。如果为False,则返回与凸包点相对应的轮廓点的索引

简化版: hull = cv.convexHull(cnt)

但是,如果要查找凸度缺陷,则需要传递returnPoints = False。为了理解它,以上面的矩形图像为例。

  • 首先,获取它的轮廓为cnt
  • 之后,获取returnPoints = True的凸包,得到以下值:[[[234 202]],[[51 202]],[[51 79]],[[234 79]]],它们是四个角矩形的点。
  • 但是,returnPoints = False执行相同的操作,则会得到以下结果:[[129],[67],[0],[142]]这些是轮廓中相应点的索引。例如,检查第一个值:cnt [129] = [[234,202]]与第一个结果相同(对于其他结果依此类推)。

检查凸度

cv2.isContourConvex()具有检查曲线是否凸出的函数。它只是返回True还是False

k = cv.isContourConvex(cnt)

边界矩形

有两种类型的边界矩形。 OpenCV 17: 轮廓特征

1.直角矩形

它是一个矩形,不考虑物体的旋转。所以边界矩形的面积不是最小的。它是由函数cv2.boundingRect()找到的

cv2.boundingRect(img) img是一个二值图,也就是它的参数; 返回四个值,分别是x,y,w,h;x,y是矩阵左上点的坐标,w,h是矩阵的宽和高

import cv2
import numpy as np

img = cv2.imread('box.png', 0)
ret, thresh = cv2.threshold(img, 127, 255, 0)
cnt, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
x, y, w, h = cv2.boundingRect(cnts[0])
cv2.rectangle(img, (x, y), (x+w, y+h), (0,255,0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV 17: 轮廓特征

2.旋转矩形

边界矩形是用最小面积绘制的,所以它考虑了旋转。使用的函数是cv2.minAreaRect()。它返回一个Box2D结构,其中包含以下细节 (中心(x,y),(宽度,高度),旋转角度)。但要画出这个矩形,我们需要矩形的四个角。它由函数cv2.boxPoints()获得

img = cv2.imread('approx.png', 0)
ret, thresh = cv2.threshold(img, 127, 250, 0)
cnt, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
rect = cv2.minAreaRect(cnt[0])
box = cv2.boxPoints(rect)
box = np.int64(box)
cv2.drawContours(img, [box], 0, (255,255,0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

两个矩形都显示在一张单独的图像中。绿色矩形显示正常的边界矩形。红色矩形是旋转后的矩形。 OpenCV 17: 轮廓特征

最小闭合圈

接下来,使用函数cv2.minEnclosingCircle()查找对象的圆。它是一个以最小面积完全覆盖物体的圆

img = cv2.imread('approx.png', 0)
ret, thresh = cv2.threshold(img, 127, 250, 0)
cnt, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
(x, y), radius = cv2.minEnclosingCircle(cnt[0])
center = (int(x), int(y))
radius = int(radius)
cv2.circle(img, center, radius, (255,255,0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV 17: 轮廓特征

拟合一个椭圆

下一个是把一个椭圆拟合到一个物体上。它返回内接椭圆的旋转矩形。

img = cv2.imread('approx.png', 0)
ret, thresh = cv2.threshold(img, 127, 250, 0)
cnt, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
ellipse = cv2.fitEllipse(cnt[0])
cv2.ellipse(img, ellipse, (255,255,0), 3)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV 17: 轮廓特征

拟合直线

同样,可以将一条直线拟合到一组点。下图包含一组白点。我们可以近似一条直线。

import cv2
# 直线
import cv2
img = cv2.imread('approx.png', 0)
rows, cols = img.shape[:2]
ret, thresh = cv2.threshold(img, 127, 250, 0)
cnt, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
[vx,vy,x,y] = cv2.fitLine(cnt[0], cv2.DIST_L2, 0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv2.line(img,(cols-1,righty),(0,lefty),(255,255,0),2)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV 17: 轮廓特征

附加资源

转载自:https://juejin.cn/post/7202531472719314999
评论
请登录