目录

一、函数 cv.findContours

二、轮廓层级(返回参数hierarchy)

三、轮廓寻找方式

1. RETR_LIST

2. RETR_TREE

3. RETR_EXTERNAL

4. RETR_CCOMP

四、实例

五、contourArea(contour)函数详解

六、 drawContours()函数详解

七、基于链码的二值图像轮廓跟踪法

代码复现:


  • 转载请注明本函数文详细地址

  • 本文讲解了opencv自带轮廓跟踪以及绘制轮廓的函数

  • 本文讲解了一种基于链码的一种轮廓检测方法

  • 本文一到四部分转载至:《opencv cv.findContours 函数详解》

一、函数 cv.findContours

contours, hierarchy = cv.findContours( image, mode, method[, contours[, hierarchy[, offset]]] )参数1:源图像参数2:轮廓的检索方式,这篇文章主要讲解这个参数参数3:一般用 cv.CHAIN_APPROX_SIMPLE,就表示用尽可能少的像素点表示轮廓contours:图像轮廓坐标,是一个列表hierarchy:[Next, Previous, First Child, Parent],文中有详细解释

我们使用cv.findContours()寻找轮廓时,参数2表示轮廓的检索方式(RetrievalModes),当我们传入的是cv.RETR_TREE,它表示什么意思呢?另外,函数返回值hierarchy有什么用途呢?下面我们就来研究下这两个问题。

二、轮廓层级(返回参数hierarchy)

图中总共有8条轮廓,2和2a分别表示外层和里层的轮廓,3和3a也是一样。从图中看得出来:

轮廓0/1/2是最外层的轮廓,我们可以说它们处于同一轮廓等级:0级
    轮廓2a是轮廓2的子轮廓,反过来说2是2a的父轮廓。轮廓2a算一个等级:1级
    同样3是2a的子轮廓,轮廓3处于一个等级:2级
    类似的,3a是3的子轮廓,等等…………
    这里面OpenCV关注的就是两个概念:同一轮廓等级和轮廓间的子属关系。

如果我们打印出cv.findContours()函数的返回值hierarchy,会发现它是一个包含4个值的数组:[Next, Previous, First Child, Parent]
Next:与当前轮廓处于同一层级的下一条轮廓
举例来说,前面图中跟0处于同一层级的下一条轮廓是1,所以Next=1;同理,对轮廓1来说,Next=2;那么对于轮廓2呢?没有与它同一层级的下一条轮廓了,此时Next=-1。
Previous:与当前轮廓处于同一层级的上一条轮廓
跟前面一样,对于轮廓1来说,Previous=0;对于轮廓2,Previous=1;对于轮廓2a,没有上一条轮廓了,所以Previous=-1。
First Child:当前轮廓的第一条子轮廓
比如对于轮廓2,第一条子轮廓就是轮廓2a,所以First Child=2a;对轮廓3,First Child=3a。
Parent:当前轮廓的父轮廓
比如2a的父轮廓是2,Parent=2;轮廓2没有父轮廓,所以Parent=-1。

现在既然我们了解了轮廓层级的概念,那么类似cv.RETR_TREE的轮廓寻找方式又是啥意思呢?

三、轮廓寻找方式

OpenCV中有四种轮廓寻找方式RetrievalModes,下面分别来看下:

1. RETR_LIST

这是最简单的一种寻找方式,它不建立轮廓间的子属关系,也就是所有轮廓都属于同一层级。这样,hierarchy中的后两个值[First Child, Parent]都为-1。比如同样的图,我们使用cv.RETR_LIST来寻找轮廓:

contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, 2)
print(hierarchy)
# 结果如下
[[[ 1 -1 -1 -1][ 2  0 -1 -1][ 3  1 -1 -1][ 4  2 -1 -1][ 5  3 -1 -1][ 6  4 -1 -1][ 7  5 -1 -1][-1  6 -1 -1]]]

因为没有从属关系,所以轮廓0的下一条是1,1的下一条是2……

如果你不需要轮廓层级信息的话,cv.RETR_LIST更推荐使用,因为性能更好

2. RETR_TREE

cv.RETR_TREE就是之前我们一直在使用的方式,它会完整建立轮廓的层级从属关系,前面已经详细说明过了。

3. RETR_EXTERNAL

这种方式只寻找最高层级的轮廓,也就是它只会找到前面我们所说的3条0级轮廓:
实验讲解 RETR_EXTERNAL

contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, 2)
print(len(contours), hierarchy, sep='\n')
# 结果如下
3
[[[ 1 -1 -1 -1][ 2  0 -1 -1][-1  1 -1 -1]]]

4. RETR_CCOMP

相比之下cv.RETR_CCOMP比较难理解,它把所有的轮廓只分为2个层级,不是外层的就是里层的。结合代码和图片,我们来理解下:

实验讲解 RETR_CCOMP

contours, hierarchy = cv.findContours(thresh, cv.RETR_CCOMP, 2)
print(hierarchy)
# 结果如下
[[[ 1 -1 -1 -1][ 2  0 -1 -1][ 4  1  3 -1][-1 -1 -1  2][ 6  2  5 -1][-1 -1 -1  4][ 7  4 -1 -1][-1  6 -1 -1]]]

使用这个参数找到的轮廓序号与之前不同。
图中括号里面1代表外层轮廓,2代表里层轮廓。比如说对于轮廓2,Next就是4,Previous是1,它有里层的轮廓3,所以First Child=3,但因为只有两个层级,它本身就是外层轮廓,所以Parent=-1。大家可以针对其他的轮廓自己验证一下。

四、实例

import cv2 as cv
import numpy# 1.读入图片
img = cv.imread('test_contours.jpg')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
contours, thresh = cv.threshold(img_gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)# 2.寻找轮廓
contours, hierarchy = cv.findContours(thresh, cv.RETR_LIST, 2)print(len(contours),hierarchy)# 3.绘制轮廓
cv.drawContours(img, contours, -1, (0, 0, 255), 2)cv.imshow('result',img)
cv.waitKey(0)
cv.destroyAllWindows()

五、contourArea(contour)函数详解

contourArea(contour)
#contour是存放着一个轮廓的坐标点的列表

六、 drawContours()函数详解

drawContours函数解析cv2.drawContours(draw_img,#绘制轮廓的画布contours, #轮廓信息-1, #-1表示绘制所有的轮廓,具体的数字的话就是取列表中的某一个轮廓进行绘制( 0,0,225),#绘制轮廓的颜色,BGR通道的值,这里是指用红色进行绘制2)#表示线宽,-1表示填充

七、基于链码的二值图像轮廓跟踪法

轮廓跟踪法大部分的思路都是一致的,就是找到黑白两个像素相邻的物体像素格,然后根据一定的规则进行下一个边界点的查找,当查找的下一个边界点回到起始点时则结束这个轮廓的扫描。一般都是链表和一定的邻域搜索规则结合使用进行邻域搜索寻找下一个边界点

链码可参考:https://baike.baidu.com/item/%E9%93%BE%E7%A0%81/4272744?fr=aladdin

链码表示了搜索的方向:

以下定义了利用当前点和上一个边界点的相对位置关系的邻域搜索方法

因此可以得出寻找下一边界点的准则如下:设当前点 P ( x, y ) 在上一边界点 C 的 8 邻域内的位置编码为 n, 则从当前点( x, y ) 的8邻域内的编码为 n 的位置, 顺时针方向移动 2 个像素的位置就是下一边界点的起始搜索位置.若不是边界点,则从搜索的起始点开始按照逆时针方向顺次搜索, 共搜索 5 次便可以找到下一个边界点

顺序说明:整体的轮廓是逆时针方向得到的,邻域搜索的方法为逆时针,邻域起始点的位置由顺时针获得,这里的顺序都是通过链码来进行控制的

参考论文:

《二值图像中目标物体轮廓的边界跟踪算法》

代码复现:

import cv2
import numpy as np
import timestart = time.time()
img_src = cv2.imread("dilate_img.jpg")#dialte_img,jpg是一张二值图,但是由imread读取进来默认是3通道的图片# 2.灰度化与二值化
img_gray = cv2.cvtColor(img_src,cv2.COLOR_RGB2GRAY)#将其变成单通道
ret, img_bin = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)#防止变换过程中非0和非255的值出现而影响结果
img_bin = cv2.copyMakeBorder(img_bin,1,1,1,1,borderType=cv2.BORDER_CONSTANT,value=0)#在图片四周各补零一行,即增加一行黑色像素
#初始化起始点
start_x = -1
start_y = -1
is_start_point = 0#判断是否为起始点的标志#寻找起始点
h,w = img_bin.shape
for i in range(h):for j in range(w):if (img_bin[i,j] == 255) and (img_bin[i-1,j] == 0):start_x = istart_y = jis_start_point = 1breakif is_start_point == 1:break
#(1,39)#定义链码相对应的增量坐标
neibor = [(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1),(1,0),(1,1)]#邻域点
temp = 2#链码值,也是neibor的索引序号,这里是从链码的2号位进行搜索
contours = [(start_x,start_y)]#用于存储轮廓点#将当前点设为轮廓的开始点
current_x = start_x
current_y = start_y#temp=2,表示从链码的2方向进行邻域检索,通过当前点和邻域点集以及链码值确定邻域点
neibor_x = current_x + neibor[temp][0]
neibor_y = current_y + neibor[temp][1]#因为当前点的值为起始点,而终止检索的条件又是这个,所以会产生冲突,因此先寻找第二个边界点
is_contour_point = 0
while is_contour_point == 0:  # 邻域点循环,当是目标像素点时跳出if img_bin[neibor_x, neibor_y] == 255:# 将符合条件的邻域点设为当前点进行下一次的边界点搜索current_x = neibor_xcurrent_y = neibor_yis_contour_point = 1contours.append((current_x, current_y))temp = temp - 2  # 作为下一个边界点的邻域检测起始点,顺时针旋转90度print(1)if temp < 0:temp = len(neibor) + tempelse:temp = temp + 1  # 逆时针旋转45度进行搜索if temp >= 8:temp = temp - len(neibor)neibor_x = current_x + neibor[temp][0]neibor_y = current_y + neibor[temp][1]#开始第三个及以后的边界点的查找
while not((current_x == start_x) and (current_y == start_y)):#轮廓扫描循环is_contour_point = 0while is_contour_point == 0:#邻域点循环,当是目标像素点时跳出if img_bin[neibor_x,neibor_y] == 255:#邻域是白点时,即为边界#将符合条件的邻域点设为当前点进行下一次的边界点搜索current_x = neibor_xcurrent_y = neibor_yis_contour_point = 1#将判断是否为边界点的标签设置为1,用于跳出循环contours.append((current_x,current_y))temp = temp - 2#作为下一个边界点的邻域检测起始点,顺时针旋转90度if temp < 0:temp = len(neibor) + tempelse:temp = temp + 1#逆时针旋转45度进行搜索if temp >= 8:temp = temp - len(neibor)neibor_x = current_x + neibor[temp][0]neibor_y = current_y + neibor[temp][1]#对已经检测到的轮廓进行标记
for xy in (contours):x = xy[0]y = xy[1]img_src[x-1,y-1] = (0,0,255)
cv2.imshow('',img_src)
cv2.waitKey(0)print((contours))
end = time.time()
print("时长%fms"%((end-start)*1000))

对于多轮廓边界的扫描,需要每进行完一条轮廓的扫描就对该轮廓进行标记,以免重复扫描

