嚴格來講矩是概率與統計中的一個概念,是隨機變量的一種數字特征。設 x 為隨機變量,C為常數,則量E[(x−c)^k]稱為X關於C點的k階矩。比較重要的兩種情況如下:
1.c=0,這時a_k=E(X^k)稱為X的k階原點矩;
2.c=E(X),這時μ_k=E[(X−EX)^k]稱為X的k階中心矩
一階原點矩就是期望,一階中心矩μ_1=0,二階中心矩μ_2就是X的方差Var(X)。在統計學上,高於4階的矩極少使用,μ_3可以去衡量分布是否有偏,μ_4可以衡量分布(密度)在均值拘謹的陡峭程度。
對於數學來說

矩、中心矩、質心、patch方向
一階原點矩就是期望。二階中心矩就是隨機變量的的方差. 在統計學上,高於4階的矩極少使用。三階中心距可以去衡量分布是否有偏。四階中心矩可以去衡量分布在均值附近的陡峭程度如何。
那針對一幅圖像,我們把像素的坐標看成是一個二維隨機變量(X, Y),那么一副灰度圖可以用二維灰度圖密度函數來表示,因此可以用矩來描述灰度圖像的特征。

空間矩的實質為面積或者質量。可以通過一階矩計算質心/重心。

重心(中心centers):
Hu矩




class Moments{ public: Moments(); Moments(double m00, double m10, double m01, double m20, double m11, double m02, double m30, double m21, double m12, double m03 ); Moments( const CvMoments& moments ); operator CvMoments() const; // spatial moments 空間矩 double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; // central moments 中心矩 double mu20, mu11, mu02, mu30, mu21, mu12, mu03; // central normalized moments 中心歸一化矩 double nu20, nu11, nu02, nu30, nu21, nu12, nu03; }


#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
//定義窗口名字的宏
#define WINDOW_NAME1 "【原始圖】"
#define WINDOW_NAME2 "【圖像輪廓】"
//全局變量的聲明
Mat g_srcImage, g_grayImage;
int g_nThresh = 100;
int g_nMaxThresh = 255;
RNG g_rng(12345);
Mat g_cannyMat_output;
vector<vector<Point> > g_vContours;
vector<Vec4i>g_vHierarchy;
//全局函數聲明
void on_ThreshChange(int, void*);
//main()函數
int main()
{
//改變console字體顏色
system("color 1E");
//讀入原圖,返回3通道圖像數據
g_srcImage = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\01.jpg", 1);
//源圖像轉化為灰度圖像並平滑
cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
blur(g_grayImage, g_grayImage, Size(3, 3));
//創建新窗口
namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
imshow(WINDOW_NAME1, g_srcImage);
//創建滾動條並進行初始化
createTrackbar("閾值:", WINDOW_NAME1, &g_nThresh, g_nMaxThresh, on_ThreshChange);
on_ThreshChange(0, 0);
waitKey(0);
return 0;
}
void on_ThreshChange(int, void *)
{
//使用canny檢測邊緣
Canny(g_grayImage, g_cannyMat_output, g_nThresh, g_nThresh * 2, 3);
//找到輪廓
findContours(g_cannyMat_output, g_vContours, g_vHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
//計算矩
vector<Moments> mu(g_vContours.size());
for (unsigned int i = 0; i < g_vContours.size(); i++)
{
mu[i] = moments(g_vContours[i], false);
}
//計算中心矩
vector<Point2f>mc(g_vContours.size());
for (unsigned int i = 0; i < g_vContours.size(); i++)
{
mc[i] = Point2f(static_cast<float>(mu[i].m10 / mu[i].m00), static_cast<float>(mu[i].m01 / mu[i].m00));
}
//繪制輪廓
Mat drawing = Mat::zeros(g_cannyMat_output.size(), CV_8UC3);
for (unsigned int i = 0; i < g_vContours.size(); i++)
{
//隨機生成顏色值
Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));
//繪制外層和內層輪廓
drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point());
//繪制圓
circle(drawing, mc[i], 4, color, -1, 8, 0);
}
//顯示到窗口中
namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE);
imshow(WINDOW_NAME2, drawing);
//通過m00計算輪廓面積和Opencv函數比較
printf("\t輸出內容:面積和輪廓長度\n");
for (unsigned int i = 0; i < g_vContours.size(); i++)
{
printf(">通過m00計算出輪廓[%d]的面積:(M_00) = %.2f \n Opencv函數計算出面積 = %.2f,長度:%.2f \n\n", i, mu[i].m00, contourArea(g_vContours[i]), arcLength(g_vContours[i], true));
Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));
drawContours(drawing, g_vContours, i, color, 2, 8, g_vHierarchy, 0, Point());
circle(drawing, mc[i], 4, color, -1, 8, 0);
}
}
本文參考:圖像的形狀特征——圖像的矩
