2015-01-06 15:39:36 yang6464158 阅读数 15701
  • 大数据—Scala

    一、Scala核心编程课程简介 近年来随着大数据的兴起,大数据核心框架Spark和Kafka也受到到广泛关注, Spark底层是Scala实现的, 因此也带火了Scala语言,目前Scala被全球知名公司(如:谷歌、百度、阿里、Twitter、京东等)广泛用于Spark开发。新一代的物联网时代到来,会对大数据应用人才的需求越加紧迫。 尚硅谷网罗和整合了学员很喜爱的师资,打造出专注于Spark开发的Scala课程,我们讲解Scala核心编程技术,同时也讲解编程思想、设计模式和Scala底层实现,让您有豁然开朗的感受。 二、课程内容和目标 本课程重点讲解Scala核心编程,内容包括: Scala语言概述、运算符、程序流程控制、数据结构之集合、Map映射、过滤、化简、折叠、扫描、拉链、视图、并行集合、高阶函数、函数柯里化、偏函数、参数推断、控制抽象、Trait、面向对象编程、异常处理、惰性函数、Akka及Actor模型、Spark Master和Worker通讯、隐式转换、隐式参数、工厂模式、单例模式、观察者模式、装饰者模式、代理模式、泛型、上下界、视图界定、上下文界定、协变逆变不变和源码剖析。通过系统全面的学习,学员能掌握Scala编程思想和Scala底层机制,为进一步学习Spark打下坚实基础。 三、谁适合学 1.希望以较低的投入和风险,来了解自己是否适合转型从事Spark开发的求职人员。 2.有一定的Java基础,或者自学过一些Java书籍与视频资料,想系统掌握Scala语言的程序员。

    2508 人正在学习 去看看 缪传海

特征矩的知识在概率论和数理统计中有介绍,空间矩的方法在图像应用中比较广泛,包括零阶矩求面积、一阶矩确定重心、二阶矩确定主方向、二阶矩和三阶矩可以推导出七个不变矩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;
}
结果如下:

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



2017-04-20 13:56:22 chaipp0607 阅读数 10409
  • 大数据—Scala

    一、Scala核心编程课程简介 近年来随着大数据的兴起,大数据核心框架Spark和Kafka也受到到广泛关注, Spark底层是Scala实现的, 因此也带火了Scala语言,目前Scala被全球知名公司(如:谷歌、百度、阿里、Twitter、京东等)广泛用于Spark开发。新一代的物联网时代到来,会对大数据应用人才的需求越加紧迫。 尚硅谷网罗和整合了学员很喜爱的师资,打造出专注于Spark开发的Scala课程,我们讲解Scala核心编程技术,同时也讲解编程思想、设计模式和Scala底层实现,让您有豁然开朗的感受。 二、课程内容和目标 本课程重点讲解Scala核心编程,内容包括: Scala语言概述、运算符、程序流程控制、数据结构之集合、Map映射、过滤、化简、折叠、扫描、拉链、视图、并行集合、高阶函数、函数柯里化、偏函数、参数推断、控制抽象、Trait、面向对象编程、异常处理、惰性函数、Akka及Actor模型、Spark Master和Worker通讯、隐式转换、隐式参数、工厂模式、单例模式、观察者模式、装饰者模式、代理模式、泛型、上下界、视图界定、上下文界定、协变逆变不变和源码剖析。通过系统全面的学习,学员能掌握Scala编程思想和Scala底层机制,为进一步学习Spark打下坚实基础。 三、谁适合学 1.希望以较低的投入和风险,来了解自己是否适合转型从事Spark开发的求职人员。 2.有一定的Java基础,或者自学过一些Java书籍与视频资料,想系统掌握Scala语言的程序员。

    2508 人正在学习 去看看 缪传海

图像的hu矩是一种具有平移、旋转和尺度不变性的图像特征。

普通矩的计算:
f(x,y)的p+q阶原点矩可以表示为:
这里写图片描述

而数字图像是一个二维的离散信号,对上述公式进行离散化之后:
这里写图片描述
其中C与R分别表示图像的列与行。

各阶矩的物理意义:
0阶矩(m00):目标区域的质量
1阶矩(m01,m10):目标区域的质心
2阶矩(m02,m11,m20):目标区域的旋转半径
3阶矩(m03,m12,m21,m30):目标区域的方位和斜度,反应目标的扭曲

但是目标区域往往伴随着空间变换(平移,尺度,旋转),所以需要在普通矩的基础上构造出具备不变性的矩组—hu矩。

中心矩:构造平移不变性
由零阶原点矩和一阶原点矩,我们可以求得目标区域的质心坐标:
这里写图片描述
由求得的质心坐标,我们可以构造出中心矩:

这里写图片描述

由于我们选择了以目标区域的质心为中心构建中心矩,那么矩的计算时永远是目标区域中的点相对于目标区域的质心,而与目标区域的位置无关,及具备了平移不变性。

归一化中心矩:构造尺度不变性

