OpenCV库的学习笔记(十一)Contours

入门

目标

  • 理解什么是轮廓
  • 学习寻找轮廓,绘制轮廓等
  • 你会看到这些函数:cv.findContours(), cv.drawContours()

什么是轮廓

轮廓可以简单地解释为连接所有连续点(沿着边界)的曲线,具有相同的颜色或强度。轮廓是形状分析和对象检测与识别的有用工具。

  • 为了获得更高的准确性,请使用二进制图像。所以在找到轮廓之前,应用阈值或Canny边缘检测
  • 从 OpenCV 3.2 开始,findContours() 不再修改源图像。
  • 在 OpenCV 中,寻找轮廓就像从黑色背景中寻找白色物体。所以记住,要找到的对象应该是白色的,背景应该是黑色的。

 下面就讲述如何在二进制图像中找到轮廓:

import numpy as np
import cv2 as cv

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

看,cv.findContours() 函数中有三个参数,第一个是源图像,第二个是轮廓检索模式,第三个是轮廓近似方法。它输出轮廓层次结构Contours 是图像中所有轮廓的 Python 列表。每个单独的轮廓都是对象边界点的 (x,y) 坐标的 Numpy 数组。

稍后我们将详细讨论第二个和第三个参数以及层次结构。到那时,代码示例中赋予它们的值将适用于所有图像。

 如何绘制轮廓

要绘制轮廓,使用 cv.drawContours 函数。如果您有边界点,它也可以用来绘制任何形状。它的第一个参数是源图像,第二个参数是应该作为 Python 列表传递的轮廓,第三个参数是轮廓的索引(在绘制单个轮廓时很有用。要绘制所有轮廓,传递 -1),其余参数是颜色厚度

  • 要绘制图像中的所有轮廓
  • 要绘制单个轮廓,请说第 4 个轮廓
  • 但大多数时候,下面的方法会有用
cv.drawContours(img, 等高线, -1, (0,255,0), 3)

cv.drawContours(img, contours, 3, (0,255,0), 3)

cnt = contours[4]
cv.drawContours(img, [cnt], 0, (0,255,0), 3)

 最后两种方法是相同的,但是当你继续前进时,你会发现最后一种更有用。为了很更高的查询和遍历吧maybe。

 轮廓近似法

这是 cv.findContours 函数中的第三个参数。它实际上表示什么?

上面,我们告诉轮廓是具有相同强度的形状的边界。它存储形状边界的 (x,y) 坐标。但它存储所有坐标吗?这是由这种轮廓近似方法指定的。

如果你传递 cv.CHAIN_APPROX_NONE所有的边界点都会被存储。但实际上我们需要所有的点吗?例如,您找到了一条直线的轮廓。你需要线上的所有点来代表那条线吗?不,我们只需要那条线的两个端点。这就是 cv.CHAIN_APPROX_SIMPLE 所做的。它删除所有冗余点并压缩轮廓,从而节省内存。

下面的矩形图像展示了这种技术。只需在轮廓数组中的所有坐标上绘制一个圆圈(以蓝色绘制)。第一张图显示了我用 cv.CHAIN_APPROX_NONE 得到的点(734 点),第二张图显示了用 cv.CHAIN_APPROX_SIMPLE 得到的点(只有 4 点)。看,它节省了多少内存!!!

轮廓特征

目标

  • 找到轮廓的不同特征,如面积、周长、质心、边界框等
  • 你会看到很多与轮廓相关的函数。

Moments

Image Moments 可以帮助您计算一些特征,例如物体的质心、物体的面积等。查看 Image Moments 上的维基百科页面

函数 cv.moments() 给出了所有计算的矩值的字典。从这一刻起,您可以提取有用的数据,如面积、质心等。质心由关系给出,C_x={}\frac{M10}{M00 }C_y={}\frac{M01}{M00 }。这可以按如下方式完成,见下文:

import numpy as np
import cv2 as cv
img = cv.imread('star.jpg',0)
ret,thresh = cv.threshold(img,127,255,0)
contours,hierarchy = cv.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv.moments(cnt)
print( M )
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print(cx,cy)

Contour Area

轮廓区域由函数 cv.contourArea() 或时刻 M['m00'] 给出。

area = cv.contourArea(cnt)

Contour Perimeter

