OpenCV(9): 轮廓检测代码详解以及图像轮廓检测处理的全部流程

引言

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,这个方法用于展示图像,按任意键继续

结果:一些边缘消失了。 

猜你喜欢

转载自blog.csdn.net/m0_50317149/article/details/130068053