【opencv】(6) 图像轮廓处理

各位同学好,今天和大家分享一下opencv中如何获取图像轮廓,以及对轮廓的一些其他操作。内容有:

(1)轮廓检测:cv2.findContours();(2)轮廓绘制:cv2.drawContours();(3)轮廓近似:cv2.approxPolyDP();(4)面积计算:cv2.contourArea();(5)周长计算:cv2.arcLength();(6)外接矩形:cv2.rectangle();(7)外接圆:cv2.circle()


在开始前,先导入需要用到的库文件以及图像数据,定义一个图像显示函数,方便后续绘图。

import cv2
import numpy as np
# 获取图片所在文件夹
filepath = 'C:\\...\\opencv\\img'
# 获取文件夹中的某一张图片
img = cv2.imread(filepath+'\\test2.png')
# 定义绘图函数
def cv_show(name,img):
    # 传入自定义图像名,即图像变量
    cv2.imshow(name,img) 
    # 图片不会自动消失
    cv2.waitKey(0)
    # 手动关闭窗口
    cv2.destroyWindow()

1. 图像轮廓

1.1 获取图像轮廓

方法: contours, hierarchy = cv2.findContours(img, mode, method)

参数:

img输入的图像,最好是二值图

mode轮廓检索模式,如下

RETR_EXTERNAL:只检索最外面的轮廓

RETR_LIST:检索所有的轮廓,并将其保存到一条链表中

RETR_CCOMP:检索所有的轮廓,并将它们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界

RETR_TREE:常用,检索所有的轮廓,并重构嵌套轮廓的整个层次。保存了所有的轮廓,用哪个调用哪个

method:轮廓逼近方法,如下

CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。正常画出所有轮廓

CHAIN_APPROX_SIMPLE:压缩水平的、垂直的、斜的部分,即函数只保留他们的终点坐标。压缩得到更精简的结果,例如一个矩形轮廓只需4个点来保存轮廓信息

返回值:

contours:列表,保存轮廓信息。每一个元素都是图像的轮廓

hierarchy:数组,分层保存轮廓信息。元素数和轮廓数一致


1.2 绘制图像轮廓

方法: cv2.drawContours(img, contours, contourIdx, color, thickness)

参数:

img:指明在哪幅图像上绘制轮廓;image为三通道才能显示轮廓

contours:图像轮廓信息,列表形式

contours:指定绘制轮廓列表中的哪条轮廓。如果是-1,则绘制其中的所有轮廓。

color:代表绘制轮廓的线条颜色,根据BGR调整,若为(0,0,255)则为红色。

thickness:线条粗细


1.3 代码演示

首先将读入的图像变成灰度图cv2.cvtColor(),再使用图像阈值处理方法将灰度图转换成二值图cv2.threshold(),像素值超过阈值127的变成255,低于127的像素值变成0,实现二值化。将二值化处理后的图像thresh传入轮廓检测函数。图像阈值处理方法见下文第4节:【opencv】(2) 图像处理:边界填充、图像融合、图像阈值、数值计算

#(1)图像处理
# 将读入的图像变成灰度图 
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 图像数值超过127取255,小于127的变成0
ret,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
# ret接收阈值,thresh接收处理后的图像
#(2)轮廓检测
# 输入图像最好是二值图
contours,hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 返回值:contours轮廓信息;hierarchy层级,把结果保存在一个层级中
#(3)绘制轮廓
# 注意要将原图(彩色图)复制一份,不然原图会在函数处理完之后改变
draw = img.copy()  #draw改变不会导致img改变
# 在draw画板上绘制轮廓;画第几个轮廓,-1代表所有轮廓;BGR分别对应(0,0,255),用红色线画轮廓;线条宽2
res = cv2.drawContours(draw, contours,-1, (0, 0, 255), 2)
# 展示图像
cv_show('res',res)

第1张为原始图像,第2张为contourIdx=-1时绘制轮廓后的图像 ,第3张为contourIdx=3时绘制的轮廓。contourIdx中保存了所有的轮廓信息。

【opencv】(6) 图像轮廓处理_第1张图片 【opencv】(6) 图像轮廓处理_第2张图片  【opencv】(6) 图像轮廓处理_第3张图片


 2. 轮廓特征

2.1 轮廓计算

轮廓计算有非常多种方法,这里只介绍两个,计算轮廓周长和面积。

在图像轮廓获取函数中cv2.findContours(),我们获取了轮廓信息返回值contours,在计算时,不能将轮廓全部放进去计算,计算时需要选出其中一个。下面使用轮廓信息中的第0个轮廓计算它的面积和周长。

