3.8 等高线

概念

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

  • 为了获得更好的精度,请使用二进制图像。在找到等高线之前,应用阈值或canny边缘检测。
  • 等高线函数会修改源图像。所以,如果你想要源图像,即使在找到轮廓之后,也要把它存储到其他变量中。
  • 在OpenCV中,查找等高线就像从黑色背景中查找白色对象。所以记住,要找到的物体应该是白色的,背景应该是黑色的。

让我们看看如何找到二值图像的轮廓:

import numpy as np
import cv2

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

cv2.findcontours() 函数中有三个参数,
第一个是源图像
第二个是轮廓检索模式
第三个是轮廓近似方法
它输出图像、轮廓和层次。轮廓是图像中所有轮廓的python列表。每个轮廓都是对象边界点(x,y)坐标的numpy数组。

绘制等高线

使用 cv2.drawContours, 它也可以用来绘制任何给出边界点的形状。它的第一个参数是源图像,第二个参数是应该作为等高线列表,第三个参数是等高线索引(在绘制个体轮廓时使用)。要绘制所有轮廓,传递-1)、颜色、厚度等。

要绘制图像中的所有轮廓:

img = cv2.drawContours(img, contours, -1, (0,255,0), 3)

要绘制第4个轮廓:

img = cv2.drawContours(img, contours, 3, (0,255,0), 3)

但大多数情况下,下面的方法更适合:

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

等高线近似算法

这是 cv2.findContours 函数中的第三个参数的说明。

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

特征

  • Moments
import cv2
import numpy as np

img = cv2.imread('star.jpg',0)
ret,thresh = cv2.threshold(img,127,255,0)
contours,hierarchy = cv2.findContours(thresh, 1, 2)

cnt = contours[0]
M = cv2.moments(cnt)
print M

你可以提取有用的数据,如面积、质心等。质心可以通过下面的方式来获取

cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
  • 等高线面积
area = cv2.contourArea(cnt)
  • 周长
perimeter = cv2.arcLength(cnt,True)
  • 近似

它将一个轮廓形状近似为另一个形状,根据我们指定的精度,顶点数量较少。

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

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

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

image.png
  • 凸包

凸面船体看起来类似于轮廓近似,但事实并非如此(在某些情况下两者都可能提供相同的结果)。在这里,cv2.converxHull()函数检查曲线是否存在凸性缺陷,并对其进行修正。一般来说,凸曲线是指总是凸出的曲线,或者至少是平的曲线。如果它在内部膨胀,就称为凸性缺陷。例如,检查下面的手图像。红线表示手的凸面外壳。双面箭头标记显示了凸性缺陷,即船体与轮廓的局部最大偏差。

image.png
hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]

points 是我们经过的轮廓。
hull 是输出,通常我们避免它。
clockwise:方向标志。如果为真,则输出凸壳为顺时针方向。否则,它是逆时针方向的。
returnPoints:默认为真。然后返回船体点的坐标。如果为false,则返回与外壳点对应的轮廓点索引。

因此,如上图所示,要得到一个凸面外壳,以下就足够了:

hull = cv2.convexHull(cnt)
  • 检查凸度

有一个函数可以检查曲线是否是凸的,cv2.isContourConvex()。它只是返回正确还是错误。

k = cv2.isContourConvex(cnt)
  • 边框

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

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

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

在这里,边界矩形是用最小面积绘制的,因此它也考虑了旋转。使用的函数是 cv2.minareect() 。但是要画这个矩形,我们需要四个角。它是通过函数 cv2.boxpoints() 获得的。

rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
im = cv2.drawContours(im,[box],0,(0,0,255),2)
image.png
  • 最小闭圆
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
image.png
  • 拟合椭圆
ellipse = cv2.fitEllipse(cnt)
im = cv2.ellipse(im,ellipse,(0,255,0),2)
image.png
  • 拟合直线
rows,cols = img.shape[:2]
[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)
image.png

轮廓特性

在这里,我们将学习提取一些经常使用的对象属性,如坚固性、等效直径、遮罩图像、平均强度等。

  • 宽高比
x,y,w,h = cv2.boundingRect(cnt)
aspect_ratio = float(w)/h
  • 范围

范围是轮廓区域与边界矩形区域的比率。

area = cv2.contourArea(cnt)
x,y,w,h = cv2.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area
  • 坚固性

坚固性是指轮廓面积与其凸面外壳面积之比。

area = cv2.contourArea(cnt)
hull = cv2.convexHull(cnt)
hull_area = cv2.contourArea(hull)
solidity = float(area)/hull_area
  • 等效直径

等效直径是面积与轮廓面积相同的圆的直径。

area = cv2.contourArea(cnt)
equi_diameter = np.sqrt(4*area/np.pi)
  • 方向

方向是指向对象的角度。下面的方法也给出了长轴和短轴的长度。

(x,y),(MA,ma),angle = cv2.fitEllipse(cnt)
  • 掩模和像素点

在某些情况下,我们可能需要包含该对象的所有点。可以这样做:

mask = np.zeros(imgray.shape,np.uint8)
cv2.drawContours(mask,[cnt],0,255,-1)
pixelpoints = np.transpose(np.nonzero(mask))
#pixelpoints = cv2.findNonZero(mask)

这里,有两种方法,一种是使用numpy函数,另一种是使用opencv函数(最后一行注释)。结果也相同,但有轻微的差异。numpy以(行、列)格式给出坐标,而opencv以(x、y)格式给出坐标。所以基本上答案会互换。注意,row=x,column=y。

  • 最大值、最小值和他们的位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray,mask = mask)
  • 平均颜色或平均强度
mean_val = cv2.mean(im,mask = mask)
  • 极值点

极端点是指物体的最上面、最下面、最右边和最左边的点。

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])
image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容