为抵消尺度变化对中心矩的影响,利用零阶中心矩u00对各阶中心距进行归一化处理,得到归一化中心矩:

这里写图片描述

由上文可知,零阶矩表示目标区域的质量(面积),那么如果目标区域的尺度发生变化(缩小2倍),显然其零阶中心矩也会相应变小,使得矩具备尺度不变性。

hu矩:构造旋转不变性
利用二阶和三阶规格中心矩可以导出下面7个不变矩组(Φ1 Φ7),它们在图像平移、旋转和比例变化时保持不变。
这里写图片描述

2019-08-23 21:19:44 weixin_43917062 阅读数 78
  • 大数据—Scala

    一、Scala核心编程课程简介 近年来随着大数据的兴起,大数据核心框架Spark和Kafka也受到到广泛关注, Spark底层是Scala实现的, 因此也带火了Scala语言,目前Scala被全球知名公司(如:谷歌、百度、阿里、Twitter、京东等)广泛用于Spark开发。新一代的物联网时代到来,会对大数据应用人才的需求越加紧迫。 尚硅谷网罗和整合了学员很喜爱的师资,打造出专注于Spark开发的Scala课程,我们讲解Scala核心编程技术,同时也讲解编程思想、设计模式和Scala底层实现,让您有豁然开朗的感受。 二、课程内容和目标 本课程重点讲解Scala核心编程,内容包括: Scala语言概述、运算符、程序流程控制、数据结构之集合、Map映射、过滤、化简、折叠、扫描、拉链、视图、并行集合、高阶函数、函数柯里化、偏函数、参数推断、控制抽象、Trait、面向对象编程、异常处理、惰性函数、Akka及Actor模型、Spark Master和Worker通讯、隐式转换、隐式参数、工厂模式、单例模式、观察者模式、装饰者模式、代理模式、泛型、上下界、视图界定、上下文界定、协变逆变不变和源码剖析。通过系统全面的学习,学员能掌握Scala编程思想和Scala底层机制,为进一步学习Spark打下坚实基础。 三、谁适合学 1.希望以较低的投入和风险,来了解自己是否适合转型从事Spark开发的求职人员。 2.有一定的Java基础,或者自学过一些Java书籍与视频资料,想系统掌握Scala语言的程序员。

    2508 人正在学习 去看看 缪传海

图像的矩可以反应,一阶矩与形状有关,二阶矩显示曲线围绕直线平均值的扩展程度,三阶矩则是关于平均值的对称性的测量。由二阶矩和三阶矩可以导出一组共七个不变矩。不变矩是图像的统计特性,满足平移、伸缩、旋转均不变的不变性,在图像识别领域得到了广泛的应用。
1.矩的定义
矩本身是概率与统计中的概念,其本质是数学期望。在这里插入图片描述
对二维离散函数f(x,y),其(j+k)阶矩可以表示为:m(j,k)=x=0N1y=0M1xjykf(x,y)m\left ( j,k \right )=\sum_{x=0}^{N-1}\sum_{y=0}^{M-1}x^{j}y^{k}f\left ( x,y \right )。其中参数(j+k)为矩的阶次,如果只关心目标形状,可是目标区域为1,背景区域为0,这样零阶矩就可以表示目标的面积。
2.质心坐标及中心矩
m10指的是目标区域上所有x坐标的总和,m01指的是目标区域上所有y坐标的总和,这样二值图像中的质心坐标为:
在这里插入图片描述
在这里插入图片描述
为了消除原点矩对平移旋转的变化,把坐标原点平移到质心坐标位置,从而得到中心矩为:
在这里插入图片描述
中心矩和原点矩之间有一定的关系,其三阶以内的关系如下:
在这里插入图片描述
3.不变矩
利用三阶以内的归一化中心矩可以得到不变矩。
在这里插入图片描述

其MATLAB实现代码如下:

function inv_m7 = invariable_moment(in_image)
% 功能:计算图像的Hu的七个不变矩
% 输入:in_image-RGB图像
% 输出:inv_m7-七个不变矩

% 将输入的RGB图像转换为灰度图像   
% image=rgb2gray(in_image);
%将图像矩阵的数据类型转换成双精度型
image=double(in_image);
%%%=================计算 、 、 =========================
%计算灰度图像的零阶几何矩 
m00=sum(sum(image));     
m10=0;
m01=0;
[row,col]=size(image);
for i=1:row
    for j=1:col
        m10=m10+i*image(i,j);
        m01=m01+j*image(i,j);
    end
end
%%%=================计算 、 ================================
u10=m10/m00;
u01=m01/m00;
%%%=================计算图像的二阶几何矩、三阶几何矩============
m20 = 0;m02 = 0;m11 = 0;m30 = 0;m12 = 0;m21 = 0;m03 = 0;
for i=1:row
    for j=1:col
        m20=m20+i^2*image(i,j);
        m02=m02+j^2*image(i,j);
        m11=m11+i*j*image(i,j);
        m30=m30+i^3*image(i,j);
        m03=m03+j^3*image(i,j);
        m12=m12+i*j^2*image(i,j);
        m21=m21+i^2*j*image(i,j);
    end
end
%%%=================计算图像的二阶中心矩、三阶中心矩============
y00=m00;
y10=0;
y01=0;
y11=m11-u01*m10;
y20=m20-u10*m10;
y02=m02-u01*m01;
y30=m30-3*u10*m20+2*u10^2*m10;
y12=m12-2*u01*m11-u10*m02+2*u01^2*m10;
y21=m21-2*u10*m11-u01*m20+2*u10^2*m01;
y03=m03-3*u01*m02+2*u01^2*m01;
%%%=================计算图像的归格化中心矩====================
        n20=y20/m00^2;
        n02=y02/m00^2;
        n11=y11/m00^2;
        n30=y30/m00^2.5;
        n03=y03/m00^2.5;
        n12=y12/m00^2.5;
        n21=y21/m00^2.5;
%%%=================计算图像的七个不变矩======================
h1 = n20 + n02;                      
h2 = (n20-n02)^2 + 4*(n11)^2;
h3 = (n30-3*n12)^2 + (3*n21-n03)^2;  
h4 = (n30+n12)^2 + (n21+n03)^2;
h5=(n30-3*n12)*(n30+n12)*((n30+n12)^2-3*(n21+n03)^2)+(3*n21-n03)*(n21+n03)*(3*(n30+n12)^2-(n21+n03)^2);
h6 = (n20-n02)*((n30+n12)^2-(n21+n03)^2)+4*n11*(n30+n12)*(n21+n03);
h7=(3*n21-n03)*(n30+n12)*((n30+n12)^2-3*(n21+n03)^2)+(3*n12-n30)*(n21+n03)*(3*(n30+n12)^2-(n21+n03)^2);
 
inv_m7= [h1 h2 h3 h4 h5 h6 h7]; 
2016-07-15 18:17:37 boon_228 阅读数 7307
  • 大数据—Scala

    一、Scala核心编程课程简介 近年来随着大数据的兴起,大数据核心框架Spark和Kafka也受到到广泛关注, Spark底层是Scala实现的, 因此也带火了Scala语言,目前Scala被全球知名公司(如:谷歌、百度、阿里、Twitter、京东等)广泛用于Spark开发。新一代的物联网时代到来,会对大数据应用人才的需求越加紧迫。 尚硅谷网罗和整合了学员很喜爱的师资,打造出专注于Spark开发的Scala课程,我们讲解Scala核心编程技术,同时也讲解编程思想、设计模式和Scala底层实现,让您有豁然开朗的感受。 二、课程内容和目标 本课程重点讲解Scala核心编程,内容包括: Scala语言概述、运算符、程序流程控制、数据结构之集合、Map映射、过滤、化简、折叠、扫描、拉链、视图、并行集合、高阶函数、函数柯里化、偏函数、参数推断、控制抽象、Trait、面向对象编程、异常处理、惰性函数、Akka及Actor模型、Spark Master和Worker通讯、隐式转换、隐式参数、工厂模式、单例模式、观察者模式、装饰者模式、代理模式、泛型、上下界、视图界定、上下文界定、协变逆变不变和源码剖析。通过系统全面的学习,学员能掌握Scala编程思想和Scala底层机制,为进一步学习Spark打下坚实基础。 三、谁适合学 1.希望以较低的投入和风险,来了解自己是否适合转型从事Spark开发的求职人员。 2.有一定的Java基础,或者自学过一些Java书籍与视频资料,想系统掌握Scala语言的程序员。

    2508 人正在学习 去看看 缪传海

一 原理

    几何矩是由Hu(Visual pattern recognition by moment invariants)1962年提出的,具有平移、旋转和尺度不变性。 定义如下:

① (p+q)阶不变矩定义

② 对于数字图像,离散化,定义为

 

③ 归一化中心矩定义

④Hu矩定义

 



