mirror of
https://github.com/eddyem/eddys_snippets.git
synced 2026-03-20 00:30:59 +03:00
first commit
This commit is contained in:
23
Zernike/Makefile
Normal file
23
Zernike/Makefile
Normal 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
26
Zernike/README
Normal 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
402
Zernike/Z-BTA_test.c
Normal 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
112
Zernike/simple_list.c
Normal 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
38
Zernike/simple_list.h
Normal 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
404
Zernike/test.c
Normal 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
52
Zernike/zern_private.h
Normal 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
515
Zernike/zernike.c
Normal 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
92
Zernike/zernike.h
Normal 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
616
Zernike/zernikeR.c
Normal 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
386
Zernike/zernike_annular.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user