OpenCV学习笔记(九)——图像轮廓(上)
在计算轮廓时,可能并不需要实际的轮廓,而仅需要一个接近于轮廓的近似多边形。OpenCV 提供了多种计算轮廓近似多边形的方法。
x,y,w,h = cv2.boundingRect( array )
import cv2
import numpy as np
#---------------读取并显示原始图像------------------
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
#---------------提取图像轮廓------------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
image,contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#---------------构造矩形边界------------------
x,y,w,h = cv2.boundingRect(contours[0])
brcnt = np.array([[[x, y]], [[x+w, y]], [[x+w, y+h]], [[x, y+h]]])
cv2.drawContours(o, [brcnt], -1, (255, 255,255), 2)
#---------------显示矩形边界------------------
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
retval =cv2.minAreaRect( points )
需要注意,返回值retval的结构不符合函数cv2.drawContours()的参数结构要求。因此,必须将其转换为符合要求的结构,才能使用。函数cv2.boxPoints()能够将上述返回值retval转换为符合要求的结构。函数cv2.boxPoints()的语法格式是:
points = cv2.boxPoints( box )
import cv2
import numpy as np
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
image,contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
rect = cv2.minAreaRect(contours[0])
print("返回值rect:\n",rect)
points = cv2.boxPoints(rect)
print("\n 转换后的points:\n",points)
points = np.int0(points) #取整
image=cv2.drawContours(o,[points],0,(255,255,255),2)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
同时,程序还会输出如下结果:
返回值 rect:
((280.3699951171875, 138.58999633789062), (154.99778747558594,
63.78103256225586), -8.130102157592773)
转换后的 points:
[[208.16002 181.12 ]
[199.14 117.979996]
[352.57996 96.06 ]
[361.59998 159.2 ]]
center, radius = cv2.minEnclosingCircle( points )
import cv2
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
image,contours, hierarchy = cv2.findContours(binary,
cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
(x,y),radius = cv2.minEnclosingCircle(contours[0])
center = (int(x),int(y))
radius = int(radius)
cv2.circle(o,center,radius,(255,255,255),2)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
retval = cv2.fitEllipse( points )
import cv2
o = cv2.imread('cc.bmp')
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
image,contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
cv2.imshow("original",o)
ellipse = cv2.fitEllipse(contours[0])
print("ellipse=",ellipse)
cv2.ellipse(o,ellipse,(0,255,0),3)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
line = cv2.fitLine( points, distType, param, reps, aeps )
import cv2
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
image,contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
[vx,vy,x,y] = cv2.fitLine(contours[0], cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv2.line(o,(cols-1,righty),(0,lefty),(0,255,0),2)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
retval, triangle = cv2.minEnclosingTriangle( points )
import cv2
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
image,contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
area,trgl = cv2.minEnclosingTriangle(contours[0])
print("area=",area)
print("trgl:",trgl)
for i in range(0, 3):
cv2.line(o, tuple(trgl[i][0]),tuple(trgl[(i + 1) % 3][0]), (255,255,255), 2)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
显示如下的运行结果:
area= 12904.00390625
trgl: [[[193.25641 107. ]]
[[222.58975 211. ]]
[[441.41025 107. ]]]
approxCurve = cv2.approxPolyDP( curve, epsilon, closed )
函数 cv2.approxPolyDP()采用的是Douglas-Peucker 算法(DP 算法)。以下图为例,该算法首先从轮廓中找到距离最远的两个点,并将两点相连(见(b)图)。接下来,在轮廓上找到一个离当前直线最远的点,并将该点与原有直线连成一个封闭的多边形,此时得到一个三角形,如(c)图所示。
将上述过程不断地迭代,将新找到的距离当前多边形最远的点加入到结果中。当轮廓上所有的点到当前多边形的距离都小于函数cv2.approxPolyDP()的参数epsilon 的值时,就停止迭代。最终可以得到如图12-23 的(f)图所示的处理结果。
通过上述过程可知,epsilon 是逼近多边形的精度信息。通常情况下,将该精度设置为多边形总长度的百分比形式。
import cv2
#----------------读取并显示原始图像-------------------------------
o = cv2.imread('cc.bmp')
cv2.imshow("original",o)
#----------------获取轮廓-------------------------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
image,contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#----------------epsilon=0.1*周长-------------------------------
adp = o.copy()
epsilon = 0.1*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.1",adp)
#----------------epsilon=0.09*周长-------------------------------
adp = o.copy()
epsilon = 0.09*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.09",adp)
#----------------epsilon=0.055*周长-------------------------------
adp = o.copy()
epsilon = 0.055*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.055",adp)
#----------------epsilon=0.05*周长-------------------------------
adp = o.copy()
epsilon = 0.05*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.05",adp)
#----------------epsilon=0.02*周长-------------------------------
adp = o.copy()
epsilon = 0.02*cv2.arcLength(contours[0],True)
approx = cv2.approxPolyDP(contours[0],epsilon,True)
adp=cv2.drawContours(adp,[approx],0,(0,0,255),2)
cv2.imshow("result0.02",adp)
#----------------等待释放窗口-------------------------------
cv2.waitKey()
cv2.destroyAllWindows()
逼近多边形是轮廓的高度近似,但是有时候,我们希望使用一个多边形的凸包来简化它。凸包跟逼近多边形很像,只不过它是物体最外层的“凸”多边形。凸包指的是完全包含原有轮廓,并且仅由轮廓上的点所构成的多边形。凸包的每一处都是凸的,即在凸包内连接任意两点的直线都在凸包的内部。在凸包内,任意连续三个点的内角小于180°。
hull = cv2.convexHull( points[, clockwise[, returnPoints]] )
import cv2
# --------------读取并绘制原始图像------------------
o = cv2.imread('hand.bmp')
cv2.imshow("original",o)
# --------------提取轮廓------------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
image,contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
# --------------寻找凸包------------------
hull = cv2.convexHull(contours[0])
# --------------绘制凸包------------------
cv2.polylines(o, [hull], True, (0, 255, 0), 2)
# --------------显示凸包------------------
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
凸包与轮廓之间的部分,称为凸缺陷。
convexityDefects = cv2.convexityDefects( contour, convexhull )
import cv2
#----------------原图--------------------------
img = cv2.imread('hand.bmp')
cv2.imshow('original',img)
#----------------构造轮廓--------------------------
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255,0)
image,contours, hierarchy = cv2.findContours(binary,
cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
#----------------凸包--------------------------
cnt = contours[0]
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
print("defects=\n",defects)
#----------------构造凸缺陷--------------------------
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])
cv2.line(img,start,end,[0,0,255],2)
cv2.circle(img,far,5,[255,0,0],-1)
#----------------显示结果,释放图像--------------------------
cv2.imshow('result',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
retval = cv2.isContourConvex( contour )
retval = cv2.pointPolygonTest( contour, pt, measureDist )
OpenCV学习笔记(九)——图像轮廓(下)