OpenCV 笔记(15):轮廓的矩特征

305 阅读3分钟

矩(moment) 是概率与统计中的一个概念,是随机变量的一种数字特征,是对变量分布和形态特点的一组度量。

矩的定义如下:

mn = xnf(x)dxm_n = \textstyle \int_{-\infty}^{\infty} x^nf(x) dx

其中,f(x) 是随机变量的概率密度函数或概率质量函数,n 是正整数表示矩的阶数。

矩可以用来描述变量的均值、方差、偏度、峰度等特性。矩的性质如下:

  • 零阶矩是变量的均值。
  • 一阶矩是变量的均方根。
  • 二阶矩是变量的方差。
  • 三阶矩是变量的偏度。
  • 四阶矩是变量的峰度。

在图像处理中,图像的矩是指图像的某些特定像素灰度的加权平均值,或者是图像具有类似功能或意义的属性。图像矩可以表示图像的一些特征,通过这些特征便于对图像进行识别。

1. 空间矩/几何矩

空间矩是图像矩特征中最基本的一种,它只考虑图像的像素值

mpq = x,y f(x,y)(xp+yq)m_{pq} = \displaystyle \sum_{x,y} f(x,y)(x^p+y^q)

其中,f(x,y) 表示输入的图像,p、q 是正整数。

1.1 零阶矩

m00 = x,y f(x,y)m_{00} = \displaystyle \sum_{x,y} f(x,y)

零阶矩可以用来描述图像的面积。

1.2 一阶矩

m10 = x,y xf(x,y)m_{10} = \displaystyle \sum_{x,y} xf(x,y)

m01 = x,y yf(x,y)m_{01} = \displaystyle \sum_{x,y} yf(x,y)

一阶矩可以用来描述图像的质心。

1.3 二阶矩

m20 = x,y x2f(x,y)m_{20} = \displaystyle \sum_{x,y} x^2f(x,y)

m02 = x,y y2f(x,y)m_{02} = \displaystyle \sum_{x,y} y^2f(x,y)

m11 = x,y xyf(x,y)m_{11} = \displaystyle \sum_{x,y} xyf(x,y)

二阶矩可以用来描述图像的周长、长轴、短轴、扭矩等信息。

1.4 三阶矩

m30 = x,yx3f(x,y)m_{30} = \displaystyle \sum_{x,y}x^3f(x,y)

m03 = x,yy3f(x,y)m_{03} = \displaystyle \sum_{x,y}y^3f(x,y)

m21 = x,yx2yf(x,y)m_{21} = \displaystyle \sum_{x,y}x^2yf(x,y)

m12 = x,yxy2f(x,y)m_{12} = \displaystyle \sum_{x,y}xy^2f(x,y)

下面的代码找到图像中的有效轮廓后,通过 moments() 函数计算轮廓的空间矩。

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace std;
using namespace cv;

bool ascendSort(vector<Point> a,vector<Point> b)
{
    return contourArea(a) > contourArea(b);
}

int main(int argc, char **argv) {
    Mat src = imread(".../test.jpg");
    imshow("src", src);

    Mat gray,thresh;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);

    threshold(gray,thresh,0,255,THRESH_BINARY_INV | THRESH_OTSU);
    imshow("thresh", thresh);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(thresh, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    sort(contours.begin(), contours.end(), ascendSort);//ascending sort

    for (size_t i = 0; i< contours.size(); i++) {
        double area = contourArea(contours[i]);

        if (area < 11000) {
            continue;
        }

        RotatedRect rrt = minAreaRect(contours[i]);

        Point2f pt[4];
        rrt.points(pt);
        line(src, pt[0], pt[1], Scalar(0, 0, 255), 8, 8);
        line(src, pt[1], pt[2], Scalar(0, 0, 255), 8, 8);
        line(src, pt[2], pt[3], Scalar(0, 0, 255), 8, 8);
        line(src, pt[3], pt[0], Scalar(0, 0, 255), 8, 8);

        Moments m = cv::moments(contours[i]);

        printf("m.m00 = %f, m.m10 = %f, m.m01 = %f,m.m20 = %f, m.m11 = %f, m.m02 = %f,m.m30 = %f, m.m21 = %f, m.m12 = %f, m.m03 = %f \n", m.m00, m.m10, m.m01, m.m20, m.m11, m.m02,m.m30,m.m21,m.m12,m.m03);

    }
    imshow("result", src);

    waitKey(0);
    return 0;
}

查找轮廓.png

调用 moments() 函数会返回 Moments 对象,包含了空间矩以及中心矩和归一化中心矩。

class CV_EXPORTS_W_MAP Moments
{
public:
    //! the default constructor
    Moments();
    //! the full constructor
    Moments(double m00, double m10, double m01, double m20, double m11,
            double m02, double m30, double m21, double m12, double m03 );
    ////! the conversion from CvMoments
    //Moments( const CvMoments& moments );
    ////! the conversion to CvMoments
    //operator CvMoments() const;

