轮廓矩


原理部分:

一、概率论上的定义

看到矩这个字,很容易联想到概率论,在概率论中,定义如下:

或者说:

设 X 和 Y 是随机变量,c 为常数,k 为正整数, 
如果E(|X−c|^k)E(|X−c|^k)存在,则称E(|X−c|^k)E(|X−c|^k)为 X 关于点 c 的 k 阶矩。

  • c = 0 时, 称为 k 阶原点矩;
  • c = E(x) 时,称为 k 阶中心矩。

如果E(|X−c1|^p⋅|Y−c2|^q)存在,则称其为 X,Y 关于 c 点 p+q 阶矩。

其余基本概率论知识可参考:https://www.cnblogs.com/wyuzl/p/7845948.html

 二、在图像学上的定义

一幅M×N的数字图像f(i,j),其p+q阶几何矩和中心矩为:

其中:

  • f(i,j)为图像在坐标点(i,j)处的灰度值。
  • 重心:,也是图像的一阶矩

三、几何矩的基本意义

(1)零阶矩

可以发现,当图像为二值图时,就是这个图像上白色区域的总和,因此,可以用来求二值图像(轮廓,连通域)的面积。

(2)一阶矩

当图像为二值图时,就是白色像素关于x坐标的累加和,而则是y坐标的累加和

由此,可获得图像的重心:,也就是前文提到的。

(3)二阶矩

二阶矩可以用来求物体形状的方向。

后续会有介绍,具体可参考:https://blog.csdn.net/qq826309057/article/details/70039397

四、由几何矩可表示出中心距如下:

摘自:https://blog.csdn.net/keith_bb/article/details/70197104

为了消除图像比例变化带来的影响,定义规格化中心矩如下:

利用二阶和三阶规格中心矩可以导出下面7个不变矩组(Φ1 Φ7),它们在图像平移、旋转和比例变化时保持不变


Opencv应用部分

核心函数:

(1)求矩

Moments moments(inputArray array, bool binaryImage=false) 
  • 输入参数,可以是光栅图像(单通道,8位或浮点的二维数组)或二维数组(1N或N1)
  • 默认值false,若此参数取true,则所有非零像素为1.此参数仅对图像使用

(2)计算轮廓面积

double contourArea(inputArray contour, bool oriented=false) 
  • 输入的向量,二维点(轮廓顶点)
  • 面向区域标识符,若为true,该函数返回一个带符号的面积值,其正负取决于轮廓的方向(顺时针还是逆时针)。根据这个特性我们可以根据面积的符号来确定轮廓的位置。需要注意的是,这个参数有默认值false,表示以绝对值返回,不带符号。

(3) 计算轮廓长度

double arcLength(inputArray curve,bool closed) 
  • 输入的二维点集
  • 一个用于指示曲线是否封闭的标识符,默认值closed,表示曲线封闭

一、求图像的重心和方向

前面提到二阶矩可以用来求物体形状的方向。 

其中:

,


fastAtan2()为opencv的函数,输入向量,返回一个0-360的角度。

个人认为:关于fastAtan2()的推算如下:

当然这只是个人推导,有兴趣的同学可以验证一下。

另外,fastAtan2()返回的角度指自然坐标系下x轴正半轴按顺时针到图像轴的角,如下图的α角:

【注意:实际处理时,需把目标部分变成白色,背景为黑色。这里只是为了方便看才把目标变成黑色而已】

