# 一、轮廓检测基础理论

# 1、轮廓概述

边缘和轮廓区别:边缘是零散的点,轮廓是整体。

在二值图中找轮廓。

# 2、API 介绍

# 1、cv.findContours 函数(查找轮廓)

contours, hierarchy = cv2.findContours(img,mode,method)

** 参数: **

图 1

图 2

** 返回:**
contours:轮廓
hierarchy:层级

# 1、根据二值图找到轮廓
    contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
    # 轮廓      层级                               轮廓检索模式 (推荐此)  轮廓逼近方法

# 2、cv.drawContours 函数(画出轮廓)

# 2、画出轮廓
    dst = cv.drawContours(img, contours, -1,                (0, 0, 255), 3)
    #                           轮廓     第几个 (默认 - 1:所有)   颜色       线条厚度

#

# 检测轮廓并画出:(用二值图检测轮廓)

# 提取轮廓
def GetGontours():
    # 1、根据二值图找到轮廓
    contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
    # 轮廓      层级                               轮廓检索模式 (推荐此)  轮廓逼近方法
    # 2、画出轮廓
    dst = cv.drawContours(img, contours, -1,                (0, 0, 255), 3)
    #                           轮廓     第几个 (默认 - 1:所有)   颜色       线条厚度
    cv.imshow('dst', dst)

# 二、代码及效果

# 轮廓提取
import cv2 as cv
# 转二进制图像
def ToBinray():
    global imgray, binary
    # 1、灰度图
    imgray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    cv.imshow('imgray', imgray)
    # 2、二进制图像
    ret, binary = cv.threshold(imgray, 127, 255, 0)
    # 阈值 二进制图像
    cv.imshow('binary', binary)
# 提取轮廓
def GetGontours():
    # 1、根据二值图找到轮廓
    contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    # 轮廓      层级                               轮廓检索模式 (推荐此)  轮廓逼近方法
    # 2、画出轮廓
    dst = cv.drawContours(img, contours, -1,                (0, 0, 255), 3)
    #                           轮廓     第几个 (默认 - 1:所有)   颜色       线条厚度
    cv.imshow('dst', dst)
if __name__ == '__main__':
    img = cv.imread('Resource/test11.jpg')
    cv.imshow('img', img)
    ToBinray()          #转二进制
    GetGontours()       #提取轮廓
    cv.waitKey(0)

图 3

图 4

图 5

图 6

图 7

# 三、轮廓检测的属性

# 1、画出单个轮廓

# 画出第一个轮廓
    cnt = contours[0]
    dst = cv.drawContours(img, cnt, -1, (0, 0, 255), 3)
    cv.imshow('dst1', dst)

图 8

图 9

# 2、显示面积和周长

# 获取轮廓面积
    area = cv.contourArea(cnt)
    print("轮廓面积:", area)
    # 周长(True 表示合并)
    perimeter = cv.arcLength(cnt, True)
    print("轮廓周长:", perimeter)

# 代码及效果

# 获取轮廓信息
def GetContours_Attrib():
    # 画出第一个轮廓
    cnt = contours[0]
    dst = cv.drawContours(img, cnt, -1, (0, 0, 255), 3)
    cv.imshow('dst1', dst)
    # 获取轮廓面积
    area = cv.contourArea(cnt)
    print("轮廓面积:", area)
    # 周长(True 表示合并)
    perimeter = cv.arcLength(cnt, True)
    print("轮廓周长:", perimeter)

图 10

# 四、近似轮廓

# 1、步骤

1、获取轮廓外围

2、设置精度(从轮廓到近似轮廓的最大距离)

3、获取近似轮廓

4、绘制轮廓

# 2、API

# 2、设置精度(从轮廓到近似轮廓的最大距离)
    epsilon = 0.03 * cv.arcLength(cnt, True)
    #                            轮廓  闭合轮廓还是曲线
# 3、获取近似轮廓
    approx = cv.approxPolyDP(cnt, epsilon,          True)
    #                             近似度 (这里为 10%)   闭合轮廓还是曲线

# 3、实现

# 轮廓近似
def GetApprox():
    # 1、取外围轮廓
    cnt = contours[0]
    # 2、设置精度(从轮廓到近似轮廓的最大距离)
    epsilon = 0.05 * cv.arcLength(cnt, True)
    #                            轮廓  闭合轮廓还是曲线
    # 3、获取近似轮廓
    approx = cv.approxPolyDP(cnt, epsilon,          True)
    #                             近似度 (这里为 5%)   闭合轮廓还是曲线
    # 4、绘制轮廓
    draw_img = img.copy()
    res = cv.drawContours(draw_img, [approx], -1, (0, 0, 255), 3)
    # 显示
    cv.imshow("res", res)

#

# 各精度的近似轮廓:

精度 epsilon=0.01 时的近似轮廓:

图 11

