OpenCV-Python实现轮廓拟合


Posted in Python onJune 08, 2021

前言

什么是轮廓?

轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同 的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。

  • 为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理 或者 Canny 边界检测。
  • 查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图 像的话,你应该将原始图像存储到其他变量中。
  • 在 OpenCV 中,查找轮廓就像在黑色背景中超白色物体。你应该记住, 要找的物体应该是白色而背景应该是黑色。

在计算轮廓时,可能并不需要实际的轮廓,而仅需要一个接近于轮廓的近似多边形。比如矩形其实都是差不多的轮廓,都是长宽不相等且平行的四边形,那么只要提供一个近似的轮廓,我们就可以区分形状。

在OpenCV中,它给我们提供了cv2.boundingRect()函数来绘制轮廓的矩形边界,其完整定义如下:

def boundingRect(array):

array:前面已经介绍过,array是一个灰度图像,或者轮廓。

该函数返回3个值时,是矩形边界的左上角顶点的坐标值以及矩形边界的宽与高。返回4个值时,是矩形左上角顶点的x坐标,y坐标,以及宽高。

绘制椭圆的矩形边界

现在,我们还是使用前面的一张椭圆图形,如下图所示:

OpenCV-Python实现轮廓拟合

得到图形之后,我们使用上面的函数,计算该图像轮廓的4值,代码如下:

import cv2

img = cv2.imread("26_1.jpg")

# 转换为灰度图像
gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)

ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
x, y, w, h = cv2.boundingRect(contours[0])
print(x, y, w, h)

运行之后,控制台输出如下内容:

OpenCV-Python实现轮廓拟合

这里我们得到了椭圆的矩形左上角坐标为(53,120),其宽高分别为272与84。

既然我们已经得到了其矩形边界的坐标以及宽高,那么我们可以开始绘制其矩形边界。前面提取轮廓绘制用的是cv2.drawContours()函数,这里同样也是。

代码如下:

import cv2
import numpy as np

img = cv2.imread("26_1.jpg")
cv2.imshow("img1",img)
# 转换为灰度图像
gray= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
x, y, w, h = cv2.boundingRect(contours[0])
rect=np.array([[[x,y],[x+w,y],[x+w,y+h],[x,y+h]]])#1
cv2.drawContours(img,[rect],-1,(255,255,255),2)#1

cv2.imshow("img2",img)

cv2.waitKey()
cv2.destroyAllWindows()

运行之后,其椭圆的矩形边界就被我们标记出来了,效果如下:

OpenCV-Python实现轮廓拟合

当然,这里我们还可以使用另一个函数cv2.rectangle()来绘制矩形边界,值需要更换上面代码中注释1的两个代码,具体如下所示:

cv2.rectangle(img, (x, y), (x + w, y + h), (255, 255,255),2)

最小包围矩形框

在OpenCV中,它还提供了cv2.minAreaRect()来绘制最小包围矩形框,其完整定义如下:

def minAreaRect(points):

其中points参数是轮廓,返回值为矩形特征信息,包括矩形的中心(x,y),宽高,以及旋转角度。

特别注意,minAreaRect函数的返回值并不能直接代入drawContours()函数中。因此,我们必须将其转换为符合要求的结构才能接着操作。通过cv2.boxPoint()函数就可以转换为符合drawContours()的结构参数。

还是上面那张图,不过我们用旋转后的椭圆原图,代码如下:

import cv2
import numpy as np

img = cv2.imread("26_4.jpg")

cv2.imshow("img1",img)
# 转换为灰度图像
gray= cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
rect= cv2.minAreaRect(contours[0])
print(rect)
points=cv2.boxPoints(rect)
print(points)
points=np.int0(points)
print(points)
cv2.drawContours(img,[points],0,(255,255,255),2)

cv2.imshow("img2",img)

cv2.waitKey()
cv2.destroyAllWindows()

运行之后,图像效果以及控制台的输出信息如下:

OpenCV-Python实现轮廓拟合

OpenCV-Python实现轮廓拟合

这里我们可以清楚的看到minAreaRect()函数返回值的转换过程。先通过boxPoints()函数转换为drawContours()函数能接受的参数格式,然后通过取整转换为具体的像素坐标值。

最小包围圆形框

既然有最小包围矩形框,那么一定就有最小包围圆形框。在OpenCV中,它给我们提供cv2.minEnclosingCircle()函数来绘制最小包围圆形框。

函数的完整定义如下:

def minEnclosingCircle(points):

这里的参数与上面的points参数一致,但是其返回值并不相同,毕竟绘制圆形肯定与绘制矩形的参数肯定不一样。

它有两个返回值,一个是圆形的中心坐标(x,y),一个是圆形的半径r。下面,我们直接来绘制上面椭圆的最小包围圆形框。具体代码如下所示:

import cv2
import numpy as np

img = cv2.imread("26_4.jpg")

