43 #include "irplib_utils.h"
44 #include "irplib_strehl.h"
56 #ifndef IRPLIB_STREHL_RAD_CENTRAL
57 #define IRPLIB_STREHL_RAD_CENTRAL 5
60 #define IRPLIB_DISK_BG_MIN_PIX_NB 30
61 #define IRPLIB_DISK_BG_REJ_LOW 0.1
62 #define IRPLIB_DISK_BG_REJ_HIGH 0.1
68 static cpl_image * irplib_strehl_generate_otf(
double,
double,
double,
double,
70 static double PSF_H1(
double,
double,
double);
71 static double PSF_H2(
double,
double);
72 static double PSF_G(
double,
double);
73 static double PSF_sinc(
double);
74 static double PSF_TelOTF(
double,
double);
75 static cpl_error_code update_bad_pixel_map(cpl_image* im);
88 cpl_error_code update_bad_pixel_map(cpl_image* im)
90 int szx = cpl_image_get_size_x(im);
91 int szy = cpl_image_get_size_y(im);
93 cpl_mask* bpm = cpl_image_get_bpm(im);
95 for (x = 1; x <=szx; x++)
98 for(y = 1; y <= szy; y++)
101 double value = cpl_image_get(im, x, y, &isnull);
104 cpl_mask_set(bpm, x, y, CPL_BINARY_1);
108 return cpl_error_get_code();
140 cpl_error_code irplib_strehl_mark_bad_and_compute(cpl_image * im,
163 cpl_ensure_code(!update_bad_pixel_map(im), cpl_error_get_code());
164 return irplib_strehl_compute(im, m1, m2, lam, dlam, pscale, size, xpos, ypos,
212 cpl_error_code irplib_strehl_compute(
const cpl_image * im,
236 double star_radius, max_radius;
239 const double window_size = (double)(IRPLIB_STREHL_RAD_CENTRAL);
242 const double strehl_error_coefficient = CPL_MATH_PI * 0.007 / 0.0271;
246 cpl_errorstate prestate;
249 cpl_ensure_code(window_size > 0.0, CPL_ERROR_ILLEGAL_INPUT);
252 cpl_ensure_code(im != NULL, CPL_ERROR_NULL_INPUT);
253 cpl_ensure_code(strehl != NULL, CPL_ERROR_NULL_INPUT);
254 cpl_ensure_code(strehl_err != NULL, CPL_ERROR_NULL_INPUT);
255 cpl_ensure_code(star_bg != NULL, CPL_ERROR_NULL_INPUT);
256 cpl_ensure_code(star_peak != NULL, CPL_ERROR_NULL_INPUT);
257 cpl_ensure_code(star_flux != NULL, CPL_ERROR_NULL_INPUT);
258 cpl_ensure_code(psf_peak != NULL, CPL_ERROR_NULL_INPUT);
259 cpl_ensure_code(psf_flux != NULL, CPL_ERROR_NULL_INPUT);
261 cpl_ensure_code(pscale > 0.0, CPL_ERROR_ILLEGAL_INPUT);
263 cpl_ensure_code(r1 > 0.0, CPL_ERROR_ILLEGAL_INPUT);
264 cpl_ensure_code(r2 > 0.0, CPL_ERROR_ILLEGAL_INPUT);
265 cpl_ensure_code(r3 > r2, CPL_ERROR_ILLEGAL_INPUT);
271 psf = irplib_strehl_generate_psf(m1, m2, lam, dlam, pscale, size);
272 cpl_ensure_code(psf != NULL, CPL_ERROR_ILLEGAL_OUTPUT);
275 *psf_peak = cpl_image_get_max(psf);
276 cpl_image_delete(psf);
278 assert( *psf_peak > 0.0);
282 *star_bg = irplib_strehl_ring_background(im, xpos, ypos, r2/pscale, r3/pscale,
283 IRPLIB_BG_METHOD_AVER_REJ);
286 star_radius = r1/pscale;
289 *star_flux = irplib_strehl_disk_flux(im, xpos, ypos, star_radius, *star_bg);
291 cpl_ensure_code(*star_flux > 0.0, CPL_ERROR_ILLEGAL_OUTPUT);
294 max_radius = window_size < star_radius ? window_size : star_radius;
295 cpl_ensure_code(!irplib_strehl_disk_max(im, xpos, ypos, max_radius,
296 star_peak), cpl_error_get_code());
297 *star_peak -= *star_bg;
299 cpl_ensure_code(*star_peak > 0.0, CPL_ERROR_ILLEGAL_OUTPUT);
303 *strehl = (*star_peak * *psf_flux ) / ( *star_flux * *psf_peak);
306 cpl_msg_warning(cpl_func,
"Extreme Strehl-ratio=%g, star_peak=%g, "
307 "star_flux=%g, psf_peak=%g, psf_flux=%g", *strehl,
308 *star_peak, *star_flux, *psf_peak, *psf_flux);
319 prestate = cpl_errorstate_get();
320 while (cpl_flux_get_noise_ring(im, ring, noise_box_sz, noise_nsamples,
321 bg_noise, NULL) && --ring_tries > 0);
322 if (ring_tries > 0) {
323 cpl_errorstate_set(prestate);
325 return cpl_error_set_where(cpl_func);
328 *strehl_err = strehl_error_coefficient * (*bg_noise) * pscale *
329 star_radius * star_radius / *star_flux;
332 cpl_ensure_code(*strehl_err >= 0.0, CPL_ERROR_ILLEGAL_OUTPUT);
334 return CPL_ERROR_NONE;
354 double irplib_strehl_disk_flux(
const cpl_image * im,
360 const double sqr = rad * rad;
371 cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
372 cpl_ensure(cpl_image_get_type(im) == CPL_TYPE_FLOAT,
373 CPL_ERROR_UNSUPPORTED_MODE, 0.0);
374 cpl_ensure(rad > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
376 nx = cpl_image_get_size_x(im);
377 ny = cpl_image_get_size_y(im);
380 lx = (int)(xpos - rad);
381 ly = (int)(ypos - rad);
386 ux = (int)(xpos + rad) + 1;
387 uy = (int)(ypos + rad) + 1;
388 if (ux > (nx-1)) ux = nx-1;
389 if (uy > (ny-1)) uy = ny-1;
391 pim = cpl_image_get_data_float_const(im);
392 for (j=ly ; j<uy ; j++) {
393 yj = (double)j - ypos;
394 sqrest = sqr - yj * yj;
395 for (i=lx; i<ux ; i++) {
396 xi = (double)i - xpos;
397 if (sqrest >= xi * xi && irplib_isnan(pim[i+j*nx]) == 0) {
398 flux += (double)pim[i+j*nx] - bg;
420 double irplib_strehl_ring_background(
const cpl_image * im,
425 irplib_strehl_bg_method mode)
428 const double sqr_int = rad_int * rad_int;
429 const double sqr_ext = rad_ext * rad_ext;
431 cpl_vector * pix_arr;
440 cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
441 cpl_ensure(cpl_image_get_type(im) == CPL_TYPE_FLOAT,
442 CPL_ERROR_UNSUPPORTED_MODE, 0.0);
443 cpl_ensure(rad_int > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
444 cpl_ensure(rad_ext > rad_int, CPL_ERROR_ILLEGAL_INPUT, 0.0);
446 cpl_ensure(mode == IRPLIB_BG_METHOD_AVER_REJ ||
447 mode == IRPLIB_BG_METHOD_MEDIAN,
448 CPL_ERROR_UNSUPPORTED_MODE, 0.0);
450 nx = cpl_image_get_size_x(im);
451 ny = cpl_image_get_size_y(im);
454 lx = (int)(xpos - rad_ext);
455 ly = (int)(ypos - rad_ext);
460 ux = (int)(xpos + rad_ext) + 1;
461 uy = (int)(ypos + rad_ext) + 1;
462 if (ux > (nx-1)) ux = nx-1;
463 if (uy > (ny-1)) uy = ny-1;
465 npix = (ux - lx + 1) * (uy - ly + 1);
466 cpl_ensure(npix >= IRPLIB_DISK_BG_MIN_PIX_NB, CPL_ERROR_DATA_NOT_FOUND, 0.0);
469 pix_arr = cpl_vector_new(npix);
473 pim = cpl_image_get_data_float_const(im);
475 for (j=ly ; j<uy ; j++) {
476 yj = (double)j - ypos;
477 for (i=lx ; i<ux; i++) {
478 xi = (double)i - xpos;
479 dist = yj * yj + xi * xi;
480 if (sqr_int <= dist && dist <= sqr_ext &&
481 irplib_isnan(pim[i+j*nx]) == 0) {
482 cpl_vector_set(pix_arr, npix, (
double)pim[i+j*nx]);
488 if (npix < IRPLIB_DISK_BG_MIN_PIX_NB) {
489 cpl_vector_delete(pix_arr);
490 cpl_ensure(0, CPL_ERROR_DATA_NOT_FOUND, 0.0);
496 cpl_vector_set_size(pix_arr, npix);
498 if (mode == IRPLIB_BG_METHOD_AVER_REJ) {
499 const int low_ind = (int)((
double)npix * IRPLIB_DISK_BG_REJ_LOW);
500 const int high_ind = (int)((
double)npix
501 * (1.0 - IRPLIB_DISK_BG_REJ_HIGH));
504 cpl_vector_sort(pix_arr, 1);
506 for (i=low_ind ; i<high_ind ; i++) {
507 flux += cpl_vector_get(pix_arr, i);
509 if (high_ind - low_ind > 1) flux /= (double)(high_ind - low_ind);
511 flux = cpl_vector_get_median(pix_arr);
514 cpl_vector_delete(pix_arr);
540 cpl_image * irplib_strehl_generate_psf(
548 cpl_image * otf_image = irplib_strehl_generate_otf(m1, m2, lam, dlam,
551 if (otf_image == NULL)
return NULL;
562 if (cpl_image_fft(otf_image, NULL, CPL_FFT_UNNORMALIZED) ||
565 cpl_image_abs(otf_image) ||
568 cpl_image_normalise(otf_image, CPL_NORM_FLUX)) {
570 cpl_image_delete(otf_image);
596 static cpl_image * irplib_strehl_generate_otf(
604 cpl_image * otf_image;
610 double f, rsq, fc, invfc, lambda;
617 cpl_ensure(m2 > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
618 cpl_ensure(m1 > m2, CPL_ERROR_ILLEGAL_INPUT, NULL);
619 cpl_ensure(lam > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
620 cpl_ensure(dlam > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
621 cpl_ensure(size > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
622 cpl_ensure(pscale > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
625 pscale /= (double)206265;
626 lam /= (double)1.0e6;
627 dlam /= (double)1.0e6;
634 invsize = (double)1.0 / (
double)size;
637 f_max = m1 * pscale * (double)size / lam;
640 otf_image = cpl_image_new(size, size, CPL_TYPE_DOUBLE);
641 if (otf_image==NULL)
return NULL;
642 otf_data = cpl_image_get_data_double(otf_image);
647 for (k=1 ; k<=9 ; k++) {
649 lambda = (double)(lam - dlam*(
double)(k-5)/8.0);
650 fc = (double)f_max * (
double)lam / lambda;
655 for (j=0 ; j<size ; j++) {
656 y = (double)(j-pix0);
657 sincy = PSF_sinc(CPL_MATH_PI * y * invsize);
658 for (i=0 ; i<size ; i++) {
659 x = (double)(i-pix0);
665 f = sqrt(rsq) * invfc;
666 a = PSF_TelOTF(f,obs_ratio) *
667 PSF_sinc(CPL_MATH_PI * x * invsize) * sincy;
672 otf_data[pos++] += a / 9.0;
682 static double PSF_H1(
687 const double e = fabs(1.0-v) > 0.0 ? -1.0 : 1.0;
689 return((v*v/CPL_MATH_PI)*acos((f/v)*(1.0+e*(1.0-u*u)/(4.0*f*f))));
695 static double PSF_H2(
double f,
698 const double tmp1 = (2.0 * f) / (1.0 + u);
699 const double tmp2 = (1.0 - u) / (2.0 * f);
701 return -1.0 * (f/CPL_MATH_PI) * (1.0+u)
702 * sqrt((1.0-tmp1*tmp1)*(1.0-tmp2*tmp2));
708 static double PSF_G(
double f,
711 if (f <= (1.0-u)/2.0)
return(u*u);
712 if (f >= (1.0+u)/2.0)
return(0.0);
713 else return(PSF_H1(f,u,1.0) + PSF_H1(f,u,u) + PSF_H2(f,u));
719 static double PSF_sinc(
double x)
721 return fabs(x) > fabs(sin(x)) ? sin(x)/x : 1.0;
727 static double PSF_TelOTF(
double f,
730 return((PSF_G(f,1.0)+u*u*PSF_G(f/u,1.0)-2.0*PSF_G(f,u))/(1.0-u*u));
746 cpl_error_code irplib_strehl_disk_max(
const cpl_image *
self,
753 const double sqr = radius * radius;
756 float peak = FLT_MAX;
761 cpl_boolean first = CPL_TRUE;
765 cpl_ensure_code(ppeak != NULL, CPL_ERROR_NULL_INPUT);
766 cpl_ensure_code(
self != NULL, CPL_ERROR_NULL_INPUT);
767 cpl_ensure_code(cpl_image_get_type(
self) == CPL_TYPE_FLOAT,
768 CPL_ERROR_UNSUPPORTED_MODE);
769 cpl_ensure_code(radius > 0.0, CPL_ERROR_ILLEGAL_INPUT);
771 nx = cpl_image_get_size_x(
self);
772 ny = cpl_image_get_size_y(
self);
775 lx = (int)(xpos - radius);
776 ly = (int)(ypos - radius);
781 ux = (int)(xpos + radius) + 1;
782 uy = (int)(ypos + radius) + 1;
783 if (ux > (nx-1)) ux = nx-1;
784 if (uy > (ny-1)) uy = ny-1;
786 pself = cpl_image_get_data_float_const(
self);
787 for (j=ly ; j<uy ; j++) {
788 yj = (double)j - ypos;
789 sqrest = sqr - yj * yj;
790 for (i=lx; i<ux ; i++) {
791 xi = (double)i - xpos;
792 if (sqrest >= xi * xi && irplib_isnan(pself[i+j*nx]) == 0) {
793 if (first || pself[i+j*nx] > peak) {
795 peak = pself[i+j*nx];
801 cpl_ensure_code(!first, CPL_ERROR_DATA_NOT_FOUND);
803 *ppeak = (double)peak;
805 return CPL_ERROR_NONE;