UVES Pipeline Reference Manual  5.4.6
uves_backsub.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: 2012-03-02 16:23:31 $
23  * $Revision: 1.53 $
24  * $Name: not supported by cvs2svn $
25  * $Log: not supported by cvs2svn $
26  * Revision 1.52 2011/12/08 13:58:44 amodigli
27  * Fox warnings with CPL6
28  *
29  * Revision 1.51 2010/09/24 09:32:02 amodigli
30  * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
31  *
32  * Revision 1.49 2010/03/08 13:58:54 amodigli
33  * now background image has value as computed-no positivity check
34  *
35  * Revision 1.48 2010/01/04 14:01:53 amodigli
36  * less verbose bkg subtraction
37  *
38  * Revision 1.47 2008/09/29 06:55:06 amodigli
39  * add #include <string.h>
40  *
41  * Revision 1.46 2008/09/17 14:50:58 amodigli
42  * use cpl_table_erase_selected in place of uves_table_erase_selected_dfs02356
43  *
44  * Revision 1.45 2007/11/20 16:12:51 amodigli
45  * replaced round by uves_round_double
46  *
47  * Revision 1.44 2007/10/17 14:36:59 amodigli
48  * resale radius_y by frame bin size
49  *
50  * Revision 1.43 2007/08/21 13:08:26 jmlarsen
51  * Removed irplib_access module, largely deprecated by CPL-4
52  *
53  * Revision 1.42 2007/06/06 08:17:33 amodigli
54  * replace tab with 4 spaces
55  *
56  * Revision 1.41 2007/05/22 11:29:39 jmlarsen
57  * Removed MIDAS flag for good
58  *
59  * Revision 1.40 2007/04/24 12:50:29 jmlarsen
60  * Replaced cpl_propertylist -> uves_propertylist which is much faster
61  *
62  * Revision 1.39 2007/04/10 07:06:17 jmlarsen
63  * Changed interface of polynomial_regression_2d()
64  *
65  * Revision 1.38 2007/03/28 11:38:21 jmlarsen
66  * Killed MIDAS flag, removed dead code
67  *
68  * Revision 1.37 2007/02/12 10:04:24 jmlarsen
69  * Added debugging statements
70  *
71  * Revision 1.36 2007/02/09 08:50:58 jmlarsen
72  * Use define's rather than hard-coded recipe names
73  *
74  * Revision 1.35 2007/01/15 08:46:48 jmlarsen
75  * Shortened lines
76  *
77  * Revision 1.34 2006/11/15 15:02:14 jmlarsen
78  * Implemented const safe workarounds for CPL functions
79  *
80  * Revision 1.32 2006/11/15 14:04:08 jmlarsen
81  * Removed non-const version of parameterlist_get_first/last/next which is
82  * already in CPL, added const-safe wrapper, unwrapper and deallocator functions
83  *
84  * Revision 1.31 2006/11/06 15:19:41 jmlarsen
85  * Removed unused include directives
86  *
87  * Revision 1.30 2006/09/20 12:53:57 jmlarsen
88  * Replaced stringcat functions with uves_sprintf()
89  *
90  * Revision 1.29 2006/09/11 08:20:44 jmlarsen
91  * Renamed identifier reserved by POSIX
92  *
93  * Revision 1.28 2006/08/23 09:33:03 jmlarsen
94  * Renamed local variables shadowing POSIX reserved names
95  *
96  * Revision 1.27 2006/08/17 13:56:52 jmlarsen
97  * Reduced max line length
98  *
99  * Revision 1.26 2006/08/11 14:36:11 jmlarsen
100  * Implemented workaround for slow cpl_table_erase_selected
101  *
102  * Revision 1.25 2006/08/07 11:35:35 jmlarsen
103  * Disabled parameter environment variable mode
104  *
105  * Revision 1.24 2006/07/14 12:18:33 jmlarsen
106  * Disable compiler warning
107  *
108  * Revision 1.23 2006/07/03 12:57:50 jmlarsen
109  * Threshold background image to positive
110  *
111  * Revision 1.22 2006/06/13 11:54:24 jmlarsen
112  * Don't threshold to zero
113  *
114  * Revision 1.21 2006/06/01 13:04:11 jmlarsen
115  * Moved doxygen marker to exclude documentation of #define's
116  *
117  * Revision 1.20 2006/04/06 08:29:06 jmlarsen
118  * Minor doc change
119  *
120  * Revision 1.19 2006/03/24 13:54:27 jmlarsen
121  * Use different smoothing default values depending on type of frame (flat or science)
122  *
123  * Revision 1.18 2006/03/09 10:51:14 jmlarsen
124  * Changed order of for loops
125  *
126  * Revision 1.17 2006/03/03 13:54:11 jmlarsen
127  * Changed syntax of check macro
128  *
129  * Revision 1.16 2006/02/28 09:15:22 jmlarsen
130  * Minor update
131  *
132  * Revision 1.15 2006/02/17 10:12:32 jmlarsen
133  * Removed mixed code-declarations
134  *
135  * Revision 1.14 2006/02/15 13:19:15 jmlarsen
136  * Reduced source code max. line length
137  *
138  * Revision 1.13 2005/12/19 16:17:55 jmlarsen
139  * Replaced bool -> int
140  *
141  */
142 
143 #ifdef HAVE_CONFIG_H
144 # include <config.h>
145 #endif
146 
147 /*----------------------------------------------------------------------------*/
154 /*----------------------------------------------------------------------------*/
155 
156 
157 #include <uves_backsub.h>
158 
159 #include <uves_parameters.h>
160 #include <uves_pfits.h>
161 #include <uves_dump.h>
162 #include <uves_utils.h>
163 #include <uves_utils_wrappers.h>
164 #include <uves_utils_cpl.h>
165 #include <uves_error.h>
166 #include <uves_msg.h>
167 #include <uves.h>
168 
169 #include <cpl.h>
170 #include <string.h>
171 #include <stdbool.h>
172 #include <float.h>
173 /*-----------------------------------------------------------------------------
174  Functions prototypes
175  -----------------------------------------------------------------------------*/
176 static int first_order(const polynomial *order_locations, int nx);
177 static int last_order (const polynomial *order_locations, int nx, int ny);
178 static cpl_error_code lower_to_average(cpl_image *image, int RADX, int RADY);
179 static double sample_background(const cpl_image *image, int x0, double y_0,
180  int radius_x, int radius_y, int nx, int ny,
181  background_measure_method BM_METHOD);
182 static cpl_error_code subtract_background(cpl_image *image, cpl_image *background_im,
183  const polynomial *background_pol);
184 
185 /*-----------------------------------------------------------------------------
186  Defines
187  -----------------------------------------------------------------------------*/
188 
189 /* This is sort of ugly, because we fine tune parameters depending on
190  wavelength and also different for masterflat/science exposures.
191  A 'perfect' background subtraction algorithm should not need to
192  know about its context.
193 */
194 
195 #define BACKSUB_FLAT_SMOOTHX_BLUE (25.0/4096)
196 #define BACKSUB_FLAT_SMOOTHX_RED (50.0/4096)
197 #define BACKSUB_FLAT_SMOOTHY_BLUE (100.0/2048)
198 #define BACKSUB_FLAT_SMOOTHY_RED (300.0/2048)
199 
200 #define BACKSUB_SCI_SMOOTHX_BLUE (300.0/4096)
201 #define BACKSUB_SCI_SMOOTHX_RED (300.0/4096)
202 #define BACKSUB_SCI_SMOOTHY_BLUE (200.0/2048)
203 #define BACKSUB_SCI_SMOOTHY_RED (500.0/2048)
204 
205 #define BACKSUB_SMOOTHY_WLEN 859.9
206 
209 /*-----------------------------------------------------------------------------
210  Implementation
211  -----------------------------------------------------------------------------*/
212 
213 /*----------------------------------------------------------------------------*/
221 /*----------------------------------------------------------------------------*/
222 
223 cpl_parameterlist *
224 uves_backsub_define_parameters(void)
225 {
226  const char *name = "";
227  char *full_name = NULL;
228  cpl_parameterlist *parameters = NULL;
229  cpl_parameter *p = NULL;
230 
231  parameters = cpl_parameterlist_new();
232 
233  //
234  name = "mmethod";
235  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
236 
237  uves_parameter_new_enum(p, full_name,
238  CPL_TYPE_STRING,
239  "Background measuring method. If equal to 'median' "
240  "the background is sampled using the median of a subwindow. "
241  "If 'minimum', the subwindow minimum value is used. "
242  "If 'no', no background subtraction is done.",
243  UVES_BACKSUB_ID,
244  "median", /* Default */
245  3, /* Number of options */
246  "median", "minimum", "no"); /* List of options */
247  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
248  cpl_parameterlist_append(parameters, p);
249  cpl_free(full_name);
250 
251  //
252  name = "npoints";
253  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
254  uves_parameter_new_range(p, full_name,
255  CPL_TYPE_INT,
256  "This is the number of columns in interorder space "
257  "used to sample the background.",
258  UVES_BACKSUB_ID,
259  82, 0, INT_MAX);
260  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
261  cpl_parameterlist_append(parameters, p);
262  cpl_free(full_name);
263 
264  //
265  name = "radiusy";
266  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
267  uves_parameter_new_range(p, full_name,
268  CPL_TYPE_INT,
269  "The height (in pixels) of the background sampling "
270  "window is (2*radiusy + 1). "
271  "This parameter is not corrected for binning.",
272  UVES_BACKSUB_ID,
273  2, 0, INT_MAX);
274  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
275  cpl_parameterlist_append(parameters, p);
276  cpl_free(full_name);
277 
278  //
279  name = "sdegree";
280  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
281  uves_parameter_new_range(p, full_name,
282  CPL_TYPE_INT,
283  "Degree of interpolating splines. Currently "
284  "only degree = 1 is supported",
285  UVES_BACKSUB_ID,
286  1, 0, INT_MAX);
287  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
288  cpl_parameterlist_append(parameters, p);
289  cpl_free(full_name);
290 
291  //
292  name = "smoothx";
293  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
294  uves_parameter_new_range(p, full_name,
295  CPL_TYPE_DOUBLE,
296  "If spline interpolation is used to measure the background, "
297  "the x-radius of the post-smoothing window is "
298  "(smoothx * image_width). Here, 'image_width' is the image "
299  "width after binning. If negative, the default values are used: "
300  make_str(BACKSUB_FLAT_SMOOTHX_BLUE) " for blue flat-field frames, "
301  make_str(BACKSUB_FLAT_SMOOTHX_RED) " for red flat-field frames, "
302  make_str(BACKSUB_SCI_SMOOTHX_BLUE) " for blue science frames and "
303  make_str(BACKSUB_SCI_SMOOTHX_RED) " for red science frames.",
304  UVES_BACKSUB_ID,
305  -1.0, -DBL_MAX, DBL_MAX);
306  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
307  cpl_parameterlist_append(parameters, p);
308  cpl_free(full_name);
309 
310  //
311  name = "smoothy";
312  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
313  uves_parameter_new_range(p, full_name,
314  CPL_TYPE_DOUBLE,
315  "If spline interpolation is used to measure the "
316  "background, the y-radius of the post-smoothing "
317  "window is (smoothy * image_height). Here, "
318  "'image_height' is the image height after binning. "
319  "If negative, the default values are used: "
320  make_str(BACKSUB_FLAT_SMOOTHY_BLUE) " for blue flat-field frames, "
321  make_str(BACKSUB_FLAT_SMOOTHY_RED) " for red flat-field frames, "
322  make_str(BACKSUB_SCI_SMOOTHY_BLUE) " for blue science frames and "
323  make_str(BACKSUB_SCI_SMOOTHY_RED) " for red science frames.",
324  UVES_BACKSUB_ID,
325  -1.0, -DBL_MAX, DBL_MAX);
326  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
327  cpl_parameterlist_append(parameters, p);
328  cpl_free(full_name);
329 
330  if (cpl_error_get_code() != CPL_ERROR_NONE)
331  {
332  cpl_msg_error(__func__, "Creation of spline background subtraction "
333  "parameters failed: '%s'", cpl_error_get_where());
334  cpl_parameterlist_delete(parameters);
335  return NULL;
336  }
337  else
338  {
339  return parameters;
340  }
341 }
342 
343 /*----------------------------------------------------------------------------*/
353 /*----------------------------------------------------------------------------*/
354 background_measure_method
355 uves_get_bm_method(const cpl_parameterlist *parameters, const char *context,
356  const char *subcontext)
357 {
358  const char *bm = "";
359  background_measure_method result = 0;
360 
361  check( uves_get_parameter(parameters, context, subcontext, "mmethod", CPL_TYPE_STRING, &bm),
362  "Could not read parameter");
363 
364  if (strcmp(bm, "median" ) == 0) result = BM_MEDIAN;
365  else if (strcmp(bm, "minimum") == 0) result = BM_MINIMUM;
366  else if (strcmp(bm, "no" ) == 0) result = BM_NO;
367  else
368  {
369  /* Impossible */ assure(false, CPL_ERROR_ILLEGAL_INPUT,
370  "No such background measuring method: '%s'", bm);
371  }
372 
373  cleanup:
374  return result;
375 }
376 
377 /*----------------------------------------------------------------------------*/
411 /*----------------------------------------------------------------------------*/
412 
413 cpl_error_code
414 uves_backsub_spline(cpl_image *image, const uves_propertylist *raw_header,
415  const cpl_table *ordertable, const polynomial *order_locations,
416  const cpl_parameterlist *parameters, const char *context,
417  enum uves_chip chip,
418  bool flat_field,
419  cpl_image **background)
420 {
421  /* Recipe parameters */
422  background_measure_method BM_METHOD;
423  int npoints;
424  int radius_y;
425  int bin_x=1;
426  int bin_y=1;
427 
428  int sdegree;
429  double SMOOTHX;
430  double SMOOTHY;
431 
432  /* Local variables */
433  int nx, ny;
434  int x, y;
435  int stepx;
436  int radius_x;
437  int smooth_x, smooth_y; /* Window radius in pixels */
438 
439  passure( image != NULL, " ");
440  passure( raw_header != NULL, " ");
441  passure( ordertable != NULL, " ");
442  passure( order_locations != NULL, " ");
443  passure( parameters != NULL, " ");
444  passure( context != NULL, " ");
445  passure( uves_polynomial_get_dimension(order_locations) == 2,
446  "%d", uves_polynomial_get_dimension(order_locations));
447  passure( background != NULL, " ");
448 
449  /* Get recipe parameters */
450  check( BM_METHOD = uves_get_bm_method(parameters, context, UVES_BACKSUB_ID),
451  "Error getting background measuring method");
452 
453  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
454  "npoints", CPL_TYPE_INT , &npoints) , "Could not read parameter");
455  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
456  "radiusy", CPL_TYPE_INT , &radius_y), "Could not read parameter");
457 
458  check(bin_x=uves_pfits_get_binx(raw_header),"error getting %s",UVES_BINX);
459  check(bin_y=uves_pfits_get_biny(raw_header),"error getting %s",UVES_BINY);
460 
461  radius_y = uves_round_double((double)radius_y/bin_y);
462 
463  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
464  "sdegree", CPL_TYPE_INT , &sdegree) , "Could not read parameter");
465  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
466  "smoothx", CPL_TYPE_DOUBLE, &SMOOTHX) , "Could not read parameter");
467  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
468  "smoothy", CPL_TYPE_DOUBLE, &SMOOTHY) , "Could not read parameter");
469 
470 
471  /* Get other parameters */
472  nx = cpl_image_get_size_x(image);
473  ny = cpl_image_get_size_y(image);
474 
475 
476  if (BM_METHOD == BM_NO)
477  {
478  uves_msg("Skipping background subtraction");
479 
480  /* Calculate a zero-background */
481  check( *background = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE),
482  "Error allocating image");
483  }
484  else {
485  /* If negative, set default values for smoothx, smoothy */
486  if (SMOOTHX < 0)
487  {
488  if (chip == UVES_CHIP_BLUE)
489  {
490  SMOOTHX = (flat_field) ?
491  BACKSUB_FLAT_SMOOTHX_BLUE : BACKSUB_SCI_SMOOTHX_BLUE;
492  }
493  else
494  {
495  SMOOTHX = (flat_field) ?
496  BACKSUB_FLAT_SMOOTHX_RED : BACKSUB_SCI_SMOOTHX_RED;
497  }
498  }
499  if (SMOOTHY < 0)
500  {
501  double wlen;
502 
503  /* Read wavelength from raw header */
504 
505  check( wlen = uves_pfits_get_gratwlen(raw_header, chip),
506  "Error reading central wavelength");
507 
508  /* The criterion is not if the chip is BLUE/RED,
509  but whether the wlen is < 860A */
510  if (wlen < BACKSUB_SMOOTHY_WLEN)
511  {
512  SMOOTHY = (flat_field) ?
513  BACKSUB_FLAT_SMOOTHY_BLUE : BACKSUB_SCI_SMOOTHY_BLUE;
514  }
515  else
516  {
517  SMOOTHY = (flat_field) ?
518  BACKSUB_FLAT_SMOOTHY_RED : BACKSUB_SCI_SMOOTHY_RED;
519  }
520  }
521 
522  assure( 0 < SMOOTHX, CPL_ERROR_ILLEGAL_INPUT, "Illegal smoothx factor: %e", SMOOTHX);
523  assure( 0 < SMOOTHY, CPL_ERROR_ILLEGAL_INPUT, "Illegal smoothy factor: %e", SMOOTHY);
524 
525  smooth_x = uves_round_double(SMOOTHX * nx - 0.5);
526  smooth_y = uves_round_double(SMOOTHY * ny - 0.5);
527 
528  assure( 0 < npoints, CPL_ERROR_ILLEGAL_INPUT,
529  "Illegal number of sample points: %d", npoints);
530  stepx = nx / npoints;
531  assure( 0 < stepx, CPL_ERROR_ILLEGAL_INPUT, "Illegal step size: %d", stepx);
532  radius_x = stepx/2;
533  assure( 0 < radius_x, CPL_ERROR_ILLEGAL_INPUT, "Illegal x sample radius: %d", radius_x);
534  assure( 0 < radius_y, CPL_ERROR_ILLEGAL_INPUT, "Illegal y sample radius: %d", radius_y);
535  assure( 0 < smooth_x, CPL_ERROR_ILLEGAL_INPUT, "Illegal x sample smooth: %d", smooth_x);
536  assure( 0 < smooth_y, CPL_ERROR_ILLEGAL_INPUT, "Illegal y sample smooth: %d", smooth_y);
537  assure( sdegree == 1, CPL_ERROR_UNSUPPORTED_MODE,
538  "Spline degree must be 1. It is %d", sdegree);
539 
540  uves_msg("Sample window (pixels): radx, rady = %d, %d", radius_x, radius_y);
541 
542  check( *background = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE),
543  "Error allocating background image");
544 
545  /* Process */
546 
547  for (x = stepx; x <= nx; x += stepx) {
548  int order, minorder, maxorder;
549  /* Find min. and max. order where background positions are inside image */
550 
551  minorder = cpl_table_get_column_min(ordertable, "Order");
552 
553  /* If outside image, move to inside image */
554  while (uves_round_double(
555  uves_polynomial_evaluate_2d(order_locations, x + radius_x, minorder - 0.5)
556  ) - radius_y < 1 ||
557  uves_round_double(
558  uves_polynomial_evaluate_2d(order_locations, x - radius_x, minorder - 0.5))
559  - radius_y < 1 )
560  {
561  int sign;
562 
563  for (sign = -1; sign <= 1; sign += 2)
564  {
565  assure(
566  uves_polynomial_evaluate_2d(order_locations,
567  x + sign*radius_x, minorder+1 - 0.5) >
568  uves_polynomial_evaluate_2d(order_locations,
569  x + sign*radius_x, minorder - 0.5),
570  CPL_ERROR_ILLEGAL_INPUT,
571  "Order polynomial is not well-formed: "
572  "p(%d, %f) = %e; p(%d, %f) = %e",
573  x + sign*radius_x, minorder+1 - 0.5, uves_polynomial_evaluate_2d(
574  order_locations, x + sign*radius_x, minorder+1 - 0.5
575  ),
576  x + sign*radius_x, minorder - 0.5, uves_polynomial_evaluate_2d(
577  order_locations, x + sign*radius_x, minorder - 0.5)
578  );
579  }
580 
581  minorder += 1;
582  }
583 
584  maxorder = cpl_table_get_column_max(ordertable, "Order");
585 
586  /* If outside image, move to inside image */
587  while (uves_round_double(
588  uves_polynomial_evaluate_2d(order_locations, x + radius_x, maxorder + 0.5)
589  ) + radius_y > ny ||
590  uves_round_double(
591  uves_polynomial_evaluate_2d(order_locations, x - radius_x, maxorder + 0.5)
592  ) + radius_y > ny ) {
593  int sign;
594  for (sign = -1; sign <= 1; sign += 2)
595  {
596  assure(
598  order_locations, x + sign*radius_x, maxorder-1 - 0.5) <
599  uves_polynomial_evaluate_2d(order_locations,
600  x + sign*radius_x, maxorder - 0.5),
601  CPL_ERROR_ILLEGAL_INPUT,
602  "Order polynomial is not well-formed: "
603  "p(%d, %f) = %e; p(%d, %f) = %e",
604  x + sign*radius_x, maxorder-1 - 0.5, uves_polynomial_evaluate_2d(
605  order_locations, x + sign*radius_x, maxorder-1 - 0.5),
606  x + sign*radius_x, maxorder - 0.5, uves_polynomial_evaluate_2d(
607  order_locations, x + sign*radius_x, maxorder - 0.5)
608  );
609  }
610 
611  maxorder -= 1;
612  }
613 
614  /* Move to min. order inside image */
615  while (uves_round_double(uves_polynomial_evaluate_2d(
616  order_locations, x + radius_x, minorder - 1.5)
617  ) - radius_y >= 1 &&
618  uves_round_double(uves_polynomial_evaluate_2d(
619  order_locations, x - radius_x, minorder - 1.5)
620  ) - radius_y >= 1 )
621  {
622  int sign;
623  for (sign = -1; sign <= 1; sign += 2)
624  {
625  assure(
627  order_locations, x + sign*radius_x, minorder-1 - 1.5) <
629  order_locations, x + sign*radius_x, minorder - 1.5),
630  CPL_ERROR_ILLEGAL_INPUT,
631  "Order polynomial is not well-formed: "
632  "p(%d, %f) = %e ; p(%d, %f) = %e",
633  x + sign*radius_x, minorder-1 - 1.5,
635  order_locations, x + sign*radius_x, minorder-1 - 1.5),
636  x + sign*radius_x, minorder - 1.5,
638  order_locations, x + sign*radius_x, minorder - 1.5));
639  }
640 
641  minorder -= 1;
642  }
643 
644  /* Move to max. order inside image */
645  while (uves_round_double( uves_polynomial_evaluate_2d(
646  order_locations, x + radius_x, maxorder + 1.5)
647  ) + radius_y <= ny &&
648  uves_round_double( uves_polynomial_evaluate_2d(
649  order_locations, x - radius_x, maxorder + 1.5)
650  ) + radius_y <= ny ) {
651  int sign;
652  for (sign = -1; sign <= 1; sign += 2)
653  {
654  assure(
656  order_locations, x + sign*radius_x, maxorder+1 + 1.5)
657  >
659  order_locations, x + sign*radius_x, maxorder + 1.5),
660  CPL_ERROR_ILLEGAL_INPUT,
661  "Order polynomial is not well-formed: "
662  "p(%d, %f) = %e ; p(%d, %f) = %e",
663  x + sign*radius_x, maxorder+1 + 1.5,
665  order_locations, x + sign*radius_x, maxorder+1 + 1.5),
666  x + sign*radius_x, maxorder + 1.5,
668  order_locations, x + sign*radius_x, maxorder + 1.5));
669  }
670 
671  maxorder += 1;
672  }
673 
674  uves_msg_debug("(x, order) = (%d, %f - %f) ", x, minorder-.5, maxorder+.5);
675 
676  for (order = minorder; order <= maxorder; order++) {
677  int ylo, yhi;
678  double backlo, backhi;
679 
680  /* Sample background above and below order using the median of a window
681  * with size (2*radius_x + 1) * (2*radius_y + 1)
682  */
683 
684  ylo = uves_round_double(
685  uves_polynomial_evaluate_2d(order_locations, x, order - 0.5) );
686  yhi = uves_round_double(
687  uves_polynomial_evaluate_2d(order_locations, x, order + 0.5) );
688 
689  /* Fail cleanly if input polynomial is corrupted */
690  assure( yhi > ylo, CPL_ERROR_ILLEGAL_INPUT,
691  "Order polynomial is not well-formed: "
692  "p(%d, %f) = %d ; p(%d, %f) = %d",
693  x, order - 0.5, ylo,
694  x, order + 0.5, yhi);
695 
696 
697  check( backlo =
698  sample_background(
699  image, x, ylo, radius_x, radius_y, nx, ny, BM_METHOD),
700  "Error sampling background level");
701 
702  check( backhi = sample_background(
703  image, x, yhi, radius_x, radius_y, nx, ny, BM_METHOD),
704  "Error sampling background level");
705 
706  uves_msg_debug("Background sample at (x, y, order) = (%d, %d, %f) = %f",
707  x, ylo, order-0.5, backlo);
708  uves_msg_debug("Background sample at (x, y, order) = (%d, %d, %f) = %f",
709  x, yhi, order+0.5, backhi);
710 
711  /* Extrapolate (linearly, or constant if MIDAS) if first order */
712  if (order == minorder) {
713  for (y = 1; y <= ylo; y++) {
714  double back = backlo + (backhi - backlo)*(y - ylo)/(yhi - ylo);
715  cpl_image_set(*background, x, y, back);
716 
717  cpl_image_set(*background, x, y, back);
718  }
719  }
720 
721  /* Make a linear interpolation (1-degree, no-smooth spline) from ylo to yhi */
722  for (y = ylo; y <= yhi; y++) {
723  double back;
724  back = backlo + (backhi - backlo) * (y - ylo) / (yhi - ylo);
725  /* We know that yhi > ylo */
726  cpl_image_set(*background, x, y, back);
727  }
728 
729  /* Extrapolate (linearly, or constant if MIDAS) if last order */
730  if (order == maxorder) {
731  for (y = yhi; y <= ny; y++) {
732  double back;
733  back = backlo + (backhi - backlo) * (y - ylo) / (yhi - ylo);
734 
735  cpl_image_set(*background, x, y, back);
736  }
737  }
738  }
739  }/* For column... */
740 
741  /* Now interpolate between columns */
742  for (y = 1; y <= ny; y++) {
743  int col;
744  for (col = stepx; col+stepx <= nx; col += stepx) {
745  int pis_rejected; /* Not used, all pixels read are good; they've just been set */
746 
747  double backlo, backhi;
748 
749  /* Read this and next column */
750  backlo = cpl_image_get(*background, col , y, &pis_rejected);
751  backhi = cpl_image_get(*background, col+stepx, y, &pis_rejected);
752 
753  /* Extrapolate (linear) before first column */
754  if (col == stepx)
755  for (x = 1; x <= col; x++)
756  {
757  double back = backlo + (backhi - backlo) * (x - col) / stepx;
758  cpl_image_set(*background, x, y, back);
759  }
760 
761  /* Interpolate between columns */
762  for (x = col; x <= col + stepx; x++)
763  {
764  double back = backlo + (backhi - backlo) * (x - col) / stepx;
765  cpl_image_set(*background, x, y, back);
766  }
767 
768  /* Extrapolate (linear) after last column */
769  if (col+stepx+stepx > nx)
770  for (x = col; x <= nx; x++)
771  {
772  double back = backlo + (backhi - backlo) * (x - col) / stepx;
773  cpl_image_set(*background, x, y, back);
774  }
775  }
776  }
777 
778  /* All pixels in background image have been set.
779  * Smooth background.
780  */
781 
782  uves_msg("Smoothing window (pixels): smox, smoy = %d, %d", smooth_x, smooth_y);
783  check( uves_filter_image_average(*background, smooth_x, smooth_y),
784  "Error applying average filter to background image");
785 
786  uves_msg("Subtracting background image");
787 
788  check( subtract_background(image, *background, NULL),
789  "Error subtracting background image");
790 
791 
792  } /* BM_METHOD was not 'no' */
793 
794 
795  cleanup:
796  return cpl_error_get_code();
797 }
798 
799 /*----------------------------------------------------------------------------*/
844 /*----------------------------------------------------------------------------*/
845 cpl_error_code
846 uves_backsub_poly(cpl_image *image,
847  const cpl_table *orders, const polynomial *order_locations,
848  background_measure_method BM_METHOD,
849  int NPOINTS,
850  int radius_y,
851  int DEGX,
852  int DEGY,
853  double KAPPA)
854 {
855  cpl_table *t = NULL;
856  polynomial *background = NULL;
857  int nx, ny;
858  int stepx, stepy; /* Step size */
859  int radius_x; /* Sample window x-radius */
860  double mse, rmse; /* mse, rms of fit */
861  cpl_size total_clipped = 0;
862 
863  if (BM_METHOD == BM_NO)
864  {
865  uves_msg("Skipping background subtraction");
866  }
867  else
868  {
869  passure( image != NULL, " ");
870  passure( orders == NULL || order_locations == NULL, " ");
871 
872  nx = cpl_image_get_size_x(image);
873  ny = cpl_image_get_size_y(image);
874 
875  assure( NPOINTS < nx, CPL_ERROR_ILLEGAL_INPUT,
876  "Number of sample columns (%d) larger than image width (%d pixels)",
877  NPOINTS, nx);
878 
879  stepx = nx/NPOINTS;
880  stepy = ny/NPOINTS;
881 
882  radius_x = stepx/2;
883 
884  /* First sample background */
885  if (orders != NULL)
886  {
887  /* Using the order table */
888 
889  int x, ordersrow, row;
890 
891  /* Check input */
892  passure( cpl_table_has_column(orders, "Slope"), " ");
893  passure( cpl_table_has_column(orders, "Intersept"), " ");
894 
895  passure( cpl_table_get_column_type(orders, "Slope") == CPL_TYPE_DOUBLE,
896  "%s",
897  uves_tostring_cpl_type(cpl_table_get_column_type(orders, "Slope")));
898 
899  passure( cpl_table_get_column_type(orders, "Intersept") == CPL_TYPE_DOUBLE,
900  "%s",
901  uves_tostring_cpl_type(cpl_table_get_column_type(orders, "Slope")));
902 
903  /* This check is computationally cheap because
904  there are never very many order lines */
905  passure( uves_table_is_sorted_double(orders, "Intersept", false), " ");
906 
907  /* Need at least two lines to identify inter-order region */
908  assure ( cpl_table_get_nrow(orders) >= 2, CPL_ERROR_ILLEGAL_INPUT,
909  "Only %" CPL_SIZE_FORMAT " line(s) in order table", cpl_table_get_nrow(orders));
910 
911  t = cpl_table_new( (nx/stepx + 1)*(cpl_table_get_nrow(orders) + 1) );
912  cpl_table_new_column(t, "X", CPL_TYPE_INT);
913  cpl_table_new_column(t, "Y", CPL_TYPE_INT);
914  cpl_table_new_column(t, "Z", CPL_TYPE_DOUBLE);
915 
916  row = 0;
917  for (ordersrow = -1; ordersrow < cpl_table_get_nrow(orders); ordersrow++)
918  {
919  double slope, intersept;
920 
921  /* Sample positions between this and the next orderline */
922 
923  /* Lowest and highest orders are special cases */
924  if (ordersrow == -1)
925  {
926  slope = cpl_table_get_double(
927  orders, "Slope" , 0, NULL);
928 
929  /* Interorder space below lowest order line is at:
930  intersept0 - (intersept1-intersept0)/2 */
931  intersept =
932  0.5*cpl_table_get_double(orders, "Intersept", 0, NULL) -
933  0.5*cpl_table_get_double(orders, "Intersept", 1, NULL) ;
934  }
935  else if (ordersrow == cpl_table_get_nrow(orders) - 1)
936  {
937  slope = cpl_table_get_double(
938  orders, "Slope" , ordersrow, NULL);
939 
940  /* Interorder space above highest order line is at:
941  intersept(N) + (intersept(N)-intersept(N-1))/2 */
942  intersept =
943  0.5*cpl_table_get_double(
944  orders, "Intersept", ordersrow, NULL) -
945  0.5*cpl_table_get_double(
946  orders, "Intersept", ordersrow-1, NULL) ;
947  }
948  else /* The most common case */
949  {
950  slope =
951  (cpl_table_get_double(
952  orders, "Slope", ordersrow , NULL) +
953  cpl_table_get_double(
954  orders, "Slope", ordersrow+1, NULL) ) / 2;
955 
956  intersept =
957  (cpl_table_get_double(
958  orders, "Intersept", ordersrow , NULL) +
959  cpl_table_get_double(
960  orders, "Intersept", ordersrow+1, NULL) ) / 2;
961  }
962 
963  /* Sample the interorder space */
964  for (x = 1 + stepx/2; x <= nx; x += stepx)
965  {
966  int y = uves_round_double(intersept + slope * x);
967 
968  if (1 <= y && y <= ny)
969  {
970  double z;
971 
972  check( z = sample_background(
973  image,
974  x, y,
975  radius_x, radius_y,
976  nx, ny,
977  BM_METHOD),
978  "Error sampling background "
979  "(x, y) = (%d, %d)", x, y);
980 
981  cpl_table_set_int (t, "X" , row, x);
982  cpl_table_set_int (t, "Y" , row, y);
983  cpl_table_set_double(t, "Z" , row, z);
984  row++;
985  }
986  }
987  } /* for ordersrow... */
988 
989  cpl_table_set_size(t, row);
990 
991  }/* if orders != NULL */
992 
993  else if (order_locations != NULL)
994  {
995  /* Sample background using the polynomial */
996 
997  int x, minorder, maxorder, order;
998  int row; /* Pointing to row in temporary table */
999 
1000  /* Check input */
1001  assure( uves_polynomial_get_dimension(order_locations) == 2,
1002  CPL_ERROR_ILLEGAL_INPUT,
1003  "Order location polynomial must be 2d. It is %d!",
1004  uves_polynomial_get_dimension(order_locations));
1005 
1006  check(( minorder = first_order(order_locations, nx),
1007  maxorder = last_order(order_locations, nx, ny)),
1008  "Error getting min. and max. order numbers");
1009 
1010  t = cpl_table_new( (nx/stepx + 1) * (maxorder-minorder+1));
1011  cpl_table_new_column(t, "X", CPL_TYPE_INT);
1012  cpl_table_new_column(t, "Y", CPL_TYPE_INT);
1013  cpl_table_new_column(t, "Z", CPL_TYPE_DOUBLE);
1014 
1015  row = 0;
1016  for (order = minorder; order <= maxorder; order++) {
1017  /* Sample the interorder space from (minorder+0.5) to (maxorder+0.5) */
1018  for (x = 1+stepx/2; x <= nx; x += stepx) {
1019  int y = uves_round_double(
1020  uves_polynomial_evaluate_2d(order_locations, x, order + 0.5));
1021 
1022  if (1 <= y && y <= ny) {
1023  double z;
1024 
1025  check( z = sample_background(image,
1026  x, y,
1027  radius_x, radius_y,
1028  nx, ny,
1029  BM_METHOD),
1030  "Error sampling background (x, order) = (%d, %d+0.5)",
1031  x, order);
1032 
1033  cpl_table_set_int (t, "X" , row, x);
1034  cpl_table_set_int (t, "Y" , row, y);
1035  cpl_table_set_double(t, "Z" , row, z);
1036  row++;
1037  }
1038  }
1039  }
1040 
1041  cpl_table_set_size(t, row);
1042  }
1043  else
1044  {
1045  /* Grid sampling (order positions unknown) */
1046  int x, y, row;
1047 
1048  t = cpl_table_new((nx/stepx + 1) * (ny/stepy + 1));
1049  cpl_table_new_column(t, "X" , CPL_TYPE_INT);
1050  cpl_table_new_column(t, "Y" , CPL_TYPE_INT);
1051  cpl_table_new_column(t, "Z" , CPL_TYPE_DOUBLE);
1052 
1053  row = 0;
1054  for (y = 1 + stepy/2; y <= ny; y += stepy)
1055  {
1056  for (x = 1+stepx/2; x <= nx; x += stepx)
1057  {
1058  double z;
1059 
1060  check( z = sample_background(image,
1061  x, y,
1062  radius_x, radius_y,
1063  nx, ny,
1064  BM_METHOD),
1065  "Error sampling background (x, y) = (%d, %d)", x, y);
1066 
1067  cpl_table_set_int (t, "X" , row, x);
1068  cpl_table_set_int (t, "Y" , row, y);
1069  cpl_table_set_double(t, "Z" , row, z);
1070  row++;
1071  }
1072  }
1073  cpl_table_set_size(t, row);
1074  }
1075 
1076  /* Sampling done. Fit poly. */
1077 
1078  total_clipped = 0;
1079  {
1080  cpl_size n_clipped;
1081  cpl_size deg_xy=(DEGX + 1)*(DEGY + 1);
1082  do {
1083  assure( cpl_table_get_nrow(t) > (DEGX + 1)*(DEGY + 1),
1084  CPL_ERROR_ILLEGAL_OUTPUT,
1085  "Too few sample points available (%" CPL_SIZE_FORMAT " point(s)) to make the fit "
1086  "(more than %" CPL_SIZE_FORMAT " points needed). "
1087  "Increase number of sample points or increase kappa",
1088  cpl_table_get_nrow(t), deg_xy);
1089 
1090  /* Fit, calculate Zfit */
1091  uves_polynomial_delete(&background);
1092  check( background = uves_polynomial_regression_2d(
1093  t, "X", "Y", "Z", NULL,
1094  DEGX, DEGY, "Zfit", NULL, NULL, &mse,
1095  NULL, NULL, -1, -1),
1096  "Error fitting polynomial");
1097 
1098  /* Residual := Z - Zfit */
1099  cpl_table_duplicate_column(t, "Residual", t, "Z");
1100  cpl_table_subtract_columns(t, "Residual", "Zfit");
1101 
1102  /* Compute residuals w.r.t. median of Z
1103  (i.e. subtract median(residual) from all residuals),
1104  then get stdev based on this new mean/median value.
1105  This is to make kappa sigma clipping more robust */
1106 
1107  cpl_table_subtract_scalar(t, "Residual",
1108  cpl_table_get_column_median(t, "Residual"));
1109  rmse = cpl_table_get_column_stdev(t, "Residual");
1110 
1111  /* One-sided kappa-sigma clipping */
1112  if (KAPPA > 0)
1113  {
1114  check( n_clipped = uves_select_table_rows(
1115  t, "Residual", CPL_GREATER_THAN, KAPPA * rmse),
1116  "Error selecting rows");
1117  }
1118  else
1119  {
1120  n_clipped = 0;
1121  }
1122 
1123  total_clipped += n_clipped;
1124 
1125  uves_msg_debug("RMS = %f. %" CPL_SIZE_FORMAT " of %" CPL_SIZE_FORMAT " points rejected in kappa-sigma clipping",
1126  rmse, n_clipped, cpl_table_get_nrow(t));
1127 
1128  cpl_table_erase_selected(t);
1129 
1130  if (n_clipped > 0)
1131  {
1132  cpl_table_erase_column(t, "Zfit");
1133  cpl_table_erase_column(t, "Residual");
1134  }
1135 
1136  } while (n_clipped > 0);
1137  }
1138 
1139  /* Try to do some quality checking of the background subtraction.
1140  The number of rejected points (the signal) is often around 10-20 % */
1141  {
1142  double percentage =
1143  100.0 * ( (double)total_clipped ) / (total_clipped + cpl_table_get_nrow(t));
1144 
1145  if (KAPPA > 0) {
1146  uves_msg("%" CPL_SIZE_FORMAT " of %" CPL_SIZE_FORMAT " points (%.2f %%) were rejected in "
1147  "kappa-sigma clipping. RMS = %.2f ADU",
1148  total_clipped,
1149  cpl_table_get_nrow(t) + total_clipped,
1150  percentage,
1151  sqrt(mse));
1152  }
1153 
1154  /* For grid sampling: */
1155  if (orders == NULL && order_locations == NULL)
1156  {
1157  if (total_clipped == 0)
1158  {
1159  uves_msg_warning("No points rejected during background "
1160  "estimation. Background subtraction is "
1161  "uncertain. Try to decrease KAPPA "
1162  "(current value is %f)", KAPPA);
1163  }
1164  if (percentage > 40)
1165  {
1166  uves_msg_warning("%f %% of the sample points were "
1167  "rejected during "
1168  "background estimation", percentage);
1169  }
1170  }
1171  }
1172 
1173  check( subtract_background(image, NULL, background),
1174  "Error subtracting background polynomial");
1175  } /* BM_METHOD wasn't 'no' */
1176 
1177  cleanup:
1178  uves_free_table(&t);
1179  uves_polynomial_delete(&background);
1180 
1181  return cpl_error_get_code();
1182 }
1183 
1184 /*----------------------------------------------------------------------------*/
1198 /*----------------------------------------------------------------------------*/
1199 /* Recipe parameter creation code for this function
1200 / * Backsmoothx, Backsmoothy * /
1201  uves_parameter_new_range(p, uves_orderpos.preproc.backsmoothx,
1202  CPL_TYPE_INT,
1203  "Radius of window used for average filtering in the "
1204  "background subtraction (mode=smooth) step",
1205  uves_orderpos.preproc,
1206  5, 0, INT_MAX);
1207  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "backsmoothx");
1208  cpl_parameterlist_append(recipe->parameters, p);
1209 
1210  uves_parameter_new_range(p, uves_orderpos.preproc.backsmoothy,
1211  CPL_TYPE_INT,
1212  "Radius of window used for average filtering in the "
1213  "background subtraction (mode=smooth) step",
1214  uves_orderpos.preproc,
1215  30, 0, INT_MAX);
1216  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "backsmoothy");
1217  cpl_parameterlist_append(recipe->parameters, p);
1218 
1219 / * Backsmoothiter * /
1220  uves_parameter_new_range(p, uves_orderpos.preproc.backsmoothiter,
1221  CPL_TYPE_INT,
1222  "Number of iterations when estimating the background "
1223  "(mode=smooth)",
1224  uves_orderpos.preproc,
1225  10, 1, INT_MAX);
1226  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "backsmoothiter");
1227  cpl_parameterlist_append(recipe->parameters, p);
1228 */
1229 cpl_error_code
1230 uves_backsub_smooth(cpl_image *image, int RADX, int RADY, int ITER)
1231 {
1232  cpl_image *background = NULL;
1233  int i;
1234 
1235  assure( RADX >= 0 && RADY >= 0, CPL_ERROR_ILLEGAL_INPUT,
1236  "Negative radius ((%d)x(%d))", RADX, RADY);
1237  assure( ITER >= 1, CPL_ERROR_ILLEGAL_INPUT,
1238  "Non-positive number of iterations (%d)", ITER);
1239 
1240  /* First estimate background */
1241  background = cpl_image_duplicate(image);
1242 
1243  for (i = 0; i < ITER; i++) {
1244  //uves_msg_debug("i=%d,%d ...",i, ITER);
1245  uves_msg("i = %d", i);
1246  check( lower_to_average(background,
1247  RADX, RADY), "Error smoothing image");
1248  }
1249 
1250  /* Then subtract background */
1251  check( cpl_image_subtract(image, background), "Could not subtract background image");
1252 
1253  cleanup:
1254  uves_free_image(&background);
1255 
1256  return cpl_error_get_code();
1257 }
1258 
1259 /*----------------------------------------------------------------------------*/
1278 /*----------------------------------------------------------------------------*/
1279 
1280 static double
1281 sample_background(const cpl_image *image, int x0, double y_0,
1282  int radius_x, int radius_y, int nx, int ny,
1283  background_measure_method BM_METHOD)
1284 {
1285  double result = 0;
1286  /* Use a table to calculate the median. Invalid rows are ignored */
1287  cpl_table *temp = NULL;
1288  bool found_good = false;
1289  int row;
1290  int x, y;
1291 
1292  check(
1293  (temp = cpl_table_new( (2*radius_x + 1) * (2*radius_y + 1) ),
1294  row = 0,
1295  cpl_table_new_column(temp, "Flux", CPL_TYPE_DOUBLE)),
1296  "Error allocating table");
1297 
1298  for(y = y_0 - radius_y; y <= y_0 + radius_y; y++)
1299  {
1300  for (x = x0 - radius_x; x <= x0 + radius_x; x++)
1301  {
1302  if (1 <= x && x <= nx &&
1303  1 <= y && y <= ny)
1304  {
1305  int pis_rejected;
1306  double flux = cpl_image_get(image, x, y, &pis_rejected);
1307  if( !pis_rejected )
1308  {
1309  cpl_table_set(temp, "Flux", row, flux);
1310  found_good = true;
1311  }
1312  else
1313  {
1314  cpl_table_set_invalid(temp, "Flux", row);
1315  }
1316  }
1317  else
1318  {
1319  cpl_table_set_invalid(temp, "Flux", row);
1320  }
1321 
1322  row++;
1323  }
1324  }
1325 
1326  assure( found_good, CPL_ERROR_ILLEGAL_INPUT, "No valid pixels in sample window");
1327 
1328  if (BM_METHOD == BM_MEDIAN)
1329  {
1330  result = cpl_table_get_column_median(temp, "Flux");
1331  }
1332  else if (BM_METHOD == BM_MINIMUM)
1333  {
1334  result = cpl_table_get_column_min(temp, "Flux");
1335  }
1336  else
1337  {
1338  assure( false, CPL_ERROR_UNSUPPORTED_MODE,
1339  "Unsupported background sample method: %d", BM_METHOD);
1340  }
1341 
1342  cleanup:
1343  uves_free_table(&temp);
1344  return result;
1345 }
1346 
1347 /*----------------------------------------------------------------------------*/
1356 /*----------------------------------------------------------------------------*/
1357 static int
1358 first_order(const polynomial *order_locations, int nx)
1359 {
1360  int result;
1361 
1362  result = 0;
1363  while (uves_polynomial_evaluate_2d(order_locations, 1 , result + 0.5) < 1 ||
1364  uves_polynomial_evaluate_2d(order_locations, nx, result + 0.5) < 1 )
1365  {
1366  result++;
1367  }
1368 
1369  while (uves_polynomial_evaluate_2d(order_locations, 1 , result - 0.5) >= 1 ||
1370  uves_polynomial_evaluate_2d(order_locations, nx, result - 0.5) >= 1 )
1371  {
1372  result -= 1;
1373 
1374  /* Fail cleanly even if 'order_locations' is corrupted */
1375  assure( result > -100000,
1376  CPL_ERROR_CONTINUE,
1377  "Invalid polynomial: p(x=1, order=%d) = %f p(x=%d, order=%d) = %f",
1378  result, uves_polynomial_evaluate_2d(order_locations, 1.0, result),
1379  nx, result, uves_polynomial_evaluate_2d(order_locations, nx, result));
1380  }
1381 
1382  cleanup:
1383  return result;
1384 }
1385 
1386 
1387 /*----------------------------------------------------------------------------*/
1397 /*----------------------------------------------------------------------------*/
1398 static int
1399 last_order(const polynomial *order_locations, int nx, int ny)
1400 {
1401  int result;
1402 
1403  result = 0;
1404  while (uves_polynomial_evaluate_2d(order_locations, 1 , result - 0.5) > ny ||
1405  uves_polynomial_evaluate_2d(order_locations, nx, result - 0.5) > ny )
1406  {
1407  result--;
1408  }
1409 
1410  while (uves_polynomial_evaluate_2d(order_locations, 1 , result + 1.5) <= ny ||
1411  uves_polynomial_evaluate_2d(order_locations, nx, result + 1.5) <= ny )
1412  {
1413  result += 1;
1414 
1415  /* Fail cleanly even if 'order_locations' is corrupted */
1416  assure( result < 100000,
1417  CPL_ERROR_CONTINUE,
1418  "Invalid polynomial: p(x=1, order=%d) = %f p(x=%d, order=%d) = %f",
1419  result, uves_polynomial_evaluate_2d(order_locations, 1.0, result),
1420  nx, result, uves_polynomial_evaluate_2d(order_locations, nx, result));
1421  }
1422 
1423  cleanup:
1424  return result;
1425 }
1426 
1427 /*----------------------------------------------------------------------------*/
1439 /*----------------------------------------------------------------------------*/
1440 static cpl_error_code
1441 lower_to_average(cpl_image *image, int RADX, int RADY)
1442 {
1443  cpl_image *average = NULL;
1444  double *image_data = NULL;
1445  double *average_data = NULL;
1446  int nx, ny;
1447  int x, y;
1448 
1449  passure( image != NULL, "Null image");
1450  nx = cpl_image_get_size_x(image);
1451  ny = cpl_image_get_size_y(image);
1452 
1453  /* Create smoothed image */
1454  uves_msg("Filtering...");
1455  check( average = cpl_image_duplicate(image), "Error copying image");
1456  check( uves_filter_image_average(average, RADX, RADY), "Error applying average filter");
1457  uves_msg("done");
1458 
1459  image_data = cpl_image_get_data(image);
1460  average_data = cpl_image_get_data(average);
1461  uves_msg("Lowering...");
1462  for (y = 0; y < ny; y++)
1463  {
1464  for (x = 0; x < nx; x++)
1465  {
1466  if (image_data[x + y*nx] > average_data[x + y*nx])
1467  {
1468  image_data[x + y*nx] = average_data[x + y*nx];
1469  }
1470  }
1471  }
1472  uves_msg("done");
1473 
1474  cleanup:
1475  uves_free_image(&average);
1476 
1477  return cpl_error_get_code();
1478 }
1479 
1480 /*----------------------------------------------------------------------------*/
1492 /*----------------------------------------------------------------------------*/
1493 
1494 static cpl_error_code
1495 subtract_background(cpl_image *image, cpl_image *background_im,
1496  const polynomial *background_pol)
1497 {
1498  int nx, ny;
1499  int x, y;
1500 
1501  double *image_data;
1502  double *background_data = NULL;
1503 
1504  passure(image != NULL, " ");
1505  /* Exactly one of 'background_im' and 'background_pol' must be non-NULL */
1506  passure((background_im == NULL) != (background_pol == NULL), " ");
1507 
1508  /* For efficiency, don't call cpl_image_get() */
1509  /* The following check is too strict. It can be avoided to solve PIPE-4893
1510  assure(cpl_image_count_rejected(image) == 0,
1511  CPL_ERROR_UNSUPPORTED_MODE, "Input image contains bad pixels");
1512  */
1513  assure(cpl_image_get_type(image) == CPL_TYPE_DOUBLE,
1514  CPL_ERROR_UNSUPPORTED_MODE,
1515  "Input image is of type %s. double expected",
1516  uves_tostring_cpl_type(cpl_image_get_type(image)));
1517 
1518  if (background_im != NULL)
1519  {
1520  assure(cpl_image_count_rejected(background_im) == 0,
1521  CPL_ERROR_UNSUPPORTED_MODE, "Background image contains bad pixels");
1522  assure(cpl_image_get_type(background_im) == CPL_TYPE_DOUBLE,
1523  CPL_ERROR_UNSUPPORTED_MODE,
1524  "Background image is of type %s. double expected",
1525  uves_tostring_cpl_type(cpl_image_get_type(background_im)));
1526  }
1527 
1528  image_data = cpl_image_get_data_double(image);
1529  if (background_im != NULL)
1530  {
1531  background_data = cpl_image_get_data_double(background_im);
1532  }
1533 
1534  nx = cpl_image_get_size_x(image);
1535  ny = cpl_image_get_size_y(image);
1536 
1537  for (y = 1; y <= ny; y++)
1538  {
1539  for (x = 1; x <= nx; x++)
1540  {
1541  double back;
1542  double flux, new_flux;
1543 
1544  if (background_im != NULL)
1545  {
1546  /* Slow: back = cpl_image_get(background_im, x, y, &pis_rejected); */
1547  back = background_data[(x-1) + (y-1) * nx];
1548  }
1549  else
1550  {
1551  /* Evaluate at (x,y) */
1552  back = uves_polynomial_evaluate_2d(background_pol,
1553  x,
1554  y);
1555  }
1556 
1557  /* Slow: flux = cpl_image_get(image, x, y, &pis_rejected); */
1558  flux = image_data[(x-1) + (y-1) * nx];
1559 
1560 /* Exclude these sanity checks for backwards compatibility */
1561 #if 0
1562  /* Make sure the estimated background is between zero and flux-value */
1563  if (back < 0)
1564  {
1565  back = 0.0;
1566  }
1567  if (back > flux)
1568  {
1569  back = flux;
1570  }
1571 
1572  /* Then subtract the background.
1573  * Pixel flux may be negative. Make sure the result is non-negative.
1574  */
1575  new_flux = uves_max_double(0, flux - back);
1576 #else
1577  new_flux = flux-back;
1578 #endif
1579 
1580  /* Slow: cpl_image_set(image, x, y, new_flux); */
1581  image_data[(x-1) + (y-1) * nx] = new_flux;
1582 
1583  if (background_im != NULL)
1584  {
1585  /* Slow: cpl_image_set(background_im, x, y, flux - new_flux); */
1586  background_data[(x-1) + (y-1) * nx] = flux - new_flux;
1587  }
1588  }
1589  }/* for each pixel... */
1590 
1591  cleanup:
1592  return cpl_error_get_code();
1593 }
int uves_polynomial_get_dimension(const polynomial *p)
Get the dimension of a polynomial.
void uves_polynomial_delete(polynomial **p)
Delete a polynomial.
#define uves_msg_warning(...)
Print an warning message.
Definition: uves_msg.h:87
bool uves_table_is_sorted_double(const cpl_table *t, const char *column, const bool reverse)
Determine if a table is sorted.
Definition: uves_utils.c:3847
cpl_error_code uves_filter_image_average(cpl_image *image, int radius_x, int radius_y)
Average filter.
#define passure(BOOL,...)
Definition: uves_error.h:207
double uves_pfits_get_gratwlen(const uves_propertylist *plist, enum uves_chip chip)
find out the central wavelength
Definition: uves_pfits.c:1371
int uves_pfits_get_binx(const uves_propertylist *plist)
Find out the x binning factor.
Definition: uves_pfits.c:1176
#define uves_msg(...)
Print a message on 'info' or 'debug' level.
Definition: uves_msg.h:119
polynomial * uves_polynomial_regression_2d(cpl_table *t, const char *X1, const char *X2, const char *Y, const char *sigmaY, int degree1, int degree2, const char *polynomial_fit, const char *residual_square, const char *variance_fit, double *mse, double *red_chisq, polynomial **variance, double kappa, double min_reject)
Fit a 2d polynomial to three table columns.
Definition: uves_utils.c:2869
int uves_pfits_get_biny(const uves_propertylist *plist)
Find out the y binning factor.
Definition: uves_pfits.c:1194
double uves_polynomial_evaluate_2d(const polynomial *p, double x1, double x2)
Evaluate a 2d polynomial.
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
#define check(CMD,...)
Definition: uves_error.h:198