求Zernike多项式的偏导数

目的:根据Zernike多项式的偏导数公式撰写代码

由于前一节介绍了使用Matlab代码实现Zernike多项式,而本次的目的是用C语言实现对Zernike多项式求导,得到数值解(Matlab版暂时还没有整理好)。在使用夏克-哈特曼波前探测从波前斜率中重构出原始波前需要用到Zernike多项式的偏导数,该方法也称为Zernike模式法重建【1,2】,与之对应的是区域法重构波前【3】。

本文中的代码部分包括Zernike多项式,Zernike多项式在 x 和y 方向上的偏导数。需要修改的参数:宏定义的Zernike系数的个数(NumofZerCoe) 和 二维数组的分辨率(或称为像素数:Pixels)。

1 理论部分介绍

1.1 Zernike多项式公式【4】

eq?%5C%5B%5Cmathop%20Z%5Cnolimits_n%5Em%20%5Cleft%28%20%7B%5Crho%20%2C%5Ctheta%20%7D%20%5Cright%29%20%3D%20%5Cmathop%20N%5Cnolimits_n%5Em%20%5Cmathop%20R%5Cnolimits_n%5Em%20%5Cleft%28%20%5Crho%20%5Cright%29%5Cmathop%20%5CTheta%20%5Cnolimits_m%20%5Cleft%28%20%5Ctheta%20%5Cright%29%5C%5D

其中,

eq?%5C%5B%5Cmathop%20%5CTheta%20%5Cnolimits_m%20%5Cleft%28%20%5Ctheta%20%5Cright%29%20%3D%20%5Cleft%5C%7B%20%7B%5Cbegin%7Barray%7D%7B*%7B20%7D%7Bc%7D%7D%20%7B%5Ccos%20%5Cleft%28%20%7B%5Cleft%7C%20m%20%5Cright%7C%5Ctheta%20%7D%20%5Cright%29%2C%7D%26%7Bm%20%5Cge%200%7D%5C%5C%20%7B%5Csin%20%5Cleft%28%20%7B%5Cleft%7C%20m%20%5Cright%7C%5Ctheta%20%7D%20%5Cright%29%2C%7D%26%7Bm%20%3C%200%7D%20%5Cend%7Barray%7D%7D%20%5Cright.%5C%5D

eq?R%5Em_n%28%5Crho%20%29%20%3D%20%5Csum_%7Bk%3D0%7D%5E%7B%5Ctfrac%7Bn-m%7D%7B2%7D%7D%20%5Cfrac%7B%28-1%29%5Ek%5C%2C%28n-k%29%21%7D%7Bk%21%5Cleft%20%28%5Ctfrac%7Bn+m%7D%7B2%7D-k%20%5Cright%20%29%21%20%5Cleft%20%28%5Ctfrac%7Bn-m%7D%7B2%7D-k%20%5Cright%29%21%7D%20%5C%3B%5Crho%5E%7Bn-2%5C%2Ck%7D

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARXlSZTE=,size_20,color_FFFFFF,t_70,g_se,x_16

1.2 Zernike多项式的偏导数【5】

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBARXlSZTE=,size_20,color_FFFFFF,t_70,g_se,x_16

有了Zernike多项式偏导数的表示式就可以用代码实现它了。

重要说明:Zernike多项式定义在单位圆(非离散下)是正交的,但是用代码实现它的时候设置的网格是离散的,严格来说Zernike多项式并不是正交的。那么,Zernike多项式的偏导数呢,相关文献也给出了结论:即Zernike多项式的偏导数之间并不是正交的,本人能力有限给不了证明哦,看过的文献也不记得了哈哈,等以后注意到了再回来修改。

2. Matlab代码实现

