当前位置:   article > 正文

opencv入门:凸包,利用形状场景算法比较轮廓,轮廓的特征值_凸包算法求轮廓

凸包算法求轮廓

凸包

逼近多边形是轮廓的高度近似,但是有时候,我们希望使用一个多边形凸包来简化它,凸包和逼近多边形很想,但是是最外层凸起的多边形。凸包指完全包含原有轮廓,并且仅由轮廓上的点所构成的多边形,凸包每一处都是凸的,
在这里插入图片描述
边缘与凸包之间的部分被称为图缺陷,可以用来处理手势识别的问题。

获取凸包

hull = cv2.convexHull( points[, clockwise[, returnPoints]] )

  • hull凸包角点
  • clockwise 布尔值,True时,凸包角点将按顺时针方向排序,False就是逆时针排列凸包角点
  • returnPoints 布尔值默认True,函数返回凸包角点的 x/y 轴坐标,False时,返回轮廓中凸包角点的索引

在这里插入图片描述
看来懂一点PS 还时有用的。。。

凸缺陷

凸包与轮廓之间的部分就是凸缺陷,使用convexityDefects = cv2.convexityDefects( contour, convexhull ) 来获取。返回值时凸缺陷点集,是一个数组,每一行包括的值是 [起点,终点,轮廓上距离凸包最远的点,最远点到凸包的近似距离] .另外前三个值都是轮廓点的索引,要到轮廓点中再寻找。参数就是轮廓和凸包。
在这里插入图片描述
我那个图不好理解,,,,凸缺陷的点就是两个凸包角点连线,然后轮廓上距离这条线最远的点就是凸缺陷的点

需要注意的是,用 cv2.convexityDefects()计算凸缺陷时,要使用凸包作为参数。在查找该 凸包时,所使用函数cv2.convexHull()的参数 returnPoints 的值必须是 False。

o = cv2.imread('20.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_TREE,
 cv2.CHAIN_APPROX_SIMPLE)

cnt = contours[0]
hull = cv2.convexHull(cnt,returnPoints = False)            
defects = cv2.convexityDefects(cnt,hull)         

for i in range(defects.shape[0]):
    s,e,f,d = defects[i,0]     
    start = tuple(cnt[s][0])           # 得到的是索引,要再轮廓中选出来
    end = tuple(cnt[e][0])
    far = tuple(cnt[f][0])
    cv2.line(o,start,end,[0,0,255],2)
    cv2.circle(o,far,5,[255,0,0],-1)

cv2.imshow('result',o)
cv2.waitKey(0)
cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这里插入图片描述

几何学测试

  1. 检测轮廓是否是凸型的

retval = cv2.isContourConvex( contour ) 返回值是布尔值,True表示轮廓是凸的,contour 是判断的轮廓

在这里插入图片描述
可以看到逼近多边形就不是凸型的

  1. 点到轮廓的距离
    retval = cv2.pointPolygonTest( contour, pt, measureDist ) 用来计算点到多边形轮廓的最短距离(也就是垂线距离),又叫点和多边形的关系测试。
  • contour 轮廓
  • pt 待判定的点
  • measureDist 布尔值,表示距离的判定方式,True时,表示计算颠倒轮廓的距离,如果再轮廓外返回负值,轮廓上返回0,内部就是正数,False 时,不计算距离,只返回 -1,0,1 表示点相对于轮廓的位置,外,上,内。

在这里插入图片描述
A 点再轮廓外,返回的距离时负值。

利用形状场景算法比较轮廓

用矩比较是一种非常有效的办法,但是现在的shape 模块更厉害就是,,,模块中的形状场景算法可以更高效的比较形状

计算形状场景距离

使用 距离 作为形状比较的度量标准,这是因为形状之间的差异值和距离有相似之处。。。深奥的咱也不懂。。

retval = cv2.createShapeContextDistanceExtractor( [, nAngularBins[, nRadialBins[, innerRadius[, outerRadius[, iterations[, comparer[, transformer]]]]]]] ) 使用这个函数计算形状场景距离,使用 形状上下文算法,在每个点上附加一个“形状上下文”描述符,让每个点都能够捕获剩余点相对于它的分布特征,从而提供全局鉴别特征。。。哈哈

