00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031
00032
00033
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
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
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
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
00272 const double window_size = (double)(IRPLIB_STREHL_RAD_CENTRAL);
00273
00274
00275 const double strehl_error_coefficient = CPL_MATH_PI * 0.007 / 0.0271;
00276 double ring[4];
00277
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
00286 cpl_ensure_code(window_size > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00287
00288
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
00305
00306
00307
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
00314 *psf_peak = cpl_image_get_max(psf);
00315 cpl_image_delete(psf);
00316
00317 assert( *psf_peak > 0.0);
00318 *psf_flux = 1.0;
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
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
00340 star_radius = r1/pscale;
00341
00342
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
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
00365
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
00380
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);
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
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
00440 const int lx = (int)(xpos - rad);
00441 const int ly = (int)(ypos - rad);
00442
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
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
00501 const int lx = (int)(xpos - rad_ext);
00502 const int ly = (int)(ypos - rad_ext);
00503
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
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
00525 pix_arr = cpl_vector_new(mpix);
00526
00527
00528
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
00559
00560
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
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 {
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
00618
00619
00620
00621
00622
00623
00624
00625
00626 cpl_image_fft(otf_image, NULL, CPL_FFT_UNNORMALIZED) ||
00627
00628
00629 cpl_image_abs(otf_image) ||
00630
00631
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
00670 const double obs_ratio = m1 != 0.0 ? m2 / m1 : 0.0;
00671
00672 const double rpscale = pscale * CPL_MATH_2PI / (double)(360 * 60 * 60);
00673
00674 const double f_max = m1 * rpscale * (double)size;
00675
00676
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
00687 cpl_ensure(size % 2 == 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00688
00689
00690 cpl_ensure(2.0 * lam > dlam, CPL_ERROR_ILLEGAL_INPUT, NULL);
00691
00692
00693 lam /= 1.0e6;
00694 dlam /= 1.0e6;
00695
00696
00697 otf_data = (double*)cpl_malloc(size * size * sizeof(*otf_data));
00698
00699
00700
00701
00702
00703
00704 for (j = 0; j <= pix0; j++) {
00705 double sinc_y_9 = 0.0;
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;
00714 double otfxy = 0.0;
00715 int k;
00716
00717 assert( j > 0 );
00718
00719
00720
00721 for (k = 4; k >= -4; k--) {
00722
00723 const double lambda = lam - dlam * (double)k / 8.0;
00724
00725
00726
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
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
00746
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
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;
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
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
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
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
00848 const int lx = (int)(xpos - radius);
00849 const int ly = (int)(ypos - radius);
00850
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
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
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;
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
01051 const double threshold = median + sigma * med_dist;
01052
01053
01054
01055 code = cpl_mask_threshold_image(selection, self, threshold, DBL_MAX,
01056 CPL_BINARY_1);
01057
01058 if (code) break;
01059
01060
01061 cpl_image_delete(labels);
01062 labels = cpl_image_labelise_mask_create(selection, &nlabels);
01063 }
01064 sigma *= 2.0;
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
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
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