    //! @name spatial moments
    //! @{
    CV_PROP_RW double  m00, m10, m01, m20, m11, m02, m30, m21, m12, m03;
    //! @}

    //! @name central moments
    //! @{
    CV_PROP_RW double  mu20, mu11, mu02, mu30, mu21, mu12, mu03;
    //! @}

    //! @name central normalized moments
    //! @{
    CV_PROP_RW double  nu20, nu11, nu02, nu30, nu21, nu12, nu03;
    //! @}
};

空间矩的优点如下:

  • 计算简单
  • 易于实现。
  • 能够描述图像的形状、大小、位置等信息。

空间矩的缺点,也很明显:

  • 不具有尺度不变性和旋转不变性。
  • 对噪声敏感。

尺度不变性是指图像中的物体尺寸变化不应该影响计算机视觉算法的性能。旋转不变性是指图像中的物体旋转后,计算机视觉算法仍能正确识别或检测该物体。

2. 中心矩

中心矩考虑了图像的像素值和质心位置。

μpq = x,y(xx)p(yy)qf(x,y)\mu_{pq} = \displaystyle \sum_{x,y}(x-\overline{x})^p(y-\overline{y})^qf(x,y)

其中,f(x,y) 表示输入的图像,x\overline{x}y\overline{y} 是图像的质心,p、q 是正整数。

质心可以通过下面的公式进行计算: x = m10m00\overline{x} = \frac{m_{10}}{m_{00}}y = m01m00\overline{y} = \frac{m_{01}}{m_{00}}

2.1 零阶矩

μ00 = x,yf(x,y)\mu_{00} = \displaystyle \sum_{x,y}f(x,y)

中心矩的零阶矩与空间矩的零阶矩相同,可以用来描述图像的面积。

2.2 一阶矩

μ10 = x,y(xx)f(x,y)\mu_{10} = \displaystyle \sum_{x,y}(x-\overline{x})f(x,y)

μ01 = x,y(yy)f(x,y)\mu_{01} = \displaystyle \sum_{x,y}(y-\overline{y})f(x,y)

中心矩的一阶矩可以用来描述图像的质心位置的偏移。μ10\mu_{10} 表示图像质心在 x 方向的偏移量,μ01\mu_{01} 表示图像质心在 y 方向的偏移量。

2.3 二阶矩

μ20 = x,y(xx)2f(x,y)\mu_{20} = \displaystyle \sum_{x,y}(x-\overline{x})^2f(x,y)

μ02 = x,y(yy)2f(x,y)\mu_{02} = \displaystyle \sum_{x,y}(y-\overline{y})^2f(x,y)

μ11 = x,y(xx)(yy)f(x,y)\mu_{11} = \displaystyle \sum_{x,y}(x -\overline{x})(y-\overline{y})f(x,y)

中心矩的二阶矩表示图像的形状和大小。

二阶矩的物理意义如下:

μ20\mu_{20} 表示图像的长轴方向的方差。

μ02\mu_{02} 表示图像的短轴方向的方差。

μ11\mu_{11} 表示图像的主轴方向的偏心度。

2.4 三阶矩

μ30 = x,y(xx)3f(x,y)\mu_{30} = \displaystyle \sum_{x,y}(x-\overline{x})^3f(x,y)

μ03 = x,y(yy)3f(x,y)\mu_{03} = \displaystyle \sum_{x,y}(y-\overline{y})^3f(x,y)

μ21 = x,y(xx)2(yy)f(x,y)\mu_{21} = \displaystyle \sum_{x,y}(x-\overline{x})^2(y-\overline{y})f(x,y)

μ12 = x,y(xx)(yy)2f(x,y)\mu_{12} = \displaystyle \sum_{x,y}(x-\overline{x})(y-\overline{y})^2f(x,y)

三阶矩的物理意义如下:

μ30\mu_{30} 表示图像的偏度,反映了图像的对称性。

μ03\mu_{03} 表示图像的峰度,反映了图像的尖锐程度。

μ21\mu_{21} 表示图像的矩形程度,反映了图像的旋转不变性。

μ12\mu_{12} 表示图像的椭圆程度,反映了图像的旋转不变性。

2.5 归一化中心矩

中心矩实现了平移不变性,但是仍然不具有尺度不变性和旋转不变性。

为了使目标区域不受缩放造成的尺度变化带来的影响也就是实现**尺度不变性,**使用μ00\mu_{00}对各阶中心距进行归一化处理:

ηpq = μpqμ00p+q+22\eta_{pq} = \frac{\mu_{pq}}{\mu_{00}^{\frac{p+q+2}{2}}},其中 p+q = 2,3,4,...

可得归一化中心距

3. Hu 矩

Hu 矩是从中心矩推导出来的,图像在旋转、缩放、平移等操作后,Hu 矩仍能保持矩的不变性,实现了平移不变性、尺度不变性旋转不变性。

hu[0]= η20 + η02hu[0]= \eta_{20} + \eta_{02}

