图像的矩

r囧r小猫 2022-03-21 02:11 435阅读 0赞

原理

  • 图像的矩是对图像特征描述的一个量,大小为 M × N M \times N M×N 图像可以看成二维的随机变量 ( x , y ) (x,y) (x,y)

从而定义二维的 ( p + q ) (p+q) (p+q)的空间矩为: m p q = ∑ 0 M − 1 ∑ 0 N − 1 x p y q f ( x , y ) 其 中 p , q 是 0 , 1 , 2… 整 数 m_{pq}=\sum_{0}^{M-1} \sum_{0}^{N-1} {x^py^qf(x,y)} \space \space \space \space \space \space \space \space \space \space 其中p,q是0,1,2…整数 mpq​=0∑M−1​0∑N−1​xpyqf(x,y) 其中p,q是0,1,2…整数

m 00 = ∑ 0 M − 1 ∑ 0 N − 1 f ( x , y ) 相 当 于 总 质 量 m_{00}=\sum_{0}^{M-1} \sum_{0}^{N-1} {f(x,y)}\space\space 相当于总质量 m00​=0∑M−1​0∑N−1​f(x,y) 相当于总质量
m 01 = ∑ 0 M − 1 ∑ 0 N − 1 y f ( x , y ) , m 10 = ∑ 0 M − 1 ∑ 0 N − 1 x f ( x , y ) , x ˉ = m 10 m 00 y ˉ = m 01 m 00 ( x ˉ , y ˉ ) 相 当 于 质 心 m_{01}=\sum_{0}^{M-1} \sum_{0}^{N-1} y{f(x,y)},\space \space m_{10}=\sum_{0}^{M-1} \sum_{0}^{N-1} x{f(x,y)},\bar{x}=\frac{m_{10}}{m{_{00}}}\space \space \space \space \bar{y}=\frac{m_{01}}{m{_{00}}} \space \space \space \space(\bar{x},\bar{y})相当于质心 m01​=0∑M−1​0∑N−1​yf(x,y), m10​=0∑M−1​0∑N−1​xf(x,y),xˉ=m00​m10​​ yˉ​=m00​m01​​ (xˉ,yˉ​)相当于质心

相应的 ( p + q ) (p+q) (p+q)阶中心矩: μ p q = ∑ 0 M − 1 ∑ 0 N − 1 ( x − x ˉ ) p ( y − y ˉ ) q f ( x , y ) 其 中 p , q 是 0 , 1 , 2… 整 数 \mu_{pq}=\sum_{0}^{M-1} \sum_{0}^{N-1}{(x-\bar{x})^p(y-\bar{y})^q}f(x,y) \space \space \space \space \space \space \space \space \space \space 其中p,q是0,1,2…整数 μpq​=0∑M−1​0∑N−1​(x−xˉ)p(y−yˉ​)qf(x,y) 其中p,q是0,1,2…整数 x ˉ = m 10 m 00 y ˉ = m 01 m 00 ( x ˉ , y ˉ ) 相 当 于 质 心 \bar{x}=\frac{m_{10}}{m{_{00}}}\space \space \space \space \bar{y}=\frac{m_{01}}{m{_{00}}} \space \space \space \space \space \space(\bar{x},\bar{y})相当于质心 xˉ=m00​m10​​ yˉ​=m00​m01​​ (xˉ,yˉ​)相当于质心

归一化的中心矩 η p q \eta_{pq} ηpq​ : η p q = μ p q μ 00 γ 其 中 γ = p + q 2 + 1 , p + q = 2 , 3… \eta_{pq}=\frac{\mu_{pq}}{\mu_{00}^{\gamma}} \space \space \space \space \space \space \space 其中 \space \gamma=\frac{p+q}{2}+1,\space \space\space \space p+q=2,3… ηpq​=μ00γ​μpq​​ 其中 γ=2p+q​+1, p+q=2,3…

由二阶矩和三阶矩推出以下7个不变矩组(Hu不变矩):

