import cv2
import numpy as np
# img = cv2.imread('messimg.png',0)
img = cv2.imread('./star.jpg', 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': 4.0, 'm10': 360.0, 'm01': 610.0, 'm20': 32401.0, 'm11': 54900.0, 'm02': 93026.66666666666, 'm30': 2916270.0, 'm21': 4941152.5, 'm12': 8372400.0, 'm03': 14187075.0, 'mu20': 1.0, 'mu11': 0.0, 'mu02': 1.6666666666569654, 'mu30': 0.0, 'mu21': 0.0, 'mu12': 8.731149137020111e-10, 'mu03': 3.725290298461914e-09, 'nu20': 0.0625, 'nu11': 0.0, 'nu02': 0.10416666666606034, 'nu30': 0.0, 'nu21': 0.0, 'nu12': 2.7284841053187847e-11, 'nu03': 1.1641532182693481e-10}
根据这些矩的值,可以计算出对象的重心:
和
#This can be done as follows:
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print(cx)
print(cy)
90 152
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt,True)
epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
下边,第二幅图中的绿线是当 epsilon = 10%
时得到的近似轮廓,
第三幅图是当 epsilon = 1%
时得到的近似轮廓。第三个参数设定弧线是否闭合。
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]]
,这与前面得到结果的第一个值是一样的。
在凸检验中还会遇到这些。
凸性检测
函数 cv2.isContourConvex()
可以可以用来检测一个曲线是不是凸的。
它只能返回 True
或 False
。没什么大不了的。
k = cv2.isContourConvex(cnt)
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
旋转的边界矩形 这个边界矩形是面积最小的,因为它考虑了对象的旋转。用到的函数为 cv2.minAreaRect()
。
返回的是一个 Box2D 结构,其中包含矩形左上角角点的坐标(x,y),矩形的宽和高(w,h),以及旋转角度。
但是要绘制这个矩形需要矩形的 4 个角点,可以通过函数 cv2.boxPoints()
获得。
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
# box = np.int0(box)
box = np.int64(box)
img = cv2.drawContours(img,[box],0,(0,0,255),2)
img
array([[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]], shape=(177, 177), dtype=uint8)
把这两中边界矩形显示在下图中,其中绿色的为直矩形,红的为旋转矩形。
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
ellipse = cv2.fitEllipse(cnt)
im = cv2.ellipse(im,ellipse,(0,255,0),2)
rows,cols = img.shape[:2 ]
#cv2.fitLine(points, distType, param, reps, aeps[, line ]) → line
#points – Input vector of 2D or 3D points, stored in std::vector<> or Mat.
#line – Output line parameters. In case of 2D fitting, it should be a vector of
#4 elements (likeVec4f) - (vx, vy, x0, y0), where (vx, vy) is a normalized
#vector collinear to the line and (x0, y0) is a point on the line. In case of
#3D fitting, it should be a vector of 6 elements (like Vec6f) - (vx, vy, vz,
#x0, y0, z0), where (vx, vy, vz) is a normalized vector collinear to the line
#and (x0, y0, z0) is a point on the line.
#distType – Distance used by the M-estimator
#distType=CV_DIST_L2
#ρ(r) = r2 /2 (the simplest and the fastest least-squares method)
#param – Numerical parameter ( C ) for some types of distances. If it is 0, an optimal value
#is chosen.
#reps – Sufficient accuracy for the radius (distance between the coordinate origin and the
#line).
#aeps – Sufficient accuracy for the angle. 0.01 would be a good default value for reps and
#aeps.
[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) +y)
righty = int(((cols-x)*vy/vx)+y)
img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)