直播网站开发核心技术,wordpress支持php几,专做童装的网站,wordpress安装与使用说明第12章#xff1a;图像轮廓一、查找并绘制轮廓#xff1a;1. 查找图像轮廓#xff1a;2. 绘制图像轮廓#xff1a;3. 绘制轮廓实例#xff1a;二、矩特征1. 矩的计算#xff1a;moments函数2. 计算轮廓面积#xff1a;contourArea函数3. 计算轮廓长度#xff1a;arcLen…
第12章图像轮廓一、查找并绘制轮廓1. 查找图像轮廓2. 绘制图像轮廓3. 绘制轮廓实例二、矩特征1. 矩的计算moments函数2. 计算轮廓面积contourArea函数3. 计算轮廓长度arcLength函数三、Hu矩1. Hu矩函数2. 形状匹配四、轮廓拟合1. 矩形包围框2. 最小包围矩形框3. 最小包围圆形4. 最优拟合椭圆5. 最优拟合直线6. 最小外包三角形7. 逼近多边形五、凸包1. 凸包2. 凸缺陷3. 几何学测试六、利用形状场景算法比较轮廓1. 计算形状场景距离2. 计算Hausdorrf距离七、轮廓的特征值1. 宽高比2. Extent:3. Solidity:4. 等效直径5. 方向6. 掩膜和像素点7. 最大值和最小值及它们的位置:8. 平均颜色及平均灰度:9. 极点前面所提到的边缘检测虽然能够检测出图像的边缘但是边缘是不连续的检测到的边缘并不是一个整体。图像轮廓是指将边缘连接起来形成一个整体。
边缘是不连续的不是一个整体图像轮廓是将边缘连接起来形成一个整体。
图像轮廓是图像中重要的特征信息通过对图像轮廓的操作我们能够获取目标图像的大小、位置、方向等信息。
OpenCV中提供了查找轮廓和绘制轮廓的函数
查找图像轮廓cv2.findContours()能够查找图像内所有轮廓的信息。绘制图像轮廓cv2.drawContours()
一、查找并绘制轮廓
一条图像轮廓对应的是图像中一系列的像素点这些点以某种方式表示图像中的一条曲线。在OpenCV中通过cv2.findContours()来查找图像轮廓并根据参数以特定的方式返回表示的轮廓。
通过cv2.drawContours()来将查找到图像轮廓绘制到图像上该函数可以根据参数在图像上绘制不同样式实心/空心点线条的不同粗细、颜色等的轮廓可以绘制全部轮廓也可以仅绘制指定的轮廓。
1. 查找图像轮廓
OpenCV中查找图像轮廓的函数是CV2.findContours()具体语法如下
image, contours, hierarchy cv2.findContoursimage,mode,method
返回值为 image与参数中的原始图像image一致。 原始的输入图像在OpenCV 4.X中该返回值已经被取消。 contours返回的轮廓。 返回值是一组轮廓信息每个轮廓都是由若干个点所构成的。例如contours[i]是第i个轮廓下标从0开始,contours[i][j]是第i个轮廓内的第j个点。 hierarchy图像的拓扑信息轮廓层次。 图像内的轮廓可能位于不同的位置。比如一个轮廓在另一个轮廓的内部。在这种情况下我们将外部的轮廓称为父轮廓内部的轮廓称为子轮廓。按照上述关系分类一幅图像中所有轮廓之间就建立了父子关系。 根据轮廓之间的关系就能够确定一个轮廓与其他轮廓是如何连接的。比如确定一个轮廓是某个轮廓的子轮廓或者是某个轮廓的父轮廓。上述关系被称为层次组织结构返回值hierarchy就包含上述层次关系。 每个轮廓contours[i]对应4个元素来说明当前轮廓的层次关系。其形式为 [Next,Previous,First_Child,Parent] 式中各元素的含义为 Next后一个轮廓的索引编号。Previous前一个轮廓的索引编号。First_Child第1个子轮廓的索引编号。Parent父轮廓的索引编号。 如果上述各个参数所对应的关系为空时也就是没有对应的关系时则将该参数所对应的值设为“-1”。使用print语句可以查看hierarchy的值printhierarchy 需要注意轮廓的层次结构是由参数mode决定的。也就是说使用不同的mode得到轮廓的编号是不一样的得到的hierarchy也不一样。
参数为 image原始图像。必须为8位单通道图像所有非零值被处理为1所有零值保持不变。也就是说灰度图像会被自动处理为二值图像。在实际操作时可以根据需要预先使用阈值处理等函数将待查找轮廓的图像处理为二值图像。 mode轮廓检索模式 参数mode决定了轮廓的提取方式具体有如下4种 cv2.RETR_EXTERNAL只检测外轮廓。 cv2.RETR_LIST对检测到的轮廓不建立等级关系。 cv2.RETR_CCOMP检索所有轮廓并将它们组织成两级层次结构。上面的一层为外边界下面的一层为内孔的边界。如果内孔内还有一个连通物体那么这个物体的边界仍然位于顶层。 cv2.RETR_TREE建立一个等级树结构的轮廓。 由于这里只有两层轮廓所以使用参数 cv2.RETR_CCOMP 和 cv2.RETR_TREE得到的层次结构是一致的。 method轮廓的近似方法 参数method决定了如何表达轮廓可以为如下值 cv2.CHAIN_APPROX_NONE存储所有的轮廓点相邻两个点的像素位置差不超过1即maxabsx1-x2,absy2-y11。cv2.CHAIN_APPROX_SIMPLE压缩水平方向、垂直方向、对角线方向的元素只保留该方向的终点坐标。例如在极端的情况下一个矩形只需要用4个点来保存轮廓信息。cv2.CHAIN_APPROX_TC89_L1使用teh-Chinl chain近似算法的一种风格。cv2.CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain近似算法的一种风格。 例如左图是使用参数值 cv2.CHAIN_APPROX_NONE存储的轮廓保存了轮廓中的每一个点右图是使用参数值cv2.CHAIN_APPROX_SIMPLE存储的轮廓仅仅保存了边界上的四个点。
注意
在使用函数cv2.findContours查找图像轮廓时需要注意以下问题
待处理的源图像必须是灰度二值图。因此在通常情况下都要预先对图像进行阈值分割或者边缘检测处理得到满意的二值图像后再将其作为参数使用。在OpenCV中都是从黑色背景中查找白色对象。因此对象必须是白色的背景必须是黑色的。在OpenCV 4.x中函数cv2.findContours仅有两个返回值。
2. 绘制图像轮廓
在OpenCV中使用函数cv2.drawContours()实现图像轮廓的绘制具体的函数语法是
imagecv2.drawContoursimage, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]]
其中函数的返回值为image表示目标图像即绘制了边缘的原始图像。
函数有如下参数 image待绘制轮廓的图像。需要注意函数cv2.drawContours会在图像image上直接绘制轮廓。也就是说在函数执行完以后image不再是原始图像而是包含了轮廓的图像。因此如果图像image还有其他用途的话则需要预先复制一份将该副本图像传递给函数cv2.drawContours使用。 contours需要绘制的轮廓。该参数的类型与函数 cv2.findContours的输出 contours 相同都是list类型。 contourIdx需要绘制的边缘索引告诉函数cv2.drawContours要绘制某一条轮廓还是全部轮廓。如果该参数是一个整数或者为零则表示绘制对应索引号的轮廓如果该值为负数通常为“-1”则表示绘制全部轮廓。 color绘制的颜色用BGR格式表示。 thickness可选参数表示绘制轮廓时所用画笔的粗细。如将该值设置为“-1”则表示要绘制实心轮廓。 lineType可选参数表示绘制轮廓时所用的线型。 hierarchy对应函数cv2.findContours所输出的层次信息。 maxLevel控制所绘制的轮廓层次的深度。如果值为0表示仅仅绘制第0层的轮廓如果值为其他的非零正数表示绘制最高层及以下的相同数量层级的轮廓。 offset偏移参数。该参数使轮廓偏移到不同的位置展示出来。
3. 绘制轮廓实例
示例1绘制出一幅图像内所有的轮廓
import cv2
import numpy as npimg cv2.imread(../contour.bmp)
img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# print(contours)
print(hierarchy)temp np.zeros(img.shape, np.uint8)
cv2.drawContours(temp, contours, -1, (255, 255, 255), 5)
cv2.imshow(img, img)
cv2.imshow(rst, temp)
cv2.waitKey()
cv2.destroyAllWindows()# 输出层次信息
[[[ 1 -1 -1 -1][ 2 0 -1 -1][-1 1 -1 -1]]]示例2使用轮廓绘制功能提取前景对象
import cv2
import numpy as npimg cv2.imread(../flower.jpeg)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
t, new_img cv2.threshold(gray_img, 50, 255, cv2.THRESH_BINARY)
contours, hierarchy cv2.findContours(new_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
mask np.zeros(new_img.shape, np.uint8)
mask cv2.drawContours(mask, contours, -1, (255, 255, 255), -1)
rst cv2.bitwise_and(img, img, maskmask)cv2.imshow(img, img)
cv2.imshow(gray_img, gray_img)
cv2.imshow(new_img, new_img)
cv2.imshow(mask, mask)
cv2.imshow(rst, rst)
cv2.waitKey()
cv2.destroyAllWindows()二、矩特征
比较两个轮廓最简单的方法就是比较二者的轮廓矩。 轮廓矩代表了一个轮廓、一幅图像、一组点集的全局特征。矩信息包含对应对象的几何特征例如大小、位置、角度、形状等。矩特征被广泛应用在模式识别、图像识别等方面。
1. 矩的计算moments函数
在OpenCV中可以通过cv2.moments()函数来获取图像的轮廓特征通常情况下我们将获取到的轮廓特征称为轮廓矩。
轮廓矩描述了一个轮廓的重要特征使用轮廓矩可以很方便的比较两个轮廓
具体语法 retval cv2.moments( array, [, binaryImage] )
参数
array可以是点集也可以是灰度图像或者二值图像。当array是点集时函数会把这些点集当成轮廓中的顶点把整个点集作为一条轮廓而不是把它们当成独立的点来看待。binaryImage该参数为True时array内所有的非零值都被处理为1。该参数仅在参数array为图像时有效。
返回值retval是矩特征主要包括
空间矩 零阶矩m00一阶矩m10,m01二阶矩m20,m11,m02三阶矩m30,m21,m12,m03 中心矩 二阶中心矩mu20,mu11,mu02三阶中心矩mu30,mu21,mu12,mu03 归一化中心矩 二阶Hu矩nu20,nu11,nu02三阶Hu矩nu30,nu21,nu12,nu03
上述的矩特征信息看起来比较抽象。但是很明显如果两个轮廓的矩特征信息完全一致那么这两个轮廓就是一致的。零阶矩中m00的含义比较直观是一个轮廓的面积。
矩特征信息能够用来比较两个轮廓是否相似。例如有两个轮廓不管它们出现在图像的哪个位置我们都可以通过函数cv2.moments的m00矩判断其面积是否一致。
中心矩
中心矩具有的平移不变性使它能够忽略两个对象的位置关系帮助我们比较不同位置上两个对象的一致性。
例如在很多情况下我们希望比较不同位置的两个对象的一致性。解决这一问题的方法是引入中心矩。中心矩通过减去均值而获取平移不变性因而能够比较不同位置的两个对象是否一致。
归一化中心矩
归一化中心距具有平移、缩放不变形。
除了考虑平移不变性外我们还会考虑经过缩放后大小不一致的对象的一致性。也就是说我们希望图像在缩放前后能够拥有一个稳定的特征值。也就是说让图像在缩放前后具有同样的特征值。显然中心矩不具有这个属性。例如两个形状一致、大小不一的对象其中心矩是有差异的。归一化中心矩通过除以物体总尺寸而获得缩放不变性。
在OpenCV中函数cv2.moments会同时计算上述空间矩、中心矩和归一化中心距。
示例
import cv2
import numpy as npimg cv2.imread(../contour.bmp)
img_gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contour, hierarchy cv2.findContours(img_gray, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
cv2.imshow(img, img)for i in range(len(contour)):temp np.zeros(img_gray.shape, np.uint8)rst cv2.drawContours(temp, contour, i, 255, 3)cv2.imshow(frst_{i}, rst)for n, item in enumerate(contour):print(f轮廓{n}的矩为:\n {cv2.moments(item)})print(f轮廓{n}的面积为: {cv2.moments(item)[m00]})cv2.waitKey()
cv2.destroyAllWindows()轮廓0的矩为:{m00: 9209.5, m10: 1017721.8333333333, m01: 2389982.5, m20: 119678938.58333333, m11: 264115139.7083333, m02: 627149283.9166666, m30: 14819578435.95, m21: 31059127282.616665, m12: 69306755254.31667, m03: 166344041770.35, mu20: 7212710.227465913, mu11: 3366.9156102240086, mu02: 6918397.298907757, mu30: -2880.996063232422, mu21: 174896.51574015617, mu12: 103129.86562001705, mu03: -6266.268524169922, nu20: 0.08504061263542004, nu11: 3.9697222979357786e-05, nu02: 0.08157055062519235, nu30: -3.539586534588115e-07, nu21: 2.1487754181991495e-05, nu12: 1.2670516573109425e-05, nu03: -7.698726136189045e-07}
轮廓0的面积为: 9209.5
轮廓1的矩为:{m00: 13572.0, m10: 4764940.166666666, m01: 3239293.833333333, m20: 1682579697.5, m11: 1137215771.8333333, m02: 799816300.5, m30: 597523663576.15, m21: 401501587677.93335, m12: 280779729659.4667, m03: 203776751049.05002, mu20: 9675571.953775883, mu11: -55175.564665317535, mu02: 26678624.500047207, mu30: -550033.6081542969, mu21: -48973895.52901983, mu12: 1704572.9323253632, mu03: 145759473.83877563, nu20: 0.05252776773308551, nu11: -0.00029954293752635475, nu02: 0.14483573662328061, nu30: -2.5631829122147227e-05, nu21: -0.002282206947059113, nu12: 7.943391363704586e-05, nu03: 0.006792461171429966}
轮廓1的面积为: 13572.0
轮廓2的矩为:{m00: 8331.0, m10: 1055010.8333333333, m01: 757410.8333333333, m20: 138592698.0, m11: 95918474.75, m02: 74976013.33333333, m30: 18814393628.350002, m21: 12600844964.533333, m12: 9495480797.366667, m03: 7928377343.35, mu20: 4989546.103385642, mu11: 2422.1211806088686, mu02: 6116192.129312888, mu30: -256257.12244033813, mu21: 110165.07764232159, mu12: 321106.038520813, mu03: -152856.36076641083, nu20: 0.07188971649383602, nu11: 3.489808519246561e-05, nu02: 0.08812250835798138, nu30: -4.045135827151561e-05, nu21: 1.7390061131886812e-05, nu12: 5.0688056135402214e-05, nu03: -2.4129075338702876e-05}
轮廓2的面积为: 8331.02. 计算轮廓面积contourArea函数
在OpenCV中可以通过函数cv2.contourArea()来计算轮廓的面积。
retval cv2.contourArea(contour [, oriented])
retval返回的面积值。contour轮廓。oriented 是布尔型值。当它为 True 时返回的值包含正/负号用来表示轮廓是顺时针还是逆时针的。该参数的默认值是False表示返回的retval是一个绝对值。
示例
import cv2
import numpy as npimg cv2.imread(../contour.bmp)
img_gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(img_gray, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
cv2.imshow(img, img)for i in range(len(contours)):temp np.zeros(img_gray.shape, np.uint8)rst cv2.drawContours(temp, contours, i, 255, 3)cv2.imshow(frst_{i}, rst)for n, item in enumerate(contours):print(f轮廓{n}的面积为: {cv2.contourArea(item)})cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
轮廓0的面积为: 9209.5
轮廓1的面积为: 13572.0
轮廓2的面积为: 8331.03. 计算轮廓长度arcLength函数
在OpenCV中可以通过函数cv2.arcLength()来计算轮廓的长度。
retvalcv2.arcLengthcurve,closed
返回值retval是轮廓的长度周长。
参数
curve是轮廓。closed是布尔型值用来表示轮廓是否是封闭的。该值为True时表示轮廓是封闭的。
import cv2
import numpy as npimg cv2.imread(../contour.bmp)
img_gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(img_gray, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
cv2.imshow(img, img)for i in range(len(contours)):temp np.zeros(img_gray.shape, np.uint8)rst cv2.drawContours(temp, contours, i, 255, 3)cv2.imshow(frst_{i}, rst)for n, item in enumerate(contours):print(f轮廓{n}的周长为: {cv2.arcLength(item, closedTrue)})cv2.waitKey()
cv2.destroyAllWindows()
# 输出结果
轮廓0的周长为: 381.0710676908493
轮廓1的周长为: 595.4213538169861
轮廓2的周长为: 356.9360725879669三、Hu矩
Hu矩是归一化中心矩的线性组合。 Hu矩具有旋转、缩放、平移不变性所以经常会使用Hu距来识别图像的特征。
Hu 矩是归一化中心矩的线性组合每一个矩都是通过归一化中心矩的组合运算得到的。
函数cv2.moments返回的归一化中心矩中包含
二阶Hu矩nu20,nu11,nu02三阶Hu矩nu30,nu21,nu12,nu03
为了表述上的方便将上述字母“nu”表示为字母“v”则归一化中心矩为 二阶Hu矩v20,v11,v02 三阶Hu矩v30,v21,v12,v03
上述7个Hu矩的计算公式为 1. Hu矩函数
在OpenCV中使用函数cv2.HuMoments可以得到Hu距。该函数的参数是cv2.moments函数的返回值。返回7个Hu矩值。
具体语法
hucv2.HuMomentsm 返回值hu表示返回的Hu矩值参数m是由函数cv2.moments计算得到矩特征值。
示例1验证Hu矩中的第0个矩h0v20v02
import cv2img cv2.imread(../contour.bmp)
img_grey cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_moment cv2.moments(img_grey)
nu02 img_moment[nu02]
nu20 img_moment[nu20]
hum1 cv2.HuMoments(cv2.moments(img_grey)).flatten()print(fcv2.moments(img_grey)\n{img_moment})
print(fhum1\n{hum1})
print(fnu20nu02: {nu20}{nu20} {nu20nu02})
print(fhum1[0]{hum1[0]})
print(fhum1[0]-(nu02nu20) {hum1[0]-(nu20nu02)})# 输出结果
cv2.moments(img_grey)
{m00: 8093955.0, m10: 1779032490.0, m01: 1661859225.0, m20: 505157033370.0, m11: 389611273740.0, m02: 391007192445.0, m30: 164339255189040.0, m21: 115872895560000.0, m12: 93612106111380.0, m03: 98481842760465.0, mu20: 114129828440.44452, mu11: 24338480021.574287, mu02: 49792534874.306694, mu30: 3135971915955.592, mu21: 1454447544071.9954, mu12: -2324769529267.9106, mu03: -2247068709894.701, nu20: 0.0017421181018673839, nu11: 0.00037151117457122763, nu02: 0.0007600508782649917, nu30: 1.682558608581603e-05, nu21: 7.80361974403396e-06, nu12: -1.2473201575996879e-05, nu03: -1.20563095054236e-05}hum1
[ 2.50216898e-03 1.51653824e-06 4.20046078e-09 3.70286211e-11-1.41810711e-20 -2.66632116e-14 3.48673128e-21]nu20nu02: 0.00174211810186738390.0017421181018673839 0.0025021689801323754、
hum1[0]0.0025021689801323754
hum1[0]-(nu02nu20) 0.02. 形状匹配
我们可以通过Hu矩来判断两个对象的一致性。例如之前计算了两个对象Hu矩的差但是结果比较抽象。为了更直观方便地比较Hu矩值OpenCV提供了函数cv2.matchShapes对两个对象的Hu矩进行比较。
函数cv2.matchShapes允许我们提供两个对象对二者的Hu矩进行比较。这两个对象可以是轮廓也可以是灰度图。不管是什么cv2.matchShapes都会提前计算好对象的Hu矩值。
函数cv2.matchShapes的语法格式为
retvalcv2.matchShapescontour1,contour2,method,parameter
式中retval是返回值。
该函数有如下4个参数 contour1第1个轮廓或者灰度图像。 contour2第2个轮廓或者灰度图像。 method比较两个对象的Hu矩的方法具体如表12-1所示。 A表示对象1,B表示对象2 式中和分别是对象A和对象B的Hu矩。 parameter应用于method的特定参数该参数为扩展参数目前截至OpenCV 4.1.0版本暂不支持该参数因此将该值设置为0。
示例使用函数cv2.matchShapes计算三幅不同图像的匹配度。
import cv2img1 cv2.imread(../contour2.bmp)
img1 cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
img3 cv2.imread(../lena.bmp)
img3 cv2.cvtColor(img3, cv2.COLOR_BGR2GRAY)# 对img1进行旋转缩放
h, w img1.shape
m cv2.getRotationMatrix2D((w/2, h/2), 90, 0.5)
img_rotate cv2.warpAffine(img1, m, (w, h))
cv2.imshow(img1, img1)
cv2.imshow(img2, img_rotate)
cv2.imshow(img3, img3)contours1, hierarchy1 cv2.findContours(img1, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
contours2, hierarchy2 cv2.findContours(img_rotate, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
contours3, hierarchy3 cv2.findContours(img3, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)res1 cv2.matchShapes(contours1[0], contours1[0], 1, 0)
res2 cv2.matchShapes(contours1[0], contours2[0], 1, 0)
res3 cv2.matchShapes(contours1[0], contours3[0], 1, 0)
print(相同图像的matchShapes, res1)
print(相似图像的matchShapes, res2)
print(不相似图像的matchShapes, res3)cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
相同图像的matchShapes0.0
相似图像的matchShapes0.0252828927661784
不相似图像的matchShapes0.6988263089300291从以上结果可以看出
同一幅图像的Hu矩是不变的二者差值为0。相似的图像即使发生了平移、旋转和缩放后函数cv2.matchShapes的返回值仍然比较接近。不相似图像cv2.matchShapes函数返回值的差较大。
四、轮廓拟合
在计算轮廓时有时候可能并不需要实际的轮廓而仅需要一个接近于轮廓的近似多边形。OpenCV提供了多种计算轮廓近似多边形的方法。
1. 矩形包围框
在OpenCV中通过函数cv2.boundingRect()能够绘制出轮廓的矩形边界具体语法为 retval cv2.boundingRect(array)
返回值
retval表示返回的矩形边界的左上角顶点的坐标值及矩形边界的宽度和高度。
参数
array灰度图像或轮廓。
示例
import cv2
import numpy as npimg cv2.imread(../contour2.bmp)
grey_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(grey_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)x, y, w, h cv2.boundingRect(contours[0])
print(f顶点及宽高分别为:{x}, {y}, {w}, {h})
new_img img.copy()
# brcnt np.array([[[x, y]], [[xw, y]], [[xw, yh]], [[x, yh]]])
# rst cv2.drawContours(new_img, [brcnt], -1, (255, 255, 255), 2)
rst cv2.rectangle(new_img, (x, y), (xw, yh), (255, 255, 255), 2)
cv2.imshow(img, img)
cv2.imshow(rst, rst)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
顶点及宽高分别为:165, 70, 241, 1212. 最小包围矩形框
在OpenCV中通过函数cv2.minAreaRect()能够绘制轮廓的最小包围矩形框。
retval cv2.minAreaRect( points )
返回值
retval表示返回的矩形特征信息。该值的结构是最小外接矩形的中心x,y,宽度高度旋转角度。
参数 points轮廓。 注意返回值retval是不符合函数cv2.drawContours()的参数结构要求。因此必须将其转换为符合要求的结构才能使用。函数 cv2.boxPoints()能够将上述返回值 retval 转换为符合要求的结构。 函数cv2.boxPoints()的语法格式是 points cv2.boxPointsbox points返回值是轮廓点。能够用于函数cv2.drawContours()的参数。 box是函数cv2.minAreaRect()返回值的类型的值。
示例
import cv2
import numpy as npimg cv2.imread(../contour2.bmp)
grey_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(grey_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
rect cv2.minAreaRect(contours[0])
print(f返回值rect{rect})
points cv2.boxPoints(rect)
points np.int0(points)
new_img img.copy()
rst cv2.drawContours(new_img, [points], 0, (255, 255, 255), 2)
cv2.imshow(img, img)
cv2.imshow(rst, rst)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
返回值rect((284.41119384765625, 132.4847412109375), (80.4281997680664, 238.48388671875), 72.19464874267578)3. 最小包围圆形
在OpenCV中通过函数cv2.minEnclosingCircle()通过迭代算法构造一个对象的的面积最小包围圆形。具体语法
center,radiuscv2.minEnclosingCirclepointscenter返回值最小包围圆形的中心。radius返回值最小包围圆形的半径。points轮廓。
示例
import cv2
import numpy as npimg cv2.imread(../contour2.bmp)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(gray_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)(x, y), radius cv2.minEnclosingCircle(contours[0])
center (int(x), int(y))
radius int(radius)
new_img img.copy()
rst cv2.circle(new_img, center, radius, (255, 255, 255), 2)
cv2.imshow(img, img)
cv2.imshow(rst, rst)
cv2.waitKey()
cv2.destroyAllWindows()4. 最优拟合椭圆
在OpenCV中通过函数cv2.fitEllipse可以用来构造最优拟合椭圆。该函数的语法格式是
retvalcv2.fitEllipsepoints retval返回值是RotatedRect类型的值。这是因为该函数返回的是拟合椭圆的外接矩形retval包含外接矩形的质心、宽、高、旋转角度等参数信息这些信息正好与椭圆的中心点、轴长度、旋转角度等信息吻合。points轮廓。
示例
import cv2
import numpy as npimg cv2.imread(../contour2.bmp)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(gray_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)ellipse cv2.fitEllipse(contours[0])
print(fellipse{ellipse})
new_img img.copy()
rst cv2.ellipse(new_img, ellipse, (0, 0, 255), 2)
cv2.imshow(img, img)
cv2.imshow(rst, rst)
cv2.waitKey()
cv2.destroyAllWindows()5. 最优拟合直线
在OpenCV中函数cv2.fitLine用来构造最优拟合直线该函数的语法格式为 linecv2.fitLine(points,distType,param,reps,aeps) line返回值返回的是最优拟合直线参数。点斜式 斜率 k line[1] / line[0] 截距 b line[3] - k * line[2] 通过点斜式的方式来确定一条直线 points轮廓。 distType距离类型。拟合直线时要使输入点到拟合直线的距离之和最小其类型如表所示 param距离参数与所选的距离类型有关。当此参数被设置为0时该函数会自动选择最优值。 reps用于表示拟合直线所需要的径向精度通常该值被设定为0.01。 aeps用于表示拟合直线所需要的角度精度通常该值被设定为0.01。
import cv2
import numpy as npimg cv2.imread(../contour2.bmp)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(gray_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)rows, cols img.shape[:2]
vx, vy, x, y cv2.fitLine(contours[0], cv2.DIST_L2, 0, 0.01, 0.01)
# 截距b
lefty int((-x*vy/vx)y)
righty int(((cols-x)*vy/vx)y)
new_img img.copy()
rst cv2.line(new_img, (cols-1, righty), (0, lefty), (0, 255, 0), 2)
cv2.imshow(img, img)
cv2.imshow(rst, rst)
cv2.waitKey()
cv2.destroyAllWindows()6. 最小外包三角形
在OpenCV中函数cv2.minEnclosingTriangle用来构造最小外包三角形。该函数的语法格式为
retval,trianglecv2.minEnclosingTrianglepoints retval最小外包三角形的面积。triangle最小外包三角形的三个顶点集。points轮廓。
import cv2
import numpy as npimg cv2.imread(../contour2.bmp, -1)
contours, hierarchy cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)area, trgl cv2.minEnclosingTriangle(contours[0])
print(farea{area})
print(ftrgl:{trgl})
new_img img.copy()
for i in range(0, 3):cv2.line(new_img, tuple(trgl[i][0]), tuple(trgl[(i 1) % 3][0]), (255, 255, 255), 2)cv2.imshow(img, img)
cv2.imshow(rst, new_img)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
area26509.7265625
area26509.7265625
trgl:
[[[ 52.674423 207.67442 ]][[443.27908 156.27907 ]][[323.3256 36.325584]]]7. 逼近多边形
在OpenCV中通过函数cv2.approxPolyDP()来构造指定精度的逼近多边形曲线。具体语法格式为
approxCurve cv2.approxPolyDP(curve,epsilon,closed) approxCurve返回值逼近多边形的点集。curve是轮廓。psilon为精度原始轮廓的边界点与逼近多边形边界之间的最大距离。closed 是布尔型值。该值为 True 时逼近多边形是封闭的否则逼近多边形是不封闭的。
函数cv2.approxPolyDP采用的是Douglas-Peucker算法DP算法。该算法首先从轮廓中找到距离最远的两个点并将两点相连见b图。接下来在轮廓上找到一个离当前直线最远的点并将该点与原有直线连成一个封闭的多边形此时得到一个三角形如图c所示。
将上述过程不断地迭代将新找到的距离当前多边形最远的点加入到结果中。当轮廓上所有的点到当前多边形的距离都小于函数cv2.approxPolyDP的参数epsilon的值时就停止迭代。
通过上述过程可知epsilon是逼近多边形的精度信息。通常情况下将该精度设置为轮廓总长度的百分比形式。
示例
import cv2img cv2.imread(../contour3.bmp)
grey_img cv2.cvtColor(img, cv2.cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(grey_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cv2.imshow(img, img)adp img.copy()
epsilon 0.1 * cv2.arcLength(contours[0], True)
approx cv2.approxPolyDP(contours[0], epsilon, True)
adp cv2.drawContours(adp, [approx], 0, (0, 255, 0), thickness2)
cv2.imshow(rst0.1, adp)adp img.copy()
epsilon 0.08 * cv2.arcLength(contours[0], True)
approx cv2.approxPolyDP(contours[0], epsilon, True)
adp cv2.drawContours(adp, [approx], 0, (0, 255, 0), 2)
cv2.imshow(rst0.08, adp)adp img.copy()
epsilon 0.05 * cv2.arcLength(contours[0], True)
approx cv2.approxPolyDP(contours[0], epsilon, True)
adp cv2.drawContours(adp, [approx], 0, (0, 255, 0), 2)
cv2.imshow(rst0.05, adp)adp img.copy()
epsilon 0.04 * cv2.arcLength(contours[0], True)
approx cv2.approxPolyDP(contours[0], epsilon, True)
adp cv2.drawContours(adp, [approx], 0, (0, 255, 0), 2)
cv2.imshow(rst0.04, adp)adp img.copy()
epsilon 0.01 * cv2.arcLength(contours[0], True)
approx cv2.approxPolyDP(contours[0], epsilon, True)
adp cv2.drawContours(adp, [approx], 0, (0, 255, 0), 2)
cv2.imshow(rst0.01, adp)cv2.waitKey()
cv2.destroyAllWindows() 五、凸包
1. 凸包
逼近多边形是在轮廓内部逼近轮廓凸包是在轮廓外部包围轮廓的凸多边形是物体最外层的“凸”多边形。
逼近多边形是在轮廓内部按照指定精度逼近轮廓是轮廓的高度近似。凸包是在轮廓外部包围原有轮廓并且仅由轮廓上的点所构成的凸多边形。凸包的每一处都是凸的即在凸包内连接任意两点的直线仍在凸包内。在凸包内任意连续三个点的内角小于180°。
例如下图最外层的多边形为机械手的凸包在手边缘与凸包之间的部分被称为凸缺陷Convexity Defect凸缺陷能够用来处理手势识别等问题。 在OpenCV通过cv2.convexHull()函数来获取轮廓的凸包。语法格式为
hullcv2.convexHullpoints[,clockwise[,returnPoints]] hull返回值为凸包角点。points轮廓。clockwise布尔型值。该值为True时凸包角点将按顺时针方向排列该值为False时则以逆时针方向排列凸包角点。returnPoints布尔型值。默认值是 True函数返回凸包角点的x/y轴坐标当为 False时函数返回轮廓中凸包角点的索引。
import cv2img cv2.imread(../grey_hand.jpg)
grey_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
t, grey_img cv2.threshold(grey_img, 20, 255, cv2.THRESH_BINARY)
contours, hierarchy cv2.findContours(grey_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)new_img img.copy()
cv2.drawContours(new_img, contours, 0, (0, 0, 255), 2)
hull cv2.convexHull(contours[0])
rst cv2.polylines(new_img, [hull], True, (0, 255, 255), 2)
cv2.imshow(img, img)
cv2.imshow(rst, rst)
cv2.waitKey()
cv2.destroyAllWindows()2. 凸缺陷
凸包与轮廓之间的部分称为凸缺陷。在OpenCV中使用函数cv2.convexityDefects获取凸缺陷。其语法格式如下 convexityDefectscv2.convexityDefectscontour,convexhull convexityDefects返回值为凸缺陷点集。它是一个数组每一行包含的值是[起点终点轮廓上距离凸包最远的点最远点到凸包的近似距离]。 注意返回结果中[起点终点轮廓上距离凸包最远的点最远点到凸包的近似距离]的前三个值是轮廓点的索引所以需要到轮廓点中去找它们。 contour轮廓。 convexhull凸包。 注意用 cv2.convexityDefects()计算凸缺陷时需要传入凸包作为参数。在查找该凸包时所使用函数cv2.convexHull()的参数returnPoints的值必须是False。
import cv2img cv2.imread(../grey_hand.jpg)
grey_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
t, grey_img cv2.threshold(grey_img, 20, 255, cv2.THRESH_BINARY)
contours, hierarchy cv2.findContours(grey_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours contours[0]hull cv2.convexHull(contours, returnPointsFalse)
convexity_defects cv2.convexityDefects(contours, hull)
print(convexity_defects)
new_img img.copy()
for i in range(convexity_defects.shape[0]):s, e, f, d convexity_defects[i, 0]start tuple(contours[s][0])end tuple(contours[e][0])far tuple(contours[f][0])cv2.line(new_img, start, end, [0, 255, 0], 2)cv2.circle(new_img, start, 5, [0, 0, 255], -1)cv2.circle(new_img, far, 5, [255, 0, 0], -1)cv2.imshow(img, img)
cv2.imshow(result, new_img)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
[[[ 0 2 1 162]][[ 2 4 3 114]][[ 4 258 157 39819]][[ 258 260 259 142]][[ 261 265 262 114]][[ 266 268 267 162]][[ 268 454 331 3007]][[ 455 578 513 5631]][[ 579 585 580 114]][[ 586 722 693 37006]][[ 722 724 723 162]][[ 724 726 725 142]][[ 726 910 781 47500]][[ 911 915 912 114]][[ 915 917 916 162]][[ 917 1105 1028 46699]]]3. 几何学测试
下面介绍几种与凸包相关的几种几何学测试
(1) 测试轮廓是否是图形的
在OpenCV中可以用函数cv2.isContourConvex()来判断轮廓是否是凸形的其语法格式为 retvalcv2.isContourConvex(contour)
retval返回值布尔类型型。该值为True时表示轮廓为凸形的否则不是凸形的。contour要判断的轮廓。
import cv2img cv2.imread(../grey_hand.jpg)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
t, gray_img cv2.threshold(gray_img, 20, 255, cv2.THRESH_BINARY)
contours, hierarchy cv2.findContours(gray_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)new_img img.copy()
hull cv2.convexHull(contours[0])
cv2.polylines(new_img, [hull], True, (0, 255, 0), 2)
print(f判断构造出来的多边形是否是凸形的{cv2.isContourConvex(hull)})
cv2.imshow(rst1, new_img)new_img img.copy()
epsilon 0.005 * cv2.arcLength(contours[0], True)
approx cv2.approxPolyDP(contours[0], epsilon, True)
rst cv2.drawContours(new_img, [approx], 0, [0, 0, 255], 2)
print(f判断构造出来的多边形是否是凸形的{cv2.isContourConvex(approx)})
cv2.imshow(rst2, rst)cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
判断构造出来的多边形是否是凸形的True
判断构造出来的多边形是否是凸形的False(2)点到轮廓的距离
在OpenCV中通过cv2.pointPolygonTest()函数来计算点到多边形轮廓的最短距离也就是垂线距离这个计算过程又称点和多边形的关系测试。函数的语法格式为
retvalcv2.pointPolygonTestcontour,pt,measureDist retval返回值与参数measureDist的值有关。contour为轮廓。pt为待判定的点。measureDist为布尔型值表示距离的判定方式。 当值为True时表示计算点到轮廓的距离。如果点在轮廓的外部返回值为负数如果点在轮廓上返回值为0如果点在轮廓内部返回值为正数。当值为False时不计算距离只返回“-1”、“0”和“1”中的一个值表示点相对于轮廓的位置关系。如果点在轮廓的外部返回值为“-1”如果点在轮廓上返回值为“0”如果点在轮廓内部返回值为“1”。
import cv2img cv2.imread(../contour2.bmp)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# t, gray_img cv2.threshold(gray_img, 20, 255, cv2.THRESH_BINARY)
contours, hierarchy cv2.findContours(gray_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)new_img img.copy()
hull cv2.convexHull(contours[0])
rst cv2.polylines(new_img, [hull], True, (0, 255, 0), 2)dist_a cv2.pointPolygonTest(hull, (300, 150), True)
font cv2.FONT_HERSHEY_SIMPLEX
cv2.circle(rst, (300, 150), 5, [0, 0, 255], -1)
cv2.putText(rst, A, (305, 155), font, 1, (0, 255, 0), 3)
print(fdist_a{dist_a})dist_b cv2.pointPolygonTest(hull, (300, 250), True)
font cv2.FONT_HERSHEY_SIMPLEX
cv2.circle(rst, (300, 250), 5, [0, 0, 255], -1)
cv2.putText(rst, B, (305, 255), font, 1, (0, 255, 0), 3)
print(fdist_b{dist_b})dist_c cv2.pointPolygonTest(hull, (405, 120), True)
font cv2.FONT_HERSHEY_SIMPLEX
cv2.circle(rst, (405, 120), 5, [0, 0, 255], -1)
cv2.putText(rst, C, (410, 125), font, 1, (0, 255, 0), 3)
print(fdist_c{dist_c})cv2.imshow(img, img)
cv2.imshow(rst, rst)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
dist_a18.454874482756573
dist_b-77.26881150094928
dist_c-0.0六、利用形状场景算法比较轮廓
OpenCV中用矩特征来比较形状是一种非常有效的方法。不过从OpenCV3开始有了更有效的方法OpenCV3中提供来专门的模块shape该模块中的形状场景算法能够更高效的比较形状。
比较轮廓的两种方法
矩特征形状场景算法
注意使用形状场景算法需要事先安装opencv-contrib-python这个库并且opencv-contrib-python这个库的版本要和opencv的一样。
1. 计算形状场景距离
OpenCV中形状场景算法使用距离 来作为形状比较的度量标准。 这是因为形状之间的差异值和距离有相似之处比如二者都只能是零或正数又比如当两个形状一模一样时距离值和差值都是零。
OpenCV中使用函数cv2.createShapeContextDistanceExtractor()来计算形状场景距离。 其使用的“形状上下文算法”在计算距离时会在每一个点上附加一个形状上下文描述符让每个点都能够捕获其他剩余点相对于它的分布特征从而提供全局鉴别特征。
cv2.createShapeContextDistanceExtractor()函数的语法格式为
retvalcv2.createShapeContextDistanceExtractor([,nAngularBins[, nRadialBins[, innerRadius[, outerRadius[, iterations[, comparer[, transformer]]]]]]] ) retval返回值nAngularBins为形状匹配中使用的形状上下文描述符建立的角容器的数量。nRadialBins为形状匹配中使用的形状上下文描述符建立的径向容器的数量。innerRadius形状上下文描述符的内半径。outerRadius形状上下文描述符的外半径。iterations迭代次数。comparer直方图代价提取算子。该函数使用了直方图代价提取仿函数可以直接采用直方图代价提取仿函数的算子作为参数。transformer形状变换参数。
函数cv2.createShapeContextDistanceExtractor的参数都是可选参数结果为retval.
该结果可以通过函数cv2.ShapeDistanceExtractor.computeDistance()计算两个不同形状之间的距离。此函数的语法格式为
retvalcv2.ShapeDistanceExtractor.computeDistancecontour1,contour2 coutour1和coutour2是不同的轮廓。
示例
构建形状场景算法对象。调用computeDistance()方法进行轮廓比较计算不同轮廓的距离。
import cv2img1 cv2.imread(../contour2.bmp)
cv2.imshow(img1, img1)
gray1 cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
contours1, hierarchy1 cv2.findContours(gray1, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnt1 contours1[0]h, w img1.shape[:2]
m cv2.getRotationMatrix2D((w/2, h/2), 90, 0.5)
img_rotate cv2.warpAffine(gray1, m, (w, h))
cv2.imshow(img2, img_rotate)
contours2, hierarchy2 cv2.findContours(img_rotate, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnt2 contours2[0]img3 cv2.imread(../grey_hand.jpg)
gray3 cv2.cvtColor(img3, cv2.COLOR_BGR2GRAY)
t, gray3 cv2.threshold(gray3, 20, 255, cv2.THRESH_BINARY)
cv2.imshow(img3, gray3)
contours3, hierarchy3 cv2.findContours(gray3, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnt3 contours3[0]# 构造距离提取算子对象
sd cv2.createShapeContextDistanceExtractor()
# 计算距离
d1 sd.computeDistance(cnt1, cnt1)
print(f与自身的距离d1{d1})
d2 sd.computeDistance(cnt1, cnt2)
print(f与旋转缩放后的自身图像的距离d2{d2})
d3 sd.computeDistance(cnt1, cnt3)
print(f与不相似对象的距离d3{d3})cv2.waitKey()
cv2.destroyAllWindows()# 数据结果
与自身的距离d10.00035154714714735746
与旋转缩放后的自身图像的距离d21.3983277082443237
与不相似对象的距离d3293.24066162109375从上述结果来看
相同图像之间的形状场景距离几乎为零。相似图像之间的形状场景距离较小。不同图像之间的形状场景距离较大
2. 计算Hausdorrf距离
Hausdorff距离的计算方法是
1针对图像 A 内的每一个点寻找其距离图像 B 的最短距离将这个最短距离作为Hausdorff直接距离D1。
2针对图像 B 内的每一个点寻找其距离图像 A 的最短距离将这个最短距离作为Hausdorff直接距离D2。
3将上述D1、D2中的较大者作为Hausdorff距离。
通常情况下Hausdorff距离H·是根据对象A和对象B之间的Hausdorff直接距离h·来定义的用数学公式表式如下HA,BmaxℎA,B,ℎB,A
其中 式中‖∙‖表示点a和点b的某种范数通常是欧氏距离。
麦吉尔大学的学者 Normand 和 Mikael 对 Hausdorff 距离给出了详细的说明网址为http://cgm.cs.mcgill.ca/~godfried/teaching/cg-projects/98/normand/main.html
OpenCV提供了函数cv2.createHausdorffDistanceExtractor来计算Hausdorff距离。
其语法格式为
retvalcv2.createHausdorffDistanceExtractor[,distanceFlag[,rankProp]] retval返回值distanceFlag距离标记是可选参数。rankProp为一个比例值范围在0到1之间也是可选参数。
示例
通过cv2.createHausdorffDistanceExtractor()构建Hausdorff对象。调用computeDistance()方法进行轮廓比较计算不同图像的Hausdorff距离。
import cv2img1 cv2.imread(../contour2.bmp)
cv2.imshow(img1, img1)
gray1 cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
contours1, hierarchy1 cv2.findContours(gray1, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnt1 contours1[0]h, w img1.shape[:2]
m cv2.getRotationMatrix2D((w/2, h/2), 90, 0.5)
img_rotate cv2.warpAffine(gray1, m, (w, h))
cv2.imshow(img2, img_rotate)
contours2, hierarchy2 cv2.findContours(img_rotate, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnt2 contours2[0]img3 cv2.imread(../grey_hand.jpg)
gray3 cv2.cvtColor(img3, cv2.COLOR_BGR2GRAY)
t, gray3 cv2.threshold(gray3, 20, 255, cv2.THRESH_BINARY)
cv2.imshow(img3, gray3)
contours3, hierarchy3 cv2.findContours(gray3, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnt3 contours3[0]# 构造距离提取算子对象
hd cv2.createHausdorffDistanceExtractor()
# 计算距离
d1 hd.computeDistance(cnt1, cnt1)
print(f与自身的hausdorff距离d1{d1})
d2 hd.computeDistance(cnt1, cnt2)
print(f与旋转缩放后的自身图像的hausdorff距离d2{d2})
d3 hd.computeDistance(cnt1, cnt3)
print(f与不相似对象的hausdorff距离d3{d3})cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
与自身的hausdorff距离d10.0
与旋转缩放后的自身图像的hausdorff距离d242.42640686035156
与不相似对象的hausdorff距离d3146.81961059570312七、轮廓的特征值
轮廓本身的属性特征及轮廓所包围对象的特征对于描述图像具有重要意义。下面介绍几种轮廓本身属性及所包围对象的特征。
1. 宽高比
使用宽高比AspectRation来描述轮廓例如矩形轮廓的宽高比为
宽高比宽度Width/高度Height
import cv2img cv2.imread(../contour3.bmp)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)x, y, w, h cv2.boundingRect(contours[0])
new_img img.copy()
cv2.rectangle(new_img, (x, y), (xw, yh), (255, 0, 0), 3)
aspect_ratio float(w)/h
print(aspect_ratio)
cv2.imshow(rst, new_img)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
2.1252. Extent:
使用轮廓面积与矩形边界矩形包围框、矩形轮廓面积之比 Extend 来描述图像及其轮廓特征。计算方法为 import cv2img cv2.imread(../contour3.bmp)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)new_img img.copy()
x, y, w, h cv2.boundingRect(contours[0])
cv2.drawContours(new_img, contours[0], -1, (0, 255, 255), 3)
cv2.rectangle(new_img, (x, y), (xw, yh), (255, 0, 0), 3)
rectArea w * h
cntArea cv2.contourArea(contours[0])
extend float(cntArea) / rectArea
print(extend)
cv2.imshow(rst, new_img)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
0.67205882352941183. Solidity:
使用轮廓面积与凸包面积之比 Solidity 来衡量图像、轮廓及凸包的特征。其计算方法为 import cv2img cv2.imread(../contour3.bmp)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)new_img img.copy()
cv2.drawContours(new_img, contours[0], -1, (0, 255, 255), 2)
cntArea cv2.contourArea(contours[0])hull cv2.convexHull(contours[0])
hullArea cv2.contourArea(hull)
cv2.polylines(new_img, [hull], True, (255, 0, 0), 2)
solidity float(cntArea)/hullArea
print(solidity)cv2.imshow(rst, new_img)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
0.89770662476059524. 等效直径
使用等效直径来衡量轮廓的特征值该值是与轮廓面积相等的圆形的直径。其计算公式为 import cv2
import numpy as npimg cv2.imread(../contour3.bmp)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)new_img img.copy()
cv2.drawContours(new_img, contours[0], -1, (0, 255, 0), 2)
cntArea cv2.contourArea(contours[0])equiDiameter np.sqrt(4*cntArea/np.pi)
print(equiDiameter)
cv2.circle(new_img, (100, 100), int(equiDiameter/2), (0, 255, 255), 3)cv2.imshow(rst, new_img)
cv2.waitKey()
cv2.destroyAllWindows()# 结果
107.876825309606645. 方向
在 OpenCV 中函数 cv2.fitEllipse()可以用来构造最优拟合椭圆还可以在返回值内分别返回椭圆的中心点、轴长、旋转角度等信息。使用这种形式能够更直观地获取椭圆的方向等信息。
函数cv2.fitEllipse()返回各个属性值的语法格式为
x,y), (MA,ma) ,anglecv2.fitEllipse(cnt)
(x,y)椭圆的中心点。(MA,ma)椭圆水平方向轴和垂直方向轴的长度。angle椭圆的旋转角度。
import cv2
import numpy as npimg cv2.imread(../contour3.bmp)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)new_img img.copy()
cv2.drawContours(new_img, contours[0], -1, (0, 255, 0), 2)
cntArea cv2.contourArea(contours[0])ellipse cv2.fitEllipse(contours[0])
cv2.ellipse(new_img, ellipse, (255,0 , 0), 2)
print(f(x, y): {ellipse[0][0], ellipse[0][1]})
print(f(MA, ma): {ellipse[1][0], ellipse[1][1]})
print(fangle: {ellipse[2]})cv2.imshow(rst, new_img)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
(x, y): (256.41845703125, 137.38284301757812)
(MA, ma): (69.56119537353516, 179.77560424804688)
angle: 81.936477661132816. 掩膜和像素点
如果希望获取某对象的掩模图像及其对应的点。可以通过将函数cv2.drawContours()的轮廓宽度参数 thickness 设置为“-1”即可获取特定对象的实心轮廓即特定对象的掩模。
希望获取轮廓像素点的具体位置信息。一般情况下轮廓像素点的位置就是图像内非零的像素点的位置可以通过两种方式获取轮廓像素点的位置信息。一种是使用Numpy函数另外一种是使用OpenCV函数。 使用Numpy函数获取轮廓像素点 numpy.nonzero函数能够找出数组内非零元素的位置但是其返回值是将行、列分别显示的。 例如对于如下数组a应用函数numpy.nonzero 返回的数组a内非零元素的位置信息为 array[0,1,1,2,2,2,3,4,4],dtypeint64,array[3,2,4,2,3,4,0,0,4],dtypeint64 使用numpy.transpose函数处理上述返回值则得到这些点的x,y形式的坐标 使用OpenCV函数获取轮廓点 OpenCV提供了函数cv2.findNonZero()用于查找非零元素的索引。该函数的语法格式为 idxcv2.findNonZerosrc idx为返回值表示非零元素的索引位置。需要注意的是在返回的索引中每个元素对应的是(列号行号)的格式。src为参数表示要查找非零元素的图像。
import cv2
import numpy as npimg np.zeros((5, 5), dtypenp.uint8)
for _ in range(10):i np.random.randint(0, 5)j np.random.randint(0, 5)img[i, j] 1print(fimg:\n{img})
loc cv2.findNonZero(img)
print(fimg:\n内非零位置{loc})# 输出结果
img:
[[1 1 0 0 0][0 1 0 0 1][0 0 1 0 1][0 1 0 0 0][0 0 1 0 0]]
img内非零位置:
[[[0 0]][[1 0]][[1 1]][[4 1]][[2 2]][[4 2]][[1 3]][[2 4]]]7. 最大值和最小值及它们的位置:
OpenCV 提供了函数 cv2.minMaxLoc()用于在指定的对象内查找最大值、最小值及其位置。该函数的语法格式是
min_val,max_val,min_loc,max_loccv2.minMaxLoc(imgray,maskmask) min_val最小值。max_val最大值。min_loc最小值的位置。max_loc最大值的位置。imgray单通道图像。mask掩模。通过使用掩模图像可以得到掩模指定区域内的最值信息。
import cv2
import numpy as npimg cv2.imread(../lena.bmp)
gray_img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# contours, hierarchy cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)min_val, max_val, min_loc, max_loc cv2.minMaxLoc(gray_img)
print(fmin_val: {min_val})
print(fmax_val: {max_val})
print(fmin_loc: {min_loc})
print(fmax_loc: {max_loc})
new_img1 img[min_loc[0]: min_loc[0]100, min_loc[1]: min_loc[1]100]
new_img2 img[max_loc[0]: max_loc[0]100, max_loc[1]: max_loc[1]100]cv2.imshow(img, img)
cv2.imshow(new_img1, new_img1)
cv2.imshow(new_img2, new_img2)
cv2.waitKey()
cv2.destroyAllWindows()8. 平均颜色及平均灰度:
OpenCV 提供了函数 cv2.mean()用于计算一个对象的平均颜色或平均灰度。该函数的语法格式为
mean_valcv2.mean(im,maskmask) mean_val表示返回的平均值。im原图像。mask掩模。
import cv2
import numpy as npimg cv2.imread(../lena.bmp)
mean_val cv2.mean(img)
print(mean_val)cv2.imshow(img, img)
cv2.waitKey()
cv2.destroyAllWindows()# 输出结果
(124.05046081542969, 124.05046081542969, 124.05046081542969, 0.0)注意函数cv2.mean能够计算各个通道的均值。上述4个值分别是RGB和A通道alpha通道的均值。本例中没有A通道所以A通道为0RGB三个通道的值相同所以计算出的均值也是一样的。 9. 极点
有时我们希望获取某个对象内的极值点例如最左端、最右端、最上端、最下端的四个点。
OpenCV提供了相应的函数来找出这些点通常的语法格式是
leftmosttuple(cnt [cnt[:,:,0].argmin()] [0])rightmosttuple(cnt[cnt[:,:,0].argmax()] [0])topmosttuple(cnt[cnt[:,:,1].argmin()] [0])bottommosttuple(cnt[cnt[:,:,1].argmax()] [0])
import cv2
import numpy as npimg cv2.imread(../contour2.bmp)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, hierarchy cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)new_img img.copy()
cv2.drawContours(new_img, contours[0], -1, (250, 150, 150), 2)cnt contours[0]
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])
print(fleftmost: {leftmost})
print(frightmost: {rightmost})
print(ftopmost: {topmost})
print(fbottommost: {bottommost})cv2.putText(new_img, A, leftmost, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.putText(new_img, B, rightmost, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.putText(new_img, C, topmost, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.putText(new_img, D, bottommost, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow(rst, new_img)cv2.waitKey()
cv2.destroyAllWindows()# 结果
leftmost: (165, 141)
rightmost: (405, 127)
topmost: (348, 70)
bottommost: (180, 190)均值。上述4个值分别是RGB和A通道alpha通道的均值。本例中没有A通道所以A通道为0RGB三个通道的值相同所以计算出的均值也是一样的。