//#################################################################################//
double M[7] = {0};        //HU不变矩
bool HuMoment(IplImage* img)
{
int bmpWidth = img->width;
int bmpHeight = img->height;
int bmpStep = img->widthStep; 
int bmpChannels = img->nChannels;
    uchar*pBmpBuf = (uchar*)img->imageData;

double m00=0,m11=0,m20=0,m02=0,m30=0,m03=0,m12=0,m21=0;  //中心矩 
double x0=0,y0=0;    //计算中心距时所使用的临时变量(x-x') 
double u20=0,u02=0,u11=0,u30=0,u03=0,u12=0,u21=0;//规范化后的中心矩
//double M[7];    //HU不变矩 
double t1=0,t2=0,t3=0,t4=0,t5=0;//临时变量, 
//double Center_x=0,Center_y=0;//重心 
int Center_x=0,Center_y=0;//重心 
int i,j;            //循环变量

//  获得图像的区域重心(普通矩)
double s10=0,s01=0,s00=0;  //0阶矩和1阶矩  
for(j=0;j<bmpHeight;j++)//y
    {
for(i=0;i<bmpWidth;i++)//x
        {
            s10+=i*pBmpBuf[j*bmpStep+i];
            s01+=j*pBmpBuf[j*bmpStep+i];
            s00+=pBmpBuf[j*bmpStep+i];
        }
    }
    Center_x=(int)(s10/s00+0.5);
    Center_y=(int)(s01/s00+0.5);

//  计算二阶、三阶矩(中心矩)
    m00=s00; 
for(j=0;j<bmpHeight;j++) 
    {
for(i=0;i<bmpWidth;i++)//x 
        { 
            x0=(i-Center_x); 
            y0=(j-Center_y); 
            m11+=x0*y0*pBmpBuf[j*bmpStep+i]; 
            m20+=x0*x0*pBmpBuf[j*bmpStep+i]; 
            m02+=y0*y0*pBmpBuf[j*bmpStep+i]; 
            m03+=y0*y0*y0*pBmpBuf[j*bmpStep+i];
            m30+=x0*x0*x0*pBmpBuf[j*bmpStep+i]; 
            m12+=x0*y0*y0*pBmpBuf[j*bmpStep+i]; 
            m21+=x0*x0*y0*pBmpBuf[j*bmpStep+i]; 
        } 
    } 

//  计算规范化后的中心矩: mij/pow(m00,((i+j+2)/2)
    u20=m20/pow(m00,2); 
    u02=m02/pow(m00,2); 
    u11=m11/pow(m00,2);
    u30=m30/pow(m00,2.5); 
    u03=m03/pow(m00,2.5);
    u12=m12/pow(m00,2.5); 
    u21=m21/pow(m00,2.5);

//  计算中间变量
    t1=(u20-u02); 
    t2=(u30-3*u12); 
    t3=(3*u21-u03); 
    t4=(u30+u12);
    t5=(u21+u03);

//  计算不变矩 
    M[0]=u20+u02; 
    M[1]=t1*t1+4*u11*u11; 
    M[2]=t2*t2+t3*t3; 
    M[3]=t4*t4+t5*t5;
    M[4]=t2*t4*(t4*t4-3*t5*t5)+t3*t5*(3*t4*t4-t5*t5); 
    M[5]=t1*(t4*t4-t5*t5)+4*u11*t4*t5;
    M[6]=t3*t4*(t4*t4-3*t5*t5)-t2*t5*(3*t4*t4-t5*t5);

returntrue;
}

下面的代码计算轮廓的矩,并根据1阶中心矩得到轮廓的质心,代码如下:

src = imread( "../star1.jpg" ,1 );

/// Convert image to gray and blur it
cvtColor( src, src_gray, CV_BGR2GRAY );
blur( src_gray, src_gray, Size(3,3) );

namedWindow( "image", CV_WINDOW_AUTOSIZE );
imshow( "image", src );

Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;

//利用canny算法检测边缘
Canny( src_gray, canny_output, thresh, thresh*2, 3 );
namedWindow( "canny", CV_WINDOW_AUTOSIZE );
imshow( "canny", canny_output );
//查找轮廓
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] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); }

//画轮廓及其质心
Mat drawing = Mat::zeros( canny_output.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() );
    circle( drawing, mc[i], 4, color, -1, 8, 0 );
    }

namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
imshow( "Contours", drawing );

//打印轮廓面积和轮廓长度
printf("\t Info: Area and Contour Length \n");
for( int i = 0; i< contours.size(); i++ )
    {
    printf(" * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f \n", i, mu[i].m00, contourArea(contours[i]), arcLength( contours[i], true ) );
    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 );
    }


2018-01-20 20:14:17 KYJL888 阅读数 2193
  • 大数据—Scala

    一、Scala核心编程课程简介 近年来随着大数据的兴起,大数据核心框架Spark和Kafka也受到到广泛关注, Spark底层是Scala实现的, 因此也带火了Scala语言,目前Scala被全球知名公司(如:谷歌、百度、阿里、Twitter、京东等)广泛用于Spark开发。新一代的物联网时代到来,会对大数据应用人才的需求越加紧迫。 尚硅谷网罗和整合了学员很喜爱的师资,打造出专注于Spark开发的Scala课程,我们讲解Scala核心编程技术,同时也讲解编程思想、设计模式和Scala底层实现,让您有豁然开朗的感受。 二、课程内容和目标 本课程重点讲解Scala核心编程,内容包括: Scala语言概述、运算符、程序流程控制、数据结构之集合、Map映射、过滤、化简、折叠、扫描、拉链、视图、并行集合、高阶函数、函数柯里化、偏函数、参数推断、控制抽象、Trait、面向对象编程、异常处理、惰性函数、Akka及Actor模型、Spark Master和Worker通讯、隐式转换、隐式参数、工厂模式、单例模式、观察者模式、装饰者模式、代理模式、泛型、上下界、视图界定、上下文界定、协变逆变不变和源码剖析。通过系统全面的学习,学员能掌握Scala编程思想和Scala底层机制,为进一步学习Spark打下坚实基础。 三、谁适合学 1.希望以较低的投入和风险,来了解自己是否适合转型从事Spark开发的求职人员。 2.有一定的Java基础,或者自学过一些Java书籍与视频资料,想系统掌握Scala语言的程序员。

    2508 人正在学习 去看看 缪传海

