在上次的笔记中,整理记录了有关轮廓发现及轮廓信息提取的一部分内容,同时还记录了Hu矩的计算方式,今天就来记录一下Hu矩的一个应用——轮廓匹配。
在《OpenCV学习笔记(19)——模板匹配》中,我们提到了使用模板匹配来实现对图像中的目标物体进行寻找的方法,但是由于模板匹配的工作条件比较苛刻,而且当模板和目标物体的大小或角度出现偏差时就无法达到比较好的效果,所以模板匹配这种方法只能作为一种入门级别的模式识别方法。

今天我们先了解一下图像的各种矩及其计算方法,然后使用基于Hu矩的轮廓匹配来实现对图像中目标物体的寻找。

之前我们通过moment()求得了轮廓的几何矩、中心距和归一化矩,然后使用轮廓的x、y方向的两个一阶几何矩和零阶几何矩来求取轮廓的质心坐标,而今天要计算的Hu矩,也是通过几何矩来进行计算的。

首先一起了解一下图像的各种矩是怎么得来的,以及这些矩有什么用处。
几何矩:假如一幅图像的坐标点是(x,y),用行列来表示的话就是(col,row),其灰度值是f(x,y),那么该图像的(p+q)阶几何矩mpq可以用下面这段代码来计算,其中height、width分别为图像的高和宽。

int p, q;
int mpq = 0;
for(int row = 0; row < height; row++)
{for(int col = 0; col < width; col++){mpq += pow(col, p) * pow(row, q) * f(x , y);}
}

当使用不同的p和q进行计算后,就能得到不同阶的几何矩,而不同阶几何矩的含义如下:
零阶矩(m00):与图像或某个轮廓的面积相关;
一阶矩(m01,m10):与图像或某个轮廓的质心相关;
二阶矩(m02,m11,m20):与图像或某个轮廓的旋转半径相关;
三阶矩(m03,m12,m21,m30):与图像或某个轮廓的斜度或扭曲程度相关。
可见几何矩是与图像息息相关的,当图像的尺寸或者角度发生变化时,其几何矩也会随之变化,也就是说几何矩不具有空间不变性、尺度不变性和旋转不变性,所以几何矩难以用来表示一幅图像的特征。

那么就需要做进一步改进,利用之前求得的质心坐标来计算图像的( p+q )阶中心距mupq,可以用以下代码计算,其中质心坐标为(x0,y0):

int p, q;
//求取一阶几何矩和零阶几何矩
int m10 = 0;
int m01 = 0;
int m00 = 0;
for(int row = 0; row < height; row++)
{for(int col = 0; col < width; col++){m10 += pow(col, 1) * pow(row, 0) * f(x , y);m01 += pow(col, 0) * pow(row, 1) * f(x , y);m00 += pow(col, 0) * pow(row, 0) * f(x , y);}
}
//计算质心坐标
int x0 = m10 / m00;
int y0 = m01 / m00;
//计算(p+q)阶中心距
int mupq;
for(int row = 0; row < height; row++)
{for(int col = 0; col < width; col++){mupq += pow( (col-x0) , p) * pow( (row-y0) , q) * f(x , y);}
}

可见中心距的结果,会一直围绕质心为中心来计算,也就是说会以图像或轮廓中的每个点到质心的距离来计算,所以当图像或轮廓发生平移时,中心距不会发生改变。即中心距具有空间不变性,针对只进行平移的图像或者轮廓,可以用中心距来作为它的一个特征。

但是仅仅具有空间不变性仍是不够的,很多时候都会涉及到图像尺寸的变换,而这时中心距仍会随着图像尺寸的变化而变化。因此,可以利用零阶中心矩来对各阶中心距进行归一化,从而抵消尺度变化带来的影响,这就是归一化矩。(p+q)阶归一化矩nupq可以通过以下代码来计算:

