目录
图像轮廓检测
轮廓的查找
轮廓的绘制
轮廓的特征
面积
周长
根据面积显示特定轮廓
轮廓的近似
给定轮廓的最小外接圆、外接矩形
外接圆
外接矩形
图像轮廓检测
轮廓的查找
API函数
image, contours, hierarchy = cv2.findContours(img, mode, method)
代入参数含义:
img:需要实现轮廓检测的原图
mode: 轮廓的检索模式,主要有四种方式:
cv2.RETR_EXTERNAL:只检测外轮廓,所有子轮廓被忽略
cv2.RETR_LIST:检测的轮廓不建立等级关系,所有轮廓属于同一等级
cv2.RETR_CCOMP:返回所有的轮廓,只建立两个等级的轮廓。一个对象的外轮廓为第1级组织结构。
而对象内部中空洞的轮廓为第2级组织结构,空洞中的任何对象的轮廓又是第 1 级组织结构。
-> cv2.RETR_TREE:返回所有的轮廓,建立一个完整的组织结构的轮廓。
method:轮廓的近似方法,主要有以下两种:
-> cv2.CHAIN_APPROX_NONE:存储所有的轮廓点。
cv2.CHAIN_APPROX_SIMPLE:压缩模式,只保留该方向的终点坐标,
例如一个矩形轮廓只需4个点来保存轮廓信息。
返回值
image:返回处理的原图
contours:包含图像中所有轮廓的list对象。
其中每一个独立的轮廓信息以边界点坐标(x,y)的形式储存在numpy数组中。
hierarchy:轮廓的层次结构。一个包含4个值的数组:[Next, Previous, First Child, Parent]
Next:与当前轮廓处于同一层级的下一条轮廓
Previous:与当前轮廓处于同一层级的上一条轮廓
First Child:当前轮廓的第一条子轮廓
Parent:当前轮廓的父轮廓
注意:做轮廓检测前需要将图片读取为二值数据,即像素值只为0和255。
获取给定图像phone.png中物体的轮廓信息,包括轮廓的数量和层次结构,以便进行后续的图像处理。
import cv2
# 读取原图
phone = cv2.imread('phone.png')
# 对原图进行灰度处理
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
# 对灰度图进行阈值处理,得到二值图像
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
# 查找二值图像中的轮廓
_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 打印轮廓的层次结构
print(hierarchy)
# 打印轮廓的数量
print(len(contours))
输出信息解读
输出的
hierarchy
是一个包含了轮廓之间关系信息的数组。其中每个子数组[6 -1 1 -1]
等代表了一个轮廓的相关信息:
第一个数字(如 6、2、3 等)表示下一个同级轮廓的索引。
第二个数字(如 -1)表示前一个同级轮廓的索引。
第三个数字(如 1、-1 等)表示第一个子轮廓的索引。
第四个数字(如 -1)表示父轮廓的索引。
输出的
9
表示检测到的轮廓数量。
轮廓的绘制
API函数
cv2.drawContours(image, contours, contourIdx, color, thickness=None,
lineType=None, hierarchy=None, maxLevel=None, offset=None)
参数含义
image:要在其上绘制轮廓的输入图像。
contours:轮廓列表,通常由cv2.findContours()函数返回。
contourIdx:要绘制的轮廓的索引。如果为负数,则绘制所有轮廓。 -1 color:轮廓的颜色,以BGR格式表示。例如,(0, 255, 0)表示绿色。
thickness:轮廓线的粗细。默认值为1。
lineType:轮廓线的类型。默认值为cv2.LINE_8。
hierarchy:轮廓层次结构。通常由cv2.findContours()函数返回。
maxLevel:绘制的最大轮廓层级。默认值为None,表示绘制所有层级。 offset:轮廓点的偏移量。默认值为None。
绘制出刚刚的phone.png图像中的轮廓
# 读取原图
phone = cv2.imread('phone.png')
# 对原图进行灰度处理
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
# 对灰度图进行阈值处理,得到二值图像
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
# 查找二值图像中的轮廓
_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 复制原始图像
image_copy = phone.copy()
# 在复制的图像上绘制轮廓
image_copy = cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=3)
# 显示绘制了轮廓的图像,窗口标题为'Contours_show'
cv2.imshow('Contours_show', image_copy)
# 等待用户按键,参数 0 表示无限等待
cv2.waitKey(0)
轮廓的特征
面积
API函数
cv2.contourArea(contour[, oriented])
参数含义
contour:顶点构成的二维向量组(如轮廓列表contours中的一个轮廓)
oriented:定向区域标志,默认值为 False,返回面积的绝对值,
Ture 时则根据轮廓方向返回带符号的数值
周长
API函数
arcLength(InputArray curve, bool closed)
参数含义
curve:输入的二维点集(轮廓顶点),可以是 vector 或 Mat 类型。
closed:用于指示曲线是否封闭。
问题:1. 求上述例子中 contours
列表中第一个和第二个轮廓的面积,并打印出这两个面积的值。
2. 计算 contours 列表中第一个封闭的轮廓的周长并打印
import cv2
# 读取原图
phone = cv2.imread('phone.png')
# 对原图进行灰度处理
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
# 对灰度图进行阈值处理,得到二值图像
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
# 查找二值图像中的轮廓
_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 计算 contours 列表中第一个轮廓的面积,并将结果存储在 area_0 变量中
area_0 = cv2.contourArea(contours[0])
# 计算 contours 列表中第二个轮廓的面积,并将结果存储在 area_1 变量中
area_1 = cv2.contourArea(contours[1])
# 打印第一个和第二个轮廓的面积
print(area_0,area_1)
# 计算 contours 列表中第一个轮廓的周长
length = cv2.arcLength(contours[0], closed=True)
# 打印第一个轮廓的周长
print(length)
根据面积显示特定轮廓
绘制出上述案例中面积大于10000的轮廓
import cv2
# 读取原图
phone = cv2.imread('phone.png')
# 对原图进行灰度处理
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
# 对灰度图进行阈值处理,得到二值图像
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
# 查找二值图像中的轮廓
_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 创建一个空列表用于存储面积大于 10000 的轮廓
a_list = []
# 遍历 contours 中的每个轮廓
for i in range(len(contours)):
# 如果当前轮廓的面积大于 10000
if cv2.contourArea(contours[i]) > 10000:
# 将该轮廓添加到 a_list 列表中
a_list.append(contours[i])
# 复制原始图像
image_copy = phone.copy()
# 在复制的图像上绘制面积大于 10000 的轮廓
image_copy = cv2.drawContours(image=image_copy, contours=a_list, contourIdx=-1, color=(0, 255, 0), thickness=3 )
# 显示绘制了特定轮廓的图像,窗口标题为'Contours_show_10000'
cv2.imshow('Contours_show_10000', image_copy)
# 等待用户按键
cv2.waitKey(0)
轮廓的近似
API函数
approx = cv2.approxPolyDP(curve, epsilon, closed)
参数含义:
curve:输入轮廓。
epsilon:近似精度,即两个轮廓之间最大的欧式距离。该参数越小,得到的近似结果越接近实际轮廓;
反之,得到的近似结果会更加粗略。
closed:布尔类型的参数,表示是否封闭轮廓。
如果是 True,表示输入轮廓是封闭的,近似结果也会是封闭的;
否则表示输入轮廓不是封闭的,近似结果也不会是封闭的。
返回值:
approx:近似结果,是一个ndarray数组,为1个近似后的轮廓,包含了被近似出来的轮廓上的点的坐标
import cv2
# 读取图像'phone.png'
phone = cv2.imread('phone.png')
# 将图像转换为灰度图
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
# 对灰度图进行二值化处理
ret,phone_thresh = cv2.threshold(phone_gray,120,255,cv2.THRESH_BINARY)
# 获取二值化图像的轮廓
_,contours, hierarchy = cv2.findContours(phone_thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 设置近似精度
epsilon = 0.01 * cv2.arcLength(contours[0],True)
# 对第一个轮廓进行近似
approx = cv2.approxPolyDP(contours[0], epsilon, True)
# 复制原始图像
phone_new = phone.copy()
# 在复制的图像上绘制近似后的轮廓
image_contours = cv2.drawContours(phone_new,[approx],contourIdx=-1,color=(0,255,0),thickness=3)
# 显示原始图像
cv2.imshow('phone',phone)
cv2.waitKey(0)
# 显示绘制了近似轮廓的图像
cv2.imshow('image_contours',image_contours)
cv2.waitKey(0)
给定轮廓的最小外接圆、外接矩形
外接圆
计算API函数
(x,y),r = cv2.minEnclosingCircle(cnt)
输入参数含义:
cnt 指定的轮廓
返回值:
(x,y) 是外接圆的圆心坐标
r 是外接圆的半径。
外接矩形
计算API函数
x,y,w,h = cv2.boundingRect(cnt)
输入参数含义:
cnt 指定的轮廓
返回值:
(x,y) 外接矩形的左上角坐标
w 矩形的宽度
h 矩形的高度
绘制出上述案例中第七个轮廓的最小外接圆与外接矩形
import cv2
# 读取原图
phone = cv2.imread('phone.png')
# 对原图进行灰度处理
phone_gray = cv2.cvtColor(phone,cv2.COLOR_BGR2GRAY)
# 对灰度图进行阈值处理,得到二值图像
ret, phone_binary = cv2.threshold(phone_gray, 120, 255, cv2.THRESH_BINARY)
# 查找二值图像中的轮廓
_,contours, hierarchy = cv2.findContours(phone_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 获取第 7 个轮廓
cnt = contours[6]
# 计算轮廓的外接圆
(x,y),r = cv2.minEnclosingCircle(cnt)
# 绘制外接圆
phone_circle = cv2.circle(phone.copy(),(int(x),int(y)),int(r),(0,255,0),2)
# 显示绘制了外接圆的图像
cv2.imshow('phone_circle',phone_circle)
cv2.waitKey(0)
# 计算轮廓的最小外接矩形
x,y,w,h = cv2.boundingRect(cnt)
# 绘制矩形
phone_rectangle = cv2.rectangle(phone.copy(),(x,y),(x+w,y+h),(0,255,0),2)
# 显示绘制了矩形的图像
cv2.imshow('phone_rectangle',phone_rectangle)
cv2.waitKey(0)