opencv图像轮廓

1.1什么是轮廓   cv2.findContours()

轮廓可以简单认为成连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。为了准确,要使用二值化图像。需要进行阀值化处理或者Canny边界检测。查找轮廓的函数会修改原始图像。如果之后想继续使用原始图像,应该将原始图像储存到其他变量中。在OpenCV中,查找轮廓就像在黑色背景中超白色物体。你应该记住,要找的物体应该是白色而背景应该是黑色。

如何在一个二值图像中查找轮廓。

函数cv2.findContours()有三个参数,第一个是输入图像,第二个是轮廓检索模式,第三个是轮廓近似方法。返回值有三个,第一个是图像,第二个是轮廓,第三个是(轮廓的)层析结构。轮廓(第二个返回值)是一个Python列表,其中储存这图像中所有轮廓。每一个轮廓都是一个Numpy数组,包含对象边界点(x,y)的坐标。

1.2怎样绘制轮廓 cv2.drawContours()

函数cv2.drawContours()可以被用来绘制轮廓。它可以根据你提供的边界点绘制任何形状。它的第一个参数是原始图像,第二个参数是轮廓,一个python列表,第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设置为-1时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度。

在一幅图像上绘制所有的轮廓:

img = cv2.imread('gray.png')

imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

ret,thresh = cv2.threshold(imgray,127,255,0)

image ,contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

#绘制所有轮廓

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

#绘制独立轮廓,如第四个轮廓

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

#但是大多数时候,下面方法更有用

cnt = contours[3]

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

while(1):

    cv2.imshow('img',img)

    cv2.imshow('imgray',imgray)

    cv2.imshow('image',image)

    cv2.imshow('imag',imag)

    if cv2.waitKey(1) == ord('q'): break

cv2.destroyAllWindows()


1.3轮廓的近似方法

之前提到轮廓是一个形状具有相同灰度值的边界,它会存储形状边界上所有的(x,y)坐标。实际上我们不需要所有的点,当需要直线时,找到两个端点即可。cv2.CHAIN_APPROX_SIMPLE可以实现。它会将轮廓上的冗余点去掉,压缩轮廓,从而节省内存开支。

2. 轮廓特征

2.1矩  cv2.moments()

图像的矩可以帮助我们计算图像的质心,面积等。

函数cv2.moments()会将计算得到的矩以一个字典的形式返回。

img = cv2.imread('rectangle.png',0)

ret,thresh = cv2.threshold(img,127,255,0)

image,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'])

2.2轮廓面积  cv2.contourArea()

可以使用函数cv2.contourArea()计算得到,也可以用矩(0阶矩),M['m00']。

area=cv2.contourArea(cnt)

2.3轮廓周长  cv2.arcLength()

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

perimeter= cv2.arcLength(cnt,True)

2.4轮廓近似  cv2.approxPolyDP()

将轮廓形状近似到另外一种由更少点组成的轮廓形状,新轮廓的点的数目由我们设定的准确度来决定,使用的Douglas-Peucker算法。

算法步骤

连接曲线首尾两点A、B形成一条直线AB;

计算曲线上离该直线段距离最大的点C,计算其与AB的距离d;

比较该距离与预先给定的阈值threshold的大小,如果小于threshold,则以该直线作为曲线的近似,该段曲线处理完毕。

如果距离大于阈值,则用点C将曲线分为两段AC和BC,并分别对两段曲线进行步骤[1~3]的处理。

当所有曲线都处理完毕后,依次连接各个分割点形成折线,作为原曲线的近似。

假设我们要在一幅图像中查找一个矩形,但是由于图像的种种原因我们不能得到一个完美的矩形,而是一个“坏形状”,现在就可以使用这个函数来近似这个形状,第二个参数是epsilon,它是从原始轮廓到近似轮廓的最大距离,它是一个准确度参数。

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

approx= cv2.approxPolyDP(cnt,epsilon,True)

temp = cv2.cvtColor(image.copy(), cv2.COLOR_GRAY2BGR)

cv2.drawContours(temp, [approx], -1, (0, 255, 0), 2)

epsilon2=0.01*cv2.arcLength(cnt,True)

approx2= cv2.approxPolyDP(cnt,epsilon2,True)

temp2 = cv2.cvtColor(image.copy(), cv2.COLOR_GRAY2BGR)

cv2.drawContours(temp2, [approx2], -1, (0, 255, 0), 2)

plt.subplot(231),plt.imshow(img,'gray'),plt.title('original')

plt.subplot(232),plt.imshow(temp,'gray'),plt.title(' epsilon = 10%')