cv2.imshow("img1", img)
# 转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
(x, y), r = cv2.minEnclosingCircle(contours[0])
center = (int(x), int(y))
r = int(r)
cv2.circle(img, center, r, (255, 255, 255), 2)

cv2.imshow("img2", img)

cv2.waitKey()
cv2.destroyAllWindows()

运行之后,效果如下所示:

OpenCV-Python实现轮廓拟合

最优拟合椭圆

在OpenCV中,它给我们提供了cv2.fitEllipse()函数绘制最优拟合椭圆。其完整的定义如下:

def fitEllipse(points):

其中points参数与前文一致,而它的返回值是RotatedRect类型,这是因为该函数返回的是拟合椭圆的外接矩形,包括矩形的质心,宽高,旋转角度等信息,这些信息正好与椭圆的中心点,轴长度,旋转角度一致。

下面,我们来使用该函数绘制最优拟合椭圆,这里我们选取如上图所示的一张矩形图。具体代码如下:

import cv2

img = cv2.imread("27.jpg")

cv2.imshow("img1", img)
# 转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
ellipse = cv2.fitEllipse(contours[0])

cv2.ellipse(img, ellipse, (0, 0, 255), 3)

cv2.imshow("img2", img)

cv2.waitKey()
cv2.destroyAllWindows()

运行之后,效果如下所示:

OpenCV-Python实现轮廓拟合

最优拟合直线

在OpenCV中,它还提供了cv2.fitLine()函数绘制最优拟合直线,其完整定义如下:

def fitEllipse(points):

points:与前文一样,是轮廓

distType:距离类型。拟合直线时,要使输入点到拟合直线的距离之和最小。详细参数定义参考开发文档,这里不在赘述。

param:距离参数,与所选距离类型有关。当该参数为0时,自动选择最优值。

reps:用于表示拟合直线所需要的径向精度,通常该值被设定为0.01

aeps:用于表示拟合直线所需要的角度精度,通常该值被设定为0.01

对于二维直线,返回值line为4维,前两维代表拟合出的直线的方向,后两位代表直线上的一点。

下面,我们来直接使用代码绘制最优拟合直线。

import cv2

img = cv2.imread("27.jpg")

cv2.imshow("img1", img)
# 转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
ellipse = cv2.fitEllipse(contours[0])

cv2.ellipse(img, ellipse, (0, 0, 255), 3)

cv2.imshow("img2", img)

cv2.waitKey()
cv2.destroyAllWindows()

运行之后,效果如下所示:

OpenCV-Python实现轮廓拟合

对于绘制直线来说,我们需要获取绘制直线的起点以及终点,这里lefty为起点,righty为终点。

最小外包三角形

在OpenCV,它还提供了cv2.minEnclosingTriangle()函数来绘制最小外包三角形。其完整定义如下:

def minEnclosingTriangle(points, triangle=None):

其中points与前文类似,其返回值triangle为外包三角形的三个顶点集。

下面,我们直接构建最小外包三角形,具体代码如下:

import cv2

img = cv2.imread("27.jpg")

cv2.imshow("img1", img)
# 转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

area, trg1 = cv2.minEnclosingTriangle(contours[0])
print(area)
print(trg1)
for i in range(0, 3):
    cv2.line(img, tuple(trg1[i][0]), tuple(trg1[(i + 1) % 3][0]), (0, 255, 0), 2)

cv2.imshow("img2", img)

cv2.waitKey()
cv2.destroyAllWindows()

运行之后,效果如下:

OpenCV-Python实现轮廓拟合

需要注意的是,在cv2中没有直接绘制三角形的函数,所以我们通过绘制三条直线,绘制三角形,minEnclosingTriangle()函数第一个返回值为三角形面积,第二返回值是三点坐标。

逼近多边形

在OpenCV中,它还提供了cv2.approxPolyDP()函数构建指定边数的逼近多边形。其完整定义如下:

def approxPolyDP(curve, epsilon, closed, approxCurve=None):

curve:轮廓

epsilon:精度,原始轮廓的边界点与逼近多边形边界之间的最大距离

closed:布尔类型。为True时,表示逼近多边形是封闭的。为False时,biao表示毕竟多边形是不封闭的。

approxCurve为该函数的返回值,是逼近多边形的点集。。

下面,我们来实现各类逼近多边形的绘制,代码如下:

import cv2

img = cv2.imread("24.jpg")

list=[0.1,0.09,0.055,0.05,0.02]

cv2.imshow("img", img)
# 转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

for i, val in enumerate(list):
    epsilon = val * cv2.arcLength(contours[0], True)
    approx = cv2.approxPolyDP(contours[0], epsilon, True)
    cv2.drawContours(img, [approx], 0, (0, 255, 0), 2)

    cv2.imshow("img"+str(i), img)

cv2.waitKey()
cv2.destroyAllWindows()

运行之后,效果如下:

