OpenCV中的轮廓

21 篇文章 3 订阅
11 篇文章 0 订阅

a) 初始轮廓

1.什么是轮廓 轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。
为了更准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理或者Canny边界检测。
查找轮廓的函数会修改原始图像(若不想可使用img.copy()函数)。在OpenCV中,查找轮廓就像在黑色背景中找白色物体。So要找的物体应该是白色,而背景应该是黑色。
查找轮廓的函数:cv2.findContours() 参数1:输入图像 参数2:轮廓检索模式 参数3:轮廓近似方法 返回值1:输出图像
返回值2:轮廓(是一个python列表,其中存储着图像的所有轮廓,每个轮廓都是一个Numpy数组,包含对象边界点(x,y)的坐标)。
返回值3:轮廓的层析结构
2. 怎样绘制轮廓 函数:cv2.drawContours():可以根据提供的边界点绘制任何形状
第一个参数是原始图像,第二个参数是轮廓(一个Python列表),第三个参数是轮廓的索引(在绘制独立轮廓时很有用,设置为-1时绘制所有的轮廓),接下来的参数就是轮廓的颜色和厚度等。
3. 轮廓的近似方法
函数cv2.findContours()的第三个参数,上面已经说到轮廓是一个形状具有相同灰度值的边界,它会存储形状边界上所有的(x,y)坐标,但是需要将所有的边界点都存储吗?这就是这个参数要告诉函数的。
这个参数被设置为cv2.CHAIN_APPROX_NONE时,所有的边界点都会被存储。例如,当我们找的边界是一条直线时,只需要这条直线的两个端点而已。这就是将其设置为cv2.CHAIN_APPROX_SIMPLE,它会将轮廓上的冗余点都去掉,压缩轮廓,节省内存。

b) 轮廓特征
i. 矩
图像的矩可以帮助我们计算图像的质心,面积等。
函数cv2.moments()会将计算得到的矩以一个字典的形式返回。
Eg:

M = cv2.moments(contours[1])
print(M)

可以计算出对象的重心:Cx = int(M[‘m10’] / M[‘m00’])
Cy = int(M[‘m01’] / M[‘m00’])
ii. 轮廓面积
轮廓的面积可以使用函数:cv2.contourArea()计算得到,也可以使用矩(0阶矩)M[‘m00’]

area = cv2.contourArea(contours[1])
print(area)

iii.轮廓周长
也称为弧长。可以使用函数cv2.arcLength()计算得到。这个函数的第二个参数可以用来指定对象的形状是闭合的(True),还是打开的(一个曲线)

    perimeter = cv2.arcLength(contours[1], True)
    print(perimeter)

iv. 轮廓近似
将轮廓形状近似到另一种由更少点组成的轮廓形状,新轮廓的点多额数目有我们设定的准确度来决定。

    epsilon = cv2.arcLength(contours[0], True)
    approx = cv2.approxPolyDP(contours[0], epsilon, True)

v. 凸包
凸包与轮廓近似相似,但不同,虽然有些情况下他们给出的结果是一样的。函数cv2.convexHull()可以用来检测一个曲线是否具有凸性缺陷,并能纠正缺陷。
hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]
参数: points传入的轮廓
Hull输出,通常不需要
Clockwise方向标志,如果设置为True,输出的凸包是顺时针方向否则是逆时针
returnPoints默认值为True。她会返回凸包上点的坐标,如果设置为False,就会返回与凸包点对应得轮廓上的点。

hull = cv2.convexHull(contours[0])
print(hull)# [[[388 272]] [[ 21 272]] [[ 21  24]] [[388  24]]] ###1 
hull = cv2.convexHull(contours[0], returnPoints = False)
print(hull)#    [[147] [ 73] [  0] [164]]  ###2
print(contours[0][147]) # [[388 272]] ###3
###2就是###1轮廓点的索引。

vi. 凸性检测
函数:cv2.isContourConvex()可以用来检测一个曲线是不是凸的,返回True是凸的,False不是:

K = cv2.isContourConvex(contours[0])

vii. 边界矩形
1, 直边界矩形(没有旋转的矩形),使用函数cv2.boundingRect(),返回4个值,分别是矩形左上角坐标(x,y),矩形的宽和高(w,h)

    x,y,w,h = cv2.boundingRect(contours[1])
    img = cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2)

2, 旋转的边界矩形,这个边界矩形是面积最小的。使用函数cv2.minAreaRect()。返回的是一个Box2D结构,其中包含矩形左上角角点的坐标(x,y),矩形的宽和高(w,h),以及旋转角度。但是绘制这个矩形需要矩形的4个角点,可以通过函数cv2.boxPoints()获得。

    rect = cv2.minAreaRect(contours[3])
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    img = cv2.drawContours(img, [box], 0, (0,0,255), 2)

viii. 最小外接圆
函数cv2.minEnclosingCircle()可以找到一个对象的外切圆。

    (x, y), radius = cv2.minEnclosingCircle(contours[3])
    cennt = (int(x), int(y))
    radius = int(radius)
    img = cv2.circle(img, cennt, radius, (9,211,55), 2)

ix. 椭圆拟合
使用的函数为cv2.ellipse(),返回值其实就是旋转边界矩形的内切圆。

    ellipse = cv2.fitEllipse(contours[3])
    img = cv2.ellipse(img, ellipse, (0,157,157), 2)

