Advertisement

18. 轮廓发现-cv2.findContours()、cv2.drawContours()

阅读量:

1. 定义

在进行对象轮廓检测时,我们主要依赖于图像边缘提取这一技术作为基础方法。因此, 选择合适的边缘检测阈值将直接影响到最终的检测结果。

该特征可被视为由连续的点(附着边界)连接而成的一条曲线,并具备相同的色调或亮度。
这一特征在形状分析、物体检测以及识别方面具有重要意义。
为了提高准确性, 通常会采用二值化图像。
在进行边缘检测之前(如Canny边缘检测), 一般会对原始图像进行二值化处理。
若需后续操作原始图像, 则应在程序开始前将原始图像存入其他变量。
与OpenCV中的边缘提取类似, 在黑色背景中寻找白色物体时等同于寻找边缘。

2. 实现函数-cv2.findContours()、cv2.drawContours()

2.1 轮廓发现-cv2.findContours()

参数如下: cv2.findContours( (Image数组 image, 轮廓数组 contours, 水平层次信息 hierarchy, 整数模式 mode, 方法名称 method, 偏移量 offset = DefaultPoint() )
参数说明:

image为单通道图像矩阵,在实际应用中通常表示为灰度图;其中二值图像更为常见;这些二值图像通常是通过Canny和拉普拉斯等边缘检测算子处理得到的。

contours为一矢量,
该矢量内部每个元素存储一个由连续Point构成的一组点集,
每一个Point集合即代表一个轮廓。
则该矢量中存有与之对应的多个Point集合数目。

hierarchy字段定义为vector类型,在OpenCV中Vec<int,4>被定义为四个连续存储的整数变量组成的向量。根据定义可知,hierarchy字段实际上也是一个容器,其中每个元素都是一个包含四个整数变量的四维向量。从结构上看,hierarchy字段与contour容器一一对应,并且它们所包含的轮廓数量相同。在hierarchy容器中的每一个四维向量元素,hierarchy[i][0]至hierarchy[i][3]分别用来表示第i个轮廓与其相关联的后续轮廓、前驱轮廓、父轮廓以及嵌套轮廓的具体索引编号。如果当前轮廓没有相应的后续、前驱、父或嵌套轮廓信息,则相应的位置将被设置为默认值-1

4. mode:int类型的,定义轮廓的检索模式:

CV_RETR_EXTERNAL仅识别最外围的边界线,并不考虑位于外围边界线内的内部边界线。

2):OpenCV的CV_RETR_LIST方法会遍历所有边界(内外边界),但该方法不会定义任何层次结构关系,在这种检索模式下所有的外边界和内边界都是相互独立存在的。因此,在hierarchy矩阵中与这些外边界相关联的第三和第四个元素都会被赋值为-1(后续内容会详细说明)。

CV_RETR_CCOMP用于遍历所有的边缘,并根据层级结构将这些边缘分为两种关系类型:最外层边界作为主层次区域;当内部区域包含其他细节时,则将这些细节所属的所有子区域归入主层次中

  1. :CV_RETR_TREE, 对所有边界进行遍历并构建层次结构树。外围层级将包含内部层级的边界元素,并且这些内部层级的边界元素还可以进一步嵌套。

5. method:int类型,定义轮廓的近似方法:

取值一:CV_CHAIN_APPROX_NONE用于记录所有连续的轮廓点并将其包含在contours向量中;

第二种情况:CV_CHAIN_APPROX_SIMPLE仅记录轮廓折线顶点的信息,并将这些折线顶点坐标存储到contours向量中;它不会包含相邻折线段之间直线上的其他中间像素坐标

选择第三和第四项:CV_CHAIN\_APPROX\_TC89\_L1CV_CHAIN\_APPROX\_TC89\_KCOS 采用了 Teh-Chinℓ 近似算法。

6. Point:offset, 所有的contour information相对于original image corresponding points的offset value等价于在每一个detection到的contour point上添加相应的offset value, 并且该offset value还可以取负数值.

2.2 轮廓显示 - cv2.drawContours()

  • 参数如下: cv2.drawContours(img, contours, contourldx, color, thickness)

参数说明:1. img:绘制在图像上的轮廓 2. 所有被识别出的边缘 3. 边缘对应的索引号 4. 颜色参数 5. 线条宽度参数

3. 实现代码

复制代码
 import cv2 as cv

    
 import numpy as np
    
  
    
  
    
 def edge_demo(image):
    
     blurred = cv.GaussianBlur(image, (3, 3), 0)
    
     gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)
    
  
    
     # grad_x = cv.Sobel(gray, cv.CV_16SC1, 1, 0)
    
     # grad_y = cv.Sobel(gray, cv.CV_16SC1, 0, 1)
    
  
    
     # edge_output = cv.Canny(grad_x, grad_y, 30, 150)
    
     edge_output = cv.Canny(gray, 50, 150)
    
     return edge_output
    
  
    
  
    
 def contours_demo(image):
    
     """
    
     . 轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同 的颜色或者灰度。
    
     轮廓在形状分析和物体的检测和识别中很有用。
    
     . 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理或者 Canny 边界检测
    
     . 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图像的话,
    
     你应该将原始图像存储到其他变量中.
    
     . 在 OpenCV 中,查找轮廓就像在黑色背景中超白色物体。要找的物体应该是白色而背景应该是黑色。
    
   26.   27.     """
    
     # dst = cv.GaussianBlur(image, (3, 3), 0)
    
     # gray = cv.cvtColor(dst, cv.COLOR_BGR2GRAY)
    
     # ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
    
     # cv.imshow("binary image", binary)
    
     binary = edge_demo(image)
    
     """
    
     • 函数 cv2.findContours() 有三个参数, 第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。
    
     • 检索模式:
    
     • CV_RETR_EXTERNAL - 只提取外层的轮廓  
    
     • CV_RETR_LIST - 提取所有轮廓,并且放置在 list 中  
    
     • CV_RETR_CCOMP - 提取所有轮廓,并且将其组织为两层的 hierarchy: 
    
         顶层为连通域的 外围边界,次层为洞的内层边界。  
    
     • CV_RETR_TREE - 提取所有轮廓,并且重构嵌套轮廓的全部 hierarchy
    
     • 逼近方法 (对所有节点, 不包括使用内部逼近的 CV_RETR_RUNS).  点的存贮情况,是不是都被存贮
    
     • CV_CHAIN_CODE - Freeman 链码的输出轮廓. 其它方法输出多边形(定点序列).  
    
     • CV_CHAIN_APPROX_NONE - 将所有点由链码形式翻译为点序列形式  
    
     • CV_CHAIN_APPROX_SIMPLE - 压缩水平、垂直和对角分割,即函数只保留末端的象素 点;  
    
     • CV_CHAIN_APPROX_TC89_L1,  CV_CHAIN_APPROX_TC89_KCOS - 应用 Teh-Chin 链逼近算法.  
    
     • CV_LINK_RUNS - 通过连接为 1 的水平碎片使用完全不同的轮廓提取算法。仅有 CV_RETR_LIST 提取模式可以在本方法中应用.  
    
     • offset:每一个轮廓点的偏移量. 当轮廓是从图像 ROI 中提取出来的时候,使用偏移量有用,因为可以从整个图像上下文来对轮廓做分析. 
    
     • 返回值有三个,第一个是图像,第二个是轮廓,第三个是(轮廓的)层析结构。
    
     轮廓(第二个返回值)是一个 Python 列表,其中存储这图像中的所有轮廓。
    
     每一个轮廓都是一个 Numpy 数组,包含对象边界点(x,y)的坐标。
    
     """
    
     contours, hierarchy= cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    
     for i, contour in enumerate(contours):
    
     # 函数 cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。
    
     # 它的第一个参数是原始图像,第二个参数是轮廓,一个 Python 列表。
    
     # 第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设 置为 -1 时绘制所有轮廓)。
    
     # 接下来的参数是轮廓的颜色和厚度等。
    
     cv.drawContours(image, contours, i, (0, 0, 255), 2)  # 2为像素大小,-1时填充轮廓
    
     print(i)
    
     cv.imshow("detect contours", image)
    
  
    
  
    
 def main():
    
     src = cv.imread("../images/circle.png")
    
     # cv.imshow("demo",src)
    
     contours_demo(src)
    
     cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    
     cv.destroyAllWindows()  # 关闭所有窗口
    
  
    
  
    
 if __name__ == '__main__':
    
     main()
    
    
    
    
    代码解读
复制代码
复制代码

全部评论 (0)

还没有任何评论哟~