Zernike矩

Zernike在1934年引入了一组定义在单位圆 上的复值函数集{ },{ }具有完备性和正交性,使得它可以表示定义在单位圆盘内的任何平方可积函数。其定义为:

 表示原点到点 的矢量长度; 表示矢量 与 轴逆时针方向的夹角。

 是实值径向多项式:

称为Zernike多项式。

Zernike多项式满足正交性:

其中

 为克罗内克符号, 

 是 的共轭多项式。

由于Zernike多项式的正交完备性,所以在单位圆内的任何图像 都可以唯一的用下面式子来展开:

式子中 就是Zernike矩,其定义为:

注意式子中 和 采用的是不同的坐标系( 采用直角坐标,而 采用的极坐标系,在计算的时候要进行坐标转换)

对于离散的数字图像,可将积分形式改为累加形式:

 

我们在计算一副图像的Zernike矩时,必须将图像的中心移到坐标的原点,将图像的像素点映射到单位圆内,由于Zernike矩具有旋转不变性,我们可以将 作为图像的不变特征,其中图像的低频特征有p值小的提取,高频特征由p值高的 提取。从上面可以看出,Zernike矩可以构造任意高阶矩。

由于Zernike矩只具有旋转不变性,不具有平移和尺度不变性,所以要提前对图像进行归一化,我们采用标准矩的方法来归一化一副图像,标准矩定义为:

 ,

由标准矩我们可以得到图像的"重心",

我们将图像的"重心"移动到单位圆的圆心(即坐标的原点),便解决了平移问题。

我们知道 表征了图像的"面积",归一图像的尺度无非就是把他们的大小变为一致的,(这里的大小指的是图像目标物的大小,不是整幅图像的大小,"面积"也是目标物的"面积")。

所以,对图像进行变换 就可以达到图像尺寸一致的目的。

综合上面结果,对图像进行 变换,最终图像 的Zernike矩就是平移,尺寸和旋转不变的。


     Zernike 不变矩相比 Hu 不变矩识别效果会好一些,因为他描述了图像更多的细节内容,特别是高阶矩,但是由于Zernike 不变矩计算时间比较长,所以出现了很多快速的算法,大家可以 google 一下。

    用 Zernike 不变矩来识别手势轮廓,识别率大约在 40%~50% 之间,跟 Hu 不变矩一样, Zernike 不变矩一般用来描述目标物形状占优势的图像,不适合用来描述纹理丰富的图像,对于纹理图像,识别率一般在 20%~30% 左右,很不占优势。


ZernikeMoment.h文件代码:

#pragma once
#include <iostream>  
#include "opencv2/opencv.hpp"  
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"




using namespace cv;
using namespace std;


#define MaxP(x,y) (float)(x>y?x:y)
#define MinP(x,y) (float)(x<y?x:y)
#define PI  3.14






typedef struct 
{
float    rou;
float    sigma;
}
RS_POINT;


typedef struct 
{
float    x;
float    y;
}CARTESIAN_POINT;




typedef struct 
{
float    r;
float    t;
}POLAR_POINT;




typedef struct 
{
float    Cnm;
float    Snm;
}ZERNIKE;




class ZernikeMoment{


    private:
  IplImage*    oriImg; 
       IplImage*    grayImg;
  float        Cnm,Snm;
       float        Z_mode;




public:

ZernikeMoment();
~ZernikeMoment();


        float               getRpqr(float p,float q,float r);
   RS_POINT*           get_rou_sigma(float x,float y);
CARTESIAN_POINT*    get_x_y(float r,float s,int N);
        
int                 Img2Gray(void);
void                 ClearOpenCV(void);


float               get_8_XYValue(int x,int y);
float               get_32_XYValue(int x,int y);


        float               getZernike(int n,int m);
        

void Caculate_8_Zernike(int n,int m);
void Caculate_32_Zernike(int n,int m);


};


Zernike矩.cpp文件中的代码:

// ZernikeTest.cpp : 定义控制台应用程序的入口点。
/************************************************************
读入图像是在RGB2GRAY函数中,自己修改对应图像的路径
*************************************************************/
#include "stdafx.h"
#include <math.h>
#include "ZernikeMoment.h"
//#include "cv.h"
//#include "highgui.h" 
//#include <opencv2/opencv.hpp>
#include <iostream>  
using namespace std;
//using namespace cv;


double factorial(long n)//////////////
{
if(n < 0)
return(0.0) ;
if(n == 0)
return(1.0) ;
else
return(n * factorial(n-1)) ;
}


