文章目录
- 引入
- 1 如何找到轮廓
- 2 绘制轮廓
- 3 轮廓近似方法
- 4 轮廓特征
- 4.1 特征矩
- 4.2 轮廓面积
- 4.3 轮廓周长
- 4.4 轮廓近似
- 4.5 轮廓凸包
- 4.6 检查凸度
- 4.7 边界矩形
- 4.8 最小闭合圈:
- 4.9 拟合一个椭圆
- 4.10 拟合直线
- 5 轮廓属性
- 5.1 长宽比
- 5.2 范围
- 5.3 坚实度
- 5.4 等效直径
- 5.5 取向
- 5.6 掩码与像素点
- 5.7 最大值、最小值和它们的位置
- 5.8 平均颜色或平均强度
- 5.9 极端点
- 5.10 凸性缺陷
- 5.11 点多边形测试
- 5.12 形状匹配
- 参考文献
引入
轮廓可以简单地理解为连接具有相同颜色或强度的所有连续点的曲线。轮廓是用于形状分析以及对象检测和识别的工具。
注意点:
1)为了获得更高的准确性,应尽量使用二进制图像;在找到轮廓之前,需要应用阈值或canny边缘检测。
2)找到轮廓就像从黑色背景中找到白色物体。
1 如何找到轮廓
函数:indcontour(图像,轮廓检索模式,轮廓逼近方法)
返回值:边界点,层次结构
特别地,轮廓是图像中所有轮廓的python列表,每一个单独的轮廓是一个坐标的numpy数组的边界点对象。
import cv2 as cv
import numpy as np
img = cv.imread("1.jpg")
img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
_, img = cv.threshold(img, 127, 255, 0)
contours, hierarchy = cv.findContours(img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cv.imshow("", img)
cv.waitKey(0)
输出如下:
2 绘制轮廓
函数:cv.drawcontours(图像,边界点,轮廓索引[所哟均使用则输入-1],颜色、厚度)
import cv2 as cv
import numpy as np
ori_img = cv.imread("1.jpg")
img = cv.cvtColor(ori_img, cv.COLOR_BGR2GRAY)
_, img = cv.threshold(img, 127, 255, 0)
contours, hierarchy = cv.findContours(img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
cv.drawContours(img, contours, -1, (0, 255, 0), 3)
cv.imshow("", img)
cv.waitKey(0)
输出如下:
3 轮廓近似方法
轮廓近似方法有下:
1)cv.CHAIN_APPROX_NONE:存储所有点;
2)cv.CHAIN_APPROX_SIMPLE删除所有冗余点并压缩轮廓:
4 轮廓特征
4.1 特征矩
用途:帮助计算一些特征,例如物体的质心、面积。
函数:cv.moments:
import cv2 as cv
ori_img = cv.imread("1.jpg", 0)
_, img = cv.threshold(ori_img, 127, 255, 0)
cnts, _ = cv.findContours(img, 1, 2)
cnt = cnts[0]
M = cv.moments(cnt)
print(M)
例如质心的关系由以下给出:
4.2 轮廓面积
函数cv.contourArea()或者M[‘m00’]
import cv2 as cv
ori_img = cv.imread("1.jpg", 0)
_, img = cv.threshold(ori_img, 127, 255, 0)
cnts, _ = cv.findContours(img, 1, 2)
cnt = cnts[0]
m = cv.moments(cnt)
print(m["m00"])
print(cv.contourArea(cnt))
4.3 轮廓周长
函数cv.arcLength():第二个参数为True时指定为闭合轮廓,否则为曲线:
import cv2 as cv
ori_img = cv.imread("1.jpg", 0)
_, img = cv.threshold(ori_img, 127, 255, 0)
cnts, _ = cv.findContours(img, 1, 2)
cnt = cnts[0]
print(cv.arcLength(cnt, True))
4.4 轮廓近似
根据指定的精度,将轮廓形状近似为顶点数量较少的其他的形状。例如下图中,真实轮廓并不是一个完美的矩形,这时便需要使用轮廓近似。
import cv2 as cv
ori_img = cv.imread("1.jpg", 0)
_, img = cv.threshold(ori_img, 127, 255, 0)
cnts, _ = cv.findContours(img, 1, 2)
cnt = cnts[0]
epsilon = 0.1 * cv.arcLength(cnt, True)
approx = cv.approxPolyDP(cnt, epsilon, True)
print(approx)
4.5 轮廓凸包
凸包外观看起来与轮廓近似相似。
函数:cv.convexHull()检查曲线是否存在凹凸缺陷并对其进行校对。
具体地,凸曲线是始终突出或至少平坦的曲线;如果在内部突出,则称为凸度缺陷。
如下图中,红线显示手的凸包,双向箭头则表示凸度缺陷。
参数包括:
1)轮廓;
2)输出,通常忽略;
3)输出凸包的方向:True为顺时针;反正逆时针;
4)returnPoints:是否返回凸包的坐标。
如果只需要获取以上凸包,以下语句即可:
hull = cv.convexHull(cnt)
如果需要查找凸度缺陷,则需要传递returnPoints为False。
4.6 检查凸度
cv.isContourConvex()用于检查曲线是否突出:
k = cv.isContourConvex(cnt)
4.7 边界矩形
1)直角矩形:返回物体的左上角坐标、矩形的高宽:
x,y,w,h = cv.boundingRect(cnt)
cv.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
2)旋转矩形:返回结构(中心,高宽,旋转角度),若需要画出改图,则要获取矩形的四个角:
rect = cv.minAreaRect(cnt)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.drawContours(img,[box],0,(0,0,255),2)
示例如下:
4.8 最小闭合圈:
(x,y),radius = cv.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv.circle(img,center,radius,(0,255,0),2)
示例如下:
4.9 拟合一个椭圆
ellipse = cv.fitEllipse(cnt)
cv.ellipse(img,ellipse,(0,255,0),2)
示例如下:
4.10 拟合直线
rows,cols = img.shape[:2]
[vx,vy,x,y] = cv.fitLine(cnt, cv.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)
5 轮廓属性
5.1 长宽比
对象边界矩形的宽与高的比值:
x,y,w,h = cv.boundingRect(cnt)
aspect_ratio = float(w)/h
5.2 范围
轮廓区域与边界矩形区域的比值:
area = cv.contourArea(cnt)
x,y,w,h = cv.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area
5.3 坚实度
等高线面积与其凸包面积之比:
area = cv.contourArea(cnt)
hull = cv.convexHull(cnt)
hull_area = cv.contourArea(hull)
solidity = float(area)/hull_area
5.4 等效直径
面积与轮廓面积相同的圆的直径:
area = cv.contourArea(cnt)
equi_diameter = np.sqrt(4*area/np.pi)
5.5 取向
物体指向的角度:
(x,y),(MA,ma),angle = cv.fitEllipse(cnt)
5.6 掩码与像素点
构成对象的所有点:
mask = np.zeros(imgray.shape,np.uint8)
cv.drawContours(mask,[cnt],0,255,-1)
pixelpoints = np.transpose(np.nonzero(mask))
#pixelpoints = cv.findNonZero(mask)
5.7 最大值、最小值和它们的位置
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(imgray,mask = mask)
5.8 平均颜色或平均强度
mean_val = cv.mean(im,mask = mask)
5.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])
5.10 凸性缺陷
凸包上的任何偏差都可以认为是凸性缺陷。Opencv中使用cv.convexityDefects()函数来找到它:
hull = cv.convexHull(cnt,returnPoints = False)
defects = cv.convexityDefects(cnt,hull)
其返回值包含:起点、终点、最远点以及到最远点的近似距离。以上结果可以形象话如下:
import cv2 as cv
img = cv.imread("star.png")
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(img_gray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, 2, 1)
cnt = contours[0]
hull = cv.convexHull(cnt, returnPoints=False)
defects = cv.convexityDefects(cnt, hull)
for i in range(defects.shape[0]):
s, e, f, d = defects[i, 0]
start = tuple(cnt[s][0])
end = tuple(cnt[e][0])
far = tuple(cnt[f][0])
cv.line(img, start, end, [0, 255, 0], 2)
cv.circle(img, far, 5, [0, 0, 255], -1)
cv.imshow('img', img)
cv.waitKey(0)
cv.destroyAllWindows()
测试图像如下:
输出如下:
5.11 点多边形测试
找到图像中一点到轮廓线的最短距离。若在轮廓之外,则返回负;内则正;上则零:
dist = cv.pointPolygonTest(cnt,(50,50),True)
如果只是想判断在轮廓的内外关系,则将True设置为False,这样也能节省更多的时间。
5.12 形状匹配
cv.matchShapes()用于比较两个形状或者两个轮廓,并返回一个显示相似性的度量。结果越低,则匹配效果越好:
import cv2 as cv
import numpy as np
img1 = cv.imread('star.jpg',0)
img2 = cv.imread('star2.jpg',0)
ret, thresh = cv.threshold(img1, 127, 255,0)
ret, thresh2 = cv.threshold(img2, 127, 255,0)
contours,hierarchy = cv.findContours(thresh,2,1)
cnt1 = contours[0]
contours,hierarchy = cv.findContours(thresh2,2,1)
cnt2 = contours[0]
ret = cv.matchShapes(cnt1,cnt2,1,0.0)
print( ret )
参考文献
【1】Opencv中文文档。