这是对于 OpenCV 官方文档中 图像处理 的学习笔记。学习笔记中会记录官方给出的例子,也会给出自己根据官方的例子完成的更改代码,同样彩蛋的实现也会结合多个知识点一起实现一些小功能,来帮助我们对学会的知识点进行结合应用。
如果有喜欢我笔记的请麻烦帮我关注、点赞、评论。谢谢诸位。
学习笔记:
学习笔记目录里面会收录我关于OpenCV系列学习笔记博文,大家如果有什么不懂的可以通过阅读我的学习笔记进行学习。
【OpenCV学习笔记】- 学习笔记目录
内容
- 找到轮廓的不同特征,如面积、周长、质心、边界框等
- 您将看到许多与轮廓相关的功能。
1.矩
图像矩可帮助您计算一些特征,例如物体的质心、物体的面积等。查看关于 图像矩的维基百科页面。
函数 cv2.moments() 给出所有计算出的矩值的字典。
示例代码:
# OpenCV中的轮廓
# 2. 轮廓特征
# 矩
import cv2
img = cv2.imread('../image/3.9.1.png', 0)
ret, thresh = cv2.threshold(img, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv2.moments(cnt)
print(M)
控制台/命令行打印:
{'m00': 30420.0, 'm10': 10373220.0, 'm01': 5992740.0, 'm20': 3676074480.0, 'm11': 2043524340.0, 'm02': 1223411280.0, 'm30': 1348207403400.0, 'm21': 724186672560.0, 'm12': 417183246480.0, 'm03': 257891573160.0, 'mu20': 138806460.0, 'mu11': 0.0, 'mu02': 42841500.0, 'mu30': 0.0, 'mu21': 0.0, 'mu12': 0.0, 'mu03': 0.0, 'nu20': 0.15, 'nu11': 0.0, 'nu02': 0.046296296296296294, 'nu30': 0.0, 'nu21': 0.0, 'nu12': 0.0, 'nu03': 0.0}
在图像矩中,您可以提取有用的数据,例如面积,质心等。质心由关系C给出和
。可以按照以下步骤进行:
示例代码:
# OpenCV中的轮廓
# 2. 轮廓特征
# 矩
import cv2
img = cv2.imread('../image/3.9.1.png', 0)
ret, thresh = cv2.threshold(img, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv2.moments(cnt)
# print(M)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print(cx)
print(cy)
控制台/命令行打印:
341
197
2.轮廓面积
轮廓区域由函数 cv2.contourArea() 或从力矩M[‘m00’]中给出。
示例代码:
area = cv2.contourArea(cnt)
3.轮廓周长
也称为弧长。可以使用 cv2.arcLength() 函数找到它。第二个参数指定形状是闭合轮廓(如果通过True)还是曲线。
示例代码:
perimeter = cv2.arcLength(cnt,True)
4.轮廓近似
根据我们指定的精度,它可以将轮廓形状近似为顶点数量较少的其他形状。它是 Douglas-Peucker 算法的实现。检查维基百科页面上的算法和演示。
为了理解这一点,假设您试图在图像中找到一个正方形,但是由于图像中的某些问题,您没有得到一个完美的正方形,而是一个“坏形状”(如下图所示)。现在,您可以使用此功能来近似形状。在这种情况下,第二个参数称为epsilon,它是从轮廓到近似轮廓的最大距离。它是一个精度参数。需要正确选择 epsilon 才能获得正确的输出。
示例代码:
# OpenCV中的轮廓
# 2. 轮廓特征
# 4.轮廓近似
import cv2
img = cv2.imread('../image/3.9.2.png')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("img", img)
cv2.waitKey(0)
# img = cv2.imread('../image/3.9.2.png', 0)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
img2 = img.copy()
epsilon = 0.1 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
cv2.drawContours(img, approx, -1, (0, 255, 0), 3)
cv2.imshow("approx", img)
cv2.waitKey(0)
epsilon2 = 0.01 * cv2.arcLength(cnt, True)
approx2 = cv2.approxPolyDP(cnt, epsilon2, True)
cv2.drawContours(img2, approx2, -1, (0, 255, 0), 3)
cv2.imshow("approx2", img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
下面,在第二张图片中,绿线显示了 精度 epsilon = 10% 时的近似曲线。第三幅图显示了精度 epsilon = 1% 时的情况。第三个参数指定曲线是否闭合。
效果图:
5.凸包
凸包外观看起来与轮廓逼近相似,但并非如此(在某些情况下两者可能提供相同的结果)。在这里,cv2.convexHull() 函数检查曲线是否存在凸凹缺陷并对其进行校正。一般而言,凸曲线是始终凸出或至少平坦的曲线。如果在内部凸出,则称为凸度缺陷。例如,检查下面的手的图像。红线显示手的凸包。双向箭头标记显示凸度缺陷,这是船体与轮廓线之间的局部最大偏差。
关于它的语法,有一些事情需要讨论:
hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]])
参数详细信息:
- points: 就是我们传入的轮廓。
- hull: 是输出,通常我们避免它。
- clockwise:方向标记。如果为True,则输出凸包为顺时针方向。否则,其方向为逆时针方向。
- returnPoints:默认情况下为True。然后返回船体点的坐标。如果为False,则返回与船体点相对应的轮廓点的索引。
因此,要获得如上图所示的凸包,以下内容就足够了:
hull = cv2.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.检查凸度
cv2.isContourConvex() 是一个函数用来检查曲线是否为凸多边形。它只是返回True还是False。
k = cv2.isContourConvex(cnt)
7.边界矩形
有两种类型的边界矩形。
7.a. 直角矩形
它是一个直角矩形,不考虑对象的旋转。因此,边界矩形的面积将不会最小。它可以通过函数 cv2.boundingRect() 找到。
令(x,y)为矩形的左上角坐标,而(w,h)为矩形的宽度和高度。
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
7.b.旋转矩形
在这里,边界矩形是用最小面积绘制的,因此它也考虑了旋转。使用的函数是 cv2.minAreaRect() 。它返回一个Box2D结构,其中包含以下细节-(中心(x,y),(宽度,高度),旋转角度)。但是要绘制此矩形,我们需要矩形的4个角。它是通过函数 cv2.boxPoints() 获得的.
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img,[box],0,(0,0,255),2)
两个矩形都显示在单个图像中。绿色矩形显示法线边界矩形。红色矩形是旋转的矩形。
8.最小外圆
接下来,我们使用函数 cv2.minEnclosingCircle() 找到对象的外接圆。它是一个以最小面积完全覆盖对象的圆圈。
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv2.circle(img,center,radius,(0,255,0),2)
9.拟合椭圆
下一步是使椭圆适合对象。它返回椭圆所在的旋转矩形。
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img,ellipse,(0,255,0),2)
10.拟合线
同样,我们可以将一条直线拟合到一组点。下图包含一组白点。我们可以近似一条直线。
rows,cols = img.shape[:2]
[vx,vy,x,y] = cv2.fitLine(cnt, cv.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),(0,255,0),2)