比较两个轮廓最简单的方法是比较二者的轮廓矩。轮廓矩代表了一个轮廓、一幅图像、一组点集的全局特征。

矩信息包含了对应对象不同类型的几个特征,例如大小、位置、角度、形状等。矩特征被广泛地应用在模式识别、图像识别中。

矩的计算:moments函数

OpenCV提供了函数cv2.moments()来获取图像的moments特征。通常情况下,我们将使用函数cv2.moments()获取的轮廓特征成为“轮廓矩”。

retval = cv2.moments(array[,binaryImage])
  • 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的含义比较直观,它表示一个轮廓的面积。

1、返回值retval,用来比较两个轮廓是否相似,例如有两个轮廓,不管它们出现在图像的哪个位置,都可以通过函数cv2.moments()的m00矩来判断其面积是否一致。

2、位置发生变化时,虽然周长、面积等不发生变化,但是更高阶的特征会随着位置的变化而变化。在很多情况下,希望比较不同位置的两个对象的一致性,解决这一问题就引入了中心矩。中心矩具有平移不变性。

3、归一化中心矩不仅具有平移不变性,还具有放缩不变性。

 

例:使用函数cv2.moments()提取一幅图像的特征。

import cv2
import numpy as np
img = cv2.imread('E:\Blog\OpenCV\lunkuo.png')
cv2.imshow('IMG',img)
# ====处理为二值图=====
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)   
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) #阈值处理

# =====查找轮廓=======
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

n = len(contours)  #有n个轮廓
contoursImg = []   #构建空白画板
for i in range(n):
    temp = np.zeros(img.shape,np.uint8)
    contoursImg.append(temp)  #在画板上建立了一个和img一模一样大小的黑背景
    contoursImg[i] = cv2.drawContours(contoursImg[i],contours,i,(0,0,255),5)
    cv2.imshow('NO-'+str(i),contoursImg[i])

print("观察各个轮廓的矩:")
for i in range(n):
    print('轮廓'+str(i)+'的矩:\n',cv2.moments(contours[i]))

print("观察各个轮廓的面积:")
for i in range(n):
    print('轮廓'+str(i)+'的面积:%d\n' %cv2.moments(contours[i])['m00'])

cv2.waitKey()

输出的图像

观察各个轮廓的矩:
轮廓0的矩:
{'m00': 6453.0, 'm10': 1339175.5, 'm01': 575620.1666666666, 'm20': 282837219.6666666, 'm11': 119458458.0, 'm02': 54608064.666666664, 'm30': 60739248384.75, 'm21': 25292761670.266666, 'm12': 11332922148.166666, 'm03': 5411542332.55, 'mu20': 4921363.506702244, 'mu11': 1395.4700268656015, 'mu02': 3261624.828860827, 'mu30': 86144.66669464111, 'mu21': 62551677.61128932, 'mu12': -6540.101512312889, 'mu03': -41489953.201865196, 'nu20': 0.11818493326411932, 'nu11': 3.3511755791374877e-05, 'nu02': 0.07832685234621375, 'nu30': 2.5752786786273007e-05, 'nu21': 0.018699706882114274, 'nu12': -1.9551510995358336e-06, 'nu03': -0.01240334381195705}
轮廓1的矩:
{'m00': 3960.0, 'm10': 1324620.0, 'm01': 279341.1666666666, 'm20': 443681256.6666666, 'm11': 93439620.25, 'm02': 22915133.833333332, 'm30': 148810015155.0, 'm21': 31300701065.083332, 'm12': 7665112267.25, 'm03': 2057344257.25, 'mu20': 595866.6666666269, 'mu11': 1.4901161193847656e-08, 'mu02': 3210212.774067063, 'mu30': 3.0517578125e-05, 'mu21': 3115240.4875117466, 'mu12': -1.1920928955078125e-06, 'mu03': -12006596.90111041, 'nu20': 0.037997823351356166, 'nu11': 9.50232195301988e-16, 'nu02': 0.20471206854320112, 'nu30': 3.092517032335895e-14, 'nu21': 0.00315684758075882, 'nu12': -1.208014465756209e-15, 'nu03': -0.01216695678306724}
轮廓2的矩:
{'m00': 8998.0, 'm10': 548878.0, 'm01': 656854.0, 'm20': 39805312.666666664, 'm11': 40064028.33333333, 'm02': 54515475.33333333, 'm30': 3199622142.0, 'm21': 2905291813.3333335, 'm12': 3324850408.0, 'm03': 4938139166.0, 'mu20': 6323754.666666664, 'mu11': -4065.6666666716337, 'mu02': 6565133.333333328, 'mu30': 4.76837158203125e-07, 'mu21': 9.5367431640625e-07, 'mu12': 9.5367431640625e-07, 'mu03': 9.5367431640625e-07, 'nu20': 0.0781057550793395, 'nu11': -5.021573126091483e-05, 'nu02': 0.0810870634971725, 'nu30': 6.208763523899747e-17, 'nu21': 1.2417527047799495e-16, 'nu12': 1.2417527047799495e-16, 'nu03': 1.2417527047799495e-16}
观察各个轮廓的面积:
轮廓0的面积:6453

