Opencv-Python学习笔记(九):轮廓检测

本篇博客记录学习OpenCV图像处理中的轮廓检测

  • 理解什么是轮廓。
  • 学习找轮廓,绘制轮廓等。
  • 学习以下两个函数:cv2.findContours(),cv2.drawContours(),

一、什么是轮廓

轮廓可以简单地认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。

  • 为了获得更高的准确性,要使用二进制图像。因此,在找到轮廓之前,要进行阈值化处理或Canny边缘检测。
  • findContours函数会修改源图像。如果在找到轮廓之后还想使用原始图像的话,应该将原始图像存储到其他变量中。
  • 在OpenCV中,找到轮廓就像从黑色背景中找到白色物体。因此请记住,要找到的对象应该是白色,背景应该是黑色。

我们如何在一个二值图像中查找轮廓呢,当然是cv2.findContours()啦:

该函数有三个参数,第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。返回值有三个,第一个是图像,第二个是轮廓,第三个是(轮廓的)层析结构。轮廓(第二个返回值)是一个Python列表,其中存储这图像中的所有轮廓。每一个轮廓都是一个Numpy 数组,包含对象边界点(x,y)的坐标。

轮廓检索模式:

  • CV_RETR_EXTERNAL=0:只检索最外面的轮廓;
  • CV_RETR_LIST=1:检索所有的轮廓,并将其保存到一条链表中;
  • CV_RETR_CCOMP=2:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
  • CV_RETR_TREE=3:检索所有的轮廓,并重构嵌套轮廓的整个层次。

我们经常用的是最后标红的一种。

轮廓近似方法(对所有节点, 不包括使用内部逼近的 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 提取模式可以在本方法中应用

二、如何绘制轮廓

函数cv2.drawContours() 可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。它的第一个参数是原始图像,第二个参数是轮廓,一个Python 列表。第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设置为-1 时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度等。

示例代码:

# -*- coding: utf-8 -*-
# @Time    : 2019/10/27 9:19
# @Author  : MMagicLoren
# @Email   : [email protected]
# @File    : 轮廓检测.py
# @Software: PyCharm
import cv2 as cv
import numpy as np


def contours_demo(image):
    # dst = cv.GaussianBlur(image, (3, 3), 0)
    gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
    ret, thresh = cv.threshold(gray, 127, 255, cv.THRESH_BINARY)
    # cv.imshow("thresh image", thresh)
    contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)  # OpenCV 新版调用,返回两个参数
    draw_img = src.copy()  # drawContours函数返回值会将原图像覆盖,因此在做处理时先copy一份原图像
    res = cv.drawContours(draw_img, contours, -1, (0, 0, 255), 2)  # 颜色通道为BGR
    cv.imshow("res", res)


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/contours.png")  # 读入图片放进src中
    cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    cv.imshow("input image", src)
    contours_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()

Opencv-Python学习笔记(九):轮廓检测_第1张图片

res = cv.drawContours(draw_img, contours, 0, (0, 0, 255), 2)  # 颜色通道为BGR,将这行代码的第三个参数改为0,是以下结果,是六边形的外部,(在不越界的情况下,可以修改其他值看看效果):

Opencv-Python学习笔记(九):轮廓检测_第2张图片

接下来我们换一张图片进行测试:

Opencv-Python学习笔记(九):轮廓检测_第3张图片

如果我们直接用上面的程序进行测试,会发现如下结果,找不到路轮廓:

Opencv-Python学习笔记(九):轮廓检测_第4张图片

这个时候就需要我们修改一些参数。

修改代码如下:

ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)

contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)  # OpenCV 新版调用,返回两个参数
运行结果如下:

Opencv-Python学习笔记(九):轮廓检测_第5张图片

我们会发现这个结果不是我们想要的,我们加入一个高斯滤波,将一些噪点去除,修改代码如下:

