图像轮廓
什么是图形轮廓
图像轮廓是具有相同颜色或灰度的连续点的曲线,轮廓在形状分析和物体检测与识别中很有用处
轮廓的作用:
- 用于图形分析
- 物体的识别与检测
查找轮廓
OpenCV
提供的findContours()
方法可以通过计算像素梯度来判断出图像的边缘,然后将边缘的点封装成数组返回
注意:
- 为了保证检测的准确性,需要先对图像进行
二值化
或Canny边缘检测
操作 - 画轮廓时会修改输入的图像,如果之后想继续使用原始图像,应该将原始图像存储一份到其他变量中(备份)——
img_copy = img.copy()
关键API:contours,hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
利用该函数可以获取轮廓的信息
其中:
-
image
:被检测的图像,必须是8位单通道二值图像。- 如果原始图像是彩色图像,则必须转为灰度图像
cv2.cvtColor()
,再经过二值化阈值处理cv2.threshold()
- 如果原始图像是彩色图像,则必须转为灰度图像
-
mode
:查找轮廓的模式 (写模式名或数字都可以)-
cv2.RETR_EXTERNAL / 0
:表示只检测外围轮廓
-
cv2.RETR_LIST / 1
:检测的轮廓不建立等级关系(属于同一轮廓线上即为同一级,),即检测所有的轮廓,从右到左,从里到外画轮廓(一般不会出现这么复杂的索引)
-
cv2.RETR_CCOMP / 2
: 每层最多两级,从小到大,最里到外
-
cv2.RETR_TREE / 3
:按照树型存储轮廓,从大到小,从右到左 ——> 先从外到内把右边的算完,再算左边的,!最常用!
-
-
method
:轮廓近似方法,也叫ApproximationMode。总的来说就是如何去保存你的轮廓cv2.CHAIN_APPROX_NONE
:保存所有轮廓上的点,会把轮廓上所有像素点的坐标都保存下来,存储信息较大cv2.CHAIN_APPROX_SIMPLE
:只保存角点,比如四边形,只保存四边形的四个角,存储信息较少,比较常用
返回值:
contours
:检测出所有轮廓的集合,是list
类型,可以通过contours[i](i = 0,1,2...)
来调用某一个轮廓,即每一个元素都是某个轮廓的像素坐标数组hierarchy
:轮廓间的层级关系
# 查找轮廓
import cv2
import numpy as np
# 导入图片 该图片显示是黑白的,实际上是三通道的图像
img = cv2.imread('./contours.jpeg')
# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 对比一下原图与灰度图
cv2.imshow('img',img)
cv2.imshow('gray',gray)
# 进行二值化操作(注意!threshold有两个返回值!一个是阈值,一个是二值化处理后的图片)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响
# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓
print(type(contours))
print(contours)
# 展示
# cv2.imshow('dst',dst)
# cv2.imshow('img and dst ',np.hstack((gray,dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
绘制轮廓
通过cv2.findContours()
方法找到图像轮廓之后,为了方便开发人员观测,最好能把轮廓画出来,于是OpenCV
提供了drawContours()
方法专门用来绘制这些轮廓
关键API:img = cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
其中:
image
:操作的图像,由于我们可以设置轮廓的颜色color
,所以就不太适合用二值化或灰度化后的图片进行操作,应该用三通道的彩图进行绘画(原图)contours
:轮廓点,由查找轮廓的函数cv2.findContours()
的返回值可得contourIdx
:要绘制的轮廓编号,-1
表示绘制所有轮廓,此处是索引!因此不能写成contours[]
的形式color
:轮廓的颜色,使用BGR格式,如(0,0,255)表示红色thickness
:线宽,-1
表示全部填充
返回值:
img
:可以不使用,在执行的时候就已经在对参数中的image
进行绘制了
# 绘制轮廓
import cv2
import numpy as np
# 导入图片 该图片显示是黑白的,实际上是三通道的图像
img = cv2.imread('./contours.jpeg')
# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow('img',img)
cv2.imshow('gray',gray)
# 进行二值化操作(注意!threshold有两个返回值!一个是阈值,一个是二值化处理后的图片)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响
# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓
# 绘制轮廓:会直接修改原图
# 如果想保持原图不变,可以copy一份
img_copy = img.copy()
cv2.drawContours(img_copy,contours,-1,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受
# 展示
cv2.imshow('img',img)
cv2.imshow('img_copy',img_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
如果我们想只画一个轮廓,那么可以根据目标轮廓的层级来绘制(我们使用的是cv2.RETR_TREE
:从左到右、从外到内)
例如把参数中的-1
改成0
cv2.drawContours(img_copy,contours,0,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受
结果:
计算轮廓的面积和周长
轮廓面积
轮廓面积是指每个轮廓中所有的像素点围成区域的面积,单位为像素
轮廓面积是轮廓重要的统计特性之一,通过轮廓面积的大小可以进一步分析每个轮廓隐含的信息,例如通过轮廓面积区分物体大小、识别不同的物体
在查找到轮廓后,可能会有很多细小的轮廓,我们可以通过轮廓的面积进行过滤(比如小于某一个阈值,就把这个轮廓过滤掉)
关键API:cv2.contourArea(contour[, oriented])
- 参数是
countour
(不加s
,加了s(countours)
后表示轮廓的集合)
轮廓周长
关键API:cv2.arcLength(curve, closed)
curve
:轮廓closed
:是否是闭合的轮廓,为布尔类型,一般设置为closed = True
# 计算轮廓的面积和周长
import cv2
import numpy as np
# 导入图片 该图片显示是黑白的,实际上是三通道的图像
img = cv2.imread('./contours.jpeg')
# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow('img',img)
cv2.imshow('gray',gray)
# 进行二值化操作(注意!threshold有两个返回值!一个是阈值,一个是二值化处理后的图片)