什么是轮廓?

轮廓是一系列相连的点组成的曲线,代表了物体的基本外形,相对于边缘,轮廓是连续的,边缘并不全部连续。

寻找轮廓

寻找轮廓 OpenCV 为我们提供了一个现成的函数 findContours()

import cv2 as cvimg = cv.imread("black.png")
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)print(len(contours[0]))

这段代码先用 threshold() 对图像进行降噪处理,它的原型函数如下:

retval, dst = cv.threshold(src, thresh, maxval, type[, dst] )
  • dst:结果图像。
  • src:原图像。
  • thresh:当前阈值。
  • maxVal:最大阈值,一般为255。
  • type:阈值类型,可选值如下:

enum ThresholdTypes {
THRESH_BINARY = 0, # 大于阈值的部分被置为 255 ,小于部分被置为 0
THRESH_BINARY_INV = 1, # 大于阈值部分被置为 0 ,小于部分被置为 255
THRESH_TRUNC = 2, # 大于阈值部分被置为 threshold ,小于部分保持原样
THRESH_TOZERO = 3, # 小于阈值部分被置为 0 ,大于部分保持不变
THRESH_TOZERO_INV = 4, # 大于阈值部分被置为 0 ,小于部分保持不变
THRESH_OTSU = 8, # 自动处理,图像自适应二值化,常用区间 [0,255] };

查找轮廓使用的函数为 findContours() ,它的原型函数如下:

cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
  • image:源图像。
  • mode:表示轮廓检索模式。

cv2.RETR_EXTERNAL 表示只检测外轮廓。
cv2.RETR_LIST 检测的轮廓不建立等级关系。
cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
cv2.RETR_TREE 建立一个等级树结构的轮廓。

  • method:表示轮廓近似方法。

cv2.CHAIN_APPROX_NONE 存储所有的轮廓点。
cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。

这里可以使用 print(len(contours[0])) 函数将包含的点的数量打印出来,比如在上面的示例中,使用参数 cv2.CHAIN_APPROX_NONE 轮廓点有 1382 个,而使用参数 cv2.CHAIN_APPROX_SIMPLE 则轮廓点只有 4 个。

绘制轮廓

绘制轮廓使用到的 OpenCV 为我们提供的 drawContours() 这个函数,下面是它的三个简单的例子:

# To draw all the contours in an image:
cv2.drawContours(img, contours, -1, (0,255,0), 3)
# To draw an individual contour, say 4th contour:
cv2.drawContours(img, contours, 3, (0,255,0), 3)
# But most of the time, below method will be useful:
cnt = contours[4]
cv2.drawContours(img, [cnt], 0, (0,255,0), 3)

drawContours() 函数中有五个参数:

  • 第一个参数是源图像。
  • 第二个参数是应该包含轮廓的列表。
  • 第三个参数是列表索引,用来选择要绘制的轮廓,为-1时表示绘制所有轮廓。
  • 第四个参数是轮廓颜色。
  • 第五个参数是轮廓线的宽度,为 -1 时表示填充。

我们接着前面的示例把使用 findContours() 找出来的轮廓绘制出来:

import cv2 as cvimg = cv.imread("black.png")
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow("img", img)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)print(len(contours[0]))# 绘制绿色轮廓
cv.drawContours(img, contours, -1, (0,255,0), 3)cv.imshow("draw", img)cv.waitKey(0)
cv.destroyAllWindows()

特征矩

特征矩可以帮助我们计算一些图像的特征,例如物体的质心,物体的面积等,使用的函数为 moments()

moments() 函数会将计算得到的矩以字典形式返回。

import cv2 as cvimg = cv.imread("number.png")gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)cnt = contours[0]
# 获取图像矩
M = cv.moments(cnt)
print(M)# 质心
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])print(f'质心为:[{cx}, {cy}]')

这时,我们取得了这个图像的矩,矩 M 中包含了很多轮廓的特征信息,除了示例中展示的质心的计算,还有如 M[‘m00’] 表示轮廓面积。

轮廓面积

area = cv.contourArea(cnt)
print(f'轮廓面积为:{area}')

这里取到的轮廓面积和上面 M[‘m00’] 保持一致。

轮廓周长

perimeter = cv.arcLength(cnt, True)
print(f'轮廓周长为:{perimeter}')

参数 True 表示轮廓是否封闭,我们这里的轮廓是封闭的,所以这里写 True 。

轮廓外接矩形

轮廓外接矩形分为正矩形和最小矩形。使用 cv2.boundingRect(cnt) 来获取轮廓的外接正矩形,它不考虑物体的旋转,所以该矩形的面积一般不会最小;使用 cv.minAreaRect(cnt) 可以获取轮廓的外接最小矩形。

