比较两个轮廓最简单的方法是比较二者的轮廓矩,轮廓矩代表了一个轮廓,一副图像,一组点集的全局特征,矩信息包含了对应对象不同类型的集合特征,例如大小,位置,角度,形状等,矩特征广泛应用在模式识别,图像识别方面。
retval = cv2.moments( array[, binaryImage] )
获取图像的 Moments特征,使用轮廓矩可以方便的比较两个轮廓
阿巴阿巴,,上面的啥矩 都是根据公式计算得到的,大多数矩都是通过数学公式得到的抽象特征,但是很明显,如果两个轮廓的矩一致,那么这两个轮廓就是一致的。m00 的含义就很直观,表示一个轮廓的面积。
cv2.moments 返回的特征值可以用来比较两个轮廓是否相似,例如 不管两个轮廓出现再那个位置,可以通过函数cv2.moments() 的m00 矩来判断面积是否一致。
当轮廓位置改变,虽然面积,周长等特征不会变,但是更高阶的特征会随位置而改变,中心矩通过减去均值而获取平移不变性,从而可以比较不同位置的两个对象是否一致。
归一化中心矩通过除以物体总尺寸而获得缩放不变性,通过计算提取对象的归一化中心矩属性值,该属性值不但拥有平移不变性,还有缩放不变性,也就从缩放前后的图像中提取稳定的特征值来比较轮廓。
再opencv 中cv2.moments() 会同时计算上述三种,,i了i了,Opencv就是为了让你忽略一些细节嘛,,,
o = cv2.imread('16.jpg')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 转成二值图
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
n=len(contours)
contoursImg=[]
for i in range(n): # 分别展示轮廓
temp=np.zeros(o.shape,np.uint8)
contoursImg.append(temp)
contoursImg[i]=cv2.drawContours(contoursImg[i],contours,i,255,3)
cv2.imshow("contours[" + str(i)+"]",contoursImg[i])
print("观察各个轮廓的矩(moments):")
for i in range(n):
print("轮廓"+str(i)+"的矩:\n",cv2.moments(contours[i]))
print("观察各个轮廓的面积:")
for i in range(n):
print("轮廓"+str(i)+"的面积:%d" %cv2.moments(contours[i])['m00'])
cv2.waitKey()
cv2.destroyAllWindows()
观察各个轮廓的矩(moments):
轮廓0的矩:
{'m00': 13120.0, 'm10': 4473920.0, 'm01': 4696960.0, 'm20': 1553596053.3333333, 'm11': 1601663360.0, 'm02': 1688863253.3333333, 'm30': 548864979520.0, 'm21': 556187387093.3334, 'm12': 575902369386.6666, 'm03': 609876771200.0, 'mu20': 27989333.333333254, 'mu11': 0.0, 'mu02': 7351573.333333254, 'mu30': 6.103515625e-05, 'mu21': 6.866455078125e-05, 'mu12': -1.33514404296875e-05, 'mu03': 0.0001220703125, 'nu20': 0.16260162601625971, 'nu11': 0.0, 'nu02': 0.042708333333332876, 'nu30': 3.095604527046157e-15, 'nu21': 3.4825550929269264e-15, 'nu12': -6.771634902913468e-16, 'nu03': 6.191209054092314e-15}
轮廓1的矩:
{'m00': 18786.0, 'm10': 3146685.0, 'm01': 5044317.5, 'm20': 555159008.0, 'm11': 844939709.0, 'm02': 1382559550.1666665, 'm30': 102398186421.5, 'm21': 149071547775.08334, 'm12': 231585626560.58334, 'm03': 386320282897.75, 'mu20': 28084245.452091992, 'mu11': 8472.30844771862, 'mu02': 28086057.097026825, 'mu30': -145894.54983520508, 'mu21': 344820.28427505493, 'mu12': 144172.43611335754, 'mu03': -344821.7702636719, 'nu20': 0.07957820229870524, 'nu11': 2.400673632979375e-05, 'nu02': 0.07958333569092557, 'nu30': -3.0161523412948118e-06, 'nu21': 7.128645373777938e-06, 'nu12': 2.9805502071507094e-06, 'nu03': -7.128676094377516e-06}
轮廓2的矩:
{'m00': 14403.5, 'm10': 4514816.666666666, 'm01': 1937989.6666666665, 'm20': 1431976362.4166665, 'm11': 607478227.2083333, 'm02': 277542370.4166666, 'm30': 459385631581.7, 'm21': 192678982549.58334, 'm12': 86999188792.48334, 'm03': 41860027704.0, 'mu20': 16794668.13325596, 'mu11': 10039.136375546455, 'mu02': 16786030.075306743, 'mu30': 34851.058837890625, 'mu21': 394498.4239034653, 'mu12': 68360.05676651001, 'mu03': -391748.9297027588, 'nu20': 0.08095344827348691, 'nu11': 4.839051899328739e-05, 'nu02': 0.08091181121511656, 'nu30': 1.3997352022029674e-06, 'nu21': 1.584437746123557e-05, 'nu12': 2.7455687451494556e-06, 'nu03': -1.5733948568992055e-05}
观察各个轮廓的面积: # 就是厉害啊,,,
轮廓0的面积:13120
轮廓1的面积:18786
轮廓2的面积:14403
retval =cv2.contourArea(contour [, oriented] ))
o = cv2.imread('16.jpg')
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
n=len(contours)
contoursImg=[]
for i in range(n):
if cv2.contourArea(contours[i])>15000: # 增加一个筛选
print("contours["+str(i)+"]面积=",cv2.contourArea(contours[i]))
contours[1]面积= 18786.0
retval = cv2.arcLength( curve, closed )
计算轮廓的长度
Hu矩是归一化中心矩的线性组合,Hu矩再图像旋转,缩放,平移等操作后,仍能保持矩的不变性,经常使用 Hu 矩来识别图像的特征。在 OpenCV 中,使用函数 cv2.HuMoments()可以得到 Hu 距。该函数使用 cv2.moments()函数的返回值作为参数,返回 7 个 Hu 矩值。
hu = cv2.HuMoments( m )
hu 表示返回的Hu 矩阵,参数m 是cv2.moments() 计算得到的矩特征值。
Hu 矩是归一化中心矩的线性组合,每一个矩都是通过归一化中心矩的组合运算得到的,函数 cv2.moments()返回的归一化中心矩中包含:(为了表示方便,将 nu 表示为 v
# 针对ℎ0 = 20 + 02 来验证一下
o1 = cv2.imread('16.jpg')
gray = cv2.cvtColor(o1,cv2.COLOR_BGR2GRAY)
HuM1=cv2.HuMoments(cv2.moments(gray)).flatten() # flatten 是把数组降成一维的。相关操作可以去看看https://www.cnblogs.com/yvonnes/p/10020926.html
print("\nHuM1=\n",HuM1)
print("\ncv2.moments(gray)['nu20']+cv2.moments(gray)['nu02']=%f+%f=%f\n"
%(cv2.moments(gray)['nu20'],cv2.moments(gray)['nu02'],
cv2.moments(gray)['nu20']+cv2.moments(gray)['nu02']))
print("HuM1[0]=",HuM1[0])
print("\nHu[0]-(nu02+nu20)=",
HuM1[0]-(cv2.moments(gray)['nu20']+cv2.moments(gray)['nu02']))
HuM1=
[ 1.38134402e-03 6.55976051e-09 1.54351609e-09 5.29039691e-11
-1.50331297e-20 -3.70483310e-15 1.59756875e-21]
cv2.moments(gray)['nu20']+cv2.moments(gray)['nu02']=0.000652+0.000730=0.001381
HuM1[0]= 0.0013813440210695568
Hu[0]-(nu02+nu20)= 0.0 # 说明式子是合理的。。。
# 第二个 小栗子
o1 = cv2.imread('12.jpg') # 这里分别计算了三个图像的 Hu 矩
gray1 = cv2.cvtColor(o1,cv2.COLOR_BGR2GRAY)
HuM1=cv2.HuMoments(cv2.moments(gray1)).flatten()
o2 = cv2.imread('18-1.jpg')
gray2 = cv2.cvtColor(o2,cv2.COLOR_BGR2GRAY)
HuM2=cv2.HuMoments(cv2.moments(gray2)).flatten()
o3 = cv2.imread('18-2.jpg')
gray3 = cv2.cvtColor(o3,cv2.COLOR_BGR2GRAY)
HuM3=cv2.HuMoments(cv2.moments(gray3)).flatten()
print("\nHuM1-HuM2=",HuM1-HuM2)
print("\nHuM2-HuM3=",HuM2-HuM3) # 计算一下Hu 矩的差值
cv2.imshow("original1",o1)
cv2.imshow("original2",o2)
cv2.imshow("original3",o3)
cv2.waitKey()
cv2.destroyAllWindows()
HuM1-HuM2= [ 6.75354178e-04 1.31608683e-07 9.92863109e-10 2.15456221e-10
-1.10706347e-20 9.76947585e-14 1.12568713e-19] # 额,,差这么多是不是相似啊。。
HuM2-HuM3= [-1.27872218e-05 -4.53260375e-09 2.65510725e-12 -4.60117007e-12
-1.00868574e-22 -1.22339009e-15 -2.49242170e-22]
# 反正展示了图像经过缩放,平移旋转等操作后Hu 矩基本保持稳定,,
我们可以通过Hu 矩来判断两个对象的一致性。但是结果比较抽象,,确实,,但是我们亲爱的opencv 提供了 cv2.matchShapes() 对两个对象的Hu矩进行比较。
retval = cv2.matchShapes( contour1, contour2, method, parameter )
o1 = cv2.imread('18-1.jpg')
o2 = cv2.imread('18-2.jpg')
o3 = cv2.imread('12.jpg')
gray1 = cv2.cvtColor(o1,cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(o2,cv2.COLOR_BGR2GRAY)
gray3 = cv2.cvtColor(o3,cv2.COLOR_BGR2GRAY)
ret, binary1 = cv2.threshold(gray1,127,255,cv2.THRESH_BINARY)
ret, binary2 = cv2.threshold(gray2,127,255,cv2.THRESH_BINARY)
ret, binary3 = cv2.threshold(gray3,127,255,cv2.THRESH_BINARY)
contours1, hierarchy = cv2.findContours(binary1,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
contours2, hierarchy = cv2.findContours(binary2,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
contours3, hierarchy = cv2.findContours(binary3,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
cnt1 = contours1[0]
cnt2 = contours2[0]
cnt3 = contours3[0]
ret0 = cv2.matchShapes(cnt1,cnt1,1,0.0)
ret1 = cv2.matchShapes(cnt1,cnt2,1,0.0)
ret2 = cv2.matchShapes(cnt1,cnt3,1,0.0)
print("相同图像的 matchShape=",ret0)
print("相似图像的 matchShape=",ret1)
print("不相似图像的 matchShape=",ret2)
相同图像的 matchShape= 0.0
相似图像的 matchShape= 0.12473414346068246 # 比较相似的
不相似图像的 matchShape= 1.7976931348623157e+308 # 可以看到差的非常大,,,真的很直观。。我感觉不应该差那么多啊