参考 http://blog.csdn.net/yang6464158/article/details/42459595

特征矩的知识在概率论和数理统计中有介绍,空间矩的方法在图像应用中比较广泛,包括零阶矩求面积、一阶矩确定重心、二阶矩确定主方向、二阶矩和三阶矩可以推导出七个不变矩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代码如下所示:
[cpp] view plain copy
  1. #include <opencv2/opencv.hpp>  
  2. using namespace cv;  
  3. using namespace std;  
  4. Mat src; Mat src_gray;  
  5. int thresh = 30;  
  6. int max_thresh = 255;  
  7. RNG rng(12345);  
  8. int main(){  
  9.     src = imread( "opencv-logo.png" ,CV_LOAD_IMAGE_COLOR );  
  10.     cvtColor( src, src_gray, CV_BGR2GRAY );//灰度化  
  11.     GaussianBlur( src, src, Size(3,3), 0.1, 0, BORDER_DEFAULT );  
  12.     blur( src_gray, src_gray, Size(3,3) ); //滤波  
  13.     namedWindow( "image", CV_WINDOW_AUTOSIZE );  
  14.     imshow( "image", src );  
  15.     moveWindow("image",20,20);  
  16.     //定义Canny边缘检测图像  
  17.     Mat canny_output;  
  18.     vector<vector<Point> > contours;  
  19.     vector<Vec4i> hierarchy;  
  20.     //利用canny算法检测边缘  
  21.     Canny( src_gray, canny_output, thresh, thresh*3, 3 );  
  22.     namedWindow( "canny", CV_WINDOW_AUTOSIZE );  
  23.     imshow( "canny", canny_output );  
  24.     moveWindow("canny",550,20);  
  25.     //查找轮廓  
  26.     findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );  
  27.     //计算轮廓矩  
  28.     vector<Moments> mu(contours.size() );  
  29.     forint i = 0; i < contours.size(); i++ )  
  30.     { mu[i] = moments( contours[i], false ); }  
  31.     //计算轮廓的质心  
  32.     vector<Point2f> mc( contours.size() );  
  33.     forint i = 0; i < contours.size(); i++ )  
  34.     { mc[i] = Point2d( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); }  
  35.   
  36.     //画轮廓及其质心并显示  
  37.     Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );  
  38.     printf("\t\t 几何特性\n");  
  39.     forint i = 0; i< contours.size(); i++ )  
  40.     {  
  41.         Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );  
  42.         drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );  
  43.         circle( drawing, mc[i], 4, color, -1, 8, 0 );         
  44.         rectangle(drawing, boundingRect(contours.at(i)), cvScalar(0,255,0));  
  45.         printf("目标%d - 面积:%.2f - 周长: %.2f ", i, mu[i].m00, contourArea(contours[i]), arcLength( contours[i], true ) );  
  46.         RotatedRect r = fitEllipse(contours.at(i));  
  47.         double majorAxis = r.size.height > r.size.width ? r.size.height : r.size.width;//长轴大小  
  48.         double minorAxis = r.size.height > r.size.width ? r.size.width : r.size.height;//短轴大小  
  49.         double area = mu[i].m00;//面积  
  50.         int perimeter = arcLength(contours.at(i), true);  
  51.         double orientation = r.angle;  
  52.         double orientation_rads = orientation*3.1416/180;  
  53.         printf("- 偏移角度: %.1f\n\n", orientation);  
  54.         double diameter = sqrt((4*area)/3.1416);//直径  
  55.         double eccentricity = sqrt(1-pow(minorAxis/majorAxis,2));//离心率  
  56.         double roundness = pow(perimeter, 2)/(2*3.1416*area);//圆滑度  
  57.         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);  
  58.         char tam[100];  
  59.         sprintf(tam, "%.2f", orientation);  
  60.         putText(drawing, tam, Point(mc[i].x, mc[i].y), FONT_HERSHEY_SIMPLEX, 0.5, cvScalar(0,220,120),1.5);  
  61.     }  
  62.     namedWindow( "Contours", CV_WINDOW_AUTOSIZE );  
  63.     imshow( "Contours", drawing );  
  64.     moveWindow("Contours",1100,20);  
  65.     waitKey(0);  
  66.     src.release();  
  67.     src_gray.release();  
  68.     return 0;  
  69. }  
结果如下:

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








矩、中心矩、质心、patch方向

author@jason_ql 
http://blog.csdn.net/lql0716