也称为弧长。可以使用 cv.arcLength() 函数找到它。第二个参数指定 shape 是闭合轮廓(如果传递 True),还是只是一条曲线。

Contour Approximation

根据我们指定的精度,它将轮廓形状近似为具有较少顶点数的另一个形状。它是 Douglas-Peucker 算法的一个实现。

要理解这一点,假设您试图在图像中找到一个正方形,但由于图像中的一些问题,您没有得到一个完美的正方形,而是一个“坏形状”(如下图第一张所示)。现在您可以使用此函数来近似形状。其中,第二个参数称为 epsilon,它是从轮廓到近似轮廓的最大距离。它是一个精度参数。需要明智地选择 epsilon 以获得正确的输出。

epsilon = 0.1*cv.arcLength(cnt,True)
approx = cv.approxPolyDP(cnt,epsilon,True)

下面,在第二张图片中,绿线显示了 epsilon = 10% 弧长的近似曲线。第三张图片显示了 epsilon = 1%弧长。第三个参数指定曲线是否闭合

Convex Hull(凸包)

Convex Hull 看起来类似于等高线近似,但事实并非如此(在某些情况下两者可能提供相同的结果)。在这里,cv.convexHull() 函数检查曲线的凸性缺陷并进行修正。一般来说,凸曲线是始终凸出或至少平坦的曲线。如果内部鼓起,则称为凸缺陷。例如,检查下面的手图像。红线表示手的凸包。双向箭头标记显示凸性缺陷,这是船体与轮廓的局部最大偏差。

 关于它的语法,有一些事情要讨论:

hull = cv.convexHull(points[, hull[, clockwise[, returnPoints]]

参数详细信息:

  • points是我们进入的轮廓。
  • hull 是输出,通常我们会避免它。
  • clockwise方向:方向标志。如果为 True,则输出凸包为顺时针方向。否则,它是逆时针方向的。
  • returnPoints :默认情况下,True。然后它返回船体点的坐标。如果为 False,则返回与外壳点对应的轮廓点的索引。

因此,要获得如上图所示的凸包,以下就足够了:

hull = cv.convexHull(cnt)

但是如果要找凸性缺陷,需要通过returnPoints = False。为了理解它,我们将使用上面的矩形图像。首先我发现它的轮廓为cnt。现在我发现它的凸包 returnPoints = True,我得到以下值:[[[234 202]]、[[51 202]]、[[51 79]]、[[234 79]]],这是四个角矩形的点。现在,如果对 returnPoints = False 执行相同操作,我将得到以下结果:[[129]、[67]、[0]、[142]]。这些是轮廓中对应点的索引。例如,检查第一个值:cnt[129] = [[234, 202]] 与第一个结果相同(其他结果以此类推)。

Checking Convexity

有一个函数可以检查曲线是否是凸的,cv.isContourConvex()。它只返回 True 或 False。没什么大不了的。

Bounding Rectangle

它是一个直矩形,没有考虑物体的旋转。所以边界矩形的面积不会最小。它由函数 cv.boundingRect() 找到。

(x,y) 为矩形的左上角坐标(w,h) 其宽度和高度

x,y,w,h = cv.boundingRect(cnt)
cv.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)

Rotated Rectangle

在这里,边界矩形是用最小面积绘制的,所以它也考虑了旋转。使用的函数是 cv.minAreaRect()。它返回一个 Box2D 结构,其中包含以下详细信息 - (中心(x,y),(宽度,高度),旋转角度)。但是要绘制这个矩形,我们需要矩形的 4 个角。它是通过函数 cv.boxPoints() 获得的。

这块注意多了一个boxPoints的过程。

rect = cv.minAreaRect(cnt)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.drawContours(img,[box],0,(0,0,255),2)

两个矩形都显示在单个图像中。绿色矩形显示法线边界矩形。红色矩形是旋转后的矩形。

Minimum Enclosing Circle

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

(x,y),radius = cv.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv.circle(img,center,radius,(0,255,0),2)

Fitting an Ellipse

下一个是将椭圆拟合到一个对象。它返回内接椭圆的旋转矩形。

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

Fitting a Line

类似地,我们可以将一条线拟合到一组点。下图包含一组白点。我们可以近似为一条直线。

注意这块用线拟合的算法。

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值