【opencv学习】【轮廓检测】

今天学习轮廓检测方法

import cv2
import numpy as np


# 展示图像,封装成函数
def cv_show_image(name, img):
    cv2.imshow(name, img)
    cv2.waitKey(0)  # 等待时间,单位是毫秒,0代表任意键终止
    cv2.destroyAllWindows()

# 边缘检测的函数
# cv2.findContours(img, mode, method)
#       img: 输入图像,为了很高的精确度,建议使用二值图像
#       mode: RETR_EXTERNAL(只检测外部轮廓)、
#             RETR_LIST(检测所有轮廓并把结果保留到一个List中)
#             RETR_CCOMP,检测所有轮廓,并把结果包装成两层,第一层是各部分外部轮廓,第二层是空洞内部边界
#             RETR_TREE, 检测所有的轮廓,并重构嵌套轮廓的层次
# RETR_LIST 从解释的角度来看,这中应是最简单的。它只是提取所有的轮廓,而不去创建任何父子关系。
# RETR_EXTERNAL 如果你选择这种模式的话,只会返回最外边的的轮廓,所有的子轮廓都会被忽略掉。
# RETR_CCOMP 在这种模式下会返回所有的轮廓并将轮廓分为两级组织结构。
# RETR_TREE 这种模式下会返回所有轮廓,并且创建一个完整的组织结构列表。它甚至会告诉你谁是爷爷,爸爸,儿子,孙子等。
#       methord: 轮廓检测的方法
#             CHAIN_APPROX_NONE: 以freeman链码的方式输出轮廓,
#             CHAIN_APPROX_SIMPLE: 只保留终点部分,也就是对水平和垂直和倾斜的直线只保存终点。数据更少更精简。


img = cv2.imread('images/contour.png')  # 转成灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, threshold = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)  # 转成二值图像
cv_show_image('binary_src_img', threshold)

# contours,所有的轮廓信息,是一个list结构
# hierarchy 是一个层级结构,也是保留所有结果的一个结构
contours, hierarchy = cv2.findContours(threshold, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
print(len(contours))
print(type(contours))

draw_img = img.copy()  # 拷贝一个图像出来,保留原始图像
# -1 表示全部轮廓都画出来,后面俩参数是轮廓的颜色和粗细,drawContours会在输入的图像上原地修改
res = cv2.drawContours(draw_img, contours, contourIdx=-1, color=(0, 255, 0), thickness=1)
cv_show_image('draw_contours_img', res)


# 轮廓的特征
for i in range(len(contours)):
    cont = contours[i]
    print('第{}轮廓的面积是{},周长是{}', i, cv2.contourArea(cont), cv2.arcLength(cont, True))  # true封闭的


# 轮廓近似
# 存在一个曲线从点A到点B,存在连线AB,曲线上存在一个点C,C到直线AB的距离d最大。如果d <= 某个阈值,那么我们可以使用AB直线直接代替曲线AB
# 如果d > 某个阈值,那么想换个曲线是不能使用AB直线代替的,那么这时候就得把曲线AB继续划分成两段,分别是曲线AC和曲线CB。进一步尝试
# 进一步尝试,看看这个曲线是否能被直线AC和直线CB代替。这个是个分治/递归的求解了。
draw_img = img.copy()
for i in range(len(contours)):
    cont = contours[i]
    epsilon = 0.03 * cv2.arcLength(cont, True)
    approx = cv2.approxPolyDP(cont, epsilon, True)  # 求轮廓的近似,True表示封闭的

    # 画出近似的轮廓,传入给drawContours的轮廓类型必须是list
    draw_img = cv2.drawContours(draw_img, [approx], contourIdx=-1, color=(0, 255, 0), thickness=1)

cv_show_image('approx_contours_img', draw_img)



# 外界矩形
draw_img = img.copy()
for i in range(len(contours)):
    cont = contours[i]
    x,y,w,h = cv2.boundingRect(cont)  # 根据轮廓求出外接最远的矩形坐标。

    draw_img = cv2.rectangle(draw_img, pt1=(x,y), pt2=(x+w, y+h), color=(0, 255, 0), thickness=2)  # 画出这个矩形,会在原图上画

cv_show_image('rectangle_contours_img', draw_img)



# 外接圆
draw_img = img.copy()
for i in range(len(contours)):
    cont = contours[i]
    (x,y), radius = cv2.minEnclosingCircle(cont)  # 根据轮廓求出外接最远的圆形圆心和半径。
    center = (int(x), int(y))
    radius = int(radius)

    draw_img = cv2.circle(draw_img, center=center, radius=radius, color=(0, 255, 0), thickness=2)  # 画出这个矩形,会在原图上画

cv_show_image('circle_contours_img', draw_img)


原始图像如下:
请添加图片描述
画出所有轮廓:
轮廓有内部轮廓和外部轮廓,如果使用mode = RETR_EXTERNAL 可只画出外轮廓
请添加图片描述

画出所有近似轮廓:
请添加图片描述
画出所有轮廓的外接最大矩形

请添加图片描述

画出所有轮廓的外接最大圆形

请添加图片描述

猜你喜欢

转载自blog.csdn.net/qq_29367075/article/details/122911622