图像轮廓检测
一、轮廓概念
轮廓和边缘的区别:
边缘主要是反映梯度的变化,可以是零零散散的线条;
轮廓是一个整体;
findContours 方法说明
findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> contours, hierarchy
-
mode:轮廓检索模式
- RETR_EXTERNAL :只检索最外面的轮廓;
- RETR_LIST: 检索所有的轮廓,并将其保存到一条链表当中;
- RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
- RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次; (最常用)
-
method:轮廓逼近方法
- CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
- CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
出参:
- contours:轮廓信息
- hierarchy:层级
实现
import cv2
import numpy as np
import matplotlib.pyplot as plt
lena = cv2.imread('lena.jpg')
gray = cv2.cvtColor(lena, cv2.COLOR_BGR2GRAY)
plt.imshow(gray)
# 二值化;目的为了更好的进行检测
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
plt.imshow(thresh)
计算轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
np.array(contours).shape # (421,)
绘制轮廓
'''
第三个参数:画哪一个轮廓;-1 代表所有轮廓;在有多个轮廓时看起来比较明显。
第四个参数:BGR 颜色模式;
第四个参数:绘制线条的宽度
传入 src 图片不能使用原图,否则会在原图上花轮廓;所以这里使用 copy 复制一份;
'''
img = gray.copy()
res = cv2.drawContours(img, contours, -1, (0,0,255), 2)
plt.imshow(res)
img = gray.copy()
res = cv2.drawContours(img, contours, -1, (0,0,255), 1)
plt.imshow(res)
二、轮廓特征
# 当使用辅助函数计算特征的时候,需要把各个轮廓单独取出来,才能使用
cnt = contours[1]
cnt
array([[[210, 261]],
[[209, 262]],
[[210, 262]],
[[211, 262]],
[[212, 262]],
[[211, 261]]], dtype=int32)
# 面积
cv2.contourArea(cnt) # 2.0
# 周长 True-闭合;
cv2.arcLength(cnt, True) # 6.828427076339722
cv2.arcLength(cnt, False) # 5.828427076339722
三、轮廓近似
A点到B点的曲线,上面一点 C 距离AB直线最远,如果这个距离 d1 小于阈值,则 AB曲线和 AB直线近似;
否则进一步求 AC 和 CB 的近似线;不断二分下去;
使用场景:做 外界矩形、外界圆。
cloud = cv2.imread('pie.png', 0)
plt.imshow(cloud)
# 计算轮廓
contours, hierarchy = cv2.findContours(cloud, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
len(contours) # 3
img1 = cloud.copy()
res = cv2.drawContours(img1, contours, -1, (0,0,255), 4)
plt.imshow(res)
cnt = contours[0]
img2 = cloud.copy()
res = cv2.drawContours(img2, [cnt], -1, (0,0,255), 4)
plt.imshow(res)
近似
epsilon = 0.1 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
cloud1 = cloud.copy()
res = cv2.drawContours(cloud1, [approx], -1, (0, 0, 255), 2)
plt.imshow(res)
边界矩形
x, y, w, h = cv2.boundingRect(cnt)
img3 = cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
plt.imshow(img3)
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
# 轮廓面积与边界矩形比
extent = float(area) / rect_area
外接圆
(x, y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
img = cv2.circle(img, center, radius, (0, 255, 0), 2)