function [Zernike, DZernike_Dx, DZernike_Dy] = DzernikeDxDy(j, res)

    x           = linspace(-1,1,res);
    [x,y]       = meshgrid(x,x);
    [theta,rho] = cart2pol(x,y);% 由(x,y)换算(r,theta)
    [n,m]       = Noll_to_Zernike(j);
    if m == 0
       deltam = 1;
    else
       deltam = 0;
    end
    Norm        = sqrt(2*(n+1)/(1+deltam));% 归一化因子
    Rnm_rho     = zeros(res, res);         % 径向多项式初始化
    DRnm_Drho   = zeros(res, res);         % 径向多项式对极径偏导数的初始化
    for s = 0:(n-abs(m))/2
        Rnm_rho =   Rnm_rho + (-1)^s.* ...
                    prod(1:(n-s))* ...
                    rho.^(n-2*s)/ ...
                    (prod(1:s)*...
                    prod(1:((n+abs(m))/2-s))* ...
                    prod(1:((n-abs(m))/2-s))); % 径向多项式
  
 
        DRnm_Drho = DRnm_Drho + (n-2*s)*(-1)^s.*...
                    prod(1:(n-s))*...
                    rho.^(n-2*s-1)/...
                    (prod(1:s)*...
                    prod(1:((n+abs(m))/2-s))*...
                    prod(1:((n-abs(m))/2-s))); % 径向多项式 对极径的偏导数
    end
    
    DT_Dtheta     = zeros(res, res);
    if m < 0
        DT_Dtheta =  abs(m) * cos( abs(m) * theta );
    else 
        DT_Dtheta = -abs(m) * sin( abs(m) * theta ) ;
    end
            
    Zernike        = zeros(res, res); % Zernike多项式初始化
    DZernike_Dx    = zeros(res, res); % Zernike多项式在x方向上的初始化
    DZernike_Dy    = zeros(res, res); % Zernike多项式在y方向上的初始化
    if m < 0
        T           = sin(abs(m) .* theta);
        Zernike     = -Norm.*Rnm_rho .* sin(m .* theta); % m<0时候的zernike多项式
        DT_Dtheta   = abs(m) * cos( abs(m) * theta );
        
        DZernike_Dx = Norm .* ( DRnm_Drho .* T .* cos(theta) - ...
                     (Rnm_rho ./rho) .* DT_Dtheta .* sin(theta));
                 
        DZernike_Dy = Norm .* ( DRnm_Drho .* T .* sin(theta) + ...
                     (Rnm_rho ./rho) .* DT_Dtheta .* cos(theta));       
    else
        T           = cos( abs(m) .* theta);
        Zernike     = Norm.*Rnm_rho .*cos( m .* theta);  % m>0时候的zernike多项式
        DT_Dtheta   = -abs(m) * sin( abs(m) * theta ) ;
        
        DZernike_Dx = Norm .* ( DRnm_Drho .* T .* cos(theta) - ...
                     (Rnm_rho ./rho) .* DT_Dtheta .* sin(theta));
                 
        DZernike_Dy = Norm .* ( DRnm_Drho .* T .* sin(theta) + ...
                     (Rnm_rho ./rho) .* DT_Dtheta .* cos(theta));        
    end
    Zernike     = Zernike .* (rho<=1);
    DZernike_Dx = DZernike_Dx .* (rho<=1);
    DZernike_Dy = DZernike_Dy .* (rho<=1);
end

 调用方式如下:

clc; clear all; close all
num         = 54;                    % Zernike模式的个数
nPx         = 60;                   % 分辨率     
Zerpol      = zeros(nPx, nPx, num);
DZernike_Dx = zeros(nPx, nPx, num);
DZernike_Dy = zeros(nPx, nPx, num);
for i= 1:num
    [Zerpol(:,:,i), DZernike_Dx(:,:,i), DZernike_Dy(:,:,i)] = DzernikeDxDy(i, nPx);
end
function [n,m] = Noll_to_Zernike(j)
    if(j<1)
        error('Noll indices start at 1.');
    end

    n  = 0;
    m  = 0;
    j1 = j-1;
    while (j1 > n)
        n  = n + 1;
        j1 = j1 - n;
        m  = (-1)^j * (mod(n,2) + 2*floor((j1+mod(n+1,2))/2));
    end
end

参考文献:

[1] WANG J Y, MARKEY J K. Modal compensation of atmospheric turbulence phase distortion [J]. JOSA, 1978, 68(1): 78-87.

[2] CUBALCHINI R. Modal wave-front estimation from phase derivative measurements [J]. JOSA, 1979, 69(7): 972-7.

[3] SOUTHWELL W H. Wave-front estimation from wave-front slope measurements [J]. JOSA, 1980, 70(8): 998-1006.

[4] NOLL R J. Zernike polynomials and atmospheric turbulence [J]. JOsA, 1976, 66(3): 207-11.

[5] Efficient and stable numerical method for evaluation of Zernike polynomials and their Cartesian derivatives

3.C代码如下

# include <stdio.h>
# include <math.h>
# include <malloc.h>
#define	NumofZerCoe	30	// Zernike系数的个数,从piston开始
#define	Pixels		60	// 像素数
#define	NR_END		0
#define	NRANSI
#define	FREE_ARG	char*

typedef struct XYRT
{/*  
 在结构体中 定义直角坐标(X,Y)和极坐标(R,T),然后用结构体进行传参、节省函数的形式参数的个数
 */
	double X[Pixels][Pixels];
	double Y[Pixels][Pixels];
	double R[Pixels][Pixels];
	double T[Pixels][Pixels];
}st_XT, *st_pXT;
/* * * * * * * * * * * * * * * *
 * 函数的声明
 * * * * * * * * * * * * * * * */
int		power_int(int a, int n);
double		power_double(double a, int n);
double		factoria_double(int value);
double		DZernikeDtheta(int n, int m, double Radial, double Theta);
double		DZernikeDradial(int n, int m, double Radial, double Theta);
double		NormCoeficients(int n, int m);
double		DRadialPolynomialsDr(int n, int m, double);
double		ZernikeRadialPolynomials(int n, int m, double);
double		**matrix(long nrl, long nrh, long ncl, long nch);
void		Noll_to_Zernike(int j, int *n, int *m);
void		ZernikePolynomials(int n, int m, st_pXT CartesiantoandPolar, double **Zernikepoly);
void		DZernikeDxDyFromCartesian(int n, int m, st_pXT CartesiantoandPolar, double ** DzernikeDx, double ** DzernikeDy);
void		free_matrix(double **m, long nrl, long nrh, long ncl, long nch);
void		ZernikeMask(st_pXT CartesiantoandPolar, double **mask);
void		Transpose(double **X, double **X_T);
void		meshgrid(double *x, st_pXT Coor);
void		linspace(double x1, double x2, int n, double *y);
void		InitMatrix(double **Ma, int Long, int Width);

/* * * * * * * * * * * * * * * *
 * 阶乘的运算,result = value ! 
 * 输入:int 类型的 value
 * 输出:阶乘后的结果- double类型
 * * * * * * * * * * * * * * * */
double factoria_double(int value) {
	double result = 1.0;
	while (value != 0) {
		result = result * value;
		value--;
	}
	return result;
}

/* * * * * * * * * * * * * * * *
 * 幂指数的运算,result = a^n
 * 输入:double 类型的 a, 
 * 输出:double类型
 * * * * * * * * * * * * * * * */
double power_double(double a, int n)
{
	double result = 1.0;
	for (int i = 0; i < n; i++) {
		result = result * a;
	}
	return result;
}

/* * * * * * * * * * * * * * * *
 * 幂指数的运算,result = a^n
 * 输入:int 类型的 a,
 * 输出:int类型
 * * * * * * * * * * * * * * * * */
int power_int(int a, int n){	
	double result = 1.0;
	for (int i = 0; i < n; i++) {
		result = result * a;
	}
	return result;
}

/* * * * * * * * * * * * * * * *
 * 函数目的:根据Zernike多项式 的序号j 求 径向级数(n) 和 角向级数(m)
 * 输入参数:序号j, 指针n 和 m,
 * 无返回值;可以直接对指针操作,把求得的结果放在 n 和 m 中
 * * * * * * * * * * * * * * * */
void Noll_to_Zernike(int j, int *n, int *m){
	if (j < 1)
		printf(" Noll indices j must be more than 1 !");
	else{
		int nn	= 0;
		int mm	= 0;
		int j1	= j - 1;
		int ss	= 0;
		while (j1 > nn){
			nn		= nn + 1;
			j1		= j1 - nn;
			double s	= (j1 + ((nn + 1) % 2)) / 2.0;
			ss		= floor(s);
			mm		= power_int(-1, j) * ((nn % 2) + 2 * ss);
		}
		*m	= mm;
		*n	= nn;
	}
}

/* * * * * * * * * * * * * * * * *
 * 函数目的:和Matlba 中 linspace 用法一样
 * 输入参数:x1 :起始点; y1:终止点; n:采样点数; *y:返回的结果
 * 输出:可以直接对指针操作,就不设置了返回值,
 * * * * * * * * * * * * * * * * */
void linspace(double x1, double x2, int n, double *y){
	double d	= (x2 - x1) / (n - 1);
	for (int i	= 0; i < n; i++){
		*(y+i)	= x1 + i * d;
	}
}

/* * * * * * * * * * * * * * * * *
 * 函数目的:和Matlba 中 meshgrid 用法一样
 * 输入参数:*x
 * 输出:可以直接对指针操作,就不设置了返回值,
 * * * * * * * * * * * * * * * * */
void meshgrid(double *x, st_pXT CartesiantoandPolar){
	for (int i = 0; i < Pixels; i++) {
		for (int j = 0; j < Pixels; j++) {
			CartesiantoandPolar->X[i][j] = *(x + j);
			CartesiantoandPolar->Y[j][i] = CartesiantoandPolar->X[i][j];
		}
	}
}

/* * * * * * * * * * * * * * * * *
 * 函数目的:二维数组的转置
 * 输入参数:转置前的参数:**X,转置后的参数: **X_T 
 * 输出:可以直接对指针操作,就不设置了返回值
 * * * * * * * * * * * * * * * * */
void Transpose(double **X, double **X_T){	
	int i, j;
	double temp;
	for (i = 0; i < Pixels; i++) {
		for (j = 0; j < Pixels; j++) {
			X_T[j][i] = X[i][j];
		}
	}
}

/* * * * * * * * * * * * * * * * *
 * 函数目的:求出单位圆的 pupil mask
 * 输入参数:结构体中 直角坐标和极坐标(-1,1范围内);用指针的指针mask接收最后的结果
 * 输出:可以直接对指针操作,就不设置了返回值
 * 直角坐标转为极坐标,,也要得到相应的mask,mask是一个圆,其内部元素只有0和1
 * * * * * * * * * * * * * * * * */
void ZernikeMask(  st_pXT CartesiantoandPolar, double **mask ){
	for (int i = 0; i < Pixels; i++){
		for (int j = 0; j < Pixels; j++){
			CartesiantoandPolar->R[i][j] = sqrt((CartesiantoandPolar->X[i][j]) * (CartesiantoandPolar->X[i][j]) + (CartesiantoandPolar->Y[i][j]) * (CartesiantoandPolar->Y[i][j])  );// 极径
			CartesiantoandPolar->T[i][j] = atan2( CartesiantoandPolar->X[i][j] ,  CartesiantoandPolar->Y[i][j]  );		// 极角
			if (CartesiantoandPolar->R[i][j] <= 1.0){
				mask[i][j] = 1.0;	
			}else
				mask[i][j] = 0;
		}
	}
}

/* * * * * * * * * * * * * * * * *
 * 函数目的:申请动态内存后 用于 初始化 二级指针函数(指针的指针),这一步必不可少
 * 输入参数:待初始化的 变量Ma(指针的指针),二维数组的长度:Long,二维数组的长度:Width
 * 输出:可以直接对指针操作,就不设置了返回值
 * * * * * * * * * * * * * * * * */
void InitMatrix(double **Ma, int Long, int Width){
	for (int i = 0; i < Long; i++) {
		for (int j = 0; j < Width; j++) {
			Ma[i][j] = 0.0;	
		}
	}
}

/* * * * * * * * * * * * * * * * *
 * 函数目的:用指针的指针后 需要申请动态内存,不然,用二维数组作为函数的形式参数时必须要设置数组的某个维度
 * 输入参数:对于 m*n的二维数组的													行方向索引的起点 nrl(尽量用0),行方向索引的终点  nrh( m - 1),													列方向索引的起点 ncl(尽量用0),列方向索引的终点 nch( n - 1),
 * 输出:申请后的结果。
 * 用法: 为二维数组A[nrl, nrh][ncl, nch]  申请内存,
 * 一般来说,索引都是从 0 开始,此时的 nrl = ncl = 0;
 * 数组的长 和 宽 分别为 m 和 n,则		:
	nrh  = nrl + n -1 = n-1
	nch  = ncl + m - 1 = m - 1
  * * * * * * * * * * * * * * * * */
double **matrix(long nrl, long nrh, long ncl, long nch){
	long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1;
	double **m;
	m = (double **)malloc((size_t)((nrow + NR_END) * sizeof(double*)));
	if (!m){
		printf("allocation failure 1 in matrix()");
	}
	m += NR_END;
	m -= nrl;
	m[nrl] = (double *)malloc((size_t)((nrow*ncol + NR_END) * sizeof(double)));
	if (!m[nrl]){
		printf("allocation failure 2 in matrix()");
	}
	m[nrl] += NR_END;
	m[nrl] -= ncl;
	for (i = nrl + 1; i <= nrh; i++)
		m[i] = m[i - 1] + ncol;
	return m;
}

/* * * * * * * * * * * * * * * * *
 * 函数目的:申请动态内存后必须释放内存。
 * 输入参数:待释放内存的变量m(指针的指针),对于 m*n的二维数组的																											行方向索引的起点 nrl(尽量用0),行方向索引的终点  nrh( m - 1)																												列方向索引的起点 ncl(尽量用0),列方向索引的终点 nch( n - 1),
 * 输出:可以直接对指针操作,就不设置了返回值
  * * * * * * * * * * * * * * * * */
void free_matrix(double **m, long nrl, long nrh, long ncl, long nch){
/* free a double matrix allocated by matrix() , 用法同 **matrix()   */
	free((FREE_ARG)(m[nrl] + ncl - NR_END));
	free((FREE_ARG)(m + nrl - NR_END));
}


/* * * * * * * * * * * * * * * * *
 * 函数目的:Zernike多项式的径向表达式。
 * 输入参数:径向级数(n) ,角向级数(m),极坐标 极径(Radial)
 * 输出:    径向多项式:RadialPolynomials
 * * * * * * * * * * * * * * * * */
double ZernikeRadialPolynomials(int n, int m, double Radial) {
	double RadialPolynomials = 0.0;
	for (int s = 0; s <= (n - abs(m)) / 2; s++) {
		RadialPolynomials =	RadialPolynomials +
					power_double(-1, s) * 
					factoria_double(n - s) /
					factoria_double(s) /
					factoria_double((n +  abs(m)) / 2 - s) /
					factoria_double((n  -  abs(m)) / 2 - s) *
					power_double(Radial, n - 2 * s);
	}
	return RadialPolynomials;
}

/* * * * * * * * * * * * * * * * *
 * 函数目的:Zernike多项式的径向表达式 对 极径 的偏导数
 * 输入参数:径向级数(n) ,角向级数(m),极坐标(Radial),
 * 输出:    径向多项式对r 的偏导数 DrzDr
 * * * * * * * * * * * * * * * * */
double DRadialPolynomialsDr(int n, int m, double Radial){
	double DrzDr = 0.0;
	for (int s = 0; s <= (n - abs(m)) / 2; s++) {
		DrzDr =	DrzDr + 
				power_double(-1, s) * 
				factoria_double(n - s) * (n - 2 * s) /
				factoria_double(s) /
				factoria_double((n + abs(m)) / 2 - s) /
				factoria_double((n -  abs(m)) / 2 - s) *
				power_double(Radial, n - 2 * s - 1);
	}
	return DrzDr;
}

/* * * * * * * * * * * * * * * * *
 * 函数目的:Zernike多项式中的常数项
 * 输入参数:径向级数(n) ,角向级数(m)
 * 输出:    coefficients. 
 * 这个函数没有使用。
 * * * * * * * * * * * * * * * * */
double NormCoeficients(int n, int m) {
	// 求的是 归一化后的系数
	double coefficients;
	if (m != 0)
		coefficients = sqrt(2 * (n + 1));
	else
		coefficients = sqrt(2 * (n + 1) / 2);
	return coefficients;
}

/* * * * * * * * * * * * * * * * * *
 * 函数目的:Zernike多项式 对 极径r 的偏导数
 * 输入参数:径向级数(n) ,角向级数(m),直角坐标和极角Theta
 * 输出:    Zernike多项式 对极径 的偏导数
 * * * * * * * * * * * * * * * * */
double DZernikeDradial(int n, int m, double Radial, double Theta) {
	double DrzDr	= DRadialPolynomialsDr(n, m, Radial);
	double DzDr	= 0;
	if (m > 0) {
		DzDr = DrzDr * cos( (double)m * Theta );
	}else if (m < 0) {
		DzDr = DrzDr * sin( (double)(-m)  *Theta );
	}else {
		DzDr = DrzDr;
	}
	return DzDr;
}

/* * * * * * * * * * * * * * * * * 
 * 函数目的:Zernike多项式 对 极角θ 的偏导数
 * 输入参数:径向级数(n) ,角向级数(m),极坐标-- 极径Radial 和 极角Theta
 * 输出:    zernike多项式对极角的偏导数DzernikeDt
 * * * * * * * * * * * * * * * * */
double DZernikeDtheta(int n, int m, double Radial, double Theta){	
	double RadialPolynomials = ZernikeRadialPolynomials( n, m, Radial);
	double DzernikeDt = 0.0;
	if (m > 0) {
		DzernikeDt = -(double)m * RadialPolynomials * sin((double)m * Theta);
	}else if (m < 0) {
		DzernikeDt = -(double)m * RadialPolynomials * cos((double)(-m) * Theta);
	}else {
		DzernikeDt = 0;
	}
	return DzernikeDt;
}


/* * * * * * * * * * * * * * * * * 
 * 函数目的:Zernike多项式 对 直角坐标x 和 y 的偏导数
 * 输入参数:径向级数(n) ,角向级数(m),直角坐标和极坐标(CartesiantoandPola--结构体指针),对x偏导数(DzernikeDx),对y偏导数(DzernikeDx)
 * 输出:    可以直接对指针的指针 指向的地址 操作,就不设置了返回值
 * * * * * * * * * * * * * * * * * */
void DZernikeDxDyFromCartesian(int n, int m, st_pXT CartesiantoandPolar, double ** DzernikeDx ,  double ** DzernikeDy)
{
	/*必须说明
	我在 [-1, 1] 区间上 使用 linspace() 和 meshgrid() 函数 时,把 得到的 二维网格上的点X 和Y 弄反了,
	所以下面的函数 DzernikeDx 和 DzernikeDy 需要把 公式(理论) 中的 CartesiantoandPolar->X 和 CartesiantoandPolar->Y 相互替换。
	理论中的公式 在 那篇 英文 文献中。
	否则得到的错误结果,我也是调试了一下午才发现这个错误。我也懒得改了,还请读者注意一下即可。
	*/
	double **mask = matrix(0, Pixels - 1, 0, Pixels - 1);	// mask函数
	InitMatrix(mask, Pixels, Pixels);
	ZernikeMask(CartesiantoandPolar, mask);
	for (int i = 0; i < Pixels; i++) {
		for (int j = 0; j < Pixels; j++) {
			double DzDtheta	 = DZernikeDtheta(n, m, CartesiantoandPolar->R[i][j], CartesiantoandPolar->T[i][j]);
			double DZDradius = DZernikeDradial(n, m, CartesiantoandPolar->R[i][j], CartesiantoandPolar->T[i][j]);
			DzernikeDx[i][j] = mask[i][j] * ( CartesiantoandPolar->Y[i][j] * DZDradius  / CartesiantoandPolar ->R[i][j] )   -
					   ( CartesiantoandPolar->X[i][j]  / CartesiantoandPolar->R[i][j] / CartesiantoandPolar->R[i][j] * DzDtheta);
			DzernikeDy[i][j] = mask[i][j] * ( CartesiantoandPolar->X[i][j] * DZDradius / CartesiantoandPolar->R[i][j] ) +
					  ( CartesiantoandPolar->Y[i][j]  / CartesiantoandPolar->R[i][j] / CartesiantoandPolar->R[i][j] * DzDtheta);
			if (m != 0) {
				double Coefficients	= sqrt(2 * (n + 1));
				DzernikeDx[i][j]	= DzernikeDx[i][j] * Coefficients;
				DzernikeDy[i][j]	= DzernikeDy[i][j] * Coefficients;
			}else {
				double Coefficients	= sqrt(n + 1);
				DzernikeDx[i][j]	= DzernikeDx[i][j] * Coefficients;
				DzernikeDy[i][j]	= DzernikeDy[i][j] * Coefficients;
			}
		}
	}
	free_matrix(mask, 0, Pixels - 1, 0, Pixels - 1);
}


/* * * * * * * * * * * * * * * * * * 
 * 函数目的:求Zernike多项式 
 * 输入参数:径向级数(n) ,角向级数(m),直角坐标和极坐标(CartesiantoandPola--结构体指针),多项式(Zernikepoly)
 * 输出:    可以直接对指针的指针 指向的地址 操作,就不设置了返回值
 * * * * * * * * * * * * * * * * * */
void ZernikePolynomials(int n , int m, st_pXT CartesiantoandPolar, double **Zernikepoly){	
	double **mask = matrix(0, Pixels - 1, 0, Pixels - 1);	// mask函数
	InitMatrix(mask, Pixels, Pixels);
	ZernikeMask(CartesiantoandPolar, mask);
	if (m < 0){
		double Coefficients = sqrt(2 * ( n + 1) );
		for (int i = 0; i < Pixels; i++) {
			for (int j = 0; j < Pixels; j++) {
				double RadialPolynomials = ZernikeRadialPolynomials(n, m, CartesiantoandPolar->R[i][j]);
				Zernikepoly[i][j] = mask[i][j] * Coefficients * RadialPolynomials * sin((double)abs(m) * CartesiantoandPolar->T[i][j]);
			}
		}
	}else if(m>0){
		double Coefficients = sqrt(2 * (n + 1));
		for (int i = 0; i < Pixels; i++){
			for (int j = 0; j < Pixels; j++){
				double RadialPolynomials = ZernikeRadialPolynomials(n, m, CartesiantoandPolar->R[i][j]);
				Zernikepoly[i][j] = mask[i][j] * Coefficients * RadialPolynomials* cos((double)m * CartesiantoandPolar->T[i][j] );
			}
		}
	}else{
		double Coefficients = sqrt( n + 1);
		for (int i = 0; i < Pixels; i++){
			for (int j = 0; j < Pixels; j++){
				double RadialPolynomials = ZernikeRadialPolynomials(n, m, CartesiantoandPolar->R[i][j]);
				Zernikepoly[i][j] = mask[i][j] * Coefficients * RadialPolynomials;
			}
		}
	}
	free_matrix(mask, 0, Pixels - 1, 0, Pixels - 1);
}

// 终于等来了主函数
int main(void){
	int n[NumofZerCoe] = { 0 }; // n初始化
	int m[NumofZerCoe] = { 0 }; // m初始化
	double x1 = -1.0, x2 = 1.0;
	double y[Pixels] = { 0 };   
	linspace(x1, x2, Pixels, y);											// ==> y = linspace(x1,x2,Pixels)
	st_XT CartesiantoandPolar = { 0 };									// 定义一个 含有 直角和极坐标的结构体 指针
	meshgrid(y, &CartesiantoandPolar);								// 实现:[XX,YY ] = meshgrid(y);
	double **mask = matrix(0, Pixels - 1, 0, Pixels - 1);// 单位圆的mask函数
	InitMatrix(mask, Pixels, Pixels);
	ZernikeMask(&CartesiantoandPolar, mask);	 // 这一步必须加上,才会得到极坐标 R 和 T, 不要单独分出来实现R 和T 

	/*FILE *fpWrite4 = fopen("CartesiantoandPolar.txt", "w");
	if (fpWrite4 == NULL) {
		return 0;
	}
	for (int i = 0; i < Pixels; i++) {
		for (int j = 0; j < Pixels; j++) {
			fprintf(fpWrite4, "%3.3f\t", mask[i][j]);
		}
		fprintf(fpWrite4, "\n");
	}
	fclose(fpWrite4);*/


	// 前 NumofZerCoe 阶模式 保存到  ZERPOL[][][]中,这是三维数组。
	double ZERPOLs[Pixels][Pixels][NumofZerCoe] = { 0 };	// 保存的是 前NumofZerCoe个 Zernike多项式
	double DZDXs[Pixels][Pixels][NumofZerCoe] = { 0 };	// 保存的是 前NumofZerCoe个 Zernike多项式对x的偏导数
	double DZDYs[Pixels][Pixels][NumofZerCoe] = { 0 };	// 保存的是 前NumofZerCoe个 Zernike多项式对y的偏导数

	for (int j = 0; j < NumofZerCoe; j++){
		double **Zernikepoly = matrix(0, Pixels - 1, 0, Pixels - 1);	// Zernike多项
		InitMatrix(Zernikepoly, Pixels, Pixels);

		double **DzernikeDx = matrix(0, Pixels - 1, 0, Pixels - 1);	// Zernike多项式对x的偏导数
		InitMatrix(DzernikeDx, Pixels, Pixels);

		double **DzernikeDy = matrix(0, Pixels - 1, 0, Pixels - 1);	// Zernike多项式对y的偏导数
		InitMatrix(DzernikeDy, Pixels, Pixels);

		Noll_to_Zernike(j + 1, &n[j], &m[j]);
		//printf("j = %d\t, n = %d\t, m = %d \n", 1 + j, n[j], m[j]);	// 显示 j  n  m  三个值

		ZernikePolynomials(n[j], m[j], &CartesiantoandPolar, Zernikepoly);

		// 调用函数  返回 多项式的 偏导数
		DZernikeDxDyFromCartesian(n[j], m[j], &CartesiantoandPolar, DzernikeDx, DzernikeDy);

		for (int ii = 0; ii < Pixels; ii++) {
			for (int jj = 0; jj < Pixels; jj++) {
				ZERPOLs[ii][jj][j] = Zernikepoly[ii][jj] * mask[ii][jj];
				DZDXs[ii][jj][j]		= DzernikeDx[ii][jj] * mask[ii][jj];
				DZDYs[ii][jj][j]		= DzernikeDy[ii][jj] * mask[ii][jj];
			}
		}
		free_matrix(Zernikepoly, 0, Pixels - 1, 0, Pixels - 1);
		free_matrix(DzernikeDx, 0, Pixels - 1, 0, Pixels - 1);
		free_matrix(DzernikeDy, 0, Pixels - 1, 0, Pixels - 1);
	}

	free_matrix(mask, 0, Pixels - 1, 0, Pixels - 1);

	FILE *fpWrite1 = fopen("Zernikes.txt", "w");
	if (fpWrite1 == NULL) {
		return 0;
	}
	for (int ii = 0; ii < NumofZerCoe; ii++) {
		for (int i = 0; i < Pixels; i++) {
			for (int j = 0; j < Pixels; j++) {
				fprintf(fpWrite1, "%3.3f\t", ZERPOLs[i][j][ii]);
			}
			fprintf(fpWrite1, "\n");
		}
	}
	fclose(fpWrite1);


	FILE *fpWrite2 = fopen("DzDxs.txt", "w");
	if (fpWrite2 == NULL) {
		return 0;
	}
	for (int ii = 0; ii < NumofZerCoe; ii++) {
		for (int i = 0; i < Pixels; i++) {
			for (int j = 0; j < Pixels; j++) {
				fprintf(fpWrite2, "%3.3f\t", DZDXs[i][j][ii]);
			}
			fprintf(fpWrite2, "\n");
		}
	}
	fclose(fpWrite2);

	FILE *fpWrite3 = fopen("DzDys.txt", "w");
	if (fpWrite3 == NULL) {
		return 0;
	}
	for (int ii = 0; ii < NumofZerCoe; ii++) {
		for (int i = 0; i < Pixels; i++) {
			for (int j = 0; j < Pixels; j++) {
				fprintf(fpWrite3, "%3.3f\t", DZDYs[i][j][ii]);
			}
			fprintf(fpWrite3, "\n");
		}
	}
	fclose(fpWrite3);
/*
matlab 中 加载 data.txt 后 ,用下面语句 画图
data = reshape(data', [60,60,20]);
figure(10);imagesc(data(:,:,5));colormap(jet);colorbar
*/
	return 0;
}

需要修改的参数:宏定义的Zernike系数的个数(NumofZerCoe) 和 二维数组的分辨率(或称为像素数:Pixels)

写入到txt中的数据 在Matlab 中加载的方式 :

data1=importdata('data.txt');

如果写入到txt中的是三维数组的话,则

data = reshape(data', [Pixels,Pixels,NumofZerCoe]); % 转置 后 在 使用 reshape,即可读取三维数组中的数组

figure(10);imagesc(data(:,:,5));colormap(jet);colorbar

如果写入到txt中的是二维数组的话,则

data = reshape(data', [Pixels,Pixels]);

另外,常见的报错:

1)error C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation,

use _CRT_SECURE_NO_WARNINGS. See online help for details

解决方案,项目 =》属性 =》c/c++ =》预处理器=》点击预处理器定义,编辑,加入_CRT_SECURE_NO_WARNINGS

2)编译时没有错误,但是调试或者运行时程序就报错了!

debug调试提示:xxx.exe 中的 0x00e731d7 处未处理的异常: 0xC00000FD: Stack overflow

原来是栈溢出了!

【解决方案】扩大栈空间的大小,在vs工程里面:项目->属性->链接器->系统->堆栈保留大小(注:这里填的是字节数)如果你想把他扩大为2M的话,1024*1024*2 = 2097152(这个数字填进去)4M = 4194304

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值