图像的矩是归一化的灰度级图像的二维随机变量的概率密度,是一个统计学特征。OpenCV中实现了这个矩的算子是Moments();其中分为零阶矩M00、一阶矩M10和M01、二阶矩M20,M02和M11;其中当图像为二值图时,M00是图像面积(白色区域)的总和,或者说连通域的面积;而这时M10和M01是图像白色区域上x和y坐标值的累计,所以图像的的重心(Xc,Yc)可以由:
Xc=M10/M00;
Yc=M01/M00;
图像的二阶矩一般用来求图像的方向,方法是:
下面是OpenCV的实现代码:
1.调用opencv的矩算子:
//图像的矩
//根据S.M.罗斯的概率论教程,一阶矩指E[X],即数列X的均值称为一阶矩。
//以此类推,E[Xn] ,n≥1,称为X的 n阶矩,也就是二阶矩、三阶矩...
//1. 矩的概念
//图像识别的一个核心问题是图像的特征提取,简单描述即为用一组简单的
//数据(图像描述量)来描述整个图像,这组数据越简单越有代表性越好。
//良好的特征不受光线、噪点、几何形变的干扰。图像识别发展几十年,
//不断有新的特征提出,而图像不变矩就是其中一个。
//矩是概率与统计中的一个概念,是随机变量的一种数字特征。设XX为随
//机变量,cc为常数,kk为正整数。则量E[(x−c)k]E[(x−c)k]称为XX关
//于cc点的kk阶矩。
//比较重要的有两种情况:
//1. c=0c=0。这时ak=E(Xk)ak=E(Xk)称为XX的kk阶原点矩
//2. c=E(X)c=E(X)。这时μk=E[(X−EX)k]μk=E[(X−EX)k]称为XX的kk阶中心矩。
//一阶原点矩就是期望。一阶中心矩μ1=0μ1=0,二阶中心矩μ2μ2就是XX的方
//差Var(X)Var(X)。在统计学上,高于4阶的矩极少使用。μ3μ3可以去衡量分布
//是否有偏。μ4μ4可以去衡量分布(密度)在均值附近的陡峭程度如何。
//针对于一幅图像,我们把像素的坐标看成是一个二维随机变量(X,Y)(X,Y),那么
//一幅灰度图像可以用二维灰度密度函数来表示,因此可以用矩来描述灰度图像的特征。
//不变矩(Invariant Moments)是一处高度浓缩的图像特征,具有平移、灰度、尺
//度、旋转不变性。M.K.Hu在1961年首先提出了不变矩的概念。1979年M.R.Teague
//根据正交多项式理论提出了Zernike矩。下面主要介绍这两种矩特征的算法原理与实现。
void imgMoment()//27图像的矩
{
//1.查找轮廓前的预处理
Mat srcImg = imread("D:/ImageTest/11.png");
Mat copyImg = srcImg.clone();
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY_INV);
imshow("thresh", srcImg);
//2.查找轮廓
vector<vector<Point>> contours;
findContours(srcImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//最外层轮廓
drawContours(copyImg, contours, -1, Scalar(0, 255, 0), 2, 8);
//3.轮廓矩的计算
Moments moments0 = moments(contours[0],false);//计算轮廓矩
cout << moments0.m00<< endl;//输出空间矩之一的m00
//4添加文字
char outText[10] = { 0 };
double outValue =moments0.m00 ;
sprintf(outText, "%.1f", outValue);
putText(copyImg, outText, Point(10, 50),//起始点
CV_FONT_HERSHEY_COMPLEX, //字体
2, //字体大小
Scalar(0, 255, 0), 2, 8);
imshow("contours", copyImg);
waitKey(0);
}
效果:
2.矩的其他参数,角度,重心,面积:
void imgMoment2()//图像的矩其他参数
{
//1.查找轮廓前的预处理
Mat srcImg = imread("D:/ImageTest/12.png");
Mat copyImg = srcImg.clone();
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY_INV);
imshow("thresh", srcImg);
//2.查找轮廓
vector<vector<Point>> contours;
findContours(srcImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//最外层轮廓
drawContours(copyImg, contours, -1, Scalar(0, 255, 0), 2, 8);
//3.轮廓矩的计算
Moments moments0 = moments(contours[0],false);//计算轮廓矩
cout << moments0.m00<< endl;//输出空间矩之一的m00
//4添加文字
char outText[10] = { 0 };
double outValue =moments0.m00 ;
sprintf(outText, "%.1f", outValue);
putText(copyImg, outText, Point(10, 50),//起始点
CV_FONT_HERSHEY_COMPLEX, //字体
2, //字体大小
Scalar(0, 255, 0), 2, 8);
Moments m = moments(contours[0], true);//moments()函数计算出三阶及以下的矩
Point2d center(m.m10 / m.m00, m.m01 / m.m00);//此为重心
//计算方向
double a = m.m20 / m.m00 - center.x*center.x;
double b = m.m11 / m.m00 - center.x*center.y;
double c = m.m02 / m.m00 - center.y*center.y;
double theta = fastAtan2(2*b,(a - c))/2;//此为形状的方向
cout <<"theta:"<<theta<<endl;
circle(copyImg,Point(m.m10 / m.m00, m.m01 / m.m00),3,Scalar(0,0,255),-1,8);
char outText2[10] = { 0 };
double outValue2 =m.m10 / m.m00 ;
sprintf(outText2, "%.1f", outValue2);
putText(copyImg, outText2, Point(m.m10 / m.m00, m.m01 / m.m00),//起始点
CV_FONT_HERSHEY_COMPLEX, //字体
1, //字体大小
Scalar(0, 0,255), 1, 8);
char outText3[10] = { 0 };
double outValue3 =m.m01 / m.m00 ;
sprintf(outText3, "%.1f", outValue3);
putText(copyImg, outText3, Point(m.m10 / m.m00+100, m.m01 / m.m00),//起始点
CV_FONT_HERSHEY_COMPLEX, //字体
1, //字体大小
Scalar(0, 0, 255), 1, 8);
char outText4[10] = { 0 };
double outValue4 =theta ;
sprintf(outText4, "%.1f", outValue4);
putText(copyImg, outText4, Point(m.m10 / m.m00, m.m01 / m.m00+25),//起始点
CV_FONT_HERSHEY_COMPLEX, //字体
1, //字体大小
Scalar(255, 0, 0), 1, 8);
imshow("moments", copyImg);
waitKey(0);
}
效果:
3.图像hu矩:
void imgHuMoment()//图像hu矩
{
// 不变矩(Invariant Moments)是一处高度浓缩的图像特征,具有平移、
// 灰度、尺度、旋转不变性。M.K.Hu在1961年首先提出了不变矩的概
// 念。1979年M.R.Teague根据正交多项式理论提出了Zernike矩;
// 利用二阶和三阶规格中心矩可以导出7个不变矩组(Φ1 Φ7)(Φ1 Φ7),
// 它们在图像平移、旋转和比例变化时保持不变。OpenCV只有Hu矩阵封装;
Mat image = imread("D:/ImageTest/lena.png");
Mat copyImg=image.clone();
cvtColor(image, image, CV_BGR2GRAY);
Moments mts = moments(image);
double hu[7];
HuMoments(mts, hu);
for (int i=0; i<7; i++)
{
cout << log(abs(hu[i])) <<endl;//7个值
char outText0[10] = { 0 };
double outValue0 =i ;
sprintf(outText0, "%.0f", outValue0);
putText(copyImg, outText0, Point(5, 30+30*i),//起始点
CV_FONT_HERSHEY_COMPLEX, //字体
1, //字体大小
Scalar(255, 0,255), 2, 8);
char outText[10] = { 0 };
double outValue =log(abs(hu[i])) ;
sprintf(outText, "%.1f", outValue);
putText(copyImg, outText, Point(35, 30+30*i),//起始点
CV_FONT_HERSHEY_COMPLEX, //字体
1, //字体大小
Scalar(0, 0, 255), 2, 8);
}
imshow("moments", copyImg);
waitKey(0);
}
效果:
4.图像矩的匹配:
void imgMatchShapes()//图像矩和匹配
{
//1.查找模版图像的轮廓
Mat templateImg = imread("D:/ImageTest/0tu.JPG");
Mat copyImg1 = templateImg.clone();
GaussianBlur(templateImg, templateImg,Size(5,5),0);//高斯滤波去除小噪点
cvtColor(templateImg, templateImg, CV_BGR2GRAY);
threshold(templateImg, templateImg, 100, 255, CV_THRESH_BINARY_INV );
imshow("thresh1", templateImg);
vector<vector<Point>> contours1;
findContours(templateImg, contours1, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//最外层轮廓
drawContours(copyImg1, contours1, -1, Scalar(0, 255, 0), 2, 8);
imshow("contours1", copyImg1);
//2.查找待测试图像的轮廓
Mat testImg = imread("D:/ImageTest/t.png");
Mat copyImg2 = testImg.clone();
GaussianBlur(testImg, testImg,Size(5,5),0);//高斯滤波去除小噪点
cvtColor(testImg, testImg, CV_BGR2GRAY);
threshold(testImg, testImg, 100, 255, CV_THRESH_BINARY_INV);//确保黑中找白
imshow("thresh2", testImg);
vector<vector<Point>> contours2;
findContours(testImg, contours2, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//最外层轮廓
// drawContours(copyImg2, contours2, -1, Scalar(0, 255, 0), 1, 8);
// imshow("contours2", copyImg2);
//3.形状匹配---比较两个形状或轮廓间的相似度
for (int i = 0; i < contours2.size();i++)//遍历待测试图像的轮廓
{
//返回此轮廓与模版轮廓之间的相似度,a0越小越相似
double a0 = matchShapes(contours1[0],contours2[i],CV_CONTOURS_MATCH_I1,0);
cout << "match " << i << " value: " << a0 << endl;//输出两个轮廓间的相似度
if (a0<0.05)//如果此轮廓与模版轮廓的相似度小于0.1
{
drawContours(copyImg2, contours2, i, Scalar(0, 255, 0), 2, 8);//则在待测试图像上画出此轮廓
}
else
{
drawContours(copyImg2, contours2, i, Scalar(0, 0, 255), 2, 8);//红色轮廓没有匹配上
}
imshow("copyImg2", copyImg2);
if (waitKey(0) == 27)//等待按键进行下一个轮廓,ESC则退出
{
cout << "ESC" << endl;
break;
}
}
// imshow("copyImg2", copyImg2);
waitKey(0);
}
效果: