针对物体轮廓,opencv还提供了一些相关的函数,来处理轮廓查找,绘制,拟合,以及计算轮廓周长和面积等,详细介绍如下:

1. 寻找和绘制轮廓

  opencv的findContours()能寻找图片中的轮廓,实现的是下面论文的算法:

    Satoshi Suzuki and others. Topological structural analysis of digitized binary images by border following. Computer Vision, Graphics, and Image Processing, 30(1):32–46, 1985.

  函数对应的参数如下: 

contours, hierarchy    =cv2.findContours(image, mode, method, offset=None)
    image: 单通道的二值图(若输入灰度图,非零像素点会被设成1,0像素点设成0)
    
    mode: 轮廓检索模式,包括RETR_EXTERNAL,RETR_LIST,RETR_CCOMP,RETR_TREE,RETR_FLOODFILL
        RETR_EXTERNAL: 只检索最外层的轮廓 (返回值会设置所有hierarchy[i][2]=hierarchy[i][3]=-1)
        RETR_LIST: 检索所有的轮廓,但不建立轮廓间的层次关系(hierarchy relationship)
        RETR_CCOMP:  检测所有的轮廓,但只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层,只有内围轮廓不再包含子轮廓时,其为内层。
        RETR_TREE:检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
        RETR_FLOODFILL:
        
    method: 轮廓的近似方法,包括CHAIN_APPROX_NONE,CHAIN_APPROX_SIMPLE,CHAIN_APPROX_TC89_L1,CHAIN_APPROX_TC89_KCOS
        CHAIN_APPROX_NONE: 保存物体边界上所有连续的轮廓点到contours中,即点(x1,y1)和点(x2,y2),满足max(abs(x1-x2),abs(y2-y1))==1,则认为其是连续的轮廓点
        CHAIN_APPROX_SIMPLE: 仅保存轮廓的拐点信息到contours,拐点与拐点之间直线段上的信息点不予保留
        CHAIN_APPROX_TC89_L1: 采用Teh-Chin chain近似算法 
        CHAIN_APPROX_TC89_KCOS:采用Teh-Chin chain近似算法
        
    offset:偏移量,所有的轮廓信息相对于原始图像的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量(在图片裁剪时比较有用)

    
返回值:
    contours:返回的轮廓点集合,一个list,每一个元素是一个轮廓,轮廓是一个N*1*2的ndarray
    hierarchy: 轮廓之间的层次关系,每一个元素对应contours中相应索引轮廓的层次关系,是一个N*4的array,hierarchy[i][0]~hierarchy[i][3]分别表示第i个轮廓的后一个轮廓,
        前一个轮廓,第一个内嵌轮廓(子轮廓),父轮廓的索引编号,如果当前轮廓没有对应的后一个轮廓、前一个轮廓、内嵌轮廓或父轮廓,则hierarchy[i][0]~hierarchy[i][3]的相应位被设置为默认值-1

  Teh-Chin chain近似算法: C-H Teh and Roland T. Chin. On the detection of dominant points on digital curves. Pattern Analysis and Machine Intelligence, IEEE Transactions on, 11(8):859–872, 1989.

 

  opencv还提供drawContours()函数来绘制检测到的轮廓,其对应参数如下:

image=cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None

    image: 绘制的轮廓的图像矩阵
    contours: 所有的轮廓集合(findContours()返回值)
    contourIdx: 轮廓集合的索引,表示指定一个轮廓进行绘制;若为负数,表示绘制所有轮廓
    color: 绘制使用的颜色
    thickness:线的粗细
    lineType: 线的类型,包括FILLED,LINE_4,LINE_8,LINE_AA
    hierarchy: 轮廓的层次关系(findContours()返回值)
    maxLevel: 0表示只有指定的轮廓被绘制,1表示绘制指定的轮廓和其第一层内嵌轮廓,2表示绘制指定轮廓和其所有的内嵌轮廓(只有hierarchy部位None时,才起作用)
    offset: 绘制轮廓时的偏移量

一般轮廓寻找和拟合,能解决一些简单的目标定位问题,其大致流程如下:

  • 对图像边缘检测或阈值分割得到二值图(适当的形态学处理)

  • 利用findContours()函数寻找二值图中多个轮廓

  • 对于每一个轮廓采用boundingRect(), minAreaRect()等进行拟合得到目标位置框

findContours()函数使用示例代码及结果如下:

#coding:utf-8

import cv2

img = cv2.imread(r"D:\data\receipt.jpg")
img1 = img.copy()
img2 = img.copy()
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gaussian = cv2.GaussianBlur(img_gray, (3, 3), 1)
edge = cv2.Canny(img_gaussian, 100, 300)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 1))
edge = cv2.dilate(edge, kernel, iterations=2) #横向的形态学膨胀
# thre, edge = cv2.threshold(img_gaussian, 0, 255, cv2.THRESH_OTSU+cv2.THRESH_BINARY)

#寻找轮廓
contours, hierarchy = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img1, contours, -1, (0,0,255))

#轮廓拟合
num = len(contours)
for i in range(num):
    area = cv2.contourArea(contours[i], oriented=False)
    if 30 < area < 8000:  #限定轮廓的面积
        rect = cv2.boundingRect(contours[i])
        print(rect)
        cv2.rectangle(img2, (rect[0], rect[1]), (rect[0]+rect[2], rect[1]+rect[3]), (0, 255, 0))


# cv2.imshow("img_gray", img_gray)
cv2.imshow("img", img)
cv2.imshow("img_contour", img1)
cv2.imshow("img_rect", img2)
cv2.imshow("edge", edge)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.findContours()

相关文章: