在findContours()函数中,我们一共返回了三个参数:image,contours,hierarchy。其中image代表的是修改之后的原图,contours代表的是轮廓,hierarchy代表的是轮廓的层次结构。我们主要来讨论一下第二个返回值:contours。以及轮廓可以进行的一些操作。
contours代表的是找到的轮廓,它是一个numpy中的列表结构,那么接下来就让我们探究一下这个列表究竟是怎么存储轮廓的。
import numpy as np
import cv2
img=cv2.imread('D://zopencv//contours.png')
imgcopy=img.copy()
imgray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,img_thre=cv2.threshold(imgray,127,255,cv2.THRESH_BINARY)
image,contours,hierarchy=cv2.findContours(img_thre,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
mask1=cv2.drawContours(imgcopy,contours,-1,(0,0,255),2)
cv2.imshow('mask1',mask1)
cv2.waitKey(0)
cv2.destroyAllWindows()
之前我们已经讨论过了,drawContours()函数的第三个参数是轮廓的索引,如果我们定为-1就会把所有轮廓画出。那你有没有想过,他既然是索引,我们是不是可以一个一个轮廓的画出来。
我们将索引定为0:
mask1=cv2.drawContours(imgcopy,contours,0,(0,0,255),2)
只有正方形的轮廓被画了出来,也就是说,正方形的轮廓在轮廓列表contours中的索引是0.
同理,我们可以把索引分别设置成1,2。箭头和圆的轮廓分别被画出来了。从这我们也可以看出,contours列表中的大致情况。
有了这些理解,我们下面理解轮廓的操作就会顺利许多。
简言之就是计算一个图像的轮廓包起来的面积。
构造函数如下:
cv2.contourArea()
一定要把contours列表按索引单独计算,容易理解。
image,contours,hierarchy=cv2.findContours(img_thre,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt=contours[0]
area=cv2.contourArea(cnt)
print(area)
#3080.0
同理,图像的轮廓周长
构造函数:
cv2.arcLength(cnt,True)
image,contours,hierarchy=cv2.findContours(img_thre,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt=contours[0]
length=cv2.arcLength(cnt,True)
#222.0
我们在实际应用中,有时候图像会长的不尽人意,轮廓也因此奇形怪状,但是我们想让图像的轮廓能变得“整齐”一些,我们就需要用到轮廓近似的方法。
其原理就是:我们先设置一个值thre=x,然后在图像中找到两个带你A,B,两点间做辅助线,得到距离AB实际边缘最远距离d,只要d小于thre,那么AB之间的实际轮廓就可以近似为AB直接做的辅助线(直线)。
但是如果d>thre,那么我们就在AC、BC之间再连线,重复AB之间的做法,如果d小于thre,就可近似为直线,否则继续重复操作。有点像二分操作的思想。
道理就是这样,OpenCV中提供给我们现成的函数:
approx = cv2.approxPolyDP(cnt,epsilon,True)
import numpy as np
import cv2
img=cv2.imread('D://zopencv//lunkuojinsi.png')
imgcopy=img.copy()
imgray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,img_thre=cv2.threshold(imgray,127,255,cv2.THRESH_BINARY)
image,contours,hierarchy=cv2.findContours(img_thre,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt=contours[0]
epsilon=0.1*cv2.arcLength(cnt,True)
mask2=cv2.approxPolyDP(cnt,epsilon,True)
mask1=cv2.drawContours(imgcopy,[mask2],-1,(0,0,255),2)
cv2.imshow('mask1',mask1)
cv2.waitKey(0)
cv2.destroyAllWindows()
我们取周长的十分之一作为界限,得到如上图像。很显然,轮廓变得整齐了起来。
我们在物体识别追踪之类的项目中常常需要把识别到的物体框起来,这时候就需要用到边界矩形。
两个函数一起调用:
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
import numpy as np
import cv2
img=cv2.imread('D://zopencv//contours.png')
imgcopy=img.copy()
imgray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,img_thre=cv2.threshold(imgray,127,255,cv2.THRESH_BINARY)
image,contours,hierarchy=cv2.findContours(img_thre,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt=contours[1]
x,y,w,h=cv2.boundingRect(cnt)
cv2.rectangle(img,(x,y),(x+w,y+h),(255,255,0),thickness=2)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
函数:
(x,y),radious=cv2.minEnclosingCircle(cnt)
这个函数返回轮廓最小外接圆的圆心坐标和半径,但要注意返回值不是int型。
import numpy as np
import cv2
img=cv2.imread('D://zopencv//contours.png')
imgcopy=img.copy()
imgray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,img_thre=cv2.threshold(imgray,127,255,cv2.THRESH_BINARY)
image,contours,hierarchy=cv2.findContours(img_thre,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt=contours[1]
(x,y),radious=cv2.minEnclosingCircle(cnt)
img=cv2.circle(img,(int(x),int(y)),int(radious),(255,255,0),2)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()