2019-6-23 opencv的轮廓检测函数findContours(使用drawContours绘制)

边缘(edge):不同区域的分界线。图像局部灰度显著变化的部分。一阶导数的局部极大值,二阶导数的过零点。
2019-6-23 opencv的轮廓检测函数findContours(使用drawContours绘制)_第1张图片

轮廓(contour):物体在场景中的完整边界。边缘的连接构成轮廓。

边缘和轮廓关系,网上有一个解释的比较清楚:

  • 一般认为轮廓是对物体的完整边界的描述,边缘点一个个连接起来构成轮廓。边缘可以是一段边缘,而轮廓一般是完整的。

opencv中轮廓检测主要由cv2.findContours函数实现的。

函数定义如下

cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])  
  • image:寻找轮廓的图片对象,8位单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像。
  • mode:轮廓的检索模式
    • cv2.RETR_EXTERNAL表示只检测外轮廓 。
    • cv2.RETR_LIST检测的轮廓不建立等级关系。
    • cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
    • cv2.RETR_TREE建立一个等级树结构的轮廓。
  • method:轮廓的逼近方法
    • cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
    • cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。
    • cv2.CHAIN_APPROX_TC89_L1和cv2.CHAIN_APPROX_TC89_KCOS都是使用teh-Chinl chain近似算法

函数的返回值

image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  • image:是原图像
  • contours:图像的轮廓,以列表的形式表示,每个元素都是图像中的一个轮廓。
  • hierarchy:相应轮廓之间的关系。这是一个ndarray,其中的元素个数和轮廓个数相同,每个轮廓contours[i] 对应4个hierarchy元素hierarchy[i][0] ~ hierarchy[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数。

代码实现,可以看到最基本的轮廓检测过程如下

# -*- coding: cp936 -*-
import cv2
from matplotlib import pyplot as plt

# Step1. 读入图像

image_tmp = cv2.imread('test.jpg',0)

plt.subplot(221), plt.imshow(image_tmp,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]),plt.yticks([])


image_canny = cv2.Canny(image_tmp, 200, 300) # 边缘检测
plt.subplot(224), plt.imshow(image_canny,cmap = 'gray')
plt.title('Edge Image'),plt.xticks([]),plt.yticks([])


# Step2. 二值化
ret, thresh = cv2.threshold(image_tmp, 127, 255, cv2.THRESH_BINARY)

# Step3. 轮廓提取
image, contour, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Step4. 轮廓绘制
color = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
result = cv2.drawContours(color, contour, -1, (0,255,0), 2) #轮廓用绿色绘制
plt.subplot(223),plt.imshow(result)
plt.title('Contour Image'),plt.xticks([]),plt.yticks([])
plt.show()

2019-6-23 opencv的轮廓检测函数findContours(使用drawContours绘制)_第2张图片

左下是轮廓检测效果,右下是边缘检测效果,可以对比一下。
关于drawContours()函数,是用于轮廓的绘制或填充,可以参见https://www.cnblogs.com/gengyi/p/10295407.html

cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]]) -> image
  • image - 目标图像
  • contours - 所有的输入轮廓,每个轮廓为点矢量(a point vector)/点向量 形式,与findcontours中的返回值 contours 的列表list形式一致
  • contourIdx - 指定轮廓列表的索引 ID(将被绘制),若为负数,则所有的轮廓将会被绘制。
  • color - 绘制轮廓的颜色。
  • thickness - 绘制轮廓线条的宽度,若为负值或CV.FILLED则将填充轮廓内部区域
  • lineType - Line connectivity,(有的翻译线型,有的翻译线的连通性)
  • hierarchy - 层次结构信息,与函数findcontours()的hierarchy有
  • maxLevel - 绘制轮廓的最高级别。
    • 0,则绘制指定轮廓
    • 1,则绘制该轮廓和所有嵌套轮廓(nested contours)
    • 2,则绘制该轮廓、嵌套轮廓(nested contours)/子轮廓和嵌套-嵌套轮廓(all the nested-to-nested contours)/孙轮廓,等等。该参数只有在层级结构时才用到。
  • offset - 按照偏移量移动所有的轮廓(点坐标)。

以上获得的是图像的连续轮廓,还可以绘制图像的边界框,最小矩形框(车牌识别会用到),最小闭圆,最小外包三角形
代码如下

# -*- coding: cp936 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt

# Step1. 读入图像

image_tmp = cv2.imread('test2.jpg',0)

plt.subplot(231), plt.imshow(image_tmp,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]),plt.yticks([])

'''
image_canny = cv2.Canny(image_tmp, 200, 300) # 边缘检测
plt.subplot(232), plt.imshow(image_canny,cmap = 'gray')
plt.title('Edge Image'),plt.xticks([]),plt.yticks([])
'''


# Step2. 二值化
ret, thresh = cv2.threshold(image_tmp, 127, 255, cv2.THRESH_BINARY)

# Step3. 轮廓提取
image, contour, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Step4. 轮廓绘制
color= cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
result = cv2.drawContours(color, contour, -1, (0,255,0), 2)
plt.subplot(232),plt.imshow(result)
plt.title('Contour Image'),plt.xticks([]),plt.yticks([])

color_rectangle = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
color_rectangle_min = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
color_circle = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
color_triangle = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)

print len(contour)
for c in contour:
    # 绘制边界框
    x,y,w,h = cv2.boundingRect(c)   # 将轮廓信息转化为(x,y)坐标,并返回宽和高

    # rectangle有四个参数:要绘制的图像,左上角坐标,右下角坐标,颜色,轮廓宽度(这里为2)
    result_rectangle=cv2.rectangle(color_rectangle, (x, y), (x + w, y + h), (0, 255, 0), 2)

    # 绘制最小矩形区域    
    rect = cv2.minAreaRect(c)
    box = cv2.boxPoints(rect) # 获得矩形四个角的坐标, 返回浮点型
    box = np.int0(box) # 把浮点型转化为整型
    result_rectangle_min=cv2.drawContours(color_rectangle_min, [box], 0, (0, 255, 0), 3)
    
    
    # 绘制最小闭圆
    (x, y), radius = cv2.minEnclosingCircle(c)
    center = (int(x), int(y))
    radius = int(radius)
    result_circle=cv2.circle(color_circle, center, radius, (255, 0 , 0), 3)

    
    # 绘制最小外包三角形
    retval, points = cv2.minEnclosingTriangle(c)

    for i in range(0, 3):
        # 这里使用了一个小技巧, 从i 绘制到 (i + 1) % 3
        result_triangle=cv2.line(color_triangle, tuple(points[i][0]), tuple(points[(i + 1) % 3][0]), (0, 0, 255), 2) 
    

plt.subplot(233),plt.imshow(color_rectangle)
plt.title('Rectangle'),plt.xticks([]),plt.yticks([])

    
plt.subplot(234),plt.imshow(color_rectangle_min)
plt.title('Rectangle-min'),plt.xticks([]),plt.yticks([])

plt.subplot(235),plt.imshow(result_circle)
plt.title('Circle'),plt.xticks([]),plt.yticks([])

plt.subplot(236),plt.imshow(result_triangle)
plt.title('Triangle'),plt.xticks([]),plt.yticks([])


plt.show()


2019-6-23 opencv的轮廓检测函数findContours(使用drawContours绘制)_第3张图片

原始图像里有2个7边形,1个比较正,1个是斜的。可以更好的体会一下边界框和最小矩形的区别(右上和左下)。顺便打印了一下识别出的轮廓(contours )数量,结果有5个。

你可能感兴趣的:(IT,opencv)