概念
等高线可以简单地解释为连接所有连续点(沿着边界)的曲线,具有相同的颜色或强度。等高线是形状分析、目标检测和识别的有用工具。
- 为了获得更好的精度,请使用二进制图像。在找到等高线之前,应用阈值或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%相同。第三个参数指定曲线是否关闭。
- 凸包
凸面船体看起来类似于轮廓近似,但事实并非如此(在某些情况下两者都可能提供相同的结果)。在这里,cv2.converxHull()函数检查曲线是否存在凸性缺陷,并对其进行修正。一般来说,凸曲线是指总是凸出的曲线,或者至少是平的曲线。如果它在内部膨胀,就称为凸性缺陷。例如,检查下面的手图像。红线表示手的凸面外壳。双面箭头标记显示了凸性缺陷,即船体与轮廓的局部最大偏差。
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)
- 最小闭圆
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)
- 拟合椭圆
ellipse = cv2.fitEllipse(cnt)
im = cv2.ellipse(im,ellipse,(0,255,0),2)
- 拟合直线
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)
轮廓特性
在这里,我们将学习提取一些经常使用的对象属性,如坚固性、等效直径、遮罩图像、平均强度等。
- 宽高比
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])