first commit

This commit is contained in:
Eddy
2014-10-10 17:36:02 +04:00
commit 1a63497f04
62 changed files with 9055 additions and 0 deletions

23
Zernike/Makefile Normal file
View File

@@ -0,0 +1,23 @@
LOADLIBES = -lm -lgsl -lgslcblas
SRCS = zernike.c zernikeR.c zernike_annular.c
CC = gcc
DEFINES = -D_GNU_SOURCE
#-D_XOPEN_SOURCE=501
CXX = gcc
CFLAGS = -Wall -Werror $(DEFINES)
OBJS = $(SRCS:.c=.o)
all : zernike btatest
zernike : $(OBJS) test.o
$(CC) $(CFLAGS) test.o $(OBJS) $(LOADLIBES) -o zernike
btatest : $(OBJS) Z-BTA_test.o simple_list.o
$(CC) $(CFLAGS) Z-BTA_test.o simple_list.o $(OBJS) $(LOADLIBES) -o btatest
clean:
/bin/rm -f *.o *~
depend:
$(CXX) -MM $(SRCS)
### <DEPENDENCIES ON .h FILES GO HERE>
# name1.o : header1.h header2.h ...
test.o zernike.o zernikeR.o zernike_annular.o Z-BTA_test.o : zernike.h
zernike.o zernikeR.o zernike_annular.o : zern_private.h
simple_list.o Z-BTA_test.o : simple_list.h

26
Zernike/README Normal file
View File