hu[1]=(η20  η02)2+4η112hu[1] =(\eta_{20} - \eta_{02})^2+4\eta_{11}^2

hu[2]=(η303η12)2+(3η21η03)2hu[2] = (\eta_{30} - 3\eta_{12})^2 +( 3\eta_{21}-\eta_{03})^2

hu[3]=(η30+ η12)2+(η21+η03)2hu[3] = (\eta_{30} + \eta_{12})^2 + ( \eta_{21}+\eta_{03})^2

hu[4]=(η30 3η12)(η30+η12)[(η30+η12)23(η21+η03)2]+(3η21η03)[3(η30+η12)2(η21+η03)2]hu[4] = (\eta_{30} - 3\eta_{12}) (\eta_{30}+\eta_{12})[(\eta_{30}+\eta_{12})^2-3(\eta_{21}+\eta_{03})^2]+(3\eta_{21}-\eta_{03})[3(\eta_{30}+\eta_{12})^2-(\eta_{21}+\eta_{03})^2]

hu[5]=(η20η02)[(η30+η12)2(η21+η03)2+4η11(η30+η12)(η21+η03)]hu[5] = (\eta_{20} - \eta_{02}) [(\eta_{30}+\eta_{12})^2-(\eta_{21}+\eta_{03})^2+4\eta_{11}(\eta_{30}+\eta_{12})(\eta_{21}+\eta_{03})]

hu[6]=(3η21η03)(η30+η12)[3(η30+η12)2(η21+η03)2]+(η30 3η12)(η21+η03)[3(η30+η12)2(η21+η03)2]hu[6] = (3\eta_{21}-\eta_{03})(\eta_{30}+\eta_{12})[3(\eta_{30}+\eta_{12})^2-(\eta_{21}+\eta_{03})^2]+(\eta_{30} - 3\eta_{12}) (\eta_{21}+\eta_{03})[3(\eta_{30}+\eta_{12})^2-(\eta_{21}+\eta_{03})^2]

Hu矩的特性如下:

  • 具有尺度不变性和旋转不变性。
  • 对噪声鲁棒性较好。
  • 可以描述图像的形状、大小、位置等信息。

OpenCV 提供了 HuMoments() 函数计算 Hu 矩。

下面的代码,通过 sample 图匹配原图中的物体。

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace std;
using namespace cv;

bool ascendSort(vector<Point> a,vector<Point> b)
{
    return contourArea(a) > contourArea(b);
}

int main(int argc, char **argv) {
    Mat src = imread(".../test.jpg");
    imshow("src", src);

    Mat gray,thresh;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);

    threshold(gray,thresh,0,255,THRESH_BINARY_INV | THRESH_OTSU);
    imshow("thresh", thresh);

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(thresh, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    sort(contours.begin(), contours.end(), ascendSort);//ascending sort

    Mat sample = imread(".../sample.jpg");
    imshow("sample", sample);

    Mat gray2,thresh2;
    cvtColor(sample, gray2, cv::COLOR_BGR2GRAY);
    threshold(gray2,thresh2,0,255,THRESH_BINARY_INV | THRESH_OTSU);

    vector<vector<Point>> contours2;
    vector<Vec4i> hierarchy2;
    findContours(thresh2, contours2, hierarchy2, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    sort(contours2.begin(), contours2.end(), ascendSort);//ascending sort

    Moments sample_moments = cv::moments(contours2[0]);

    Mat sample_hu;
    HuMoments(sample_moments, sample_hu);

    for (size_t i = 0; i< contours.size(); i++) {
        double area = contourArea(contours[i]);

        if (area < 11000) {
            continue;
        }

        Moments m = cv::moments(contours[i]);

        Mat hu;
        HuMoments(m, hu);

        double dist = matchShapes(hu, sample_hu, CONTOURS_MATCH_I1, 0);
        printf("contour match distance : %.2f\n", dist);
        if (dist < 0.5) {
            RotatedRect rrt = minAreaRect(contours[i]);

            Point2f pt[4];
            rrt.points(pt);
            line(src, pt[0], pt[1], Scalar(0, 0, 255), 8, 8);
            line(src, pt[1], pt[2], Scalar(0, 0, 255), 8, 8);
            line(src, pt[2], pt[3], Scalar(0, 0, 255), 8, 8);
            line(src, pt[3], pt[0], Scalar(0, 0, 255), 8, 8);
        }
    }
    imshow("result", src);

    waitKey(0);
    return 0;
}

sample 图:

sample.jpg

匹配到 sample 中的物体,并用红线框出:

匹配到sample中的物体.png

matchShapes() 函数能够比较两个形状或两个轮廓,并返回一个显示相似性的度量。其结果越小,匹配就越好。

4. 总结

轮廓的矩特征是描述轮廓形状、大小、位置等信息的一种重要特征。轮廓的矩特征在图像处理、计算机视觉等领域有广泛的应用,矩特征可以用来进行轮廓的匹配、分类、识别等工作。