中山营销型网站,厦门工程建设招聘信息网站,Wordpress百万数据查询多久,网络营销与直播电商好就业吗文章目录 前言查找并绘制轮廓查找图像轮廓#xff1a;findContours函数绘制图像轮廓#xff1a;drawContours函数轮廓实例 矩特征Hu矩Hu矩函数形状匹配 前言
图像轮廓指的是图像中物体边缘或形状的外部线条#xff0c;通常通过图像处理技术来检测和提取。轮廓是用于描述物体… 文章目录 前言查找并绘制轮廓查找图像轮廓findContours函数绘制图像轮廓drawContours函数轮廓实例 矩特征Hu矩Hu矩函数形状匹配 前言
图像轮廓指的是图像中物体边缘或形状的外部线条通常通过图像处理技术来检测和提取。轮廓是用于描述物体形状、进行目标识别、图像分割等操作的重要特征之一。
边缘检测虽然能够检测出边缘但边缘是不连续的检测到的边缘并不是一个整体。图像轮廓是指将边缘连接起来形成的一个整体用于后续的计算。
OpenCV提供了查找图像轮廓的函数cv2.findContours()该函数能够查找图像内的轮廓信息而函数cv2.drawContours()能够将轮廓绘制出来。
图像轮廓是图像中非常重要的一个特征信息通过对图像轮廓的操作我们能够获取目标图像的大小、位置、方向等信息。
查找并绘制轮廓
一个轮廓对应着一系列的点这些点以某种方式表示图像中的一条曲线。在OpenCV中函数cv2.findContours()用于查找图像的轮廓并能够根据参数返回特定表示方式的轮廓曲线。函数cv2.drawContours()能够将查找到的轮廓绘制到图像上该函数可以根据参数在图像上绘制不同样式实心/空心点以及线条的不同粗细、颜色等的轮廓可以绘制全部轮廓也可以仅绘制指定的轮廓。
查找图像轮廓findContours函数
函数cv2.findContours()的语法格式为
image, contours, hierarchy cv2.findContours( image, mode, method)式中的返回值为
image与函数参数中的原始图像image一致。contours返回的轮廓。hierarchy图像的拓扑信息轮廓层次。image原始图像。8位单通道图像所有非零值被处理为1所有零值保持不变。也就是说灰度图像会被自动处理为二值图像。在实际操作时可以根据需要预先使用阈值处理等函数将待查找轮廓的图像处理为二值图像。mode轮廓检索模式。method轮廓的近似方法。
函数cv2.findContours()的返回值及参数的含义比较丰富下面对上述返回值和参数逐一做出说明。
1返回值image
该返回值与参数image是一致的就是原始输入图像。在OpenCV 4.X中该返回值已经被取消。在OpenCV 4.X中函数cv2.findContours()仅有两个返回值其语法格式为
contours, hierarchy cv2.findContours( image, mode, method)2返回值contours
该返回值返回的是一组轮廓信息每个轮廓都是由若干个点所构成的。例如contours[i]是第i个轮廓下标从0开始, contours[i][j]是第i个轮廓内的第j个点。
图1所示为提取的轮廓示例函数cv2.findContours()提取出左图的3个轮廓每一个轮廓都是由若干个像素点构成的。 图1 下面针对图1来简单介绍一下contours的基本属性。
1type属性
返回值contours的type属性是list类型list的每个元素都是图像的一个轮廓用Numpy中的ndarray结构表示。
例如使用如下语句获取轮廓contours的类型
print (type(contours))结果为class ‘list’。
使用如下语句获取轮廓contours中每个元素的类型
print (type(contours[0]))结果为class ‘numpy.ndarray’。
2轮廓的个数使用如下语句可以获取轮廓的个数
使用如下语句可以获取轮廓的个数
print (len(contours))结果为“3”表示在图1中存在3个轮廓
3每个轮廓的点数每一个轮廓都是由若干个像素点构成的点的个数不固定具体个数取决于轮廓的形状。
例如使用如下语句可以获取每个轮廓内点的个数
print (len(contours[0])) #打印第0个轮廓的长度点的个数:4
print (len(contours[1])) #打印第1个轮廓的长度点的个数:60
print (len(contours[2])) #打印第2个轮廓的长度点的个数:184使用如下语句可以获取每个轮廓内点的shape属性
print(contours[0].shape)
print(contours[1].shape)
print(contours[2].shape)4轮廓内的点
使用如下语句可以获取轮廓内第0个轮廓中具体点的位置属性
print (contours[0]) #打印第0个轮廓中的像素点3返回值hierarchy
图像内的轮廓可能位于不同的位置。比如一个轮廓在另一个轮廓的内部。在这种情况下我们将外部的轮廓称为父轮廓内部的轮廓称为子轮廓。按照上述关系分类一幅图像中所有轮廓之间就建立了父子关系。
根据轮廓之间的关系就能够确定一个轮廓与其他轮廓是如何连接的。比如确定一个轮廓是某个轮廓的子轮廓或者是某个轮廓的父轮廓。上述关系被称为层次组织结构返回值hierarchy就包含上述层次关系。
每个轮廓contours[i]对应4个元素来说明当前轮廓的层次关系。其形式为
[Next, Previous, First_Child, Parent]式中各元素的含义为
Next后一个轮廓的索引编号。Previous前一个轮廓的索引编号。First_Child第1个子轮廓的索引编号。Parent父轮廓的索引编号。
如果上述各个参数所对应的关系为空时也就是没有对应的关系时则将该参数所对应的值设为“-1”。
使用print语句可以查看hierarchy的值
print(hierarchy)需要注意轮廓的层次结构是由参数mode决定的。也就是说使用不同的mode得到轮廓的编号是不一样的得到的hierarchy也不一样。
4参数image
该参数表示输入的图像必须是8位单通道二值图像。一般情况下都是将图像处理为二值图像后再将其作为image参数使用的。
5参数mode
参数mode决定了轮廓的提取方式具体有如下4种
cv2.RETR_EXTERNAL只检测外轮廓。cv2.RETR_LIST对检测到的轮廓不建立等级关系。cv2.RETR_CCOMP检索所有轮廓并将它们组织成两级层次结构。上面的一层为外边界下面的一层为内孔的边界。如果内孔内还有一个连通物体那么这个物体的边界仍然位于顶层。cv2.RETR_TREE建立一个等级树结构的轮廓。
下面分别对这四种情况进行简单的说明。
1cv2.RETR_EXTERNAL只检测外轮廓例如在图2中仅检测到两个外轮廓轮廓的序号如图中的数字标注所示。 图2 import cv2# 读取图像
image cv2.imread(C:\\Users\\Administrator\\Desktop\\res2.png, cv2.IMREAD_GRAYSCALE)# 检测轮廓
# contours, hierarchy cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours, hierarchy cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 打印轮廓的hierarchy信息
print(Hierarchy:\n, hierarchy)# 显示图像和轮廓
cv2.imshow(Contours, image)
cv2.waitKey(0)
cv2.destroyAllWindows()其中
输出值“[ 1-1-1-1]”表示的是第0个轮廓的层次。它即第0个轮廓的后一个轮廓就是第1个轮廓因此第1个元素的值是“1”。它的前一个轮廓不存在因此第2个元素的值是“-1”。它不存在子轮廓因此第3个元素的值是“-1”。它不存在父轮廓因此第4个元素的值是“-1”。输出值“[-1 0-1-1]”表示的是第1个轮廓的层次。它即第1个轮廓的后一个轮廓是不存在的因此第1个元素的值是“-1”。它的前一个轮廓是第0个轮廓因此第2个元素的值是“0”。它不存在子轮廓因此第3个元素的值是“-1”。它不存在父轮廓因此第4个元素的值是“-1”。
此时轮廓之间的关系为
2cv2.RETR_LIST对检测到的轮廓不建立等级关系
例如在图3中检测到三个轮廓各个轮廓的序号如图中数字的标注所示。 图3 import cv2# 读取图像
image cv2.imread(C:\\Users\\Administrator\\Desktop\\res2.png, cv2.IMREAD_GRAYSCALE)# 检测轮廓
contours, hierarchy cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# contours, hierarchy cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# cv2.RETR_LIST
contours, hierarchy cv2.findContours(image, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# 打印轮廓的hierarchy信息
print(Hierarchy:\n, hierarchy)# 显示图像和轮廓
cv2.imshow(Contours, image)
cv2.waitKey(0)
cv2.destroyAllWindows()其中
输出值“[ 1-1-1-1]”表示的是第0个轮廓的层次。它即第0个轮廓的后一个轮廓是第1个轮廓因此第1个元素的值是“1”。它的前一个轮廓不存在因此第2个元素的值是“-1”。它不存在子轮廓因此第3个元素的值是“-1”。它不存在父轮廓因此第4个元素的值是“-1”。输出值“[2 0-1-1]”表示的是第1个轮廓的层次。它即第1个轮廓的后一个轮廓是第2个轮廓因此第1个元素的值是“2”。它的前一个轮廓是第0个轮廓因此第2个元素的值是“0”。它不存在子轮廓因此第3个元素的值是“-1”。它不存在父轮廓因此第4个元素的值是“-1”。输出值“[-1 1-1-1]”表示的是第2个轮廓的层次。它即第2个轮廓的后一个轮廓是不存在的因此第1个元素的值是“-1”。它的前一个轮廓是第1个轮廓因此第2个元素的值是“1”。它不存在子轮廓因此第3个元素的值是“-1”。它不存在父轮廓因此第4个元素的值是“-1”。
从上述分析可以看出当参数mode为cv2.RETR_LIST时各个轮廓之间没有建立父子关系。此时轮廓之间的关系为
3cv2.RETR_CCOMP建立两个等级的轮廓
当参数mode为cv2.RETR_CCOMP时建立两个等级的轮廓。上面的一层为外边界下面的一层为内孔边界。
例如在图4中检测到三个轮廓各轮廓的序号如图中数字的标注所示。 图4 import cv2# 读取图像
image cv2.imread(C:\\Users\\Administrator\\Desktop\\res2.png, cv2.IMREAD_GRAYSCALE)# 检测轮廓
contours, hierarchy cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# contours, hierarchy cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# cv2.RETR_LIST
contours, hierarchy cv2.findContours(image, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
# 打印轮廓的hierarchy信息
print(Hierarchy:\n, hierarchy)# 显示图像和轮廓
cv2.imshow(Contours, image)
cv2.waitKey(0)
cv2.destroyAllWindows()其中
输出值“[ 1-1-1-1]”表示的是第0个轮廓的层次。它即第0个轮廓的后一个轮廓是第1个轮廓因此第1个元素的值是“1”。它的前一个轮廓不存在因此第2个元素的值是“-1”。它不存在子轮廓因此第3个元素的值是“-1”。它不存在父轮廓因此第4个元素的值是“-1”。输出值“[-1 0 2-1]”表示的是第1个轮廓的层次。它即第1个轮廓的后一个轮廓不存在因此第1个元素的值是“-1”。它的前一个轮廓是第0个轮廓因此第2个元素的值是“0”。它的第1个子轮廓是第2个轮廓因此第3个元素的值是“2”。它不存在父轮廓因此第4个元素的值是“-1”。输出值“[-1-1-1 1]”表示的是第2个轮廓的层次。它即第2个轮廓的后一个轮廓不存在因此第1个元素的值是“-1”。它的前一个轮廓也不存在因此第2个元素的值是“-1”。它不存在子轮廓因此第3个元素的值是“-1”。它的父轮廓是第1个轮廓因此第4个元素的值是“1”。
此时轮廓关系为 4cv2.RETR_TREE建立一个等级树结构的轮廓
例如在图5中检测到三个轮廓各个轮廓的序号如图中的数字标注所示。 图5 import cv2# 读取图像
image cv2.imread(C:\\Users\\Administrator\\Desktop\\res2.png, cv2.IMREAD_GRAYSCALE)# 检测轮廓
# contours, hierarchy cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# contours, hierarchy cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# cv2.RETR_LIST
contours, hierarchy cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 打印轮廓的hierarchy信息
print(Hierarchy:\n, hierarchy)# 显示图像和轮廓
cv2.imshow(Contours, image)
cv2.waitKey(0)
cv2.destroyAllWindows()其中
输出值“[ 1-1-1-1]”表示的是第0个轮廓的层次。它即第0个轮廓的后一个轮廓是第1个轮廓因此第1个元素的值为“1”。它的前一个轮廓不存在因此第2个元素的值是“-1”。它不存在子轮廓因此第3个元素的值是“-1”。它不存在父轮廓因此第4个元素的值是“-1”。输出值“[-1 0 2-1]”表示的是第1个轮廓的层次。它即第1个轮廓的后一个轮廓不存在因此第1个元素的值是“-1”。它的前一个轮廓是第0个轮廓因此第2个元素的值是“0”。它的第1个子轮廓是第2个轮廓因此第3个元素的值是“2”。它的父轮廓不存在因此第4个元素的值是“-1”。输出值“[-1-1-1 1]”表示的是第2个轮廓的层次。它即第2个轮廓的后一个轮廓不存在因此第1个元素的值是“-1”。它的前一个轮廓是不存在的因此第2个元素的值是“-1”。它的子轮廓是不存在的因此第3个元素的值是“-1”。它的父轮廓是第1个轮廓因此第1个元素的值是“1”。
此时轮廓之间的关系为
需要注意本例中仅有两层轮廓所以使用参数cv2.RETR_CCOMP和cv2.RETR_TREE得到的层次结构是一致的。当有多层轮廓时使用参数cv2.RETR_CCOMP也会得到仅有两层的层次结构而使用参数cv2.RETR_TREE会得到含有多个层次的结构。限于篇幅这里不再列举更多层轮廓的层次关系。
6参数method
参数method决定了如何表达轮廓可以为如下值
● cv2.CHAIN_APPROX_NONE存储所有的轮廓点相邻两个点的像素位置差不超过1即max(abs(x1-x2), abs(y2-y1))1。● cv2.CHAIN_APPROX_SIMPLE压缩水平方向、垂直方向、对角线方向的元素只保留该方向的终点坐标。例如在极端的情况下一个矩形只需要用4个点来保存轮廓信息。● cv2.CHAIN_APPROX_TC89_L1使用teh-Chinl chain近似算法的一种风格。● cv2.CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain近似算法的一种风格。
例如在图6中左图是使用参数值cv2.CHAIN_APPROX_NONE存储的轮廓保存了轮廓中的每一个点右图是使用参数值cv2.CHAIN_APPROX_SIMPLE存储的轮廓仅仅保存了边界上的四个点。 图6 在使用函数cv2.findContours()查找图像轮廓时需要注意以下问题
待处理的源图像必须是灰度二值图。因此在通常情况下都要预先对图像进行阈值分割或者边缘检测处理得到满意的二值图像后再将其作为参数使用。在OpenCV中都是从黑色背景中查找白色对象。因此对象必须是白色的背景必须是黑色的。在OpenCV 4.x中函数cv2.findContours()仅有两个返回值。
绘制图像轮廓drawContours函数
在OpenCV中可以使用函数cv2.drawContours()绘制图像轮廓。该函数的语法格式是
image cv2.drawContours(
image,
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偏移参数。该参数使轮廓偏移到不同的位置展示出来。函数cv2.drawContours()的参数image和返回值image在函数运算后的值是相同的。因此也可以将函数cv2.drawContours()写为没有返回值的形式
cv2.drawContours(
image,
contours,
contourIdx,
color[,
thickness[,
lineType[,
hierarchy[,
maxLevel[,
offset]]]]] )轮廓实例
【例1】绘制一幅图像内的所有轮廓。如果要绘制图像内的所有轮廓需要将函数cv2.drawContours()的参数contourIdx的值设置为“-1”。根据题目的要求及分析编写代码如下
import cv2# 读取图像
o cv2.imread(rC:\\Users\\Administrator\\Desktop\\res3.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_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 绘制轮廓
o cv2.drawContours(o, contours, -1, (0, 0, 255), 5)# 显示结果图像
cv2.imshow(result, o)
cv2.waitKey()
cv2.destroyAllWindows()在本程序中轮廓的颜色被设置为红色由于黑白印刷的原因在纸质书中显示为灰色即(0, 0, 255)参数thickness轮廓线条的粗细被设置为“5”。
运行上述程序结果如图7所示图像内的所有轮廓都被绘制出来了。 图7 【例2】逐个显示一幅图像内的边缘信息。如果要绘制图像内的某个具体轮廓需要将函数cv2.drawContours()的参数contourIdx设置为具体的索引值。本例通过循环语句逐一绘制轮廓。根据题目要求及分析编写代码如下
import cv2
import numpy as npo cv2.imread(rC:\\Users\\Administrator\\Desktop\\res3.png)
cv2.imshow(original, o)
resized_img cv2.resize(o, (300, 300)) # 调整大小到 300x300
cv2.imshow(original, resized_img)
gray cv2.cvtColor(o, cv2.COLOR_BGR2GRAY)
ret, binary cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
n len(contours)
contoursImg []
for i in range(n):temp np.zeros(o.shape, np.uint8)contoursImg.append(temp)contoursImg[i] cv2.drawContours(contoursImg[i], contours, i, (255, 255, 255), 5)# 缩放图像resized_img cv2.resize(contoursImg[i], (300, 300)) # 调整大小到 300x300cv2.imshow(fcontours[{i}], resized_img)# cv2.imshow(contours[ str(i) ], contoursImg[i])
cv2.waitKey()
cv2.destroyAllWindows()运行上述程序结果如图8所示图像内的轮廓被逐一绘制出来。 图8 【例3】使用轮廓绘制功能提取前景对象。将函数cv2.drawContours()的参数thickness的值设置为“-1”可以绘制前景对象的实心轮廓。将该实心轮廓与原始图像进行“按位与”操作即可将前景对象从原始图像中提取出来。根据题目的要求及分析编写代码如下
# test.jpgimport cv2
import numpy as npo cv2.imread(rC:\\Users\\Administrator\\Desktop\\test.jpg)
# cv2.imshow(original, o)
resized_img cv2.resize(o, (350, 500)) # 调整大小到 300x300
cv2.imshow(original, resized_img)
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)
contours, hierarchy cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
mask np.zeros(o.shape, np.uint8)
mask cv2.drawContours(mask, contours, -1, (255, 255, 255), -1)
mask cv2.resize(mask, (350, 500)) # 调整大小到 300x300
cv2.imshow(mask, mask)
loc cv2.bitwise_and(resized_img, mask)
loc cv2.resize(loc, (350, 500)) # 调整大小到 300x300
cv2.imshow(location, loc)
cv2.waitKey()
cv2.destroyAllWindows()本例中将函数cv2.drawContours()的参数thickness设置为“-1”得到了前景对象的实心轮廓mask。接下来通过语句“cv2.bitwise_and(o, mask)”将原始图像o与实心轮廓mask进行“按位与”运算就得到了原始图像的前景对象。运行上述程序结果如图9所示。其中 图9 矩特征
比较两个轮廓最简单的方法是比较二者的轮廓矩。轮廓矩代表了一个轮廓、一幅图像、一组点集的全局特征。矩信息包含了对应对象不同类型的几何特征例如大小、位置、角度、形状等。矩特征被广泛地应用在模式识别、图像识别等方面。
矩的计算moments函数
OpenCV提供了函数cv2.moments()来获取图像的moments特征。通常情况下我们将使用函数cv2.moments()获取的轮廓特征称为“轮廓矩”。轮廓矩描述了一个轮廓的重要特征使用轮廓矩可以方便地比较两个轮廓。函数cv2.moments()的语法格式为
retval cv2.moments( array[, binaryImage] )式中有两个参数
array可以是点集也可以是灰度图像或者二值图像。当array是点集时函数会把这些点集当成轮廓中的顶点把整个点集作为一条轮廓而不是把它们当成独立的点来看待。binaryImage该参数为True时array内所有的非零值都被处理为1。该参数仅在参数array为图像时有效。
该函数的返回值retval是矩特征主要包括
1空间矩
零阶矩m00一阶矩m10, m01二阶矩m20, m11, m02三阶矩m30, m21, m12, m03
2中心矩
● 二阶中心矩mu20, mu11, mu02● 三阶中心矩mu30, mu21, mu12, mu03
3归一化中心矩
● 二阶Hu矩nu20, nu11, nu02● 三阶Hu矩nu30, nu21, nu12, nu03
上述矩都是根据公式计算得到的大多数矩比较抽象。但是很明显如果两个轮廓的矩一致那么这两个轮廓就是一致的。虽然大多数矩都是通过数学公式计算得到的抽象特征但是零阶矩“m00”的含义比较直观它表示一个轮廓的面积。
矩特征函数cv2.moments()所返回的特征值能够用来比较两个轮廓是否相似。例如有两个轮廓不管它们出现在图像的哪个位置我们都可以通过函数cv2.moments()的m00矩判断其面积是否一致。
在位置发生变化时虽然轮廓的面积、周长等特征不变但是更高阶的特征会随着位置的变化而发生变化。在很多情况下我们希望比较不同位置的两个对象的一致性。解决这一问题的方法是引入中心矩。中心矩通过减去均值而获取平移不变性因而能够比较不同位置的两个对象是否一致。很明显中心矩具有的平移不变性使它能够忽略两个对象的位置关系帮助我们比较不同位置上两个对象的一致性。
除了考虑平移不变性外我们还会考虑经过缩放后大小不一致的对象的一致性。也就是说我们希望图像在缩放前后能够拥有一个稳定的特征值。也就是说让图像在缩放前后具有同样的特征值。显然中心矩不具有这个属性。例如两个形状一致、大小不一的对象其中心矩是有差异的。
归一化中心矩通过除以物体总尺寸而获得缩放不变性。它通过上述计算提取对象的归一化中心矩属性值该属性值不仅具有平移不变性还具有缩放不变性。
在OpenCV中函数cv2.moments()会同时计算上述空间矩、中心矩和归一化中心距。
【例4】使用函数cv2.moments()提取一幅图像的特征。根据题目的要求及分析编写代码如下
import cv2
import numpy as np
o cv2.imread(moments.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)
nlen(contours)
contoursImg[]
for i in range(n):tempnp.zeros(image.shape, np.uint8)contoursImg.append(temp)contoursImg[i]cv2.drawContours(contoursImg[i], contours, i,255,3)cv2.imshow(contours[ str(i)], contoursImg[i])print(观察各个轮廓的矩moments:)for i in range(n):print(轮廓str(i)的矩\n, cv2.moments(contours[i]))print(观察各个轮廓的面积)for i in range(n):print(轮廓str(i)的面积%d %cv2.moments(contours[i])[m00])cv2.waitKey()cv2.destroyAllWindows()本例中首先使用函数cv2.moments()提取各个轮廓的特征接下来通过语句cv2.moments(contours[i])[‘m00’])提取各个轮廓矩的面积信息。
运行上述程序会显示如图10所示的图像。其中
● (a)图是原始图像。● (b)图是原始图像的第0个轮廓。● ©图是原始图像的第1个轮廓。● (d)图是原始图像的第2个轮廓。 图10 同时程序会显示如下输出结果
观察各个轮廓的矩moments:
轮廓 0 的矩{m00: 2953.5, m10: 141914.0, m01: 674953.8333333333, m20: 7297301.083333333, m11: 32432175.458333332, m02: 155353008.91666666, m30: 396608955.90000004, m21: 1667753129.2333333, m12: 7465179622.3, m03: 36008721801.950005, mu20: 478413.86613340117, mu11: 1026.5490167029202, mu02: 1107985.3407868445, mu30: 2747.422912657261, mu21: 25787.93059720099, mu12: 86460.36071126908, mu03: -9125.510330200195, nu20: 0.05484408663078589, nu11: 0.00011768083491774101, nu02: 0.12701647740036529, nu30: 5.795395481220832e-06, nu21: 5.439688799439629e-05, nu12: 0.0001823789055053718, nu03: -1.924929033962443e-05}
轮廓 0 的面积2953.5
轮廓 1 的矩{m00: 6956.5, m10: 1133880.6666666665, m01: 834706.1666666666, m20: 192610842.25, m11: 136054500.54166666, m02: 102240819.08333333, m30: 33935221179.300003, m21: 23111377235.933334, m12: 16665018223.266666, m03: 12768156053.050001, mu20: 7792993.2971582115, mu11: 855.0689752697945, mu02: 2084938.299697727, mu30: 1012.8543243408203, mu21: -158289.87633812428, mu12: -16717.500845849514, mu03: 1969.0043239593506, nu20: 0.16103590702740464, nu11: 1.7669309179795806e-05, nu02: 0.0430835646054556, nu30: 2.5094006894295327e-07, nu21: -3.921716235660208e-05, nu12: -4.141850129870927e-06, nu03: 4.878313385539436e-07}
轮廓 1 的面积6956.5
轮廓 2 的矩{m00: 779.0, m10: 233716.5, m01: 18296.0, m20: 70194404.66666666, m11: 5489360.916666666, m02: 463822.5, m30: 21104513696.25, m21: 1648724552.9166667, m12: 139165821.08333334, m03: 12496190.0, mu20: 74504.3171801418, mu11: 173.3890671795234, mu02: 34113.10847240052, mu30: -237.9855079650879, mu21: -1859.8601903982926, mu12: 1102.246367705986, mu03: 216.54015137441456, nu20: 0.12277403336317387, nu11: 0.00028572404827545173, nu02: 0.05621424470726354, nu30: -1.4050988149079054e-05, nu21: -0.00010980867582098307, nu12: 6.507812506076738e-05, nu03: 1.2784825121402051e-05}
轮廓 2 的面积779.0补充 计算轮廓的面积contourArea函数 计算轮廓的长度arcLength函数 Hu矩
Hu矩是归一化中心矩的线性组合。Hu矩在图像旋转、缩放、平移等操作后仍能保持矩的不变性所以经常会使用Hu距来识别图像的特征。
在OpenCV中使用函数cv2.HuMoments()可以得到Hu距。该函数使用cv2.moments()函数的返回值作为参数返回7个Hu矩值。
Hu矩函数
函数cv2.HuMoments()的语法格式为
hu cv2.HuMoments( m )式中返回值hu表示返回的Hu矩值参数m是由函数cv2.moments()计算得到矩特征值。
【例8】计算图像的Hu矩对其中第0个矩的关系进行演示。
Hu矩是归一化中心矩的线性组合每一个矩都是通过归一化中心矩的组合运算得到的。函数cv2.moments()返回的归一化中心矩中包含
二阶Hu矩 n u 20 , n u 11 , n u 02 nu_{20}, nu_{11}, nu_{02} nu20,nu11,nu02三阶Hu矩 n u 30 , n u 21 , n u 12 , n u 03 nu_{30}, nu_{21}, nu_{12}, nu_{03} nu30,nu21,nu12,nu03
为了表述上的方便将上述字母nu表示为字母v则归一化中心矩为
二阶Hu矩 v 20 , v 11 , v 02 v20, v11, v02 v20,v11,v02三阶Hu矩 v 30 , v 21 , v 12 , v 03 v30, v21, v12, v03 v30,v21,v12,v03
上述7个Hu矩的计算公式为 本例对Hu矩中的第0个矩 h 0 v 20 v 02 ℎ_0v_{20}v_{02} h0v20v02的关系进行验证即Hu矩中第0个矩对应的函数cv2.moments()的返回值为 h 0 n u 20 n u 02 ℎ0nu20nu02 h0nu20nu02
根据题目的要求及分析编写代码如下
import cv2
# o1 cv2.imread(cs1.bmp)
o1 cv2.imread(rC:\\Users\\Administrator\\Desktop\\moment.png)
gray cv2.cvtColor(o1, cv2.COLOR_BGR2GRAY)
HuM1cv2.HuMoments(cv2.moments(gray)).flatten()
print(cv2.moments(gray)\n, cv2.moments(gray))
print(\nHuM1\n, HuM1)
print(\ncv2.moments(gray)[nu20]cv2.moments(gray)[nu02]%f%f%f\n%(cv2.moments(gray)[nu20], cv2.moments(gray)[nu02],cv2.moments(gray)[nu20]cv2.moments(gray)[nu02]))
print(HuM1[0], HuM1[0])
print(\nHu[0]-(nu02nu20),HuM1[0]-(cv2.moments(gray)[nu20]cv2.moments(gray)[nu02]))
程序运行结果显示Hu[0]-(nu02nu20)0.0。从该结果可知关系 h 0 n u 20 n u 02 ℎ_0nu_{20}nu_{02} h0nu20nu02成立。
cv2.moments(gray){m00: 2819590.0, m10: 397570476.0, m01: 403860142.0, m20: 71305223188.0, m11: 45735796942.0, m02: 68512957510.0, m30: 14697923289666.0, m21: 6934198814642.0, m12: 6404215738518.0, m03: 13009490405824.0, mu20: 15246617721.649727, mu11: -11209669913.949831, mu02: 10666598891.810768, mu30: 344041464963.81635, mu21: -117915129251.81013, mu12: -45108937401.78011, mu03: 140498447760.5766, nu20: 0.0019177923774410773, nu11: -0.0014100058063420402, nu02: 0.0013416957400911801, nu30: 2.57718671649104e-05, nu21: -8.832926717512085e-06, nu12: -3.3790739229389187e-06, nu03: 1.0524624794695901e-05}HuM1[ 3.25948812e-03 8.28435283e-06 2.66019518e-09 5.04299032e-103.02264955e-19 7.35730936e-14 -4.99811415e-19]cv2.moments(gray)[nu20]cv2.moments(gray)[nu02]0.0019180.0013420.003259HuM1[0] 0.0032594881175322574Hu[0]-(nu02nu20) 0.0Process finished with exit code 0【例9】计算三幅不同图像的Hu矩并进行比较。根据题目的要求编写代码如下
import cv2
#----------------计算图像o1的Hu矩-------------------
o1 cv2.imread(cs1.bmp)
gray1 cv2.cvtColor(o1, cv2.COLOR_BGR2GRAY)
HuM1cv2.HuMoments(cv2.moments(gray1)).flatten()
#----------------计算图像o2的Hu矩-------------------
o2 cv2.imread(cs3.bmp)
gray2 cv2.cvtColor(o2, cv2.COLOR_BGR2GRAY)
HuM2cv2.HuMoments(cv2.moments(gray2)).flatten()
#----------------计算图像o3的Hu矩-------------------
o3 cv2.imread(lena.bmp)
gray3 cv2.cvtColor(o3, cv2.COLOR_BGR2GRAY)
HuM3cv2.HuMoments(cv2.moments(gray3)).flatten()
#---------打印图像o1、图像o2、图像o3的特征值------------
print(o1.shape, o1.shape)
print(o2.shape, o2.shape)
print(o3.shape, o3.shape)
print(cv2.moments(gray1)\n, cv2.moments(gray1))
print(cv2.moments(gray2)\n, cv2.moments(gray2))
print(cv2.moments(gray3)\n, cv2.moments(gray3))
print(\nHuM1\n, HuM1)
print(\nHuM2\n, HuM2)
print(\nHuM3\n, HuM3)
#---------计算图像o1与图像o2、图像o3的Hu矩之差----------------
print(\nHuM1-HuM2, HuM1-HuM2)
print(\nHuM1-HuM3, HuM1-HuM3)
#---------显示图像----------------
cv2.imshow(original1, o1)
cv2.imshow(original2, o2)
cv2.imshow(original3, o3)
cv2.waitKey()
cv2.destroyAllWindows()运行上述程序会显示各个图像的shape属性、moments属性、HuMoments属性以及不同图像的Hu矩之差。
D:\Code\py_demo\.venv\Scripts\python.exe D:\Code\py_demo\operator_cv\sb_hu.py
o1.shape (400, 500, 3)
o2.shape (400, 500, 3)
o3.shape (400, 500, 3)
cv2.moments(gray1){m00: 23410570.0, m10: 6894702439.0, m01: 4322923538.0, m20: 2490728253949.0, m11: 1296698767586.0, m02: 1124670909868.0, m30: 975876474148585.0, m21: 473799154186464.0, m12: 342275113887012.0, m03: 337006213255976.0, mu20: 460153102538.6518, mu11: 23544313804.690384, mu02: 326413203396.332, mu30: -28715366381641.523, mu21: 785697984.9051791, mu12: 2350378948735.8237, mu03: 8779049674558.577, nu20: 0.0008396115002459006, nu11: 4.2959781270095615e-05, nu02: 0.0005955849865874677, nu30: -1.0828901211539646e-05, nu21: 2.962959186229814e-10, nu12: 8.863554484129715e-07, nu03: 3.3106825242450395e-06}
cv2.moments(gray2){m00: 28034550.0, m10: 6816434088.0, m01: 5445534050.0, m20: 2251390234926.0, m11: 1331515245914.0, m02: 1388596230890.0, m30: 841561841935968.0, m21: 438638459033146.0, m12: 340557983142876.0, m03: 400222877167790.0, mu20: 594014829362.2085, mu11: 7466030721.150593, mu02: 330835678653.22784, mu30: 5287351850554.212, mu21: -2310498476300.1636, mu12: 28610218122.137913, mu03: 1971363833879.6504, nu20: 0.0007558056051916368, nu11: 9.4995403963859e-06, nu02: 0.0004209448114148325, nu30: 1.270586505153425e-06, nu21: -5.552284521895067e-07, nu12: 6.8752294310948995e-09, nu03: 4.7373209781990474e-07}
cv2.moments(gray3){m00: 26617062.0, m10: 6626512283.0, m01: 4619614220.0, m20: 2204631080519.0, m11: 1158027266838.0, m02: 1224946110016.0, m30: 825815455682453.0, m21: 385764754114036.0, m12: 307812229426686.0, m03: 378612525048194.0, mu20: 554912376149.9802, mu11: 7940515281.743969, mu02: 423173339541.70013, mu30: 657431568603.1985, mu21: -821148323850.9675, mu12: 96649147636.55907, mu03: 19122299481335.758, nu20: 0.0007832568081619774, nu11: 1.1208008547027264e-05, nu02: 0.0005973076353573567, nu30: 1.7986649164798885e-07, nu21: -2.2465770612065727e-07, nu12: 2.6442209252425936e-08, nu03: 5.231663771877604e-06}HuM1[1.43519649e-03 6.69311106e-08 1.92880003e-10 1.09816797e-105.71662342e-21 1.57910213e-14 1.49252448e-20]HuM2[ 1.17675042e-03 1.12492716e-07 6.13950909e-12 1.63855014e-123.42641324e-24 5.40282209e-16 -3.90755201e-24]HuM3[ 1.38056444e-03 3.50795727e-08 3.48866553e-11 2.51126730e-117.35977227e-22 -4.60754057e-15 1.04139331e-22]HuM1-HuM2 [ 2.58446070e-04 -4.55616057e-08 1.86740494e-10 1.08178247e-105.71319700e-21 1.52507391e-14 1.49291523e-20]HuM1-HuM3 [5.46320433e-05 3.18515379e-08 1.57993347e-10 8.47041240e-114.98064619e-21 2.03985618e-14 1.48211054e-20]
同时还会显示三幅原始图像如图11所示 图11 从上述输出结果可以看到由于Hu矩的值本身就非常小因此在这里并没有发现两个对象的Hu矩差值的特殊意义。
形状匹配
我们可以通过Hu矩来判断两个对象的一致性。例如前面计算了两个对象Hu矩的差但是结果比较抽象。为了更直观方便地比较Hu矩值OpenCV提供了函数cv2.matchShapes()对两个对象的Hu矩进行比较。
函数cv2.matchShapes()允许我们提供两个对象对二者的Hu矩进行比较。这两个对象可以是轮廓也可以是灰度图。不管是什么cv2.matchShapes()都会提前计算好对象的Hu矩值。
函数cv2.matchShapes()的语法格式为
retval cv2.matchShapes( contour1, contour2, method, parameter )式中retval是返回值。
该函数有如下4个参数
contour1第1个轮廓或者灰度图像。contour2第2个轮廓或者灰度图像。method比较两个对象的Hu矩的方法具体如表1所示。 表1 在表1中A表示对象1, B表示对象2 式中和分别是对象A和对象B的Hu矩。
parameter应用于method的特定参数该参数为扩展参数目前截至OpenCV 4.1.0版本暂不支持该参数因此将该值设置为0。
【例10】使用函数cv2.matchShapes()计算三幅不同图像的匹配度。根据题目要求编写代码如下
import cv2
o1 cv2.imread(aa1.jpeg)
o2 cv2.imread(aa2.png)
o3 cv2.imread(aa3.jpeg)
gray1 cv2.cvtColor(o1, cv2.COLOR_BGR2GRAY)
gray2 cv2.cvtColor(o2, cv2.COLOR_BGR2GRAY)
gray3 cv2.cvtColor(o3, cv2.COLOR_BGR2GRAY)
cv2.imshow(o1,gray1)
cv2.imshow(o2,gray2)
cv2.imshow(o3,gray3)
ret, binary1 cv2.threshold(gray1,127,255, cv2.THRESH_BINARY)
ret, binary2 cv2.threshold(gray2,127,255, cv2.THRESH_BINARY)
ret, binary3 cv2.threshold(gray3,127,255, cv2.THRESH_BINARY)
contours1, hierarchy cv2.findContours(binary1,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
contours2, hierarchy cv2.findContours(binary2,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
contours3, hierarchy cv2.findContours(binary3,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
cnt1 contours1[0]
cnt2 contours2[0]
cnt3 contours3[0]
ret0 cv2.matchShapes(cnt1, cnt1,1,0.0)
ret1 cv2.matchShapes(cnt1, cnt2,1,0.0)
ret2 cv2.matchShapes(cnt1, cnt3,1,0.0)
print(相同图像的matchShape, ret0)
print(相似图像的matchShape, ret1)
print(不相似图像的matchShape, ret2)###################################### result #################################
D:\Code\py_demo\.venv\Scripts\python.exe D:\Code\py_demo\operator_cv\sb_hu2.py
相同图像的matchShape 0.0
相似图像的matchShape 0.9588739815881875
不相似图像的matchShape 1.7976931348623157e308从以上结果可以看出
同一幅图像的Hu矩是不变的二者差值为0。相似的图像即使发生了平移、旋转和缩放后函数cv2.matchShapes()的返回值仍然比较接近。例如图像o1和图像o2, o2是对o1经过缩放、旋转和平移后得到的但是对二者应用cv2.matchShapes()函数后返回值的差较小。不相似图像cv2.matchShapes()函数返回值的差较大。例如图像o1和图像o3的差别较大因此对二者应用cv2.matchShapes()函数后返回值的差也较大。