图像轮廓是指将边缘连接起来形成的一个整体,用于后续的计算。因为边缘检测得到的边缘是不连续的。
查找图像内的轮廓信息: cv2.findContours()
绘制轮廓: cv2.drawContours()
image,contours,hierarchy=cv2.findContours (image,mode,method)
返回值:
image: 与函数参数中image的原始图像一致,但在OpenCV 4.x中,这个返回值已经被取消。
contours: 返回的轮廓。每个轮廓由若干个点构成,contours[i][j是第i个轮廓内的第j个点,contours的type属性是list
hierarchy :图像的拓扑信息(轮廓层次),外部的轮廓成为父轮廓,内部的轮廓成为子轮廓。
每个轮廓contours[i]对应4个元素来说明当前轮廓的层次信息:[Next,Previous,First_Child,Parent],没有对饮关系时,参数值为-1,其含义为:
(1)Next: 后一个轮廓的索引编号
(2)Previous: 前一个轮廓的索引编号
(3)First_Child: 第一个子轮廓的索引编号
(4)Parent: 父轮廓的索引编号
image: 必须是8位单通道的二值图像
mode: 轮廓检索模式
(1) cv2.RETR_EXTERNAL: 只检测外轮廓。
(2)cv2.RETR_LIST: 对检测到的轮廓不建立等级关系。
(3)cv2.RETR_cCOMP: 检索所有轮廓并将它们组织成两级层次结构
(4)cv2.RETR_TREE: 建立一个等级树结构的轮廓
method: 轮廓的近似方法
(1)cv2.CHAIN_APPROX_NONE: 存储所有的轮廓点,相邻两个点的像素位置差不超过1。
(2)cv2.CHAIN_APPROX_SIMPLE: 压缩水平方向、垂直方向、对角线方向的元素,只保留该方向的终点坐标。
(3)cv2.CHAIN_APPROX_TC89_L1: 使用teh-Chinl chain近似算法的一种风格。
(4)cv2.CHAIN_APPROX_TC89_KCOS: 使用teh-Chinl chain近似算法的一种风格。
注意: 待处理图像必须是灰度二值图,对象必须是白色,背景必须是蓝色
image=cv2.drawContours (image,contours,contourldx,color[,thickness[,lineType[,hierarchy[,maxLevel[,offset]]]]])
contours: 需要绘制的轮廓
contourldx: 边缘索引,参数为整数或者0,表示绘制对应索引号的轮廓,值为负数-1,表示绘制全部轮廓
color: 绘制的颜色
thickness: 绘制轮廓时所用的画笔粗细。默认为-1,表示绘制实心轮廓
lineType: 绘制轮廓的线型
hierarchy: 对应查找函数输出的层次信息
maxLevel: 控制所绘制的轮廓层次的深度,为0则表示第0层的轮廓,为其他的非零正数则表示绘制最高层及以下的相同数量层级的轮廓
offset:偏移参数,使轮廓偏移到不同的位置展示出来
示例:绘制前景图像对应的实心轮廓,将实心轮廓与原始图像按位与操作,即可将前景图像从原始图像中提取出来
import cv2
import numpy as np
o=cv2.imread ('loc3.png')
cv2.imshow ("original",o)
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY) #图像从BGR转为灰度
ret,binary = cv2.threshold(gray,127,255, cv2.THRESH_BINARY) #阈值处理为二值图像
contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE) #查找图像轮廓
mask=np.zeros (o.shape,np.uint8)
mask=cv2.drawContours(mask, contours,-1,(255,255,255),-1) #绘制图像轮廓
cv2.imshow ("mask",mask)
loc=cv2.bitwise_and(o, mask) #按位与得到提取的原图像的前景图像
cv2.imshow ("location",loc)
cv2.waitKey ()
cv2.destroyAllWindows ()
结果:
使用函数cv2.moments()获取的轮廓特征称为**“轮廓矩”,轮廓矩是描述一个轮廓的重要特征,使用轮廓矩可以方便的比较两个轮廓**。
retval=cv2.moments (array[,binarylmage])
array: 可以是点集,也可以是灰度图像或者二值图像。
binarylmage: 参数为Ture时,array内所有非零值都被处理为1,参数仅在array为图像是有效。
retval: 返回值是特征。
(1)空间矩
零阶矩:m00(表示轮廓面积)
一阶矩:m10,m01·二阶矩:m20,m11,m02
三阶矩:m30,m21,m12,m03
(2)中心矩(中心矩通过减去均值二获得平移不变性,可以比较不同位置的两个图像是否一致)
二阶中心矩:mu20,mu 11,mu02
三阶中心矩:mu30,mu21,mu12,mu03
(3)归一化中心矩 (通过除以物体总尺寸获得放缩不变性,该属性具有平移不变性和放缩不变性)
二阶Hu矩:nu20,nu11,nu02
三阶Hu矩:nu30,nu21,nu12,nu03
示例:提取图像的特征
import cv2
import numpy as np
o = cv2 .imread ('moments.png')
cv2.imshow ( "original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY) #转化为灰度图像
ret,binary = cv2.threshold (gray,127,255,cv2.THRESH_BINARY) #转为二值图像
contours,hierarchy = cv2.findContours (binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE) #寻找轮廓
n=len(contours) #轮廓个数
contoursImg=[]
for i in range (n) :
temp=np.zeros(binary.shape,np.uint8) #生成黑背景
contoursImg.append(temp)
contoursImg [i]=cv2.drawContours (contoursImg[i],contours,i,255,3) #绘制轮廓
cv2.imshow ( "contours[" + str(i)+"]",contoursImg[i]) #显示轮廓
print ("观察各个轮廓的面积: ")
for i in range (n) :
print("轮廓"+str(i)+"的面积:%d" %cv2.moments (contours[i])['m00']) #显示轮廓的面积
cv2.waitKey ()
cv2.destroyAllWindows ()
retval=cv2.contourArea(contour[,oriented]))
retval: 返回值为面积
contour: 轮廓
oriented: 布尔型,Ture时,返回的值包括正负号,用来表示轮廓是顺时针还是逆时针。默认值为False,返回的是一个绝对值
retval=cv2.arcLength (curve,closed)
retval: 轮廓的长度(周长)
curve: 轮廓
closed: 布尔型值,Ture表示轮廓是封闭
Hu矩是归一化中心矩的线性组合,Hu可以保持矩的不变性
hu=cv2.HuMoments(m)
hu: 返回的Hu矩值
m: 由cv.2moments()计算得到矩特征值
二阶Hu矩: v20,v11,v02
三阶Hu矩: v30,v21,v12,v03
cv2.matchShapes () ,对两个对象的Hu矩进行比较。
retval=cv2.matchShapes (contour1,contour2,method,parameter)
contour1 : 第1个轮廓或者灰度图像。
contour2: 第2个轮廓或者灰度图像。
method: 比较两个对象的Hu矩的方法
A表示对象1,B表示对象2
parameter: 应用于method的特定参数,为拓展参数,但OpenCV4.1.0不支持该参数,设置为0。
在计算轮廓时候,仅需要一个接近于轮廓的近似多边形。
cv2.boundingRect ()函数能够绘制轮廓的矩形边界。
retval=cv2.boundingRect (array)
retval: 返回的矩形边界的左上角顶点的坐标值及矩形边界的宽度和高度 (元组形式)
array: 灰度图像或轮廓
还可以有四个返回值:
x, y,w,h=cv2.boundingRect ( array)
矩形边界左上角顶点的x坐标。
矩形边界左上角顶点的y坐标。
矩形边界的x方向的长度。
矩形边界的y方向的长度。
示例:绘制矩形包围框
import cv2
#一———-----------读取并显示原始图像--
o = cv2.imread('cc.png')
cv2.imshow ( "original", o)
#一——------------提取图像轮廓-------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret,binary = cv2.threshold (gray,127,255, cv2.THRESH_BINARY)
contours,hierarchy = cv2.findContours (binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
#一—-------------构造矩形边界-------------
x,y,w,h = cv2.boundingRect (contours[0])
cv2.rectangle(o, (x, y), (x+w, y+h),(255,255,255),2)
#一―-------------显示矩形边界-------------
cv2.imshow ( "result",o)
cv2.waitKey ()
cv2.destroyAllWindows ()
retval=cv2.minAreaRect (points)
retval: 返回的矩形特征信息,结构为:最小外接矩形的中心(x,y),(宽度,高度),旋转角度
points: 轮廓
cv2,boxPoints() 函数可以将v2.minAreaRect()函数的返回值retval转换为符合要求的结构
points=cv2.boxPoints ( box)
points: 能够用于函数cv2.drawContours()参数的轮廓点
box: cv2.minAreaRect ()返回值的类型的值。
示例:用cv2.minAreaRect()计算图像的最小包围矩形框
import cv2
import numpy as np
o = cv2.imread ( 'cc.png')
cv2.imshow ( "original" , o)
gray = cv2.cvtColor (o, cv2.COLOR_BGR2GRAY)
ret,binary = cv2.threshold(gray,127,255, cv2.THRESH_BINARY)
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: ln" , points)
points = np.int0(points) #取整
image=cv2.drawContours(o, [points],0,(255,255,255),2)
cv2.imshow ( "result",o)
cv2.waitKey ()
cv2.destroyAllWindows ()
函数cv2.minEnclosingCircle()通过迭代算法构造一个对象的面积最小包围圆形 。
center,radius=cv2.minEnclosingCircle (points)
center: 最小包围圆形的中心
radius: 最小包围圆形的半径
points: 轮廓
示例:构造图像的最小包围圆形
import cv2
o = cv2.imread ('cc.png' )
cv2.imshow ("original",o)
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
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)
retval: 是RotaedRect类型的值
points: 轮廓
line=cv2.fitLine (points,distType,param,reps,aeps)
line: 返回的是最优拟合直线参数
points: 轮廓
distType: 距离类型
param: 距离参数,当此参数为0时,函数会自动选择最优值
reps: 拟合直线所需要的径向精度,通常为0.01
aeps: 拟合直线所需要的角度精度,通常为0.01
示例:构造最优拟合直线
import cv2
o= cv2.imread ( 'cc.png')
cv2.imshow ( "original",o)
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours,hierarchy = cv2.findContours (binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
rows,cols = binary.shape [ :2]
[vx,vy,x,y] = cv2.fitLine (contours[0],cv2.DIST_L2,0,0.01,0.01) #vx和vy是有关直线与y正半轴夹角的三角函数tan 。 ,x和y是拟合直线上一点的横纵坐标
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) #(cols-1,righty)起点,(0,lefty)终点
cv2.imshow ( "result",o)
cv2.waitKey ()
cv2.destroyAllWindows ()
retval,triangle=cv2.minEnclosingTriangle (points)
retval: 最小外包三角形的面积
triangle: 最小外包三角形的三个顶点集
points: 轮廓
approxCurve=cv2.approxPolyDP (curve,epsilon,closed)
approxCurve: 逼近多边形的点集
curve: 轮廓
epsilon: 精度,原始轮廓的边界点与逼近多边形边界之间的最大距离。设置为多边形总长度的百分比形式。
closed: True时,逼近多边形封闭,否则不封闭
该函数采用DP算法,此算法首先从轮廓中找到距离最远的两个点,两点相连,在轮廓上找一个离当前直线最远的点,并将该点与原有直线连成一个封闭的图形,此过程不断迭代,当轮廓上所有的点到当前多边形的距离都小于参数epsilon的值时,停止迭代。
凸包是指完全包含原有轮廓,并且仅由轮廓上的点所构成的多边形。凸包内任意三点的内角和小于180°。
如图为凸包示意图,机械手边缘与凸包之间的部分称为凸缺陷,凸缺陷可以用来处理手势识别等问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ItLFCXS4-1659528877458)(D:\python\opencv学习笔记\opencv3 笔记\笔记图片\第十二章\凸包示意图.png)]
hull=cv2.convexHull (points[,clockwise[,returnPoints]
points: 轮廓
clockwise: True时,凸包角点将按顺时针方向排列,False时,逆时针
returnPoints:True,函数返回凸包角点的x/y坐标,False返回凸包角点的索引
示例:获取手的轮廓凸包
import cv2
#--------------读取并绘制原始图像-------------
o=cv2.imread ( 'hand.png')
cv2.imshow("original", o)
#—-------------提取轮廓--------------
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret,binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
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)
convexityDefects: 凸缺陷点集,是一个数组,每一行的起始值为[起点,终点,轮廓上距离凸包最远点,最远点到凸包的近似距离],前三个值是轮廓点的索引。
contour: 轮廓
convexhull: 凸包
(1)测试轮廓是否是凸形
retval=cv2.isContourConvex (contour)
retval: 布尔型,True轮廓为凸形
contour: 要判断的轮廓
(2)点到轮廓的距离
retval=cv2.pointPolygonTest (contour,pt,measureDist)
retval: 与参数measureDist有关
contour: 轮廓
pt: 待判定的点
measureDist: 表示距离的判定方式
True: 表示计算点到轮廓的距离,点在外轮廓,返回值为负数,点在轮廓上,返回0,在轮廓内,返回正数
False: 不计算距离,值返回-1,0.1中的一个值,对应上面的。
v2.createShapeContextDistanceExtractor()函数用于计算形状场景距离,在每个点附加上一个形状上下文描述符,让每个点都能够捕获剩余点相对于它的分布特征。
retval=cv2.createShapeContextDistanceExtractor([,nAngularBins[,nRadialBins[,innerRadius[,outerRadius[,iterations[,comparer[,transform]]]]]]])
nAngularBins: 为形状匹配中使用的形状上下文描述符建立的角容器的数量。
nRadialBins: 为形状匹配中使用的形状上下文描述符建立的径向容器的数量。
innerRadius: 形状上下文描述符的内半径。
outerRadius: 形状上下文描述符的外半径。
iterations: 迭代次数。
comparer: 直方图代价提取算子。
transformer: 形状变换参数。
返回值结果retval可以用以下函数计算
retval=cv2. ShapeDistanceExtractor.computeDistance ( contour1 ,contour2)
contour1 ,contour2为两个不同的轮廓。
Hausdorff距离的计算方法是:
(1) 针对图像A内的每一个点,寻找其距离图像B的最短距离,将这个最短距离作为Hausdorff直接距离D1。
(2)针对图像B内的每一个点,寻找其距离图像A的最短距离,将这个最短距离作为Hausdorff直接距离D2。
(3) 将上述D1、D2中的较大者作为Hausdorf距离。
retval=cv2.createHausdorffDistanceExtractor ([,distanceFlag[,rankProp]])
distanceFlag: 距离标记
rankProp: 比例值,在0-1之间
宽高比=宽度(Width) /高度(Height)
可以使用轮廓面积与矩形边界(矩形包围框、矩形轮廓)面积之比Extend来描述图像及其轮廓特征。计算方法为:
等效直径 = ( 4 × 轮廓面积 π ) 等效直径=\sqrt(\frac{4×轮廓面积}{π}) 等效直径=(π4×轮廓面积)
构造最优拟合椭圆
(x,y), (MA,ma) ,angle=cv2.fitEllipse (cnt)
(x,y): 椭圆的中心点
(MA,ma): 椭圆水平方向轴和椭圆方向轴的长度
angle: 椭圆的旋转角度
获取轮廓像素点位置信息有两种方式:Numpy函数和OpenCV函数
(1)Numpy函数获取轮廓像素点
numpy.nonzero () 函数能够找出数组内非零元素的位置,但是其返回值是将行、列分别显示的。
使用numpy.transpose () 函数处理上述返回值,则得到这些点的(x,y)形式的坐标
(2)OpenCV函数获取轮廓点
idx=cv2.findNonzero (src)
idx: 返回值,非零元素的索引位置(列号,行号)
src: 要查找非零元素的图像
函数cv2.minMaxLoc (),用于在指定的对象内查找最大值、最小值及其位置。
min_val,max_val,min_loc,max_loc=cv2.minMaxLoc (imgray,mask=mask)
min_val: 最小值
max_val: 最大值
min_loc: 最小值位置
max_loc: 最大值位置
imgray: 单通道图像
mask: 掩模
cv2.mean (),用于计算一个对象的平均颜色或平均灰度。
mean_val=cv2.mean ( im,mask=mask)
im: 原图像
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])