OpenCV——空间矩算子特征矩一阶矩二阶矩中心矩重心目标方向

特征矩的知识在概率论和数理统计中有介绍,空间矩的方法在图像应用中比较广泛,包括零阶矩求面积、一阶矩确定重心、二阶矩确定主方向、二阶矩和三阶矩可以推导出七个不变矩Hu不变矩,不变矩具有旋转,平移、缩放等不变性,因此在工业应用和模式识别中得到广泛的应用。

目标物体灰度函数特征矩的公式定义如下:

如果是二值图像,那么f(x,y)就变成

在OpenCV中,可以很方便的计算多边形区域的3阶特征矩,opencv中的矩主要包括以下几种:空间矩,中心矩和中心归一化矩。

class Moments { public: ...... // 空间矩 double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03;// 中心矩double mu20, mu11, mu02, mu30, mu21, mu12, mu03;// 中心归一化矩 double nu20, nu11, nu02, nu30, nu21, nu12, nu03; }空间矩的公式为:

image

可以知道,对于01二值化的图像,m00即为轮廓的面积。中心矩的公式为:

image

其中:

image

归一化的中心矩公式为:

image
参考于 链接

二阶中心距,也叫作方差,它告诉我们一个随机变量在它均值附近波动的大小,方差越大,波动性越大。方差也相当于机械运动中以重心为转轴的转动惯量。(The moment of inertia.)

三阶中心距告诉我们一个随机密度函数向左或向右偏斜的程度。

在均值不为零的情况下,原点距只有纯数学意义。

A1,一阶矩就是 E(X),即样本均值。具体说来就是A1=(西格玛Xi)/n ----(1)
A2,二阶矩就是 E(X^2)即样本平方均值 ,具体说来就是 A2=(西格玛Xi^2)/n-----(2)
Ak,K阶矩就是 E(X^k)即样本K次方的均值,具体说来就是 Ak=(西格玛Xi^k)/n,-----(3)

不变矩的物理含义:
如果把图像看成是一块质量密度不均匀的薄板,其图像的灰度分布函数f(x,y)就是薄板的密度分布函数,则其各阶矩有着不同的含义,如零阶矩表示它的总质量;一阶矩表示它的质心;二阶矩又叫惯性矩,表示图像的大小和方向。事实上,如果仅考虑阶次为2的矩集,则原始图像等同于一个具有确定的大小、方向和离心率,以图像质心为中心且具有恒定辐射率的椭圆。由三阶矩以下矩构成的七个矩不变量具有平移、旋转和尺度不变性等等。当密度分布函数发生改变时,图像的实质没有改变,仍然可以看做一个薄板,只是密度分布有所改变。虽然此时各阶矩的值可能发生变化,但由各阶矩计算出的不变矩仍具有平移、旋转和尺度不变性。通过这个思想,可对图像进行简化处理,保留最能反映目标特性的信息,再用简化后的图像计算不变矩特征,可减少计算量。

研究表明,只有基于二阶矩的不变矩对二维物体的描述才是真正的与旋转、平移和尺度无关的。较高阶的矩对于成像过程中的误差,微小的变形等因素非常敏感,所以相应的不变矩基本上不能用于有效的物体识别。即使是基于二阶矩的不变矩也只能用来识别外形相差特别大的物理,否则他们的不变矩会因为很相似而不能识别。

在OpenCV中,还可以很方便的得到Hu不变距,Hu不变矩在图像旋转、缩放、平移等操作后,仍能保持矩的不变性,所以有时候用Hu不变距更能识别图像的特征。

1、在数学领域,矩 非常的常见
2、在计算机视觉中,使用2维离散形式的矩计算方法
3、使用矩,可以计算物体的面积,物体的质心等。
4、中心矩的计算方法是:某个矩除以0阶矩
5、高阶矩具有旋转不变性,尺度不变性,变换不变性等。


我们很熟悉概率论中的一阶矩二阶矩高阶矩,但是很多人可能和我一样,不明白图像中矩是拿来干嘛的。

在计算机视觉的书中,虽然有提到矩,但是讲的很泛泛也很笼统。自然Google百度这些东西也是靠不牢的。在阅读了相关论文之后,我终于大致对矩在图像中的应用有了了解。
其实矩除了在概率论中有体现,在几何中也是学过的。比方说零阶矩是物体的质量,一阶矩和零阶矩可以算出物体的中心,而二阶矩是用来计算物体的方向的。
拿图像出来来说,图像可以看成是一个平板的物体,其一阶矩和零阶矩就可以拿来计算某个形状的重心,而二阶矩就可以拿来计算形状的方向。
光说不练假把式,下面给出他们的计算公式:

其中M00即零阶矩


M20和M02为二阶矩,接下来计算物体形状的方向



OpenCV代码如下所示:
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
Mat src; Mat src_gray;
int thresh = 30;
int max_thresh = 255;
RNG rng(12345);
int main(){
	src = imread( "opencv-logo.png" ,CV_LOAD_IMAGE_COLOR );
	cvtColor( src, src_gray, CV_BGR2GRAY );//灰度化
	GaussianBlur( src, src, Size(3,3), 0.1, 0, BORDER_DEFAULT );
	blur( src_gray, src_gray, Size(3,3) ); //滤波
	namedWindow( "image", CV_WINDOW_AUTOSIZE );
	imshow( "image", src );
	moveWindow("image",20,20);
	//定义Canny边缘检测图像
	Mat canny_output;
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
	//利用canny算法检测边缘
	Canny( src_gray, canny_output, thresh, thresh*3, 3 );
	namedWindow( "canny", CV_WINDOW_AUTOSIZE );
	imshow( "canny", canny_output );
	moveWindow("canny",550,20);
	//查找轮廓
	findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
	//计算轮廓矩
	vector<Moments> mu(contours.size() );
	for( int i = 0; i < contours.size(); i++ )
	{ mu[i] = moments( contours[i], false ); }
	//计算轮廓的质心
	vector<Point2f> mc( contours.size() );
	for( int i = 0; i < contours.size(); i++ )
	{ mc[i] = Point2d( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); }

	//画轮廓及其质心并显示
	Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
	printf("\t\t 几何特性\n");
	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() );
		circle( drawing, mc[i], 4, color, -1, 8, 0 );		
		rectangle(drawing, boundingRect(contours.at(i)), cvScalar(0,255,0));
		printf("目标%d - 面积:%.2f - 周长: %.2f ", i, mu[i].m00, contourArea(contours[i]), arcLength( contours[i], true ) );
		RotatedRect r = fitEllipse(contours.at(i));
		double majorAxis = r.size.height > r.size.width ? r.size.height : r.size.width;//长轴大小
		double minorAxis = r.size.height > r.size.width ? r.size.width : r.size.height;//短轴大小
		double area = mu[i].m00;//面积
		int perimeter = arcLength(contours.at(i), true);
		double orientation = r.angle;
		double orientation_rads = orientation*3.1416/180;
		printf("- 偏移角度: %.1f\n\n", orientation);
		double diameter = sqrt((4*area)/3.1416);//直径
		double eccentricity = sqrt(1-pow(minorAxis/majorAxis,2));//离心率
		double roundness = pow(perimeter, 2)/(2*3.1416*area);//圆滑度
		line(drawing, Point(mc[i].x, mc[i].y), Point(mc[i].x+30*cos(orientation_rads), mc[i].y+30*sin(orientation_rads)), cvScalar(0,0,200), 3);
		char tam[100];
		sprintf(tam, "%.2f", orientation);
		putText(drawing, tam, Point(mc[i].x, mc[i].y), FONT_HERSHEY_SIMPLEX, 0.5, cvScalar(0,220,120),1.5);
	}
	namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
	imshow( "Contours", drawing );
	moveWindow("Contours",1100,20);
	waitKey(0);
	src.release();
	src_gray.release();
	return 0;
}
结果如下:

计算得到的第二个目标物面积和周长明显不正确,具体原因有待查找



  • 8
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值