返回结果可以通过retval=cv2.ShapeDistanceExtractor.computeDistance(contour1, contour2) 计算两个不同形状之间的距离,参数是两个不同的轮廓。

  • nAngularBins:为形状匹配中使用的形状上下文描述符建立的角容器的数量。
  • nRadialBins:为形状匹配中使用的形状上下文描述符建立的径向容器的数量。
  • innerRadius:形状上下文描述符的内半径。
  • outerRadius:形状上下文描述符的外半径。
  • iterations:迭代次数。
  • comparer:直方图代价提取算子。该函数使用了直方图代价提取仿函数,可以直接采用直方图代价提取仿函数的算子作为参数。
  • transformer:形状变换参数。.。。这些参数涨涨见识就行。。。
o1 = cv2.imread('18-1.jpg') 
cv2.imshow("original1",o1) 
gray1 = cv2.cvtColor(o1,cv2.COLOR_BGR2GRAY) 
ret, binary1 = cv2.threshold(gray1,127,255,cv2.THRESH_BINARY) 
contours1, hierarchy = cv2.findContours(binary1,
 cv2.RETR_LIST,
 cv2.CHAIN_APPROX_SIMPLE)
cnt1 = contours1[0]

o2 = cv2.imread('18-2.jpg') 
cv2.imshow("original2",o2) 
gray2 = cv2.cvtColor(o2,cv2.COLOR_BGR2GRAY) 
ret, binary2 = cv2.threshold(gray2,127,255,cv2.THRESH_BINARY) 
contours2, hierarchy = cv2.findContours(binary2,
 cv2.RETR_LIST,
 cv2.CHAIN_APPROX_SIMPLE) 
cnt2 = contours2[0]

o3 = cv2.imread('20.jpg') 
cv2.imshow("original3",o3) 
gray3 = cv2.cvtColor(o3,cv2.COLOR_BGR2GRAY) 
ret, binary3 = cv2.threshold(gray3,127,255,cv2.THRESH_BINARY) 
contours3, hierarchy = cv2.findContours(binary3,
 cv2.RETR_LIST,
 cv2.CHAIN_APPROX_SIMPLE)
cnt3 = contours3[0]

sd = cv2.createShapeContextDistanceExtractor()  # 构造距离提取算子,,
d1 = sd.computeDistance(cnt1,cnt1)
print("与自身的距离 d1=", d1)
d2 = sd.computeDistance(cnt1,cnt2)
print("与旋转缩放后的自身图像的距离 d2=", d2)
d3 = sd.computeDistance(cnt1,cnt3)
print("与不相似对象的距离 d3=", d3)
cv2.waitKey()
cv2.destroyAllWindows()

与自身的距离 d1= 0.0
与旋转缩放后的自身图像的距离 d2= 0.45604294538497925   # 差距很明显,还是matchShapes好啊,就一句话就行,这还要多一句。。
与不相似对象的距离 d3= 24022.529296875
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

在这里插入图片描述
然后还有一个啥 豪斯多夫距离,,,函数换成cv2.createHausdorffDistanceExtractor() 其他一样,至于计算方法,呵呵

轮廓的特征值

轮廓自身的一些属性特征及轮廓所包围对象的特征对于描述图像具有重要意义。所以到底有啥用。。

宽高比

就是宽度/高度
在这里插入图片描述

Extent

轮廓面积(对象面积) / 矩形边界面积
在这里插入图片描述

Solidity

轮廓面积(对象面积) / 凸包面积

在这里插入图片描述

等效直径

该值是与轮廓面积相等的圆形的直径
在这里插入图片描述
在这里插入图片描述
这里圆形的面积与那箭头面积一致。额,视觉欺骗

掩模和像素点

获取某对象的掩模图像及其对应的点,cv2.drawContours() 的轮廓宽度参数 thickness 设置为-1,可以获取特定对象的实心轮廓,及特定对象的掩模。。另外还可以获取轮廓像素点的具体位置信息。轮廓是图像内非零的像素点,可以通过numpy 函数和opencv 函数获得。

  1. 使用Numpy函数获取轮廓像素点
    numpy.nonzero() 函数找出数组内非零原始的位置,返回值是将行,列分别显示的。再用 Numpy.transpose() 转置处理就可以获得这些点的(x,y)形式的坐标。