1、几何矩理论

1.1 矩与数学期望

  • 数学期望

定义(一维离散):设X[a,b],密度为f(x),数学期望为: 

E(X)=i=1xiP(xi)


定义(一维连续):设X为连续型随机变量,其概率密度为f(x),则X的数学期望为: 

E(X)=+xf(x)dx

注:假定广义积分绝对收敛,即+|x|f(x)dx存在


定义(二维离散):对于离散变量(X,Y)P(xi,yi)PX(xi)=jP(xi,yj)期望为:

E(X)=ixiPX(xi)=jixiP(xi,yj)

E(Y)=jyjPY(yj)=jiyjP(xi,yj)


定义(二维连续):连续变量(X,Y)f(x,y)

fX(x)=+f(x,y)dy

E(X)=+xfX(x)dx=+x(+f(x,y)dy)dx=++xf(x,y)dxdy

E(Y)=+yfY(y)dy=++yf(x,y)dxdy

  • 原点矩

定义1:设X是随机变量,则称νk(X)=E(Xk)Xk原点矩

X是离散型随机变量,则:

νk(X)=ixkip(xi)

X是连续型随机变量,则:

νk(X)=+xkf(x)dx

  • 中心距

定义2:设X是随机变量,则称 

μk(X)=E(XE(X))k
Xk中心距

X是离散型随机变量,则: 

μk(X)=i(xiE(X))kp(xi)

X是连续型随机变量,则: 

μk(X)=+(xE(X))kf(x)dx

  • 原点矩与中心距

当中心距中的E(X)为0时,此时为k阶原点矩,即原点矩是中心距的特殊情况。

一阶原点矩就是数学期望,二阶中心距就是方差,在实际中常用低阶矩,高于四阶矩极少使用。

原点矩与中心距的关系式: 

μ2=ν2ν21

μ3=ν33ν2ν1+2ν31

μ4=ν44ν3ν1+6ν2ν213ν41

以上可对μr用组合数拆开得到。

1.2 图像的矩

把图像的像素看做密度函数f(x,y),对该像素点求期望E,即是图像的矩(原点矩)。具体的求解过程参看下面第2节。

一般来说,一阶矩零阶矩可以计算某个形状的重心,二阶矩可以计算形状的方向。

图像的矩主要表征了图像区域的几何特征,又称几何矩,由于具有旋转、平移、尺度等不变的特兴奋,所以又称为不变矩。 
利用不变矩可以计算出物体的圆形度(物体形状和园的接近程度)、物体的矩形度(物体形状和矩形的接近程度)、物体的水平和垂直对称性、物体的主轴方向、扁度等。

  • 原点矩: 

    mpq=x1My1Nxpyqf(x,y)

  • 中心距: 

    μpq=x1My1N(xx0)p(yy0)qf(x,y)

  • 归一化中心距: 

    ηpq=μpqμr00

    其中r=p+q+22,p+q=2,3,...

  • 一阶矩: 
    见下面第2节.

  • 二阶矩:

M20=xyx2I(x,y)

M02=xyy2I(x,y)

M11=xyxyI(x,y)

M20M02分别表示图像围绕通过重心的垂直和水平轴线的惯性矩。 
M30M03可以度量图像对于垂直和水平轴线的对称性等。

物体形状的方向: 

θ=arctan(b,(ac))2=arctan(b/(ac))2,θ[9090]

其中:

根据一阶矩的质心C=(M10M00,M01M00)

a=M20M00C20

b=2(M11M00C0C1)

c=M02M00C21

2、质心原理

在图像处理中,一阶矩与形状有关,二阶矩显示曲线围绕直线平均值到扩展程度,三阶矩是关于平均值到对称性到测量.由二阶矩和三阶矩可以导出一组共7个不变矩.而不变矩是图像到统计特性,满足平移,伸缩,旋转均不变到不变性.

  • moments of a patch(矩): 

    mpq=x=r,y=rrxpyqI(x,y)(1)

  • 角点为中心: 

    m00=x=r,y=rrx0y0I(x,y)=x=r,y=rrI(x,y)(1-1)

  • 一阶矩m01

    m01=x=r,y=rrx0y1I(x,y)=x=r,y=rryI(x,y)(1-2)

  • 一阶矩m10

    m10=x=r,y=rrx1y0I(x,y)=x=r,y=rrxI(x,y)(1-3)

  • centroid(质心,亦可称为重心): 

    C=(m10m00,m01m00)(2)

    计算质心的优势:对噪声不敏感。当有外部噪声干扰时,计算出的质心不会偏离太大。从数学的角度来看,这种方法是计算一个连通域的质心(或一个团块儿blob的质心)。

  • 构造一个向量OC,从角点中心O到质心C

  • orientation of patch(方向): 

    θ=atan2(m01,m10)(3)

    建立以角点为圆心的坐标系,如图这里写图片描述 
    在图中,P为角点,园内为取点区域,每个方格代表一个像素。 
    则质心Q可根据式(2)求得。


