翻译及二次校对:cvtutorials.com
在这篇文章中,我们将学习
- • 找到轮廓的不同特征,如面积、周长、中心点、边界盒等。
- • 你会看到很多与轮廓线有关的函数。
1. 矩
图像矩帮助你计算一些特征,如物体的质心、物体的面积等。
函数cv.ments()给出了一个所有计算出的矩的字典。见下文:
代码语言:javascript复制import numpy as np
import cv2 as cv
img = cv.imread('star.jpg',0)
ret,thresh = cv.threshold(img,127,255,0)
contours,hierarchy = cv.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv.moments(cnt)
print(M)
从这个矩,你可以提取有用的数据,如面积、中心点等。中心点是由Cx=M10/M00和Cy=M01/M00的关系给出的。这可以按以下方式进行。
代码语言:javascript复制cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
2. 轮廓线面积
轮廓线面积由函数cv.contourArea()或从矩M['m00']给出。
代码语言:javascript复制area = cv.contourArea(cnt)
3. 轮廓线周长
它也被称为弧长。它可以用cv.arcLength()函数计算出来。第二个参数指定形状是一个封闭的轮廓(如果传递的是True),还是只是一条曲线。
代码语言:javascript复制perimeter = cv.arcLength(cnt,True)
4. 轮廓逼近
它根据我们指定的精度,将一个轮廓形状逼近到另一个顶点数量较少的形状。它是Douglas-Peucker算法的一个实现。
为了理解这一点,假设你试图在图像中找到一个正方形,但由于图像中的一些问题,你没有得到一个完美的正方形,而是一个 "坏形状"(如下图所示)。现在,你可以用这个函数来近似地处理这个形状。在这个函数中,第二个参数叫做epsilon,它是轮廓到近似轮廓的最大距离。它是一个精度参数。为了得到正确的输出,需要明智地选择epsilon。
代码语言:javascript复制epsilon = 0.1*cv.arcLength(cnt,True)
approx = cv.approxPolyDP(cnt,epsilon,True)
下面,在第二张图片中,绿线显示了epsilon为弧长的10%时的近似曲线。第三张图显示的是epsilon为弧长的1%时的情况。第三个参数指定曲线是否是封闭的。
5. 凸面体
凸面体看起来与轮廓逼近相似,但它不是(两者在某些情况下可能提供相同的结果)。在这里,cv.convexHull()函数检查曲线是否有凸性缺陷并进行修正。一般来说,凸形曲线是指总是凸出来的曲线,或者至少是平的。而如果是向内隆起,则被称为凸性缺陷。例如,请看下面的手的图片。红线表示手的凸体。双面的箭头标志显示了凸性缺陷,这是局部最大凸包与轮廓的偏差。
关于它的语法,有一点需要讨论。
代码语言:javascript复制hull = cv.convexHull(point[, hull[, clockwise[, returnPoints])
参数细节:
- • points是我们传入的轮廓线。
- • hull是输出,通常我们避免使用它。
- • clockwise:方向标志。如果它是True,输出的凸面体是顺时针方向的。否则,它的方向是逆时针的。
- • returnPoints : 默认为 "真"。然后,它返回凸包点的坐标。如果是False,它返回与凸包点对应的轮廓点的索引。
因此,要得到上图中的凸包,只需按以下方法即可:
代码语言:javascript复制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]],这与第一个结果相同(其他的也是如此)。
当我们讨论凸性缺陷时,你会再次看到它。
6. 检查凸性
有一个函数可以检查一条曲线是否是凸的,即cv.isContourConvex()。它只是返回True或False。没什么大不了的。
代码语言:javascript复制k = cv.isContourConvex(cnt)
7. 边界矩形
有两种类型的边界矩形。
7.a. 直线边界矩形
这是一个直线矩形,它不考虑物体的旋转。因此,边界矩形的面积不会是最小的。它是由函数cv.boundingRect()找到的。
(x,y)为矩形的左上角坐标,(w,h)为其宽度和高度。
代码语言:javascript复制x,y,w,h = cv.boundingRect(cnt)
cv.rectangle(img,(x,y),(x w,y h),(0,255,0),2)
7.b. 旋转的矩形
这里,边界矩形是以最小面积绘制的,所以它也考虑了旋转。使用的函数是cv.minAreaRect()。它返回一个包含以下细节的Box2D结构--(中心(x,y),(宽度,高度),旋转的角度)。但是要画这个矩形,我们需要矩形的4个角。它可以通过函数cv.boxPoints()获得
代码语言:javascript复制rect = cv.minAreaRect(cnt)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.drawContours(img,[box],0,(0,0,255),2)
两个矩形都显示在一张图片上。绿色矩形显示的是正常的边界矩形。红色矩形是旋转后的矩形。
8. 最小包围圈
接下来,我们使用cv.minEnclosingCircle()函数找到一个物体的圆。它是一个以最小面积完全覆盖物体的圆。
代码语言:javascript复制(x,y),radius = cv.minEnclosingCircle(cnt)
center = (int(x),int(y))
半径 = int(radius)
cv.circle(img,center,radius,(0,255,0),2)
9. 拟合椭圆
下一个是将一个椭圆拟合到一个物体上。它返回旋转后的矩形以及内接的椭圆。
代码语言:javascript复制ellipse = cv.fitEllipse(cnt)
cv.ellipse(img,ellipse,(0,255,0),2)
10. 拟合直线
同样地,我们可以将一条线拟合到一组点上。下面的图片包含一组白色的点。我们可以对它进行近似的直线拟合。
代码语言:javascript复制rows,cols = img.shape[:2] 。
[vx,vy,x,y] = cv.fitLine(cnt, cv.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) y)
righty = int(((cols-x)*vy/vx) y)
cv.line(img,(cols-1,righty),(0,lefty),(0,255,0) ,2)