测试代码:

  1. #include<opencv2/imgproc/imgproc.hpp>
  2. #include<opencv2/highgui/highgui.hpp>
  3. #include<opencv2/features2d/features2d.hpp>
  4. #include<stdlib.h>
  5. #include<stdio.h>
  6. #include<iostream>
  7. using namespace std;
  8. using namespace cv;
  9. void main()
  10. {
  11. Mat srcImg;
  12. srcImg = imread("F:\\opencv_re_learn\\flash.jpg");
  13. if (!srcImg.data){
  14. cout<< "failed to read" << endl;
  15. system("pause");
  16. return;
  17. }
  18. Mat srcGray;
  19. cvtColor(srcImg, srcGray, CV_BGR2GRAY);
  20. Mat thresh;
  21. threshold(srcGray, thresh, 100, 255, CV_THRESH_BINARY_INV |
  22. CV_THRESH_OTSU);//二值化时主要要让目标部分是白色像素
  23. Moments m = moments(thresh, true);//moments()函数计算出三阶及一下的矩
  24. Point2d center(m.m10 / m.m00, m.m01 / m.m00);//此为重心
  25. //计算方向
  26. double a = m.m20 / m.m00 - center.x*center.x;
  27. double b = m.m11 / m.m00 - center.x*center.y;
  28. double c = m.m02 / m.m00 - center.y*center.y;
  29. double theta = fastAtan2(2 * b, (a - c)) / 2;//此为形状的方向
  30. cout << 2 * b << endl;
  31. cout << a - c << endl;
  32. cout <<"角度是:"<< theta << endl;
  33. //绘制重心
  34. circle(srcImg, center, 2, Scalar(0, 0, 255),2);
  35. imshow("src", srcImg);
  36. imshow("Gray", srcGray);
  37. imshow("Thresh", thresh);
  38. waitKey(0);
  39. }

二、轮廓匹配

轮廓匹配并没有直接用到Moments ,但是是基于轮廓矩的7个不变形矩进行比较的。

相关函数:

  1. double MatchShapes(const void* object1, const void* object2,
  2. int method, double parameter = 0);
  • 第一个参数是待匹配的物体1
  • 第二个是待匹配的物体2
  • 第三个参数method有三种输入:【即三种不同的判定物体相似的方法】
  1. CV_CONTOURS_MATCH_I1
  2. CV_CONTOURS_MATCH_I2
  3. CV_CONTOURS_MATCH_I3

测试代码:

  1. #include<opencv2/highgui/highgui.hpp>
  2. #include<opencv2/imgproc/imgproc.hpp>
  3. #include<opencv2/core/core.hpp>
  4. #include<iostream>
  5. using namespace std;
  6. using namespace cv;
  7. RNG rng(12345);
  8. Mat init_src(string filename)
  9. {
  10. Mat srcImg = imread(filename);
  11. if (!srcImg.data){
  12. cout << "filed to read" << endl;
  13. system("pause");
  14. return Mat::zeros(100, 100, CV_8UC1);
  15. }
  16. return srcImg;
  17. }
  18. vector<vector<Point>> find_contour(Mat thresh,string window_name)
  19. {
  20. Mat canny_output;
  21. vector<vector<Point>>contours;
  22. vector<Vec4i> hierarchy;
  23. medianBlur(thresh, thresh, 5);
  24. //canny边缘检测
  25. //如果图片的轮廓分明,建议可以不进行canny边缘检测,可直接对二值化图像进行findcontours
  26. //Canny(thresh, thresh, 10, 10 * 2, 3);
  27. //寻找轮廓
  28. findContours(thresh, contours, hierarchy,
  29. CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
  30. Mat drawing = Mat::zeros(thresh.size(), CV_8UC3);
  31. for (int i = 0; i < contours.size(); i++){
  32. //定义随机颜色
  33. Scalar color = Scalar(rng.uniform(0, 255),
  34. rng.uniform(0, 255), rng.uniform(0, 255));
  35. //绘制
  36. drawContours(drawing, contours, i, color,
  37. 2, 8, hierarchy, 0, Point());
  38. }
  39. //显示
  40. namedWindow(window_name, CV_WINDOW_AUTOSIZE);
  41. imshow(window_name, drawing);
  42. return contours;
  43. }
  44. void main()
  45. {
  46. //图片读取、预处理
  47. Mat srcImg1 = init_src("F:\\opencv_re_learn\\angle_test2.jpg");
  48. Mat srcImg2 = init_src("F:\\opencv_re_learn\\angle_test.jpg");
  49. Mat srcGray1, srcGray2;
  50. cvtColor(srcImg1, srcGray1, CV_BGR2GRAY);
  51. cvtColor(srcImg2, srcGray2, CV_BGR2GRAY);
  52. Mat thresh1, thresh2;
  53. threshold(srcGray1, thresh1, 100, 255, CV_THRESH_BINARY_INV |
  54. CV_THRESH_OTSU);
  55. threshold(srcGray2, thresh2, 100, 255, CV_THRESH_BINARY_INV |
  56. CV_THRESH_OTSU);
  57. //imshow("thresh1", thresh1);
  58. //imshow("thresh2", thresh2);
  59. //获取轮廓
  60. vector<vector<Point>> contours1 = find_contour(thresh1,"contour1");
  61. vector<vector<Point>> contours2 = find_contour(thresh2,"contour2");
  62. //轮廓比较
  63. cout << "first method to match, result:" <<
  64. matchShapes(contours1[0], contours2[0], CV_CONTOURS_MATCH_I1, 0) << endl;;
  65. cout << "second method to match, result:" <<
  66. matchShapes(contours1[0], contours2[0], CV_CONTOURS_MATCH_I2, 0) << endl;
  67. cout << "third method to match, result:" <<
  68. matchShapes(contours1[0], contours2[0], CV_CONTOURS_MATCH_I2, 0)<<endl;
  69. imshow("src1", srcImg1);
  70. imshow("src2", srcImg2);
  71. waitKey(0);
  72. }

应该只需修改图片的路径即可进行调试

1)读入两张相同的图片,三种方法匹配的结果都是0,表示完全匹配

2)还是读入上面的两张图片,但是其中一张经过旋转

结果:三种方法匹配的结果都很接近0,表示这两个轮廓还是基本匹配的

3)读入两张不同的图片

结果明显偏离0,但是可以说还是有一点相似性吧。。。



参考文章:

https://blog.csdn.net/qq_31531635/article/details/73692611

https://blog.csdn.net/qq826309057/article/details/70039397

https://blog.csdn.net/keith_bb/article/details/70197104

https://blog.csdn.net/mingzhentanwo/article/details/45155307

Opencv轮廓矩【判断形态方向、匹配度】相关推荐

  1. opencv轮廓及点在轮廓内判断

    查找轮廓 轮廓到底是什么?一个轮廓一般对应一系列的点,也就是图像中的一条曲线.表示的方法可能根据不同情况而有所不同.有多重方法可以表示曲线.在openCV中一般用序列来存储轮廓信息.序列中的每一个元素 ...

  2. 使用OpenCV计算图像的轮廓矩的代码

    图像处理开发需求.图像处理接私活挣零花钱,请加微信/QQ 2487872782 图像处理开发资料.图像处理技术交流请加QQ群,群号 271891601 轮廓矩用来干嘛?说实话,我是没有找到相关文章专门 ...

  3. OpenCV之imgproc 模块. 图像处理(5)在图像中寻找轮廓 计算物体的凸包 创建包围轮廓的矩形和圆形边界框 为轮廓创建可倾斜的边界框和椭圆 轮廓矩 多边形测试

    在图像中寻找轮廓 目标 在这个教程中你将学到如何: 使用OpenCV函数 findContours 使用OpenCV函数 drawContours 原理 例程 教程的代码在下面给出. 你也可以从 这里 ...

  4. OpenCV-Python实战(11)——OpenCV轮廓检测相关应用

    OpenCV-Python实战(11)--OpenCV轮廓检测相关应用 0. 前言 1. 轮廓绘制 2. 轮廓筛选 3. 轮廓识别 4. 轮廓匹配 小结 系列链接 0. 前言 在计算机视觉领域,轮廓通 ...

  5. Python,OpenCV轮廓属性、轮廓检测及绘制

    Python,OpenCV轮廓属性.轮廓检测及绘制 1. 效果图 2. 源码 2.1 轮廓属性 2.2 轮廓特征 参考 这篇博客将介绍OpenCV中的轮廓,轮廓的特征及属性(质心,面积,轮廓,近似轮廓 ...

  6. OpenCV-Python实战(10)——详解 OpenCV 轮廓检测

    OpenCV-Python实战(10)--详解 OpenCV 轮廓检测 0. 前言 1. 轮廓介绍 2. 轮廓检测 3. 轮廓压缩 4. 图像矩 4. 1 一些基于矩的对象特征 4.2 Hu 不变矩 ...

  7. 12月19日 OpenCV 实战基础学习笔记——特征匹配

    文章目录 前言 一.特征匹配 1.Brute-force 蛮力匹配 2.1 对 1 匹配 3.k 对最佳匹配 二.答题卡识别 前言 本文为12月19日 OpenCV 实战基础学习笔记--特征匹配,分为 ...

  8. OpenCV学习之六: 使用方向梯度直方图估计图像旋转角度

    OpenCV学习之六: 使用方向梯度直方图估计图像旋转角度 原文:http://blog.csdn.net/zhjm07054115/article/details/26964275 下面的代码通过计 ...

  9. 德国计算机课程匹配度,匹配度对于德国留学有多重要

    对于留学的申请来说,匹配都是一个比较重要的因素.那么为什么德国的留学这么看重匹配度?到出国留学网这里了解他的原因吧. 一.课程匹配度这么重要 大家在申请的时候可能第一个考虑的.就是语言或者是APS审核 ...

  10. 德国计算机课程匹配度,为什么德国大学就这么看重本科的课程匹配?

    hi~大家12月好. 不知不觉2019年已经进入最后一个月了,回忆一下这一年,大家过得怎么样? 在12月的第一个工作日,我要给大家掰一掰,这个让大家恨得深沉,累得头秃的大问题. 德国大学硕士申请为何如 ...

最新文章

  1. Python3学习笔记-数据类型和变量
  2. if语句的一种使用失误
  3. oracle常用函数归纳
  4. vba 提取 json某个值_Excel中提取不重复值的方法汇总(5种基础+VBA+1个自定义函数)...
  5. postgresql主从备份_基于windows平台的postgresql主从数据库流备份配置
  6. js php c语言for循环,小蚂蚁学习C语言(8)——C语言for循环
  7. java 祖父类_JAVA的XX.put中的put方法,是在哪个父类或祖父类中,详述父类链。
  8. 也谈zabbix性能优化
  9. EasyRecovery数据恢复软件100%恢复的成功率
  10. csgo服务器怎么显示cmd,CSGO服务器搭建教程——KZ模式
  11. 有各组方差怎么算组间平方和_方差分析:组间离差平方和组内离差平方的定义是什么?...
  12. android高仿ios键盘,iOS仿工商银行app自定义键盘
  13. SCI论文写作是用主动语态好还是被动语态好?
  14. arcgis pro发布矢量切片服务及利用arcgis api for javascript进行调用
  15. 千万别小瞧九宫格 一道题就能让候选人原形毕露!
  16. 【JMeter】Jmeter分布式压测教程
  17. 浅谈递归函数—C语言
  18. 月球公转与自转(一)
  19. 您的Android手机可以充当盖革计数器的双重职责
  20. Android禁用截屏

热门文章

  1. asp.net core 返回的Server Kestrel是什么
  2. 成为java高手的八大条件
  3. SQL注入攻击和防御
  4. factory工厂模式之工厂方法FactoryMethod
  5. [20180801]insert导致死锁.txt
  6. 机器学习重塑供应链管理的10个途径
  7. 51nod 1060 最复杂的数
  8. 空间索引 - 各数据库空间索引使用报告
  9. 深入学习之mysql(二)表的操作
  10. linux 复制文件时,报cp: omitting directory `XXX'