# -*- coding: utf-8 -*-
# @Time    : 2019/10/27 9:19
# @Author  : MMagicLoren
# @Email   : [email protected]
# @File    : 轮廓检测.py
# @Software: PyCharm
import cv2 as cv
import numpy as np


def contours_demo(image):
    dst = cv.GaussianBlur(image, (1, 1), 0)
    gray = cv.cvtColor(dst, cv.COLOR_BGR2GRAY)
    ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
    # cv.imshow("thresh image", thresh)
    contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)  # OpenCV 新版调用,返回两个参数
    draw_img = src.copy()  # drawContours函数返回值会将原图像覆盖,因此在做处理时先copy一份原图像
    res = cv.drawContours(draw_img, contours, -1, (0, 0, 255), 2)  # 颜色通道为BGR
    cv.imshow("res", res)


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/circle.png")  # 读入图片放进src中
    cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    cv.imshow("input image", src)
    contours_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()

运行结果:

Opencv-Python学习笔记(九):轮廓检测_第6张图片

如果我们将这行代码的最后一个参数改为-1,他会进行填充。

res = cv.drawContours(draw_img, contours, -1, (0, 0, 255), -1)  # 颜色通道为BGR

Opencv-Python学习笔记(九):轮廓检测_第7张图片

接下来我们用我们之前学过的Canny检测来提取二值化图像看看效果。

# -*- coding: utf-8 -*-
# @Time    : 2019/10/27 9:19
# @Author  : MMagicLoren
# @Email   : [email protected]
# @File    : 轮廓检测.py
# @Software: PyCharm
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):
    """
    dst = cv.GaussianBlur(image, (1, 1), 0)
    gray = cv.cvtColor(dst, cv.COLOR_BGR2GRAY)
    ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
    # cv.imshow("thresh image", thresh)
    """
    thresh = edge_demo(image)
    contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)  # OpenCV 新版调用,返回两个参数
    draw_img = src.copy()  # drawContours函数返回值会将原图像覆盖,因此在做处理时先copy一份原图像
    res = cv.drawContours(draw_img, contours, -1, (0, 0, 255), 2)  # 颜色通道为BGR
    cv.imshow("res", res)


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/circle.png")  # 读入图片放进src中
    cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    cv.imshow("input image", src)
    contours_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()

Opencv-Python学习笔记(九):轮廓检测_第8张图片

我们可以修改一下参数达到更好的效果。

# -*- coding: utf-8 -*-
# @Time    : 2019/10/27 9:19
# @Author  : MMagicLoren
# @Email   : [email protected]
# @File    : 轮廓检测.py
# @Software: PyCharm
import cv2 as cv
import numpy as np


def edge_demo(image):
    blurred = cv.GaussianBlur(image, (7, 7), 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(gray, 150, 255)
    return edge_output


def contours_demo(image):
    """
    dst = cv.GaussianBlur(image, (1, 1), 0)
    gray = cv.cvtColor(dst, cv.COLOR_BGR2GRAY)
    ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
    # cv.imshow("thresh image", thresh)
    """
    thresh = edge_demo(image)
    contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)  # OpenCV 新版调用,返回两个参数
    draw_img = src.copy()  # drawContours函数返回值会将原图像覆盖,因此在做处理时先copy一份原图像
    res = cv.drawContours(draw_img, contours, -1, (0, 0, 255), 2)  # 颜色通道为BGR
    cv.imshow("res", res)


if __name__ == '__main__':
    src = cv.imread("F:/Pycharm/opencv_exercises-master/images/circle.png")  # 读入图片放进src中
    cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)  # 创建窗口, 窗口尺寸自动调整
    cv.imshow("input image", src)
    contours_demo(src)
    cv.waitKey(0)  # 等有键输入或者1000ms后自动将窗口消除,0表示只用键输入结束窗口
    cv.destroyAllWindows()

Opencv-Python学习笔记(九):轮廓检测_第9张图片

今天就分享到这了,下次分享轮廓检测的特征。

你可能感兴趣的:(Python+opencv4,Python3.7,Pycharm2019,OpenCV4)