int p, q;
//求取一阶几何矩和零阶几何矩
int m10 = 0;
int m01 = 0;
int m00 = 0;
for(int row = 0; row < height; row++)
{for(int col = 0; col < width; col++){m10 += pow(col, 1) * pow(row, 0) * f(x , y);m01 += pow(col, 0) * pow(row, 1) * f(x , y);m00 += pow(col, 0) * pow(row, 0) * f(x , y);}
}
//计算质心坐标
int x0 = m10 / m00;
int y0 = m01 / m00;
//计算零阶中心距
int mu00;
for(int row = 0; row < height; row++)
{for(int col = 0; col < width; col++){mu00 += pow( (col-x0) , 0) * pow( (row-y0) , 0) * f(x , y);}
}
//计算(p+q)阶归一化矩
int nupq;
for(int row = 0; row < height; row++)
{for(int col = 0; col < width; col++){mupq += pow( (col-x0) , p) * pow( (row-y0) , q) * f(x , y);}
}
nupq = mupq / pow(mu00, (p+q)/2 );

通过归一化抵消了图像尺度变化带来的影响,所以归一化矩不仅具有空间不变性,而且具有尺度不变性。对于只进行平移或缩放的图像或者轮廓,可以使用归一化矩来表示它的特征。

但是当图像或轮廓存在旋转角度时,上述三种矩都会随图像的旋转而发生变化,所以就需要今天整理记录的主角——Hu矩来解决这个问题。
Hu矩其实是一个包含了七个不变矩的集合,这七个不变矩分别由多个二、三阶中心距组合计算而成,至于公式这里就不做记录了,毕竟网上一查就能查得到的(主要是我懒得记。。。真的太长了。。。),根据公式,我们只要计算出所需要的二、三阶中心距,就可以将这七个不变矩分别计算出来,最终得到所需要的Hu矩。Hu矩就可以比较完善的表示一幅图像或者轮廓的特征,因为其具有空间不变性、尺度不变性和旋转不变性这三大特点,当图像或轮廓发生平移、缩放、旋转时Hu矩都不会因此而发生改变。

因此,可以使用Hu矩来进行轮廓匹配。主要代码如下:

 Mat tem, src;tem = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\a.png");src = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\abc.png");Mat tem_gaus, src_gaus;GaussianBlur(tem, tem_gaus, Size(), 1, 1, 4);GaussianBlur(src, src_gaus, Size(), 1, 1, 4);Mat tem_gray, src_gray;cvtColor(tem_gaus, tem_gray, COLOR_BGR2GRAY);cvtColor(src_gaus, src_gray, COLOR_BGR2GRAY);Mat tem_binary, src_binary;threshold(tem_gray, tem_binary, 0, 255, THRESH_BINARY | THRESH_OTSU);threshold(src_gray, src_binary, 0, 255, THRESH_BINARY | THRESH_OTSU);//寻找两幅图像中的轮廓;模板图像只返回最外层轮廓,被匹配图像返回所有轮廓vector<vector<Point>> contours_tem, contours_src;vector<Vec4i> hierarchy_tem, hierarchy_src;findContours(tem_binary, contours_tem, hierarchy_tem, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);findContours(src_binary, contours_src, hierarchy_src, RETR_TREE, CHAIN_APPROX_SIMPLE);//计算模板图像的轮廓的几何矩Moments tem_moment;tem_moment = moments(contours_tem[0]);//计算模板图像的轮廓的Hu矩Mat tem_hu_moment;                   HuMoments(tem_moment, tem_hu_moment);           //通过几何矩计算Hu矩,输出的Hu矩为7个double类型的值//遍历被匹配图像中的轮廓for (int i = 0; i < contours_src.size(); i++){//计算被匹配图像的轮廓的几何矩和Hu矩Moments src_moment;src_moment = moments(contours_src[i]);Mat src_hu_moment;HuMoments(src_moment, src_hu_moment);//计算两个Hu矩之间的差异值double different;different = matchShapes(tem_hu_moment, src_hu_moment, CONTOURS_MATCH_I1, 0);cout << "Humoment  different:  " << different << endl;if (different < 1){drawContours(src, contours_src, i, Scalar(0, 0, 255), 2, 8);}}imshow("src", src);imshow("tem", tem);