# 获取某一个轮廓用于计算
cnt = contours[0]
# ==1== 面积
cv2.contourArea(cnt)  # 8500.5
# ==2== 周长
cv2.arcLength(cnt,True)  # 437.948

2.2 轮廓近似

方法: cv2.approxPolyDP(curve, epsilon, closed)

参数:

curve: 需要进行近似的轮廓

epsilon: 判断点到相对应的线段的距离的阈值,这是原始曲线与其近似值之间的最大距离。阈值越小,折线的形状越接近曲线。

closed: 若为true,曲线第一个点与最后一个点连接形成闭合曲线,若为false,曲线不闭合。

下面使用的阈值是cv2.arcLength(cnt,True),即轮廓的周长。取轮廓周长的0.1倍来作为contours[0]轮廓的近似。

# 获取图像
img = cv2.imread(filepath+'\\test1.png')  # 获取一张图像
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  # 灰度图
ret,thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 二值化
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)  # 轮廓提取
cnt = contours[0]  # 获取其中一层轮廓
draw = img.copy()  # 绘制轮廓
res = cv2.drawContours(draw, [cnt], -1, (0,0,255), 2)  # 在draw图像中绘制cnt轮廓
cv_show('res',res)  # 绘图
# 轮廓近似
epsilon = 0.1*cv2.arcLength(cnt,True)  # 以周长的百分比作为阈值,指定的越小,得到的轮廓和原来的区别较小
approx = cv2.approxPolyDP(cnt, epsilon, True)  # 近似函数,cnt为轮廓,epsilon阈值。返回近似后的轮廓
# 绘图展示
draw = img.copy()
res = cv2.drawContours(draw, [approx], -1, (255,0,0), 2)  # 将近似后的轮廓approx画在原图上,用蓝色表示,线条粗2
cv_show('res',res)

第一张图为图像轮廓,第二张图为轮廓近似后的结果

【opencv】(6) 图像轮廓处理_第4张图片   【opencv】(6) 图像轮廓处理_第5张图片


2.3 轮廓的外接矩形

首先在轮廓提取函数的返回值contours中,选取一个轮廓信息用于求它的外接矩形。

计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的

x, y, w, h = cv2.boundingRect(轮廓信息)

返回四个值。x,y是矩阵左上点的坐标;w,h是矩阵的宽和高。

外接矩形绘制函数

cv2.rectangle(img, pt1, pt2, color, thickness)

参数:

img: 原始图片作为画板

pt1: 长方形框左上角坐标 (x, y)

pt2: 长方形框右下角坐标 (x+w, y+h)

color: 线条颜色(B, G, R)

thickness: 线条粗细

在图片img上画长方形,坐标原点是图片左上角,向右为x轴正方向,向下为y轴正方向
 

# 获取轮廓信息
img = cv2.imread(filepath+'\\test1.png')  # 读取图像
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  # 灰度处理
ret,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)  # 二值化处理
contours,hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)  # 获取轮廓信息
cnt = contours[0]  # 指定某一层轮廓
# 轮廓的外接矩形
x,y,w,h = cv2.boundingRect(cnt)  # 计算外接矩形,返回矩形的左上坐标点,和一长一宽
rectangle = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)  # 根据坐标绘制矩形,绿色线条
cv_show('rectangle',rectangle)

【opencv】(6) 图像轮廓处理_第6张图片

得到矩形的宽和高,接下来就可以计算轮廓面积和外接矩形面积的比值。

area = cv2.contourArea(cnt)  # 轮廓面积计算函数
x,y,w,h = cv2.boundingRect(cnt)  # 获取坐标点和边长
rect_area = w*h  # 计算外接矩形面积
extent = area/rect_area  # 计算比值
print('轮廓面积和外接矩形面积之比:',extent)

2.4 轮廓的外接圆

获取轮廓的圆心坐标和半径

(x,y), radius = cv2.minEnclosingCircle(轮廓信息)

轮廓外接圆函数

cv2.circle(img, center, radius, color, thickness, shift)

img: 输入的图片作为画板

center: 圆心位置

radius: 圆的半径

color: 圆的颜色

thickness: 正数表示圆形轮廓的粗细。负数表示要绘制实心圆。

shift: 圆心坐标点和半径值的小数点位数

# 轮廓外接圆
# 返回圆心坐标和半径
(x,y),radius = cv2.minEnclosingCircle(cnt)
# 圆心坐标
center = (int(x),int(y))
# 半径
radius = int(radius)
# 绘制外接圆,输入整型
circle = cv2.circle(img,center,radius,(255,0,0),2)
cv_show('circle',circle)

【opencv】(6) 图像轮廓处理_第7张图片

你可能感兴趣的:(opencv机器视觉,python,opencv,图像处理,计算机视觉,人工智能)