两者的区别如上图,绿线代表的是外接正矩形,红线代表的是外接最小矩形,代码如下:

import cv2 as cv
import numpy as npimg = cv.imread("number.png")gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)cnt = contours[0]# 外接正矩形
x, y, w, h = cv.boundingRect(cnt)
cv.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)# 外接最小矩形
min_rect = cv.minAreaRect(cnt)
print(min_rect)box = cv.boxPoints(min_rect)
box = np.int0(box)
cv.drawContours(img, [box], 0, (0, 0, 255), 2)cv.imshow("draw", img)cv.waitKey(0)
cv.destroyAllWindows()

boundingRect() 函数的返回值包含四个值,矩形框左上角的坐标 (x, y) 、宽度 w 和高度 h 。
minAreaRect() 函数的返回值中还包含旋转信息,返回值信息为包括中心点坐标 (x,y) ,宽高 (w, h) 和旋转角度。

轮廓近似

根据我们指定的精度,它可以将轮廓形状近似为顶点数量较少的其他形状。它是由 Douglas-Peucker 算法实现的。

OpenCV 提供的函数是 approxPolyDP(cnt, epsilon, True) ,第二个参数 epsilon 用于轮廓近似的精度,表示原始轮廓与其近似轮廓的最大距离,值越小,近似轮廓越拟合原轮廓。第三个参数指定近似轮廓是否是闭合的。具体用法如下:

import cv2 as cvimg = cv.imread("number.png")gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)cnt = contours[0]# 计算 epsilon ,按照周长百分比进行计算,分别取周长 1% 和 10%
epsilon_1 = 0.1 * cv.arcLength(cnt, True)
epsilon_2 = 0.01 * cv.arcLength(cnt, True)# 进行多边形逼近
approx_1 = cv.approxPolyDP(cnt, epsilon_1, True)
approx_2 = cv.approxPolyDP(cnt, epsilon_2, True)# 画出多边形
image_1 = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)
image_2 = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)cv.polylines(image_1, [approx_1], True, (0, 0, 255), 2)
cv.polylines(image_2, [approx_2], True, (0, 0, 255), 2)cv.imshow("image_1", image_1)
cv.imshow("image_2", image_2)
cv.waitKey(0)
cv.destroyAllWindows()


第一张图是 epsilon 为原始轮廓周长的 10% 时的近似轮廓,第二张图中绿线就是 epsilon 为原始轮廓周长的 1% 时的近似轮廓。

轮廓凸包

凸包外观看起来与轮廓逼近相似,只不过它是物体最外层的「凸」多边形。

如下图,红色的部分为手掌的凸包,双箭头部分表示凸缺陷(Convexity Defects),凸缺陷常用来进行手势识别等。

import cv2 as cvimg = cv.imread("number.png")
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
cnt = contours[0]
# 绘制轮廓
image = cv.cvtColor(gray_img, cv.COLOR_GRAY2BGR)
cv.drawContours(image, contours, -1, (0, 0 , 255), 2)# 寻找凸包,得到凸包的角点
hull = cv.convexHull(cnt)# 绘制凸包
cv.polylines(image, [hull], True, (0, 255, 0), 2)cv.imshow("image", image)
cv.waitKey(0)
cv.destroyAllWindows()


还有一个函数,是可以用来判断图形是否凸形的:

print(cv.isContourConvex(hull)) # True

它的返回值是 True 或者 False 。

最小闭合圈

接下来,使用函数 cv.minEnclosingCircle() 查找对象的圆周。它是一个以最小面积完全覆盖物体的圆。

