美文网首页视觉处理
查找并绘制轮廓---OpenCV-Python开发指南(24)

查找并绘制轮廓---OpenCV-Python开发指南(24)

作者: 极客学编程 | 来源:发表于2021-04-24 19:01 被阅读0次

    什么是图像轮廓

    在前面的边缘检测中,虽然我们能够检测出边缘信息,但边缘是不连续的,检测到的边缘并不是一个整体。而图像的轮廓负责将边缘连接起来形成一个整体,用于后续的计算。

    需要特别注意,图像的轮廓是非常重要的一个特征信息,通过对图像轮廓的操作,我们能够获取目标图像的大小,位置以及方向等信息。

    查找图像轮廓

    图像中一个轮廓对应着一系列的点,这些点以某种形式表示图像中的一条曲线。在OpenCV中,它给我们提供了函数cv2.findContours()用于查找图像的轮廓,并能够根据参数返回特定表示方式的轮廓。

    该函数的完整定义如下:

    def findContours(image, mode, method, contours=None, hierarchy=None):
    

    image:原始图像,所有非0值处理为1,所有0值保持不变

    mode:轮廓检索模式

    参数 含义
    cv2.RETR_EXTERNAL 只检测外轮廓
    cv2.RETR_LIST 对检测到的轮廓不建立等级关系
    cv2.RETR_CCOMP 检测所有轮廓并将它们组织成两级层次关系。上层为外边界,下层为内孔边界。如果内孔内还有一个连同物体,那么这个物体的边界仍然位于顶层
    cv2.RETR_TREE 建立一个等级树结构轮廓

    method:轮廓的近似方法

    参数 含义
    cv2.CHAIN_APPROX_NONE 存储所有的轮廓点,相邻两个点的像素位置差不超过1
    cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对象线方向的元素,只保留该方向的终点坐标。例如,在极端的情况下,一个矩形只需要用4个点来保存轮廓信息
    cv2.CHAIN_APPROX_TC89_L1 使用teh_Chinl chain近似算法的一种风格
    cv2.CHAIN_APPROX_TC89_KCOS 使用teh_Chinl chain近似算法的一种风格

    contours:返回的轮廓。比如一个图像中有3个不交叉的形状:圆,矩形以及正反向。那么它就是一个长度为3的list,也就是有3个轮廓,contours[i][j]就是第i个轮廓的第j个点。

    当然,图像的轮廓并不一定是不交叉的,假如几个形状的轮廓相互交叉,那么就形成了父子关系,contours[i]用4个元素来说明轮廓的层次关系。

    1. Next:后一个轮廓的索引编号
    2. Previous:前一个轮廓的索引编号
    3. First_Child:第一个子轮廓的索引编号
    4. Parent:父轮廓的索引编号

    如果上面参数都为空,也就是前面讲的各个形状轮廓不交叉的情况。且值为“-1”。

    特别注意,轮廓的层次结构是由参数mode决定的,也就是说,使用不同的mode,得到的轮廓编号是不一样的,hierarchy也不一样。

    hierarchy:图像的拓扑信息

    其返回值为3个参数:image,contours以及hierarchy。而在OpenCV4.X版本中,该函数返回值只有两个:contours,hierarchy。

    下面,我们重点讲解参数mode。

    参数mode

    (1) cv2.RETR_EXTERNAL (只检测外轮廓)

    1.png

    假如我们现在我们图像如上图所示,当我们采用 cv2.RETR_EXTERNAL参数时,hierarchy的拓扑信息为:

    [[
    [1 -1 -1 -1]
    [-1 0 -1 -1]
    ]]

    [1 -1 -1 -1]这4个参数分别对应contours的4个元素。

    Next1:也就是表示它后一个轮廓,这里0的后一个轮廓为1

    Previous-1:第0个轮廓没有前一个轮廓

    First_Child-1:0它不存在子轮廓

    Parent-1:0它也不存在父轮廓

    [-1 0 -1 -1]对应图像1,意思分别如下:

    -1 :1轮廓不存在下一个轮廓

    0:第1个轮廓有前一个0轮廓,所以为0

    -1 :1它不存在子轮廓

    -1:1它也不存在父轮廓

    其拓扑结构如下:


    11.png

    (2) cv2.RETR_LIST(对检测到的轮廓不建立等级关系)


    2.png

    假设我们使用原图如上所示,那么hierarchy的值为:

    [[
    [1 -1 -1 -1]
    [2 0 -1 -1]
    [-1 1 -1 -1]
    ]]

    [1 -1 -1 -1]含义:

    1 :0轮廓的下一个轮廓是1,故返回1

    -1:第0个轮廓没有前轮廓,所以为-1

    -1 :0它不存在子轮廓

    -1:0它也不存在父轮廓

    [2 0 -1 -1]含义:

    2:1轮廓的下一个轮廓是2,故返回2

    0:第1个轮廓前轮廓为0,所以值为0

    -1 :1它不存在子轮廓

    -1:1它也不存在父轮廓

    [-1 1 -1 -1]含义:

    -1:2轮廓没有下一个轮廓,故返回-1

    1:第2个轮廓前轮廓为1,所以值为1

    -1 :2它不存在子轮廓

    -1:2它也不存在父轮廓

    此关系的拓扑结构为:


    22.png

    (3) cv2.RETR_CCOMP(建立两个等级的轮廓)

    3.png

    当mode参数为 cv2.RETR_CCOMP,原图如上所示,其hierarchy值为:

    [[
    [1 -1 -1 -1]
    [-1 0 2 -1]
    [-1 -1 -1 1]
    ]]

    [1 -1 -1 -1]含义如下:

    1:0轮廓的下一个轮廓为1,故返回1

    -1:第0个轮廓没有前轮廓,所以值为-1

    -1 :0它不存在子轮廓

    -1:0它也不存在父轮廓

    [-1 0 2 -1]含义如下:

    -1:1轮廓没有下一个轮廓,故返回-1

    0:第1个轮廓前轮廓为0,所以值为0

    2 :第1个轮廓的子轮廓为2,所以值为2

    -1:第1个轮廓它也不存在父轮廓

    [-1 -1 -1 1]含义如下:

    -1:2轮廓没有下一个轮廓,故返回-1

    -1:第2个轮廓前轮廓不存在,所以值为-1

    -1:第2个轮廓也不存在子轮廓

    1:第2个轮廓父轮廓为1,所以取值为1

    其拓扑结构如下:


    33.png

    (4) cv2.RETR_TREE(建立一个等级树结构轮廓)

    当图还是(3)图时,其mode取值为 cv2.RETR_TREE,返回的hierarchy的值为:

    [[
    [1 -1 -1 -1]
    [-1 0 2 -1]
    [-1 -1 -1 1]
    ]]

    [1 -1 -1 -1]含义如下:

    1:0轮廓下一个轮廓为1,故返回1

    -1:第0个轮廓前轮廓不存在,所以值为-1

    -1:第0个轮廓也不存在子轮廓

    -1:第0个轮廓也不存在父轮廓

    [-1 0 2 -1]含义如下:

    -1:1轮廓没有下一个轮廓,故返回-1

    0:第1个轮廓前轮廓为0,所以值为0

    2:第1个轮廓存在子轮廓2,所以取值为2

    -1:第1个轮廓不存在父轮廓

    [-1 -1 -1 1]含义如下:

    -1:2轮廓没有下一个轮廓,故返回-1

    -1:第2个轮廓没有前轮廓,所以值为-1

    -1:第2个轮廓不存在子轮廓

    1:第2个轮廓存在父轮廓1,取值为1

    拓扑结构也是(3)图。

    绘制图像轮廓

    在OpenCV中,它给我们提供了cv2.drawContours()来绘制图像轮廓,其完整定义如下:

    def drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None):
    

    image:待绘制轮廓的图像

    contours:需要绘制的轮廓

    contourIdx:需要绘制的边缘索引,-1表示绘制所有,0以及正数表示绘制对应的轮廓。

    color:绘制的颜色

    thickness:绘制轮廓的画笔,取值为“-1”表示绘制实心轮廓

    lineType:绘制轮廓的线型

    hierarchy:对应函数cv2.findContours()所输出的层次信息

    maxLevel:控制所绘制的轮廓层次的深度,如果值为0表示只绘制第0层

    offset:偏移参数,是绘制的轮廓偏移一定的位置

    下面,我们来实战绘制图像的轮廓,具体代码如下所示:

    import cv2
    
    img = cv2.imread("24.jpg")
    cv2.imshow("img", img)
    # 转换为灰度图像
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 转换为二值图
    ret, binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
    # 获取图像的轮廓参数
    contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    result = cv2.drawContours(img, contours, -1, (0, 0, 255), 5)
    
    cv2.imshow("result", result)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    运行之后,效果如下:


    4.png

    不过这种并不是终极目的,我们获取轮廓的目的是将图像分离,也就是我现在向获取这3个轮廓的单一子图怎么办呢?直接上代码吧。

    import cv2
    import numpy as np
    
    img = cv2.imread("24.jpg")
    cv2.imshow("img", img)
    # 转换为灰度图像
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 转换为二值图
    ret, binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
    # 获取图像的轮廓参数
    contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    result = []
    for i in range(len(contours)):
        temp = np.zeros(img.shape, dtype=np.uint8)
        result.append(temp)
        result[i] = cv2.drawContours(result[i], contours, i, (0, 0, 255), 5)
        cv2.imshow(str(i), result[i])
    
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    运行之后,我们就能分离出各个图形了。


    5.png

    图像轮廓结合按位与获取图像前景对象

    除了分离纯粹的图像轮廓用于机器学习识别之外,我们还可以获取图像的前景对象。这里,我们只需要更换图像,修改最后几行代码。

    具体代码如下所示:

    import cv2
    import numpy as np
    
    img = cv2.imread("4.jpg")
    cv2.imshow("img", img)
    # 转换为灰度图像
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 转换为二值图
    ret, binary = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
    # 获取图像的轮廓参数
    contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    
    mask = np.zeros(img.shape, dtype=np.uint8)
    mask = cv2.drawContours(mask, contours, -1, (255, 255, 255), -1)
    cv2.imshow("MASK",mask)
    result=cv2.bitwise_and(img,mask)
    cv2.imshow("result",result)
    
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    运行之后,效果如下所示:


    6.png

    最左边的是原始图像

    中间的是从原始图像得到的人物实心轮廓

    最右边的是提取的前景对象人物

    相关文章

      网友评论

        本文标题:查找并绘制轮廓---OpenCV-Python开发指南(24)

        本文链接:https://www.haomeiwen.com/subject/ydkelltx.html