float zernikeR(int n, int l, float r)/////////////////
{
  int m ;
  float sum = 0.0 ;
  
  if( ((n-l) % 2) != 0 )
    {
      cout <<"zernikeR(): improper values of n,l\n" ;
      return(0) ;
    }
  
  for(m = 0; m <= (n-l)/2; m++)
    {
      sum += (pow((float)-1.0,(float)m)) * ( factorial(n-m) ) / 
( factorial(m) * (factorial((n - 2*m + l) / 2)) *
 (factorial((n - 2*m - l) / 2)) ) *
( pow((float)r, (float)(n - 2*m)) );
    }
  
  return(sum) ;
}




ZernikeMoment::ZernikeMoment()
{


oriImg    = NULL;


grayImg   = NULL;


Z_mode    = 0;


}






ZernikeMoment::~ZernikeMoment()
{


}






//Fast Compute  Zernike Polynomials Rpq(r)
float ZernikeMoment::getRpqr(float p,float q,float r)//////////////////////
{
   /****  verify that p-q is even  ***




**********************************/
     float Rpqr = 0;




float Bppp=1;




int times   = (p-q)/2;
     int numbers = times+1;




     float  Bpqp  =   pow((p+q)/(p-q+2),times);   
     float* Bpqk = new float[numbers];
     
     Bpqk[0] = Bpqp;


      int k=(int)p;
 int t=(int)p;
      


//Bpqk[0] is Bpqp , Bpqk[1] is Bpq(p-2) ... Bpqk[numbers-1] is Bpqq respectively


 for(int i=0;i<(numbers-1);i++)
 {
          float  coeff = ((k+q)*(k-q))  /  ((k+p)*(p-k+2));
 Bpqk[i+1]   =  (-1)*(Bpqk[i])*coeff;
 k=k-2;
 }
      




 int temp = numbers-1;
     
 
 //Compute Rpqr
 for(k=(int)q;k<=t;k=k+2)
 {
          
 Rpqr = Rpqr + (Bpqk[temp])*(pow(r,k));


          temp--;
 }




 delete[]  Bpqk;
    
      float  a = Rpqr;
      float  b = Rpqr;


 return Rpqr;


}




//Ordinary Compute Rpqr






//convert to  rou, sigma coordinate,this function was never used.
RS_POINT* ZernikeMoment::get_rou_sigma(float x,float y)
{
    
    RS_POINT   *rs_p = new RS_POINT();


    float  rou   = MaxP(abs(x),abs(y));

float  sigma;




    if(abs(x)==rou)
     
sigma = (2*(rou-x)*y)/abs(y)+x*y/rou;


    if(abs(y)==rou)
         
sigma = 2*y -x*y/rou;
  
    rs_p->rou   = rou;
    rs_p->sigma = sigma;
  


return  rs_p;


}




//Convert rou-sigma to x-y coordinate,also never used.
CARTESIAN_POINT* ZernikeMoment::get_x_y(float rou,float sigma,int N)
{
     



float r  =  2*rou/N;
    float t  =  (PI*sigma)/4*rou;
     
CARTESIAN_POINT *xy_point = new CARTESIAN_POINT();


xy_point->x = r*cos(t);
xy_point->y = r*sin(t);


      return  xy_point;
}










//Get the x,y pixel value of Image ,8 depths
float ZernikeMoment::get_8_XYValue(int x,int y)//////////////////
{
int height      = grayImg->height;
    int widthStep   = grayImg->widthStep;
char*  Data     = grayImg->imageData;


    uchar  c_value =  ((uchar *)(Data+x*widthStep))[y];

float  value = (float)c_value;


    return value;




}
//Get the x,y pixel value of Image ,32 depths
float ZernikeMoment::get_32_XYValue(int x,int y)/////////////////
{
int height      = grayImg->height;
    int widthStep   = grayImg->widthStep;
char*  Data     = grayImg->imageData;


    float  value =  ((float *)(Data+x*widthStep))[y];


    return value;




}










//RGB to Gray
//本函数部分代码被注释了,因为改为直接读入灰度图了
int  ZernikeMoment::Img2Gray(void)/////////////
{
    




int a =1;
 //if((oriImg = cvLoadImage("E:\\XH.jpg", 1)) != 0   )
    if((grayImg = cvLoadImage("lena.jpg", 0)) != 0   )//读入一张灰度图
//if((grayImg = cvLoadImage("C:\\Users\\dell\\Desktop\\测试用图\\T5.bmp", 1)) != 0   )
      
{ //grayImg  = cvCreateImage(cvSize(oriImg->width,oriImg->height),IPL_DEPTH_8U,1);




         //cvCvtColor(oriImg,grayImg,CV_BGR2GRAY);


         return 1;}

return 0;


}