import cv2 as cvimg = cv.imread("number.png")
gray_img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 降噪
ret, thresh = cv.threshold(gray_img, 127, 255, 0)
# 寻找轮廓
contours, hierarchy = cv.findContours(gray_img, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
cnt = contours[0]# 绘制最小外接圆
(x, y), radius = cv.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
cv.circle(img, center, radius, (0, 255, 0), 2)cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows()

下一个是把一个椭圆拟合到一个物体上。它返回内接椭圆的旋转矩形。

ellipse = cv.fitEllipse(cnt)
cv.ellipse(img, ellipse, (0, 255, 0), 2)

python --opencv图像处理轮廓(寻找轮廓、绘制轮廓)详解相关推荐

  1. [Python图像处理] 三十四.数字图像处理基础与几何图形绘制万字详解(推荐)

    该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门.OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子.图像增强技术.图像分割等,后期结合深度学习研究图像识别 ...

  2. python+OpenCV图像处理(十一)图像轮廓检测

    图像轮廓检测 (一)检测轮廓 在OpenCV-python中,使用cv2.findContours()函数来对图像进行轮廓检测. 返回三个值:image,contours,hierarchy cont ...

  3. python+OpenCV图像处理(三)绘制简单的几何图形、显示文字

    绘制简单的几何图形.显示文字 (一)绘制直线和矩形 img = np.zeros([512, 512, 3]) # line函数用来画直线,第一个参数可以理解为画布矩阵, # 第二个参数pt1是直线的 ...

  4. OPENCV之寻找并绘制轮廓以及提取轮廓重心坐标

    OPENCV之寻找并绘制轮廓以及提取轮廓重心坐标 1.寻找轮廓 声明:在寻找图像轮廓之前需要对图像进行阈值分割或者Canny.拉普拉斯等边缘检测算子处理. 寻找轮廓的算子: findContours( ...

  5. Opencv边缘检测、轮廓发现、绘制轮廓

    Opencv边缘检测.轮廓发现.绘制轮廓 提取图像轮廓的2个步骤 1. findContours函数找轮廓, 2. drawContours函数画轮廓 轮廓的查找--cv::findContours( ...

  6. Python+Opencv图像处理新手入门教程(三):阈值与二值化

    一步一步来吧 上一节: Python+Opencv图像处理新手入门教程(二):颜色空间转换,图像大小调整,灰度直方图 1.Intro 今天这节我们主要研究利用阈值处理图像.例如对于输入图像: 如何做一 ...

  7. Python+OpenCV图像处理(一篇全)

    参考:1.网易云课堂 Python+OpenCV图像处理 - 网易云课堂 2.[在水一方xym的博客]业精于勤荒于嬉,行成于思毁于随 - CSDN博客 https://blog.csdn.net/za ...

  8. Python OpenCV图像处理 理论 代码

    python opencv图像处理 GitHub - LeBron-Jian/ComputerVisionPractice OpenCV计算机视觉学习 & 代码 OpenCV计算机视觉学习(1 ...

  9. python画椭圆-python opencv圆、椭圆与任意多边形的绘制实例详解

    圆形的绘制 : OpenCV中使用circle(img,center,radius,color,thickness=None,lineType=None,shift=None)函数来绘制圆形 impo ...

  10. [Python图像处理] 三十八.OpenCV图像增强和图像去雾万字详解(直方图均衡化、局部直方图均衡化、自动色彩均衡化)

    该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门.OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子.图像增强技术.图像分割等,后期结合深度学习研究图像识别 ...

最新文章

  1. 运维学习之nfs系统文件服务
  2. 【PC工具】更新在线流程图绘制工具bullmind,免费云存储流程图绘制,可直接粘贴图片...
  3. java 利用ManagementFactory获取jvm,os的一些信息--转
  4. 三个python文件怎么关联___name__= __main__怎么解释 怎么用 另外两个py文件之间的互动关联...
  5. SharePoint 2010 - 如何导入\导出WebPart
  6. 第七届 蓝桥杯 省赛 第七题 剪邮票
  7. 计算机系统结构名词解释
  8. slice()和splice()、split(),number()、parseInt()和parseFloat()
  9. mybatis-plus 从2.x到3.x升级指南
  10. 学校教材订购系统 Java_高校网上教材征订系统,基于SSM框架下的JAVA系统
  11. JAVA打包成EXE文件,能在没有jre环境的电脑上运行
  12. 什么app可以绘制路线图_为什么大多数路线图都会不可避免地带来糟糕的结果
  13. 如何把照片压缩到20k一下_如何把2寸彩照压缩到20k以下?
  14. 哔哩哔哩2021校招末班车来了!
  15. 登入验证安全 上(验证码、忘记密码、客户端验证)
  16. 软件工程(4)--螺旋模型
  17. 计算机存储与图片内存占用
  18. 2020/7/6期末PTA
  19. ARIMA 时间序列模型
  20. 位置不可用 Desktop不可用

热门文章

  1. C++通过CMD修改电脑IP地址
  2. Graph_Master(连通分量_C_Trajan缩点+最小路径覆盖)
  3. 用php打竖的文字_手写php函数处理 竖排文字
  4. html5字体的格式转换,font字体
  5. 关于保险的“损失补偿原则”
  6. 先发新机再搞碰瓷,酷派复活就有胜算?
  7. 中国神话--学术性研究
  8. E: Sub-process /usr/sbin/dpkg-preconfigure --apt || true received a segmentation fault.
  9. Python3 网络爬虫:视频下载,那些事儿!
  10. Mac 下拷贝文件到移动硬盘