矩是描述图像特征的算子,如今矩技术已广泛应用于图像检索和识别 、图像匹配 、图像重建 、数字压缩 、数字水印及运动图像序列分析等领域。常见的矩描述子可以分为以下几种:几何矩、正交矩、复数矩和旋转矩。
从一幅图像计算出来的矩集,不仅可以描述图像形状的全局特征,而且可以提供大量关于该图像不同的几何特征信息,如大小,位置、方向和形状等。图像矩这种描述能力广泛应用于各种图像处理、计算机视觉和机器人技术领域的目标识别与方位估计中。
一阶矩:与形状有关;
二阶矩:显示曲线围绕直线平均值的扩展程度;
三阶矩:关于平均值的对称性测量;
由二阶矩和三阶矩可以导出7个不变矩。而不变矩是图像的统计特性,满足平移、伸缩、旋转均不变的不变性、在图像识别领域得到广泛的应用。
几何矩: 提出的时间最早且形式简单,对它的研究最为充分。几何矩对简单图像有一定的描述能力,他虽然在区分度上不如其他三种矩,但与其他几种算子比较起来,他极其的简单,一般只需用一个数字就可表达。所以,一般我们是用来做大粒度的区分,用来过滤显然不相关的文档。
数学中的矩
图像几何矩
图像的几何矩包含空间矩,中心矩和中心归一化矩。几何矩具有平移、旋转和尺度不变性,一般用于做大粒度的区分,用来过滤显然不相关的图像。
一阶矩、零阶矩:用来计算某个形状的重心。
M
00
=
∑
I
∑
J
V
(
i
,
j
)
;
M_{00}=\sum_I\sum_JV(i,j);
M00=I∑J∑V(i,j);
M
10
=
∑
I
∑
J
i
∗
V
(
i
,
j
)
;
M_{10}=\sum_I\sum_Ji*V(i,j);
M10=I∑J∑i∗V(i,j);
M
01
=
∑
I
∑
J
j
∗
V
(
i
,
j
)
;
M_{01}=\sum_I\sum_Jj*V(i,j);
M01=I∑J∑j∗V(i,j);
i
c
=
M
10
M
00
,
j
c
=
M
01
M
00
i_c=\frac{M_{10}}{M_{00}} , j_c=\frac{M_{01}}{M_{00}}
ic=M00M10,jc=M00M01
其中, M 00 M_{00} M00是零阶矩, M 10 M_{10} M10、 M 01 M_{01} M01是一阶矩。其中 i c i_c ic和 j c j_c jc是图像的重心坐标。
二阶矩 :用来计算形状的方向。
M
20
=
∑
i
∑
j
i
2
V
(
i
,
j
)
M_{20}=\sum_i\sum_ji^2V(i,j)
M20=i∑j∑i2V(i,j)
M
02
=
∑
i
∑
j
j
2
I
(
i
,
j
)
M_{02}=\sum_i\sum_jj^2I(i,j)
M02=i∑j∑j2I(i,j)
M
11
=
∑
I
∑
J
i
∗
j
∗
V
(
i
,
j
)
M_{11}=\sum_{I}\sum_{J}i*j*V(i,j)
M11=I∑J∑i∗j∗V(i,j)
那么物体的方向:
OpenCV中的API
在OpenCV中,可以很方便的计算多边形区域的3阶特征矩。opencv中的矩主要包括以下几种:
-
空间矩/几何矩
空间矩的实质就是面积或者质量,可以通过一阶矩计算质心/重心。空间矩:spatial moments: double m00 double m10 double m01 double m20 double m11 double m02 double m30 double m21 double m12 double m03
各阶矩的物理意义:
0阶矩( M 00 M_{00} M00)——————目标区域的质量
1阶矩( M 01 , M 10 M_{01},M_{10} M01,M10)————目标区域的质心
2阶矩( M 02 , M 11 , M 20 M_{02},M_{11},M_{20} M02,M11,M20)———表示旋转半径
3阶矩( M 03 , M 12 , M 21 , m 30 M_{03},M_{12},M_{21},m_{30} M03,M12,M21,m30)——描述目标方位和斜度,反映目标的扭曲程度 -
中心矩
中心矩体现的是图像强度的最大和最小方向(中心矩可以构建图像的协方差矩阵),其只具有平移不变性,所以用中心矩做匹配效果不会很好。中心矩:构造平移不变形
质心的坐标: x = m 10 m 00 , y = m 01 m 00 x=\frac{m_{10}}{m_{00}},y=\frac{m_{01}}{m_{00}} x=m00m10,y=m00m01中心矩central moments: double mu20 double mu11 double mu02 double mu30 double mu21 double mu12 double mu03
-
归一化的中心矩
归一化后具有尺度不变性。中心归一化矩central normalized moments: double nu20 double nu11 double nu02 double nu30 double nu21 double nu12 double nu03
-
Hu矩
Hu矩由于具有尺度、旋转、平移不变性,可以用来做匹配。
OpenCV中提供了:
moments()来计算图像中的中心矩。
HuMoments()用于由中心矩计算Hu矩。
函数contourArea函数计算轮廓面积
arcLength来计算轮廓或者曲线长度。
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
);
//空间矩
//! @{
double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03;
//! @}
//中心矩
//! @{
CV_PROP_RW double mu20, mu11, mu02, mu30, mu21, mu12, mu03;
//! @}
//中心归一化矩
//! @{
CV_PROP_RW double nu20, nu11, nu02, nu30, nu21, nu12, nu03;
//! @}
};
1、moments函数
-
说明
计算多边形3阶之前的所有矩。 -
说明
Moments moments( InputArray array, bool binaryImage = false );
-
参数
array 像素图(单通道、8位或浮点二维数组)或二维的( 1 × N 1 \times N 1×N or N × 1 N \times 1 N×1 )的点。 binaryImage 如果为真,则所有非零图像像素视为1。该参数仅用于图像。 仅适用于Python绑定的轮廓矩计算:注意numpy
输入数组的类型应该是np.int32或np.float32。
2、contourArea函数
-
说明
该函数计算轮廓区域。与矩类似,使用格林公式计算面积。因此,如果使用drawContours或fillPoly绘制轮廓,则返回的区域和非零像素的数量可以不同。同样,对于带有自相交的轮廓,该功能肯定会给出错误的结果。 -
声明
double contourArea( InputArray contour, bool oriented = false );
-
参数
contour 二维点(轮廓顶点)的输入向量,存储在std :: vector或Mat中。 oriented 定向区域标志。如果为true,则函数根据轮廓方向(顺时针或逆时针)返回带符号的区域值。使用此功能,您可以通过取一个区域的符号来确定轮廓的方向。默认情况下,该参数为false,表示返回绝对值。
3、arcLength函数
-
说明
计算轮廓周长或曲线长度。
该函数计算曲线长度或闭合轮廓周长。 -
函数
double arcLength( InputArray curve, bool closed );
-
参数
curve 二维点的输入向量,存储在std :: vector或Mat中。 closed 指示曲线是否闭合的标志。
应用
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
void getCenterPoint(Mat& src) {
Mat binary, gray,dst;
dst = src.clone();
//1.将图片转化成灰度图像
cvtColor(src, gray, COLOR_BGR2GRAY);
imshow("gray", gray);
//2.转化图片成为二值化
threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
imshow("binary", binary);
//3.发现轮廓
vector<vector<Point>> contours;
findContours(binary, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
//4.计算轮廓的矩
vector<Moments> mu(contours.size());
vector<Point2f> mc(contours.size());
vector<double> mu_area(contours.size());
for (size_t i = 0; i < contours.size(); i++)
{
int r = (i + 60) % 255;
int g = (i + 20) % 255;
int b = (i + 140) % 255;
Scalar color = Scalar(r,g,b);
//4.1面积过滤,过滤面积小于300的
double area = contourArea(contours[i]);
if (area < 300)
continue;
//4.2横纵比过滤
Rect rect = boundingRect(contours[i]);
float radio = float(rect.width) / float(rect.height);
if (radio < 3 && radio>1) {
//4.3计算矩
mu[i] = moments(contours[i]);
//5. 由矩计算轮廓的中心矩、面积
double x = mu[i].m10 / mu[i].m00;
double y = mu[i].m01 / mu[i].m00;
//5.1中心矩
mc[i] = Point2d(x, y);
//5.2面积
mu_area[i] = mu[i].m00;
cout << "第" << i << "个轮廓的中心:(" << mc[i].x << "," << mc[i].y << "),面积为:" << mu_area[i] << endl;
//5.3画出轮廓
drawContours(dst, contours, i, color, 2);
//5.4画出中心矩
circle(dst, Point(mc[i]), 5, color, -1);
}
}
imshow("contour", src);
imshow("dst", dst);
}
int main() {
Mat src = imread("D:/test/huahua.png");
if (src.empty()) {
cout << "input the image error!" << endl;
}
getCenterPoint(src);
waitKey(0);
return 0;
}