首先读取一张模板图像和一张匹配图像,通过预处理后将这两幅图像都转变为二值图像,然后分别进行轮廓提取。注意对于模板图像我们只需要它的外层轮廓,而匹配图像则需要找到它的全部轮廓。

然后对模板轮廓进行矩的计算,tem_moment = moments(contours_tem[0]),输出的是它的几何矩(mpq)、中心距(mupq)和归一化矩(nupq)。再进行Hu矩的计算,利用APIHuMoments(tem_moment, tem_hu_moment),其中第一个参数是上面计算矩后输出的moment对象,第二个参数就是输出的Hu矩,这里使用Mat类型定义Hu矩便于后续进行轮廓匹配,Hu矩中的每一行就是一个不变矩。
随后对匹配图像中的轮廓进行遍历,分别求每个轮廓的Hu矩,再通过different = matchShapes(tem_hu_moment, src_hu_moment, CONTOURS_MATCH_I1, 0)来计算每个轮廓的Hu矩和模板轮廓Hu矩的差异值。

matchShapes()这个API是用来对两个轮廓进行比较差异的,其返回值是一个double类型的差异值,其主要参数如下:
前两个参数contour1、contour2:输入的需要匹配的两个轮廓或者两个轮廓的Hu矩,不推荐直接传入轮廓,因为轮廓受其他因素影响变化大;而Hu矩具有空间不变性、尺度不变性和旋转不变性,以Hu矩作为参数传入可以使得计算结果较为稳定,也比直接传入轮廓计算得到的结果更准确;

第三个参数:可选的比较方法,一般使用CONTOURS_MATCH_I;

第四个参数:OpenCV3.X版本后已经被废弃,直接输入0即可。

最后对差异值进行阈值判断,由于当两个轮廓相似时,其Hu矩之间的差异会很小,所以差异值的阈值一般取0~1,如果该轮廓和模板轮廓的Hu矩之间差异值小于阈值,就将该轮廓在匹配图像中绘制出来。

至此,我们就通过两个轮廓Hu矩的比较,实现了在图像中寻找匹配目标的效果。其匹配效果如下:

从上图可见,当我把原图中的 “A” 扣出来后又将宽和高缩小到了原来的二分之一,并且旋转了180°后再进行基于Hu矩的轮廓匹配,结果仍能够准确得在原图像中找到 “A” 这个轮廓。通过所有轮廓的不同差异值可见,只有一个是处于0~1之间的,这个差异值也就是匹配到的目标轮廓和模板轮廓的Hu矩的差异值。

好的,本次笔记到此结束,谢谢阅读~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

