opencv入门:矩特征,Hu矩

矩特征

比较两个轮廓最简单的方法是比较二者的轮廓矩,轮廓矩代表了一个轮廓,一副图像,一组点集的全局特征,矩信息包含了对应对象不同类型的集合特征,例如大小,位置,角度,形状等,矩特征广泛应用在模式识别,图像识别方面。

矩的计算:moments 函数

retval = cv2.moments( array[, binaryImage] ) 获取图像的 Moments特征,使用轮廓矩可以方便的比较两个轮廓

  • array 可以是点集 ,也可以是灰度图或二值图。当array 是点集时,函数会把这些点集当作轮廓中的顶点,将整个点集当作一条轮廓,而不是当作独立的点
  • binaryImage True时,array 内所有非零值都处理为1,参数仅在array 是图像时有效。
  • retval 返回的矩特征,主要包括
  1. 空间矩,零阶矩 m00 ,一阶矩:m10, m01,二阶矩:m20, m11, m02,三阶矩:m30, m21, m12, m03
  2. 中心矩,二阶中心矩:mu20, mu11, mu02,三阶中心矩:mu30, mu21, mu12, mu03
  3. 归一化中心矩,二阶 Hu 矩:nu20, nu11, nu02,三阶 Hu 矩:nu30, nu21, nu12, nu03

阿巴阿巴,,上面的啥矩 都是根据公式计算得到的,大多数矩都是通过数学公式得到的抽象特征,但是很明显,如果两个轮廓的矩一致,那么这两个轮廓就是一致的。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

opencv入门:矩特征,Hu矩_第1张图片

计算轮廓的面积: contourArea 函数

retval =cv2.contourArea(contour [, oriented] ))

  • retval 面积值
  • contour 轮廓
  • oriented 布尔值,True,返回值包含正负号,用来表示轮廓是顺时针 还是逆时针,默认False ,表示返回的retval 是一个绝对值
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

计算轮廓的长度,arcLength 函数

retval = cv2.arcLength( curve, closed ) 计算轮廓的长度

  • curve轮廓
  • closed 布尔值,表示轮廓是否封闭

opencv入门:矩特征,Hu矩_第2张图片
opencv入门:矩特征,Hu矩_第3张图片

Hu矩

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

  • 二阶 Hu 矩:v20, v11, v02
  • 三阶 Hu 矩:v30, v21, v12, v03
    opencv入门:矩特征,Hu矩_第4张图片
    贴上提醒我是一个渣渣,,,数学真是博大精深,,这谁想出来的。。
# 针对ℎ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 矩基本保持稳定,,

opencv入门:矩特征,Hu矩_第5张图片
感觉这个介绍胡矩(Hu矩)的文章挺好 脑补链接

形状匹配

我们可以通过Hu 矩来判断两个对象的一致性。但是结果比较抽象,,确实,,但是我们亲爱的opencv 提供了 cv2.matchShapes() 对两个对象的Hu矩进行比较。
retval = cv2.matchShapes( contour1, contour2, method, parameter )

  • contour1 第一个轮廓或者灰度图像
  • contour2 第二个灰度或轮廓图像 ,不管是什么,这个函数都会提前计算好对象的Hu矩。
  • method 比较两个对象的Hu 矩的方法,ℎ和ℎ分别是对象 A 和对象 B 的 Hu 矩。
    opencv入门:矩特征,Hu矩_第6张图片
  • parameter 应用于mathod 的特定函数,4.5 了还没有支持
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   # 可以看到差的非常大,,,真的很直观。。我感觉不应该差那么多啊

你可能感兴趣的:(opencv从入门到放弃,opencv)