OpenCV-Python实现轮廓拟合

cv2.approxPolyDP()函数采用的是Douglas-Peucker算法,该算法的原理是首先从轮廓中找到距离最远的两个点,并将两个点相连。接下来,在轮廓上找到一个离当前直线最远的点,并将该点与原有直线连成一个封闭的多边形,此时得到一个三角形。以此类推四边形,五边形,六边形等。当前多边形的距离都小于函数cv2.approxPolyDP()的参数epsilon的值时,就停止迭代。

到此这篇关于OpenCV-Python实现轮廓拟合的文章就介绍到这了,更多相关OpenCV 轮廓拟合内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python实现排序算法
Feb 14 Python
编写Python脚本把sqlAlchemy对象转换成dict的教程
May 29 Python
Python通过RabbitMQ服务器实现交换机功能的实例教程
Jun 29 Python
Python找出最小的K个数实例代码
Jan 04 Python
Python爬虫小技巧之伪造随机的User-Agent
Sep 13 Python
Python从使用线程到使用async/await的深入讲解
Sep 16 Python
Python之使用adb shell命令启动应用的方法详解
Jan 07 Python
Python中类似于jquery的pyquery库用法分析
Dec 02 Python
python 读写文件包含多种编码格式的解决方式
Dec 20 Python
python画图常规设置方式
Mar 05 Python
为了顺利买到演唱会的票用Python制作了自动抢票的脚本
Oct 16 Python
Python中的matplotlib绘制百分比堆叠柱状图,并为每一个类别设置不同的填充图案
Apr 20 Python
python图像处理基本操作总结(PIL库、Matplotlib及Numpy)
Django drf请求模块源码解析
Python中OpenCV实现查找轮廓的实例
python字符串的多行输出的实例详解
Jun 08 #Python
Python机器学习之基于Pytorch实现猫狗分类
Python中json.load()和json.loads()有哪些区别
python 爬取哔哩哔哩up主信息和投稿视频
Jun 07 #Python
You might like
PHP使用range协议实现输出文件断点续传代码实例
2014/07/04 PHP
PHP 5.3新增魔术方法__invoke概述
2014/07/23 PHP
jquery+php+ajax显示上传进度的多图片上传并生成缩略图代码
2014/10/15 PHP
Yii使用ajax验证显示错误messagebox的解决方法
2014/12/03 PHP
使用 PHPStorm 开发 Laravel
2015/03/24 PHP
dvwa+xampp搭建显示乱码的问题及解决方案
2015/08/23 PHP
fsockopen pfsockopen函数被禁用,SMTP发送邮件不正常的解决方法
2015/09/20 PHP
PHP实现可自定义样式的分页类
2016/03/29 PHP
PHP使用递归算法无限遍历数组示例
2017/01/13 PHP
PHP中register_shutdown_function函数的基础介绍与用法详解
2017/11/28 PHP
在页面上点击任一链接时触发一个事件的代码
2007/04/07 Javascript
jQuery+CSS 实现的超Sexy下拉菜单
2010/01/17 Javascript
IE6图片加载的一个BUG解决方法
2010/07/13 Javascript
NodeJS 模块开发及发布详解分享
2012/03/07 NodeJs
关于JS判断图片是否加载完成且获取图片宽度的方法
2013/04/09 Javascript
在JavaScript中处理时间之setMinutes()方法的使用
2015/06/11 Javascript
jQuery easyui的validatebox校验规则扩展及easyui校验框validatebox用法
2016/01/18 Javascript
Js的Array数组对象详解
2016/02/22 Javascript
jQuery移动端日期(datedropper)和时间(timedropper)选择器附源码下载
2016/04/19 Javascript
AngularJS实现根据变量改变动态加载模板的方法
2016/11/04 Javascript
ES6通过babel转码使用webpack使用import关键字
2016/12/13 Javascript
微信小程序 图片宽度自适应的实现
2017/04/06 Javascript
JavaWeb表单及时验证功能在输入后立即验证(含用户类型,性别,爱好...的验证)
2017/06/09 Javascript
利用jquery如何从json中读取数据追加到html中
2017/12/01 jQuery
vue 避免变量赋值后双向绑定的操作
2020/11/07 Javascript
python在windows命令行下输出彩色文字的方法
2015/03/19 Python
python 计算平均平方误差(MSE)的实例
2019/06/29 Python
手动安装python3.6的操作过程详解
2020/01/13 Python
一个J2EE项目团队的主要人员组成是什么
2012/06/04 面试题
室内设计自我鉴定
2013/10/15 职场文书
初三学习决心书
2014/03/11 职场文书
组工干部演讲稿
2014/09/02 职场文书
优秀学生干部事迹材料
2014/12/24 职场文书
债务纠纷代理词
2015/05/25 职场文书
感恩教师主题班会
2015/08/12 职场文书
Python中json.load()和json.loads()有哪些区别
2021/06/07 Python