精度 epsilon=0.02 时的近似轮廓:

图 12
精度 epsilon=0.03 时的近似轮廓:

图 13

精度 epsilon=0.04 时的近似轮廓:

图 14

精度 epsilon=0.05 时的近似轮廓:

图 15

# 五、边界矩形和外接圆

边界矩形:根据坐标、长宽绘制矩形。
外接圆:根据圆心坐标、半径绘制圆。

# 1、边界矩形

# 获取边界矩形
def BoundingRect():
    # 1、取外围轮廓
    cnt = contours[0]
    # 2、获取正方形坐标长宽
    x, y, w, h = cv.boundingRect(cnt)
    # 3、画出矩形
    dst = img.copy()
    dst = cv.rectangle(dst, (x,y),(x+w,y+h), (0,0,255), 3)
    # 显示
    cv.imshow("dst", dst)

图 16

# 2、外接圆

# 获取外接圆
def Circle():
    # 1、获取第一个轮廓
    cnt = contours[0]
    # 2、获取外接圆
    (x, y), radius = cv.minEnclosingCircle(cnt)
    # 坐标   半径
    # 3、画圆
    dst = img.copy()
    dst = cv.circle(dst, (int(x), int(y)), int(radius), (0, 0, 255), 3)
    # 显示
    cv.imshow("dst", dst)

图 17

# 总代码

# 轮廓提取、属性、近似轮廓、边界矩形和外接圆
import cv2 as cv
# 转二进制图像
def ToBinray():
    global imgray, binary
    # 1、灰度图
    imgray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    cv.imshow('imgray', imgray)
    # 2、二进制图像
    ret, binary = cv.threshold(imgray, 127, 255, 0)
    # 阈值 二进制图像
    cv.imshow('binary', binary)
# 提取轮廓
def GetContours():
    global contours
    # 1、根据二值图找到轮廓
    contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
    # 轮廓      层级                               轮廓检索模式 (推荐此)  轮廓逼近方法
    # 2、画出轮廓
    dst = img.copy()
    dst = cv.drawContours(dst, contours, -1,                (0, 0, 255), 3)
    #                           轮廓     第几个 (默认 - 1:所有)   颜色       线条厚度
    cv.imshow('contours', dst)
# 获取轮廓信息
def GetContours_Attrib():
    # 画出第一个轮廓
    cnt = contours[0]
    dst = img.copy()
    dst = cv.drawContours(dst, cnt, -1, (0, 0, 255), 3)
    cv.imshow('contour0', dst)
    # 获取轮廓面积
    area = cv.contourArea(cnt)
    print("轮廓面积:", area)
    # 周长(True 表示合并)
    perimeter = cv.arcLength(cnt, True)
    print("轮廓周长:", perimeter)
# 轮廓近似
def GetApprox():
    # 1、取外围轮廓
    cnt = contours[0]
    # 2、设置精度(从轮廓到近似轮廓的最大距离)
    epsilon = 0.01 * cv.arcLength(cnt, True)
    #                            轮廓  闭合轮廓还是曲线
    # 3、获取近似轮廓
    approx = cv.approxPolyDP(cnt, epsilon,          True)
    #                             近似度 (这里为 5%)   闭合轮廓还是曲线
    # 4、绘制轮廓
    dst = img.copy()
    dst = cv.drawContours(dst, [approx], -1, (0, 0, 255), 3)
    # 显示
    cv.imshow("apporx", dst)
# 获取边界矩形
def BoundingRect():
    # 1、取外围轮廓
    cnt = contours[0]
    # 2、获取正方形坐标长宽
    x, y, w, h = cv.boundingRect(cnt)
    # 3、画出矩形
    dst = img.copy()
    dst = cv.rectangle(dst, (x,y),(x+w,y+h), (0,0,255), 3)
    # 显示
    cv.imshow("rect", dst)
# 获取外接圆
def Circle():
    # 1、获取第一个轮廓
    cnt = contours[0]
    # 2、获取外接圆
    (x, y), radius = cv.minEnclosingCircle(cnt)
    # 坐标   半径
    # 3、画圆
    dst = img.copy()
    dst = cv.circle(dst, (int(x), int(y)), int(radius), (0, 0, 255), 3)
    # 显示
    cv.imshow("circle", dst)
if __name__ == '__main__':
    img = cv.imread('Resource/contour.jpg')
    cv.imshow('img', img)
    ToBinray()              #转二进制
    GetContours()           #提取轮廓
    GetContours_Attrib()    #获取轮廓信息
    GetApprox()             #轮廓近似
    BoundingRect()          #边界矩形
    Circle()                #外接圆
    cv.waitKey(0)

# 参考资料

https://www.bilibili.com/video/BV1PV411774y?p=25

http://woshicver.com/FifthSection/4_9_2_%E8%BD%AE%E5%BB%93%E7%89%B9%E5%BE%81/