目录
一、轮廓
1、什么是轮廓
2、怎么绘制轮廓
3、轮廓的近似方法
二、轮廓特征
1、矩
2、轮廓面积
3、轮廓的周长(弧长)
4、轮廓近似
5、凸包、凸性检测
6、边界矩形
7、最小外接圆
8、椭圆拟合
9、直线拟合
轮廓可以简单认为是成为连续的点(连着边界)连在一起的曲线,具有相同的颜色或者是灰度。轮廓在形状分析和物体识别方面中很有用。
找到轮廓:contours, hierarchy = cv2.findContours(thresh, mode, method)
注意:opencv2返回两个值:contours:hierarchy;opencv3会返回三个值,分别是img, countours, hierarchy
绘制轮廓:cv2.drawContours( img, contours , contourIdx , color(r,g,b),thickness)
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('outline1.png')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray,127,255,0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
imag = cv2.drawContours(img,contours,-1,(0,255,0),3)
while True:
cv2.imshow('img',img)
cv2.imshow('imag', imag)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
对于每一个轮廓的counters都会有一个这样对应列表:
之前 提到的轮廓是一个形状具有相同灰度值的边界,它会存储形状边界上的所有的(x,y)坐标。实际上,我们不需要所有的点,当需要直线的时候,找到两个端点就可。cv2.CHAIN_APPROX_SIMPLE可以实现。它会将轮廓上的冗余点去掉,压缩轮廓,从而节约内存开支。
图像的矩可以帮助我们计算图像的质心、面积等
函数cv2.moments()会将计算得到的矩以一个字典的形式返回
import cv2
img = cv2.imread('rectangle.png',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)
print(len(M))
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print(cx,cy)
#输出
{'m00': 135072.0, 'm10': 54501552.0, 'm01': 34443360.0, 'm20': 24467257248.0, 'm11': 13897895760.0, 'm02': 9716674464.0, 'm30': 11870574279480.0, 'm21': 6239150598240.0, 'm12': 3920678146224.0, 'm03': 2953896996960.0, 'mu20': 2475881016.0, 'mu11': -1.9073486328125e-06, 'mu02': 933617663.9999981, 'mu30': 0.0, 'mu21': 0.0018310546875, 'mu12': 0.00079345703125, 'mu03': 0.00048828125, 'nu20': 0.13570601851851852, 'nu11': -1.0454407429638942e-16, 'nu02': 0.0511727078891257, 'nu30': 0.0, 'nu21': 2.7307880219458127e-16, 'nu12': 1.1833414761765188e-16, 'nu03': 7.2821013918555e-17}
24
403 255
我们计算对象的重心:
函数cv2.contourArea()计算得到
import cv2
img = cv2.imread('rectangle.png',0)
ret, thresh = cv2.threshold(img, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
area = cv2.contourArea(cnt)
print(area)
#输出
135072.0
函数cv2.arcLength(cnt, True)计算周长(闭合:True)
import cv2
img = cv2.imread('rectangle.png',0)
ret, thresh = cv2.threshold(img, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
perimeter = cv2.arcLength(cnt, True)
print(perimeter)
#输出
1514.0
将轮廓形状近似到另外一种由更少点组成的轮廓形状,新轮廓形状的点的数目由我们设定的准确度来决定。
假设我们要在一幅图片中查找一个矩形,但是由于图像的种种原因我们找不到一个完美的矩形,而是一个鼓鼓的矩形,那么我们可以用函数来近似这个形状:
epsilon = 0.1*cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
import cv2
img = cv2.imread('rectangle.png',0)
ret, thresh = cv2.threshold(img, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
epsilon = 0.1*cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
print(approx)
#输出
[[[169 111]]
[[169 399]]
[[638 399]]
[[638 111]]]
暂不处理
直边界矩形,一个直矩形,没有旋转,所以直矩形的面积不是最小的。可以通过函数找到:
注意注意:千万不要直接img = cv2.imread('outline2.png',0)这样读取一个灰度图片,这样,你在画图像的时候你会发现,我为什么画出来的只有灰度线条!
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x,y), (x+w,y+h),(0,0,255),2)
rect = cv2.minAreaRect(cnt)
box = np.int0(cv2.boxPoints(rect))
cv2.polylines(img,[box],True,(255,0,0),2)
import numpy as np
import cv2
img = cv2.imread('outline2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
#画出直形矩形
x,y,w,h = cv2.boundingRect(cnt)
print(x,y,w,h)
cv2.rectangle(img, (x,y), (x+w,y+h),(0,0,255),2)
#画出最小矩形(由画多边形的代码画出)
rect = cv2.minAreaRect(cnt)
box = np.int0(cv2.boxPoints(rect))
print(box)#矩形的四个点
cv2.polylines(img,[box],True,(255,0,0),2)
while True:
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
#输出
161 52 545 400
[[191 480]
[ 93 313]
[663 -19]
[761 148]]
(x,y), radius = cv2.minEnclosingCircle(cnt)
得到圆心坐标和半径
import cv2
img = cv2.imread('outline2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
(x,y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv2.circle(img, center, radius, (0,255,0),2)
while True:
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img, ellipse,(0,255,0),2)
import cv2
img = cv2.imread('outline2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img, ellipse,(0,255,0),2)
while True:
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
可以根据一组点拟合出一条直线,同样我们也可以为图像中的白色点拟合出一条直线
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)
cv2.line(img, (cols-1,righty),(0,lefty),(0,255,0),5)
import cv2
img = cv2.imread('outline2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
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)
cv2.line(img, (cols-1,righty),(0,lefty),(0,255,0),5)
print((cols-1,righty),(0,lefty))
while True:
cv2.imshow('image',img)
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
#输出
(797, -12) (0, 571)