//Cleanning Work,release memory,etc
void ZernikeMoment::ClearOpenCV(void)///////////////
{


if(oriImg!=NULL){


        cvReleaseImage( &oriImg  );
oriImg = NULL;


}



if(grayImg!=NULL){


        cvReleaseImage( &grayImg );


grayImg = NULL;


}
               





}




//Function to caculate Zernike_8_(n,m), a very important function.
void ZernikeMoment::Caculate_8_Zernike(int n,int m)////////////////////
{
  


    int height      =  grayImg->height;
    int widthStep   =  grayImg->widthStep;


float N         =  MinP(height,widthStep);
    
float N2        =  N/2;






float Rpqr_C =0;
float Rpqr_S =0;
    







for(float r=1;r<N2;r++)
{


        float temp_C = 0;
float temp_S = 0;
      


for(float s=1;s<=8*r;s++)
{
    
float xy_v = get_8_XYValue(r,s);
 
             temp_C = temp_C + cos((PI*m*s)/(4*r))*xy_v;
             temp_S = temp_S + sin((PI*m*s)/(4*r))*xy_v;


}
    
    //float Rpqr   =    getRpqr(n,m,(2*r)/N);
float Rpqr =     zernikeR(n,m,(2*r)/N);
Rpqr_C = Rpqr_C  +    temp_C* Rpqr;
    Rpqr_S = Rpqr_S  +    temp_S* Rpqr;
 
}


    


Cnm = Rpqr_C*(2*n+2)/pow(N,2);
    Snm = Rpqr_S*(2*n+2)/pow(N,2);

float l_c = pow(Cnm,2);
float l_s = pow(Cnm,2);
float l_p = l_c + l_s;


    Z_mode = pow((float)l_p,(float)0.5);



}






//Function to caculate Zernike_32_(n,m), a very important function.


void ZernikeMoment::Caculate_32_Zernike(int n,int m)////////////
{
    int height      =  grayImg->height;
    int widthStep   =  grayImg->widthStep;


float N         =  MinP(height,widthStep);
    
float N2        =  N/2;






float Rpqr_C =0;
float Rpqr_S =0;
    







for(float r=1;r<N2;r++)
{


        float temp_C = 0;
float temp_S = 0;
      


for(float s=1;s<=8*r;s++)
{
   
float xy_v = get_32_XYValue(r,s);
 
             temp_C+= cos((PI*m*s)/(4*r))*xy_v;
             temp_S+= sin((PI*m*s)/(4*r))*xy_v;




}
      
    float     Rpqr   =    getRpqr(n,m,(2*r)/N);
Rpqr_C = Rpqr_C  +    temp_C* Rpqr;
    Rpqr_S = Rpqr_S  +    temp_S* Rpqr;
 
}


    


Cnm = Rpqr_C*(2*n+2)/pow(N,2);
    Snm = Rpqr_S*(2*n+2)/pow(N,2);


    Z_mode = pow((float)pow(Cnm,2)+pow(Cnm,2),(float)0.5);
}






float ZernikeMoment::getZernike(int n,int m)//////////////
{


  int pass = Img2Gray();


  if(!pass)
 
return -1;
    


   
   int depth      = 0;
   int nChannels  = 0;
   nChannels      = grayImg->nChannels;


//    if(nChannels!=1)
//   return -1;




   depth = grayImg->depth;


  
  switch(depth)
  {
     
          case   IPL_DEPTH_8U:   Caculate_8_Zernike(n,m);    break;   
          case   IPL_DEPTH_32F:  Caculate_32_Zernike(n,m);   break;   
          default:     break;   
  }




  ClearOpenCV();


  
  return  Z_mode;




}








int main( int argc, char** argv )
{   

   int succees = 0;
      
         //Compute zernike modes ,for n = 4;


         float *z_modes = new float[9];


ZernikeMoment *z_m = new ZernikeMoment();
          
int index = 0;

 
z_modes[index++] = z_m->getZernike(0,0);


z_modes[index++] = z_m->getZernike(1,1);


             z_modes[index++] = z_m->getZernike(2,0);


z_modes[index++] = z_m->getZernike(2,2);


z_modes[index++] = z_m->getZernike(3,1);


             z_modes[index++] = z_m->getZernike(3,3);


             z_modes[index++] = z_m->getZernike(4,0);


             z_modes[index++] = z_m->getZernike(4,2);


z_modes[index++] = z_m->getZernike(4,4);


if(z_m!=NULL)
delete z_m;


            cout<<"zernike modes sequence: "<<endl;






         for(int i=0;i<9;i++)
cout<<z_modes[i]<<",\n";//
         cout<<endl;


return 0;
      
}

你可能感兴趣的:(Zernike矩)