irplib_strehl.c

00001 /* $Id: irplib_strehl.c,v 1.43 2009-11-18 21:37:48 llundin Exp $
00002  *
00003  * This file is part of the irplib package
00004  * Copyright (C) 2002,2003 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
00019  */
00020 
00021 /*
00022  * $Author: llundin $
00023  * $Date: 2009-11-18 21:37:48 $
00024  * $Revision: 1.43 $
00025  * $Name: not supported by cvs2svn $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 /*-----------------------------------------------------------------------------
00033                                    Includes
00034  -----------------------------------------------------------------------------*/
00035 
00036 #include "irplib_strehl.h"
00037 #include "irplib_utils.h"
00038 
00039 #include <assert.h>
00040 #include <stdint.h>
00041 #include <math.h>
00042 
00043 /*----------------------------------------------------------------------------*/
00047 /*----------------------------------------------------------------------------*/
00048 
00049 /*-----------------------------------------------------------------------------
00050                                    Define
00051  -----------------------------------------------------------------------------*/
00052 
00053 #ifndef IRPLIB_STREHL_RAD_CENTRAL
00054 #define IRPLIB_STREHL_RAD_CENTRAL 5
00055 #endif
00056 
00057 #ifndef IRPLIB_STREHL_DETECT_LEVEL
00058 #define IRPLIB_STREHL_DETECT_LEVEL 5.0
00059 #endif
00060 
00061 #define IRPLIB_DISK_BG_MIN_PIX_NB    30
00062 #define IRPLIB_DISK_BG_REJ_LOW       0.1
00063 #define IRPLIB_DISK_BG_REJ_HIGH      0.1
00064 
00065 #ifdef CPL_MIN
00066 #define IRPLIB_MIN CPL_MIN
00067 #else
00068 #define IRPLIB_MIN(A,B) ((A) < (B) ? (A) : (B))
00069 #endif
00070 
00071 #ifdef CPL_MAX
00072 #define IRPLIB_MAX CPL_MAX
00073 #else
00074 #define IRPLIB_MAX(A,B) ((A) > (B) ? (A) : (B))
00075 #endif
00076 
00077 /*-----------------------------------------------------------------------------
00078                                    Functions prototypes
00079  -----------------------------------------------------------------------------*/
00080 
00081 static cpl_image * irplib_strehl_generate_otf(double, double, double, double,
00082                                               int, double);
00083 static double PSF_H1(double, double, double);
00084 static double PSF_H2(double, double);
00085 static double PSF_G(double, double);
00086 static double PSF_sinc_norm(double);
00087 static double PSF_TelOTF(double, double);
00088 static cpl_error_code update_bad_pixel_map(cpl_image* im);
00089 
00090 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00091 #ifdef IRPLIB_STREHL_USE_CPL_IMAGE_FIT_GAUSSIAN
00092 static double irplib_gaussian_2d(double, double, double, double, double);
00093 #endif
00094 
00095 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(6, 9, 1)
00096 #define irplib_gaussian_eval_2d cpl_gaussian_eval_2d
00097 #else
00098 static double irplib_gaussian_eval_2d(const cpl_array *, double, double);
00099 #endif
00100 
00101 static uint32_t irplib_roundup_power2(uint32_t v) CPL_ATTR_CONST;
00102 
00103 static
00104 cpl_error_code irplib_gaussian_maxpos(const cpl_image *,
00105                                       double,
00106                                       double *,
00107                                       double *,
00108                                       double *);
00109 #endif
00110 
00111 /*-----------------------------------------------------------------------------
00112                                    Functions code
00113  -----------------------------------------------------------------------------*/
00121 cpl_error_code update_bad_pixel_map(cpl_image* im)
00122 {
00123     int szx = cpl_image_get_size_x(im);
00124     int szy = cpl_image_get_size_y(im);
00125     int x = 0;
00126     cpl_mask* bpm = cpl_image_get_bpm(im);
00127 
00128     for (x = 1; x <=szx; x++)
00129     {
00130         int y = 0;
00131         for(y = 1; y <= szy; y++)
00132         {
00133             int isnull = 0;
00134             double value = cpl_image_get(im, x, y, &isnull);
00135             if (isnan(value))
00136             {
00137                 cpl_mask_set(bpm, x, y, CPL_BINARY_1);
00138             }
00139         }
00140     }
00141     return cpl_error_get_code();
00142 }
00173 cpl_error_code irplib_strehl_mark_bad_and_compute(cpl_image *   im,
00174                                      double              m1,
00175                                      double              m2,
00176                                      double              lam,
00177                                      double              dlam,
00178                                      double              pscale,
00179                                      int                 size,
00180                                      double              xpos,
00181                                      double              ypos,
00182                                      double              r1,
00183                                      double              r2,
00184                                      double              r3,
00185                                      int                 noise_box_sz,
00186                                      int                 noise_nsamples,
00187                                      double          *   strehl,
00188                                      double          *   strehl_err,
00189                                      double          *   star_bg,
00190                                      double          *   star_peak,
00191                                      double          *   star_flux,
00192                                      double          *   psf_peak,
00193                                      double          *   psf_flux,
00194                                      double          *   bg_noise)
00195 {
00196     cpl_ensure_code(!update_bad_pixel_map(im), cpl_error_get_code());
00197     return irplib_strehl_compute(im, m1, m2, lam, dlam, pscale, size, xpos, ypos,
00198             r1,
00199             r2,
00200             r3,
00201             noise_box_sz,
00202             noise_nsamples,
00203             strehl,
00204             strehl_err,
00205             star_bg,
00206             star_peak,
00207             star_flux,
00208             psf_peak,
00209             psf_flux,
00210             bg_noise);
00211 }
00212 
00213 /*----------------------------------------------------------------------------*/
00244 /*----------------------------------------------------------------------------*/
00245 cpl_error_code irplib_strehl_compute(const cpl_image *   im,
00246                                      double              m1,
00247                                      double              m2,
00248                                      double              lam,
00249                                      double              dlam,
00250                                      double              pscale,
00251                                      int                 size,
00252                                      double              xpos,
00253                                      double              ypos,
00254                                      double              r1,
00255                                      double              r2,
00256                                      double              r3,
00257                                      int                 noise_box_sz,
00258                                      int                 noise_nsamples,
00259                                      double          *   strehl,
00260                                      double          *   strehl_err,
00261                                      double          *   star_bg,
00262                                      double          *   star_peak,
00263                                      double          *   star_flux,
00264                                      double          *   psf_peak,
00265                                      double          *   psf_flux,
00266                                      double          *   bg_noise)
00267 {
00268     cpl_image  * psf;
00269     double       star_radius, max_radius;
00270 
00271     /* FIXME: Arbitrary choice of image border */
00272     const double window_size = (double)(IRPLIB_STREHL_RAD_CENTRAL);
00273 
00274     /* Determined empirically by C. Lidman for Strehl error computation */
00275     const double strehl_error_coefficient = CPL_MATH_PI * 0.007 / 0.0271;
00276     double       ring[4];
00277     /* cpl_flux_get_noise_ring() must succeed with this many tries */
00278     int          ring_tries = 3;
00279 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00280     double xposfit, yposfit, peak;
00281     cpl_error_code code;
00282 #endif
00283     cpl_errorstate prestate = cpl_errorstate_get();
00284 
00285     /* Check compile-time constant */
00286     cpl_ensure_code(window_size > 0.0,  CPL_ERROR_ILLEGAL_INPUT);
00287 
00288     /* Test inputs */
00289     cpl_ensure_code(im != NULL,         CPL_ERROR_NULL_INPUT);
00290     cpl_ensure_code(strehl != NULL,     CPL_ERROR_NULL_INPUT);
00291     cpl_ensure_code(strehl_err != NULL, CPL_ERROR_NULL_INPUT);
00292     cpl_ensure_code(star_bg != NULL,    CPL_ERROR_NULL_INPUT);
00293     cpl_ensure_code(star_peak != NULL,  CPL_ERROR_NULL_INPUT);
00294     cpl_ensure_code(star_flux != NULL,  CPL_ERROR_NULL_INPUT);
00295     cpl_ensure_code(psf_peak != NULL,   CPL_ERROR_NULL_INPUT);
00296     cpl_ensure_code(psf_flux != NULL,   CPL_ERROR_NULL_INPUT);
00297 
00298     cpl_ensure_code(pscale > 0.0,      CPL_ERROR_ILLEGAL_INPUT);
00299 
00300     cpl_ensure_code(r1 > 0.0,      CPL_ERROR_ILLEGAL_INPUT);
00301     cpl_ensure_code(r2 > 0.0,      CPL_ERROR_ILLEGAL_INPUT);
00302     cpl_ensure_code(r3 > r2,       CPL_ERROR_ILLEGAL_INPUT);
00303 
00304     /* Computing a Strehl ratio is a story between an ideal PSF */
00305     /* and a candidate image supposed to approximate this ideal PSF. */
00306 
00307     /* Generate first appropriate PSF to find max peak */
00308     psf = irplib_strehl_generate_psf(m1, m2, lam, dlam, pscale, size);
00309     if (psf == NULL) {
00310         return cpl_error_set_where(cpl_func);
00311     }
00312 
00313     /* Compute flux in PSF and find max peak */
00314     *psf_peak = cpl_image_get_max(psf);
00315     cpl_image_delete(psf);
00316 
00317     assert( *psf_peak > 0.0); /* The ideal PSF has a positive maximum */
00318     *psf_flux = 1.0; /* The psf flux, cpl_image_get_flux(psf), is always 1 */
00319 
00320 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00321     code = irplib_gaussian_maxpos(im, IRPLIB_STREHL_DETECT_LEVEL,
00322                                   &xposfit, &yposfit, &peak);
00323     if (code) {
00324         cpl_errorstate_set(prestate);
00325     } else {
00326         xpos = xposfit;
00327         ypos = yposfit;
00328     }
00329 #endif
00330 
00331     /* Measure the background in the candidate image */
00332     *star_bg = irplib_strehl_ring_background(im, xpos, ypos,
00333                                              r2/pscale, r3/pscale,
00334                                              IRPLIB_BG_METHOD_AVER_REJ);
00335     if (!cpl_errorstate_is_equal(prestate)) {
00336         return cpl_error_set_where(cpl_func);
00337     }
00338 
00339     /* Compute star_radius in pixels */
00340     star_radius = r1/pscale;
00341 
00342     /* Measure the flux on the candidate image */
00343     *star_flux = irplib_strehl_disk_flux(im, xpos, ypos, star_radius, *star_bg);
00344 
00345     if (*star_flux <= 0.0) {
00346         return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
00347                                      "Non-positive star flux=%g (Star "
00348                                      "background=%g)", *star_flux, *star_bg);
00349     }
00350 
00351     /* Find the peak value on the central part of the candidate image */
00352     max_radius = window_size < star_radius ? window_size : star_radius;
00353     cpl_ensure_code(!irplib_strehl_disk_max(im, xpos, ypos, max_radius,
00354                                             star_peak), cpl_error_get_code());
00355     *star_peak -= *star_bg;
00356 
00357     if (*star_flux <= 0.0) {
00358         return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
00359                                      "Non-positive star peak=%g (Star "
00360                                      "background=%g, Star flux=%g)",
00361                                      *star_flux, *star_bg, *star_flux);
00362     }
00363 
00364     /* Compute Strehl */
00365     /* (StarPeak / StarFlux) / (PsfPeak / PsfFlux) */
00366     *strehl = (*star_peak * *psf_flux ) / ( *star_flux * *psf_peak);
00367 
00368 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00369     if (code == CPL_ERROR_NONE && peak > *star_peak && *star_peak > 0.0 &&
00370         *strehl * peak / *star_peak <= 1.0) {
00371         cpl_msg_debug(cpl_func, "Increasing Strehl from %g: %g (%g)",
00372                       *strehl, *strehl * peak / *star_peak,
00373                       peak / *star_peak);
00374         *strehl *= peak / *star_peak;
00375         *star_peak = peak;
00376     }
00377 #endif
00378 
00379     /* Compute Strehl error */
00380     /* computation could fail if the image contains pixels with NaN value*/
00381     ring[0] = xpos;
00382     ring[1] = ypos;
00383     ring[2] = r2/pscale;
00384     ring[3] = r3/pscale;
00385 
00386     while (cpl_flux_get_noise_ring(im, ring, noise_box_sz, noise_nsamples,
00387                                    bg_noise, NULL) && --ring_tries > 0);
00388     if (ring_tries > 0) {
00389         cpl_errorstate_set(prestate); /* Recover, if an error happened */
00390     } else {
00391         return cpl_error_set_where(cpl_func);
00392     }
00393 
00394     *strehl_err = strehl_error_coefficient * (*bg_noise) * pscale *
00395         star_radius * star_radius / *star_flux;
00396 
00397     if (*strehl > 1.0) {
00398         cpl_msg_warning(cpl_func, "Extreme Strehl-ratio=%g (strehl-error=%g, "
00399                         "star_peak=%g, star_flux=%g, psf_peak=%g, psf_flux=%g)",
00400                         *strehl, *strehl_err, *star_peak, *star_flux, *psf_peak,
00401                         *psf_flux);
00402     }
00403 
00404     /* This check should not be able to fail, but just to be sure */
00405     return *strehl_err >= 0.0
00406         ? CPL_ERROR_NONE
00407         : cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
00408                                  "Negative strehl-error=%g (Strehl-ratio=%g, "
00409                                  "star_peak=%g, star_flux=%g, psf_peak=%g, "
00410                                  "psf_flux=%g", *strehl_err, *strehl,
00411                                  *star_peak, *star_flux, *psf_peak, *psf_flux);
00412 }
00413 
00414 /*----------------------------------------------------------------------------*/
00430 /*----------------------------------------------------------------------------*/
00431 double irplib_strehl_disk_flux(const cpl_image * im,
00432                                double            xpos,
00433                                double            ypos,
00434                                double            rad,
00435                                double            bg)
00436 {
00437     const int       nx = cpl_image_get_size_x(im);
00438     const int       ny = cpl_image_get_size_y(im);
00439     /* Round down */
00440     const int       lx = (int)(xpos - rad);
00441     const int       ly = (int)(ypos - rad);
00442     /* Round up */
00443     const int       ux = (int)(xpos + rad) + 1;
00444     const int       uy = (int)(ypos + rad) + 1;
00445 
00446     const double    sqr = rad * rad;
00447     double          flux = 0.0;
00448     int             i, j;
00449 
00450 
00451     /* Check entries */
00452     cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
00453     cpl_ensure(rad > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
00454 
00455     for (j = IRPLIB_MAX(ly, 0); j < IRPLIB_MIN(uy, ny-1); j++) {
00456         const double yj = (double)j - ypos;
00457         for (i = IRPLIB_MAX(lx, 0); i < IRPLIB_MIN(ux, nx-1); i++) {
00458             const double xi   = (double)i - xpos;
00459             const double dist = yj * yj + xi * xi;
00460             if (dist <= sqr) {
00461                 int isbad;
00462                 const double value = cpl_image_get(im, i+1, j+1, &isbad);
00463 
00464                 if (!isbad && irplib_isnan(value) == 0) {
00465 
00466                     flux += value - bg;
00467 
00468                 }
00469             }
00470         }
00471     }
00472 
00473     return flux;
00474 }
00475 
00476 /*----------------------------------------------------------------------------*/
00490 /*----------------------------------------------------------------------------*/
00491 double irplib_strehl_ring_background(const cpl_image *       im,
00492                                      double                  xpos,
00493                                      double                  ypos,
00494                                      double                  rad_int,
00495                                      double                  rad_ext,
00496                                      irplib_strehl_bg_method mode)
00497 {
00498     const int       nx = cpl_image_get_size_x(im);
00499     const int       ny = cpl_image_get_size_y(im);
00500     /* Round down */
00501     const int       lx = (int)(xpos - rad_ext);
00502     const int       ly = (int)(ypos - rad_ext);
00503     /* Round up */
00504     const int       ux = (int)(xpos + rad_ext) + 1;
00505     const int       uy = (int)(ypos + rad_ext) + 1;
00506     int             mpix, npix;
00507     const double    sqr_int = rad_int * rad_int;
00508     const double    sqr_ext = rad_ext * rad_ext;
00509     cpl_vector  *   pix_arr;
00510     double          flux = 0.0;
00511     int             i, j;
00512 
00513     /* Check entries */
00514     cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
00515     cpl_ensure(rad_int > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
00516     cpl_ensure(rad_ext > rad_int, CPL_ERROR_ILLEGAL_INPUT, 0.0);
00517 
00518     cpl_ensure(mode == IRPLIB_BG_METHOD_AVER_REJ ||
00519                mode == IRPLIB_BG_METHOD_MEDIAN,
00520                CPL_ERROR_UNSUPPORTED_MODE, 0.0);
00521 
00522     mpix = (int)((2.0 * rad_ext + 1.0) * (2.0 * rad_ext + 1.0));
00523 
00524     /* Allocate pixel array to hold values in the ring */
00525     pix_arr = cpl_vector_new(mpix);
00526 
00527     /* Count number of pixels in the ring */
00528     /* Retrieve all pixels which belong to the ring */
00529     npix = 0;
00530     for (j = IRPLIB_MAX(ly, 0); j < IRPLIB_MIN(uy, ny-1); j++) {
00531         const double yj = (double)j - ypos;
00532         for (i = IRPLIB_MAX(lx, 0); i < IRPLIB_MIN(ux, nx-1); i++) {
00533             const double xi   = (double)i - xpos;
00534             const double dist = yj * yj + xi * xi;
00535             if (sqr_int <= dist && dist <= sqr_ext) {
00536                 int isbad;
00537                 const double value = cpl_image_get(im, i+1, j+1, &isbad);
00538 
00539                 if (!isbad && irplib_isnan(value) == 0) {
00540                     cpl_vector_set(pix_arr, npix, value);
00541                     npix++;
00542                 }
00543             }
00544         }
00545     }
00546 
00547     assert(npix <= mpix);
00548 
00549     if (npix < IRPLIB_DISK_BG_MIN_PIX_NB) {
00550         cpl_vector_delete(pix_arr);
00551         (void)cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, "Need "
00552                                     "at least %d (not %d <= %d) samples to "
00553                                     "compute noise", IRPLIB_DISK_BG_MIN_PIX_NB,
00554                                     npix, mpix);
00555         return 0.0;
00556     }
00557 
00558     /* Should not be able to fail now */
00559 
00560     /* Resize pixel array to actual number of values within the ring */
00561     pix_arr = cpl_vector_wrap(npix, (double*)cpl_vector_unwrap(pix_arr));
00562 
00563     if (mode == IRPLIB_BG_METHOD_AVER_REJ) {
00564         const int low_ind  = (int)((double)npix * IRPLIB_DISK_BG_REJ_LOW);
00565         const int high_ind = (int)((double)npix
00566                                    * (1.0 - IRPLIB_DISK_BG_REJ_HIGH));
00567 
00568         /* Sort the array */
00569         cpl_vector_sort(pix_arr, CPL_SORT_ASCENDING);
00570 
00571         for (i=low_ind; i<high_ind; i++) {
00572             flux += cpl_vector_get(pix_arr, i);
00573         }
00574         if (high_ind - low_ind > 1) flux /= (double)(high_ind - low_ind);
00575     } else /* if (mode == IRPLIB_BG_METHOD_MEDIAN) */ {
00576         flux = cpl_vector_get_median(pix_arr);
00577     }
00578 
00579     cpl_vector_delete(pix_arr);
00580 
00581     return flux;
00582 }
00583 
00584 /*----------------------------------------------------------------------------*/
00604 /*----------------------------------------------------------------------------*/
00605 cpl_image * irplib_strehl_generate_psf(double   m1,
00606                                        double   m2,
00607                                        double   lam,
00608                                        double   dlam,
00609                                        double   pscale,
00610                                        int      size)
00611 {
00612     cpl_image * otf_image = irplib_strehl_generate_otf(m1, m2, lam, dlam,
00613                                                        size, pscale);
00614 
00615     if (otf_image == NULL || 
00616 
00617         /* Transform back to real space
00618            - Normalization is unnecessary, due to the subsequent normalisation.
00619            - An OTF is point symmetric about its center, i.e. it is even,
00620            i.e. the real space image is real.
00621            - Because of this a forward FFT works as well.
00622            - If the PSF ever needs to have its images halves swapped add
00623            CPL_FFT_SWAP_HALVES to the FFT call.
00624         */
00625 
00626         cpl_image_fft(otf_image, NULL, CPL_FFT_UNNORMALIZED) ||
00627 
00628         /* Compute absolute values of PSF */
00629         cpl_image_abs(otf_image) ||
00630 
00631         /* Normalize PSF to get flux=1  */
00632         cpl_image_normalise(otf_image, CPL_NORM_FLUX)) {
00633 
00634         (void)cpl_error_set_where(cpl_func);
00635         cpl_image_delete(otf_image);
00636         otf_image = NULL;
00637     }
00638 
00639     return otf_image;
00640 }
00641 
00644 /*----------------------------------------------------------------------------*/
00660 /*----------------------------------------------------------------------------*/
00661 static cpl_image * irplib_strehl_generate_otf(double  m1,
00662                                               double  m2,
00663                                               double  lam,
00664                                               double  dlam,
00665                                               int     size,
00666                                               double  pscale)
00667 {
00668     double     * otf_data;
00669     /* Obscuration ratio, m1 / m2 */
00670     const double obs_ratio = m1 != 0.0 ? m2 / m1 : 0.0;
00671     /* pixel scale converted from Arsecond to radian  */
00672     const double rpscale = pscale * CPL_MATH_2PI / (double)(360 * 60 * 60);
00673     /* Cut-off frequency in pixels per central wavelength (in m) */
00674     const double f_max = m1 * rpscale * (double)size;
00675 
00676     /* Pixel corresponding to the zero frequency */
00677     const int    pix0 = size / 2;
00678     int i, j;
00679 
00680 
00681     cpl_ensure(m2       > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00682     cpl_ensure(m1       > m2,  CPL_ERROR_ILLEGAL_INPUT, NULL);
00683     cpl_ensure(dlam     > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00684     cpl_ensure(pscale   > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00685     cpl_ensure(size     > 0,   CPL_ERROR_ILLEGAL_INPUT, NULL);
00686     /* Due the the FFT, size is actually required to be a power of two */
00687     cpl_ensure(size % 2 == 0,  CPL_ERROR_ILLEGAL_INPUT, NULL);
00688 
00689     /* Ensure positive lambda */
00690     cpl_ensure(2.0 * lam > dlam, CPL_ERROR_ILLEGAL_INPUT, NULL);
00691 
00692     /* Convert wavelengths from micron to meter */
00693     lam /= 1.0e6;
00694     dlam /= 1.0e6;
00695 
00696     /* Allocate the output pixel buffer */
00697     otf_data = (double*)cpl_malloc(size * size * sizeof(*otf_data));
00698 
00699     /* Convolution with the detector pixels */
00700     /* The OTF is point symmetric so the whole image can be computed from the
00701        values of a single octant. */
00702     /* The image could be created with calloc() and j limited by
00703        f_max / (mlam - mdlam * 0.5) but this is not faster */
00704     for (j = 0; j <= pix0; j++) {
00705         double sinc_y_9 = 0.0; /* Avoid uninit warning */
00706         for (i = 0; i <= j; i++) {
00707             if (i == 0 && j == 0) {
00708                 otf_data[size * pix0 + pix0] = 1.0;
00709             } else {
00710                 const double x = (double)i;
00711                 const double y = (double)j;
00712                 const double sqdist = x * x + y * y;
00713                 double f_lambda, sinc_xy_9 = 0.0; /* Zero if OTF is zero */
00714                 double otfxy = 0.0;
00715                 int k;
00716 
00717                 assert( j > 0 );
00718 
00719                 /* 9 iterations on the wavelength  */
00720                 /* Unrolling the loop is not faster (due to the break?) */
00721                 for (k = 4; k >= -4; k--) {
00722                     /* Compute intermediate cut-off frequency   */
00723                     const double lambda = lam - dlam * (double)k / 8.0;
00724 
00725                     /* A decreasing k ensures that we either enter on the first
00726                        iteration or not at all */
00727                     if (sqdist * lambda * lambda >= f_max * f_max) break;
00728 
00729                     if (k == 4) {
00730                         f_lambda = sqrt(sqdist) / f_max;
00731                         if (i == 0) {
00732                             /* Sinc(x = 0) == 1 */
00733                             sinc_xy_9 = sinc_y_9 =
00734                                 PSF_sinc_norm(y / (double)size) / 9.0;
00735                         } else {
00736                             sinc_xy_9 = sinc_y_9 *
00737                                 PSF_sinc_norm(x / (double)size);
00738                         }
00739                     }
00740 
00741                     otfxy += PSF_TelOTF(f_lambda * lambda, obs_ratio);
00742                 }
00743                 otfxy *= sinc_xy_9;
00744 
00745                 /* When i == j the same value is written to the same
00746                    position twice. That's probably faster than a guard */
00747                 otf_data[size * (pix0 - j) + pix0 - i] = otfxy;
00748                 otf_data[size * (pix0 - i) + pix0 - j] = otfxy;
00749                 if (i < pix0) {
00750                     otf_data[size * (pix0 - j) + pix0 + i] = otfxy;
00751                     otf_data[size * (pix0 + i) + pix0 - j] = otfxy;
00752                     if (j < pix0) {
00753                         otf_data[size * (pix0 + j) + pix0 - i] = otfxy;
00754                         otf_data[size * (pix0 - i) + pix0 + j] = otfxy;
00755                         otf_data[size * (pix0 + j) + pix0 + i] = otfxy;
00756                         otf_data[size * (pix0 + i) + pix0 + j] = otfxy;
00757                     }
00758                 }
00759             }
00760         }
00761     }
00762 
00763     return cpl_image_wrap_double(size, size, otf_data);
00764 }
00765 
00766 /*----------------------------------------------------------------------------*
00767  * H1 function
00768  *----------------------------------------------------------------------------*/
00769 static double PSF_H1(
00770         double  f,
00771         double  u,
00772         double  v)
00773 {
00774     const double e = fabs(1.0-v) > 0.0 ? -1.0 : 1.0; /* e = 1.0 iff v = 1.0 */
00775 
00776     return((v*v/CPL_MATH_PI)*acos((f/v)*(1.0+e*(1.0-u*u)/(4.0*f*f))));
00777 }
00778 
00779 /*----------------------------------------------------------------------------*
00780  * H2 function
00781  *----------------------------------------------------------------------------*/
00782 static double PSF_H2(double  f,
00783                      double  u)
00784 {
00785     const double tmp1 = (2.0 * f) / (1.0 + u);
00786     const double tmp2 = (1.0 - u) / (2.0 * f);
00787 
00788     return -1.0 * (f/CPL_MATH_PI) * (1.0+u)
00789         * sqrt((1.0-tmp1*tmp1)*(1.0-tmp2*tmp2));
00790 }
00791 
00792 /*----------------------------------------------------------------------------*
00793  * G function
00794  *----------------------------------------------------------------------------*/
00795 static double PSF_G(double  f,
00796                     double  u)
00797 {
00798     if (f <= (1.0-u)/2.0) return(u*u);
00799     if (f >= (1.0+u)/2.0) return(0.0);
00800     else return(PSF_H1(f,u,1.0) + PSF_H1(f,u,u) + PSF_H2(f,u));
00801 }
00802 
00803 /*----------------------------------------------------------------------------*/
00811 /*----------------------------------------------------------------------------*/
00812 static double PSF_sinc_norm(double x)
00813 {
00814     return sin(x * CPL_MATH_PI) / (x * CPL_MATH_PI);
00815 }
00816 
00817 /*----------------------------------------------------------------------------*
00818  * Telescope OTF function
00819  *----------------------------------------------------------------------------*/
00820 static double PSF_TelOTF(double  f,
00821                          double  u)
00822 {
00823     return((PSF_G(f,1.0)+u*u*PSF_G(f/u,1.0)-2.0*PSF_G(f,u))/(1.0-u*u));
00824 }
00825 
00826 /*----------------------------------------------------------------------------*/
00837 /*----------------------------------------------------------------------------*/
00838 cpl_error_code irplib_strehl_disk_max(const cpl_image * self,
00839                                       double            xpos,
00840                                       double            ypos,
00841                                       double            radius,
00842                                       double          * ppeak)
00843 {
00844 
00845     const int       nx = cpl_image_get_size_x(self);
00846     const int       ny = cpl_image_get_size_y(self);
00847     /* Round down */
00848     const int       lx = (int)(xpos - radius);
00849     const int       ly = (int)(ypos - radius);
00850     /* Round up */
00851     const int       ux = (int)(xpos + radius) + 1;
00852     const int       uy = (int)(ypos + radius) + 1;
00853 
00854     const double    sqr = radius * radius;
00855     cpl_boolean     first = CPL_TRUE;
00856     int             i, j;
00857 
00858 
00859     /* Check entries */
00860     cpl_ensure_code(self  != NULL, CPL_ERROR_NULL_INPUT);
00861     cpl_ensure_code(ppeak != NULL, CPL_ERROR_NULL_INPUT);
00862     cpl_ensure_code(radius > 0.0,  CPL_ERROR_ILLEGAL_INPUT);
00863 
00864 
00865     for (j = IRPLIB_MAX(ly, 0); j < IRPLIB_MIN(uy, ny-1); j++) {
00866         const double yj = (double)j - ypos;
00867         for (i = IRPLIB_MAX(lx, 0); i < IRPLIB_MIN(ux, nx-1); i++) {
00868             const double xi   = (double)i - xpos;
00869             const double dist = yj * yj + xi * xi;
00870             if (dist <= sqr) {
00871                 int isbad;
00872                 const double value = cpl_image_get(self, i+1, j+1, &isbad);
00873 
00874                 if (!isbad && irplib_isnan(value) == 0 &&
00875                     (first || value > *ppeak)) {
00876                     first = CPL_FALSE;
00877                     *ppeak = value;
00878                 }
00879             }
00880         }
00881     }
00882 
00883     return first
00884         ? cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND)
00885         : CPL_ERROR_NONE;
00886 }
00887 
00888 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00889 #ifdef IRPLIB_STREHL_USE_CPL_IMAGE_FIT_GAUSSIAN
00890 /*----------------------------------------------------------------------------*/
00906 /*----------------------------------------------------------------------------*/
00907 static double irplib_gaussian_2d(double  x,
00908                                  double  y,
00909                                  double  norm,
00910                                  double  sig_x,
00911                                  double  sig_y)
00912 {
00913 
00914     /* Copied from CPL */
00915     return norm / (sig_x * sig_y * CPL_MATH_2PI *
00916                    exp(x * x / (2.0 * sig_x * sig_x) +
00917                        y * y / (2.0 * sig_y * sig_y)));
00918 }
00919 #endif
00920 
00921 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(6, 9, 1)
00922 #else
00923 /*----------------------------------------------------------------------------*/
00942 /*----------------------------------------------------------------------------*/
00943 static
00944 double irplib_gaussian_eval_2d(const cpl_array * self, double x, double y)
00945 {
00946     cpl_errorstate prestate = cpl_errorstate_get();
00947     const double B    = cpl_array_get_double(self, 0, NULL);
00948     const double A    = cpl_array_get_double(self, 1, NULL);
00949     const double R    = cpl_array_get_double(self, 2, NULL);
00950     const double M_x  = cpl_array_get_double(self, 3, NULL);
00951     const double M_y  = cpl_array_get_double(self, 4, NULL);
00952     const double S_x  = cpl_array_get_double(self, 5, NULL);
00953     const double S_y  = cpl_array_get_double(self, 6, NULL);
00954 
00955     double value = 0.0;
00956 
00957     if (!cpl_errorstate_is_equal(prestate)) {
00958         (void)cpl_error_set_where(cpl_func);
00959     } else if (cpl_array_get_size(self) != 7) {
00960         (void)cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
00961     } else if (fabs(R) < 1.0 && S_x != 0.0 && S_y != 0.0) {
00962         const double x_n  = (x - M_x) / S_x;
00963         const double y_n  = (y - M_y) / S_y;
00964 
00965         value = B + A / (CPL_MATH_2PI * S_x * S_y * sqrt(1 - R * R)) *
00966             exp(-0.5 / (1 - R * R) * ( x_n * x_n + y_n * y_n
00967                                        - 2.0 * R * x_n * y_n));
00968     } else if (fabs(R) > 1.0) {
00969         (void)cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
00970                                      "fabs(R=%g) > 1", R);
00971     } else {
00972         (void)cpl_error_set_message(cpl_func, CPL_ERROR_DIVISION_BY_ZERO,
00973                                      "R=%g. Sigma=(%g, %g)", R, S_x, S_y);
00974     }
00975 
00976     return value;
00977 }
00978 #endif
00979 
00980 /*----------------------------------------------------------------------------*/
00987 /*----------------------------------------------------------------------------*/
00988 static uint32_t irplib_roundup_power2(uint32_t v)
00989 {
00990     v |= v >> 1;
00991     v |= v >> 2;
00992     v |= v >> 4;
00993     v |= v >> 8;
00994     v |= v >> 16;
00995 
00996     return v + 1;
00997 }
00998 
00999 
01000 /*----------------------------------------------------------------------------*/
01011 /*----------------------------------------------------------------------------*/
01012 static
01013 cpl_error_code irplib_gaussian_maxpos(const cpl_image * self,
01014                                       double sigma,
01015                                       double  * pxpos,
01016                                       double  * pypos,
01017                                       double  * ppeak)
01018 {
01019 
01020     const cpl_size  nx = cpl_image_get_size_x(self);
01021     const cpl_size  ny = cpl_image_get_size_y(self);
01022     int             iretry = 3; /* Number retries with decreasing sigma */
01023     int             ifluxapert;
01024     double          med_dist;
01025     const double    median = cpl_image_get_median_dev(self, &med_dist);
01026     cpl_mask      * selection;
01027     cpl_size        nlabels = 0;
01028     cpl_image     * labels = NULL;
01029     cpl_apertures * aperts;
01030     cpl_size        npixobj;
01031     double          objradius;
01032     cpl_size        winsize;
01033     cpl_size        xposmax, yposmax;
01034     double          xposcen, yposcen;
01035     double          valmax, valfit = -1.0;
01036 #ifdef IRPLIB_STREHL_USE_CPL_IMAGE_FIT_GAUSSIAN
01037     double          norm, xcen, ycen, sig_x, sig_y, fwhm_x, fwhm_y;
01038 #endif
01039     cpl_array     * gauss_parameters = NULL;
01040     cpl_errorstate  prestate = cpl_errorstate_get();
01041     cpl_error_code  code;
01042 
01043 
01044     cpl_ensure_code( sigma > 0.0, CPL_ERROR_ILLEGAL_INPUT);
01045 
01046     selection = cpl_mask_new(nx, ny);
01047 
01048     for (; iretry > 0 && nlabels == 0; iretry--, sigma *= 0.5) {
01049 
01050         /* Compute the threshold */
01051         const double threshold = median + sigma * med_dist;
01052 
01053 
01054         /* Select the pixel above the threshold */
01055         code = cpl_mask_threshold_image(selection, self, threshold, DBL_MAX,
01056                                         CPL_BINARY_1);
01057 
01058         if (code) break;
01059 
01060         /* Labelise the thresholded selection */
01061         cpl_image_delete(labels);
01062         labels = cpl_image_labelise_mask_create(selection, &nlabels);
01063     }
01064     sigma *= 2.0; /* FIXME: unelegant */
01065 
01066     cpl_mask_delete(selection);
01067 
01068     if (code) {
01069         cpl_image_delete(labels);
01070         return cpl_error_set_where(cpl_func);
01071     } else if (nlabels == 0) {
01072         cpl_image_delete(labels);
01073         return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
01074     }
01075 
01076     aperts = cpl_apertures_new_from_image(self, labels);
01077 
01078     /* Find the aperture with the greatest flux */
01079     code = irplib_apertures_find_max_flux(aperts, &ifluxapert, 1);
01080 
01081     npixobj = cpl_apertures_get_npix(aperts, ifluxapert);
01082     objradius = sqrt((double)npixobj * CPL_MATH_1_PI);
01083     /* Size is power of two for future noise filtering w. fft */
01084     winsize = IRPLIB_MIN(IRPLIB_MIN(nx, ny), irplib_roundup_power2
01085                          ((uint32_t)(3.0 * objradius + 0.5)));
01086 
01087     xposmax = cpl_apertures_get_maxpos_x(aperts, ifluxapert);
01088     yposmax = cpl_apertures_get_maxpos_y(aperts, ifluxapert);
01089     xposcen = cpl_apertures_get_centroid_x(aperts, ifluxapert);
01090     yposcen = cpl_apertures_get_centroid_y(aperts, ifluxapert);
01091     valmax  = cpl_apertures_get_max(aperts, ifluxapert);
01092 
01093     cpl_apertures_delete(aperts);
01094     cpl_image_delete(labels);
01095 
01096     cpl_msg_debug(cpl_func, "Object radius at S/R=%g: %g (window-size=%u)",
01097                   sigma, objradius, (unsigned)winsize);
01098     cpl_msg_debug(cpl_func, "Object-peak @ (%d, %d) = %g", (int)xposmax,
01099                   (int)yposmax, valmax);
01100 
01101     gauss_parameters = cpl_array_new(7, CPL_TYPE_DOUBLE);
01102     cpl_array_set_double(gauss_parameters, 0, median);
01103 
01104     code = cpl_fit_image_gaussian(self, NULL, xposcen, yposcen,
01105                                   winsize, winsize, gauss_parameters,
01106                                   NULL, NULL, NULL,
01107                                   NULL, NULL, NULL, 
01108                                   NULL, NULL, NULL);
01109     if (!code) {
01110         const double M_x = cpl_array_get_double(gauss_parameters, 3, NULL);
01111         const double M_y = cpl_array_get_double(gauss_parameters, 4, NULL);
01112 
01113         valfit = irplib_gaussian_eval_2d(gauss_parameters, M_x, M_y);
01114 
01115         if (!cpl_errorstate_is_equal(prestate)) {
01116             code = cpl_error_get_code();
01117         } else {
01118             *pxpos        = M_x;
01119             *pypos        = M_y;
01120             *ppeak        = valfit;
01121 
01122             cpl_msg_debug(cpl_func, "Gauss-fit @ (%g, %g) = %g",
01123                           M_x, M_y, valfit);
01124         }
01125     }
01126     cpl_array_delete(gauss_parameters);
01127 
01128 #ifdef IRPLIB_STREHL_USE_CPL_IMAGE_FIT_GAUSSIAN
01129     if (code || valfit < valmax) {
01130         cpl_errorstate_set(prestate);
01131 
01132         code = cpl_image_fit_gaussian(self, xposcen, yposcen,
01133                                       (int)(2.0 * objradius),
01134                                       &norm,
01135                                       &xcen,
01136                                       &ycen,
01137                                       &sig_x,
01138                                       &sig_y,
01139                                       &fwhm_x,
01140                                       &fwhm_y);
01141 
01142         if (!code) {
01143             valfit = irplib_gaussian_2d(0.0, 0.0, norm, sig_x, sig_y);
01144 
01145             cpl_msg_debug(cpl_func, "Gauss-Fit @ (%g, %g) = %g. norm=%g, "
01146                           "sigma=(%g, %g)", xcen, ycen, valfit, norm,
01147                           sig_x, sig_y);
01148 
01149             if (valfit > valmax) {
01150                 *pxpos   = xcen;
01151                 *pypos   = ycen;
01152                 *ppeak   = valfit;
01153             }
01154         }
01155     }
01156 #endif
01157 
01158     if (code || valfit < valmax) {
01159         cpl_errorstate_set(prestate);
01160         *pxpos   = xposcen;
01161         *pypos   = yposcen;
01162         *ppeak   = valmax;
01163     }
01164 
01165     return code ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
01166 }
01167 #endif
Generated on Mon Feb 17 15:01:44 2014 for NACO Pipeline Reference Manual by  doxygen 1.6.3