o = cv2.imread('19.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) 
cnt=contours[0]

mask1 = np.zeros(gray.shape,np.uint8)
cv2.drawContours(mask1,[cnt],0,255,2)          # 绘制空心轮廓。
pixelpoints1 = np.transpose(np.nonzero(mask1))
print("pixelpoints1.shape=",pixelpoints1.shape)
print("pixelpoints1=\n",pixelpoints1)
cv2.imshow("mask1",mask1)

mask2 = np.zeros(gray.shape,np.uint8)
cv2.drawContours(mask2,[cnt],0,255,-1)
pixelpoints2 = np.transpose(np.nonzero(mask2))            # 绘制实心轮廓
print("pixelpoints2.shape=",pixelpoints2.shape)
print("pixelpoints2=\n",pixelpoints2)
cv2.imshow("mask2",mask2)

cv2.waitKey()
cv2.destroyAllWindows()

pixelpoints1.shape= (1569, 2)
pixelpoints1=
 [[206 279]
 [206 280]
 [207 278]
 ...
 [284 281]
 [285 279]
 [285 280]]
pixelpoints2.shape= (3280, 2)
pixelpoints2=
 [[207 279]
 [208 279]
 [208 280]
 ...
 [283 279]
 [283 280]
 [284 279]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

在这里插入图片描述

  1. 使用opencv 函数获取轮廓点
    idx = cv2.findNonZero( src ) 查找非零元素的索引,idx 返回值,表示非零元素的索引位置,注意返回的索引中,每个元素对应的是(列号,行号) 的格式。,src 表示要查找非零元素的图像。
mask1 = np.zeros(gray.shape,np.uint8)
cv2.drawContours(mask1,[cnt],0,255,2)
pixelpoints1 = cv2.findNonZero(mask1)          # 就这里不同
print("pixelpoints1.shape=",pixelpoints1.shape)
print("pixelpoints1=\n",pixelpoints1)
cv2.imshow("mask1",mask1)

mask2 = np.zeros(gray.shape,np.uint8)
cv2.drawContours(mask2,[cnt],0,255,-1)
pixelpoints2 = cv2.findNonZero(mask2)
print("pixelpoints2.shape=",pixelpoints2.shape)
print("pixelpoints2=\n",pixelpoints2)
cv2.imshow("mask2",mask2)

cv2.waitKey()
cv2.destroyAllWindows()

pixelpoints1.shape= (1569, 1, 2)
pixelpoints1=
 [[[279 206]]         # 跟上面的结果是到过来的。
 [[280 206]]
 [[278 207]]
 ...
 [[281 284]]
 [[279 285]]
 [[280 285]]]
pixelpoints2.shape= (3280, 1, 2)
pixelpoints2=
 [[[279 207]]
 [[279 208]]
 [[280 208]]
 ...
 [[279 283]]
 [[280 283]]
 [[279 284]]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

最大值和最小值及他们的位置

min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray,mask = mask) 用于再指定对象内查找最大值,最小值及其位置。参数是单通道图像,和掩模,通过使用掩模图像,可以得到掩模指定区域内额最值信息。

注意这个函数处理的是灰度图像,对于彩图,需要提取各个通道图像,为每个通道独立计算。

在这里插入图片描述

平均颜色及平均灰度

mean_val = cv2.mean(im,mask = mask) 计算一个对象的平均颜色或平均深度,返回值是返回的平均值,参数就是图像和掩模

# 跟上面那个基本一样,就换一句话。。。
meanVal = cv2.mean(o,mask = mask) # mask 是一个区域,所以必须是单通道的
print("meanVal=\n",meanVal)

meanVal=
 (250.5229333855288, 7.352221569778822, 7.29030469106805, 0.0)   # cv2.mean 可以计算各个通道的均值,对应RGBA
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

极点

获取某个对象的极值点,例如最左端,最右端,最上端,最下端四个点。

o = cv2.imread('19.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)
cnt=contours[0]     

leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])
rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])        # 四个点的计算方式
topmost = tuple(cnt[cnt[:,:,1].argmin()][0])
bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])
print("leftmost=",leftmost)
print("rightmost=",rightmost)
print("topmost=",topmost)
print("bottommost=",bottommost)

font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(o,'A',leftmost, font, 1,(0,0,255),2)
cv2.putText(o,'B',rightmost, font, 1,(0,0,255),2)
cv2.putText(o,'C',topmost, font, 1,(0,0,255),2)
cv2.putText(o,'D',bottommost, font, 1,(0,0,255),2)

cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()


leftmost= (151, 239)
rightmost= (335, 246)
topmost= (279, 207)
bottommost= (279, 284)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/79073
推荐阅读
相关标签
  

闽ICP备14008679号