3、中心距函数moments()

Calculates all of the moments up to the third order of a polygon or rasterized shape.

C++: Moments moments(InputArray array, bool binaryImage=false )

Python: cv2.moments(array[, binaryImage]) → retval

C: void cvMoments(const CvArr* arr, CvMoments* moments, int binary=0 )

Python: cv.Moments(arr, binary=0) → moments

Parameters: 
array – Raster image (single-channel, 8-bit or floating-point 2D array) or an array ( 1 \times N or N \times 1 ) of 2D points (Point or Point2f ). 
binaryImage – If it is true, all non-zero image pixels are treated as 1’s. The parameter is used for images only. 
moments – Output moments.

  • The function computes moments, up to the 3rd order, of a vector shape or a rasterized shape. The results are returned in the structure Moments defined as:
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

4、中心矩示例代码

  • opencv2.4.13

4.1 C++版代码

#include <QCoreApplication>
#include <opencv2/opencv.hpp>
//  Qt Creator 4.2.0(Based on Qt 5.7.1)
//  OpenCV 2.4.13
using namespace cv;
using namespace std;

#define name1 "原图"
#define name2 "效果图"

cv::Mat img, gray;
int nThresh = 100;
int nMaxThresh = 255;
cv::RNG rng(12345);  //产生一个随机数
cv::Mat cannyImg;
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;

//void on_ThreshChange( int, void* ){
//    //canny边缘检测
//    cv::Canny( gray, cannyImg, nThresh, nThresh*2, 3 );
//    //找轮廓
//    cv::findContours( cannyImg, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point( 0, 0 ) );
//    //计算矩
//    std::vector<cv::Moments> mu( contours.size() );
//    for(unsigned int i = 0; i < contours.size(); i++){
//        mu[i] = cv::moments( contours[i], false);
//    }
//    //计算中心矩
//    std::vector<cv::Point2f> mc( contours.size() );
//    for( unsigned int i = 0; i < contours.size(); i++ ){
//        mc[i] = cv::Point2f( static_cast<float>(mu[i].m10 / mu[i].m00), static_cast<float>(mu[i].m01 / mu[i].m00));
//    }
//    //画轮廓
//    cv::Mat drawing = cv::Mat::zeros( cannyImg.size(), CV_8UC3);
//    for( unsigned int i = 0; i < contours.size(); i++ ){
//        cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
//        cv::drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
//        cv::circle( drawing, mc[i], 4, color, -1, 8, 0 );
//    }

//    cv::namedWindow( name2, cv::WINDOW_NORMAL);
//    cv::imshow( name2, drawing );

//    std::cout << "输出内容: 面积和轮廓长度 \n" << std::endl;
//    for(unsigned int i = 0; i < contours.size(); i++ ){
//        std::cout << ">通过m00计算出轮廓[" << i << "]的面积:(M_00) =" << mu[i].m00 << "\n OpenCV 函数计算出的面积 = " << cv::contourArea(contours[i]) << "长度:" << cv::arcLength( contours[i], true) <<  "\n\n" << std::endl;
//        cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
//        cv::drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
//        cv::circle( drawing, mc[i], 4, color, -1, 8, 0 );
//     }
//}


int main(){
    img = cv::imread( "/home/jason/jason2/photo/1.jpg" );
    cv::cvtColor( img, gray, cv::COLOR_BGR2GRAY );
    cv::blur( gray, gray, cv::Size(3, 3) );

    cv::namedWindow( name1, cv::WINDOW_NORMAL );
    cv::imshow( name1, img );

//    cv::createTrackbar( "阈值", name1, &nThresh, nMaxThresh, on_ThreshChange );
//    on_ThreshChange( 0, 0 );

    //canny边缘检测
    cv::Canny( gray, cannyImg, nThresh, nThresh*2, 3 );
    //找轮廓
    cv::findContours( cannyImg, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point( 0, 0 ) );
    //计算矩
    std::vector<cv::Moments> mu( contours.size() );
    for(unsigned int i = 0; i < contours.size(); i++){
        mu[i] = cv::moments( contours[i], false);
    }
    //计算中心矩
    std::vector<cv::Point2f> mc( contours.size() );
    for( unsigned int i = 0; i < contours.size(); i++ ){
        mc[i] = cv::Point2f( static_cast<float>(mu[i].m10 / mu[i].m00), static_cast<float>(mu[i].m01 / mu[i].m00));
    }
    //画轮廓
    cv::Mat drawing = cv::Mat::zeros( cannyImg.size(), CV_8UC3);
    for( unsigned int i = 0; i < contours.size(); i++ ){
        cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
        cv::drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
        cv::circle( drawing, mc[i], 4, color, -1, 8, 0 );
    }

    cv::namedWindow( name2, cv::WINDOW_NORMAL);
    cv::imshow( name2, drawing );

    std::cout << "输出内容: 面积和轮廓长度 \n" << std::endl;
    for(unsigned int i = 0; i < contours.size(); i++ ){
        std::cout << ">通过m00计算出轮廓[" << i << "]的面积:(M_00) =" << mu[i].m00 << "\n OpenCV 函数计算出的面积 = " << cv::contourArea(contours[i]) << "长度:" << cv::arcLength( contours[i], true) <<  "\n\n" << std::endl;
        cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
        cv::drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
        cv::circle( drawing, mc[i], 4, color, -1, 8, 0 );
     }
    cv::waitKey(0);

    return 0;
}

  • 原图: 

