OpenCV-python学习笔记(十二)——图像轮廓

十二、图像轮廓

图像轮廓是指将边缘连接起来形成的一个整体,用于后续的计算。因为边缘检测得到的边缘是不连续的。

查找图像内的轮廓信息: cv2.findContours()

绘制轮廓: cv2.drawContours()

12.1 查找并绘制轮廓

12.1.1 查找图像轮廓

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近似算法的一种风格。

注意: 待处理图像必须是灰度二值图,对象必须是白色,背景必须是蓝色

12.1.2 绘制图像轮廓

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 ()

结果:

在这里插入图片描述
图像结果.png)]

12.2 矩特征

12.2.1 矩的计算

使用函数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 ()

结果:
在这里插入图片描述

在这里插入图片描述

12.2.2 计算轮廓面积

retval=cv2.contourArea(contour[,oriented]))

retval: 返回值为面积

contour: 轮廓

oriented: 布尔型,Ture时,返回的值包括正负号,用来表示轮廓是顺时针还是逆时针。默认值为False,返回的是一个绝对值

12.2.3 极端轮廓的长度

retval=cv2.arcLength (curve,closed)

retval: 轮廓的长度(周长)

curve: 轮廓

closed: 布尔型值,Ture表示轮廓是封闭

12.3 Hu矩

Hu矩是归一化中心矩的线性组合,Hu可以保持矩的不变性

hu=cv2.HuMoments(m)

hu: 返回的Hu矩值

m: 由cv.2moments()计算得到矩特征值

二阶Hu矩: v20,v11,v02

三阶Hu矩: v30,v21,v12,v03

12.3.1 形状匹配

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。

12.4 轮廓拟合

在计算轮廓时候,仅需要一个接近于轮廓的近似多边形。

12.4.1 矩形包围框

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 ()

结果:
在这里插入图片描述

12.4.2 最小包围矩形框

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 ()

结果:
在这里插入图片描述

在这里插入图片描述

12.4.3 最小包围圆形

函数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 ()

结果:
在这里插入图片描述

12.4.4 最优拟合椭圆

retval=cv2.fitEllipse (points)

retval: 是RotaedRect类型的值

points: 轮廓

12.4.5 最优拟合直线

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 ()

结果:
在这里插入图片描述

12.4.6 最小外包三角形

retval,triangle=cv2.minEnclosingTriangle (points)

retval: 最小外包三角形的面积

triangle: 最小外包三角形的三个顶点集

points: 轮廓

12.4.7 逼近多边形

approxCurve=cv2.approxPolyDP (curve,epsilon,closed)

approxCurve: 逼近多边形的点集

curve: 轮廓

epsilon: 精度,原始轮廓的边界点与逼近多边形边界之间的最大距离。设置为多边形总长度的百分比形式。

closed: True时,逼近多边形封闭,否则不封闭

该函数采用DP算法,此算法首先从轮廓中找到距离最远的两个点,两点相连,在轮廓上找一个离当前直线最远的点,并将该点与原有直线连成一个封闭的图形,此过程不断迭代,当轮廓上所有的点到当前多边形的距离都小于参数epsilon的值时,停止迭代。

12.5 凸包

凸包是指完全包含原有轮廓,并且仅由轮廓上的点所构成的多边形。凸包内任意三点的内角和小于180°。

如图为凸包示意图,机械手边缘与凸包之间的部分称为凸缺陷,凸缺陷可以用来处理手势识别等问题。

在这里插入图片描述

12.5.1 获取凸包

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 ()

结果:
在这里插入图片描述

12.5.2 凸缺陷

凸包与轮廓之间的部分叫做凸缺陷

convexityDefects=cv2.convexityDefects (contour,convexhull)

convexityDefects: 凸缺陷点集,是一个数组,每一行的起始值为[起点,终点,轮廓上距离凸包最远点,最远点到凸包的近似距离],前三个值是轮廓点的索引。

contour: 轮廓

convexhull: 凸包

12.5.3 几何学测试

(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中的一个值,对应上面的。

12.6 利用形状场景算法比较轮廓

12.6.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为两个不同的轮廓。

12.6.2 计算Hausdorff距离

Hausdorff距离的计算方法是:

(1) 针对图像A内的每一个点,寻找其距离图像B的最短距离,将这个最短距离作为Hausdorff直接距离D1

(2)针对图像B内的每一个点,寻找其距离图像A的最短距离,将这个最短距离作为Hausdorff直接距离D2

(3) 将上述D1、D2中的较大者作为Hausdorf距离。

retval=cv2.createHausdorffDistanceExtractor ([,distanceFlag[,rankProp]])

distanceFlag: 距离标记

rankProp: 比例值,在0-1之间

12.7 轮廓的特征值

12.7.1 宽高比

宽高比=宽度(Width) /高度(Height)

12.7.2 Extent

可以使用轮廓面积与矩形边界(矩形包围框、矩形轮廓)面积之比Extend来描述图像及其轮廓特征。计算方法为:
在这里插入图片描述

12.7.3 Solidity

轮廓面积与凸包面积之比
在这里插入图片描述

12.7.4 等效直径

等 效 直 径 = ( 4 × 轮 廓 面 积 π ) 等效直径=\sqrt(\frac{4×轮廓面积}{π}) =( π4×)

12.7.5 方向

构造最优拟合椭圆

(x,y), (MA,ma) ,angle=cv2.fitEllipse (cnt)

(x,y): 椭圆的中心点

(MA,ma): 椭圆水平方向轴和椭圆方向轴的长度

angle: 椭圆的旋转角度

12.7.6 掩模和像素点

获取轮廓像素点位置信息有两种方式:Numpy函数和OpenCV函数

(1)Numpy函数获取轮廓像素点

numpy.nonzero () 函数能够找出数组内非零元素的位置,但是其返回值是将行、列分别显示的。

使用numpy.transpose () 函数处理上述返回值,则得到这些点的(x,y)形式的坐标

(2)OpenCV函数获取轮廓点

idx=cv2.findNonzero (src)

idx: 返回值,非零元素的索引位置(列号,行号)

src: 要查找非零元素的图像

12.7.7 最小值和最大值及其位置

函数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: 掩模

12.7.8 平均颜色及平均灰度

cv2.mean (),用于计算一个对象的平均颜色或平均灰度

mean_val=cv2.mean ( im,mask=mask)

im: 原图像

mask: 掩模

12.7.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])

猜你喜欢

转载自blog.csdn.net/weixin_56321457/article/details/126148078