在这里插入图片描述
Hu不变矩对平移、尺度变化、镜像( h u [ 6 ] hu[6] hu[6]符号改变)、和旋转是不变的,可以作为很好的图像的特征。


代码

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. Mat src_gray;
  6. int thresh = 100;
  7. RNG rng(12345);
  8. void thresh_callback(int, void*);
  9. int main(void)
  10. {
  11. Mat src = imread("../res/test.png");
  12. if(src.empty())
  13. {
  14. cout << "can't load the image" << endl;
  15. return -1;
  16. }
  17. cv::cvtColor(src, src_gray, cv::COLOR_RGB2GRAY);
  18. blur(src_gray, src_gray, Size(3,3));
  19. const char* sourec_window = "Source";
  20. namedWindow(sourec_window);
  21. imshow(sourec_window, src);
  22. const int MAX_THRESH = 255;
  23. createTrackbar("Canny thresh:", sourec_window, &thresh, MAX_THRESH, thresh_callback);
  24. thresh_callback(0,0);
  25. waitKey();
  26. return 0;
  27. };
  28. void thresh_callback(int, void*)
  29. {
  30. Mat canny_output;
  31. Canny(src_gray, canny_output, thresh, thresh*2, 3);
  32. vector<vector<Point>> contours;
  33. cv::findContours(canny_output, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
  34. vector<Moments> mu(contours.size());
  35. for(size_t i=0; i<contours.size(); i++)
  36. {
  37. mu[i] = cv::moments(contours[i]); //得到每个轮廓的moments ,记录在mu中
  38. }
  39. vector<Point2f> mc(contours.size()); //每个轮廓的质心
  40. for(size_t i=0; i<contours.size(); i++)
  41. {
  42. mc[i] = Point2f(
  43. static_cast<float>(mu[i].m10 / (mu[i].m00 + 1e-5)),
  44. static_cast<float>(mu[i].m01 / (mu[i].m00 + 1e-5))
  45. ); //利用空间矩来计算质心
  46. cout << "mc[" << i << "]=" << mc[i] << endl;
  47. }
  48. Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
  49. for(size_t i=0; i<contours.size(); i++)
  50. {
  51. Scalar color = Scalar(rng.uniform(0,256), rng.uniform(0,256), rng.uniform(0,256));
  52. cv::drawContours(drawing, contours, (int)i, color, 2); //画出轮廓
  53. cv::circle(drawing, mc[i], 4,color,-1);//画出质心
  54. }
  55. imshow("contours", drawing);
  56. for(size_t i=0; i<contours.size(); i++) //打印出由cv::contourArea 和cv::arcLength 计算出来的轮廓面积和周长
  57. {
  58. cout << "contours[" << i << "]" << "Area(M_00): " << std::fixed << std::setprecision(2) << mu[i].m00 << " "
  59. << "Area OpenCV API: " << cv::contourArea(contours[i]) << " "<< "length: " << arcLength(contours[i],true)
  60. << endl;
  61. }
  62. }

结果:在中间的部分,质心基本正确,靠近边界的轮廓,质心明显往反向移,这是因为canny处理之后的图,靠近边界的只剩下一弧线,检测轮廓的时候findContours出错,将紧贴弧的周围一个像素组成的圈当成轮廓(类似于下图),所以结果出错。

20190130165133128.png20190129221700963.png

质心:
在这里插入图片描述

面积、长度:
在这里插入图片描述分析:cv::moments函数,传入的是一组点,则算出的M00其实就是面积,和OpenCV API算出来的一样


OpenCV API

  1. 计算图像的矩(最高到三阶)

Moments cv::moments

  1. ( InputArray array, //single-channel, 8-bit or floating-point 2D array 或者 an array ( 1×N or N×1 ) of 2D points (Point or Point2f ),图像的话,以像素为基础,比如计算M00:所有像素之和; 点组成的多边形的话,计算M00等于面积,所以上面例子,M00和OpenCV api算出来一样面积结果
  2. bool binaryImage = false // 如果是true,非0图像像素即使1,此参数只针对图像而言
  3. )

返回 m o m e n t s moments moments 的成员变量:
空间矩:m00、m01、 m02、m03、 m10、m11 、m12、m20、m21、m30
中心矩:mu02、mu03、mu11、mu12、mu20、mu21、mu30
归一化中心距:nu02、nu03、nu11、nu12、nu20、nu21、nu30

  1. 通过上面得到的moments 得到hu矩

void cv::HuMoments

  1. ( const Moments & moments, //由上面函数得到
  2. double hu[7] //得到7个不变矩
  3. )
  1. 计算轮廓面积

double cv::contourArea

  1. ( InputArray contour, // 一组点(不是图像),可以存在vector<Point>或者Mat
  2. bool oriented = false // false:返回是面积绝对值,true: 返回面积有正负,这样通过正负来判断轮廓点的方向(顺时针、逆时针)
  3. )
  1. 计算轮廓周长、弧的长度

double cv::arcLength

  1. ( InputArray curve, // 一组点(不是图像),可以存在vector<Point>或者Mat
  2. bool closed // 点之间是否是closed
  3. )
  4. (已经验证)比如: 三个点(0,0) ,(10,0),(10,10); closed:true 结果是34.1421closed:false结果是20

实例

  1. vector<Point> contour; //创建一个由四个点组成的轮廓
  2. contour.push_back(Point(0,0));
  3. contour.push_back(Point(10,0));
  4. contour.push_back(Point(10,10));
  5. contour.push_back(Point(5,4));
  6. Mat src(2,2,CV_8UC1, 10); //创建一个2*2 的图像
  7. double area0 = contourArea(contour); //计算四个点组成轮廓的面积
  8. cv::Moments moment1 = cv::moments(contour);// 计算轮廓的矩
  9. vector<Point> approx;
  10. approxPolyDP(contour, approx, 5, true); //由轮廓近似得到 多边形 approx
  11. cv::Moments moment2 = cv::moments(approx); // 计算 approx的矩
  12. double area1 = contourArea(approx); // 计算 approx的面积
  13. cv::Moments moments3 = cv::moments(src); //计算Mat src的矩
  14. cout << "Mat src M00: " << moments3.m00 << endl; //输出src矩的m00 ,相当于所有像素之和
  15. cout << endl;
  16. cout << "contour M00: " << moment1.m00 << endl; //contour的矩m00,
  17. cout << "approx M00: " << moment2.m00 << endl; //approx的矩m00,
  18. cout << endl;
  19. cout << "area of contourArea(contour): "<< area0 << endl; //ontourArea(contour)计算由四个点组成的面积
  20. cout << "area of contourArea(approx): "<< area1 << endl; //contourArea(approx)计算由三个点(由轮廓近似而得到)组成面积
  21. cout << "approx poly vertices: " << approx.size() << endl; //由轮廓contour四个点近似得到多边形approx的多少个点

在这里插入图片描述

分析: 可以看出,当cv::moments()传入的参数不同,结果不同,如果传入是:

  1. Mat src:二维图像,单通道8bit,则空间矩M00计算的是所有像素之和,
  2. contour 或者是approx:由点组成的多边形,M00计算的是由点组成的面积,且和contourArea函数计算结果一样

发表评论

表情:
评论列表 (有 0 条评论,435人围观)

还没有评论,来说两句吧...

相关阅读

    相关 图像

    图像的矩 零阶矩: M00 这里的图像是单通道图像, 表示图像在 点上的灰度值。 我们可以发现,当图像为二值图时, 就是这个图像上白色区域的总和,因此, 可以用来

    相关 图像不变性特征—hu

    图像的hu矩是一种具有平移、旋转和尺度不变性的图像特征。 普通矩的计算: f(x,y)的p+q阶原点矩可以表示为: ![这里写图片描述][aHR0cDovL2ltZy

    相关 图像

    原理 图像的矩是对图像特征描述的一个量,大小为 M × N M \\times N M×N 图像可以看成二维的随机变量 ( x , y ) (x,y) (x,y)