OpenCV:07图像轮廓

什么是图形轮廓

图像轮廓是具有相同颜色或灰度的连续点的曲线,轮廓在形状分析物体检测与识别中很有用处

轮廓的作用:

  • 用于图形分析
  • 物体的识别与检测

查找轮廓

注意:

  • 为了保证检测的准确性,需要先对图像进行二值化Canny边缘检测操作
  • 画轮廓时会修改输入的图像,如果之后想继续使用原始图像,应该将原始图像存储一份到其他变量中(备份)—— img_copy = img.copy()

关键API:cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])

利用该函数可以获取轮廓的信息

其中:

  • 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, 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()

结果:

在这里插入图片描述

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>