@@ -0,0 +1,26 @@
Here is my realisation of wavefront decomposition and restoration based on
Zernike polynomials (up to 100th order).
The base functions are:
double *zernfunR(int n, int m, int W, int H, double *norm);
calculates Zernike polynomial of order n and angular order m on
equidistance rectangular grid WxH (norm is NULL or value to store
sum(Z_i^2) for normalisation on decomposition.
double *zernfunNR(int p, int W, int H, double *norm);
the same but in Noll notation
double *Zdecompose(int Nmax, int W, int H, double *image, int *Zsz, int *lastIdx);
decompose image by Zernike polynomials with order <= Nmax,
Zsz is size of returned array (in Noll notation)
lastidx is last non-zero coefficient
double *Zcompose(int Zsz, double *Zidxs, int W, int H);
build image based on Zernike coeffs Zidxs
Zsz - size of array in Noll notation
W, H - size of future image
void set_prec(double val);
set precision of Zernike transforms

402
Zernike/Z-BTA_test.c Normal file
View File

@@ -0,0 +1,402 @@
/*
* Z-BTA_test.c - simple test for models of hartmannograms
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <stdio.h>
#include <math.h>
#include <getopt.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <err.h>
#include <sys/stat.h> // open/close/etc
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h> // mmap
#include "zernike.h"
#include "simple_list.h"
#define _(...) __VA_ARGS__
extern char *__progname;
#define ERR(...) err(1, __VA_ARGS__)
#define ERRX(...) errx(1, __VA_ARGS__)
// x0 = (*spot)->c.x - AxisX;
// y0 = AxisY - (*spot)->c.y;
double AxisX = 1523., AxisY = 1533.; // BUG from fitsview : xreal = x + AxisX, yreal = y - AxisY
char *name0 = NULL, *name1 = NULL; // prefocal/postfocal filenames
double distance = -1.; // distance between images
double pixsize = 30.; // pixel size
double ImHeight = 500.; // half of image height in pixels
// spots for spotlist
typedef struct{
int id; // spot identificator
double x; // coordinates of center
double y;
} spot;
// hartmannogram
typedef struct{
char *filename; // spots-filename
int len; // amount of spots
List *spots; // spotlist
} hartmann;
/**
* print usage with optional message & exit with error(1)
* @param fmt ... - printf-like message, no tailing "\n" required!
*/
void usage(char *fmt, ...){
va_list ap;
va_start(ap, fmt);
printf("\n");
if (fmt != NULL){
vprintf(fmt, ap);
printf("\n\n");
}
va_end(ap);
// "Использование:\t%s опции\n"
printf(_("Usage:\t%s options\n"),
__progname);
// "Опции:\n"
printf(_("Required options:\n"));
printf("\t--prefocal, -0 <file>\t\tfilename with spotslist in " RED "prefocal" OLDCOLOR " image\n");
printf("\t--postfocal,-1 <file>\t\tfilename with spotslist in " RED "postfocal" OLDCOLOR " image\n");
printf("\t--distance, -D <distance in mm>\tdistance between images in millimeters\n");
printf(_("Unnesessary options:\n"));
printf("\t--pixsize, -p <size in mkm>\tpixel size in microns (default: 30)\n");
exit(1);
}
/**
* converts string into double number
* @param num (o) - result of conversion
* @param str (i) - string with number
* @return num of NULL in case of error
*/
double *myatod(double *num, const char *str){
double tmp;
char *endptr;
if(!num) return NULL;
if(!str) return NULL;
tmp = strtod(str, &endptr);
if(endptr == str || *str == '\0' || *endptr != '\0')
return NULL;
*num = tmp;
return num;
}
/**
* Parce arguments of main() & fill global options
*/
void parse_args(int argc, char **argv){
int i;
char short_options[] = "0:1:D:p:"; // all short equivalents
struct option long_options[] = {
/* { name, has_arg, flag, val }, where:
* name - name of long parameter
* has_arg = 0 - no argument, 1 - need argument, 2 - unnesessary argument
* flag = NULL to return val, pointer to int - to set it
* value of val (function returns 0)
* val - getopt_long return value or value, flag setting to
* !!! last string - for zeros !!!
*/
{"prefocal", 1, 0, '0'},
{"postfocal", 1, 0, '1'},
{"distance", 1, 0, 'D'},
{"pixsize", 1, 0, '0'},
{ 0, 0, 0, 0 }
};
if(argc == 1){
usage(_("Parameters required"));
}
while(1){
int opt;
if((opt = getopt_long(argc, argv, short_options,
long_options, NULL)) == -1) break;
switch(opt){
/*case 0: // only long option
// do something?
break;*/
case '0':
name0 = strdup(optarg);
break;
case '1':
name1 = strdup(optarg);
break;
case 'D':
if(!myatod(&distance, optarg))
usage("Parameter <distance> should be a floating point number!");
break;
case 'p':
if(!myatod(&pixsize, optarg))
usage("Parameter <pixsize> should be an int or floating point number!");
break;
default:
usage(NULL);
}
}
argc -= optind;
argv += optind;
if(argc > 0){
// "Игнорирую аргумент[ы]:\n"
printf(_("Ignore argument[s]:\n"));
for (i = 0; i < argc; i++)
printf("%s ", argv[i]);
printf("\n");
}
if(!name0 || !name1)
usage("You should point to both spots-files");
if(distance < 0.)
usage("What is the distance between images?");
}
typedef struct{
char *data;
size_t len;
} mmapbuf;
/**
* Mmap file to a memory area
*
* @param filename (i) - name of file to mmap
* @return stuct with mmap'ed file or die
*/
mmapbuf *My_mmap(char *filename){
int fd;
char *ptr;
size_t Mlen;
struct stat statbuf;
if(!filename) ERRX(_("No filename given!"));
if((fd = open(filename, O_RDONLY)) < 0)
ERR(_("Can't open %s for reading"), filename);
if(fstat (fd, &statbuf) < 0)
ERR(_("Can't stat %s"), filename);
Mlen = statbuf.st_size;
if((ptr = mmap (0, Mlen, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
ERR(_("Mmap error for input"));
if(close(fd)) ERR(_("Can't close mmap'ed file"));
mmapbuf *ret = MALLOC(mmapbuf, 1);
ret->data = ptr;
ret->len = Mlen;
return ret;
}
void My_munmap(mmapbuf *b){
if(munmap(b->data, b->len))
ERR(_("Can't munmap"));
FREE(b);
}
/**
* Read spots-file and fill hartmann structure
* @param filename - name of spots-file
* @return dynamically allocated hartmanogram structure
*/
hartmann *read_spots(char *filename){
assert(filename);
mmapbuf *M = NULL;
int L = 0;
List *spots = NULL, *curspot = NULL;
M = My_mmap(filename);
hartmann *H = MALLOC(hartmann, 1);
H->filename = strdup(filename);
char *pos = M->data, *epos = pos + M->len;
for(; pos && pos < epos; pos = strchr(pos+1, '\n')){
spot *Spot = MALLOC(spot, 1);
double x, y;
if(3 != sscanf(pos, "%d %*s %*s %*s %*s %lf %lf", &Spot->id, &x, &y))
continue;
Spot->x = x + AxisX - ImHeight;
Spot->y = y - AxisY + ImHeight;
L++;
if(spots)
curspot = list_add(&curspot, LIST_T(Spot));
else
curspot = list_add(&spots, LIST_T(Spot));
};
H->len = L;
H->spots = spots;
My_munmap(M);
return H;
}
void h_free(hartmann **H){
listfree_function(free);
list_free(&((*H)->spots));
listfree_function(NULL);
free((*H)->filename);
FREE(*H);
}
// temporary structure for building of coordinates-gradients list
typedef struct{
double x;
double y;
double Dx;
double Dy;
} CG;
/**
*
* @param H (i) - array of thwo hartmannograms (0 - prefocal, 1 - postfocal)
* @param coords (o) - array of gradients' coordinates on prefocal image (allocated here) - the same as H[0]->spots coordinates
* @param grads (o) - gradients' array (allocated here)
* @param scale (o) - scale of polar coordinate R (== Rmax)
* @return size of built arrays
*/
size_t get_gradients(hartmann *H[], polar **coords, point **grads, double *scale){
size_t Sz = 0, i;
assert(H); assert(H[0]); assert(H[1]);
List *S0 = H[0]->spots, *S1;
List *CG_list = NULL, *curCG = NULL;
//printf(RED "\nspots\n" OLDCOLOR "\n");
double Scale = pixsize * 1e-6 / distance / 2., S = 0.; // tg(2a)=dx/D -> a \approx dx/(2D)
/*
* Both lists have sorted structure
* but they can miss some points - that's why we should find exact points.
* To store dinamycally data I use List
*/
for(; S0; S0 = S0->next){
spot *Sp0 = (spot*)S0->data;
int Id0 = Sp0->id;
S1 = H[1]->spots;
for(; S1; S1 = S1->next){
spot *Sp1 = (spot*)S1->data;
if(Sp1->id > Id0) break; // point with Id0 not found
if(Sp1->id == Id0){
CG *cg = MALLOC(CG, 1);
/* printf("id=%d (%g, %g), dx=%g, dy=%g\n", Sp0->id, Sp0->x, Sp0->y,
(Sp1->x-Sp0->x)*Scale, (Sp1->y-Sp0->y)*Scale);
*/ cg->x = Sp0->x; cg->y = Sp0->y;
cg->Dx = -(Sp1->x-Sp0->x)*Scale;
cg->Dy = -(Sp1->y-Sp0->y)*Scale;
Sz++;
if(CG_list)
curCG = list_add(&curCG, cg);
else
curCG = list_add(&CG_list, cg);
break;
}
}
}
polar *C = MALLOC(polar, Sz), *cptr = C;
point *G = MALLOC(point, Sz), *gptr = G;
curCG = CG_list;
for(i = 0; i < Sz; i++, cptr++, gptr++, curCG = curCG->next){
double x, y, length, R;
CG *cur = (CG*)curCG->data;
x = cur->x; y = cur->y;
R = sqrt(x*x + y*y);
cptr->r = R;
if(S < R) S = R; // find max R
cptr->theta = atan2(y, x);
x = cur->Dx; y = cur->Dy;
length = sqrt(1. + x*x + y*y); // length of vector for norm
gptr->x = x / length;
gptr->y = y / length;
}
cptr = C;
for(i = 0; i < Sz; i++, cptr++)
cptr->r /= S;
*scale = S;
*coords = C; *grads = G;
listfree_function(free);
list_free(&CG_list);
listfree_function(NULL);
return Sz;
}
int main(int argc, char **argv){
int _U_ i;
double scale;
hartmann _U_ *images[2];
parse_args(argc, argv);
images[0] = read_spots(name0);
images[1] = read_spots(name1);
polar *coords = NULL; point *grads = NULL;
size_t _U_ L = get_gradients(images, &coords, &grads, &scale);
h_free(&images[0]);
h_free(&images[1]);
/* printf(GREEN "\nSpots:\n" OLDCOLOR "\n\tr\ttheta\tDx\tDy\n");
for(i = 0; i < L; i++){
printf("%8.1f%8.4f%8.4f%8.4f\n", coords[i].r, coords[i].theta,
grads[i].x, grads[i].y);
}
*/ int Zsz, lastidx;
double *Zidxs = LS_gradZdecomposeR(15, L, coords, grads, &Zsz, &lastidx);
lastidx++;
printf("\n" RED "GradZ decompose, coefficients (%d):" OLDCOLOR "\n", lastidx);
for(i = 0; i < lastidx; i++) printf("%5.3f, ", Zidxs[i]);
printf("\n\n");
const int GridSize = 15;
int G2 = GridSize * GridSize;
polar *rect = MALLOC(polar, G2), *rptr = rect;
double *mirZ = MALLOC(double, G2);
#define ADD 0.
int j; double Stp = 2./((double)GridSize - 1.);
for(j = 0; j < GridSize; j++){
double y = ((double) j + ADD) * Stp - 1.;
for(i = 0; i < GridSize; i++, rptr++){
double x = ((double) i + ADD)* Stp - 1.;
double R2 = x*x + y*y;
rptr->r = sqrt(R2);
rptr->theta = atan2(y, x);
//printf("x=%g, y=%g, r=%g, t=%g\n", x,y,rptr->r, rptr->theta);
if(R2 > 1.) continue;
mirZ[j*GridSize+i] = R2 / 32.; // mirror surface, z = r^2/(4f), or (z/R) = (r/R)^2/(4[f/R])
//mirZ[j*GridSize+i] = R2;
}
}
printf("\n\nCoeff: %g\n\n", Zidxs[4]*sqrt(3.));
Zidxs[4] = 0.; Zidxs[1] = 0.; Zidxs[2] = 0.;
double *comp = ZcomposeR(lastidx, Zidxs, G2, rect);
printf("\n");
double SS = 0.; int SScntr = 0;
for(j = GridSize-1; j > -1; j--){
for(i = 0; i < GridSize; i++){
int idx = j*GridSize+i;
double Diff = mirZ[idx] - comp[idx];
// printf("%7.3f", Diff);
printf("%7.3f", comp[idx]*1e3);
if(rect[idx].r < 1.){ SS += Diff; SScntr++;}
//printf("%7.3f", (comp[idx] + 0.03)/ mirZ[idx]);
}
printf("\n");
}
SS /= SScntr;
printf("\naver: %g\n", SS);
for(j = GridSize-1; j > -1; j--){
for(i = 0; i < GridSize; i++){
int idx = j*GridSize+i;
//printf("%7.3f", (comp[idx] + SS) / mirZ[idx]);
double Z = (fabs(comp[idx]) < 2*DBL_EPSILON) ? 0. : comp[idx] + SS - mirZ[idx];
printf("%7.3f", Z * 1e3);
}
printf("\n");
}
FREE(comp); FREE(Zidxs);
FREE(coords);
FREE(grads);
return 0;
}

112
Zernike/simple_list.c Normal file
View File

@@ -0,0 +1,112 @@
/*
* simple_list.c - simple one-direction list
*
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include "simple_list.h"
#define MALLOC alloc_simple_list
/**
* Memory allocation with control
* @param N - number of elements
* @param S - size of one element
* @return allocated memory or die
*/
static void *alloc_simple_list(size_t N, size_t S){
void *p = calloc(N, S);
if(!p) err(1, "malloc");
return p;
}
void (*listdata_free)(void *node_data) = NULL; // external function to free(listdata)
#define FREE(arg) do{free(arg); arg = NULL;}while(0)
/**
* add element v to list with root root (also this can be last element)
* @param root (io) - pointer to root (or last element) of list or NULL
* if *root == NULL, just created node will be placed there
* @param v - data inserted
* @return pointer to created node
*/
List *list_add(List **root, void *v){
List *node, *last;
if((node = (List*) MALLOC(1, sizeof(List))) == 0) return NULL; // allocation error
node->data = v; // insert data
if(root){
if(*root){ // there was root node - search last
last = *root;
while(last->next) last = last->next;
last->next = node; // insert pointer to new node into last element in list
}
if(!*root) *root = node;
}
return node;
}
/**
* set listdata_free()
* @param fn - function that freec memory of (node)
*/
void listfree_function(void (*fn)(void *node)){
listdata_free = fn;
}
/**
* remove all nodes in list
* @param root - pointer to root node
*/
void list_free(List **root){
List *node = *root, *next;
if(!root || !*root) return;
do{
next = node->next;
if(listdata_free)
listdata_free(node->data);
free(node);
node = next;
}while(node);
*root = NULL;
}
#ifdef STANDALONE
int main(void) {
List *tp = NULL, *root_p = NULL;
int i, ins[] = {4,2,6,1,3,4,7};
for(i = 0; i < 7; i++){
if(!(tp = list_add(&tp, ins[i])))
err(1, "Malloc error"); // can't insert
if(!root_p) root_p = tp;
}
tp = root_p;
i = 0;
do{
printf("element %d = %d\n", i++, tp->data);
tp = tp->next;
}while(tp);
list_free(&root_p);
return 0;
}
#endif

38
Zernike/simple_list.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* simple_list.h - header file for simple list support
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#pragma once
#ifndef __SIMPLE_LIST_H__
#define __SIMPLE_LIST_H__
#define LIST_T(x) ((void*) x)
typedef struct list_{
void *data;
struct list_ *next;
} List;
// add element v to list with root root (also this can be last element)
List *list_add(List **root, void *v);
// set listdata_free()
void listfree_function(void (*fn)(void *node));
void list_free(List **root);
#endif // __SIMPLE_LIST_H__

404
Zernike/test.c Normal file
View File

@@ -0,0 +1,404 @@
/*
* test.c - test for zernike.c
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <stdio.h>
#include <math.h>
#include "zernike.h"
/**/
double IdxsOri[] = {2., // смещение, 0
1.1, -0.8, // наклон, 1-2
5.5, -3.2, 0., // астигматизм, дефокус, аст., 3-5
6.8, 5.5, 0., 0.,// трилистник, кома, 6-9
0., 0., 3.3, 1.4, 8.}; // 10-14
/**
double IdxsOri[] = {1., // смещение, 0
1., 1., // наклон, 1-2
1., 1., 0., // астигматизм, дефокус, аст., 3-5
1., 1., 0., 0.,// трилистник, кома, 6-9
0., 0., 1., 1., 1.}; // 10-14
**/
const int ORI_SZ = 15;
const int W = 16, H = 16, Pmax = 8;
//#define PLOT_
#define RAD 57.2957795130823
#define D2R(x) ((x) / RAD)
#define R2D(x) ((x) * RAD)
void plotRD(double *A, double *A1, int W, int H){
int i,j;
double S = 0., N = 0.;
printf("\n\nRemaining abs. differences:\n");
for(j = 0; j < H; j++){
for(i = 0; i < W; i++){
double d = fabs(A1[j*W+i] - A[j*W+i]);
if(d > DBL_EPSILON){
N++;
S += d;
}
printf("%5.2f ", d);
}
printf("\n");
}
double dA = S/N;
printf("average abs difference: %g\n", dA);
if(dA < 1.) return;
printf("Corrected diff:\n");
for(j = 0; j < H; j++){
for(i = 0; i < W; i++){
double X = 0.;
if(fabs(A1[j*W+i]) > DBL_EPSILON) // X = A1[j*W+i]+dA;
X = fabs(A1[j*W+i] - A[j*W+i])-dA;
printf("%6.2f ", X);
}
printf("\n");
}
}
int main(int argc, char **argv){
int i, j, lastidx;
double *Z, *Zidxs;
#if 0
//double xstart, ystart;
double pt = 4. / ((W>H) ? W-1 : H-1);
#ifdef PLOT_
Z = zernfunN(1, W,H, NULL);
printf("\nZernike for square matrix: \n");
for(j = 0; j < H; j++){
for(i = 0; i < W; i++)
printf("%5.2f ", Z[j*W+i]);
printf("\n");
}
FREE(Z);
#endif
Z = Zcompose(ORI_SZ, IdxsOri, W, H);
#ifdef PLOT_
printf("\n\nDecomposition for image: \n");
for(j = 0; j < H; j++){
for(i = 0; i < W; i++)
printf("%6.2f ", Z[j*W+i]);
printf("\n");
}
#endif
/*
xstart = (W-1)/2., ystart = (H-1)/2.;
for(j = 0; j < H; j++){
for(i = 0; i < W; i++){
double yy = (j - ystart)*pt, xx = (i - xstart)*pt;
if((xx*xx + yy*yy) <= 1.)
Z[j*W+i] =
//100*(xx*xx*xx*xx*xx-yy*yy*yy);
100.*(xx-0.3)*yy+200.*xx*yy*yy+300.*yy*yy*yy*yy;
printf("%6.2f ", Z[j*W+i]);
}
printf("\n");
}
*/
Zidxs = Zdecompose(Pmax, W, H, Z, &j, &lastidx);
printf("\nCoefficients: ");
for(i = 0; i <= lastidx; i++) printf("%5.3f, ", Zidxs[i]);
double *Zd = Zcompose(j, Zidxs, W, H);
#ifdef PLOT_
printf("\n\nComposed image:\n");
for(j = 0; j < H; j++){
for(i = 0; i < W; i++)
printf("%6.2f ", Zd[j*W+i]);
printf("\n");
}
plotRD(Z, Zd, W, H);
#endif
FREE(Zd);
//W--, H--;
point *testArr = MALLOC(point, W*H);
/*
xstart = (W-1)/2., ystart = (H-1)/2.;
for(j = 0; j < H; j++){
point *p = &testArr[j*W];
for(i = 0; i < W; i++, p++){
double yy = (j - ystart)*pt, xx = (i - xstart)*pt;
if((xx*xx + yy*yy) <= 1.){
//p->x = 500.*xx*xx*xx*xx;
//p->y = -300.*yy*yy;
p->x = 100.*yy+200.*yy*yy;
p->y = 100.*(xx-0.3)+400.*xx*yy+1200.*yy*yy*yy;
}
printf("(%4.1f,%4.1f) ",p->x, p->y);
}
printf("\n");
}
*/
for(j = 1; j < H-1; j++){
point *p = &testArr[j*W+1];
double *d = &Z[j*W+1];
for(i = 1; i < W-1; i++, p++, d++){
p->x = (d[1]-d[-1])/pt;
p->y = (d[W]-d[-W])/pt;
}
}
#ifdef PLOT_
printf("\n\nGradients of field\n");
for(j = 0; j < H; j++){
point *p = &testArr[j*W];
for(i = 0; i < W; i++, p++)
printf("(%4.1f,%4.1f) ",p->x, p->y);
printf("\n");
}
#endif
//Pmax = 2;
double *_Zidxs = gradZdecompose(Pmax, W, H, testArr, &j, &lastidx);
printf("\nCoefficients: ");
for(i = 0; i <= lastidx; i++) printf("%5.3f, ", _Zidxs[i]);
//lastidx = j;
point *gZd = gradZcompose(j, _Zidxs, W, H);
#ifdef PLOT_
printf("\n\nComposed image:\n");
for(j = 0; j < H; j++){
point *p = &gZd[j*W];
for(i = 0; i < W; i++, p++)
printf("(%4.1f,%4.1f) ",p->x, p->y);
printf("\n");
}
printf("\n\nRemaining abs differences:\n");
for(j = 0; j < H; j++){
point *p = &gZd[j*W];
point *d = &testArr[j*W];
for(i = 0; i < W; i++, p++, d++)
printf("%5.2f ",sqrt((p->x-d->x)*(p->x-d->x)+(p->y-d->y)*(p->y-d->y)));
printf("\n");
}
#endif
FREE(gZd);
FREE(testArr);
lastidx++;
double *ZidxsN = convGradIdxs(_Zidxs, lastidx);
printf("\nCoefficients for Zernike:\n i n m Z[i] gradZ[i] S[i] Zori[i]\n");
for(i = 0; i < lastidx; i++){
int n,m;
convert_Zidx(i, &n, &m);
printf("%2d%3d%3d%8.1f%8.1f%8.1f", i, n,m, Zidxs[i], ZidxsN[i],_Zidxs[i]);
if(i < ORI_SZ) printf("%8.1f", IdxsOri[i]);
printf("\n");
}
FREE(Zidxs);
#ifdef PLOT_
printf("\n\nComposed image:\n");
Zd = Zcompose(lastidx, ZidxsN, W, H);
for(j = 0; j < H; j++){
for(i = 0; i < W; i++)
printf("%6.1f ", Zd[j*W+i]);
printf("\n");
}
plotRD(Z, Zd, W, H);
FREE(Zd);
#endif
FREE(_Zidxs); FREE(ZidxsN);
#endif // 0
int Sz = 256;
double dTh = D2R(360./32);
polar *P = MALLOC(polar, Sz), *Pptr = P;
void print256(double *Par){
for(j = 0; j < 32; j++){
for(i = 0; i < 8; i++) printf("%6.1f", Par[i*32+j]);
printf("\n");
}
}
double Z_PREC = get_prec();
void print256diff(double *Ori, double *App){
printf(RED "Difference" OLDCOLOR " from original in percents:\t\t\t\t\tabs:\n");
for(j = 0; j < 32; j++){
for(i = 0; i < 8; i++){
double den = Ori[i*32+j];
if(fabs(den) > Z_PREC)
printf("%6.0f", fabs(App[i*32+j]/den-1.)*100.);
else
printf(" /0 ");
}
printf("\t");
for(i = 0; i < 8; i++)
printf("%7.3f", fabs(App[i*32+j]-Ori[i*32+j]));
printf("\n");
}
}
void print_std(double *p1, double *p2, int sz){
int i;
double d = 0., d2 = 0., dmax = 0.;
for(i = 0; i < sz; i++, p1++, p2++){
double delta = (*p2) - (*p1);
d += delta;
d2 += delta*delta;
double m = fabs(delta);
if(m > dmax) dmax = m;
}
d /= sz;
printf("\n" GREEN "Std: %g" OLDCOLOR ", max abs diff: %g\n\n", (d2/sz - d*d), dmax);
}
void print_idx_diff(double *idx){
int i;
double *I = idx;
//printf("\nDifferences of indexes (i-i0 / i/i0):\n");
printf("\nidx (app / real):\n");
for(i = 0; i < ORI_SZ; i++, I++)
printf("%d: (%3.1f / %3.1f), ", i, *I, IdxsOri[i]);
//printf("%d: (%3.1f / %3.1f), ", i, *I - IdxsOri[i], *I/IdxsOri[i]);
print_std(idx, IdxsOri, ORI_SZ);
printf("\n");
}
void mktest__(double* (*decomp_fn)(int, int, polar*, double*, int*, int*), char *msg){
Zidxs = decomp_fn(Pmax, Sz, P, Z, &j, &lastidx);
printf("\n" RED "%s, coefficients (%d):" OLDCOLOR "\n", msg, lastidx);
lastidx++;
for(i = 0; i < lastidx; i++) printf("%5.3f, ", Zidxs[i]);
printf("\nComposing: %s(%d, Zidxs, %d, P)\n", msg, lastidx, Sz);
double *comp = ZcomposeR(lastidx, Zidxs, Sz, P);
print_std(Z, comp, Sz);
print_idx_diff(Zidxs);
print256diff(Z, comp);
FREE(comp);
FREE(Zidxs);
printf("\n");
}
#define mktest(a) mktest__(a, #a)
double R_holes[] = {.175, .247, .295, .340, .379, .414, .448, .478};
for(i = 0; i < 8; i++){
// double RR = (double)i * 0.1 + 0.3;
// double RR = (double)i * 0.14+0.001;
// double RR = (double)i * 0.07 + 0.5;
double RR = R_holes[i]; // / 0.5;
double Th = 0.;
for(j = 0; j < 32; j++, Pptr++, Th += dTh){
Pptr->r = RR;
Pptr->theta = Th;
}
}
Z = ZcomposeR(ORI_SZ, IdxsOri, Sz, P);
printf(RED "Original:" OLDCOLOR "\n");
print256(Z);
mktest(ZdecomposeR);
mktest(QR_decompose);
mktest(LS_decompose);
// ann_Z(4, Sz, P, NULL);
//polar P1[] = {{0.2, 0.}, {0.6, M_PI_2}, {0.1, M_PI}, {0.92, 3.*M_PI_2}};
//ann_Z(8, 4, P1, NULL);
/*
double id_ann[] = {1.,2.,0.1,-1.,0.5};
Z = ann_Zcompose(5, id_ann, Sz, P);
*/
FREE(Z);
Z = ann_Zcompose(ORI_SZ, IdxsOri, Sz, P);
printf(RED "Annular:" OLDCOLOR "\n");
print256(Z);
Zidxs = ann_Zdecompose(7, Sz, P, Z, &j, &lastidx);
printf("\nann_Zdecompose, coefficients:\n");
for(i = 0; i <= lastidx; i++) printf("%5.3f, ", Zidxs[i]);
double *comp = ann_Zcompose(lastidx, Zidxs, Sz, P);
print_std(Z, comp, Sz);
print_idx_diff(Zidxs);
print256diff(Z, comp);
FREE(comp);
FREE(Zidxs);
/*
* TEST for gradients on hartmann's net
*/
point *gradZ = MALLOC(point, Sz);
Pptr = P;
point *dP = gradZ;
for(i = 0; i < Sz; i++, Pptr++, dP++){
double x = Pptr->r * cos(Pptr->theta), y = Pptr->r * sin(Pptr->theta);
double x2 _U_ = x*x, x3 _U_ = x*x2;
double y2 _U_ = y*y, y3 _U_ = y*y2;
double sx, cx;
sincos(x, &sx, &cx);
double val = 1000.*x3 + y2 + 30.*cx*y3;
Z[i] = val; val *= 0.05;
dP->x = 3000.*x2 - 30.*sx*y3 + val * drand48();
dP->y = 2.*y + 90.*cx*y2 + val * drand48();
/*
double val = 50.*x3*y3 + 3.*x2*y2 - 2.*x*y3 - 8.*x3*y + 7.*x*y;
Z[i] = val; val *= 0.05; // 5% from value
dP->x = 150.*x2*y3 + 6.*x*y2 - 2.*y3 - 24.*x2*y + 7.*y + val * drand48(); // + error 5%
dP->y = 150.*x3*y2 + 6.*x2*y - 6.*x*y2 - 8.*x3 + 7.*x + val * drand48();
*/
}
printf("\n" RED "Z cubic:" OLDCOLOR "\n");
print256(Z);
mktest(ZdecomposeR);
mktest(LS_decompose);
mktest(QR_decompose);
Zidxs = LS_gradZdecomposeR(Pmax+2, Sz, P, gradZ, &i, &lastidx);
lastidx++;
printf("\n" RED "GradZ decompose, coefficients (%d):" OLDCOLOR "\n", lastidx);
for(i = 0; i < lastidx; i++) printf("%5.3f, ", Zidxs[i]);
comp = ZcomposeR(lastidx, Zidxs, Sz, P);
double averD = 0.;
for(i = 0; i < Sz; i++) averD += Z[i] - comp[i];
averD /= (double)Sz;
printf("Z0 = %g\n", averD);
for(i = 0; i < Sz; i++) comp[i] += averD;
print256diff(Z, comp);
FREE(comp);
FREE(Zidxs);
Zidxs = gradZdecomposeR(Pmax+2, Sz, P, gradZ, &i, &lastidx);
/*
point *gZd = gradZcomposeR(lastidx, Zidxs, Sz, P);
printf("\n\nRemaining abs differences:\n");
for(i = 0; i < Sz; i++){
point p = gZd[i];
point d = gradZ[i];
printf("%5.2f ",sqrt((p.x-d.x)*(p.x-d.x)+(p.y-d.y)*(p.y-d.y)));
}
printf("\n");
FREE(gZd);
*/
// double *ZidxsN = convGradIdxs(Zidxs, lastidx);
// FREE(Zidxs); Zidxs = ZidxsN;
lastidx++;
printf("\n" RED "GradZ decompose, coefficients (%d):" OLDCOLOR "\n", lastidx);
for(i = 0; i < lastidx; i++) printf("%5.3f, ", Zidxs[i]);
comp = ZcomposeR(lastidx, Zidxs, Sz, P);
averD = 0.;
for(i = 0; i < Sz; i++) averD += Z[i] - comp[i];
averD /= (double)Sz;
printf("Z0 = %g\n", averD);
for(i = 0; i < Sz; i++) comp[i] += averD;
print256diff(Z, comp);
FREE(comp);
FREE(Zidxs);
FREE(Z);
return 0;
}

52
Zernike/zern_private.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* zern_private.h - private variables for zernike.c & zernikeR.c
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#pragma once
#ifndef __ZERN_PRIVATE_H__
#define __ZERN_PRIVATE_H__
#include <math.h>
#include <string.h>
#include <err.h>
#include <stdio.h>
#ifndef iabs
#define iabs(a) (((a)<(0)) ? (-a) : (a))
#endif
#ifndef DBG
#define DBG(...) do{fprintf(stderr, __VA_ARGS__); }while(0)
#endif
extern double *FK;
extern double Z_prec;
// zernike.c
void build_factorial();
void free_rpow(double ***Rpow, int n);
void build_rpow(int W, int H, int n, double **Rad, double ***Rad_pow);
double **build_rpowR(int n, int Sz, polar *P);
// zernike_annular.c
polar *conv_r(polar *r0, int Sz);
#endif // __ZERN_PRIVATE_H__

515
Zernike/zernike.c Normal file
View File

@@ -0,0 +1,515 @@
/*
* zernike.c - wavefront decomposition using Zernike polynomials
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/*
* Literature:
@ARTICLE{2007OExpr..1518014Z,
author = {{Zhao}, C. and {Burge}, J.~H.},
title = "{Orthonormal vector polynomials in a unit circle Part I: basis set derived from gradients of Zernike polynomials}",
journal = {Optics Express},
year = 2007,
volume = 15,
pages = {18014},
doi = {10.1364/OE.15.018014},
adsurl = {http://adsabs.harvard.edu/abs/2007OExpr..1518014Z},
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}
@ARTICLE{2008OExpr..16.6586Z,
author = {{Zhao}, C. and {Burge}, J.~H.},
title = "{Orthonormal vector polynomials in a unit circle, Part II : completing the basis set}",
journal = {Optics Express},
year = 2008,
month = apr,
volume = 16,
pages = {6586},
doi = {10.1364/OE.16.006586},
adsurl = {http://adsabs.harvard.edu/abs/2008OExpr..16.6586Z},
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}
*
* !!!ATTENTION!!! Axe Y points upside-down!
*/
#include "zernike.h"
#include "zern_private.h"
/**
* safe memory allocation for macro ALLOC
* @param N - number of elements to allocate
* @param S - size of single element (typically sizeof)
* @return pointer to allocated memory area
*/
void *my_alloc(size_t N, size_t S){
void *p = calloc(N, S);
if(!p) err(1, "malloc");
return p;
}
double *FK = NULL;
/**
* Build pre-computed array of factorials from 1 to 100
*/
void build_factorial(){
double F = 1.;
int i;
if(FK) return;
FK = MALLOC(double, 100);
FK[0] = 1.;
for(i = 1; i < 100; i++)
FK[i] = (F *= (double)i);
}
double Z_prec = 1e-6; // precision of Zernike coefficients
/**
* Set precision of Zernice coefficients decomposition
* @param val - new precision value
*/
void set_prec(double val){
Z_prec = val;
}
double get_prec(){
return Z_prec;
}
/**
* Convert polynomial order in Noll notation into n/m
* @param p (i) - order of Zernike polynomial in Noll notation
* @param N (o) - order of polynomial
* @param M (o) - angular parameter
*/
void convert_Zidx(int p, int *N, int *M){
int n = (int) floor((-1.+sqrt(1.+8.*p)) / 2.);
*M = (int)(2.0*(p - n*(n+1.)/2. - 0.5*(double)n));
*N = n;
}
/**
* Free array of R powers with power n
* @param Rpow (i) - array to free
* @param n - power of Zernike polinomial for that array (array size = n+1)
*/
void free_rpow(double ***Rpow, int n){
int i, N = n+1;
for(i = 0; i < N; i++) FREE((*Rpow)[i]);
FREE(*Rpow);
}
/**
* Build two arrays: with R and its powers (from 0 to n inclusive)
* for cartesian quoter I of matrix with size WxH
* @param W, H - size of initial matrix
* @param n - power of Zernike polinomial (array size = n+1)
* @param Rad (o) - NULL or array with R in quater I
* @param Rad_pow (o) - NULL or array with powers of R
*/
void build_rpow(int W, int H, int n, double **Rad, double ***Rad_pow){
double Rnorm = fmax((W - 1.) / 2., (H - 1.) / 2.);
int i,j, k, N = n+1, w = (W+1)/2, h = (H+1)/2, S = w*h;
double **Rpow = MALLOC(double*, N); // powers of R
Rpow[0] = MALLOC(double, S);
for(j = 0; j < S; j++) Rpow[0][j] = 1.; // zero's power
double *R = MALLOC(double, S);
// first - fill array of R
double xstart = (W%2) ? 0. : 0.5, ystart = (H%2) ? 0. : 0.5;
for(j = 0; j < h; j++){
double *pt = &R[j*w], x, ww = w;
for(x = xstart; x < ww; x++, pt++){
double yy = (j + ystart)/Rnorm, xx = x/Rnorm;
*pt = sqrt(xx*xx+yy*yy);
}
}
for(i = 1; i < N; i++){ // Rpow - is quater I of cartesian coordinates ('cause other are fully simmetrical)
Rpow[i] = MALLOC(double, S);
double *rp = Rpow[i], *rpo = Rpow[i-1];
for(j = 0; j < h; j++){
int idx = j*w;
double *pt = &rp[idx], *pto = &rpo[idx], *r = &R[idx];
for(k = 0; k < w; k++, pt++, pto++, r++)
*pt = (*pto) * (*r); // R^{n+1} = R^n * R
}
}
if(Rad) *Rad = R;
else FREE(R);
if(Rad_pow) *Rad_pow = Rpow;
else free_rpow(&Rpow, n);
}
/**
* Calculate value of Zernike polynomial on rectangular matrix WxH pixels
* Center of matrix will be zero point
* Scale will be set by max(W/2,H/2)
* @param n - order of polynomial (max: 100!)
* @param m - angular parameter of polynomial
* @param W - width of output array
* @param H - height of output array
* @param norm (o) - (if !NULL) normalize factor
* @return array of Zernike polynomials on given matrix
*/
double *zernfun(int n, int m, int W, int H, double *norm){
double Z = 0., *Zarr = NULL;
bool erparm = false;
if(W < 2 || H < 2)
errx(1, "Sizes of matrix must be > 2!");
if(n > 100)
errx(1, "Order of Zernike polynomial must be <= 100!");
if(n < 0) erparm = true;
if(n < iabs(m)) erparm = true; // |m| must be <= n
int d = n - m;
if(d % 2) erparm = true; // n-m must differ by a prod of 2
if(erparm)
errx(1, "Wrong parameters of Zernike polynomial (%d, %d)", n, m);
if(!FK) build_factorial();
double Xc = (W - 1.) / 2., Yc = (H - 1.) / 2.; // coordinate of circle's middle
int i, j, k, m_abs = iabs(m), iup = (n-m_abs)/2, w = (W+1)/2;
double *R, **Rpow;
build_rpow(W, H, n, &R, &Rpow);
int SS = W * H;
double ZSum = 0.;
// now fill output matrix
Zarr = MALLOC(double, SS); // output matrix W*H pixels
for(j = 0; j < H; j++){
double *Zptr = &Zarr[j*W];
double Ryd = fabs(j - Yc);
int Ry = w * (int)Ryd; // Y coordinate on R matrix
for(i = 0; i < W; i++, Zptr++){
Z = 0.;
double Rxd = fabs(i - Xc);
int Ridx = Ry + (int)Rxd; // coordinate on R matrix
if(R[Ridx] > 1.) continue; // throw out points with R>1
// calculate R_n^m
for(k = 0; k <= iup; k++){ // Sum
double z = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
/(//----------------------------------- ----- -------------------------------
FK[k]*FK[(n+m_abs)/2-k]*FK[(n-m_abs)/2-k] // k!((n+|m|)/2-k)!((n-|m|)/2-k)!
);
Z += z * Rpow[n-2*k][Ridx]; // *R^{n-2k}
}
// normalize
double eps_m = (m) ? 1. : 2.;
Z *= sqrt(2.*(n+1.) / M_PI / eps_m);
double theta = atan2(j - Yc, i - Xc);
// multiply to angular function:
if(m){
if(m > 0)
Z *= cos(theta*(double)m_abs);
else
Z *= sin(theta*(double)m_abs);
}
*Zptr = Z;
ZSum += Z*Z;
}
}
if(norm) *norm = ZSum;
// free unneeded memory
FREE(R);
free_rpow(&Rpow, n);
return Zarr;
}
/**
* Zernike polynomials in Noll notation for square matrix
* @param p - index of polynomial
* @other params are like in zernfun
* @return zernfun
*/
double *zernfunN(int p, int W, int H, double *norm){
int n, m;
convert_Zidx(p, &n, &m);
return zernfun(n,m,W,H,norm);
}
/**
* Zernike decomposition of image 'image' WxH pixels
* @param Nmax (i) - maximum power of Zernike polinomial for decomposition
* @param W, H (i) - size of image
* @param image(i) - image itself
* @param Zsz (o) - size of Z coefficients array
* @param lastIdx (o) - (if !NULL) last non-zero coefficient
* @return array of Zernike coefficients
*/
double *Zdecompose(int Nmax, int W, int H, double *image, int *Zsz, int *lastIdx){
int i, SS = W*H, pmax, maxIdx = 0;
double *Zidxs = NULL, *icopy = NULL;
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
Zidxs = MALLOC(double, pmax);
icopy = MALLOC(double, W*H);
memcpy(icopy, image, W*H*sizeof(double)); // make image copy to leave it unchanged
*Zsz = pmax;
for(i = 0; i < pmax; i++){ // now we fill array
double norm, *Zcoeff = zernfunN(i, W, H, &norm);
int j;
double *iptr = icopy, *zptr = Zcoeff, K = 0.;
for(j = 0; j < SS; j++, iptr++, zptr++)
K += (*zptr) * (*iptr) / norm; // multiply matrixes to get coefficient
Zidxs[i] = K;
if(fabs(K) < Z_prec){
Zidxs[i] = 0.;
continue; // there's no need to substract values that are less than our precision
}
maxIdx = i;
iptr = icopy; zptr = Zcoeff;
for(j = 0; j < SS; j++, iptr++, zptr++)
*iptr -= K * (*zptr); // subtract composed coefficient to reduce high orders values
FREE(Zcoeff);
}
if(lastIdx) *lastIdx = maxIdx;
FREE(icopy);
return Zidxs;
}
/**
* Zernike restoration of image WxH pixels by Zernike polynomials coefficients
* @param Zsz (i) - number of elements in coefficients array
* @param Zidxs(i) - array with Zernike coefficients
* @param W, H (i) - size of image
* @return restored image
*/
double *Zcompose(int Zsz, double *Zidxs, int W, int H){
int i, SS = W*H;
double *image = MALLOC(double, SS);
for(i = 0; i < Zsz; i++){ // now we fill array
double K = Zidxs[i];
if(fabs(K) < Z_prec) continue;
double *Zcoeff = zernfunN(i, W, H, NULL);
int j;
double *iptr = image, *zptr = Zcoeff;
for(j = 0; j < SS; j++, iptr++, zptr++)
*iptr += K * (*zptr); // add next Zernike polynomial
FREE(Zcoeff);
}
return image;
}
/**
* Components of Zj gradient without constant factor
* all parameters are as in zernfun
* @return array of gradient's components
*/
point *gradZ(int n, int m, int W, int H, double *norm){
point *gZ = NULL;
bool erparm = false;
if(W < 2 || H < 2)
errx(1, "Sizes of matrix must be > 2!");
if(n > 100)
errx(1, "Order of gradient of Zernike polynomial must be <= 100!");
if(n < 1) erparm = true;
if(n < iabs(m)) erparm = true; // |m| must be <= n
int d = n - m;
if(d % 2) erparm = true; // n-m must differ by a prod of 2
if(erparm)
errx(1, "Wrong parameters of gradient of Zernike polynomial (%d, %d)", n, m);
if(!FK) build_factorial();
double Xc = (W - 1.) / 2., Yc = (H - 1.) / 2.; // coordinate of circle's middle
int i, j, k, m_abs = iabs(m), iup = (n-m_abs)/2, isum = (n+m_abs)/2, w = (W+1)/2;
double *R, **Rpow;
build_rpow(W, H, n, &R, &Rpow);
int SS = W * H;
// now fill output matrix
gZ = MALLOC(point, SS);
double ZSum = 0.;
for(j = 0; j < H; j++){
point *Zptr = &gZ[j*W];
double Ryd = fabs(j - Yc);
int Ry = w * (int)Ryd; // Y coordinate on R matrix
for(i = 0; i < W; i++, Zptr++){
double Rxd = fabs(i - Xc);
int Ridx = Ry + (int)Rxd; // coordinate on R matrix
double Rcurr = R[Ridx];
if(Rcurr > 1. || fabs(Rcurr) < DBL_EPSILON) continue; // throw out points with R>1
double theta = atan2(j - Yc, i - Xc);
// components of grad Zj:
// 1. Theta_j
double Tj = 1., costm, sintm;
sincos(theta*(double)m_abs, &sintm, &costm);
if(m){
if(m > 0)
Tj = costm;
else
Tj = sintm;
}
// 2. dTheta_j/Dtheta
double dTj = 0.;
if(m){
if(m < 0)
dTj = m_abs * costm;
else
dTj = -m_abs * sintm;
}
// 3. R_j & dR_j/dr
double Rj = 0., dRj = 0.;
for(k = 0; k <= iup; k++){
double rj = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
/(//----------------------------------- ----- -------------------------------
FK[k]*FK[isum-k]*FK[iup-k] // k!((n+|m|)/2-k)!((n-|m|)/2-k)!
);
//DBG("rj = %g (n=%d, k=%d)\n",rj, n, k);
int kidx = n - 2*k;
Rj += rj * Rpow[kidx][Ridx]; // *R^{n-2k}
if(kidx > 0)
dRj += rj * kidx * Rpow[kidx - 1][Ridx];
}
// normalisation for Zernike
double eps_m = (m) ? 1. : 2., sq = sqrt(2.*(double)(n+1) / M_PI / eps_m);
Rj *= sq, dRj *= sq;
// 4. sin/cos
double sint, cost;
sincos(theta, &sint, &cost);
// projections of gradZj
double TdR = Tj * dRj, RdT = Rj * dTj / Rcurr;
Zptr->x = TdR * cost - RdT * sint;
Zptr->y = TdR * sint + RdT * cost;
// norm factor
ZSum += Zptr->x * Zptr->x + Zptr->y * Zptr->y;
}
}
if(norm) *norm = ZSum;
// free unneeded memory
FREE(R);
free_rpow(&Rpow, n);
return gZ;
}
/**
* Build components of vector polynomial Sj
* all parameters are as in zernfunN
* @return Sj(n,m) on image points
*/
point *zerngrad(int p, int W, int H, double *norm){
int n, m;
convert_Zidx(p, &n, &m);
if(n < 1) errx(1, "Order of gradient Z must be > 0!");
int m_abs = iabs(m);
int i,j;
double K = 1., K1 = 1.;///sqrt(2.*n*(n+1.));
point *Sj = MALLOC(point, W*H);
point *Zj = gradZ(n, m, W, H, NULL);
double Zsum = 0.;
if(n == m_abs || n < 3){ // trivial case: n = |m| (in case of n=2,m=0 n'=0 -> grad(0,0)=0
for(j = 0; j < H; j++){
int idx = j*W;
point *Sptr = &Sj[idx], *Zptr = &Zj[idx];
for(i = 0; i < W; i++, Sptr++, Zptr++){
Sptr->x = K1*Zptr->x;
Sptr->y = K1*Zptr->y;
Zsum += Sptr->x * Sptr->x + Sptr->y * Sptr->y;
}
}
}else{
K = sqrt(((double)n+1.)/(n-1.));
//K1 /= sqrt(2.);
// n != |m|
// I run gradZ() twice! But another variant (to make array of Zj) can meet memory lack
point *Zj_= gradZ(n-2, m, W, H, NULL);
for(j = 0; j < H; j++){
int idx = j*W;
point *Sptr = &Sj[idx], *Zptr = &Zj[idx], *Z_ptr = &Zj_[idx];
for(i = 0; i < W; i++, Sptr++, Zptr++, Z_ptr++){
Sptr->x = K1*(Zptr->x - K * Z_ptr->x);
Sptr->y = K1*(Zptr->y - K * Z_ptr->y);
Zsum += Sptr->x * Sptr->x + Sptr->y * Sptr->y;
}
}
FREE(Zj_);
}
FREE(Zj);
if(norm) *norm = Zsum;
return Sj;
}
/**
* Decomposition of image with normals to wavefront by Zhao's polynomials
* all like Zdecompose
* @return array of coefficients
*/
double *gradZdecompose(int Nmax, int W, int H, point *image, int *Zsz, int *lastIdx){
int i, SS = W*H, pmax, maxIdx = 0;
double *Zidxs = NULL;
point *icopy = NULL;
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
Zidxs = MALLOC(double, pmax);
icopy = MALLOC(point, SS);
memcpy(icopy, image, SS*sizeof(point)); // make image copy to leave it unchanged
*Zsz = pmax;
for(i = 1; i < pmax; i++){ // now we fill array
double norm;
point *dZcoeff = zerngrad(i, W, H, &norm);
int j;
point *iptr = icopy, *zptr = dZcoeff;
double K = 0.;
for(j = 0; j < SS; j++, iptr++, zptr++)
K += (zptr->x*iptr->x + zptr->y*iptr->y) / norm; // multiply matrixes to get coefficient
if(fabs(K) < Z_prec)
continue; // there's no need to substract values that are less than our precision
Zidxs[i] = K;
maxIdx = i;
iptr = icopy; zptr = dZcoeff;
for(j = 0; j < SS; j++, iptr++, zptr++){
iptr->x -= K * zptr->x; // subtract composed coefficient to reduce high orders values
iptr->y -= K * zptr->y;
}
FREE(dZcoeff);
}
if(lastIdx) *lastIdx = maxIdx;
FREE(icopy);
return Zidxs;
}
/**
* Restoration of image with normals Zhao's polynomials coefficients
* all like Zcompose
* @return restored image
*/
point *gradZcompose(int Zsz, double *Zidxs, int W, int H){
int i, SS = W*H;
point *image = MALLOC(point, SS);
for(i = 1; i < Zsz; i++){ // now we fill array
double K = Zidxs[i];
if(fabs(K) < Z_prec) continue;
point *Zcoeff = zerngrad(i, W, H, NULL);
int j;
point *iptr = image, *zptr = Zcoeff;
for(j = 0; j < SS; j++, iptr++, zptr++){
iptr->x += K * zptr->x;
iptr->y += K * zptr->y;
}
FREE(Zcoeff);
}
return image;
}
double *convGradIdxs(double *gradIdxs, int Zsz){
double *idxNew = MALLOC(double, Zsz);
int i;
for(i = 1; i < Zsz; i++){
int n,m;
convert_Zidx(i, &n, &m);
int j = ((n+2)*(n+4) + m) / 2;
if(j >= Zsz) j = 0;
idxNew[i] = (gradIdxs[i] - sqrt((n+3.)/(n+1.))*gradIdxs[j]);
}
return idxNew;
}

92
Zernike/zernike.h Normal file
View File

@@ -0,0 +1,92 @@
/*
* zernike.h
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#pragma once
#ifndef __ZERNIKE_H__
#define __ZERNIKE_H__
#include <stdbool.h>
#include <stdlib.h>
#define _U_ __attribute__((unused))
#define RED "\033[1;31;40m"
#define GREEN "\033[1;32;40m"
#define OLDCOLOR "\033[0;0;0m"
/*************** Data structures & typedefs ***************/
// point coordinates
typedef struct{
double x,y;
} point;
typedef struct{
double r,theta;
} polar;
// 2D array
typedef struct{
double **data;
size_t len; // size of 1D arrays
size_t num; // number of 1D arrays
}_2D;
#ifndef DBL_EPSILON
#define DBL_EPSILON 2.2204460492503131e-16
#endif
/*************** Memory allocation ***************/
#define MALLOC(type, size) ((type*)my_alloc(size, sizeof(type)))
#define FREE(ptr) do{free(ptr); ptr = NULL;}while(0)
void *my_alloc(size_t N, size_t S);
/*************** Base functions ***************/
void convert_Zidx(int p, int *N, int *M);
void set_prec(double val);
double get_prec();
/*************** Zernike on rectangular equidistant coordinate matrix ***************/
double *zernfun(int n, int m, int W, int H, double *norm);
double *zernfunN(int p, int W, int H, double *norm);
double *Zdecompose(int Nmax, int W, int H, double *image, int *Zsz, int *lastIdx);
double *Zcompose(int Zsz, double *Zidxs, int W, int H);
double *gradZdecompose(int Nmax, int W, int H, point *image, int *Zsz, int *lastIdx);
point *gradZcompose(int Zsz, double *Zidxs, int W, int H);
double *convGradIdxs(double *gradIdxs, int Zsz);
/*************** Zernike on a points set ***************/
double *zernfunR(int n, int m, int Sz, polar *P, double *norm);
double *zernfunNR(int p, int Sz, polar *P, double *norm);
double *ZdecomposeR(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx);
double *ZcomposeR(int Zsz, double *Zidxs, int Sz, polar *P);
double *LS_decompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx);
double *QR_decompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx);
double *gradZdecomposeR(int Nmax, int Sz, polar *P, point *grads, int *Zsz, int *lastIdx);
double *LS_gradZdecomposeR(int Nmax, int Sz, polar *P, point *grads, int *Zsz, int *lastIdx);
point *gradZcomposeR(int Zsz, double *Zidxs, int Sz, polar *P);
/*************** Annular Zernike ***************/
_2D *ann_Z(int pmax, int Sz, polar *P, double **Norm);
double *ann_Zcompose(int Zsz, double *Zidxs, int Sz, polar *P);
double *ann_Zdecompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx);
#endif // __ZERNIKE_H__

616
Zernike/zernikeR.c Normal file
View File

@@ -0,0 +1,616 @@
/*
* zernikeR.c - Zernike decomposition for scattered points & annular aperture
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include "zernike.h"
#include "zern_private.h"
/**
* Build array with R powers (from 0 to n inclusive)
* @param n - power of Zernike polinomial (array size = n+1)
* @param Sz - size of P array
* @param P (i) - polar coordinates of points
*/
double **build_rpowR(int n, int Sz, polar *P){
int i, j, N = n + 1;
double **Rpow = MALLOC(double*, N);
Rpow[0] = MALLOC(double, Sz);
for(i = 0; i < Sz; i++) Rpow[0][i] = 1.; // zero's power
for(i = 1; i < N; i++){ // Rpow - is quater I of cartesian coordinates ('cause other are fully simmetrical)
Rpow[i] = MALLOC(double, Sz);
double *rp = Rpow[i], *rpo = Rpow[i-1];
polar *p = P;
for(j = 0; j < Sz; j++, rp++, rpo++, p++){
*rp = (*rpo) * p->r;
}
}
return Rpow;
}
bool check_parameters(n, m, Sz, P){
bool erparm = false;
if(Sz < 3 || !P)
errx(1, "Size of matrix must be > 2!");
if(n > 100)
errx(1, "Order of Zernike polynomial must be <= 100!");
if(n < 0) erparm = true;
if(n < iabs(m)) erparm = true; // |m| must be <= n
if((n - m) % 2) erparm = true; // n-m must differ by a prod of 2
if(erparm)
errx(1, "Wrong parameters of Zernike polynomial (%d, %d)", n, m);
else
if(!FK) build_factorial();
return erparm;
}
/**
* Zernike function for scattering data
* @param n,m - orders of polynomial
* @param Sz - number of points
* @param P(i) - array with points coordinates (polar, r<=1)
* @param norm(o) - (optional) norm coefficient
* @return dynamically allocated array with Z(n,m) for given array P
*/
double *zernfunR(int n, int m, int Sz, polar *P, double *norm){
if(check_parameters(n, m, Sz, P)) return NULL;
int j, k, m_abs = iabs(m), iup = (n-m_abs)/2;
double **Rpow = build_rpowR(n, Sz, P);
double ZSum = 0.;
// now fill output matrix
double *Zarr = MALLOC(double, Sz); // output matrix
double *Zptr = Zarr;
polar *p = P;
for(j = 0; j < Sz; j++, p++, Zptr++){
double Z = 0.;
if(p->r > 1.) continue; // throw out points with R>1
// calculate R_n^m
for(k = 0; k <= iup; k++){ // Sum
double z = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
/(//----------------------------------- ----- -------------------------------
FK[k]*FK[(n+m_abs)/2-k]*FK[(n-m_abs)/2-k] // k!((n+|m|)/2-k)!((n-|m|)/2-k)!
);
Z += z * Rpow[n-2*k][j]; // *R^{n-2k}
}
// normalize
double eps_m = (m) ? 1. : 2.;
Z *= sqrt(2.*(n+1.) / M_PI / eps_m );
double m_theta = (double)m_abs * p->theta;
// multiply to angular function:
if(m){
if(m > 0)
Z *= cos(m_theta);
else
Z *= sin(m_theta);
}
*Zptr = Z;
ZSum += Z*Z;
}
if(norm) *norm = ZSum;
// free unneeded memory
free_rpow(&Rpow, n);
return Zarr;
}
/**
* Zernike polynomials in Noll notation for square matrix
* @param p - index of polynomial
* @other params are like in zernfunR
* @return zernfunR
*/
double *zernfunNR(int p, int Sz, polar *P, double *norm){
int n, m;
convert_Zidx(p, &n, &m);
return zernfunR(n,m,Sz,P,norm);
}
/**
* Zernike decomposition of image 'image' WxH pixels
* @param Nmax (i) - maximum power of Zernike polinomial for decomposition
* @param Sz, P(i) - size (Sz) of points array (P)
* @param heights(i) - wavefront walues in points P
* @param Zsz (o) - size of Z coefficients array
* @param lastIdx(o) - (if !NULL) last non-zero coefficient
* @return array of Zernike coefficients
*/
double *ZdecomposeR(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx){
int i, pmax, maxIdx = 0;
double *Zidxs = NULL, *icopy = NULL;
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
Zidxs = MALLOC(double, pmax);
icopy = MALLOC(double, Sz);
memcpy(icopy, heights, Sz*sizeof(double)); // make image copy to leave it unchanged
*Zsz = pmax;
for(i = 0; i < pmax; i++){ // now we fill array
double norm, *Zcoeff = zernfunNR(i, Sz, P, &norm);
int j;
double *iptr = icopy, *zptr = Zcoeff, K = 0.;
for(j = 0; j < Sz; j++, iptr++, zptr++)
K += (*zptr) * (*iptr) / norm; // multiply matrixes to get coefficient
if(fabs(K) < Z_prec)
continue; // there's no need to substract values that are less than our precision
Zidxs[i] = K;
maxIdx = i;
iptr = icopy; zptr = Zcoeff;
for(j = 0; j < Sz; j++, iptr++, zptr++)
*iptr -= K * (*zptr); // subtract composed coefficient to reduce high orders values
FREE(Zcoeff);
}
if(lastIdx) *lastIdx = maxIdx;
FREE(icopy);
return Zidxs;
}
/**
* Restoration of image in points P by Zernike polynomials coefficients
* @param Zsz (i) - number of actual elements in coefficients array
* @param Zidxs(i) - array with Zernike coefficients
* @param Sz, P(i) - number (Sz) of points (P)
* @return restored image
*/
double *ZcomposeR(int Zsz, double *Zidxs, int Sz, polar *P){
int i;
double *image = MALLOC(double, Sz);
for(i = 0; i < Zsz; i++){ // now we fill array
double K = Zidxs[i];
if(fabs(K) < Z_prec) continue;
double *Zcoeff = zernfunNR(i, Sz, P, NULL);
int j;
double *iptr = image, *zptr = Zcoeff;
for(j = 0; j < Sz; j++, iptr++, zptr++)
*iptr += K * (*zptr); // add next Zernike polynomial
FREE(Zcoeff);
}
return image;
}
/**
* Prints out GSL matrix
* @param M (i) - matrix to print
*/
void print_matrix(gsl_matrix *M){
int x,y, H = M->size1, W = M->size2;
size_t T = M->tda;
printf("\nMatrix %dx%d:\n", W, H);
for(y = 0; y < H; y++){
double *ptr = &(M->data[y*T]);
printf("str %6d", y);
for(x = 0; x < W; x++, ptr++)
printf("%6.1f ", *ptr);
printf("\n");
}
printf("\n\n");
}
/*
To try less-squares fit I decide after reading of
@ARTICLE{2010ApOpt..49.6489M,
author = {{Mahajan}, V.~N. and {Aftab}, M.},
title = "{Systematic comparison of the use of annular and Zernike circle polynomials for annular wavefronts}",
journal = {\ao},
year = 2010,
month = nov,
volume = 49,
pages = {6489},
doi = {10.1364/AO.49.006489},
adsurl = {http://adsabs.harvard.edu/abs/2010ApOpt..49.6489M},
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}
*/
/*
* n'th column of array m is polynomial of n'th degree,
* m'th row is m'th data point
*
* We fill matrix with polynomials by known datapoints coordinates,
* after that by less-square fit we get coefficients of decomposition
*/
double *LS_decompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx){
int pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
/*
(from GSL)
typedef struct {
size_t size1; // rows (height)
size_t size2; // columns (width)
size_t tda; // data width (aligned) - data stride
double * data; // pointer to block->data (matrix data itself)
gsl_block * block; // block with matrix data (block->size is size)
int owner; // ==1 if matrix owns 'block' (then block will be freed with matrix)
} gsl_matrix;
*/
// Now allocate matrix: its Nth row is equation for Nth data point,
// Mth column is Z_M
gsl_matrix *M = gsl_matrix_calloc(Sz, pmax);
// fill matrix with coefficients
int x,y;
size_t T = M->tda;
for(x = 0; x < pmax; x++){
double norm, *Zcoeff = zernfunNR(x, Sz, P, &norm), *Zptr = Zcoeff;
double *ptr = &(M->data[x]);
for(y = 0; y < Sz; y++, ptr+=T, Zptr++) // fill xth polynomial
*ptr = (*Zptr);
FREE(Zcoeff);
}
gsl_vector *ans = gsl_vector_calloc(pmax);
gsl_vector_view b = gsl_vector_view_array(heights, Sz);
gsl_vector *tau = gsl_vector_calloc(pmax); // min(size(M))
gsl_linalg_QR_decomp(M, tau);
gsl_vector *residual = gsl_vector_calloc(Sz);
gsl_linalg_QR_lssolve(M, tau, &b.vector, ans, residual);
double *ptr = ans->data;
int maxIdx = 0;
double *Zidxs = MALLOC(double, pmax);
for(x = 0; x < pmax; x++, ptr++){
if(fabs(*ptr) < Z_prec) continue;
Zidxs[x] = *ptr;
maxIdx = x;
}
gsl_matrix_free(M);
gsl_vector_free(ans);
gsl_vector_free(tau);
gsl_vector_free(residual);
if(lastIdx) *lastIdx = maxIdx;
return Zidxs;
}
/*
* Pseudo-annular Zernike polynomials
* They are just a linear composition of Zernike, made by Gram-Schmidt ortogonalisation
* Real Zernike koefficients restored by reverce transformation matrix
*
* Suppose we have a wavefront data in set of scattered points ${(x,y)}$, we want to
* find Zernike coefficients $z$ such that product of Zernike polynomials, $Z$, and
* $z$ give us that wavefront data with very little error (depending on $Z$ degree).
*
* Of cource, $Z$ isn't orthonormal on our basis, so we need to create some intermediate
* polynomials, $U$, which will be linear dependent from $Z$ (and this dependency
* should be strict and reversable, otherwise we wouldn't be able to reconstruct $z$
* from $u$): $U = Zk$. So, we have: $W = Uu + \epsilon$ and $W = Zz + \epsilon$.
*
* $U$ is orthonormal, so $U^T = U^{-1}$ and (unlike to $Z$) this reverce matrix
* exists and is unique. This mean that $u = W^T U = U^T W$.
* Coefficients matrix, $k$ must be inversable, so $Uk^{-1} = Z$, this mean that
* $z = uk^{-1}$.
*
* Our main goal is to find that matrix $k$.
*
* 1. QR-decomposition
* In this case non-orthogonal matrix $Z$ decomposed to orthogonal matrix $Q$ and
* right-triangular matrix $R$. In our case $Q$ is $U$ itself and $R$ is $k^{-1}$.
* QR-decomposition gives us an easy way to compute coefficient's matrix, $k$.
* Polynomials in $Q$ are linear-dependent from $Z$, each $n^{th}$ polynomial in $Q$
* depends from first $n$ polynomials in $Z$. So, columns of $R$ are coefficients
* for making $U$ from $Z$; rows in $R$ are coefficients for making $z$ from $u$.
*
* 2. Cholesky decomposition
* In this case for any non-orthogonal matrix with real values we have:
* $A^{T}A = LL^{T}$, where $L$ is left-triangular matrix.
* Orthogonal basis made by equation $U = A(L^{-1})^T$. And, as $A = UL^T$, we
* can reconstruct coefficients matrix: $k = (L^{-1})^T$.
*/
double *QR_decompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx){
int pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
if(Sz < pmax) errx(1, "Number of points must be >= number of polynomials!");
int k, x,y;
//make new polar
polar *Pn = conv_r(P, Sz);
// Now allocate matrix: its Nth row is equation for Nth data point,
// Mth column is Z_M
gsl_matrix *M = gsl_matrix_calloc(Sz, pmax);
// Q-matrix (its first pmax columns is our basis)
gsl_matrix *Q = gsl_matrix_calloc(Sz, Sz);
// R-matrix (coefficients)
gsl_matrix *R = gsl_matrix_calloc(Sz, pmax);
// fill matrix with coefficients
size_t T = M->tda;
for(x = 0; x < pmax; x++){
double norm, *Zcoeff = zernfunNR(x, Sz, Pn, &norm), *Zptr = Zcoeff;
double *ptr = &(M->data[x]);
for(y = 0; y < Sz; y++, ptr+=T, Zptr++) // fill xth polynomial
*ptr = (*Zptr) / norm;
FREE(Zcoeff);
}
gsl_vector *tau = gsl_vector_calloc(pmax); // min(size(M))
gsl_linalg_QR_decomp(M, tau);
gsl_linalg_QR_unpack(M, tau, Q, R);
//print_matrix(R);
gsl_matrix_free(M);
gsl_vector_free(tau);
double *Zidxs = MALLOC(double, pmax);
T = Q->tda;
size_t Tr = R->tda;
for(k = 0; k < pmax; k++){ // cycle by powers
double sumD = 0.;
double *Qptr = &(Q->data[k]);
for(y = 0; y < Sz; y++, Qptr+=T){ // cycle by points
sumD += heights[y] * (*Qptr);
}
Zidxs[k] = sumD;
}
gsl_matrix_free(Q);
// now restore Zernike coefficients
double *Zidxs_corr = MALLOC(double, pmax);
int maxIdx = 0;
for(k = 0; k < pmax; k++){
double sum = 0.;
double *Rptr = &(R->data[k*(Tr+1)]), *Zptr = &(Zidxs[k]);
for(x = k; x < pmax; x++, Zptr++, Rptr++){
sum += (*Zptr) * (*Rptr);
}
if(fabs(sum) < Z_prec) continue;
Zidxs_corr[k] = sum;
maxIdx = k;
}
gsl_matrix_free(R);
FREE(Zidxs);
FREE(Pn);
if(lastIdx) *lastIdx = maxIdx;
return Zidxs_corr;
}
/**
* Components of Zj gradient without constant factor
* @param n,m - orders of polynomial
* @param Sz - number of points
* @param P (i) - coordinates of reference points
* @param norm (o) - norm factor or NULL
* @return array of gradient's components
*/
point *gradZR(int n, int m, int Sz, polar *P, double *norm){
if(check_parameters(n, m, Sz, P)) return NULL;
point *gZ = NULL;
int j, k, m_abs = iabs(m), iup = (n-m_abs)/2, isum = (n+m_abs)/2;
double **Rpow = build_rpowR(n, Sz, P);
// now fill output matrix
gZ = MALLOC(point, Sz);
point *Zptr = gZ;
double ZSum = 0.;
polar *p = P;
for(j = 0; j < Sz; j++, p++, Zptr++){
if(p->r > 1.) continue; // throw out points with R>1
double theta = p->theta;
// components of grad Zj:
// 1. Theta_j; 2. dTheta_j/Dtheta
double Tj = 1., dTj = 0.;
if(m){
double costm, sintm;
sincos(theta*(double)m_abs, &sintm, &costm);
if(m > 0){
Tj = costm;
dTj = -m_abs * sintm;
}else{
Tj = sintm;
dTj = m_abs * costm;
}
}
// 3. R_j & dR_j/dr
double Rj = 0., dRj = 0.;
for(k = 0; k <= iup; k++){
double rj = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
/(//----------------------------------- ----- -------------------------------
FK[k]*FK[isum-k]*FK[iup-k] // k!((n+|m|)/2-k)!((n-|m|)/2-k)!
);
//DBG("rj = %g (n=%d, k=%d)\n",rj, n, k);
int kidx = n - 2*k;
Rj += rj * Rpow[kidx][j]; // *R^{n-2k}
if(kidx > 0)
dRj += rj * kidx * Rpow[kidx - 1][j]; // *(n-2k)*R^{n-2k-1}
}
// normalisation for Zernike
double eps_m = (m) ? 1. : 2., sq = sqrt(2.*(double)(n+1) / M_PI / eps_m);
Rj *= sq, dRj *= sq;
// 4. sin/cos
double sint, cost;
sincos(theta, &sint, &cost);
// projections of gradZj
double TdR = Tj * dRj, RdT = Rj * dTj / p->r;
Zptr->x = TdR * cost - RdT * sint;
Zptr->y = TdR * sint + RdT * cost;
// norm factor
ZSum += Zptr->x * Zptr->x + Zptr->y * Zptr->y;
}
if(norm) *norm = ZSum;
// free unneeded memory
free_rpow(&Rpow, n);
return gZ;
}
/**
* Build components of vector polynomial Sj
* @param p - index of polynomial
* @param Sz - number of points
* @param P (i) - coordinates of reference points
* @param norm (o) - norm factor or NULL
* @return Sj(n,m) on image points
*/
point *zerngradR(int p, int Sz, polar *P, double *norm){
int n, m, i;
convert_Zidx(p, &n, &m);
if(n < 1) errx(1, "Order of gradient Z must be > 0!");
int m_abs = iabs(m);
double Zsum, K = 1.;
point *Sj = gradZR(n, m, Sz, P, &Zsum);
if(n != m_abs && n > 2){ // avoid trivial case: n = |m| (in case of n=2,m=0 n'=0 -> grad(0,0)=0
K = sqrt(((double)n+1.)/(n-1.));
Zsum = 0.;
point *Zj= gradZR(n-2, m, Sz, P, NULL);
point *Sptr = Sj, *Zptr = Zj;
for(i = 0; i < Sz; i++, Sptr++, Zptr++){
Sptr->x -= K * Zptr->x;
Sptr->y -= K * Zptr->y;
Zsum += Sptr->x * Sptr->x + Sptr->y * Sptr->y;
}
FREE(Zj);
}
if(norm) *norm = Zsum;
return Sj;
}
/**
* Decomposition of image with normals to wavefront by Zhao's polynomials
* by least squares method through LU-decomposition
* @param Nmax (i) - maximum power of Zernike polinomial for decomposition
* @param Sz, P(i) - size (Sz) of points array (P)
* @param grads(i) - wavefront gradients values in points P
* @param Zsz (o) - size of Z coefficients array
* @param lastIdx(o) - (if !NULL) last non-zero coefficient
* @return array of coefficients
*/
double *LS_gradZdecomposeR(int Nmax, int Sz, polar *P, point *grads, int *Zsz, int *lastIdx){
int pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
if(Zsz) *Zsz = pmax;
// Now allocate matrix: its Nth row is equation for Nth data point,
// Mth column is Z_M
int Sz2 = Sz*2, x, y;
gsl_matrix *M = gsl_matrix_calloc(Sz2, pmax);
// fill matrix with coefficients
size_t T = M->tda, S = T * Sz; // T is matrix stride, S - index of second coefficient
for(x = 1; x < pmax; x++){
double norm;
int n, m;
convert_Zidx(x, &n, &m);
point *dZcoeff = gradZR(n, m, Sz, P, &norm), *dZptr = dZcoeff;
//point *dZcoeff = zerngradR(x, Sz, P, &norm), *dZptr = dZcoeff;
double *ptr = &(M->data[x]);
// X-component is top part of resulting matrix, Y is bottom part
for(y = 0; y < Sz; y++, ptr+=T, dZptr++){ // fill xth polynomial
*ptr = dZptr->x;
ptr[S] = dZptr->y;
}
FREE(dZcoeff);
}
gsl_vector *ans = gsl_vector_calloc(pmax);
gsl_vector *b = gsl_vector_calloc(Sz2);
double *ptr = b->data;
for(x = 0; x < Sz; x++, ptr++, grads++){
// fill components of vector b like components of matrix M
*ptr = grads->x;
ptr[Sz] = grads->y;
}
gsl_vector *tau = gsl_vector_calloc(pmax);
gsl_linalg_QR_decomp(M, tau);
gsl_vector *residual = gsl_vector_calloc(Sz2);
gsl_linalg_QR_lssolve(M, tau, b, ans, residual);
ptr = &ans->data[1];
int maxIdx = 0;
double *Zidxs = MALLOC(double, pmax);
for(x = 1; x < pmax; x++, ptr++){
if(fabs(*ptr) < Z_prec) continue;
Zidxs[x] = *ptr;
maxIdx = x;
}
gsl_matrix_free(M);
gsl_vector_free(ans);
gsl_vector_free(b);
gsl_vector_free(tau);
gsl_vector_free(residual);
if(lastIdx) *lastIdx = maxIdx;
return Zidxs;
}
/**
* Decomposition of image with normals to wavefront by Zhao's polynomials
* @param Nmax (i) - maximum power of Zernike polinomial for decomposition
* @param Sz, P(i) - size (Sz) of points array (P)
* @param grads(i) - wavefront gradients values in points P
* @param Zsz (o) - size of Z coefficients array
* @param lastIdx(o) - (if !NULL) last non-zero coefficient
* @return array of coefficients
*/
double *gradZdecomposeR(int Nmax, int Sz, polar *P, point *grads, int *Zsz, int *lastIdx){
int i, pmax, maxIdx = 0;
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
double *Zidxs = MALLOC(double, pmax);
point *icopy = MALLOC(point, Sz);
memcpy(icopy, grads, Sz*sizeof(point)); // make image copy to leave it unchanged
*Zsz = pmax;
for(i = 1; i < pmax; i++){ // now we fill array
double norm;
point *dZcoeff = zerngradR(i, Sz, P, &norm);
int j;
point *iptr = icopy, *zptr = dZcoeff;
double K = 0.;
for(j = 0; j < Sz; j++, iptr++, zptr++)
K += zptr->x*iptr->x + zptr->y*iptr->y; // multiply matrixes to get coefficient
K /= norm;
if(fabs(K) < Z_prec)
continue; // there's no need to substract values that are less than our precision
Zidxs[i] = K;
maxIdx = i;
iptr = icopy; zptr = dZcoeff;
for(j = 0; j < Sz; j++, iptr++, zptr++){
iptr->x -= K * zptr->x; // subtract composed coefficient to reduce high orders values
iptr->y -= K * zptr->y;
}
FREE(dZcoeff);
}
if(lastIdx) *lastIdx = maxIdx;
FREE(icopy);
return Zidxs;
}
/**
* Restoration of image with normals Zhao's polynomials coefficients
* all like Zcompose
* @return restored image
*/
point *gradZcomposeR(int Zsz, double *Zidxs, int Sz, polar *P){
int i;
point *image = MALLOC(point, Sz);
for(i = 1; i < Zsz; i++){ // now we fill array
double K = Zidxs[i];
if(fabs(K) < Z_prec) continue;
point *Zcoeff = zerngradR(i, Sz, P, NULL);
int j;
point *iptr = image, *zptr = Zcoeff;
for(j = 0; j < Sz; j++, iptr++, zptr++){
iptr->x += K * zptr->x;
iptr->y += K * zptr->y;
}
FREE(Zcoeff);
}
return image;
}

