图像轮廓是具有相同颜色或灰度的连续点的曲线,轮廓在形状分析和物体检测与识别中很有用处
轮廓的作用:
注意:
二值化
或Canny边缘检测
操作img_copy = img.copy()
关键API:cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
利用该函数可以获取轮廓的信息
其中:
mode
:查找轮廓的模式 (写模式名或数字都可以)
method
:轮廓近似方法,也叫ApproximationMode。总的来说就是如何去保存你的轮廓
返回值-> contours, hierarchy
:需要用变量去接受
contours
:轮廓的集合,可以通过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()
关键API:drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
其中:
image
:操作的图像,由于我们可以设置轮廓的颜色color
,所以就不太适合用二值化或灰度化后的图片进行操作,应该用三通道的彩图进行绘画(原图)contours
:轮廓点,由查找轮廓的函数cv2.findContours()
的返回值可得contourIdx
:要绘制的轮廓编号,-1
表示绘制所有轮廓,此处是索引!因此不能写成contours[]
的形式color
:轮廓的颜色,如(0,0,255)表示红色thickness
:线宽,-1
表示全部填充# 绘制轮廓
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有两个返回值!一个是阈值,一个是二值化处理后的图片)
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.drawContours(img_copy,contours,0,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受
# 计算轮廓面积
area = cv2.contourArea(contours[1])
print('area:',area) # 单位是像素
# 计算轮廓周长
length = cv2.arcLength(contours[1],closed = True)
print('length:',length)
# 展示
# cv2.imshow('img',img)
# cv2.imshow('img_copy',img_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
area: 115239.5
length: 1355.0710676908493
cv2.findContours()
后的轮廓信息contours
可能过于复杂不平滑,可以用cv2.approxPolyDP()
函数来对该多边形曲线做适当近似,这就是轮廓的多边形逼近
cv2.approxPolyDP()
就是以多边形去逼近轮廓,采用的是Douglas_Peucker算法(方法名中的DP)
DP算法
的原理比较简单,核心就是不断找多边形最远的点加入形成新的多边形,直到最短距离小于指定的精度
关键API:cv2.approxPolyDP(curve, epsilon, closed[, approxCurve])
其中:
curve
:要近似逼近的轮廓epsilon
:DP算法的阈值,阈值越小,近似轮廓越贴合真是轮廓,蕴含的像素点也就越多,占用内存越大closed
:轮廓是否闭合# 多边形逼近
import cv2
import numpy as np
# 导入图片 该图片显示是黑白的,实际上是三通道的图像
img = cv2.imread('./hand.png')
# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 进行二值化操作(注意!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.drawContours(img_copy,contours,0,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受
#----------------------------------------------------------------------------------------------------------------------------
# 使用多边形逼近 近似模拟手的轮廓
approx = cv2.approxPolyDP(contours[0],20,closed = True) # 我们只有一个轮廓,因此写contours[0]
# approx本质上是一个轮廓数据,类型是ndarray
# 画出近似逼近的轮廓 ——> 要强制类型转化为列表list
cv2.drawContours(img_copy,[approx],0,(0,255,0),2) # 会直接对操作的图像进行修改,因此可以不用接受
#----------------------------------------------------------------------------------------------------------------------------
# 展示
cv2.imshow('img',img)
cv2.imshow('img_copy',img_copy)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果: