目录:

  1. 轮廓常用函数
  2. 第一个应用
  3. 第二个应用

轮廓就是连接所有连续点(沿着边界)的曲线,具有相同的颜色或灰度值。轮廓是形状分析、物体检测和识别的有用工具。为了提高提取轮廓的精确度,需要先通过阈值处理或canny边缘检测将图像转换为二值图像。

在 OpenCV 中,寻找轮廓就像从黑色背景中寻找白色物体,所以要找到的物体应该是白色的,背景应该是黑色的。

只罗列和轮廓相关的几个函数没啥意思,通过两个例子可以对其用法有更深入的理解。

一、轮廓常用函数

1、查找轮廓

在二值图像中获取轮廓:

import cv2
im = cv2.imread('test.jpg')
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

cv2.findContours() 函数中有三个参数:

  • thresh:源图像
  • cv2.RETR_TREE:表示轮廓检索模式
  • cv2.CHAIN_APPROX_SIMPLE:表示轮廓近似方法

返回值为获取到的轮廓 contourshierarchycontours为包含图像中所有轮廓的python列表(三维数组),每个轮廓是包含边界所有坐标点(x, y)的Numpy数组。hierarchy 是一个三维数组,它储存了所有等高线(轮廓)的层级结构,详情可以查看 1 和 2

轮廓是具有相同灰度值的形状的边界,即一个轮廓可以看做是一个等高线。它存储形状边界的(x, y)坐标。我们可以使用第三个参数来指定是否存储形状边界的所有坐标点。

第二个参数决定 hierarchy 采取什么样的格式输出。第三个参数可以指定两个值,如果是 cv2.CHAIN_APPROX_NONE,则存储形状边界的所有坐标点。但有时我们不需要所有的点,比如一个矩形的轮廓,我们只需要矩形的四个端点就可以了。这时我们就可以传入 cv2.CHAIN_APPROX_SIMPLE,它会移除所有冗余的点并压缩轮廓,从而节省内存。

比如下面这个例子,我们标记出矩形所有轮廓点,第一张图片是使用 cv2.CHAIN_APPROX_NONE 得到的结果,一共有734个点;第二张图片是使用 cv2.CHAIN_APPROX_SIMPLE 得到的结果,只有4个点。

2、绘制轮廓

可以使用 cv2.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)

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

注意:指定轮廓颜色的值要和图像通道数一致。

3、轮廓外接矩形

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

两者区别如下图所示,绿线表示外接正矩形,红线表示外接最小矩形:

cv2.boundingRect(cnt) 的返回值包含四个值,矩形框左上角的坐标(x, y)、宽度w和高度h。

x,y,w,h = cv2.boundingRect(cnt)

cv.minAreaRect(cnt) 的返回值中还包含旋转信息,返回值信息为包括中心点坐标(x,y),宽高(w, h)和旋转角度。

然而我们绘制矩形需要矩形的四个顶点坐标,可以通过 cv.boxPoints() 来获取,如下代码所示:

rect = cv2.minAreaRect(cnt)
print(rect)  # center(x, y), (width, height), angle of rotation
box = cv2.boxPoints(rect)  # box.shape=(4, 2)
box = np.int0(box)
cv2.drawContours(img,[box],0,(0,0,255),2)

angle的范围为 (-90,-0],如上图中的角

(与矩形框最低点相连的右边的线),一个矩形逆时针旋转,
的值变化为:-0 -> -30 -> -60 -> -0,然后不断循环。

4、轮廓面积

我们可以通过 cv2.contourArea(cnt) 来获取轮廓的面积,这里的面积表示该形状内包含的像素点数量。

5、轮廓周长

通过 cv2.arcLength(cnt,True) 来绘制轮廓周长或者曲线长度,第二个参数指定形状是为闭合轮廓(True)还是普通曲线。这里的周长/长度表示该形状边界上的像素点数量。

6、轮廓近似

我们可以将一个轮廓/曲线近似为另一个顶点数量较少的轮廓/曲线,使得它们之间的距离小于或等于指定的精度,通过 cv2.approxPolyDP(cnt, epsilon, True) 来实现。第二个参数用于轮廓近似的精度,表示原始轮廓与其近似轮廓的最大距离,值越小,近似轮廓越拟合原轮廓。第三个参数指定近似轮廓是否是闭合的。

比如下面这张图,其中物体的原始轮廓(红线所示)如第一张图所,第二张图中绿线就是epsilon为原始轮廓周长的10%时的近似轮廓,第三张图中绿线就是epsilon为原始轮廓周长的1%时的近似轮廓。

二、第一个应用

原图为:

现在我们只想获取图中圆形的内圆,不包含黑色边缘部分,就使用我们上面介绍过的函数实现。

先读取图像并将其转换为灰度图:

import cv2
import numpy as npimg = cv2.imread("shapes.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

使用阈值处理将其转换为二值图:

ret, threshed = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)

然后就可以查找二值图中的轮廓:

contours, _ = cv2.findContours(threshed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

二值图和查找到的轮廓如下所示:

我们得到了很多轮廓,但是只想要最中间的那个内圆轮廓,所以我们需要对这些轮廓进行筛选:

# 按轮廓面积将轮廓进行升序排列
cnts = sorted(contours, key=cv2.contourArea)
H, W = img.shape[:2]
for cnt in cnts:# 获取轮廓外接矩形的坐标和长宽x,y,w,h = cv2.boundingRect(cnt)# 得到第一个满足条件的轮廓,就退出循环if cv2.contourArea(cnt) > 100 and (0.8 < w/h < 1.2) and (W/4 < x + w//2 < W*3/4) and (H/4 < y + h//2 < H*3/4):circle = cntbreak

仔细观察原图,可以发现我们想获取的那个圆形轮廓面积足够大,长宽比接近1,所以满足条件:

cv2.contourArea(cnt) > 100 and (0.8 < w/h < 1.2)

而且该圆形轮廓的中心点在图像的正中间部分,所以有:

(W/4 < x + w//2 < W*3/4) and (H/4 < y + h//2 < H*3/4)

当通过满足上述条件时,我们就可以获取到我们想要的内圆轮廓。

创建轮廓掩码并与原图进行逐位与运算:

mask = np.zeros(img.shape[:2], np.uint8)
cv2.drawContours(mask, [circle], -1, 255, -1)
dst = cv2.bitwise_and(img, img, mask=mask)

得到最终结果:

三、第二个应用

在这个例子中我们使用轮廓相关知识把这个人物给抠出来,类似于抠图。

还是先读取图像,并转换为灰度图,还要对其进行高斯模糊,事先过滤掉不同轮廓之间的细线连接。

第一个应用我们通过阈值处理得到二值图,这里使用 Canny 边缘检测,得到二值图,然后对其进行膨胀和腐蚀操作,方便稍后提取轮廓。关于形态学操作的内容可以参考这篇文章。

import cv2
import numpy as npimg = cv2.imread('Levi.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)blur = cv2.GaussianBlur(gray, (3, 3), 0)edges = cv2.Canny(blur, 10, 200)  # Edge detection
edges = cv2.dilate(edges, None)  # 默认(3x3)
edges = cv2.erode(edges, None)

边缘图,膨胀图和腐蚀图如下所示:

接下来我们就可以获取处理后的图像中的轮廓。

先使用 cv2.findContours 获取所有轮廓,再根据轮廓面积进行降序排列,并获取到面积最大的轮廓,即图中人物的轮廓。

接下来通过 cv2.arcLength 得到轮廓周长,并将周长的0.1%作为近似轮廓的精度。然后再使用 cv2.approxPolyDP 得到近似轮廓。

# Find contours in edges(binary image)
contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)
max_contour = contours[0]epsilon = 0.001*cv2.arcLength(max_contour, True)
approx = cv2.approxPolyDP(max_contour, epsilon, True)

然后根据近似轮廓建立一个轮廓的掩码mask,在其上绘制出最大轮廓对应的填充多边形,用于下一步抠图。

对掩码进行高斯模糊用于平滑边缘,可以消除锯齿。

mask = np.zeros(img.shape[:2], np.uint8)
cv2.drawContours(mask, [approx], -1, 255, -1)mask = cv2.GaussianBlur(mask, (5, 5), 0)
cv2.imshow('mask', mask)dst = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow('dst', dst)

最后一步就是将掩码和原图像进行求与运算,即得到最终结果。

掩码图和结果如下所示:

这里说个题外话,这里我们通过 edges = cv2.Canny(blur, 10, 200) 得到二值图,但是要手动设置两个阈值参数,不想手动设置的话可以使用如下函数来手动设置:

 def auto_canny(image, sigma=0.33):# compute the median of the single channel pixel intensitiesv = np.median(image)# construct two thresholds using the medianlower = int(max(0, (1.0 - sigma) * v))upper = int(min(255, (1.0 + sigma) * v))edged = cv2.Canny(image, lower, upper)return edged

所以将 edges = cv2.Canny(blur, 10, 200) 替换为 edges = auto_canny(blur, sigma=0.33) 即可。

当需要框出目标的轮廓时,推荐使用 cv2.Canny() 边缘检测而非阈值处理生成二值图。理由:

  1. 提取边缘与阈值处理不同,边缘提取可以识别图片中目标的形状、轮廓,而不是简单的区分出图片中的高光与暗调,可以简单地提取图片中颜色分布位于中间调上的目标(中间调是指色阶值接近中值(128)的图像像素);
  2. 使用Canny边缘检测,提取结果的白点数量更少,对等高线检测的混淆因素减少;
  3. 不同边缘检测算法中,Canny边缘检测效果好(虽然对性能要求高);

参考:

边缘检测,框出物体的轮廓

几何形状识别与测量

Contour Features

remove the background of image:12


如果觉得有用,点个赞吧(ง •̀_•́)ง。

opencv函数findcontours_OpenCV 中的轮廓应用相关推荐

  1. opencv函数findcontours_OpenCV系列之轮廓入门 | 二十一

    目标 了解轮廓是什么. 学习查找轮廓,绘制轮廓等. 你将看到以下功能:cv.findContours(),cv.drawContours() 什么是轮廓? 轮廓可以简单地解释为连接具有相同颜色或强度的 ...

  2. OpenCV在图像中寻找轮廓

    OpenCV在图像中寻找轮廓 在图像中寻找轮廓 目标 代码 结果 在图像中寻找轮廓 目标 在本教程中,您将学习如何: 使用OpenCV函数cv :: findContours 使用OpenCV函数cv ...

  3. 使用Python,OpenCV从图像中删除轮廓

    使用Python,OpenCV从图像中删除轮廓 1. 效果图 2. 步骤 3. 源码 4. 参考 1. 使用Python.OpenCV计算轮廓的中心并标记 2. 使用Python.OpenCV检测轮廓 ...

  4. OpenCV在图像中寻找轮廓的实例(附完整代码)

    OpenCV在图像中寻找轮廓的实例 OpenCV在图像中寻找轮廓的实例 OpenCV在图像中寻找轮廓的实例 #include "opencv2/imgcodecs.hpp" #in ...

  5. OpenCV 在图像中寻找轮廓

    使用OpenCV函数 findContours 使用OpenCV函数 drawContours 滤波--消除噪声 增强--使边界轮廓更加明显 检测--选出边缘点 例程 #include "o ...

  6. 教你如何使用 OpenCV检测图像中的轮廓

    @Author:Runsen 轮廓是连接所有具有某种颜色或强度的连续点的闭合曲线,它们代表图像中发现的对象的形状.轮廓检测是一种用于形状分析和物体检测和识别的有用技术. 轮廓检测并不是图像分割的唯一算 ...

  7. 使用Python,OpenCV寻找图像中的轮廓

    使用Python和OpenCV查找图像中的形状 1. 效果图 2. 步骤 3. 源码 参考 这篇博客将讨论使用Python和OpenCV查找图像中的形状,具体是 cv2.inRange在图像中查找形状 ...

  8. OpenCV之imgproc 模块. 图像处理(5)在图像中寻找轮廓 计算物体的凸包 创建包围轮廓的矩形和圆形边界框 为轮廓创建可倾斜的边界框和椭圆 轮廓矩 多边形测试

    在图像中寻找轮廓 目标 在这个教程中你将学到如何: 使用OpenCV函数 findContours 使用OpenCV函数 drawContours 原理 例程 教程的代码在下面给出. 你也可以从 这里 ...

  9. OpenCV学习笔记(12)——OpenCV中的轮廓

    什么是轮廓 找轮廓.绘制轮廓等 1.什么是轮廓 轮廓可看做将连续的点(连着边界)连在一起的曲线,具有相同的颜色和灰度.轮廓在形态分析和物体的检测和识别中很有用. 为了更加准确,要使用二值化图像.在寻找 ...

最新文章

  1. C# http 性能优化500毫秒到 60 毫秒
  2. php in yii framework
  3. 从前端程序员的视角看小程序的稳定性保障
  4. mpVue配置sass全局变量
  5. (转载)DevExpress ASPxGridView 使用文档一:概述
  6. [Webpack 2] Ensure all source files are included in test coverage reports with Webpack
  7. Markdown 五分钟速成
  8. docker 4 section
  9. P1789 【Mc生存】插火把(python3实现)
  10. Asp.net2005 使用 NVelocity 实现 MVC
  11. linux利用位置参数数组,Shell编程1_变量、参数和数组
  12. react+百度地图实现自定义图标
  13. 16进制转ascii,转字符串
  14. 解决 ThinkPad x270 安装 ubuntu 14.04 后的网络问题
  15. 数据库入门-主键和外键设置
  16. c语言卡诺图算法实现,多变量卡诺图化简的算法实现.pdf
  17. Codeforces 128 A Statues【预处理+Bfs】
  18. Ubuntu系统的SSH出现“Connection reset by IP port 22 ”
  19. 第二次结对编程 微软学术搜索
  20. vscode编辑如何保存时自动校准eslint规范

热门文章

  1. 对PE文件进行十六进制代码(机器码)提取并保存到外部文件
  2. 【CyberSecurityLearning 47】PHP 数组
  3. S5PV210开发 -- UART 详解
  4. LR菜鸟入门 -- LightRoom安装/预设
  5. JS:js 数组赋值问题 :值传递还是引用?
  6. Bit-Z携手Bit-MY落户马来西亚 已获得经营牌照
  7. 深入讲解Android Property机制
  8. Blind Return Oriented Programming (BROP) Attack - 攻击原理
  9. 64位Linux下的栈溢出
  10. nx600打印机打印设置_win7打印机共享怎么设置