386
Zernike/zernike_annular.c Normal file
View File

@@ -0,0 +1,386 @@
/*
* zernike_annular.c - annular Zernike polynomials
*
* Copyright 2013 Edward V. Emelianoff <eddy@sao.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/*
* These polynomials realized according to formulae in:
@ARTICLE{1981JOSA...71...75M,
author = {{Mahajan}, V.~N.},
title = "{Zernike annular polynomials for imaging systems with annular pupils.}",
journal = {Journal of the Optical Society of America (1917-1983)},
keywords = {Astronomical Optics},
year = 1981,
volume = 71,
pages = {75-85},
adsurl = {http://adsabs.harvard.edu/abs/1981JOSA...71...75M},
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}
@ARTICLE{2006ApOpt..45.3442H,
author = {{Hou}, X. and {Wu}, F. and {Yang}, L. and {Wu}, S. and {Chen}, Q.
},
title = "{Full-aperture wavefront reconstruction from annular subaperture interferometric data by use of Zernike annular polynomials and a matrix method for testing large aspheric surfaces}",
journal = {\ao},
keywords = {Mathematics, Optical data processing, Metrology, Optical testing},
year = 2006,
month = may,
volume = 45,
pages = {3442-3455},
doi = {10.1364/AO.45.003442},
adsurl = {http://adsabs.harvard.edu/abs/2006ApOpt..45.3442H},
adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}
*/
#include "zernike.h"
#include "zern_private.h"
// epsilon (==rmin)
double eps = -1., eps2 = -1.; // this global variable changes with each conv_r call!
/**
* convert coordinates of points on ring into normalized coordinates in [0, 1]
* for further calculations of annular Zernike (expression for R_k^0)
* @param r0 (i) - original coordinates array
* @param Sz - size of r0
* @return dinamically allocated array with new coordinates, which zero element is
* (0,0)
*/
polar *conv_r(polar *r0, int Sz){
int x;
polar *Pn = MALLOC(polar, Sz);
double rmax = -1., rmin = 2.;
polar *p = r0, *p1 = Pn;
for(x = 0; x < Sz; x++, p++){
if(p->r < rmin) rmin = p->r;
if(p->r > rmax) rmax = p->r;
}
if(rmin > rmax || fabs(rmin - rmax) <= DBL_EPSILON || rmax > 1. || rmin < 0.)
errx(1, "polar coordinates should be in [0,1]!");
eps = rmin;
eps2 = eps*eps;
p = r0;
//rmax = 1. - eps2; // 1 - eps^2 -- denominator
rmax = rmax*rmax - eps2;
for(x = 0; x < Sz; x++, p++, p1++){
p1->theta = p->theta;
p1->r = sqrt((p->r*p->r - eps2) / rmax); // r = (r^2 - eps^2) / (1 - eps^2)
}
return Pn;
}
/**
* Allocate 2D array (H arrays of length W)
* @param len - array's width (number of elements in each 1D array
* @param num - array's height (number of 1D arrays)
* @return dinamically allocated 2D array filled with zeros
*/
_2D *_2ALLOC(int len, int num){
_2D *r = MALLOC(_2D, 1);
r->data = MALLOC(double*, num);
int x;
for(x = 0; x < num; x++)
r->data[x] = MALLOC(double, len);
r->len = (size_t)len;
r->num = (size_t)num;
return r;
}
void _2FREE(_2D **a){
int x, W = (*a)->num;
for(x = 0; x < W; x++) free((*a)->data[x]);
free((*a)->data);
FREE(*a);
}
/**
* Zernike radial function R_n^0
* @param n - order of polynomial
* @param Sz - number of points
* @param P (i) - modified polar coordinates: sqrt((r^2-eps^2)/(1-eps^2))
* @param R (o) - array with size Sz to which polynomial will be stored
*/
void Rn0(int n, int Sz, polar *P, double *R){
if(Sz < 1 || !P)
errx(1, "Size of matrix must be > 0!");
if(n < 0)
errx(1, "Order of Zernike polynomial must be > 0!");
if(!FK) build_factorial();
int j, k, iup = n/2;
double **Rpow = build_rpowR(n, Sz, P);
// now fill output matrix
double *Zptr = R;
polar *p = P;
for(j = 0; j < Sz; j++, p++, Zptr++){
double Z = 0.;
if(p->r > 1.) continue; // throw out points with R>1
// calculate R_n^m
for(k = 0; k <= iup; k++){ // Sum
double F = FK[iup-k];
double z = (1. - 2. * (k % 2)) * FK[n - k] // (-1)^k * (n-k)!
/(//------------------------------------- ---------------------
FK[k]*F*F // k!((n/2-k)!)^2
);
Z += z * Rpow[n-2*k][j]; // *R^{n-2k}
}
*Zptr = Z;
//DBG("order %d, point (%g, %g): %g\n", n, p->r, p->theta, Z);
}
// free unneeded memory
free_rpow(&Rpow, n);
}
/**
* Convert parameters n, m into index in Noll notation, p
* @param n, m - Zernike orders
* @return order in Noll notation
*/
inline int p_from_nm(int n, int m){
return (n*n + 2*n + m)/2;
}
/**
* Create array [p][pt] of annular Zernike polynomials
* @param pmax - max number of polynomial in Noll notation
* @param Sz - size of points array
* @param P (i) - array with points' coordinates
* @param Norm(o)- array [0..pmax] of sum^2 for each polynomial (dynamically allocated) or NULL
* @return
*/
#undef DBG
#define DBG(...)
_2D *ann_Z(int pmax, int Sz, polar *P, double **Norm){
int m, n, j, nmax, mmax, jmax, mW, jm;
convert_Zidx(pmax, &nmax, &mmax);
mmax = nmax; // for a case when p doesn't include a whole n powers
jmax = nmax / 2;
mW = mmax + 1; // width of row
jm = jmax + mW; // height of Q0 and h
polar *Pn = conv_r(P, Sz);
if(eps < 0. || eps2 < 0. || eps > 1. || eps2 > 1.)
errx(1, "Wrong epsilon value!!!");
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
_2D *h = _2ALLOC(mW, jm); // constants -> array [(jmax+1)+((mmax+1)-1)][mmax+1]
_2D *Q0= _2ALLOC(mW, jm); // Q(0) -> array [(jmax+1)+((mmax+1)-1)][mmax+1]
_2D *Q = _2ALLOC(Sz, (jmax + 1)*mW); // functions -> array [(jmax+1)*(mmax+1)][Sz]
DBG("Allocated Q&h, size of Q: %zdx%zd, size of H: %zdx%zd\n", Q->num, Q->len, h->num, h->len);
DBG("pmax = %d, nmax = mmax = %d, jmax = %d, jm = %d\n", pmax, nmax, jmax, jm);
DBG("eps=%g, eps2=%g\n", eps, eps2);
// in Q index of [j][m][point] is [j*mmax+m][point]
double **hd = h->data, **Qd = Q->data, **Q0d = Q0->data;
// fill h_j^0 & Q_j^0
for(j = 0; j < jm; j++){ // main matrix + triangle
DBG("j = %d, m = 0\n", j);
polar Zero = {0., 0.};
// h_j^0 = (1-eps^2)/(2(2j+1))
hd[j][0] = (1.-eps2)/(2.*j+1.)/2.;
// Q_j^0(rho, eps) = R_{2j}^0(r)
DBG("Q,h [%d][0], h=%g\n", j, hd[j][0]);
if(j <= jmax) Rn0(2*j, Sz, Pn, Qd[mW*j]);
Rn0(2*j, 1, &Zero, &Q0d[j][0]); // fill also Q(0)
}
FREE(Pn);
// fill h_j^m & Q_j&m
int JMAX = jm-1; // max number of j
for(m = 1; m <= mmax; m++, JMAX--){ // m in outern cycle because of Q_{j+1}!
DBG("\n\nm=%d\n",m);
int mminusone = m - 1;
int idx = mminusone;// index Q_j^{m-1}
for(j = 0; j < JMAX; j++, idx+=mW){
DBG("\nj=%d\n",j);
// 2(2j+2m-1) * h_j^{m-1}
// H_j^m = ------------------------------
// (j+m)(1-eps^2) * Q_j^{m-1}(0)
double H = 2*(2*(j+m)-1)/(j+m)/(1-eps2) * hd[j][mminusone]/Q0d[j][mminusone];
// h_j^m = -H_j^m * Q_{j+1}^{m-1}(0)
hd[j][m] = -H * Q0d[j+1][mminusone];
DBG("hd[%d][%d] = %g (H = %g)\n", j, m, hd[j][m], H);
// j Q_i^{m-1}(0) * Q_i^{m-1}(r)
// Q_j^m(r) = H_j^m * Sum -----------------------------
// i=0 h_i^{m-1}
if(j <= jmax){
int pt;
for(pt = 0; pt < Sz; pt++){ // cycle by points
int i, idx1 = mminusone;
double S = 0;
for(i = 0; i <=j; i++, idx1+=mW){ // cycle Sum
S += Q0d[i][mminusone] * Qd[idx1][pt] / hd[i][mminusone];
}
Qd[idx+1][pt] = H * S; // Q_j^m(rho^2)
//DBG("Q[%d][%d](%d) = %g\n", j, m, pt, H * S);
}
}
// and Q(0):
double S = 0.;
int i;
for(i = 0; i <=j; i++){
double qim = Q0d[i][mminusone];
S += qim*qim / hd[i][mminusone];
}
Q0d[j][m] = H * S; // Q_j^m(0)
DBG("Q[%d][%d](0) = %g\n", j, m, H * S);
}
}
_2FREE(&Q0);
// allocate memory for Z_p
_2D *Zarr = _2ALLOC(Sz, pmax + 1); // pmax is max number _including_
double **Zd = Zarr->data;
// now we should calculate R_n^m
DBG("R_{2j}^0\n");
// R_{2j}^0(rho) = Q_j^0(rho^2)
size_t S = Sz*sizeof(double);
for(j = 0; j <= jmax; j++){
int pidx = p_from_nm(2*j, 0);
if(pidx > pmax) break;
DBG("R[%d]\n", pidx);
memcpy(Zd[pidx], Qd[j*mW], S);
}
DBG("R_n^n\n");
// R_n^n(rho) = rho^n / sqrt(Sum_{i=0}^n[eps^{2i}])
double **Rpow = build_rpowR(nmax, Sz, P); // powers of rho
double epssum = 1., epspow = 1.;
for(n = 1; n <= nmax; n++){
int pidx = p_from_nm(n, -n); // R_n^{-n}
if(pidx > pmax) break;
DBG("R[%d] (%d, %d)\n", pidx, n, -n);
epspow *= eps2; // eps^{2n}
epssum += epspow;// sum_0^n eps^{2n}
double Sq = sqrt(epssum); // sqrt(sum...)
double *rptr = Zd[pidx], *pptr = Rpow[n];
int pt;
for(pt = 0; pt < Sz; pt++, rptr++, pptr++)
*rptr = *pptr / Sq; // rho^n / sqrt(...)
int pidx1 = p_from_nm(n, n); // R_n^{n}
if(pidx1 > pmax) break;
DBG("R[%d] (%d, %d)\n", pidx1, n, n);
memcpy(Zd[pidx1], Zd[pidx], S);
}
DBG("R_n^m\n");
/* / 1 - eps^2 \
R_{2j+|m|}^{|m|}(rho, eps) = sqrt| ------------------ | * rho^m * Q_j^m(rho^2)
\ 2(2j+|m|+1)*h_j^m / */
for(j = 1; j <= jmax; j++){
for(m = 1; m <= mmax; m++){
int N = 2*j + m, pt, pidx = p_from_nm(N, -m);
if(pidx > pmax) continue;
DBG("R[%d]\n", pidx);
double *rptr = Zd[pidx], *pptr = Rpow[m], *qptr = Qd[j*mW+m];
double hjm = hd[j][m];
for(pt = 0; pt < Sz; pt++, rptr++, pptr++, qptr++)
*rptr = sqrt((1-eps2)/2/(N+1)/hjm) * (*pptr) * (*qptr);
int pidx1 = p_from_nm(N, m);
if(pidx1 > pmax) continue;
DBG("R[%d]\n", pidx1);
memcpy(Zd[pidx1], Zd[pidx], S);
}
}
free_rpow(&Rpow, nmax);
_2FREE(&h);
_2FREE(&Q);
// last step: fill Z_n^m and find sum^2
int p;
double *sum2 = MALLOC(double, pmax+1);
for(p = 0; p <= pmax; p++){
convert_Zidx(p, &n, &m);
DBG("p=%d (n=%d, m=%d)\n", p, n, m);
int pt;
double (*fn)(double val);
double empty(double val){return 1.;}
if(m == 0) fn = empty;
else{if(m > 0)
fn = cos;
else
fn = sin;}
double m_abs = fabs((double)m), *rptr = Zd[p], *sptr = &sum2[p];
polar *Pptr = P;
for(pt = 0; pt < Sz; pt++, rptr++, Pptr++){
*rptr *= fn(m_abs * Pptr->theta);
//DBG("\tpt %d, val = %g\n", pt, *rptr);
*sptr += (*rptr) * (*rptr);
}
DBG("SUM p^2 = %g\n", *sptr);
}
if(Norm) *Norm = sum2;
else FREE(sum2);
return Zarr;
}
#undef DBG
#define DBG(...) do{fprintf(stderr, __VA_ARGS__); }while(0)
/**
* Compose wavefront by koefficients of annular Zernike polynomials
* @params like for ZcomposeR
* @return composed wavefront (dynamically allocated)
*/
double *ann_Zcompose(int Zsz, double *Zidxs, int Sz, polar *P){
int i;
double *image = MALLOC(double, Sz);
_2D *Zannular = ann_Z(Zsz-1, Sz, P, NULL);
double **Zcoeff = Zannular->data;
for(i = 0; i < Zsz; i++){ // now we fill array
double K = Zidxs[i];
if(fabs(K) < Z_prec) continue;
int j;
double *iptr = image, *zptr = Zcoeff[i];
for(j = 0; j < Sz; j++, iptr++, zptr++){
*iptr += K * (*zptr); // add next Zernike polynomial
}
}
_2FREE(&Zannular);
return image;
}
/**
* Decompose wavefront by annular Zernike
* @params like ZdecomposeR
* @return array of Zernike coefficients
*/
double *ann_Zdecompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx){
int i, pmax, maxIdx = 0;
double *Zidxs = NULL, *icopy = NULL;
pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
Zidxs = MALLOC(double, pmax);
icopy = MALLOC(double, Sz);
memcpy(icopy, heights, Sz*sizeof(double)); // make image copy to leave it unchanged
*Zsz = pmax;
double *norm;
_2D *Zannular = ann_Z(pmax-1, Sz, P, &norm);
double **Zcoeff = Zannular->data;
for(i = 0; i < pmax; i++){ // now we fill array
int j;
double *iptr = icopy, *zptr = Zcoeff[i], K = 0., norm_i = norm[i];
for(j = 0; j < Sz; j++, iptr++, zptr++)
K += (*zptr) * (*iptr) / norm_i; // multiply matrixes to get coefficient
if(fabs(K) < Z_prec)
continue; // there's no need to substract values that are less than our precision
Zidxs[i] = K;
maxIdx = i;
iptr = icopy; zptr = Zcoeff[i];
for(j = 0; j < Sz; j++, iptr++, zptr++)
*iptr -= K * (*zptr); // subtract composed coefficient to reduce high orders values
}
if(lastIdx) *lastIdx = maxIdx;
_2FREE(&Zannular);
FREE(norm);
FREE(icopy);
return Zidxs;
}