引言
Opencv图像轮廓检测主要是通过对图像进行边缘提取,并将提取出的边缘连接成为一个完整的边缘线来实现的。
图像轮廓
和边缘的区别,边缘是零散的,而图像的轮廓是一个整体
cv2.findContours() 是Opencv库中的一个函数,用于在二值化图像中查找轮廓。该函数的参数包括三个部分:
img: 需要查找轮廓的源图像,必须是一个灰度图或二值图。
mode: 轮廓检索模式,指定如何检测轮廓。有四种模式可选:
- cv2.RETR_EXTERNAL:只检测最外层轮廓线。
- cv2.RETR_LIST:检测所有轮廓线,但不建立轮廓之间的等级关系。
- cv2.RETR_CCOMP:检测所有轮廓线,并建立两层轮廓间的等级关系(外层和内层)。
- cv2.RETR_TREE:检测所有轮廓线,并重构轮廓之间的嵌套结构。
method: 轮廓逼近方法,应用于轮廓线的建立。有三种方法可选:
- cv2.CHAIN_APPROX_NONE:存储所有轮廓点,意味着不存在轮廓线之间的点的冗余。
- cv2.CHAIN_APPROX_SIMPLE:仅存储水平、竖直和对角线上的端点,使得轮廓线之间的点更加紧凑。
- cv2.CHAIN_APPROX_TC89_L1或cv2.CHAIN_APPROX_TC89_KCOS:使用Teh-Chin链逼近算法中的一个,以进一步减少轮廓线上的冗余点。
cv2.findContours() 函数返回两个值:
- contours:一个包含找到的轮廓线信息的列表。
- hierarchy:一个包含轮廓线之间等级关系的多维数组。每个元素代表一个轮廓线,包含4个值:当前轮廓线的下一层轮廓线的索引、上一层轮廓线的索引、同级别下一条轮廓线的索引和父轮廓线的索引。
代码实操
此次用的测试图片如下图,car.jpg
为了更精确的结果,往往要把图片转换成二值图 复习一下之前讲过的图像阈值threshold方法,可以将图像转换成二值图,以下是图像转换成二值图。
import cv2
#写一个方法用于展示图像,展示后按任意键继续下行代码
def cv_show(title,img):
cv2.imshow(title,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
return
img = cv2.imread('car.jpg')
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#转换成灰度图
ret, binary = cv2.threshold(img,122,255,cv2.THRESH_BINARY)#灰度图转化成二值图,通过图像阈值的方法。
cv_show('img',binary)#展示图片,按任意键继续
结果:
用转换后的二值图进行轮廓检测,完整代码如下:
import cv2
import numpy as np
#写一个方法用于展示图像,展示后按任意键继续下行代码
def cv_show(title,img):
cv2.imshow(title,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
return
img = cv2.imread('car2.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#转换成灰度图
ret, binary = cv2.threshold(gray,122,255,cv2.THRESH_BINARY)#灰度图转化成二值图,通过图像阈值的方法。
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)#寻找轮廓
copy = img.copy()
resoult = cv2.drawContours(copy,contours[:],-1,(0,0,255),1)
cv_show('res',resoult)
print(np.array(contours).shape)
解释代码:
在计算完轮廓后,我们将找到的轮廓绘制在原图上。
这段代码使用了 cv2.drawContours() 函数在输入图像的副本上绘制了所有轮廓线。
具体来说,该函数的参数依次为:
copy:输入图像的副本,由于cv2.drawContours()方法会改变原对象,所以输入的img得先copy一个。
contours[:]:待绘制的轮廓列表,即所有轮廓
-1:绘制所有轮廓
(0,0,255):轮廓线的颜色,这里为红色 (0,0,255),BGR顺序
1:轮廓线的线宽,这里为1个像素
由于 cv2.drawContours() 函数可以同时处理多个轮廓,因此传入 contours[:] 可以绘制所有轮廓线。最终的绘制结果保存在 resoult 中,可以通过 cv_show() 函数展示出来。
结果:
通过结果发现轮廓检测很好地找到了图像中的所有轮廓,但是与此同时,我们发现乱七八糟的轮廓太多了,所以我们在实际应用中,通常不会直接对二值图进行图像检测,都要进行降噪处理。
下面是降噪处理后的完整代码:
import cv2
import numpy as np
def cv_show(name,img):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
img = cv2.imread('car2.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 90, 255, cv2.THRESH_BINARY)
# 高斯滤波降噪
binary= cv2.GaussianBlur(binary, (5,5), 0)
# 形态学开闭运算降噪
kernel = np.ones((3,3), np.uint8) # 定义卷积核
binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=1)#开运算
binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=1)#闭运算
edges = cv2.Canny(binary, 100, 200, apertureSize=3)
cv_show("car2",edges)
contour,hierarchy = cv2.findContours(edges, cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)#寻找轮廓,都储存在contour中
copy = img.copy()
resoult = cv2.drawContours(copy,contour[:],-1,(0,0,255),2)
cv_show('res',resoult)
以上代码中,我先后对图像进行了高斯滤波降噪和开闭运算降噪处理,注意我调用了两次cv_show,这个方法用于展示图像,按任意键继续
结果:一些边缘消失了。