【图像处理】——Python+opencv实现二值图像的轮廓边界跟踪以及轮廓面积周长的求解(findcontours函数和contourArea函数)相关推荐

  1. 【图像处理】——实现二值图像的轮廓边界跟踪以及轮廓面积周长的求解(connectedComponentsWithStats()函数和connectedComponents()函数)

    目录 一.cv2.connectedComponents() 函数原型: 输入参数解析: 返回参数解析: 二.cv2.connectedComponentsWithStats() 函数原型: 输入参数 ...

  2. 【图像处理】——Python+opencv实现提取图像的几何特征(面积、周长、细长度、区间占空比、重心、不变矩等)

    转载请注明详细地址 本文简单介绍了图像常见几何特征的概念以及求解方法 本文介绍了Python和opencv求解几何特征的常用方法 目录 其他形状外接轮廓的方法可以参考:<OpenCV-Pytho ...

  3. [图像处理] Python+OpenCV实现车牌区域识别及Sobel算子

    由于最近太忙,这篇文章只给出相关代码供大家学习,过一段时间会详细的写一些列Python图像处理的文章,包括各种算法原理.图像识别.图像增强.图像分类.深度学习等.本篇文章主要调用OpenCV库(cv2 ...

  4. python图像识别 车牌_[图像处理] Python+OpenCV实现车牌区域识别

    点击上方蓝色字体,关注我们 15 本篇文章主要调用OpenCV库(cv2)进行车牌区域识别,具体步骤包括: 1.灰度转换:将彩色图片转换为灰度图像,常见的R=G=B=像素平均值. 2.高斯平滑和中值滤 ...

  5. 基础图像处理 python+opencv

    目录 一.图像基础处理操作 1.1.图像读取: 1.2.灰度变化: 1.3.图片显示: 1.4.相关代码: 二.图像直方图: 2.1.直方图相关介绍: 2.2.相关代码: 2.3.运行结果: 三.直方 ...

  6. Python/OpenCV 使用傅里叶变换与高斯平滑分析轮廓轨迹

    该方法基本思想是通过分析高低频信息检测出轮廓碰伤.运动轨迹突变等信息,在工业上应用可能比较广泛, 对各种不规则形状都能分析,不过对高频信息多的复杂形状可能不好区分形状与噪音. 在这个例子中讲使用一个有 ...

  7. Python+OpenCV:Hough圆检测(Hough Circle Transform)

    Python+OpenCV:Hough圆检测(Hough Circle Transform) ##################################################### ...

  8. Python+OpenCV:二维直方图(2D Histograms)

    Python+OpenCV:二维直方图(2D Histograms) ################################################################# ...

  9. Python+OpenCV:基于色彩空间转换的目标跟踪

    Python+OpenCV:基于色彩空间转换的目标跟踪 ######################################################################## ...

最新文章

  1. 如何用ping来测试Azure虚机网络延迟的监测工作
  2. 【Python】图解Pandas数据合并:concat、join、append
  3. Win10无法使用小娜搜索本地应用问题的解决方案
  4. python3.6配置环境变量_python安裝及环境变量配置
  5. 企业级 SpringBoot 教程 (八)springboot整合mongodb
  6. 别让腾讯“科技向善”太孤独!
  7. 301 302区别_302状态码代表什么,302状态码解决方法
  8. cant connect local mysql to_连接Mysql提示Can't connect to local MySQL server through socket的解决方法...
  9. java非法表达式开头,java - 我不断收到“ Battle.java:11:错误:表达式的非法开头” Heelp - SO中文参考 - www.soinside.com...
  10. 截止失真放大电路_基本共射放大电路的特点你懂了吗?
  11. window计算机截屏快捷键,电脑截图是ctrl加什么键win7快捷键截图方法详解
  12. VMware虚拟机NAT模式共享主机无线网络的配置方法
  13. 关于Google大陆手机号不能验证的问题
  14. 技校计算机应用基础,技校《计算机应用基础》教法探讨
  15. PanDownload 3.5.1 卢本伟修改版
  16. vlan的tagged、untagged
  17. predict函数 R_超星尔雅-R语言学习总结(上)
  18. 数据结构之广义表(C语言)
  19. mysql全文查询中文_MySQL-中文全文检索
  20. 小米官宣停服后,“米聊“再次上线

热门文章

  1. firedebug调试Jquery
  2. qrcode.js 二维码生成器
  3. 异常处理try...catch...throw
  4. 【Linux】 命令收集
  5. Mac系统中MongoChef链接MongoDB集群的方法
  6. eslint 禁用命令
  7. 巴黎市中心降下2019年第一场雪
  8. 顺大势逆小势策略之代码实现及可行性分析
  9. 追寻终极数据库 - 事务/分析混合处理系统的交付挑战 (3)
  10. Nginx 反向代理 websocket 协议