DETMON Pipeline Reference Manual  1.2.4
irplib_strehl.c
1 /* $Id: irplib_strehl.c,v 1.43 2009/11/18 21:37:48 llundin Exp $
2  *
3  * This file is part of the irplib package
4  * Copyright (C) 2002,2003 European Southern Observatory
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19  */
20 
21 /*
22  * $Author: llundin $
23  * $Date: 2009/11/18 21:37:48 $
24  * $Revision: 1.43 $
25  * $Name: detmon-1_2_4 $
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 /*-----------------------------------------------------------------------------
33  Includes
34  -----------------------------------------------------------------------------*/
35 
36 #include <string.h>
37 #include <assert.h>
38 #include <math.h>
39 #include <float.h>
40 
41 #include <cpl.h>
42 
43 #include "irplib_utils.h"
44 #include "irplib_strehl.h"
45 
46 /*----------------------------------------------------------------------------*/
50 /*----------------------------------------------------------------------------*/
51 
52 /*-----------------------------------------------------------------------------
53  Define
54  -----------------------------------------------------------------------------*/
55 
56 #ifndef IRPLIB_STREHL_RAD_CENTRAL
57 #define IRPLIB_STREHL_RAD_CENTRAL 5
58 #endif
59 
60 #define IRPLIB_DISK_BG_MIN_PIX_NB 30
61 #define IRPLIB_DISK_BG_REJ_LOW 0.1
62 #define IRPLIB_DISK_BG_REJ_HIGH 0.1
63 
64 /*-----------------------------------------------------------------------------
65  Functions prototypes
66  -----------------------------------------------------------------------------*/
67 
68 static cpl_image * irplib_strehl_generate_otf(double, double, double, double,
69  int, double);
70 static double PSF_H1(double, double, double);
71 static double PSF_H2(double, double);
72 static double PSF_G(double, double);
73 static double PSF_sinc(double);
74 static double PSF_TelOTF(double, double);
75 static cpl_error_code update_bad_pixel_map(cpl_image* im);
76 
77 
78 /*-----------------------------------------------------------------------------
79  Functions code
80  -----------------------------------------------------------------------------*/
88 cpl_error_code update_bad_pixel_map(cpl_image* im)
89 {
90  int szx = cpl_image_get_size_x(im);
91  int szy = cpl_image_get_size_y(im);
92  int x = 0;
93  cpl_mask* bpm = cpl_image_get_bpm(im);
94 
95  for (x = 1; x <=szx; x++)
96  {
97  int y = 0;
98  for(y = 1; y <= szy; y++)
99  {
100  int isnull = 0;
101  double value = cpl_image_get(im, x, y, &isnull);
102  if (isnan(value))
103  {
104  cpl_mask_set(bpm, x, y, CPL_BINARY_1);
105  }
106  }
107  }
108  return cpl_error_get_code();
109 }
140 cpl_error_code irplib_strehl_mark_bad_and_compute(cpl_image * im,
141  double m1,
142  double m2,
143  double lam,
144  double dlam,
145  double pscale,
146  int size,
147  double xpos,
148  double ypos,
149  double r1,
150  double r2,
151  double r3,
152  int noise_box_sz,
153  int noise_nsamples,
154  double * strehl,
155  double * strehl_err,
156  double * star_bg,
157  double * star_peak,
158  double * star_flux,
159  double * psf_peak,
160  double * psf_flux,
161  double * bg_noise)
162 {
163  cpl_ensure_code(!update_bad_pixel_map(im), cpl_error_get_code());
164  return irplib_strehl_compute(im, m1, m2, lam, dlam, pscale, size, xpos, ypos,
165  r1,
166  r2,
167  r3,
168  noise_box_sz,
169  noise_nsamples,
170  strehl,
171  strehl_err,
172  star_bg,
173  star_peak,
174  star_flux,
175  psf_peak,
176  psf_flux,
177  bg_noise);
178 }
179 
180 /*----------------------------------------------------------------------------*/
211 /*----------------------------------------------------------------------------*/
212 cpl_error_code irplib_strehl_compute(const cpl_image * im,
213  double m1,
214  double m2,
215  double lam,
216  double dlam,
217  double pscale,
218  int size,
219  double xpos,
220  double ypos,
221  double r1,
222  double r2,
223  double r3,
224  int noise_box_sz,
225  int noise_nsamples,
226  double * strehl,
227  double * strehl_err,
228  double * star_bg,
229  double * star_peak,
230  double * star_flux,
231  double * psf_peak,
232  double * psf_flux,
233  double * bg_noise)
234 {
235  cpl_image * psf;
236  double star_radius, max_radius;
237 
238  /* FIXME: Arbitrary choice of image border */
239  const double window_size = (double)(IRPLIB_STREHL_RAD_CENTRAL);
240 
241  /* Determined empirically by C. Lidman for Strehl error computation */
242  const double strehl_error_coefficient = CPL_MATH_PI * 0.007 / 0.0271;
243  double ring[4];
244  /* cpl_flux_get_noise_ring() must succeed with this many tries */
245  int ring_tries = 3;
246  cpl_errorstate prestate;
247 
248  /* Check compile-time constant */
249  cpl_ensure_code(window_size > 0.0, CPL_ERROR_ILLEGAL_INPUT);
250 
251  /* Test inputs */
252  cpl_ensure_code(im != NULL, CPL_ERROR_NULL_INPUT);
253  cpl_ensure_code(strehl != NULL, CPL_ERROR_NULL_INPUT);
254  cpl_ensure_code(strehl_err != NULL, CPL_ERROR_NULL_INPUT);
255  cpl_ensure_code(star_bg != NULL, CPL_ERROR_NULL_INPUT);
256  cpl_ensure_code(star_peak != NULL, CPL_ERROR_NULL_INPUT);
257  cpl_ensure_code(star_flux != NULL, CPL_ERROR_NULL_INPUT);
258  cpl_ensure_code(psf_peak != NULL, CPL_ERROR_NULL_INPUT);
259  cpl_ensure_code(psf_flux != NULL, CPL_ERROR_NULL_INPUT);
260 
261  cpl_ensure_code(pscale > 0.0, CPL_ERROR_ILLEGAL_INPUT);
262 
263  cpl_ensure_code(r1 > 0.0, CPL_ERROR_ILLEGAL_INPUT);
264  cpl_ensure_code(r2 > 0.0, CPL_ERROR_ILLEGAL_INPUT);
265  cpl_ensure_code(r3 > r2, CPL_ERROR_ILLEGAL_INPUT);
266 
267  /* Computing a Strehl ratio is a story between an ideal PSF */
268  /* and a candidate image supposed to approximate this ideal PSF. */
269 
270  /* Generate first appropriate PSF to find max peak */
271  psf = irplib_strehl_generate_psf(m1, m2, lam, dlam, pscale, size);
272  cpl_ensure_code(psf != NULL, CPL_ERROR_ILLEGAL_OUTPUT);
273 
274  /* Compute flux in PSF and find max peak */
275  *psf_peak = cpl_image_get_max(psf);
276  cpl_image_delete(psf);
277 
278  assert( *psf_peak > 0.0); /* The ideal PSF has a positive maximum */
279  *psf_flux = 1.0; /* The psf flux, cpl_image_get_flux(psf), is always 1 */
280 
281  /* Measure the background in the candidate image */
282  *star_bg = irplib_strehl_ring_background(im, xpos, ypos, r2/pscale, r3/pscale,
283  IRPLIB_BG_METHOD_AVER_REJ);
284 
285  /* Compute star_radius in pixels */
286  star_radius = r1/pscale;
287 
288  /* Measure the flux on the candidate image */
289  *star_flux = irplib_strehl_disk_flux(im, xpos, ypos, star_radius, *star_bg);
290 
291  cpl_ensure_code(*star_flux > 0.0, CPL_ERROR_ILLEGAL_OUTPUT);
292 
293  /* Find the peak value on the central part of the candidate image */
294  max_radius = window_size < star_radius ? window_size : star_radius;
295  cpl_ensure_code(!irplib_strehl_disk_max(im, xpos, ypos, max_radius,
296  star_peak), cpl_error_get_code());
297  *star_peak -= *star_bg;
298 
299  cpl_ensure_code(*star_peak > 0.0, CPL_ERROR_ILLEGAL_OUTPUT);
300 
301  /* Compute Strehl */
302  /* (StarPeak / StarFlux) / (PsfPeak / PsfFlux) */
303  *strehl = (*star_peak * *psf_flux ) / ( *star_flux * *psf_peak);
304 
305  if (*strehl > 1)
306  cpl_msg_warning(cpl_func, "Extreme Strehl-ratio=%g, star_peak=%g, "
307  "star_flux=%g, psf_peak=%g, psf_flux=%g", *strehl,
308  *star_peak, *star_flux, *psf_peak, *psf_flux);
309 
310  /* Compute Strehl error */
311  /* computation could fail if the image contains pixels with NaN value*/
312  ring[0] = xpos;
313  ring[1] = ypos;
314  ring[2] = r2/pscale;
315  ring[3] = r3/pscale;
316 
317  /* FIXME: With CPL 5.1 the recoverable error
318  will be CPL_ERROR_DATA_NOT_FOUND */
319  prestate = cpl_errorstate_get();
320  while (cpl_flux_get_noise_ring(im, ring, noise_box_sz, noise_nsamples,
321  bg_noise, NULL) && --ring_tries > 0);
322  if (ring_tries > 0) {
323  cpl_errorstate_set(prestate); /* Recover, if an error happened */
324  } else {
325  return cpl_error_set_where(cpl_func);
326  }
327 
328  *strehl_err = strehl_error_coefficient * (*bg_noise) * pscale *
329  star_radius * star_radius / *star_flux;
330 
331  /* This check should not be able to fail, but just to be sure */
332  cpl_ensure_code(*strehl_err >= 0.0, CPL_ERROR_ILLEGAL_OUTPUT);
333 
334  return CPL_ERROR_NONE;
335 }
336 
337 /*----------------------------------------------------------------------------*/
353 /*----------------------------------------------------------------------------*/
354 double irplib_strehl_disk_flux(const cpl_image * im,
355  double xpos,
356  double ypos,
357  double rad,
358  double bg)
359 {
360  const double sqr = rad * rad;
361  double sqrest;
362  const float * pim;
363  double flux = 0.0;
364  double yj, xi;
365  int nx, ny;
366  int lx, ly, ux, uy;
367  int i, j;
368 
369 
370  /* Check entries */
371  cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
372  cpl_ensure(cpl_image_get_type(im) == CPL_TYPE_FLOAT,
373  CPL_ERROR_UNSUPPORTED_MODE, 0.0);
374  cpl_ensure(rad > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
375 
376  nx = cpl_image_get_size_x(im);
377  ny = cpl_image_get_size_y(im);
378 
379  /* Round down */
380  lx = (int)(xpos - rad);
381  ly = (int)(ypos - rad);
382  if (lx < 0) lx = 0;
383  if (ly < 0) ly = 0;
384 
385  /* Round up */
386  ux = (int)(xpos + rad) + 1;
387  uy = (int)(ypos + rad) + 1;
388  if (ux > (nx-1)) ux = nx-1;
389  if (uy > (ny-1)) uy = ny-1;
390 
391  pim = cpl_image_get_data_float_const(im);
392  for (j=ly ; j<uy ; j++) {
393  yj = (double)j - ypos;
394  sqrest = sqr - yj * yj;
395  for (i=lx; i<ux ; i++) {
396  xi = (double)i - xpos;
397  if (sqrest >= xi * xi && irplib_isnan(pim[i+j*nx]) == 0) {
398  flux += (double)pim[i+j*nx] - bg;
399  }
400  }
401  }
402  return flux;
403 }
404 
405 /*----------------------------------------------------------------------------*/
419 /*----------------------------------------------------------------------------*/
420 double irplib_strehl_ring_background(const cpl_image * im,
421  double xpos,
422  double ypos,
423  double rad_int,
424  double rad_ext,
425  irplib_strehl_bg_method mode)
426 {
427  int npix;
428  const double sqr_int = rad_int * rad_int;
429  const double sqr_ext = rad_ext * rad_ext;
430  double dist;
431  cpl_vector * pix_arr;
432  const float * pim;
433  double flux = 0.0;
434  double yj, xi;
435  int lx, ly, ux, uy;
436  int nx, ny;
437  int i, j;
438 
439  /* Check entries */
440  cpl_ensure(im != NULL, CPL_ERROR_NULL_INPUT, 0.0);
441  cpl_ensure(cpl_image_get_type(im) == CPL_TYPE_FLOAT,
442  CPL_ERROR_UNSUPPORTED_MODE, 0.0);
443  cpl_ensure(rad_int > 0.0, CPL_ERROR_ILLEGAL_INPUT, 0.0);
444  cpl_ensure(rad_ext > rad_int, CPL_ERROR_ILLEGAL_INPUT, 0.0);
445 
446  cpl_ensure(mode == IRPLIB_BG_METHOD_AVER_REJ ||
447  mode == IRPLIB_BG_METHOD_MEDIAN,
448  CPL_ERROR_UNSUPPORTED_MODE, 0.0);
449 
450  nx = cpl_image_get_size_x(im);
451  ny = cpl_image_get_size_y(im);
452 
453  /* Round down */
454  lx = (int)(xpos - rad_ext);
455  ly = (int)(ypos - rad_ext);
456  if (lx < 0) lx = 0;
457  if (ly < 0) ly = 0;
458 
459  /* Round up */
460  ux = (int)(xpos + rad_ext) + 1;
461  uy = (int)(ypos + rad_ext) + 1;
462  if (ux > (nx-1)) ux = nx-1;
463  if (uy > (ny-1)) uy = ny-1;
464 
465  npix = (ux - lx + 1) * (uy - ly + 1);
466  cpl_ensure(npix >= IRPLIB_DISK_BG_MIN_PIX_NB, CPL_ERROR_DATA_NOT_FOUND, 0.0);
467 
468  /* Allocate pixel array to hold values in the ring */
469  pix_arr = cpl_vector_new(npix);
470 
471  /* Count number of pixels in the ring */
472  /* Retrieve all pixels which belong to the ring */
473  pim = cpl_image_get_data_float_const(im);
474  npix = 0;
475  for (j=ly ; j<uy ; j++) {
476  yj = (double)j - ypos;
477  for (i=lx ; i<ux; i++) {
478  xi = (double)i - xpos;
479  dist = yj * yj + xi * xi;
480  if (sqr_int <= dist && dist <= sqr_ext &&
481  irplib_isnan(pim[i+j*nx]) == 0) {
482  cpl_vector_set(pix_arr, npix, (double)pim[i+j*nx]);
483  npix++;
484  }
485  }
486  }
487 
488  if (npix < IRPLIB_DISK_BG_MIN_PIX_NB) {
489  cpl_vector_delete(pix_arr);
490  cpl_ensure(0, CPL_ERROR_DATA_NOT_FOUND, 0.0);
491  }
492 
493  /* Should not be able to fail now */
494 
495  /* Resize pixel array to actual number of values within the ring */
496  cpl_vector_set_size(pix_arr, npix);
497 
498  if (mode == IRPLIB_BG_METHOD_AVER_REJ) {
499  const int low_ind = (int)((double)npix * IRPLIB_DISK_BG_REJ_LOW);
500  const int high_ind = (int)((double)npix
501  * (1.0 - IRPLIB_DISK_BG_REJ_HIGH));
502 
503  /* Sort the array */
504  cpl_vector_sort(pix_arr, 1);
505 
506  for (i=low_ind ; i<high_ind ; i++) {
507  flux += cpl_vector_get(pix_arr, i);
508  }
509  if (high_ind - low_ind > 1) flux /= (double)(high_ind - low_ind);
510  } else /* if (mode == IRPLIB_BG_METHOD_MEDIAN) */ {
511  flux = cpl_vector_get_median(pix_arr);
512  }
513 
514  cpl_vector_delete(pix_arr);
515 
516  return flux;
517 }
518 
519 /*----------------------------------------------------------------------------*/
539 /*----------------------------------------------------------------------------*/
540 cpl_image * irplib_strehl_generate_psf(
541  double m1,
542  double m2,
543  double lam,
544  double dlam,
545  double pscale,
546  int size)
547 {
548  cpl_image * otf_image = irplib_strehl_generate_otf(m1, m2, lam, dlam,
549  size, pscale);
550 
551  if (otf_image == NULL) return NULL;
552 
553  /* Transform back to real space
554  - Normalization is unnecessary, due to the subsequent normalisation.
555  - An OTF is point symmetric about its center, i.e. it is even,
556  i.e. the real space image is real.
557  - Because of this a forward FFT works as well.
558  - If the PSF ever needs to have its images halves swapped add
559  CPL_FFT_SWAP_HALVES to the FFT call.
560  */
561 
562  if (cpl_image_fft(otf_image, NULL, CPL_FFT_UNNORMALIZED) ||
563 
564  /* Compute absolute values of PSF */
565  cpl_image_abs(otf_image) ||
566 
567  /* Normalize PSF to get flux=1 */
568  cpl_image_normalise(otf_image, CPL_NORM_FLUX)) {
569 
570  cpl_image_delete(otf_image);
571  return NULL;
572  }
573 
574  return otf_image;
575 }
576 
579 /*----------------------------------------------------------------------------*/
595 /*----------------------------------------------------------------------------*/
596 static cpl_image * irplib_strehl_generate_otf(
597  double m1,
598  double m2,
599  double lam,
600  double dlam,
601  int size,
602  double pscale)
603 {
604  cpl_image * otf_image;
605  double * otf_data;
606  double obs_ratio ; /* m1 / m2 */
607  double f_max ; /* cut-off frequency */
608  int pix0 ; /* Pixel corresponding to the zero frequency */
609  double a, x, y;
610  double f, rsq, fc, invfc, lambda;
611  double sincy;
612  double invsize;
613  register int pos;
614  int i, j, k;
615 
616 
617  cpl_ensure(m2 > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
618  cpl_ensure(m1 > m2, CPL_ERROR_ILLEGAL_INPUT, NULL);
619  cpl_ensure(lam > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
620  cpl_ensure(dlam > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
621  cpl_ensure(size > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
622  cpl_ensure(pscale > 0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);
623 
624  /* Convert pixel scale from sec to radians, microns in meters */
625  pscale /= (double)206265;
626  lam /= (double)1.0e6;
627  dlam /= (double)1.0e6;
628 
629  /* Obscuration ratio */
630  obs_ratio = m2 / m1;
631 
632  /* Pixel corresponding to the zero frequency */
633  pix0 = size/2;
634  invsize = (double)1.0 / (double)size;
635 
636  /* Cut-off frequency in pixels */
637  f_max = m1 * pscale * (double)size / lam;
638 
639  /* Allocate for output image */
640  otf_image = cpl_image_new(size, size, CPL_TYPE_DOUBLE);
641  if (otf_image==NULL) return NULL;
642  otf_data = cpl_image_get_data_double(otf_image);
643 
644  /* Now compute the OTF */
645  /* OPTIMIZED CODE !!! LIMITED READABILITY !!! */
646 
647  for (k=1 ; k<=9 ; k++) { /* iteration on the wavelength */
648  /* Compute intermediate cut-off frequency */
649  lambda = (double)(lam - dlam*(double)(k-5)/8.0);
650  fc = (double)f_max * (double)lam / lambda;
651  invfc = 1.0 / fc;
652 
653  /* Convolution with the detector pixels */
654  pos = 0;
655  for (j=0 ; j<size ; j++) {
656  y = (double)(j-pix0);
657  sincy = PSF_sinc(CPL_MATH_PI * y * invsize);
658  for (i=0 ; i<size ; i++) {
659  x = (double)(i-pix0);
660  rsq = x*x + y*y;
661  if (rsq < fc*fc) {
662  if (rsq < 0.01)
663  a = 1.0;
664  else {
665  f = sqrt(rsq) * invfc;
666  a = PSF_TelOTF(f,obs_ratio) *
667  PSF_sinc(CPL_MATH_PI * x * invsize) * sincy;
668  }
669  } else {
670  a = 0.0;
671  }
672  otf_data[pos++] += a / 9.0;
673  }
674  }
675  }
676  return otf_image;
677 }
678 
679 /*----------------------------------------------------------------------------*
680  * H1 function
681  *----------------------------------------------------------------------------*/
682 static double PSF_H1(
683  double f,
684  double u,
685  double v)
686 {
687  const double e = fabs(1.0-v) > 0.0 ? -1.0 : 1.0; /* e = 1.0 iff v = 1.0 */
688 
689  return((v*v/CPL_MATH_PI)*acos((f/v)*(1.0+e*(1.0-u*u)/(4.0*f*f))));
690 }
691 
692 /*----------------------------------------------------------------------------*
693  * H2 function
694  *----------------------------------------------------------------------------*/
695 static double PSF_H2(double f,
696  double u)
697 {
698  const double tmp1 = (2.0 * f) / (1.0 + u);
699  const double tmp2 = (1.0 - u) / (2.0 * f);
700 
701  return -1.0 * (f/CPL_MATH_PI) * (1.0+u)
702  * sqrt((1.0-tmp1*tmp1)*(1.0-tmp2*tmp2));
703 }
704 
705 /*----------------------------------------------------------------------------*
706  * G function
707  *----------------------------------------------------------------------------*/
708 static double PSF_G(double f,
709  double u)
710 {
711  if (f <= (1.0-u)/2.0) return(u*u);
712  if (f >= (1.0+u)/2.0) return(0.0);
713  else return(PSF_H1(f,u,1.0) + PSF_H1(f,u,u) + PSF_H2(f,u));
714 }
715 
716 /*----------------------------------------------------------------------------*
717  * sinc function
718  *----------------------------------------------------------------------------*/
719 static double PSF_sinc(double x)
720 {
721  return fabs(x) > fabs(sin(x)) ? sin(x)/x : 1.0;
722 }
723 
724 /*----------------------------------------------------------------------------*
725  * Telescope OTF function
726  *----------------------------------------------------------------------------*/
727 static double PSF_TelOTF(double f,
728  double u)
729 {
730  return((PSF_G(f,1.0)+u*u*PSF_G(f/u,1.0)-2.0*PSF_G(f,u))/(1.0-u*u));
731 }
732 
733 /*----------------------------------------------------------------------------*/
745 /*----------------------------------------------------------------------------*/
746 cpl_error_code irplib_strehl_disk_max(const cpl_image * self,
747  double xpos,
748  double ypos,
749  double radius,
750  double * ppeak)
751 {
752 
753  const double sqr = radius * radius;
754  double sqrest;
755  const float * pself;
756  float peak = FLT_MAX; /* Avoid (false) uninit warning */
757  double yj, xi;
758  int nx, ny;
759  int lx, ly, ux, uy;
760  int i, j;
761  cpl_boolean first = CPL_TRUE;
762 
763 
764  /* Check entries */
765  cpl_ensure_code(ppeak != NULL, CPL_ERROR_NULL_INPUT);
766  cpl_ensure_code(self != NULL, CPL_ERROR_NULL_INPUT);
767  cpl_ensure_code(cpl_image_get_type(self) == CPL_TYPE_FLOAT,
768  CPL_ERROR_UNSUPPORTED_MODE);
769  cpl_ensure_code(radius > 0.0, CPL_ERROR_ILLEGAL_INPUT);
770 
771  nx = cpl_image_get_size_x(self);
772  ny = cpl_image_get_size_y(self);
773 
774  /* Round down */
775  lx = (int)(xpos - radius);
776  ly = (int)(ypos - radius);
777  if (lx < 0) lx = 0;
778  if (ly < 0) ly = 0;
779 
780  /* Round up */
781  ux = (int)(xpos + radius) + 1;
782  uy = (int)(ypos + radius) + 1;
783  if (ux > (nx-1)) ux = nx-1;
784  if (uy > (ny-1)) uy = ny-1;
785 
786  pself = cpl_image_get_data_float_const(self);
787  for (j=ly ; j<uy ; j++) {
788  yj = (double)j - ypos;
789  sqrest = sqr - yj * yj;
790  for (i=lx; i<ux ; i++) {
791  xi = (double)i - xpos;
792  if (sqrest >= xi * xi && irplib_isnan(pself[i+j*nx]) == 0) {
793  if (first || pself[i+j*nx] > peak) {
794  first = CPL_FALSE;
795  peak = pself[i+j*nx];
796  }
797  }
798  }
799  }
800 
801  cpl_ensure_code(!first, CPL_ERROR_DATA_NOT_FOUND);
802 
803  *ppeak = (double)peak;
804 
805  return CPL_ERROR_NONE;
806 }