今天学习轮廓检测方法
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 可只画出外轮廓
画出所有轮廓的外接最大圆形