这里写图片描述

效果图: 
这里写图片描述

部分打印结果: 
这里写图片描述


4.2 Python版代码

# -*- coding: utf-8 -*-
"""
Created on Sun Mar 26 18:36:19 2017

@author: lql0716
"""

import cv2
import numpy as np

nThresh = 100
nMaxThresh = 255

img = cv2.imread('D:/photo/04.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray = cv2.blur(gray, (3,3))
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.imshow('img', img)

cannyImg = cv2.Canny(gray, nThresh, nThresh*2, 3)
contours, hierarchy = cv2.findContours(cannyImg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

mu = []
mc = []
retval = np.array([])
for i in range(0, np.array(contours).shape[0]):
    retval = cv2.moments(contours[i], False)
    mu.append(retval)
mu = np.array(mu)
#print mu[0]['m10']
thetas = []
for i in range(0, np.array(contours).shape[0]):
    if mu[i]['m00'] == 0.0:
        a=0
        b=0
    else:
        a = mu[i]['m10'] / mu[i]['m00']  #质心x坐标
        b = mu[i]['m01'] / mu[i]['m00']  #质心y坐标

        #根据二阶矩计算物体形状的方向
        r1 = mu[i]['m20'] / mu[i]['m00'] - a*a
        r2 = 2.0*(mu[i]['m11'] / mu[i]['m00'] - a*b)
        r3 = mu[i]['m02'] / mu[i]['m00'] - b*b
#        print r1-r3
        if r1-r3==0:
            theta = np.pi / 2
        else:
            theta = np.arctan(r2/(r1-r3)) / 2
        thetas.append(theta)
    mc.append([a,b])
mc = np.array(mc)
drawing = np.zeros(img.shape, dtype = np.uint8)
for i in range(0, mc.shape[0]):
    c1 = np.random.randint(0, 256)
    c2 = np.random.randint(0, 256)
    c3 = np.random.randint(0, 256)
    cv2.drawContours(drawing, contours, i, (c1, c2, c3), 2, 8)
    cv2.circle(drawing, (int(round(mc[i][0])), int(round(mc[i][1]))), 4, (c1, c2, c3), -1, 8, 0)
cv2.namedWindow('img2', cv2.WINDOW_NORMAL)
cv2.imshow('img2', drawing)   
cv2.waitKey(0) 

原图: 
这里写图片描述

效果图: 
这里写图片描述

5、Hu矩HuMoments()

原点矩:

mpq=x1My1Nxpyqf(x,y)

中心距:

μpq=x1My1N(xx0)p(yy0)qf(x,y)

归一化中心距:

ηpq=μpqμr00

其中r=p+q+22,p+q=2,3,...

当图像变化时,mpq也变化,而μpq具有平移不变形,但对旋转依然敏感。如果用归一化中心距,则可以保持平移不变性、伸缩不变性。

Hu矩利用二阶、三阶中心距构造了7个不变矩,它们在连续图像条件下可保持平移、旋转、伸缩不变,公式如下: 
M1=η20+η02 
M2=(η20η02)2+4η211 
M3=(η303η12)2+(3η21η03)2 
M4=(η30+η12)2+(η21+η03)2 
M5=(η303η12)(η30+η12)((η30+η12)23(η21+η03)2)+(3η21η03)(η21+η03)(3(η30+η12)2(η21+η03)2)
M6=(η20η02)((η30+η12)2(η21+η03)2)+4η11(η30+η12)(η21+η03) 
M7=(3η21η03)(η30+η12)((η30+η12)23(η21+η03)2)(η303η12)(η21+η03)(3(η30+η12)2(η21+η03)2)

以下公式为官方文档的公式: 
此处输入图片的描述

在对图片识别过程中,只有M1M2不变性保持的比较好,其他几个不变矩带来的误差比较大,有学者认为只有基于二阶矩的不变矩对二维物体的描述才真正具有旋转,缩放,平移不变性(M1M2均为二阶矩构成).

Hu矩的优势:速度快 
Hu矩的劣势:识别率低

Hu矩一般用来识别图像中大的物体,对物体的形状描述得比较好,图像的纹理特征不能太复杂,如识别水果形状或车牌字符的效果较好.


  • 参考: 

1、图像的几何矩浅析 
2、opencv官方文档-moments() 
3、形状描述与识别 
4、第三章 二值图像分



图像处理中二阶矩不变性 相关内容