OpenCV4学习笔记(23)——几何矩、中心矩、归一化矩和Hu矩的计算,以及基于Hu矩的轮廓匹配相关推荐

  1. OpenCV4学习笔记(47)——BRISK特征提取描述算法

    今天要整理记录的是OpenCV中BRISK特征提取描述算法的运用. BRISK特征提取描述算法全称为 Binary Robust Invariant Scalable Keypoints(二进制鲁棒不 ...

  2. StatQuest学习笔记23——RNA-seq简介

    StatQuest学习笔记23--RNA-seq简介 前言--主要内容 这篇笔记是StatQuest系列笔记的第58节,主要内容是讲RNA-seq的原理.StatQuest系列教程的58到62节是协录 ...

  3. OPENCV-4 学习笔记

    OPENCV-4 学习笔记 ROI-设定感兴趣的区域(region of interest) 定义: Mat imageROI; //方法一:通过Rect指定矩形区域 imageROI=image(R ...

  4. 深度学习笔记(12) Batch归一化网络

    深度学习笔记(12) Batch归一化网络 1. Batch归一化 2. 激活值归一化 3. 特殊情况不归一化 4. mini-batch上的Batch归一化 1. Batch归一化 Batch归一化 ...

  5. OpenCV4学习笔记(57)——基于GrabCut图像分割算法实现背景替换与背景虚化效果

    在上一篇笔记<OpenCV4学习笔记(56)>中,整理了关于在OpenCV中使用GrabCut图像分割算法的相关内容,那么本次笔记就以GrabCut算法为基础来实现对图像的背景替换和背景虚 ...

  6. 区块链学习笔记23——ETH反思

    区块链学习笔记23--ETH反思 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 智能合约真的智能吗 ...

  7. OpenCV4学习笔记(55)——基于KNN最近邻算法实现鼠标手写数字识别

    在上一篇博客<OpenCV4学习笔记(54)>中,整理了关于KNN最近邻算法的一些相关内容和一个手写体数字识别的例子.但是上次所实现的手写体数字识别,每次只能固定地输入测试图像进行预测,而 ...

  8. OpenCV4学习笔记(76)——基于ArUco模块+QT实现增强现实(AR)

    在<OpenCV4学习笔记(75)>中,整理记录了对于一副静态图像如何实现一个简单的增强现实效果,今天我们就结合ArUco模块和QT来实现对于实时视频流的AR效果. 我们需要先创建一个QT ...

  9. MIPS汇编语言学习笔记23:if 语句分支指令

    C语言 #include<stdio.h> int main() {int i = 3;if (i < 5){printf("yes!\n");}else{pri ...

  10. OpenCV4学习笔记(41)——ORB特征提取描述算法

    今天要整理记录的笔记内容是特征算法中比较常用的一种--ORB特征提取描述算法,顾名思义,ORB算法包含了对特征点的提取和描述这两个部分.而在上次的博文<OpenCV4学习笔记(39)>中, ...

最新文章

  1. 学习J2ME编程需要掌握的七种技术
  2. SpringBoot注解自动扫描-底层实现
  3. dag见证服务器是虚拟ip吗,exchange后端和DAG搭建.docx
  4. cad画流程图的插件_流程图控件FlowChart.NET使用教程:安排组件的使用
  5. php实现解压功能的函数
  6. codeforces 653D D. Delivery Bears(二分+网络流)
  7. 解决css冲突的问题
  8. IIS发布网站遇到的异常
  9. 彻底理解文本主题模型LDA(极致原理讲解+实战)
  10. nginx linux脚本,控制 nginx shell脚本语言
  11. 二维码生成以及扫一扫解析二维码原理
  12. 【PageHelper分页】实现拦截pageNum和pageSize
  13. YOLOv5目标检测算法——通俗易懂的解析
  14. 从零实现深度学习框架——前馈网络语言模型
  15. Xshell下载文件到本地
  16. html5创建三次贝塞尔曲线,HTML5 Canvas中使用路径描画二阶、三阶贝塞尔曲线
  17. Android2.1源码目录结构
  18. [转]计算机编程语言的入门学习
  19. 飞塔防火墙固定IP改动态IP网络设置
  20. Leetcode 592. 分数加减运算 C++

热门文章

  1. C++坦克大战(新手)
  2. 基于Arduino的音乐动感节奏灯
  3. 2014蓝桥杯B组初赛试题《啤酒和饮料》
  4. myCobot pro 机械臂(5)Robotics Toolbox for MATLA(开发环境:matlab)
  5. 滑头杨百万的股经:六分心态三分技巧一分运气
  6. 2020身高体重标准表儿童_2020儿童身高体重标准表
  7. 北航华科计算机,2021年北航,同济,北理,南开,华科大和中坚九校的此起彼伏...
  8. 【我的OpenGL学习进阶之旅】C++如何加载TGA文件?
  9. 盘点三种卫星图分幅导出的方法
  10. 【程序员入门记录】ThinkPad E470改造记录——系统改造