UVES Pipeline Reference Manual  5.5.5b3
uves_utils_cpl.c
1 /* *
2  * This file is part of the ESO UVES Pipeline *
3  * Copyright (C) 2004,2005 European Southern Observatory *
4  * *
5  * This library is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA *
18  * */
19 
20 /*
21  * $Author: amodigli $
22  * $Date: 2013-02-12 10:56:25 $
23  * $Revision: 1.92 $
24  * $Name: not supported by cvs2svn $
25  *
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 /*----------------------------------------------------------------------------*/
39 /*----------------------------------------------------------------------------*/
40 
43 #include <uves_utils_cpl.h>
44 
45 #include <uves_utils.h>
46 #include <uves_utils_wrappers.h>
47 #include <uves_dump.h>
48 #include <uves_error.h>
49 
50 #include <cpl.h>
51 #include <stdbool.h>
52 #include <string.h>
53 
54 static cpl_image *filter_median(const cpl_image *image, int radx, int rady,
55  bool extrapolate_border);
56 
57 
58 /*----------------------------------------------------------------------------*/
70 /*----------------------------------------------------------------------------*/
71 const cpl_property *
72 uves_find_property_const(const uves_propertylist *plist, const char *name,
73  int number)
74 {
75  int i = 0;
76  int size = uves_propertylist_get_size(plist);
77 
78  assure( number >= 0, CPL_ERROR_ILLEGAL_INPUT, "Number (%d) must be non-negative",
79  number);
80 
81  for (i = 0; i < size; i++)
82  {
83  const cpl_property *p = uves_propertylist_get_const(plist, i);
84 
85  if (strcmp(cpl_property_get_name(p), name) == 0)
86  {
87  if (number == 0)
88  {
89  return p;
90  }
91  else
92  /* Continue search */
93  {
94  number--;
95  }
96  }
97  }
98 
99  cleanup:
100  return NULL;
101 }
102 cpl_property *
103 uves_find_property(uves_propertylist *plist, const char *name,
104  int number)
105 {
106  return (cpl_property *) uves_find_property_const(plist, name, number);
107 }
108 
109 /*----------------------------------------------------------------------------*/
121 /*----------------------------------------------------------------------------*/
122 cpl_error_code
123 uves_filter_image_average(cpl_image *image, int radius_x, int radius_y)
124 {
125  cpl_image *aux = NULL;
126  double *image_data = NULL;
127  double *aux_data = NULL;
128  int nx, ny;
129  int i;
130 
131  /* For bad pixel handling, create a similar auxillary image that counts the bad pixels */
132 
133  assure( image != NULL, CPL_ERROR_NULL_INPUT, "Null image");
134  assure( radius_x >= 0, CPL_ERROR_ILLEGAL_INPUT, "Negative x-radius (%d)", radius_x);
135  assure( radius_y >= 0, CPL_ERROR_ILLEGAL_INPUT, "Negative y-radius (%d)", radius_y);
136  assure( cpl_image_get_type(image) == CPL_TYPE_DOUBLE, CPL_ERROR_TYPE_MISMATCH,
137  "Type is %s. double expected", uves_tostring_cpl_type(cpl_image_get_type(image)));
138 
139  nx = cpl_image_get_size_x(image);
140  ny = cpl_image_get_size_y(image);
141  image_data = cpl_image_get_data_double(image);
142 
143  /* (Disabled:) To avoid problems with overflow (the total flux in the image might
144  be larger than INT_MAX) subtract a constant (the average flux), apply the filter,
145  then add the constant */
146 
147  /* First build auxillary image:
148  *
149  * aux(x,y) = sum_{i=0,x-1} sum_{j=0,y-1} image(i,j)
150  * = sum of rectangle (0,0)-(x-1,y-1)
151  *
152  */
153 
154  aux = cpl_image_new(nx+1, ny+1, CPL_TYPE_DOUBLE); /* Initialized to zero */
155  aux_data = cpl_image_get_data(aux);
156 
157  /* Column x=0 and row y=0 are already zero and need not be calculated,
158  * start from 1. */
159 
160 /* Slow: for (x = 1; x < nx+1; x++)
161  {
162  for (y = 1; y < ny+1; y++)
163  {
164 */
165  for (i = 0; i < (nx+1)*(ny+1); i++)
166  {
167  int x = i % (nx+1);
168  int y = i / (nx+1);
169 
170  if ( x >= 1 && y >= 1)
171  {
172  aux_data[x + y*(nx+1)] = image_data[x-1 + (y-1) * nx]
173  + aux_data [x-1 + y * (nx+1)]
174  + aux_data [x + (y-1)* (nx+1)]
175  - aux_data [x-1 + (y-1)* (nx+1)];
176  }
177 
178  /* Proof of induction step
179  * (assume that formula holds up to (x-1,y) and (x,y-1) and prove formula for (x,y))
180  *
181  * aux(x,y) = image(x-1, y-1) + aux(x-1, y) + aux(x, y-1) - aux(x-1, y-1) (see code)
182  *
183  * = image(x-1, y-1)
184  * + sum_{i=0,x-2}_{j=0,y-1} image(i,j) _
185  * + sum_{i=0,x-1}_{j=0,y-2} image(i,j) \_ sum_{j=0,y-2} image(x-1, j)
186  * - sum_{i=0,x-2}_{j=0,y-2} image(i,j) _/
187  *
188  * = sum_{i=0,x-2}_{j=0,y-1} image(i,j)
189  * + sum_ {j=0,y-1} image(x-1, j)
190  *
191  * = sum_{j=0,y-1} [ ( sum_{i=0,x-2} image(i,j) ) + image(x-1,j) ]
192  * = sum_{j=0,y-1} sum_{i=0,x-1} image(i,j) q.e.d.
193  *
194  * It's simpler when you draw it...
195  */
196  }
197 
198  uves_msg_debug("Finished setting up auxillary image. Get average");
199 
200  /* Then calculate average = (flux in window) / (image size) */
201  for (i = 0; i < nx*ny; i++)
202  {
203  int x = (i % nx);
204  int y = (i / nx);
205 
206  int lower, upper;
207  int left, right;
208 
209  lower = y - radius_y; if (lower < 0) lower = 0;
210  upper = y + radius_y; if (upper >= ny) upper = ny - 1;
211 
212  left = x - radius_x; if (left < 0) left = 0;
213  right = x + radius_x; if (right >= nx) right = nx - 1;
214 
215  image_data[x + y*nx] =
216  (
217  aux_data[(right+1) + (upper+1)*(nx+1)] +
218  aux_data[ left + lower *(nx+1)] -
219  aux_data[ left + (upper+1)*(nx+1)] -
220  aux_data[(right+1) + lower *(nx+1)]
221  )
222  /
223  ( (double) (upper-lower+1) * (right-left+1) );
224  }
225 
226  cleanup:
227  uves_free_image(&aux);
228  return cpl_error_get_code();
229 }
230 
231 
232 /*----------------------------------------------------------------------------*/
246 /*----------------------------------------------------------------------------*/
247 cpl_error_code
248 uves_filter_image_median(cpl_image **image, int xwindow, int ywindow, bool extrapolate_border)
249 {
250  cpl_matrix *id = NULL;
251  cpl_image *temp = NULL;
252 
253  assure( xwindow >= 0 && ywindow >= 0, CPL_ERROR_ILLEGAL_INPUT,
254  "Illegal window radius: %d x %d",
255  (2*xwindow + 1),
256  (2*ywindow + 1));
257 
258  UVES_TIME_START("median filter");
259 
260  if (xwindow <= 1 && ywindow <= 1)
261 /* CPL 3 supports if (xwindow <= 4 && ywindow <= 4) */
262  {
263  check(( id = cpl_matrix_new(2*xwindow+1, 2*ywindow+1),
264  cpl_matrix_fill(id, 1)), "Could not create kernel matrix");
265 
266  /* Image to cpl_image_filter_median must be float or double */
267  if (cpl_image_get_type(*image) == CPL_TYPE_INT)
268  {
269  temp = cpl_image_cast(*image, CPL_TYPE_DOUBLE);
270  uves_free_image(image);
271  }
272  else
273  {
274  temp = cpl_image_duplicate(*image);
275  uves_free_image(image);
276  }
277  check( *image = uves_image_filter_median(temp, id), "Error applying median filter");
278  uves_free_image(&temp);
279 
280  /* fixme: the CPL function marks border pixels as bad. Do something
281  depending on the extrapolate_border flag */
282  }
283  else
284  {
285  temp = *image;
286  check( *image = filter_median(temp, xwindow, ywindow, extrapolate_border),
287  "Error applying median filter");
288  uves_free_image(&temp);
289  }
290 
291  UVES_TIME_END;
292 
293  cleanup:
294  uves_free_matrix(&id);
295  uves_free_image(&temp);
296  return cpl_error_get_code();
297 }
298 
300 #define DOUBLE_SWAP(a,b) { register double t=(a);(a)=(b);(b)=t; }
301 
313  double * a,
314  int n,
315  int k)
316 {
317  register double x ;
318  register int i, j, l, m ;
319 
320  l=0 ; m=n-1 ;
321  while (l<m) {
322  x=a[k] ;
323  i=l ;
324  j=m ;
325  do {
326  while (a[i]<x) i++ ;
327  while (x<a[j]) j-- ;
328  if (i<=j) {
329  DOUBLE_SWAP(a[i],a[j]) ;
330  i++ ; j-- ;
331  }
332  } while (i<=j) ;
333  if (j<k) l=i ;
334  if (k<i) m=j ;
335  }
336  return a[k] ;
337 }
338 
347 double
348 uves_tools_get_median(double *a, int n)
349 {
350  if (n % 2 == 0)
351  {
352  return
353  (uves_utils_get_kth_double(a, n, n/2) +
354  uves_utils_get_kth_double(a, n, n/2-1))/2.0;
355 
356  }
357  else
358  {
359  return uves_utils_get_kth_double(a, n, (n-1)/2);
360  }
361 }
362 
363 
364 
365 /*----------------------------------------------------------------------------*/
387 /*----------------------------------------------------------------------------*/
388 static cpl_image *
389 filter_median(const cpl_image *image, int radx, int rady, bool extrapolate_border)
390 {
391  int x, y;
392  int nx = cpl_image_get_size_x(image);
393  int ny = cpl_image_get_size_y(image);
394  cpl_image *result = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE);
395  double *result_data;
396  const double *image_data;
397  double *window = NULL;
398 
399  window = cpl_malloc(sizeof(double) * (2*radx+1)*(2*rady+1));
400  assure_mem( result );
401  assure( cpl_image_get_type(image) == CPL_TYPE_DOUBLE,
402  CPL_ERROR_UNSUPPORTED_MODE, "Type is %s",
403  uves_tostring_cpl_type(cpl_image_get_type(image)));
404 
405  result_data = cpl_image_get_data_double(result);
406  image_data = cpl_image_get_data_double_const(image);
407 
408  for (y = 1; y <= ny; y++)
409  {
410  for (x = 1; x <= nx; x++)
411  {
412  int x1, y_1, x2, y2;
413 
414  x1 = x - radx; y_1 = y - rady;
415  x2 = x + radx; y2 = y + rady;
416 
417  if (extrapolate_border)
418  {
419  /* At edge of image, move median box, so
420  that entire box is inside of image */
421  if (x1 < 1)
422  {
423  x2 += (1 - x1);
424  x1 += (1 - x1);
425  }
426  if (nx < x2)
427  {
428  x1 -= (x2 - nx);
429  x2 -= (x2 - nx);
430  }
431 
432  if (y_1 < 1)
433  {
434  y2 += (1 - y_1);
435  y_1 += (1 - y_1);
436  }
437  if (ny < y2)
438  {
439  y_1 -= (y2 - ny);
440  y2 -= (y2 - ny);
441  }
442  }
443  else { /* Rely on the use of min/max below */ }
444 
445 #if 0
446  result_data[(x-1) + (y-1)*nx] =
447  cpl_image_get_median_window(image,
448  uves_max_int(1, x1),
449  uves_max_int(1, y_1),
450  uves_min_int(nx, x2),
451  uves_min_int(ny, y2));
452 
453 #else
454  /* This saves a few (~10-20) percent execution time */
455  {
456  int i, j, k;
457 
458  k = 0;
459  for (j = uves_max_int(1 , y_1)-1;
460  j <= uves_min_int(ny, y2 )-1;
461  j++)
462  for (i = uves_max_int(1, x1)-1;
463  i <= uves_min_int(nx, x2)-1;
464  i++)
465  {
466  window[k++] = image_data[i + j*nx];
467  }
468 
469  result_data[(x-1) + (y-1)*nx] =
470  uves_utils_get_kth_double(window,k,(((k)&1)?((k)/2):(((k)/2)-1))) ;
471  }
472 #endif
473  }
474  }
475 
476 
477  assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
478  "Error calculating %dx%d median filter", radx, rady);
479 
480  cleanup:
481  cpl_free(window);
482  return result;
483 }
484 
485 
486 /*----------------------------------------------------------------------------*/
514 /*----------------------------------------------------------------------------*/
515 
516 cpl_error_code
517 uves_fit_gaussian_2d_image(const cpl_image *image, const cpl_image *noise,
518  int x1, int y_1,
519  int x2, int y2,
520  double *x0, double *y_0, double *sigmax, double *sigmay,
521  double *amplitude,
522  double *dx0, double *dy0
523  )
524 {
525  cpl_image *marginal_x = NULL;
526  cpl_image *marginal_y = NULL;
527  cpl_image *marginal_x_noise = NULL;
528  cpl_image *marginal_y_noise = NULL;
529  cpl_image *variance = NULL;
530  cpl_matrix *covariance = NULL;
531 
532  int nx, ny;
533  double norm_x, norm_y;
534  double background_x, background_y;
535 
536  /* Check input */
537  assure( image != NULL, CPL_ERROR_NULL_INPUT, "Null image");
538  nx = cpl_image_get_size_x(image);
539  ny = cpl_image_get_size_y(image);
540  assure( noise != NULL || (dx0 == NULL && dy0 == NULL), CPL_ERROR_INCOMPATIBLE_INPUT,
541  "Cannot compute uncertainty of fit with no noise image specified");
542  assure( noise == NULL ||
543  (cpl_image_get_size_x(noise) == nx &&
544  cpl_image_get_size_y(noise) == ny),
545  CPL_ERROR_INCOMPATIBLE_INPUT,
546  "Size of input image (%dx%d) and noise image (%" CPL_SIZE_FORMAT "x%" CPL_SIZE_FORMAT ") differ",
547  nx, ny,
548  cpl_image_get_size_x(noise),
549  cpl_image_get_size_y(noise));
550  assure( 1 <= x1 && x1 <= x2 && x2 <= nx &&
551  1 <= y_1 && y_1 <= y2 && y2 <= ny, CPL_ERROR_ILLEGAL_INPUT,
552  "Illegal window: (%d, %d)-(%d, %d)", x1, y_1, x2, y2);
553  assure( x0 != NULL, CPL_ERROR_NULL_INPUT, "Null x-center");
554  assure( y_0 != NULL, CPL_ERROR_NULL_INPUT, "Null y-center");
555  assure( sigmax != NULL, CPL_ERROR_NULL_INPUT, "Null sigma_x");
556  assure( sigmay != NULL, CPL_ERROR_NULL_INPUT, "Null sigma_y");
557  /* amplitude, dx0, dy0 may be NULL */
558 
559  if (noise != NULL)
560  {
561  /* Variance = noise^2 */
562  check(( variance = cpl_image_extract(noise, x1, y_1, x2, y2),
563  cpl_image_power(variance, 2.0)),
564  "Error creating variance image");
565  }
566 
567  /* Collapse along columns (result is horizontal) */
568  check( marginal_x = cpl_image_collapse_window_create(image,
569  x1, y_1, x2, y2,
570  0), /* Sum of columns */
571  "Error collapsing window (%d, %d) - (%d, %d)", x1, y_1, x2, y2);
572 
573  if (noise != NULL)
574  {
575  /* Sigma of sum = sqrt [ sum sigma_i^2 ] */
576 
577  check( marginal_x_noise = cpl_image_collapse_window_create(variance,
578  1, 1,
579  x2-x1+1, y2-y_1+1,
580  0), /* Sum of columns */
581  "Error collapsing window (1, 1) - (%d, %d)", x2-x1+1, y2-y_1+1);
582 
583  /* Sqrt */
584  cpl_image_power(marginal_x_noise, 0.5);
585  }
586 
587  /* Collapse along rows (result is vertical) */
588  check( marginal_y = cpl_image_collapse_window_create(image,
589  x1, y_1, x2, y2,
590  1), /* Sum of rows */
591  "Error collapsing window (%d, %d) - (%d, %d)", x1, y_1, x2, y2);
592 
593  if (noise != NULL)
594  {
595  check( marginal_y_noise = cpl_image_collapse_window_create(variance,
596  1, 1,
597  x2-x1+1, y2-y_1+1,
598  1), /* Sum of rows */
599  "Error collapsing window (1, 1) - (%d, %d)", x2-x1+1, y2-y_1+1);
600 
601  /* Sqrt */
602  cpl_image_power(marginal_y_noise, 0.5);
603  }
604 
605  /* Fit x-distribution */
606  uves_fit_1d_image(marginal_x, marginal_x_noise, NULL,
607  true, /* Horizontal ? */
608  false, false, /* Fix/fit background ? */
609  1, x2 - x1 + 1, 1, /* xlo, xhi, y */
610  x0, sigmax, &norm_x, &background_x, NULL,
611  NULL, NULL, /* mse, red. chi^2 */
612  (dx0 != NULL) ? &covariance : NULL,
614 
615  /* Set code 'CPL_ERROR_CONTINUE' if fitting failed, check for unexpected errors */
616  assure( cpl_error_get_code() != CPL_ERROR_CONTINUE ||
617  cpl_error_get_code() != CPL_ERROR_SINGULAR_MATRIX,
618  CPL_ERROR_CONTINUE, "Fitting along x failed");
619  assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
620  "Fitting along x failed");
621 
622  /* Map to world-coordinates */
623  *x0 += (x1 - 1);
624 
625  if (dx0 != NULL)
626  {
627  *dx0 = cpl_matrix_get(covariance, 0, 0);
628  }
629 
630 
631  /* Fit y-distribution */
632  uves_free_matrix(&covariance);
633  uves_fit_1d_image(marginal_y, marginal_y_noise, NULL,
634  false, /* Horizontal ? */
635  false, false, /* Fix/fit background ? */
636  1, y2 - y_1 + 1, 1, /* ylo, yhi, x */
637  y_0, sigmay, &norm_y, &background_y, NULL,
638  NULL, NULL, /* mse, red. chi^2 */
639  (dy0 != NULL) ? &covariance : NULL,
641 
642  /* Set code 'CPL_ERROR_CONTINUE' if fitting failed, check for unexpected errors */
643  assure( cpl_error_get_code() != CPL_ERROR_CONTINUE ||
644  cpl_error_get_code() != CPL_ERROR_SINGULAR_MATRIX,
645  CPL_ERROR_CONTINUE, "Fitting along y failed");
646  assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
647  "Fitting along y failed");
648 
649  /* Map to world-coordinates */
650  *y_0 += (y_1 - 1);
651 
652  if (dy0 != NULL)
653  {
654  *dy0 = cpl_matrix_get(covariance, 0, 0);
655  }
656 
657  /* Set amplitude = N / [ sqrt(2pi sigmax^2) sqrt(2pi sigmay^2) ].
658  *
659  * The fitted norm (area), N, is the same (up to numerical errors) in both directions,
660  * so use geometric average as an estimate of N.
661  */
662  if (amplitude != NULL)
663  {
664  *amplitude = sqrt(norm_x * norm_y) / (2*M_PI * (*sigmax) * (*sigmay));
665  }
666 
667  cleanup:
668  uves_free_matrix(&covariance);
669  uves_free_image(&variance);
670  uves_free_image(&marginal_x);
671  uves_free_image(&marginal_x_noise);
672  uves_free_image(&marginal_y);
673  uves_free_image(&marginal_y_noise);
674 
675  return cpl_error_get_code();
676 }
677 
678 
const cpl_property * uves_propertylist_get_const(const uves_propertylist *self, long position)
Access property list elements by index.
cpl_error_code uves_filter_image_median(cpl_image **image, int xwindow, int ywindow, bool extrapolate_border)
Median filter.
cpl_error_code uves_fit_gaussian_2d_image(const cpl_image *image, const cpl_image *noise, int x1, int y_1, int x2, int y2, double *x0, double *y_0, double *sigmax, double *sigmay, double *amplitude, double *dx0, double *dy0)
Fit a 2d gaussian to an image sub-window.
cpl_error_code uves_filter_image_average(cpl_image *image, int radius_x, int radius_y)
Average filter.
int uves_gauss_derivative(const double x[], const double a[], double result[])
Evaluate the derivatives of a gaussian.
Definition: uves_utils.c:4347
static cpl_image * filter_median(const cpl_image *image, int radx, int rady, bool extrapolate_border)
Median filter.
long uves_propertylist_get_size(const uves_propertylist *self)
Get the current size of a property list.
int uves_gauss(const double x[], const double a[], double *result)
Evaluate a gaussian.
Definition: uves_utils.c:4292
#define assure_mem(PTR)
Definition: uves_error.h:181
double uves_tools_get_median(double *a, int n)
returns median (not CPL median) of an array
const char * uves_tostring_cpl_type(cpl_type t)
Convert a CPL type to a string.
Definition: uves_dump.c:378
#define uves_msg_debug(...)
Print a debug message.
Definition: uves_msg.h:97
const cpl_property * uves_find_property_const(const uves_propertylist *plist, const char *name, int number)
Find named property.
#define check(CMD,...)
Definition: uves_error.h:198
double uves_utils_get_kth_double(double *a, int n, int k)
returns the kth value of an array