这里写图片描述
x. 直线拟合
可以根据一组点拟合出一条直线,同样也可以为图像中的白色点拟合出一条曲线。
c) 轮廓的性质
i. 长宽比
边界矩形的宽高比;width/height。
ii. Extent
轮廓面积与边界矩形面积的比;
iii. Solidity
轮廓面积与凸包面积的比;
iv. Equivalent Diameter
与轮廓面积相等的圆形的直径

    Area = cv2.contourArea(cnt)
    Diameter = np.sqrt(4*area/np.pi)

v. 方向
对象的方向,下面的方法还会返回长轴和短轴的长度
(x,y),(MA,ma),angle = cv2.fitEllipse(cnt)
vi. 掩模和像素点
构建对象的所有像素点,如下做法:

    Mask = np.zeros(imgray.shape, np.uint8)
    Cv2.drawContours(mask, [cnt], 0, 255, -1)# 这里一定要使用参数-1,绘制填充的轮廓
    Pixelpoints = np.transpose(np.nonzero(mask))
    # or 
    Pixelpoints = cv2.findNonZero(mask)

vii. 最大值和最小值及他们的位置
可以使用掩模图像得到这些参数。
Min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray, mask=mask)
viii. 平均颜色以及平均灰度
也可以使用同样的掩模求一个对象的平均颜色或平均灰度

    Mean_val = cv2.mean(img, mask=mask)

ix. 极点
一个对象最上面,最下面,最左边,最右边的点

leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])
rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])
topmost = tuple(cnt[cnt[:,:,1].argmin()][0])
bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])

d) 轮廓:更多函数
i. 凸缺陷
对象上的任何凹陷都被称为凸缺陷。OpenCV中又一个函数cv2.convexityDefect()可以找到凸缺陷。

    Hull = cv2.convexHull(cnt, returnPoints=False)
    Defects = cv2.convexityDefects(cnt, hull)

注意:如果要查找凸缺陷,在找凸包时,参数returnPoints要为False。
它返回一个数组,其中每一行包含的值是[起点,终点,最远点,最近点的近似距离]。
这里写图片描述
ii. Point Polygon test
求解图像中的一个点到对象轮廓的最短距离,如果点在轮廓外部返回值是负数,如果在轮廓上返回0,在内部返回正数。

Dist = cv2.pointPolygonTest(cnt, (x,y), True)# (x,y)为点坐标

此函数的第三个参数是 measureDist。如果设置为 True,就会计算最短距离。如果是 False,只会判断这个点与轮廓之间的位置关系(返回值为+1, -1, 0).
注意:如果你不需要知道具体距离,建议第三个设置为False,这样速度快2,3倍。
iii. 形状匹配
函数cv2.matchShape()可以帮助我们比较两个形状或轮廓的相似度,如果返回值越小,匹配越好。它是根据Hu矩来计算的

import cv2 
import numpy as np 
img1 = cv2.imread('./image/star1.jpg', 0)
img2 = cv2.imread('./image/star2.jpg', 0)
ret, thresh = cv2.threshold(img1, 127, 255,0)
ret, thresh2 = cv2.threshold(img2, 127, 255,0)
image, contours,hierarchy = cv2.findContours(thresh,2,1)
cnt1 = contours[0]
image, contours,hierarchy = cv2.findContours(thresh2,2,1)
cnt2 = contours[0]
ret = cv2.matchShapes(cnt1,cnt2,1,0.0)
print(ret)

自己和自己匹配的话结果为0.0;即使旋转了对匹配的结果影响也不是很大。(使用函数 cv2.matchShapes() 匹配带有字母或者数字的图片)
注意:Hu矩是归一化中心距的线性组合,之所以这样做是为了能够获取代表图像的某个特征的矩函数,这些矩函数对某些变化如缩放,旋转,镜像映射(除了h1)具有不变性。
e) 轮廓的层次结构
i. 什么是层次结构
有时对象可能位于不同的位置。还有些情况,一个形状在另外一个形状的内部。这种情况下我们称外部的形状为父,内部的形状为子。按照这种方式分类,一幅图像中的所有轮廓之间就建立父子关系。这样我们就可以确定一个轮廓与其他轮廓是怎样连接的,比如它是不是某个轮廓的子轮廓,或者是父轮廓。这种关系就成为组织结构。
ii. OpenCV中层次结构
不管层次结构是什么样的,每一个轮廓都包含自己的信息:谁是父,谁是子等。OpenCV使用一个含有四个元素的数组表示。[Next,Previous,First_Child,Parent].
Next表示同一级组织结构中的下一个轮廓。
Previous表示同一级结构中的前一个轮廓。
First_Child表示它的第一个子轮廓。
Parent表示它的父轮廓。(没有就为-1)
iii. 轮廓检索模式
1. cv2.RETR_LIST :只是提取所有的轮廓,而不去创建任何父子关系。所以这种模式下,组织结构数组的第三第四个数是-1,Next和Previous要有对应的值。
2. cv2.RETR_EXTERNAL:只会返回最外面的轮廓。所有的子轮廓都会被忽略掉。
3. cv2.RETR_CCOMP:这种模式下返回所有的轮廓并将轮廓分为两级组织结构。例如:一个对象的外轮廓为第1级组织结构,而对象内部中空洞的轮廓为第2级组织结构,空洞中的任何对象的轮廓有时第1级组织结构。空洞的组织结构为第2级。
4. cv2.RETR_TREE:最完美的一个,返回所有的轮廓,并创建一个完整的组织结构列表。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值