plt.subplot(233),plt.imshow(temp2,'gray'),plt.title(' epsilon = 1%')

plt.show()

2.5凸包  cv2.convexHull()

凸包与轮廓近似相似,但不同,虽然有些情况下它们给出的结果是一样的。函数cv2.convexHull()可以用来检测一个曲线是否具有凸性缺陷,并能纠正缺陷。一般来说,凸性曲线总是凸出来的,至少是平的。如果有地方凹进去了就被叫做凸性缺陷。例如下图中的手,红色曲线显示了手的凸包,凸性缺陷被双箭头标出来了。

hull= cv2.convexHull(points,hull,clockwise,returnPoints)

参数:

points我们要传入的轮廓

hull输出,通常不需要

clockwise方向标志,如果设置为True,输出的凸包是顺时针方向的,否则为逆时针方向。

returnPoints默认值为True。它会返回凸包上点的坐标,如果设置为False,就会返回与凸包点对应的轮廓上的点。

要获得上图的凸包,可以用下面命令:

hull=cv2.convexHull(cnt)

但是如果你想获得凸性缺陷,需要把returnPoints设置为False。以上面矩形为例,首先我们找到他的轮廓从cnt。现在把returnPoints设置为True查找凸包,得到的就是矩形的四个角点。把returnPoints设置为False,得到的是轮廓点的索引。

2.6凸性检测  cv2.isContourConvex()

函数cv2.isContourConvex()可以检测一个曲线是不是凸的。它只能返回True或者False。

k=cv2.isContourConvex(cnt)

2.7边界矩形   cv2.boundingRect()   cv2.minAreaRect()

直边界矩形,一个直矩形,没有旋转。不会考虑对象是否旋转。所以边界矩形的面积不是最小的。可以使用函数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.minAreaRect()。返回的是一个Box2D结构,其中包含矩形最上角角点坐标(x,y)矩形的宽和高(w,h)以及旋转角度。但是要绘制这个矩形需要矩形的4个角点,可以通过函数cv2.boxPoints()获得。

其中绿色的为直矩形,红色为旋转矩形。

2.8最小外接圆  cv2.minEnclosingCircle()

函数cv2.minEnclosingCircle()可以帮我们找到一个对象的外接圆。它是所有能够包括对象的圆中面积最小的一个。

(x,y),radius = cv2.minEnclosingCircle(cnt)

center = (int(x),int(y))

radius = int(radius)

img = cv2.circle(img,center,radius,(0,255,0),2)

2.9椭圆拟合  cv2.ellipse()

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

ellipse= cv2.fitEllipse(cnt)

img= cv2.ellipse(img,ellipse,(0,255,0),2)

2.10直线拟合

可以根据一组点拟合出一条直线,同样我们也可以为图像中的白色点拟合出一条直线。

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)

3.轮廓的性质

3.1长宽比   cv2.boundingRect()

边界矩形的宽高比

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

aspect_ratio = float(w)/h

3.2.Extent   cv2.contourArea()   cv2.boundingRect()

轮廓面积与边界矩形面积的比

area=cv2.contourArea(cnt)

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

rect_area=w*h

extent=float(area)/rect_area

3.3.Solidity    cv2.contourArea()    cv2.convexHull()

轮廓面积与凸包面积的比

area=cv2.contourArea(cnt)

hull=cv2.convexHull(cnt)

hull_area=cv2.contourArea(hull)

solidity=float(area)/hull_area

3.4.与轮廓面积相等的圆形的直径   cv2.contourArea() 

area=cv2.contourArea(cnt)

equi_diameter=np.sqrt(4*area/np.pi)

3.5.方向  cv2.fitEllipse()

对象的方向,下面的方法还会返回长轴和短轴的长度

(x,y),(MA,ma),angle=cv2.fitEllipse(cnt)

3.6.掩模和像素点

有时我们需要构成对象的所有像素点

mask=np.zeros(imgray.shate,np.uint8)

#这里一定要使用参数-1,绘制填充的轮廓

cv2.drawContours(mask,[cnt],0,255,-1)

pixelpoints=np.transpose(np.nonzero(mask))

3.7.最大值和最小值及它们的位置  cv2.minMaxLoc()

可以使用掩模图像得到这些参数

min_val,max_val,min_loc,max_loc=cv2.minMaxLoc(imgray,mask=mask)

3.8.平均颜色及平均灰度   cv2.mean()

同样使用相同的掩模来求得

mean_val=cv2.mean(im,mask=mask)

3.9.极点

一个对象最上,最下,最左,和最右的点

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])

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

推荐阅读更多精彩内容