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;
      
}

<think>好的,用户想了解Zernike的概念、计算方法、在图像处理中的应用以及实例。首先,我需要回忆Zernike的基本知识,确保准确无误。 Zernike基于Zernike多项式,这些多项式在单位圆上正交,适合描述图像特征。用户提供的引用[1]提到Zernike是复数,分为实部Cnm和虚部Snm,这需要详细解释。计算步骤可能包括图像预处理、坐标归一化、计算多项式积分等。 接下来是图像处理中的应用,比如形状识别、纹理分类和图像重建。引用[2]提到波前分析和像差校正,这可能涉及光学系统的应用实例,比如眼科或望远镜。需要将理论与实际例子结合,如虹膜识别或卫星图像处理。 用户可能需要具体的公式,所以得确保使用正确的LaTeX格式,行内公式用$...$,独立公式用$$...$$。例如,Zernike多项式的定义和的计算公式。 还需要生成相关问题,帮助用户深入理解。可能的问题包括与传统方法的比较、非圆瞳孔的拓展方法、优化计算效率的技巧等。 检查是否有遗漏,比如计算中的归一化步骤或实际实现时的注意事项,比如离散化处理。同时,确保引用标记正确,引用[1]和[2]分别对应概念和拓展应用。</think>### Zernike的概念与计算方法 #### 核心定义 Zernike是基于Zernike多项式的一组正交,用于描述图像在单位圆域内的特征。其数学形式为: $$Z_{nm} = \frac{n+1}{\pi} \iint_{x^2+y^2 \leq 1} V_{nm}^*(x,y) f(x,y) \,dx\,dy$$ 其中: - $n$为阶数,$m$为重复度(满足$n-|m|$为偶数且$|m| \leq n$) - $V_{nm}(x,y)$为Zernike多项式,定义为极坐标下的径向函数与角向函数的组合[^1] #### 计算步骤 1. **图像预处理**:将图像归一化到单位圆内,通过坐标变换$(x,y) \to (r\cos\theta, r\sin\theta)$,其中$r \in [0,1]$ 2. **多项式计算**: $$V_{nm}(r,\theta) = R_{nm}(r)e^{im\theta}$$ 径向多项式为: $$R_{nm}(r) = \sum_{k=0}^{(n-|m|)/2} \frac{(-1)^k (n-k)!}{k!(\frac{n+|m|}{2}-k)!(\frac{n-|m|}{2}-k)!}r^{n-2k}$$ 3. **计算**:对每个像素计算$V_{nm}^* \cdot f(x,y)$的加权和,离散形式为: $$Z_{nm} = \frac{n+1}{\pi N^2} \sum_{i=0}^{N-1}\sum_{j=0}^{N-1} V_{nm}^*(r_{ij},\theta_{ij}) f(i,j)$$ ### 图像处理应用实例 1. **形状识别** 通过Zernike的旋转不变性,可提取目标轮廓特征。例如卫星图像中舰船目标的识别,仅需计算幅值$|Z_{nm}|$即可消除方向影响。 2. **光学像差分析** 如引用[2]所述,Zernike系数可直接对应波前像差类型: - $Z_2^0$:离焦 - $Z_2^2$:像散 - $Z_3^1$:彗差 3. **生物特征识别** 虹膜识别系统中,使用前20阶Zernike编码虹膜纹理,配合相位编码实现高效匹配。 ### 实现示例(Python伪代码) ```python import numpy as np from scipy.special import jacobi def zernike_radial(n, m, r): R = np.zeros_like(r) for k in range((n-abs(m))//2 + 1): coeff = ((-1)**k * np.math.factorial(n-k) / (np.math.factorial(k) * np.math.factorial((n+abs(m))//2 - k) * np.math.factorial((n-abs(m))//2 - k))) R += coeff * r**(n - 2*k) return R def zernike_moment(image, n, m): height, width = image.shape y, x = np.mgrid[-1:1:height*1j, -1:1:width*1j] r = np.sqrt(x**2 + y**2) theta = np.arctan2(y, x) mask = (r <= 1) V = zernike_radial(n, m, r[mask]) * np.exp(1j * m * theta[mask]) moment = (n+1)/np.pi * np.sum(image[mask] * np.conj(V)) / mask.sum() return moment ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值