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
00089 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00090 #ifdef IRPLIB_STREHL_USE_CPL_IMAGE_FIT_GAUSSIAN
00091 static double irplib_gaussian_2d(double, double, double, double, double);
00092 #endif
00093
00094 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(6, 9, 1)
00095 #define irplib_gaussian_eval_2d cpl_gaussian_eval_2d
00096 #else
00097 static double irplib_gaussian_eval_2d(const cpl_array *, double, double);
00098 #endif
00099
00100 static uint32_t irplib_roundup_power2(uint32_t v) CPL_ATTR_CONST;
00101
00102 static
00103 cpl_error_code irplib_gaussian_maxpos(const cpl_image *,
00104 double,
00105 double *,
00106 double *,
00107 double *);
00108 #endif
00109
00110
00111
00112
00115
00145
00146 cpl_error_code irplib_strehl_compute(const cpl_image * im,
00147 double m1,
00148 double m2,
00149 double lam,
00150 double dlam,
00151 double pscale,
00152 int size,
00153 double xpos,
00154 double ypos,
00155 double r1,
00156 double r2,
00157 double r3,
00158 int noise_box_sz,
00159 int noise_nsamples,
00160 double * strehl,
00161 double * strehl_err,
00162 double * star_bg,
00163 double * star_peak,
00164 double * star_flux,
00165 double * psf_peak,
00166 double * psf_flux,
00167 double * bg_noise)
00168 {
00169 cpl_image * psf;
00170 double star_radius, max_radius;
00171
00172
00173 const double window_size = (double)(IRPLIB_STREHL_RAD_CENTRAL);
00174
00175
00176 const double strehl_error_coefficient = CPL_MATH_PI * 0.007 / 0.0271;
00177 double ring[4];
00178
00179 int ring_tries = 3;
00180 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00181 double xposfit = 0.0, yposfit = 0.0, peak = 0.0;
00182 cpl_error_code code;
00183 #endif
00184 cpl_errorstate prestate = cpl_errorstate_get();
00185
00186
00187 cpl_ensure_code(window_size > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00188
00189
00190 cpl_ensure_code(im != NULL, CPL_ERROR_NULL_INPUT);
00191 cpl_ensure_code(strehl != NULL, CPL_ERROR_NULL_INPUT);
00192 cpl_ensure_code(strehl_err != NULL, CPL_ERROR_NULL_INPUT);
00193 cpl_ensure_code(star_bg != NULL, CPL_ERROR_NULL_INPUT);
00194 cpl_ensure_code(star_peak != NULL, CPL_ERROR_NULL_INPUT);
00195 cpl_ensure_code(star_flux != NULL, CPL_ERROR_NULL_INPUT);
00196 cpl_ensure_code(psf_peak != NULL, CPL_ERROR_NULL_INPUT);
00197 cpl_ensure_code(psf_flux != NULL, CPL_ERROR_NULL_INPUT);
00198
00199 cpl_ensure_code(pscale > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00200
00201 cpl_ensure_code(r1 > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00202 cpl_ensure_code(r2 > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00203 cpl_ensure_code(r3 > r2, CPL_ERROR_ILLEGAL_INPUT);
00204
00205
00206
00207
00208
00209 psf = irplib_strehl_generate_psf(m1, m2, lam, dlam, pscale, size);
00210 if (psf == NULL) {
00211 return cpl_error_set_where(cpl_func);
00212 }
00213
00214
00215 *psf_peak = cpl_image_get_max(psf);
00216 cpl_image_delete(psf);
00217
00218 assert( *psf_peak > 0.0);
00219 *psf_flux = 1.0;
00220
00221 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00222 code = irplib_gaussian_maxpos(im, IRPLIB_STREHL_DETECT_LEVEL,
00223 &xposfit, &yposfit, &peak);
00224 if (code) {
00225 cpl_errorstate_set(prestate);
00226 } else {
00227 xpos = xposfit;
00228 ypos = yposfit;
00229 }
00230 #endif
00231
00232
00233 *star_bg = irplib_strehl_ring_background(im, xpos, ypos,
00234 r2/pscale, r3/pscale,
00235 IRPLIB_BG_METHOD_AVER_REJ);
00236 if (!cpl_errorstate_is_equal(prestate)) {
00237 return cpl_error_set_where(cpl_func);
00238 }
00239
00240
00241 star_radius = r1/pscale;
00242
00243
00244 *star_flux = irplib_strehl_disk_flux(im, xpos, ypos, star_radius, *star_bg);
00245
00246 if (*star_flux <= 0.0) {
00247 return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
00248 "Non-positive star flux=%g (Star "
00249 "background=%g)", *star_flux, *star_bg);
00250 }
00251
00252
00253 max_radius = window_size < star_radius ? window_size : star_radius;
00254 cpl_ensure_code(!irplib_strehl_disk_max(im, xpos, ypos, max_radius,
00255 star_peak), cpl_error_get_code());
00256 *star_peak -= *star_bg;
00257
00258 if (*star_flux <= 0.0) {
00259 return cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
00260 "Non-positive star peak=%g (Star "
00261 "background=%g, Star flux=%g)",
00262 *star_flux, *star_bg, *star_flux);
00263 }
00264
00265
00266
00267 *strehl = (*star_peak * *psf_flux ) / ( *star_flux * *psf_peak);
00268
00269 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00270 if (code == CPL_ERROR_NONE && peak > *star_peak && *star_peak > 0.0 &&
00271 *strehl * peak / *star_peak <= 1.0) {
00272 cpl_msg_debug(cpl_func, "Increasing Strehl from %g: %g (%g)",
00273 *strehl, *strehl * peak / *star_peak,
00274 peak / *star_peak);
00275 *strehl *= peak / *star_peak;
00276 *star_peak = peak;
00277 }
00278 #endif
00279
00280
00281 ring[0] = xpos;
00282 ring[1] = ypos;
00283 ring[2] = r2/pscale;
00284 ring[3] = r3/pscale;
00285
00286 while (cpl_flux_get_noise_ring(im, ring, noise_box_sz, noise_nsamples,
00287 bg_noise, NULL) && --ring_tries > 0);
00288 if (ring_tries > 0) {
00289 cpl_errorstate_set(prestate);
00290 } else {
00291 return cpl_error_set_where(cpl_func);
00292 }
00293
00294 *strehl_err = strehl_error_coefficient * (*bg_noise) * pscale *
00295 star_radius * star_radius / *star_flux;
00296
00297 if (*strehl > 1.0) {
00298 cpl_msg_warning(cpl_func, "Extreme Strehl-ratio=%g (strehl-error=%g, "
00299 "star_peak=%g, star_flux=%g, psf_peak=%g, psf_flux=%g)",
00300 *strehl, *strehl_err, *star_peak, *star_flux, *psf_peak,
00301 *psf_flux);
00302 }
00303
00304
00305 return *strehl_err >= 0.0
00306 ? CPL_ERROR_NONE
00307 : cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
00308 "Negative strehl-error=%g (Strehl-ratio=%g, "
00309 "star_peak=%g, star_flux=%g, psf_peak=%g, "
00310 "psf_flux=%g", *strehl_err, *strehl,
00311 *star_peak, *star_flux, *psf_peak, *psf_flux);
00312 }
00313
00314
00327
00328 double irplib_strehl_disk_flux(const cpl_image * im,
00329 double xpos,
00330 double ypos,
00331 double rad,
00332 double bg)
00333 {
00334 const int nx = cpl_image_get_size_x(im);
00335 const int ny = cpl_image_get_size_y(im);
00336
00337 const int lx = (int)(xpos - rad);
00338 const int ly = (int)(ypos - rad);
00339
00340 const int ux = (int)(xpos + rad) + 1;
00341 const int uy = (int)(ypos + rad) + 1;
00342
00343 const double sqr = rad * rad;
00344 double flux = 0.0;
00345 int i, j;
00346
00347
00348
00349 cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
00350 cpl_ensure(rad > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
00351
00352 for (j = IRPLIB_MAX(ly, 0); j < IRPLIB_MIN(uy, ny); j++) {
00353 const double yj = (double)j - ypos;
00354 for (i = IRPLIB_MAX(lx, 0); i < IRPLIB_MIN(ux, nx); i++) {
00355 const double xi = (double)i - xpos;
00356 const double dist = yj * yj + xi * xi;
00357 if (dist <= sqr) {
00358 int isbad;
00359 const double value = cpl_image_get(im, i+1, j+1, &isbad);
00360
00361 if (!isbad ) {
00362
00363 flux += value - bg;
00364
00365 }
00366 }
00367 }
00368 }
00369
00370 return flux;
00371 }
00372
00373
00385
00386 double irplib_strehl_ring_background(const cpl_image * im,
00387 double xpos,
00388 double ypos,
00389 double rad_int,
00390 double rad_ext,
00391 irplib_strehl_bg_method mode)
00392 {
00393 const int nx = cpl_image_get_size_x(im);
00394 const int ny = cpl_image_get_size_y(im);
00395
00396 const int lx = (int)(xpos - rad_ext);
00397 const int ly = (int)(ypos - rad_ext);
00398
00399 const int ux = (int)(xpos + rad_ext) + 1;
00400 const int uy = (int)(ypos + rad_ext) + 1;
00401 int mpix, npix;
00402 const double sqr_int = rad_int * rad_int;
00403 const double sqr_ext = rad_ext * rad_ext;
00404 cpl_vector * pix_arr;
00405 double flux = 0.0;
00406 int i, j;
00407
00408
00409 cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
00410 cpl_ensure(rad_int > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
00411 cpl_ensure(rad_ext > rad_int, CPL_ERROR_ILLEGAL_INPUT, 0.0);
00412
00413 cpl_ensure(mode == IRPLIB_BG_METHOD_AVER_REJ ||
00414 mode == IRPLIB_BG_METHOD_MEDIAN,
00415 CPL_ERROR_UNSUPPORTED_MODE, 0.0);
00416
00417 mpix = (int)((2.0 * rad_ext + 1.0) * (2.0 * rad_ext + 1.0));
00418
00419
00420 pix_arr = cpl_vector_new(mpix);
00421
00422
00423
00424 npix = 0;
00425 for (j = IRPLIB_MAX(ly, 0); j < IRPLIB_MIN(uy, ny); j++) {
00426 const double yj = (double)j - ypos;
00427 for (i = IRPLIB_MAX(lx, 0); i < IRPLIB_MIN(ux, nx); i++) {
00428 const double xi = (double)i - xpos;
00429 const double dist = yj * yj + xi * xi;
00430 if (sqr_int <= dist && dist <= sqr_ext) {
00431 int isbad;
00432 const double value = cpl_image_get(im, i+1, j+1, &isbad);
00433
00434 if (!isbad) {
00435 cpl_vector_set(pix_arr, npix, value);
00436 npix++;
00437 }
00438 }
00439 }
00440 }
00441
00442 assert(npix <= mpix);
00443
00444 if (npix < IRPLIB_DISK_BG_MIN_PIX_NB) {
00445 cpl_vector_delete(pix_arr);
00446 (void)cpl_error_set_message(cpl_func, CPL_ERROR_DATA_NOT_FOUND, "Need "
00447 "at least %d (not %d <= %d) samples to "
00448 "compute noise", IRPLIB_DISK_BG_MIN_PIX_NB,
00449 npix, mpix);
00450 return 0.0;
00451 }
00452
00453
00454
00455
00456 pix_arr = cpl_vector_wrap(npix, (double*)cpl_vector_unwrap(pix_arr));
00457
00458 if (mode == IRPLIB_BG_METHOD_AVER_REJ) {
00459 const int low_ind = (int)((double)npix * IRPLIB_DISK_BG_REJ_LOW);
00460 const int high_ind = (int)((double)npix
00461 * (1.0 - IRPLIB_DISK_BG_REJ_HIGH));
00462
00463
00464 cpl_vector_sort(pix_arr, CPL_SORT_ASCENDING);
00465
00466 for (i=low_ind; i<high_ind; i++) {
00467 flux += cpl_vector_get(pix_arr, i);
00468 }
00469 if (high_ind - low_ind > 1) flux /= (double)(high_ind - low_ind);
00470 } else {
00471 flux = cpl_vector_get_median(pix_arr);
00472 }
00473
00474 cpl_vector_delete(pix_arr);
00475
00476 return flux;
00477 }
00478
00479
00499
00500 cpl_image * irplib_strehl_generate_psf(double m1,
00501 double m2,
00502 double lam,
00503 double dlam,
00504 double pscale,
00505 int size)
00506 {
00507 cpl_image * otf_image = irplib_strehl_generate_otf(m1, m2, lam, dlam,
00508 size, pscale);
00509
00510 if (otf_image == NULL ||
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521 cpl_image_fft(otf_image, NULL, CPL_FFT_UNNORMALIZED) ||
00522
00523
00524 cpl_image_abs(otf_image) ||
00525
00526
00527 cpl_image_normalise(otf_image, CPL_NORM_FLUX)) {
00528
00529 (void)cpl_error_set_where(cpl_func);
00530 cpl_image_delete(otf_image);
00531 otf_image = NULL;
00532 }
00533
00534 return otf_image;
00535 }
00536
00539
00555
00556 static cpl_image * irplib_strehl_generate_otf(double m1,
00557 double m2,
00558 double lam,
00559 double dlam,
00560 int size,
00561 double pscale)
00562 {
00563 double * otf_data;
00564
00565 const double obs_ratio = m1 != 0.0 ? m2 / m1 : 0.0;
00566
00567 const double rpscale = pscale * CPL_MATH_2PI / (double)(360 * 60 * 60);
00568
00569 const double f_max = m1 * rpscale * (double)size;
00570
00571
00572 const int pix0 = size / 2;
00573 int i, j;
00574
00575
00576 cpl_ensure(m2 > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00577 cpl_ensure(m1 > m2, CPL_ERROR_ILLEGAL_INPUT, NULL);
00578 cpl_ensure(dlam > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00579 cpl_ensure(pscale > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00580 cpl_ensure(size > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00581
00582 cpl_ensure(size % 2 == 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
00583
00584
00585 cpl_ensure(2.0 * lam > dlam, CPL_ERROR_ILLEGAL_INPUT, NULL);
00586
00587
00588 lam /= 1.0e6;
00589 dlam /= 1.0e6;
00590
00591
00592 otf_data = (double*)cpl_malloc(size * size * sizeof(*otf_data));
00593
00594
00595
00596
00597
00598
00599 for (j = 0; j <= pix0; j++) {
00600 double sinc_y_9 = 0.0;
00601 for (i = 0; i <= j; i++) {
00602 if (i == 0 && j == 0) {
00603 otf_data[size * pix0 + pix0] = 1.0;
00604 } else {
00605 const double x = (double)i;
00606 const double y = (double)j;
00607 const double sqdist = x * x + y * y;
00608 double f_lambda = 0.0;
00609 double sinc_xy_9 = 0.0;
00610 double otfxy = 0.0;
00611 int k;
00612
00613 assert( j > 0 );
00614
00615
00616
00617 for (k = 4; k >= -4; k--) {
00618
00619 const double lambda = lam - dlam * (double)k / 8.0;
00620
00621
00622
00623 if (sqdist * lambda * lambda >= f_max * f_max) break;
00624
00625 if (k == 4) {
00626 f_lambda = sqrt(sqdist) / f_max;
00627 if (i == 0) {
00628
00629 sinc_xy_9 = sinc_y_9 =
00630 PSF_sinc_norm(y / (double)size) / 9.0;
00631 } else {
00632 sinc_xy_9 = sinc_y_9 *
00633 PSF_sinc_norm(x / (double)size);
00634 }
00635 }
00636
00637 otfxy += PSF_TelOTF(f_lambda * lambda, obs_ratio);
00638 }
00639 otfxy *= sinc_xy_9;
00640
00641
00642
00643 otf_data[size * (pix0 - j) + pix0 - i] = otfxy;
00644 otf_data[size * (pix0 - i) + pix0 - j] = otfxy;
00645 if (i < pix0) {
00646 otf_data[size * (pix0 - j) + pix0 + i] = otfxy;
00647 otf_data[size * (pix0 + i) + pix0 - j] = otfxy;
00648 if (j < pix0) {
00649 otf_data[size * (pix0 + j) + pix0 - i] = otfxy;
00650 otf_data[size * (pix0 - i) + pix0 + j] = otfxy;
00651 otf_data[size * (pix0 + j) + pix0 + i] = otfxy;
00652 otf_data[size * (pix0 + i) + pix0 + j] = otfxy;
00653 }
00654 }
00655 }
00656 }
00657 }
00658
00659 return cpl_image_wrap_double(size, size, otf_data);
00660 }
00661
00662
00663
00664
00665 static double PSF_H1(
00666 double f,
00667 double u,
00668 double v)
00669 {
00670 const double e = fabs(1.0-v) > 0.0 ? -1.0 : 1.0;
00671
00672 return((v*v/CPL_MATH_PI)*acos((f/v)*(1.0+e*(1.0-u*u)/(4.0*f*f))));
00673 }
00674
00675
00676
00677
00678 static double PSF_H2(double f,
00679 double u)
00680 {
00681 const double tmp1 = (2.0 * f) / (1.0 + u);
00682 const double tmp2 = (1.0 - u) / (2.0 * f);
00683
00684 return -1.0 * (f/CPL_MATH_PI) * (1.0+u)
00685 * sqrt((1.0-tmp1*tmp1)*(1.0-tmp2*tmp2));
00686 }
00687
00688
00689
00690
00691 static double PSF_G(double f,
00692 double u)
00693 {
00694 if (f <= (1.0-u)/2.0) return(u*u);
00695 if (f >= (1.0+u)/2.0) return(0.0);
00696 else return(PSF_H1(f,u,1.0) + PSF_H1(f,u,u) + PSF_H2(f,u));
00697 }
00698
00699
00707
00708 static double PSF_sinc_norm(double x)
00709 {
00710 return sin(x * CPL_MATH_PI) / (x * CPL_MATH_PI);
00711 }
00712
00713
00714
00715
00716 static double PSF_TelOTF(double f,
00717 double u)
00718 {
00719 return((PSF_G(f,1.0)+u*u*PSF_G(f/u,1.0)-2.0*PSF_G(f,u))/(1.0-u*u));
00720 }
00721
00722
00733
00734 cpl_error_code irplib_strehl_disk_max(const cpl_image * self,
00735 double xpos,
00736 double ypos,
00737 double radius,
00738 double * ppeak)
00739 {
00740
00741 const int nx = cpl_image_get_size_x(self);
00742 const int ny = cpl_image_get_size_y(self);
00743
00744 const int lx = (int)(xpos - radius);
00745 const int ly = (int)(ypos - radius);
00746
00747 const int ux = (int)(xpos + radius) + 1;
00748 const int uy = (int)(ypos + radius) + 1;
00749
00750 const double sqr = radius * radius;
00751 cpl_boolean first = CPL_TRUE;
00752 int i, j;
00753
00754
00755
00756 cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
00757 cpl_ensure_code(ppeak != NULL, CPL_ERROR_NULL_INPUT);
00758 cpl_ensure_code(radius > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00759
00760
00761 for (j = IRPLIB_MAX(ly, 0); j < IRPLIB_MIN(uy, ny); j++) {
00762 const double yj = (double)j - ypos;
00763 for (i = IRPLIB_MAX(lx, 0); i < IRPLIB_MIN(ux, nx); i++) {
00764 const double xi = (double)i - xpos;
00765 const double dist = yj * yj + xi * xi;
00766 if (dist <= sqr) {
00767 int isbad;
00768 const double value = cpl_image_get(self, i+1, j+1, &isbad);
00769
00770 if (!isbad &&
00771 (first || value > *ppeak)) {
00772 first = CPL_FALSE;
00773 *ppeak = value;
00774 }
00775 }
00776 }
00777 }
00778
00779 return first
00780 ? cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND)
00781 : CPL_ERROR_NONE;
00782 }
00783
00784 #ifndef IRPLIB_NO_FIT_GAUSSIAN
00785 #ifdef IRPLIB_STREHL_USE_CPL_IMAGE_FIT_GAUSSIAN
00786
00802
00803 static double irplib_gaussian_2d(double x,
00804 double y,
00805 double norm,
00806 double sig_x,
00807 double sig_y)
00808 {
00809
00810
00811 return norm / (sig_x * sig_y * CPL_MATH_2PI *
00812 exp(x * x / (2.0 * sig_x * sig_x) +
00813 y * y / (2.0 * sig_y * sig_y)));
00814 }
00815 #endif
00816
00817 #if defined CPL_VERSION_CODE && CPL_VERSION_CODE >= CPL_VERSION(6, 9, 1)
00818 #else
00819
00838
00839 static
00840 double irplib_gaussian_eval_2d(const cpl_array * self, double x, double y)
00841 {
00842 cpl_errorstate prestate = cpl_errorstate_get();
00843 const double B = cpl_array_get_double(self, 0, NULL);
00844 const double A = cpl_array_get_double(self, 1, NULL);
00845 const double R = cpl_array_get_double(self, 2, NULL);
00846 const double M_x = cpl_array_get_double(self, 3, NULL);
00847 const double M_y = cpl_array_get_double(self, 4, NULL);
00848 const double S_x = cpl_array_get_double(self, 5, NULL);
00849 const double S_y = cpl_array_get_double(self, 6, NULL);
00850
00851 double value = 0.0;
00852
00853 if (!cpl_errorstate_is_equal(prestate)) {
00854 (void)cpl_error_set_where(cpl_func);
00855 } else if (cpl_array_get_size(self) != 7) {
00856 (void)cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
00857 } else if (fabs(R) < 1.0 && S_x != 0.0 && S_y != 0.0) {
00858 const double x_n = (x - M_x) / S_x;
00859 const double y_n = (y - M_y) / S_y;
00860
00861 value = B + A / (CPL_MATH_2PI * S_x * S_y * sqrt(1 - R * R)) *
00862 exp(-0.5 / (1 - R * R) * ( x_n * x_n + y_n * y_n
00863 - 2.0 * R * x_n * y_n));
00864 } else if (fabs(R) > 1.0) {
00865 (void)cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_OUTPUT,
00866 "fabs(R=%g) > 1", R);
00867 } else {
00868 (void)cpl_error_set_message(cpl_func, CPL_ERROR_DIVISION_BY_ZERO,
00869 "R=%g. Sigma=(%g, %g)", R, S_x, S_y);
00870 }
00871
00872 return value;
00873 }
00874 #endif
00875
00876
00883
00884 static uint32_t irplib_roundup_power2(uint32_t v)
00885 {
00886 v |= v >> 1;
00887 v |= v >> 2;
00888 v |= v >> 4;
00889 v |= v >> 8;
00890 v |= v >> 16;
00891
00892 return v + 1;
00893 }
00894
00895
00896
00907
00908 static
00909 cpl_error_code irplib_gaussian_maxpos(const cpl_image * self,
00910 double sigma,
00911 double * pxpos,
00912 double * pypos,
00913 double * ppeak)
00914 {
00915
00916 const cpl_size nx = cpl_image_get_size_x(self);
00917 const cpl_size ny = cpl_image_get_size_y(self);
00918 int iretry = 3;
00919 int ifluxapert = 0;
00920 double med_dist;
00921 const double median = cpl_image_get_median_dev(self, &med_dist);
00922 cpl_mask * selection;
00923 cpl_size nlabels = 0;
00924 cpl_image * labels = NULL;
00925 cpl_apertures * aperts;
00926 cpl_size npixobj;
00927 double objradius;
00928 cpl_size winsize;
00929 cpl_size xposmax, yposmax;
00930 double xposcen, yposcen;
00931 double valmax, valfit = -1.0;
00932 #ifdef IRPLIB_STREHL_USE_CPL_IMAGE_FIT_GAUSSIAN
00933 double norm, xcen, ycen, sig_x, sig_y, fwhm_x, fwhm_y;
00934 #endif
00935 cpl_array * gauss_parameters = NULL;
00936 cpl_errorstate prestate = cpl_errorstate_get();
00937 cpl_error_code code = CPL_ERROR_NONE;
00938
00939
00940 cpl_ensure_code( sigma > 0.0, CPL_ERROR_ILLEGAL_INPUT);
00941
00942 selection = cpl_mask_new(nx, ny);
00943
00944 for (; iretry > 0 && nlabels == 0; iretry--, sigma *= 0.5) {
00945
00946
00947 const double threshold = median + sigma * med_dist;
00948
00949
00950
00951 code = cpl_mask_threshold_image(selection, self, threshold, DBL_MAX,
00952 CPL_BINARY_1);
00953
00954 if (code) break;
00955
00956
00957 cpl_image_delete(labels);
00958 labels = cpl_image_labelise_mask_create(selection, &nlabels);
00959 }
00960 sigma *= 2.0;
00961
00962 cpl_mask_delete(selection);
00963
00964 if (code) {
00965 cpl_image_delete(labels);
00966 return cpl_error_set_where(cpl_func);
00967 } else if (nlabels == 0) {
00968 cpl_image_delete(labels);
00969 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
00970 }
00971
00972 aperts = cpl_apertures_new_from_image(self, labels);
00973
00974
00975 code = irplib_apertures_find_max_flux(aperts, &ifluxapert, 1);
00976
00977 if (code) {
00978 cpl_apertures_delete(aperts);
00979 cpl_image_delete(labels);
00980 return cpl_error_set(cpl_func, CPL_ERROR_DATA_NOT_FOUND);
00981 }
00982
00983 npixobj = cpl_apertures_get_npix(aperts, ifluxapert);
00984 objradius = sqrt((double)npixobj * CPL_MATH_1_PI);
00985
00986 winsize = IRPLIB_MIN(IRPLIB_MIN(nx, ny), irplib_roundup_power2
00987 ((uint32_t)(3.0 * objradius + 0.5)));
00988
00989 xposmax = cpl_apertures_get_maxpos_x(aperts, ifluxapert);
00990 yposmax = cpl_apertures_get_maxpos_y(aperts, ifluxapert);
00991 xposcen = cpl_apertures_get_centroid_x(aperts, ifluxapert);
00992 yposcen = cpl_apertures_get_centroid_y(aperts, ifluxapert);
00993 valmax = cpl_apertures_get_max(aperts, ifluxapert);
00994
00995 cpl_apertures_delete(aperts);
00996 cpl_image_delete(labels);
00997
00998 cpl_msg_debug(cpl_func, "Object radius at S/R=%g: %g (window-size=%u)",
00999 sigma, objradius, (unsigned)winsize);
01000 cpl_msg_debug(cpl_func, "Object-peak @ (%d, %d) = %g", (int)xposmax,
01001 (int)yposmax, valmax);
01002
01003 gauss_parameters = cpl_array_new(7, CPL_TYPE_DOUBLE);
01004 cpl_array_set_double(gauss_parameters, 0, median);
01005
01006 code = cpl_fit_image_gaussian(self, NULL, xposcen, yposcen,
01007 winsize, winsize, gauss_parameters,
01008 NULL, NULL, NULL,
01009 NULL, NULL, NULL,
01010 NULL, NULL, NULL);
01011 if (!code) {
01012 const double M_x = cpl_array_get_double(gauss_parameters, 3, NULL);
01013 const double M_y = cpl_array_get_double(gauss_parameters, 4, NULL);
01014
01015 valfit = irplib_gaussian_eval_2d(gauss_parameters, M_x, M_y);
01016
01017 if (!cpl_errorstate_is_equal(prestate)) {
01018 code = cpl_error_get_code();
01019 } else {
01020 *pxpos = M_x;
01021 *pypos = M_y;
01022 *ppeak = valfit;
01023
01024 cpl_msg_debug(cpl_func, "Gauss-fit @ (%g, %g) = %g",
01025 M_x, M_y, valfit);
01026 }
01027 }
01028 cpl_array_delete(gauss_parameters);
01029
01030 #ifdef IRPLIB_STREHL_USE_CPL_IMAGE_FIT_GAUSSIAN
01031 if (code || valfit < valmax) {
01032 cpl_errorstate_set(prestate);
01033
01034 code = cpl_image_fit_gaussian(self, xposcen, yposcen,
01035 (int)(2.0 * objradius),
01036 &norm,
01037 &xcen,
01038 &ycen,
01039 &sig_x,
01040 &sig_y,
01041 &fwhm_x,
01042 &fwhm_y);
01043
01044 if (!code) {
01045 valfit = irplib_gaussian_2d(0.0, 0.0, norm, sig_x, sig_y);
01046
01047 cpl_msg_debug(cpl_func, "Gauss-Fit @ (%g, %g) = %g. norm=%g, "
01048 "sigma=(%g, %g)", xcen, ycen, valfit, norm,
01049 sig_x, sig_y);
01050
01051 if (valfit > valmax) {
01052 *pxpos = xcen;
01053 *pypos = ycen;
01054 *ppeak = valfit;
01055 }
01056 }
01057 }
01058 #endif
01059
01060 if (code || valfit < valmax) {
01061 cpl_errorstate_set(prestate);
01062 *pxpos = xposcen;
01063 *pypos = yposcen;
01064 *ppeak = valmax;
01065 }
01066
01067 return code ? cpl_error_set_where(cpl_func) : CPL_ERROR_NONE;
01068 }
01069 #endif