CR2RE Pipeline Reference Manual 1.6.8
hdrl_strehl.c
1/*
2 * This file is part of the HDRL
3 * Copyright (C) 2014 European Southern Observatory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24/*-----------------------------------------------------------------------------
25 Includes
26-----------------------------------------------------------------------------*/
27/* for j1 */
28#if !defined(_XOPEN_SOURCE) || (_XOPEN_SOURCE - 0) < 600
29#define _XOPEN_SOURCE 600
30#endif
31
32#include "hdrl_strehl.h"
33#include "hdrl_image.h"
34#include "hdrl_types.h"
35#include "hdrl_utils.h"
36
37#include <cpl.h>
38#include <assert.h>
39#include <math.h>
40#include <stdint.h>
41
42/*-----------------------------------------------------------------------------
43 Static
44 -----------------------------------------------------------------------------*/
45
46
47static hdrl_strehl_result bad_result = {
48 {NAN, NAN},
49 NAN, NAN,
50 {NAN, NAN},
51 {NAN, NAN},
52 {NAN, NAN},
53 NAN,
54 0
55};
56
76/*----------------------------------------------------------------------------*/
77
81/*-----------------------------------------------------------------------------
82 Strehl parameters Definition
83 -----------------------------------------------------------------------------*/
84
87typedef struct {
88 HDRL_PARAMETER_HEAD;
89 double wavelength ;
90 double m1 ;
91 double m2 ;
92 double pixel_scale_x;
93 double pixel_scale_y;
94 double flux_radius;
95 double bkg_radius_low;
96 double bkg_radius_high;
97} hdrl_strehl_parameter;
98
99/* parameter type */
100static hdrl_parameter_typeobj hdrl_strehl_parameter_type = {
101 HDRL_PARAMETER_STREHL, /* type */
102 (hdrl_alloc *)&cpl_malloc, /* fp_alloc */
103 (hdrl_free *)&cpl_free, /* fp_free */
104 NULL, /* fp_destroy */
105 sizeof(hdrl_strehl_parameter), /* obj_size */
106};
107
108
109
110/*----------------------------------------------------------------------------*/
117/*----------------------------------------------------------------------------*/
118static cpl_error_code
119hdrl_strehl_parameter_verify(const hdrl_parameter * param)
120{
121 cpl_error_ensure(param != NULL, CPL_ERROR_NULL_INPUT,
122 return CPL_ERROR_NULL_INPUT, "NULL Input Parameters");
123 cpl_error_ensure(hdrl_strehl_parameter_check(param),
124 CPL_ERROR_ILLEGAL_INPUT, return CPL_ERROR_ILLEGAL_INPUT,
125 "Expected Strehl parameter") ;
126
127 const hdrl_strehl_parameter * param_loc = (const hdrl_strehl_parameter *)param ;
128
129 cpl_error_ensure(param_loc->wavelength >= 0, CPL_ERROR_ILLEGAL_INPUT,
130 return CPL_ERROR_ILLEGAL_INPUT, "wavelength must be >=0");
131 cpl_error_ensure(param_loc->m1 >= 0, CPL_ERROR_ILLEGAL_INPUT,
132 return CPL_ERROR_ILLEGAL_INPUT, "m1 radius must be >=0");
133 cpl_error_ensure(param_loc->m2 >= 0, CPL_ERROR_ILLEGAL_INPUT,
134 return CPL_ERROR_ILLEGAL_INPUT, "m2 radius must be >=0");
135 cpl_error_ensure(param_loc->m1 > param_loc->m2, CPL_ERROR_ILLEGAL_INPUT,
136 return CPL_ERROR_ILLEGAL_INPUT,
137 "m1 radius must be larger than m2 radius");
138 cpl_error_ensure(param_loc->pixel_scale_x >= 0, CPL_ERROR_ILLEGAL_INPUT,
139 return CPL_ERROR_ILLEGAL_INPUT, "pixel_scale_x must be >=0");
140 cpl_error_ensure(param_loc->pixel_scale_y >= 0, CPL_ERROR_ILLEGAL_INPUT,
141 return CPL_ERROR_ILLEGAL_INPUT, "pixel_scale_y must be >=0");
142
143 cpl_error_ensure(param_loc->flux_radius >= 0, CPL_ERROR_ILLEGAL_INPUT,
144 return CPL_ERROR_ILLEGAL_INPUT, "flux_radius must be >=0");
145
146 cpl_error_ensure(param_loc->m1 >= param_loc->m2, CPL_ERROR_ILLEGAL_INPUT,
147 return CPL_ERROR_ILLEGAL_INPUT, "m1 must be >=m2");
148 if (param_loc->bkg_radius_low > 0) {
149 cpl_error_ensure(param_loc->bkg_radius_low >= param_loc->flux_radius,
150 CPL_ERROR_ILLEGAL_INPUT,return CPL_ERROR_ILLEGAL_INPUT,
151 "bkg_radius_low must be >=flux_radius");
152 cpl_error_ensure(param_loc->bkg_radius_high > param_loc->bkg_radius_low,
153 CPL_ERROR_ILLEGAL_INPUT,return CPL_ERROR_ILLEGAL_INPUT,
154 "bkg_radius_high must be >bkg_radius_low");
155 }
156 else {
157 cpl_error_ensure(param_loc->bkg_radius_high < 0,
158 CPL_ERROR_ILLEGAL_INPUT, return CPL_ERROR_ILLEGAL_INPUT,
159 "bkg_radius_high must be < 0 if bkg_radius_low is < 0");
160 }
161
162 return CPL_ERROR_NONE ;
163
164}
165
168/*----------------------------------------------------------------------------*/
188hdrl_parameter * hdrl_strehl_parameter_create(double wavelength,
189 double m1_radius, double m2_radius,
190 double pixel_scale_x, double pixel_scale_y, double flux_radius,
191 double bkg_radius_low, double bkg_radius_high) {
192
193
194 hdrl_strehl_parameter * p = (hdrl_strehl_parameter *)
195 hdrl_parameter_new(&hdrl_strehl_parameter_type);
196
197 p->wavelength = wavelength ;
198 p->m1 = m1_radius;
199 p->m2 = m2_radius;
200 p->pixel_scale_x = pixel_scale_x;
201 p->pixel_scale_y = pixel_scale_y;
202 p->flux_radius = flux_radius;
203 p->bkg_radius_low = bkg_radius_low;
204 p->bkg_radius_high = bkg_radius_high;
205
206 if (hdrl_strehl_parameter_verify((hdrl_parameter *)p)) {
207 cpl_free(p);
208 return NULL;
209 }
210 return (hdrl_parameter *)p;
211
212}
213
214/*----------------------------------------------------------------------------*/
220/*----------------------------------------------------------------------------*/
221cpl_boolean hdrl_strehl_parameter_check(const hdrl_parameter * self)
222{
223 return hdrl_parameter_check_type(self, &hdrl_strehl_parameter_type);
224}
225
226/*----------------------------------------------------------------------------*/
232/*----------------------------------------------------------------------------*/
234 const hdrl_parameter * p)
235{
236 cpl_ensure(p, CPL_ERROR_NULL_INPUT, -1.0);
237 return p != NULL ? ((const hdrl_strehl_parameter *)p)->wavelength : 0.;
238}
239
240/*----------------------------------------------------------------------------*/
246/*----------------------------------------------------------------------------*/
248 const hdrl_parameter * p)
249{
250 cpl_ensure(p, CPL_ERROR_NULL_INPUT, -1.0);
251 return p != NULL ? ((const hdrl_strehl_parameter *)p)->m1 : 0.;
252}
253
254/*----------------------------------------------------------------------------*/
260/*----------------------------------------------------------------------------*/
262 const hdrl_parameter * p)
263{
264 cpl_ensure(p, CPL_ERROR_NULL_INPUT, -1.0);
265 return p != NULL ? ((const hdrl_strehl_parameter *)p)->m2 : 0.;
266}
267
268/*----------------------------------------------------------------------------*/
274/*----------------------------------------------------------------------------*/
276 const hdrl_parameter * p)
277{
278 cpl_ensure(p, CPL_ERROR_NULL_INPUT, -1.0);
279 return p != NULL ? ((const hdrl_strehl_parameter *)p)->pixel_scale_x : 0.;
280}
281
282/*----------------------------------------------------------------------------*/
288/*----------------------------------------------------------------------------*/
290 const hdrl_parameter * p)
291{
292 cpl_ensure(p, CPL_ERROR_NULL_INPUT, -1.0);
293 return p != NULL ? ((const hdrl_strehl_parameter *)p)->pixel_scale_y : 0.;
294}
295
296/*----------------------------------------------------------------------------*/
302/*----------------------------------------------------------------------------*/
304 const hdrl_parameter * p)
305{
306 cpl_ensure(p, CPL_ERROR_NULL_INPUT, -1.0);
307 return p != NULL ? ((const hdrl_strehl_parameter *)p)->flux_radius : 0.;
308}
309
310/*----------------------------------------------------------------------------*/
316/*----------------------------------------------------------------------------*/
318 const hdrl_parameter * p)
319{
320 cpl_ensure(p, CPL_ERROR_NULL_INPUT, -1.0);
321 return p != NULL ? ((const hdrl_strehl_parameter *)p)->bkg_radius_low : 0.;
322}
323
324/*----------------------------------------------------------------------------*/
330/*----------------------------------------------------------------------------*/
332 const hdrl_parameter * p)
333{
334 cpl_ensure(p, CPL_ERROR_NULL_INPUT, -1.0);
335 return p != NULL ? ((const hdrl_strehl_parameter *)p)->bkg_radius_high : 0.;
336}
337
338/*----------------------------------------------------------------------------*/
359/*----------------------------------------------------------------------------*/
361 const char *base_context,
362 const char *prefix,
363 hdrl_parameter *par)
364{
365
366 cpl_ensure(prefix && base_context && par,
367 CPL_ERROR_NULL_INPUT, NULL);
368
369 cpl_ensure(hdrl_strehl_parameter_check(par),
370 CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
371
372 cpl_parameterlist *parlist = cpl_parameterlist_new();
373
374 /* --prefix.wavelength */
375 hdrl_setup_vparameter(parlist, prefix, ".", "", "wavelength", base_context,
376 "Wavelength [m].", CPL_TYPE_DOUBLE,
378
379 /* --prefix.m1 */
380 hdrl_setup_vparameter(parlist, prefix, ".", "", "m1", base_context,
381 "Telescope radius [m].", CPL_TYPE_DOUBLE,
383
384 /* --prefix.m2 */
385 hdrl_setup_vparameter(parlist, prefix, ".", "", "m2", base_context,
386 "Telescope obstruction radius [m].", CPL_TYPE_DOUBLE,
388
389 /* --prefix.pixscale_x */
390 hdrl_setup_vparameter(parlist, prefix, ".", "", "pixel-scale-x", base_context,
391 "Detector X pixel scale on sky [arcsec].",
392 CPL_TYPE_DOUBLE,
394
395 /* --prefix.pixscale_y */
396 hdrl_setup_vparameter(parlist, prefix, ".", "", "pixel-scale-y", base_context,
397 "Detector Y pixel scale on sky [arcsec].",
398 CPL_TYPE_DOUBLE,
400
401 /* --prefix.flux_radius */
402 hdrl_setup_vparameter(parlist, prefix, ".", "", "flux-radius", base_context,
403 "PSF Flux integration radius [arcsec].",
404 CPL_TYPE_DOUBLE,
406
407 /* --prefix.bkg_radius_low_def */
408 hdrl_setup_vparameter(parlist, prefix, ".", "", "bkg-radius-low", base_context,
409 "PSF background inner radii [arcsec].",
410 CPL_TYPE_DOUBLE,
412
413 /* --prefix.bkg_radius_high_def */
414 hdrl_setup_vparameter(parlist, prefix, ".", "", "bkg-radius-high", base_context,
415 "PSF background outer radius [arcsec].",
416 CPL_TYPE_DOUBLE,
418
419 if (cpl_error_get_code()) {
420 cpl_parameterlist_delete(parlist);
421 return NULL;
422 }
423
424 return parlist;
425}
426
427/*----------------------------------------------------------------------------*/
447/*----------------------------------------------------------------------------*/
449 const cpl_parameterlist * parlist,
450 const char * prefix)
451{
452 cpl_ensure(prefix && parlist, CPL_ERROR_NULL_INPUT, NULL);
453 char * name;
454 const cpl_parameter * par;
455 double wavelength, m1, m2, pixel_scale_x, pixel_scale_y,
456 flux_radius, bkg_radius_low,bkg_radius_high;
457
458
459 /* --wavelength */
460 name = hdrl_join_string(".", 2, prefix, "wavelength");
461 par=cpl_parameterlist_find_const(parlist, name);
462 wavelength = cpl_parameter_get_double(par);
463 cpl_free(name) ;
464
465 /* --m1 */
466 name = hdrl_join_string(".", 2, prefix, "m1");
467 par=cpl_parameterlist_find_const(parlist, name);
468 m1 = cpl_parameter_get_double(par);
469 cpl_free(name) ;
470
471 /* --m2 */
472 name = hdrl_join_string(".", 2, prefix, "m2");
473 par=cpl_parameterlist_find_const(parlist, name);
474 m2 = cpl_parameter_get_double(par);
475 cpl_free(name) ;
476
477 /* --pixel_scale_x */
478 name = hdrl_join_string(".", 2, prefix, "pixel-scale-x");
479 par=cpl_parameterlist_find_const(parlist, name);
480 pixel_scale_x = cpl_parameter_get_double(par);
481 cpl_free(name) ;
482
483 /* --pixel_scale_y */
484 name = hdrl_join_string(".", 2, prefix, "pixel-scale-y");
485 par=cpl_parameterlist_find_const(parlist, name);
486 pixel_scale_y = cpl_parameter_get_double(par);
487 cpl_free(name) ;
488
489 /* --flux_radius */
490 name = hdrl_join_string(".", 2, prefix, "flux-radius");
491 par=cpl_parameterlist_find_const(parlist, name);
492 flux_radius = cpl_parameter_get_double(par);
493 cpl_free(name) ;
494
495 /* --bkg_radius_low */
496 name = hdrl_join_string(".", 2, prefix, "bkg-radius-low");
497 par=cpl_parameterlist_find_const(parlist, name);
498 bkg_radius_low = cpl_parameter_get_double(par);
499 cpl_free(name) ;
500
501 /* --bkg_radius_high */
502 name = hdrl_join_string(".", 2, prefix, "bkg-radius-high");
503 par=cpl_parameterlist_find_const(parlist, name);
504 bkg_radius_high = cpl_parameter_get_double(par);
505 cpl_free(name) ;
506
507
508 if (cpl_error_get_code()) {
509 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
510 "Error while parsing parameterlist with prefix %s", prefix);
511 return NULL;
512 } else {
513 return hdrl_strehl_parameter_create(wavelength, m1,m2,
514 pixel_scale_x, pixel_scale_y,
515 flux_radius, bkg_radius_low, bkg_radius_high) ;
516 }
517
518}
519
520
523/* ---------------------------------------------------------------------------*/
531/* ---------------------------------------------------------------------------*/
532static hdrl_value
533hdrl_image_max_where(const hdrl_image * himg, cpl_mask * mask)
534{
535 hdrl_image * tmpimg = hdrl_image_duplicate(himg);
536 cpl_size px, py;
537 hdrl_value mx;
538 hdrl_image_reject_from_mask(tmpimg, mask);
539 cpl_image_get_maxpos(hdrl_image_get_image(tmpimg), &px, &py);
540 mx = hdrl_image_get_pixel(tmpimg, px, py, NULL);
541 hdrl_image_delete(tmpimg);
542 return mx;
543}
544
545/* ---------------------------------------------------------------------------*/
553/* ---------------------------------------------------------------------------*/
554static hdrl_value
555hdrl_image_sum_where(const hdrl_image * himg, cpl_mask * mask)
556{
557 hdrl_image * tmpimg = hdrl_image_duplicate(himg);
558 hdrl_value flux;
559 hdrl_image_reject_from_mask(tmpimg, mask);
560 flux = hdrl_image_get_sum(tmpimg);
561 hdrl_image_delete(tmpimg);
562 return flux;
563}
564
565/* ---------------------------------------------------------------------------*/
573/* ---------------------------------------------------------------------------*/
574static hdrl_value
575hdrl_image_median_where(const hdrl_image * himg, cpl_mask * mask)
576{
577 hdrl_image * tmpimg = hdrl_image_duplicate(himg);
578 hdrl_value flux;
579 hdrl_image_reject_from_mask(tmpimg, mask);
580 flux = hdrl_image_get_median(tmpimg);
581 hdrl_image_delete(tmpimg);
582 return flux;
583}
584
585/* ---------------------------------------------------------------------------*/
593/* ---------------------------------------------------------------------------*/
594static double
595hdrl_image_stdev_where(const hdrl_image * himg, cpl_mask * mask)
596{
597 hdrl_image * tmpimg = hdrl_image_duplicate(himg);
598 double mad;
599 hdrl_image_reject_from_mask(tmpimg, mask);
600 cpl_image_get_mad(hdrl_image_get_image_const(tmpimg), &mad);
601 hdrl_image_delete(tmpimg);
602 return mad * CPL_MATH_STD_MAD;
603}
604
605/* ---------------------------------------------------------------------------*/
638/* ---------------------------------------------------------------------------*/
639static cpl_image *
640compute_psf(double lam, double m1, double m2,
641 double pixscale_x, double pixscale_y,
642 double cx, double cy,
643 size_t nx, size_t ny)
644{
645 cpl_image * psf = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
646 double * data = cpl_image_get_data(psf);
647 double e = m2 / m1;
648 double as_2_rad = CPL_MATH_2PI / (360. * 3600);
649 /* inclusive linear space with pixel center at the middle. integer cx and cy
650 * will result in a psf exactly centered around the middle of the central
651 * pixel, cx/cy are in fits convention [1,nx/ny] with pixel origin in the
652 * middle */
653 double centerx = (-(nx / 2.) + cx - 1 + 0.5) * pixscale_x;
654 double centery = (-(ny / 2.) + cy - 1 + 0.5) * pixscale_y;
655 double xhigh = ((nx - 1) * pixscale_x / 2) - centerx;
656 double yhigh = ((ny - 1) * pixscale_y / 2) - centery;
657 double xlow = -((nx - 1) * pixscale_x / 2) - centerx;
658 double ylow = -((ny - 1) * pixscale_y / 2) - centery;
659 double step_x = (xhigh - xlow) / (nx - 1);
660 double step_y = (yhigh - ylow) / (ny - 1);
661
662 HDRL_OMP(omp parallel for)
663 for (size_t iy = 0; iy < ny; iy++) {
664 double y = iy == ny - 1 ? yhigh : ylow + iy * step_y;
665 for (size_t ix = 0; ix < nx; ix++) {
666 double x = ix == nx - 1 ? xhigh : xlow + ix * step_x;
667 double r = sqrt(x*x + y*y) * as_2_rad * CPL_MATH_2PI * m1 / lam;
668 if (r == 0.) {
669 data[iy * nx + ix] = 1.;
670 }
671 else {
672 double airy = (2 * j1(r) / r - 2 * e * j1(e * r) / r);
673 double c = (1 - e * e);
674 data[iy * nx + ix] = 1 / (c * c) * airy * airy;
675 }
676 }
677 }
678 return psf;
679}
680
681/*----------------------------------------------------------------------------*/
701/*----------------------------------------------------------------------------*/
702static cpl_error_code apertures_find_max_flux(const cpl_apertures * self,
703 int * ind, int nfind)
704{
705 const int nsize = cpl_apertures_get_size(self);
706 int ifind;
707
708
709 cpl_ensure_code(nsize > 0, cpl_error_get_code());
710 cpl_ensure_code(ind, CPL_ERROR_NULL_INPUT);
711 cpl_ensure_code(nfind > 0, CPL_ERROR_ILLEGAL_INPUT);
712 cpl_ensure_code(nfind <= nsize, CPL_ERROR_ILLEGAL_INPUT);
713
714 for (ifind=0; ifind < nfind; ifind++) {
715 double maxflux = -1;
716 int maxind = -1;
717 int i;
718 for (i=1; i <= nsize; i++) {
719 int k;
720
721 /* The flux has to be the highest among those not already found */
722 for (k=0; k < ifind; k++) if (ind[k] == i) break;
723
724 if (k == ifind) {
725 /* i has not been inserted into ind */
726 const double flux = cpl_apertures_get_flux(self, i);
727
728 if (maxind < 0 || flux > maxflux) {
729 maxind = i;
730 maxflux = flux;
731 }
732 }
733 }
734 ind[ifind] = maxind;
735 }
736
737 return CPL_ERROR_NONE;
738
739}
740
741/*----------------------------------------------------------------------------*/
759/*----------------------------------------------------------------------------*/
760static cpl_error_code
761gaussian_maxpos(const cpl_image * self,
762 double sigma,
763 double * pxpos,
764 double * pypos,
765 double * ppeak)
766{
767 /* copied from irplib_strehl.c r163170 */
768 const cpl_size nx = cpl_image_get_size_x(self);
769 const cpl_size ny = cpl_image_get_size_y(self);
770 int iretry = 3; /* Number retries with decreasing sigma */
771 int ifluxapert = 0;
772 double med_dist;
773 const double median = cpl_image_get_median_dev(self, &med_dist);
774 cpl_mask * selection;
775 cpl_size nlabels = 0;
776 cpl_image * labels = NULL;
777 cpl_apertures * aperts;
778 cpl_size npixobj;
779 double objradius;
780 cpl_size winsize;
781 cpl_size xposmax, yposmax;
782 double xposcen, yposcen;
783 double valmax, valfit = -1.0;
784 cpl_array * gauss_parameters = NULL;
785 cpl_errorstate prestate = cpl_errorstate_get();
786 cpl_error_code code = CPL_ERROR_NONE;
787
788
789 cpl_ensure_code( sigma > 0.0, CPL_ERROR_ILLEGAL_INPUT);
790
791 selection = cpl_mask_new(nx, ny);
792
793 /* find aperture with signal larger than sigma * median deviation */
794 for (; iretry > 0 && nlabels == 0; iretry--, sigma *= 0.5) {
795
796 /* Compute the threshold */
797 const double threshold = median + sigma * med_dist;
798
799
800 /* Select the pixel above the threshold */
801 code = cpl_mask_threshold_image(selection, self, threshold, DBL_MAX,
802 CPL_BINARY_1);
803
804 if (code) break;
805
806 /* Labelise the thresholded selection */
807 cpl_image_delete(labels);
808 labels = cpl_image_labelise_mask_create(selection, &nlabels);
809 }
810 sigma *= 2.0; /* reverse last iteration that found no labels */
811
812 cpl_mask_delete(selection);
813
814 if (code) {
815 cpl_image_delete(labels);
816 return cpl_error_set_where(cpl_func);
817 } else if (nlabels == 0) {
818 cpl_image_delete(labels);
819 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
820 }
821
822 aperts = cpl_apertures_new_from_image(self, labels);
823
824 /* Find the aperture with the greatest flux */
825 code = apertures_find_max_flux(aperts, &ifluxapert, 1);
826
827 if (code) {
828 cpl_apertures_delete(aperts);
829 cpl_image_delete(labels);
830 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
831 }
832
833 npixobj = cpl_apertures_get_npix(aperts, ifluxapert);
834 objradius = sqrt((double)npixobj * CPL_MATH_1_PI);
835 winsize = CX_MIN(CX_MIN(nx, ny), (3.0 * objradius));
836
837 xposmax = cpl_apertures_get_maxpos_x(aperts, ifluxapert);
838 yposmax = cpl_apertures_get_maxpos_y(aperts, ifluxapert);
839 xposcen = cpl_apertures_get_centroid_x(aperts, ifluxapert);
840 yposcen = cpl_apertures_get_centroid_y(aperts, ifluxapert);
841 valmax = cpl_apertures_get_max(aperts, ifluxapert);
842
843 cpl_apertures_delete(aperts);
844 cpl_image_delete(labels);
845
846 cpl_msg_debug(cpl_func, "Object radius at S/R=%g: %g (window-size=%u)",
847 sigma, objradius, (unsigned)winsize);
848 cpl_msg_debug(cpl_func, "Object-peak @ (%d, %d) = %g", (int)xposmax,
849 (int)yposmax, valmax);
850
851 /* fit gaussian to get subpixel peak position */
852
853 gauss_parameters = cpl_array_new(7, CPL_TYPE_DOUBLE);
854 cpl_array_set_double(gauss_parameters, 0, median);
855
856 code = cpl_fit_image_gaussian(self, NULL, xposmax, yposmax,
857 winsize, winsize, gauss_parameters,
858 NULL, NULL, NULL,
859 NULL, NULL, NULL,
860 NULL, NULL, NULL);
861 if (!code) {
862 const double M_x = cpl_array_get_double(gauss_parameters, 3, NULL);
863 const double M_y = cpl_array_get_double(gauss_parameters, 4, NULL);
864
865 valfit = hcpl_gaussian_eval_2d(gauss_parameters, M_x, M_y);
866
867 if (!cpl_errorstate_is_equal(prestate)) {
868 code = cpl_error_get_code();
869 } else {
870 *pxpos = M_x;
871 *pypos = M_y;
872 *ppeak = valfit;
873
874 cpl_msg_debug(cpl_func, "Gauss-fit @ (%g, %g) = %g",
875 M_x, M_y, valfit);
876 }
877 }
878 cpl_array_delete(gauss_parameters);
879
880 if (code || valfit < valmax) {
881 cpl_errorstate_set(prestate);
882 *pxpos = xposcen;
883 *pypos = yposcen;
884 *ppeak = valmax;
885 }
886
887 return code ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
888}
889
890/*----------------------------------------------------------------------------*/
902/*----------------------------------------------------------------------------*/
903static cpl_mask *
904strehl_disk_mask(const cpl_image * im,
905 double xpos,
906 double ypos,
907 double rad)
908{
909 const intptr_t nx = cpl_image_get_size_x(im);
910 const intptr_t ny = cpl_image_get_size_y(im);
911 /* Round down */
912 const intptr_t lx = (intptr_t)(xpos - rad);
913 const intptr_t ly = (intptr_t)(ypos - rad);
914 /* Round up */
915 const intptr_t ux = (intptr_t)(xpos + rad) + 1;
916 const intptr_t uy = (intptr_t)(ypos + rad) + 1;
917
918 const double sqr = rad * rad;
919 cpl_mask * m;
920
921
922 /* Check entries */
923 cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, NULL);
924 cpl_ensure(rad > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
925
926 m = cpl_mask_new(nx, ny);
927
928 for (intptr_t j = CX_MAX(ly, 0); j < CX_MIN(uy, ny); j++) {
929 const double yj = (double)j - ypos;
930 for (intptr_t i = CX_MAX(lx, 0); i < CX_MIN(ux, nx); i++) {
931 const double xi = (double)i - xpos;
932 const double dist = yj * yj + xi * xi;
933 if (dist <= sqr) {
934 if (!cpl_image_is_rejected(im, i + 1, j + 1)) {
935 cpl_mask_set(m, i + 1, j + 1, CPL_BINARY_1);
936 }
937 }
938 }
939 }
940 cpl_mask_not(m);
941
942 return m;
943}
944
945/* ---------------------------------------------------------------------------*/
952/* ---------------------------------------------------------------------------*/
953static inline cpl_image * hdrl_rebin(cpl_image * img, size_t sampling)
954{
955 cpl_size lnx = cpl_image_get_size_x(img);
956 cpl_size lny = cpl_image_get_size_y(img);
957 cpl_size nx = lnx / sampling;
958 cpl_size ny = lny / sampling;
959 cpl_image * n = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
960 double * ld = cpl_image_get_data_double(img);
961 double * nd = cpl_image_get_data_double(n);
962 for (size_t iy = 0; iy < (size_t)ny; iy++) {
963 for (size_t ix = 0; ix < (size_t)nx; ix++) {
964 for (size_t ly = 0; ly < sampling; ly++) {
965 for (size_t lx = 0; lx < sampling; lx++) {
966 nd[iy * nx + ix] += ld[((iy*sampling) + ly) * lnx + lx + (ix*sampling)];
967 }
968 }
969 }
970 }
971 return n;
972}
973#if 0
974/* ---------------------------------------------------------------------------*/
1000/* ---------------------------------------------------------------------------*/
1001static inline double
1002compute_psf_flux(double lam, double m1, double m2, double r)
1003{
1004 /* currently unused */
1005 double e = m2 / m1;
1006 r *= CPL_MATH_2PI * m1 / lam;
1007 double E = 1 - j0(r) * j0(r) - j1(r) * j1(r) + e*e *
1008 (1 - j0(e*r)*j0(e*r) - j1(e*r)*j1(e*r));
1009 E *= 1 / (1 - e * e);
1010 /* Note compared to analytical formula iof the encircled energy, here we
1011 neglect the contribute from integral term: -2*e*integral(j1*j1*2/v dv) */
1012 return E;
1013}
1014#endif
1015
1016/* ---------------------------------------------------------------------------*/
1074static hdrl_strehl_result
1075compute_strehl2(const hdrl_image * himg, double lam,
1076 double m1_radius, double m2_radius,
1077 double pixscale_x, double pixscale_y,
1078 double peak_x, double peak_y,
1079 double radius_arcsec)
1080{
1081 const cpl_image * img = hdrl_image_get_image_const(himg);
1082 double min_pscale = CX_MIN(pixscale_x, pixscale_y);
1083 double radius_pix = (radius_arcsec / min_pscale);
1084 /* could be shrunk for better performance as flux beyond a few rings is negligible
1085 * using analytic encircled energy would probably also work */
1086 cpl_size wins = 2 * radius_pix;
1087 cpl_msg_debug(cpl_func, "strehl psf window size %d", (int)wins);
1088 double smallx = peak_x - (floor(peak_x) - wins/2);
1089 double smally = peak_y - (floor(peak_y) - wins/2);
1090 /* sample psf on larger grid for a primitive integration of the flux */
1091 size_t sampling = 16;
1092 intptr_t nnx = wins * sampling;
1093 intptr_t nny = wins * sampling;
1094 cpl_image * lpsf = compute_psf(lam, m1_radius, m2_radius,
1095 pixscale_x / sampling, pixscale_y / sampling,
1096 smallx*sampling,smally*sampling,
1097 nnx, nny);
1098 /* Note: o is a hard-coded offset to get the peak in same position as in data
1099 * after down-sampling, 7 seems to work reasonably well for sampling 16 */
1100 int o = 7;
1101 cpl_image * epsf = cpl_image_extract(lpsf, 1 + o, 1 + o, nnx - o, nny - o);
1102 cpl_image * psf = hdrl_rebin(epsf, sampling);
1103 cpl_image_delete(epsf);
1104 cpl_image_delete(lpsf);
1105 /* normalize for easier comparison, not required */
1106 cpl_image_divide_scalar(psf, cpl_image_get_max(psf)/cpl_image_get_max(img));
1107 cpl_msg_debug(cpl_func, "position/peak of data: %g %g", peak_x, peak_y);
1108 {
1109 /* fit not required, just for debugging */
1110 double xposfit, yposfit, peak;
1111 gaussian_maxpos(psf, 5, &xposfit, &yposfit, &peak);
1112 cpl_msg_debug(cpl_func, "position/peak of psf: %g %g", xposfit, yposfit);
1113 }
1114
1115 /* computes ratio peak/flux over the observed PSF standard */
1116 cpl_mask * im = strehl_disk_mask(img, peak_x, peak_y, radius_pix);
1117 hdrl_value ipeak = hdrl_image_max_where(himg, im);
1118 cpl_msg_debug(cpl_func, "Computing flux on %d pixel radius, total pixels %ld",
1119 (int)(radius_pix),
1120 (long)(cpl_mask_get_size_x(im) * cpl_mask_get_size_y(im) -
1121 cpl_mask_count(im)));
1122 hdrl_value iflux = hdrl_image_sum_where(himg, im);
1123 cpl_msg_debug(cpl_func, "flux ring/total data: %g (%g) %g", iflux.data,
1124 iflux.error, cpl_image_get_flux(img));
1125 cpl_mask_delete(im);
1126
1127 double ratio_img = ipeak.data / iflux.data;
1128
1129 /* computes ratio peak/flux over the synthetic PSF */
1130 double ppeak = cpl_image_get_max(psf);
1131 cpl_mask * pm =
1132 strehl_disk_mask(psf, wins / 2 - 1, wins / 2 - 1, radius_pix);
1133 hdrl_image * tmpimg = hdrl_image_create(psf, NULL);
1134 hdrl_value pflux = hdrl_image_sum_where(tmpimg, pm);
1135 hdrl_image_delete(tmpimg);
1136 cpl_msg_debug(cpl_func, "flux ring/total psf: %g %g", pflux.data,
1137 cpl_image_get_flux(psf));
1138 cpl_mask_delete(pm);
1139
1140 double ratio_psf = ppeak / pflux.data;
1141
1142 cpl_msg_debug(cpl_func, "data peak,flux,ratio: %g %g: %g",
1143 ipeak.data, iflux.data, ratio_img);
1144 cpl_msg_debug(cpl_func, "psf peak,flux,ratio: %g %g: %g",
1145 ppeak, pflux.data, ratio_psf);
1146
1147 double strehl = ratio_img / ratio_psf;
1148 double strehl_err = strehl *
1149 sqrt(ipeak.error * ipeak.error / (ipeak.data * ipeak.data) +
1150 iflux.error * iflux.error / (iflux.data * iflux.data));
1151
1152 cpl_msg_debug(cpl_func, "Strehl ratio %g +/- %g", strehl, strehl_err);
1153 cpl_image_delete(psf);
1154
1155 hdrl_strehl_result r;
1156 r.strehl_value = (hdrl_value){strehl, strehl_err};
1157 r.star_peak = ipeak;
1158 r.star_flux = iflux;
1159 r.star_background = (hdrl_value){0., 0.};
1160
1161 /* Initialize the remaining variables with dummy values to prevent warnings.
1162 * The derived values will be filled later outside this function */
1163 r.star_x = 0.;
1164 r.star_y = 0.;
1165 r.computed_background_error = -1.;
1166 r.nbackground_pixels = 0;
1167 /* TODO:
1168 * error on negative flux, error on disk max position != fit position */
1169 return r;
1170}
1171
1172
1173/* ---------------------------------------------------------------------------*/
1208/* ---------------------------------------------------------------------------*/
1209static hdrl_strehl_result
1210compute_strehl(const hdrl_image * himg_, double lam,
1211 double m1_radius, double m2_radius,
1212 double pixscale_x, double pixscale_y, double flux_radius,
1213 double bkg_radius_low, double bkg_radius_high)
1214{
1215 hdrl_image * himg = hdrl_image_duplicate(himg_);
1216 double xposfit, yposfit, peak;
1217 double min_pscale = CX_MIN(pixscale_x, pixscale_y);
1218 const cpl_image * img = hdrl_image_get_image_const(himg);
1219 /* interpolate bad pixels, */
1220 if (hdrl_image_count_rejected(himg) != 0) {
1221 cpl_msg_warning(cpl_func,
1222 "%zu bad pixels in strehl input, interpolating.",
1223 (size_t)hdrl_image_count_rejected(himg) );
1224 cpl_detector_interpolate_rejected(hdrl_image_get_image(himg));
1225 /* should have error propagation here .. */
1226 cpl_detector_interpolate_rejected(hdrl_image_get_error(himg));
1227 }
1228
1229 if (gaussian_maxpos(img, 5, &xposfit, &yposfit, &peak) != CPL_ERROR_NONE) {
1230 goto error;
1231 }
1232 if (peak <= 0) {
1233 cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND,
1234 "detected peak of star smaller than zero, "
1235 "gaussian fit likely failed to fit the star");
1236 goto error;
1237 }
1238 hdrl_value bkg = {0, 0};
1239 double comp_bkg_error = -1;
1240 size_t nbkg_pix = 0;
1241 if ((bkg_radius_low < 0 && bkg_radius_high >= 0) ||
1242 (bkg_radius_low >= 0 && bkg_radius_high < 0)) {
1243 cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
1244 "background radius parameters must be larger "
1245 "zero or both negative");
1246 goto error;
1247 }
1248 else if (bkg_radius_low >= 0 && bkg_radius_high >= 0) {
1249 if (bkg_radius_low >= bkg_radius_high) {
1250 cpl_error_set_message(cpl_func, CPL_ERROR_INCOMPATIBLE_INPUT,
1251 "low background radius parameters must be smaller "
1252 "than large background radius");
1253 goto error;
1254 }
1255 /* compute mask of background ring */
1256 cpl_mask * high = strehl_disk_mask(img, xposfit, yposfit,
1257 bkg_radius_high / min_pscale);
1258 cpl_mask * low = strehl_disk_mask(img, xposfit, yposfit,
1259 bkg_radius_low / min_pscale);
1260 cpl_mask_xor(low, high);
1261 nbkg_pix = cpl_mask_count(low);
1262 if (nbkg_pix == 0) {
1263 cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
1264 "No valid pixels in background");
1265 cpl_mask_delete(low);
1266 cpl_mask_delete(high);
1267 goto error;
1268 }
1269
1270 cpl_mask_not(low);
1271 bkg = hdrl_image_median_where(himg, low);
1272 comp_bkg_error = hdrl_image_stdev_where(himg, low) / sqrt(nbkg_pix);
1273 /* expected difference sqrt(pi / 2) due to median */
1274 cpl_msg_debug(cpl_func, "Median estimated background: %g +- %g "
1275 "(computed error %g)", bkg.data, bkg.error,
1276 comp_bkg_error);
1277 cpl_mask_delete(low);
1278 cpl_mask_delete(high);
1279
1280 hdrl_image_sub_scalar(himg, bkg);
1281 }
1282
1283 hdrl_strehl_result r = compute_strehl2(himg, lam, m1_radius, m2_radius,
1284 pixscale_x, pixscale_y, xposfit, yposfit,
1285 flux_radius);
1286 hdrl_image_delete(himg);
1287 r.star_background = bkg;
1288 r.star_x = xposfit;
1289 r.star_y = yposfit;
1290 r.computed_background_error = comp_bkg_error;
1291 r.nbackground_pixels = nbkg_pix;
1292 return r;
1293
1294error:
1295 hdrl_image_delete(himg);
1296 return bad_result;
1297}
1300/* TODO: missing doxygen, add more comments */
1301/* ---------------------------------------------------------------------------*/
1324/* ---------------------------------------------------------------------------*/
1325hdrl_strehl_result
1326hdrl_strehl_compute(const hdrl_image * himg, hdrl_parameter* params)
1327{
1328 hdrl_strehl_result r;
1329 /* Check Entries */
1330
1331 cpl_error_ensure(himg && params, CPL_ERROR_NULL_INPUT,
1332 return bad_result,
1333 "NULL input");
1334 if (hdrl_strehl_parameter_verify(params) != CPL_ERROR_NONE) {
1335 return bad_result;
1336 }
1337
1338 /* Local Usage Parameters */
1339 const hdrl_strehl_parameter * p_loc = (hdrl_strehl_parameter *)params ;
1340
1341 r = compute_strehl(himg,p_loc->wavelength, p_loc->m1, p_loc->m2,
1342 p_loc->pixel_scale_x, p_loc->pixel_scale_y,
1343 p_loc->flux_radius, p_loc->bkg_radius_low,
1344 p_loc->bkg_radius_high);
1345 return r;
1346}
1347
hdrl_value hdrl_image_get_pixel(const hdrl_image *self, cpl_size xpos, cpl_size ypos, int *pis_rejected)
get pixel values of hdrl_image
Definition: hdrl_image.c:559
cpl_error_code hdrl_image_reject_from_mask(hdrl_image *self, const cpl_mask *map)
set bpm of hdrl_image
Definition: hdrl_image.c:407
hdrl_value hdrl_image_get_median(const hdrl_image *self)
computes the median and associated error of an image.
hdrl_image * hdrl_image_duplicate(const hdrl_image *himg)
copy hdrl_image
Definition: hdrl_image.c:391
hdrl_value hdrl_image_get_sum(const hdrl_image *self)
computes the sum of all pixel values and the associated error of an image.
cpl_image * hdrl_image_get_error(hdrl_image *himg)
get error as cpl image
Definition: hdrl_image.c:131
cpl_size hdrl_image_count_rejected(const hdrl_image *self)
return number of rejected pixels
Definition: hdrl_image.c:461
hdrl_image * hdrl_image_create(const cpl_image *image, const cpl_image *error)
create a new hdrl_image from to existing images by copying them
Definition: hdrl_image.c:295
cpl_image * hdrl_image_get_image(hdrl_image *himg)
get data as cpl image
Definition: hdrl_image.c:105
const cpl_image * hdrl_image_get_image_const(const hdrl_image *himg)
get data as cpl image
Definition: hdrl_image.c:118
void hdrl_image_delete(hdrl_image *himg)
delete hdrl_image
Definition: hdrl_image.c:379
cpl_error_code hdrl_image_sub_scalar(hdrl_image *self, hdrl_value value)
Elementwise subtraction of a scalar from an image.
double hdrl_strehl_parameter_get_m1(const hdrl_parameter *p)
Access the primary mirror radius in the Strehl parameter.
Definition: hdrl_strehl.c:247
cpl_parameterlist * hdrl_strehl_parameter_create_parlist(const char *base_context, const char *prefix, hdrl_parameter *par)
Create parameter list for the Strehl computation.
Definition: hdrl_strehl.c:360
hdrl_strehl_result hdrl_strehl_compute(const hdrl_image *himg, hdrl_parameter *params)
This function computes the Strehl ratio.
Definition: hdrl_strehl.c:1326
double hdrl_strehl_parameter_get_flux_radius(const hdrl_parameter *p)
Access the total flux radius in the Strehl parameter.
Definition: hdrl_strehl.c:303
double hdrl_strehl_parameter_get_wavelength(const hdrl_parameter *p)
Access the wavelength in the Strehl parameter.
Definition: hdrl_strehl.c:233
double hdrl_strehl_parameter_get_m2(const hdrl_parameter *p)
Access the obstruction radius in the Strehl parameter.
Definition: hdrl_strehl.c:261
double hdrl_strehl_parameter_get_bkg_radius_high(const hdrl_parameter *p)
Access the background region external radius in the Strehl parameter.
Definition: hdrl_strehl.c:331
hdrl_parameter * hdrl_strehl_parameter_create(double wavelength, double m1_radius, double m2_radius, double pixel_scale_x, double pixel_scale_y, double flux_radius, double bkg_radius_low, double bkg_radius_high)
Creates Strehl Parameters object.
Definition: hdrl_strehl.c:188
double hdrl_strehl_parameter_get_pixel_scale_x(const hdrl_parameter *p)
Access the image X pixel scale in the Strehl parameter.
Definition: hdrl_strehl.c:275
double hdrl_strehl_parameter_get_bkg_radius_low(const hdrl_parameter *p)
Access the background region internal radius in the Strehl parameter.
Definition: hdrl_strehl.c:317
hdrl_parameter * hdrl_strehl_parameter_parse_parlist(const cpl_parameterlist *parlist, const char *prefix)
Parse parameter list to create input parameters for the Strehl.
Definition: hdrl_strehl.c:448
cpl_boolean hdrl_strehl_parameter_check(const hdrl_parameter *self)
Check that the parameter is a Strehl parameter.
Definition: hdrl_strehl.c:221
double hdrl_strehl_parameter_get_pixel_scale_y(const hdrl_parameter *p)
Access the image Y pixel scale in the Strehl parameter.
Definition: hdrl_strehl.c:289
char * hdrl_join_string(const char *sep_, int n,...)
join strings together
Definition: hdrl_utils.c:812