Opencv笔记(十六)——认识轮廓

什么是轮廓?

  轮廓可以简单认为成连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。谈起轮廓不免想到边缘,它们确实很像。简单的说,轮廓是连续的,边缘并不全都连续(下图)。其实边缘主要是作为图像的特征使用,比如可以用边缘特征可以区分脸和手,而轮廓主要用来分析物体的形态,比如物体的周长和面积等,可以说边缘包括轮廓。

  • 为了准确,要使用二值化图像。需要进行阀值化处理或者Canny边界检测。
  • 查找轮廓的函数会修改原始图像。如果之后想继续使用原始图像,应该将原始图像储存到其他变量中。
  • 在OpenCV中,查找轮廓就像在黑色背景中超白色物体。你应该记住,要找的物体应该是白色而背景应该是黑色。

寻找轮廓并绘制

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

第二个参数表示轮廓的检索模式,有四种:

  • cv2.RETR_EXTERNAL表示只检测外轮廓
  • cv2.RETR_LIST检测的轮廓不建立等级关系
  • cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
  •   cv2.RETR_TREE建立一个等级树结构的轮廓。

注:opencv3会返回三个值,分别是img, countours, hierarchy。

注:cv2.findContours()函数接受的输入图像为二值图,即黑白的(不是灰度图),所以读取的图像要先转成灰度的,再转成二值图。

  使用cv2.drawContours()函数绘制轮廓,它的第一个参数是原始图像,第二个参数是轮廓,一个 Python 列表。第三个参数是轮廓的索引(在绘制独立轮廓是很有用,当设置为 -1 时绘制所有轮廓)。接下来的参数是轮廓的颜色和厚度等。

注:很多人画图时明明用了彩色,但没有效果,请检查你是在哪个图上画,画在灰度图和二值图上显然是没有彩色的。

# coding=utf-8
import cv2

img = cv2.imread("/home/wl/10.jpeg")
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转灰度图
ret, thresh = cv2.threshold(img_gray, 175, 255, 0) #转二值图
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,contours,-1,(0,255,0),3)
print len(contours)
while(1):
    cv2.imshow("img_gray",img_gray)
    cv2.imshow("thresh",img)
  #  cv2.imshow("thresh",thresh)
    cv2.imwrite("/home/wl/baocun.jpg",img_gray )
    cv2.imwrite("/home/wl/baocun1.jpg", img)
   # cv2.imwrite("/home/wl/baocun2.jpg", thresh)
    k = cv2.waitKey(1) & 0XFF
    if k==ord('q'):
        break;
cv2.destroyAllWindows()

原图:

效果图:

轮廓的近似方法

  之前提到轮廓是一个形状具有相同灰度值的边界,它会存储形状边界上所有的(x,y)坐标。实际上我们不需要所有的点,当需要直线时,找到两个端点即可。cv2.CHAIN_APPROX_SIMPLE可以实现。它会将轮廓上的冗余点去掉,压缩轮廓,从而节省内存开支。
下面用矩阵来演示,在轮廓列表中的每一个坐标上画一个蓝色圆圈。第一个显示使用cv2.CHAIN_APPROX_NONE的效果,一共734个点,第二个图是使用cv2.CHAIN_APPROX_SIMPLE的结果,只有4个点。

轮廓特征

1.矩

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

# coding=utf-8
import cv2

img = cv2.imread("/home/wl/10.jpeg")
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转灰度图
ret, thresh = cv2.threshold(img_gray, 175, 255, 0) #转二值图
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
M = cv2.moments(cnt)
print M

#{'mu02': 247555019.25, 'mu03': 0.0, 'm11': 9239924636.25, 'nu02': 0.07987551867219916, 'm12': 2599300840867.5, 'mu21': 0.0, 'mu20': 269452279.25, 'nu20': 0.08694083694083694, 'm30': 14262548547266.25,......}

计算出对象的重心:cx = int(M['m10']/M['m00'])      cy = int(M['m01']/M['m00'])

2.轮廓面积

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

area=cv2.contourArea(cnt)

3.轮廓周长

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

perimeter = cv2.arcLength(cnt,True)

4.轮廓近似

  将轮廓形状近似到另外一种由更少点组成的轮廓形状,新轮廓的点的数目由我们设定的准确度来决定,使用的Douglas-Peucker算法,可以自己Google。假设我们要在一幅图像中查找一个矩形,但是由于图像的种种原因我们不能得到一个完美的矩形,而是一个“坏形状”,现在就可以使用这个函数来近似这个形状,第二个参数是epsilon,它是从原始轮廓到近似轮廓的最大距离,它是一个准确度参数。选择一个好的 epsilon 对于得到满意结果非常重要。

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

5.凸包

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

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

参数说明:

  • points我们要传入的轮廓
  • hull输出,通常不需要
  • clockwise方向标志,如果设置为True,输出的凸包是顺时针方向的,否则为逆时针方向。
  • returnPoints默认值为True。它会返回凸包上点的坐标,如果设置为False,就会返回与凸包点对应的轮廓上的点。

6.凸性检测

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

k=cv2.isContourConvex(cnt)

7.边界矩形

  直边界矩形,一个直矩形,没有旋转。不会考虑对象是否旋转。所以边界矩形的面积不是最小的。可以使用函数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)
#(x,y)为矩形左上角的坐标,(w,h)是矩形的宽和高

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

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

8.最小外接圆

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

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

9.椭圆拟合

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

ellipse = cv2.fitEllipse(cnt) 
im = cv2.ellipse(im,ellipse,(0,255,0),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)

posted @ 2018-08-01 08:56  龙~白  阅读(717)  评论(0编辑  收藏  举报