轮廓1的面积:3960

轮廓2的面积:8998

计算轮廓的面积:contourArea函数

函数cv2.contourArea()用于计算轮廓的面积。语法:

retval = cv2.contourArea(contour [,oriented])
  • contour是轮廓
  • oriented:有方向的区域标志。
    • true:此函数依赖轮廓的方向(顺时针或逆时针)返回一个已标记区域的值。
    • false:默认值。意味着返回不带方向的绝对值

例1:使用cv2.contourArea()计算各个轮廓的面积。

import cv2
import numpy as np
img = cv2.imread('E:\Blog\OpenCV\lunkuo.png')
cv2.imshow('IMG',img)
# ====处理为二值图=====
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)   
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) #阈值处理

# =====查找轮廓=======
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

n = len(contours)  #有n个轮廓
contoursImg = []   #构建空白画板
for i in range(n):
    print('contours['+str(i)+']面积',cv2.contourArea(contours[i]))
    temp = np.zeros(img.shape,np.uint8)
    contoursImg.append(temp)  #在画板上建立了一个和img一模一样大小的黑背景
    contoursImg[i] = cv2.drawContours(contoursImg[i],contours,i,(0,0,255),5)
    cv2.imshow('NO-'+str(i),contoursImg[i])

cv2.waitKey()

结果:

contours[0]面积 6453.0
contours[1]面积 3960.0
contours[2]面积 8998.0

例2:在上面的基础上,筛选出面积大于4000的轮廓。

只需要在代码中插入if判断即可。

计算轮廓的长度:arcLength函数

retval = cv2.arcLength(curve, closed)

式中的返回值为轮廓的长度。

  • curve是轮廓
  • closed表示轮廓是否封闭,该值为True时,表示轮廓是封闭的

例1:将一幅图像内长度大于平均值的轮廓显示出来。

思路:

  • 获取轮廓并计数
  • 计算各个轮廓的长度
  • 计算总长度及平均长度
  • if判断进行筛选
import cv2
import numpy as np
img = cv2.imread('E:\Blog\OpenCV\Process\curve.png')
cv2.imshow('IMG',img)
# ====处理为二值图=====
gray = cv2.cvtColor(img,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)
# cv2.RETR_LIST 对检测的轮廓不建立等级关系

# =====各轮廓长度及长度之和、平均长度
n = len(contours)  #有n个轮廓
cntLen = []        #存储各个长度
for i in range(n):
    cntLen.append(cv2.arcLength(contours[i],True))
    print('第'+str(i)+'个轮廓的长度:%d' %cntLen[i])
cntLenSum = np.sum(cntLen)  #计算各轮廓长度之和
cntLenAvr =  cntLenSum/n
print("轮廓的总长度为:%d" %cntLenSum)    
print("轮廓的平均长度为:%d" %cntLenAvr)      

# =====显示长度超过平均值的轮廓====
contoursImg = []   #构建空白画板
for i in range(n):
    temp = np.zeros(img.shape,np.uint8)
    contoursImg.append(temp)  #在画板上建立了一个和img一模一样大小的黑背景
    contoursImg[i] = cv2.drawContours(contoursImg[i],contours,i,(0,0,255),3)
    if cv2.arcLength(contours[i],True)>cntLenAvr:
        cv2.imshow('NO-'+str(i),contoursImg[i])
cv2.waitKey()

运行结果:

第0个轮廓的长度:428
第1个轮廓的长度:633
第2个轮廓的长度:742
第3个轮廓的长度:174
第4个轮廓的长度:176
第5个轮廓的长度:570
第6个轮廓的长度:731
第7个轮廓的长度:820
轮廓的总长度为:4279
轮廓的平均长度为:534

结果图像


博主个人公众号
版权声明 ▶ 本网站名称:陶小桃Blog
▶ 本文链接:https://www.52txr.cn/2022/OpenCV35.html
▶ 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行核实删除。
▶ 转载本站文章需要遵守:商业转载请联系站长,非商业转载请注明出处!!

最后修改:2022 年 05 月 27 日
如果觉得我的文章对你有用,请随意赞赏