一、概率论上的定义
看到矩这个字,很容易联想到概率论,在概率论中,定义如下:
或者说:
设 X 和 Y 是随机变量,c 为常数,k 为正整数,
如果E(|X−c|^k)E(|X−c|^k)存在,则称E(|X−c|^k)E(|X−c|^k)为 X 关于点 c 的 k 阶矩。
如果E(|X−c1|^p⋅|Y−c2|^q)存在,则称其为 X,Y 关于 c 点 p+q 阶矩。
其余基本概率论知识可参考:https://www.cnblogs.com/wyuzl/p/7845948.html
二、在图像学上的定义
一幅M×N的数字图像f(i,j),其p+q阶几何矩和中心矩为:
其中:
三、几何矩的基本意义
(1)零阶矩
可以发现,当图像为二值图时,就是这个图像上白色区域的总和,因此,可以用来求二值图像(轮廓,连通域)的面积。
(2)一阶矩
当图像为二值图时,就是白色像素关于x坐标的累加和,而则是y坐标的累加和
由此,可获得图像的重心:,也就是前文提到的。
(3)二阶矩
二阶矩可以用来求物体形状的方向。
后续会有介绍,具体可参考:https://blog.csdn.net/qq826309057/article/details/70039397
四、由几何矩可表示出中心距如下:
摘自:https://blog.csdn.net/keith_bb/article/details/70197104
为了消除图像比例变化带来的影响,定义规格化中心矩如下:
利用二阶和三阶规格中心矩可以导出下面7个不变矩组(Φ1 Φ7),它们在图像平移、旋转和比例变化时保持不变
核心函数:
(1)求矩
Moments moments(inputArray array, bool binaryImage=false)
(2)计算轮廓面积
double contourArea(inputArray contour, bool oriented=false)
(3) 计算轮廓长度
double arcLength(inputArray curve,bool closed)
一、求图像的重心和方向
前面提到二阶矩可以用来求物体形状的方向。
其中:
,
,
fastAtan2()为opencv的函数,输入向量,返回一个0-360的角度。
个人认为:关于fastAtan2()的推算如下:
当然这只是个人推导,有兴趣的同学可以验证一下。
另外,fastAtan2()返回的角度指自然坐标系下x轴正半轴按顺时针到图像轴的角,如下图的α角:
【注意:实际处理时,需把目标部分变成白色,背景为黑色。这里只是为了方便看才把目标变成黑色而已】
测试代码:
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
using
namespace
std;
-
using
namespace cv;
-
-
void main()
-
{
-
Mat srcImg;
-
srcImg = imread(
"F:\\opencv_re_learn\\flash.jpg");
-
if (!srcImg.data){
-
cout<<
"failed to read" <<
endl;
-
system(
"pause");
-
return;
-
}
-
Mat srcGray;
-
cvtColor(srcImg, srcGray, CV_BGR2GRAY);
-
Mat thresh;
-
threshold(srcGray, thresh,
100,
255, CV_THRESH_BINARY_INV |
-
CV_THRESH_OTSU);
//二值化时主要要让目标部分是白色像素
-
Moments m = moments(thresh,
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 <<
2 * b <<
endl;
-
cout << a - c <<
endl;
-
cout <<
"角度是:"<< theta <<
endl;
-
//绘制重心
-
circle(srcImg, center,
2, Scalar(
0,
0,
255),
2);
-
imshow(
"src", srcImg);
-
imshow(
"Gray", srcGray);
-
imshow(
"Thresh", thresh);
-
waitKey(
0);
-
}
二、轮廓匹配
轮廓匹配并没有直接用到Moments ,但是是基于轮廓矩的7个不变形矩进行比较的。
相关函数:
-
double MatchShapes(const void* object1, const void* object2,
-
int method,
double parameter =
0);
测试代码:
-
#include
-
#include
-
#include
-
#include
-
using
namespace
std;
-
using
namespace cv;
-
RNG rng(12345);
-
Mat init_src(string filename)
-
{
-
Mat srcImg = imread(filename);
-
if (!srcImg.data){
-
cout <<
"filed to read" <<
endl;
-
system(
"pause");
-
return Mat::zeros(
100,
100, CV_8UC1);
-
}
-
return srcImg;
-
}
-
-
vector<
vector
> find_contour(Mat thresh,
string window_name)
-
{
-
Mat canny_output;
-
vector<
vector
>contours;
-
vector
hierarchy;
-
medianBlur(thresh, thresh,
5);
-
//canny边缘检测
-
//如果图片的轮廓分明,建议可以不进行canny边缘检测,可直接对二值化图像进行findcontours
-
//Canny(thresh, thresh, 10, 10 * 2, 3);
-
-
//寻找轮廓
-
findContours(thresh, contours, hierarchy,
-
CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(
0,
0));
-
-
Mat drawing = Mat::zeros(thresh.size(), CV_8UC3);
-
for (
int i =
0; i < contours.size(); i++){
-
//定义随机颜色
-
Scalar color = Scalar(rng.uniform(
0,
255),
-
rng.uniform(
0,
255), rng.uniform(
0,
255));
-
//绘制
-
drawContours(drawing, contours, i, color,
-
2,
8, hierarchy,
0, Point());
-
}
-
//显示
-
namedWindow(window_name, CV_WINDOW_AUTOSIZE);
-
imshow(window_name, drawing);
-
return contours;
-
}
-
-
void main()
-
{
-
//图片读取、预处理
-
Mat srcImg1 = init_src(
"F:\\opencv_re_learn\\angle_test2.jpg");
-
Mat srcImg2 = init_src(
"F:\\opencv_re_learn\\angle_test.jpg");
-
Mat srcGray1, srcGray2;
-
cvtColor(srcImg1, srcGray1, CV_BGR2GRAY);
-
cvtColor(srcImg2, srcGray2, CV_BGR2GRAY);
-
Mat thresh1, thresh2;
-
threshold(srcGray1, thresh1,
100,
255, CV_THRESH_BINARY_INV |
-
CV_THRESH_OTSU);
-
threshold(srcGray2, thresh2,
100,
255, CV_THRESH_BINARY_INV |
-
CV_THRESH_OTSU);
-
//imshow("thresh1", thresh1);
-
//imshow("thresh2", thresh2);
-
-
//获取轮廓
-
vector<
vector
> contours1 = find_contour(thresh1,
"contour1");
-
vector<
vector
> contours2 = find_contour(thresh2,
"contour2");
-
-
//轮廓比较
-
cout <<
"first method to match, result:" <<
-
matchShapes(contours1[
0], contours2[
0], CV_CONTOURS_MATCH_I1,
0) <<
endl;;
-
cout <<
"second method to match, result:" <<
-
matchShapes(contours1[
0], contours2[
0], CV_CONTOURS_MATCH_I2,
0) <<
endl;
-
cout <<
"third method to match, result:" <<
-
matchShapes(contours1[
0], contours2[
0], CV_CONTOURS_MATCH_I2,
0)<<
endl;
-
imshow(
"src1", srcImg1);
-
imshow(
"src2", srcImg2);
-
waitKey(
0);
-
}
应该只需修改图片的路径即可进行调试
1)读入两张相同的图片,三种方法匹配的结果都是0,表示完全匹配
2)还是读入上面的两张图片,但是其中一张经过旋转
结果:三种方法匹配的结果都很接近0,表示这两个轮廓还是基本匹配的
3)读入两张不同的图片
结果明显偏离0,但是可以说还是有一点相似性吧。。。
参考文章:
https://blog.csdn.net/qq_31531635/article/details/73692611
https://blog.csdn.net/qq826309057/article/details/70039397
https://blog.csdn.net/keith_bb/article/details/70197104
https://blog.csdn.net/mingzhentanwo/article/details/45155307