干货 | 轮廓逼近原理与OpenCV应用(附Python-OpenCV文档下载)
![作者头像](https://ask.qcloudimg.com/http-save/yehe-6881354/g5049se6gg.jpeg)
导 读
本文主要介绍轮廓逼近的原理及其在OpenCV中的使用演示。同时可在文末获取Python-OpenCV学习文档pdf。
背景介绍
轮廓逼近的应用比较广泛,如下路线俯视图的简化:
![](https://developer.qcloudimg.com/http-save/yehe-6881354/f28e84dd5c32a8f26cac4f011051803f.png)
通过迭代平滑一些顶点,从而产出更加线性的路线:
![](https://developer.qcloudimg.com/http-save/yehe-6881354/502866b795c58581432d6122e683fab9.gif)
当然这只是轮廓逼近的其中一个应用,后续我们将详细介绍轮廓逼近的原理和OpenCV中的使用实例。
轮廓逼近的原理
轮廓近似使用Ramer–Douglas–Peucker(RDP)算法,旨在通过给定阈值减少折线的顶点来简化折线。通俗地说,我们采用一条曲线并减少其顶点数量,同时保留其大部分形状。如下图所示:
![](https://developer.qcloudimg.com/http-save/yehe-6881354/159d14d6e5ccd13499025cd7fa8a380f.gif)
给定曲线的起点和终点,算法将首先找到距离连接两个参考点的直线距离最大的顶点。我们称它为 最大点 。 如果最大点位于小于阈值的距离,我们自动忽略起点和终点之间的所有顶点,使曲线成为一条直线。
如果最大点位于阈值之外,我们将递归地重复该算法,上图使最大点为参考之一,并重复检查过程。
注意某些顶点是如何被系统地消除的。 最后,我们保留了大部分信息,但处于不太复杂的状态。
OpenCV轮廓逼近实例
这里使用Python-OpenCV做演示,测试图像如下:
![](https://developer.qcloudimg.com/http-save/yehe-6881354/6d7f5ef3346354d27d85e4a1c221416c.png)
【1】转灰度图 + 二值化
src = cv2.imread('1.png')
cv2.imshow("src", src)
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
_, thres = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("thresh", thres)
![](https://developer.qcloudimg.com/http-save/yehe-6881354/a999137fe2ad0d7dc5d2645dd59065fe.png)
【2】查找轮廓 + 绘制原始轮廓
#opencv4.6
cnts,_ = cv2.findContours(thres, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
output = src.copy()
cv2.drawContours(output, cnts[0], -1, (0, 255, 0), 2)
cv2.imshow("src-contour", output)
![](https://developer.qcloudimg.com/http-save/yehe-6881354/69ae8b866193802acf2887dbf8bf0645.png)
【3】轮廓逼近 + 不同参数结果比较
res = src.copy()
epsilon = 0.001 * cv2.arcLength(cnts[0], True)
approx = cv2.approxPolyDP(cnts[0],epsilon,True)
cv2.drawContours(res,[approx],-1,(255,0,0),2)
epsilon = 0.001 * 轮廓长度
![](https://developer.qcloudimg.com/http-save/yehe-6881354/310c704da56c434efc0c26c25059c7f2.png)
epsilon = 0.01 * 轮廓长度
![](https://developer.qcloudimg.com/http-save/yehe-6881354/c218ea8f906a87691cc7ae921f6362a8.png)
epsilon = 0.03 * 轮廓长度
![](https://developer.qcloudimg.com/http-save/yehe-6881354/09cbab30030e423918f043f51e1f8055.png)
epsilon = 0.05 * 轮廓长度
![](https://developer.qcloudimg.com/http-save/yehe-6881354/9ab24500a30f23bd315e8cc88b907bbb.png)
上图中,随着阈值逐渐增大,逼近结果越来越平滑,最后变为矩形。
完整代码:
import cv2
import numpy as np
src = cv2.imread('1.png')
cv2.imshow("src", src)
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
_, thres = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("thresh", thres)
cnts,_ = cv2.findContours(thres, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
output = src.copy()
cv2.drawContours(output, cnts[0], -1, (0, 255, 0), 2)
cv2.imshow("src-contour", output)
res = src.copy()
epsilon = 0.05 * cv2.arcLength(cnts[0], True)