图像轮廓检测


一、轮廓概念

轮廓和边缘的区别:

边缘主要是反映梯度的变化,可以是零零散散的线条;
轮廓是一个整体;


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)


posted @ 2021-02-04 11:53  月思  阅读(302)  评论(0编辑  收藏  举报