UVES Pipeline Reference Manual  5.5.5b1
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  *
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 /*----------------------------------------------------------------------------*/
39 /*----------------------------------------------------------------------------*/
40 
41 
42 #include <uves_backsub.h>
43 
44 #include <uves_parameters.h>
45 #include <uves_pfits.h>
46 #include <uves_dump.h>
47 #include <uves_utils.h>
48 #include <uves_utils_wrappers.h>
49 #include <uves_utils_cpl.h>
50 #include <uves_error.h>
51 #include <uves_msg.h>
52 #include <uves.h>
53 
54 #include <cpl.h>
55 #include <string.h>
56 #include <stdbool.h>
57 #include <float.h>
58 /*-----------------------------------------------------------------------------
59  Functions prototypes
60  -----------------------------------------------------------------------------*/
61 static int first_order(const polynomial *order_locations, int nx);
62 static int last_order (const polynomial *order_locations, int nx, int ny);
63 static cpl_error_code lower_to_average(cpl_image *image, int RADX, int RADY);
64 static double sample_background(const cpl_image *image, int x0, double y_0,
65  int radius_x, int radius_y, int nx, int ny,
66  background_measure_method BM_METHOD);
67 static cpl_error_code subtract_background(cpl_image *image, cpl_image *background_im,
68  const polynomial *background_pol);
69 
70 /*-----------------------------------------------------------------------------
71  Defines
72  -----------------------------------------------------------------------------*/
73 
74 /* This is sort of ugly, because we fine tune parameters depending on
75  wavelength and also different for masterflat/science exposures.
76  A 'perfect' background subtraction algorithm should not need to
77  know about its context.
78 */
79 
80 #define BACKSUB_FLAT_SMOOTHX_BLUE (25.0/4096)
81 #define BACKSUB_FLAT_SMOOTHX_RED (50.0/4096)
82 #define BACKSUB_FLAT_SMOOTHY_BLUE (100.0/2048)
83 #define BACKSUB_FLAT_SMOOTHY_RED (300.0/2048)
84 
85 #define BACKSUB_SCI_SMOOTHX_BLUE (300.0/4096)
86 #define BACKSUB_SCI_SMOOTHX_RED (300.0/4096)
87 #define BACKSUB_SCI_SMOOTHY_BLUE (200.0/2048)
88 #define BACKSUB_SCI_SMOOTHY_RED (500.0/2048)
89 
90 #define BACKSUB_SMOOTHY_WLEN 859.9
91 
94 /*-----------------------------------------------------------------------------
95  Implementation
96  -----------------------------------------------------------------------------*/
97 
98 /*----------------------------------------------------------------------------*/
106 /*----------------------------------------------------------------------------*/
107 
108 cpl_parameterlist *
109 uves_backsub_define_parameters(void)
110 {
111 
112  char *full_name = NULL;
113  cpl_parameterlist *parameters = NULL;
114  cpl_parameter *p = NULL;
115 
116  parameters = cpl_parameterlist_new();
117 
118  //
119  const char* name = "mmethod";
120  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
121 
122  uves_parameter_new_enum(p, full_name,
123  CPL_TYPE_STRING,
124  "Background measuring method. If equal to 'median' "
125  "the background is sampled using the median of a subwindow. "
126  "If 'minimum', the subwindow minimum value is used. "
127  "If 'no', no background subtraction is done.",
128  UVES_BACKSUB_ID,
129  "median", /* Default */
130  3, /* Number of options */
131  "median", "minimum", "no"); /* List of options */
132  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
133  cpl_parameterlist_append(parameters, p);
134  cpl_free(full_name);
135 
136  //
137  name = "npoints";
138  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
139  uves_parameter_new_range(p, full_name,
140  CPL_TYPE_INT,
141  "This is the number of columns in interorder space "
142  "used to sample the background.",
143  UVES_BACKSUB_ID,
144  82, 0, INT_MAX);
145  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
146  cpl_parameterlist_append(parameters, p);
147  cpl_free(full_name);
148 
149  //
150  name = "radiusy";
151  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
152  uves_parameter_new_range(p, full_name,
153  CPL_TYPE_INT,
154  "The height (in pixels) of the background sampling "
155  "window is (2*radiusy + 1). "
156  "This parameter is not corrected for binning.",
157  UVES_BACKSUB_ID,
158  2, 0, INT_MAX);
159  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
160  cpl_parameterlist_append(parameters, p);
161  cpl_free(full_name);
162 
163  //
164  name = "sdegree";
165  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
166  uves_parameter_new_range(p, full_name,
167  CPL_TYPE_INT,
168  "Degree of interpolating splines. Currently "
169  "only degree = 1 is supported",
170  UVES_BACKSUB_ID,
171  1, 0, INT_MAX);
172  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
173  cpl_parameterlist_append(parameters, p);
174  cpl_free(full_name);
175 
176  //
177  name = "smoothx";
178  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
179  uves_parameter_new_range(p, full_name,
180  CPL_TYPE_DOUBLE,
181  "If spline interpolation is used to measure the background, "
182  "the x-radius of the post-smoothing window is "
183  "(smoothx * image_width). Here, 'image_width' is the image "
184  "width after binning. If negative, the default values are used: "
185  make_str(BACKSUB_FLAT_SMOOTHX_BLUE) " for blue flat-field frames, "
186  make_str(BACKSUB_FLAT_SMOOTHX_RED) " for red flat-field frames, "
187  make_str(BACKSUB_SCI_SMOOTHX_BLUE) " for blue science frames and "
188  make_str(BACKSUB_SCI_SMOOTHX_RED) " for red science frames.",
189  UVES_BACKSUB_ID,
190  -1.0, -DBL_MAX, DBL_MAX);
191  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
192  cpl_parameterlist_append(parameters, p);
193  cpl_free(full_name);
194 
195  //
196  name = "smoothy";
197  full_name = uves_sprintf("%s.%s", UVES_BACKSUB_ID, name);
198  uves_parameter_new_range(p, full_name,
199  CPL_TYPE_DOUBLE,
200  "If spline interpolation is used to measure the "
201  "background, the y-radius of the post-smoothing "
202  "window is (smoothy * image_height). Here, "
203  "'image_height' is the image height after binning. "
204  "If negative, the default values are used: "
205  make_str(BACKSUB_FLAT_SMOOTHY_BLUE) " for blue flat-field frames, "
206  make_str(BACKSUB_FLAT_SMOOTHY_RED) " for red flat-field frames, "
207  make_str(BACKSUB_SCI_SMOOTHY_BLUE) " for blue science frames and "
208  make_str(BACKSUB_SCI_SMOOTHY_RED) " for red science frames.",
209  UVES_BACKSUB_ID,
210  -1.0, -DBL_MAX, DBL_MAX);
211  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, name);
212  cpl_parameterlist_append(parameters, p);
213  cpl_free(full_name);
214 
215  if (cpl_error_get_code() != CPL_ERROR_NONE)
216  {
217  cpl_msg_error(__func__, "Creation of spline background subtraction "
218  "parameters failed: '%s'", cpl_error_get_where());
219  cpl_parameterlist_delete(parameters);
220  return NULL;
221  }
222  else
223  {
224  return parameters;
225  }
226 }
227 
228 /*----------------------------------------------------------------------------*/
238 /*----------------------------------------------------------------------------*/
239 background_measure_method
240 uves_get_bm_method(const cpl_parameterlist *parameters, const char *context,
241  const char *subcontext)
242 {
243  const char *bm = "";
244  background_measure_method result = 0;
245 
246  check( uves_get_parameter(parameters, context, subcontext, "mmethod", CPL_TYPE_STRING, &bm),
247  "Could not read parameter");
248 
249  if (strcmp(bm, "median" ) == 0) result = BM_MEDIAN;
250  else if (strcmp(bm, "minimum") == 0) result = BM_MINIMUM;
251  else if (strcmp(bm, "no" ) == 0) result = BM_NO;
252  else
253  {
254  /* Impossible */ assure(false, CPL_ERROR_ILLEGAL_INPUT,
255  "No such background measuring method: '%s'", bm);
256  }
257 
258  cleanup:
259  return result;
260 }
261 
262 /*----------------------------------------------------------------------------*/
296 /*----------------------------------------------------------------------------*/
297 
298 cpl_error_code
299 uves_backsub_spline(cpl_image *image, const uves_propertylist *raw_header,
300  const cpl_table *ordertable, const polynomial *order_locations,
301  const cpl_parameterlist *parameters, const char *context,
302  enum uves_chip chip,
303  bool flat_field,
304  cpl_image **background)
305 {
306  /* Recipe parameters */
307  background_measure_method BM_METHOD;
308  int npoints;
309  int radius_y;
310  int bin_x=1;
311  int bin_y=1;
312 
313  int sdegree;
314  double SMOOTHX;
315  double SMOOTHY;
316 
317  /* Local variables */
318  int nx, ny;
319  int x, y;
320  int stepx;
321  int radius_x;
322  int smooth_x, smooth_y; /* Window radius in pixels */
323 
324  passure( image != NULL, " ");
325  passure( raw_header != NULL, " ");
326  passure( ordertable != NULL, " ");
327  passure( order_locations != NULL, " ");
328  passure( parameters != NULL, " ");
329  passure( context != NULL, " ");
330  passure( uves_polynomial_get_dimension(order_locations) == 2,
331  "%d", uves_polynomial_get_dimension(order_locations));
332  passure( background != NULL, " ");
333 
334  /* Get recipe parameters */
335  check( BM_METHOD = uves_get_bm_method(parameters, context, UVES_BACKSUB_ID),
336  "Error getting background measuring method");
337 
338  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
339  "npoints", CPL_TYPE_INT , &npoints) , "Could not read parameter");
340  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
341  "radiusy", CPL_TYPE_INT , &radius_y), "Could not read parameter");
342 
343  check(bin_x=uves_pfits_get_binx(raw_header),"error getting %s",UVES_BINX);
344  check(bin_y=uves_pfits_get_biny(raw_header),"error getting %s",UVES_BINY);
345 
346  radius_y = uves_round_double((double)radius_y/bin_y);
347 
348  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
349  "sdegree", CPL_TYPE_INT , &sdegree) , "Could not read parameter");
350  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
351  "smoothx", CPL_TYPE_DOUBLE, &SMOOTHX) , "Could not read parameter");
352  check( uves_get_parameter(parameters, context, UVES_BACKSUB_ID,
353  "smoothy", CPL_TYPE_DOUBLE, &SMOOTHY) , "Could not read parameter");
354 
355 
356  /* Get other parameters */
357  nx = cpl_image_get_size_x(image);
358  ny = cpl_image_get_size_y(image);
359 
360 
361  if (BM_METHOD == BM_NO)
362  {
363  uves_msg("Skipping background subtraction");
364 
365  /* Calculate a zero-background */
366  check( *background = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE),
367  "Error allocating image");
368  }
369  else {
370  /* If negative, set default values for smoothx, smoothy */
371  if (SMOOTHX < 0)
372  {
373  if (chip == UVES_CHIP_BLUE)
374  {
375  SMOOTHX = (flat_field) ?
376  BACKSUB_FLAT_SMOOTHX_BLUE : BACKSUB_SCI_SMOOTHX_BLUE;
377  }
378  else
379  {
380  SMOOTHX = (flat_field) ?
381  BACKSUB_FLAT_SMOOTHX_RED : BACKSUB_SCI_SMOOTHX_RED;
382  }
383  }
384  if (SMOOTHY < 0)
385  {
386  double wlen;
387 
388  /* Read wavelength from raw header */
389 
390  check( wlen = uves_pfits_get_gratwlen(raw_header, chip),
391  "Error reading central wavelength");
392 
393  /* The criterion is not if the chip is BLUE/RED,
394  but whether the wlen is < 860A */
395  if (wlen < BACKSUB_SMOOTHY_WLEN)
396  {
397  SMOOTHY = (flat_field) ?
398  BACKSUB_FLAT_SMOOTHY_BLUE : BACKSUB_SCI_SMOOTHY_BLUE;
399  }
400  else
401  {
402  SMOOTHY = (flat_field) ?
403  BACKSUB_FLAT_SMOOTHY_RED : BACKSUB_SCI_SMOOTHY_RED;
404  }
405  }
406 
407  assure( 0 < SMOOTHX, CPL_ERROR_ILLEGAL_INPUT, "Illegal smoothx factor: %e", SMOOTHX);
408  assure( 0 < SMOOTHY, CPL_ERROR_ILLEGAL_INPUT, "Illegal smoothy factor: %e", SMOOTHY);
409 
410  smooth_x = uves_round_double(SMOOTHX * nx - 0.5);
411  smooth_y = uves_round_double(SMOOTHY * ny - 0.5);
412 
413  assure( 0 < npoints, CPL_ERROR_ILLEGAL_INPUT,
414  "Illegal number of sample points: %d", npoints);
415  stepx = nx / npoints;
416  assure( 0 < stepx, CPL_ERROR_ILLEGAL_INPUT, "Illegal step size: %d", stepx);
417  radius_x = stepx/2;
418  assure( 0 < radius_x, CPL_ERROR_ILLEGAL_INPUT, "Illegal x sample radius: %d", radius_x);
419  assure( 0 < radius_y, CPL_ERROR_ILLEGAL_INPUT, "Illegal y sample radius: %d", radius_y);
420  assure( 0 < smooth_x, CPL_ERROR_ILLEGAL_INPUT, "Illegal x sample smooth: %d", smooth_x);
421  assure( 0 < smooth_y, CPL_ERROR_ILLEGAL_INPUT, "Illegal y sample smooth: %d", smooth_y);
422  assure( sdegree == 1, CPL_ERROR_UNSUPPORTED_MODE,
423  "Spline degree must be 1. It is %d", sdegree);
424 
425  uves_msg("Sample window (pixels): radx, rady = %d, %d", radius_x, radius_y);
426 
427  check( *background = cpl_image_new(nx, ny, CPL_TYPE_DOUBLE),
428  "Error allocating background image");
429 
430  /* Process */
431 
432  for (x = stepx; x <= nx; x += stepx) {
433  int order, minorder, maxorder;
434  /* Find min. and max. order where background positions are inside image */
435 
436  minorder = cpl_table_get_column_min(ordertable, "Order");
437 
438  /* If outside image, move to inside image */
439  while (uves_round_double(
440  uves_polynomial_evaluate_2d(order_locations, x + radius_x, minorder - 0.5)
441  ) - radius_y < 1 ||
442  uves_round_double(
443  uves_polynomial_evaluate_2d(order_locations, x - radius_x, minorder - 0.5))
444  - radius_y < 1 )
445  {
446  int sign;
447 
448  for (sign = -1; sign <= 1; sign += 2)
449  {
450  assure(
451  uves_polynomial_evaluate_2d(order_locations,
452  x + sign*radius_x, minorder+1 - 0.5) >
453  uves_polynomial_evaluate_2d(order_locations,
454  x + sign*radius_x, minorder - 0.5),
455  CPL_ERROR_ILLEGAL_INPUT,
456  "Order polynomial is not well-formed: "
457  "p(%d, %f) = %e; p(%d, %f) = %e",
458  x + sign*radius_x, minorder+1 - 0.5, uves_polynomial_evaluate_2d(
459  order_locations, x + sign*radius_x, minorder+1 - 0.5
460  ),
461  x + sign*radius_x, minorder - 0.5, uves_polynomial_evaluate_2d(
462  order_locations, x + sign*radius_x, minorder - 0.5)
463  );
464  }
465 
466  minorder += 1;
467  }
468 
469  maxorder = cpl_table_get_column_max(ordertable, "Order");
470 
471  /* If outside image, move to inside image */
472  while (uves_round_double(
473  uves_polynomial_evaluate_2d(order_locations, x + radius_x, maxorder + 0.5)
474  ) + radius_y > ny ||
475  uves_round_double(
476  uves_polynomial_evaluate_2d(order_locations, x - radius_x, maxorder + 0.5)
477  ) + radius_y > ny ) {
478  int sign;
479  for (sign = -1; sign <= 1; sign += 2)
480  {
481  assure(
483  order_locations, x + sign*radius_x, maxorder-1 - 0.5) <
484  uves_polynomial_evaluate_2d(order_locations,
485  x + sign*radius_x, maxorder - 0.5),
486  CPL_ERROR_ILLEGAL_INPUT,
487  "Order polynomial is not well-formed: "
488  "p(%d, %f) = %e; p(%d, %f) = %e",
489  x + sign*radius_x, maxorder-1 - 0.5, uves_polynomial_evaluate_2d(
490  order_locations, x + sign*radius_x, maxorder-1 - 0.5),
491  x + sign*radius_x, maxorder - 0.5, uves_polynomial_evaluate_2d(
492  order_locations, x + sign*radius_x, maxorder - 0.5)
493  );
494  }
495 
496  maxorder -= 1;
497  }
498 
499  /* Move to min. order inside image */
500  while (uves_round_double(uves_polynomial_evaluate_2d(
501  order_locations, x + radius_x, minorder - 1.5)
502  ) - radius_y >= 1 &&
503  uves_round_double(uves_polynomial_evaluate_2d(
504  order_locations, x - radius_x, minorder - 1.5)
505  ) - radius_y >= 1 )
506  {
507  int sign;
508  for (sign = -1; sign <= 1; sign += 2)
509  {
510  assure(
512  order_locations, x + sign*radius_x, minorder-1 - 1.5) <
514  order_locations, x + sign*radius_x, minorder - 1.5),
515  CPL_ERROR_ILLEGAL_INPUT,
516  "Order polynomial is not well-formed: "
517  "p(%d, %f) = %e ; p(%d, %f) = %e",
518  x + sign*radius_x, minorder-1 - 1.5,
520  order_locations, x + sign*radius_x, minorder-1 - 1.5),
521  x + sign*radius_x, minorder - 1.5,
523  order_locations, x + sign*radius_x, minorder - 1.5));
524  }
525 
526  minorder -= 1;
527  }
528 
529  /* Move to max. order inside image */
530  while (uves_round_double( uves_polynomial_evaluate_2d(
531  order_locations, x + radius_x, maxorder + 1.5)
532  ) + radius_y <= ny &&
533  uves_round_double( uves_polynomial_evaluate_2d(
534  order_locations, x - radius_x, maxorder + 1.5)
535  ) + radius_y <= ny ) {
536  int sign;
537  for (sign = -1; sign <= 1; sign += 2)
538  {
539  assure(
541  order_locations, x + sign*radius_x, maxorder+1 + 1.5)
542  >
544  order_locations, x + sign*radius_x, maxorder + 1.5),
545  CPL_ERROR_ILLEGAL_INPUT,
546  "Order polynomial is not well-formed: "
547  "p(%d, %f) = %e ; p(%d, %f) = %e",
548  x + sign*radius_x, maxorder+1 + 1.5,
550  order_locations, x + sign*radius_x, maxorder+1 + 1.5),
551  x + sign*radius_x, maxorder + 1.5,
553  order_locations, x + sign*radius_x, maxorder + 1.5));
554  }
555 
556  maxorder += 1;
557  }
558 
559  uves_msg_debug("(x, order) = (%d, %f - %f) ", x, minorder-.5, maxorder+.5);
560 
561  for (order = minorder; order <= maxorder; order++) {
562  int ylo, yhi;
563  double backlo, backhi;
564 
565  /* Sample background above and below order using the median of a window
566  * with size (2*radius_x + 1) * (2*radius_y + 1)
567  */
568 
569  ylo = uves_round_double(
570  uves_polynomial_evaluate_2d(order_locations, x, order - 0.5) );
571  yhi = uves_round_double(
572  uves_polynomial_evaluate_2d(order_locations, x, order + 0.5) );
573 
574  /* Fail cleanly if input polynomial is corrupted */
575  assure( yhi > ylo, CPL_ERROR_ILLEGAL_INPUT,
576  "Order polynomial is not well-formed: "
577  "p(%d, %f) = %d ; p(%d, %f) = %d",
578  x, order - 0.5, ylo,
579  x, order + 0.5, yhi);
580 
581 
582  check( backlo =
583  sample_background(
584  image, x, ylo, radius_x, radius_y, nx, ny, BM_METHOD),
585  "Error sampling background level");
586 
587  check( backhi = sample_background(
588  image, x, yhi, radius_x, radius_y, nx, ny, BM_METHOD),
589  "Error sampling background level");
590 
591  uves_msg_debug("Background sample at (x, y, order) = (%d, %d, %f) = %f",
592  x, ylo, order-0.5, backlo);
593  uves_msg_debug("Background sample at (x, y, order) = (%d, %d, %f) = %f",
594  x, yhi, order+0.5, backhi);
595 
596  /* Extrapolate (linearly, or constant if MIDAS) if first order */
597  if (order == minorder) {
598  for (y = 1; y <= ylo; y++) {
599  double back = backlo + (backhi - backlo)*(y - ylo)/(yhi - ylo);
600  cpl_image_set(*background, x, y, back);
601 
602  cpl_image_set(*background, x, y, back);
603  }
604  }
605 
606  /* Make a linear interpolation (1-degree, no-smooth spline) from ylo to yhi */
607  for (y = ylo; y <= yhi; y++) {
608  double back;
609  back = backlo + (backhi - backlo) * (y - ylo) / (yhi - ylo);
610  /* We know that yhi > ylo */
611  cpl_image_set(*background, x, y, back);
612  }
613 
614  /* Extrapolate (linearly, or constant if MIDAS) if last order */
615  if (order == maxorder) {
616  for (y = yhi; y <= ny; y++) {
617  double back;
618  back = backlo + (backhi - backlo) * (y - ylo) / (yhi - ylo);
619 
620  cpl_image_set(*background, x, y, back);
621  }
622  }
623  }
624  }/* For column... */
625 
626  /* Now interpolate between columns */
627  for (y = 1; y <= ny; y++) {
628  int col;
629  for (col = stepx; col+stepx <= nx; col += stepx) {
630  int pis_rejected; /* Not used, all pixels read are good; they've just been set */
631 
632  double backlo, backhi;
633 
634  /* Read this and next column */
635  backlo = cpl_image_get(*background, col , y, &pis_rejected);
636  backhi = cpl_image_get(*background, col+stepx, y, &pis_rejected);
637 
638  /* Extrapolate (linear) before first column */
639  if (col == stepx)
640  for (x = 1; x <= col; x++)
641  {
642  double back = backlo + (backhi - backlo) * (x - col) / stepx;
643  cpl_image_set(*background, x, y, back);
644  }
645 
646  /* Interpolate between columns */
647  for (x = col; x <= col + stepx; x++)
648  {
649  double back = backlo + (backhi - backlo) * (x - col) / stepx;
650  cpl_image_set(*background, x, y, back);
651  }
652 
653  /* Extrapolate (linear) after last column */
654  if (col+stepx+stepx > nx)
655  for (x = col; x <= nx; x++)
656  {
657  double back = backlo + (backhi - backlo) * (x - col) / stepx;
658  cpl_image_set(*background, x, y, back);
659  }
660  }
661  }
662 
663  /* All pixels in background image have been set.
664  * Smooth background.
665  */
666 
667  uves_msg("Smoothing window (pixels): smox, smoy = %d, %d", smooth_x, smooth_y);
668  check( uves_filter_image_average(*background, smooth_x, smooth_y),
669  "Error applying average filter to background image");
670 
671  uves_msg("Subtracting background image");
672 
673  check( subtract_background(image, *background, NULL),
674  "Error subtracting background image");
675 
676 
677  } /* BM_METHOD was not 'no' */
678 
679 
680  cleanup:
681  return cpl_error_get_code();
682 }
683 
684 /*----------------------------------------------------------------------------*/
729 /*----------------------------------------------------------------------------*/
730 cpl_error_code
731 uves_backsub_poly(cpl_image *image,
732  const cpl_table *orders, const polynomial *order_locations,
733  background_measure_method BM_METHOD,
734  int NPOINTS,
735  int radius_y,
736  int DEGX,
737  int DEGY,
738  double KAPPA)
739 {
740  cpl_table *t = NULL;
741  polynomial *background = NULL;
742  int nx, ny;
743  int stepx, stepy; /* Step size */
744  int radius_x; /* Sample window x-radius */
745  double mse, rmse; /* mse, rms of fit */
746  cpl_size total_clipped = 0;
747 
748  if (BM_METHOD == BM_NO)
749  {
750  uves_msg("Skipping background subtraction");
751  }
752  else
753  {
754  passure( image != NULL, " ");
755  passure( orders == NULL || order_locations == NULL, " ");
756 
757  nx = cpl_image_get_size_x(image);
758  ny = cpl_image_get_size_y(image);
759 
760  assure( NPOINTS < nx, CPL_ERROR_ILLEGAL_INPUT,
761  "Number of sample columns (%d) larger than image width (%d pixels)",
762  NPOINTS, nx);
763 
764  stepx = nx/NPOINTS;
765  stepy = ny/NPOINTS;
766 
767  radius_x = stepx/2;
768 
769  /* First sample background */
770  if (orders != NULL)
771  {
772  /* Using the order table */
773 
774  int x, ordersrow, row;
775 
776  /* Check input */
777  passure( cpl_table_has_column(orders, "Slope"), " ");
778  passure( cpl_table_has_column(orders, "Intersept"), " ");
779 
780  passure( cpl_table_get_column_type(orders, "Slope") == CPL_TYPE_DOUBLE,
781  "%s",
782  uves_tostring_cpl_type(cpl_table_get_column_type(orders, "Slope")));
783 
784  passure( cpl_table_get_column_type(orders, "Intersept") == CPL_TYPE_DOUBLE,
785  "%s",
786  uves_tostring_cpl_type(cpl_table_get_column_type(orders, "Slope")));
787 
788  /* This check is computationally cheap because
789  there are never very many order lines */
790  passure( uves_table_is_sorted_double(orders, "Intersept", false), " ");
791 
792  /* Need at least two lines to identify inter-order region */
793  assure ( cpl_table_get_nrow(orders) >= 2, CPL_ERROR_ILLEGAL_INPUT,
794  "Only %" CPL_SIZE_FORMAT " line(s) in order table", cpl_table_get_nrow(orders));
795 
796  t = cpl_table_new( (nx/stepx + 1)*(cpl_table_get_nrow(orders) + 1) );
797  cpl_table_new_column(t, "X", CPL_TYPE_INT);
798  cpl_table_new_column(t, "Y", CPL_TYPE_INT);
799  cpl_table_new_column(t, "Z", CPL_TYPE_DOUBLE);
800 
801  row = 0;
802  for (ordersrow = -1; ordersrow < cpl_table_get_nrow(orders); ordersrow++)
803  {
804  double slope, intersept;
805 
806  /* Sample positions between this and the next orderline */
807 
808  /* Lowest and highest orders are special cases */
809  if (ordersrow == -1)
810  {
811  slope = cpl_table_get_double(
812  orders, "Slope" , 0, NULL);
813 
814  /* Interorder space below lowest order line is at:
815  intersept0 - (intersept1-intersept0)/2 */
816  intersept =
817  0.5*cpl_table_get_double(orders, "Intersept", 0, NULL) -
818  0.5*cpl_table_get_double(orders, "Intersept", 1, NULL) ;
819  }
820  else if (ordersrow == cpl_table_get_nrow(orders) - 1)
821  {
822  slope = cpl_table_get_double(
823  orders, "Slope" , ordersrow, NULL);
824 
825  /* Interorder space above highest order line is at:
826  intersept(N) + (intersept(N)-intersept(N-1))/2 */
827  intersept =
828  0.5*cpl_table_get_double(
829  orders, "Intersept", ordersrow, NULL) -
830  0.5*cpl_table_get_double(
831  orders, "Intersept", ordersrow-1, NULL) ;
832  }
833  else /* The most common case */
834  {
835  slope =
836  (cpl_table_get_double(
837  orders, "Slope", ordersrow , NULL) +
838  cpl_table_get_double(
839  orders, "Slope", ordersrow+1, NULL) ) / 2;
840 
841  intersept =
842  (cpl_table_get_double(
843  orders, "Intersept", ordersrow , NULL) +
844  cpl_table_get_double(
845  orders, "Intersept", ordersrow+1, NULL) ) / 2;
846  }
847 
848  /* Sample the interorder space */
849  for (x = 1 + stepx/2; x <= nx; x += stepx)
850  {
851  int y = uves_round_double(intersept + slope * x);
852 
853  if (1 <= y && y <= ny)
854  {
855  double z;
856 
857  check( z = sample_background(
858  image,
859  x, y,
860  radius_x, radius_y,
861  nx, ny,
862  BM_METHOD),
863  "Error sampling background "
864  "(x, y) = (%d, %d)", x, y);
865 
866  cpl_table_set_int (t, "X" , row, x);
867  cpl_table_set_int (t, "Y" , row, y);
868  cpl_table_set_double(t, "Z" , row, z);
869  row++;
870  }
871  }
872  } /* for ordersrow... */
873 
874  cpl_table_set_size(t, row);
875 
876  }/* if orders != NULL */
877 
878  else if (order_locations != NULL)
879  {
880  /* Sample background using the polynomial */
881 
882  int x, minorder, maxorder, order;
883  int row; /* Pointing to row in temporary table */
884 
885  /* Check input */
886  assure( uves_polynomial_get_dimension(order_locations) == 2,
887  CPL_ERROR_ILLEGAL_INPUT,
888  "Order location polynomial must be 2d. It is %d!",
889  uves_polynomial_get_dimension(order_locations));
890 
891  check(( minorder = first_order(order_locations, nx),
892  maxorder = last_order(order_locations, nx, ny)),
893  "Error getting min. and max. order numbers");
894 
895  t = cpl_table_new( (nx/stepx + 1) * (maxorder-minorder+1));
896  cpl_table_new_column(t, "X", CPL_TYPE_INT);
897  cpl_table_new_column(t, "Y", CPL_TYPE_INT);
898  cpl_table_new_column(t, "Z", CPL_TYPE_DOUBLE);
899 
900  row = 0;
901  for (order = minorder; order <= maxorder; order++) {
902  /* Sample the interorder space from (minorder+0.5) to (maxorder+0.5) */
903  for (x = 1+stepx/2; x <= nx; x += stepx) {
904  int y = uves_round_double(
905  uves_polynomial_evaluate_2d(order_locations, x, order + 0.5));
906 
907  if (1 <= y && y <= ny) {
908  double z;
909 
910  check( z = sample_background(image,
911  x, y,
912  radius_x, radius_y,
913  nx, ny,
914  BM_METHOD),
915  "Error sampling background (x, order) = (%d, %d+0.5)",
916  x, order);
917 
918  cpl_table_set_int (t, "X" , row, x);
919  cpl_table_set_int (t, "Y" , row, y);
920  cpl_table_set_double(t, "Z" , row, z);
921  row++;
922  }
923  }
924  }
925 
926  cpl_table_set_size(t, row);
927  }
928  else
929  {
930  /* Grid sampling (order positions unknown) */
931  int x, y, row;
932 
933  t = cpl_table_new((nx/stepx + 1) * (ny/stepy + 1));
934  cpl_table_new_column(t, "X" , CPL_TYPE_INT);
935  cpl_table_new_column(t, "Y" , CPL_TYPE_INT);
936  cpl_table_new_column(t, "Z" , CPL_TYPE_DOUBLE);
937 
938  row = 0;
939  for (y = 1 + stepy/2; y <= ny; y += stepy)
940  {
941  for (x = 1+stepx/2; x <= nx; x += stepx)
942  {
943  double z;
944 
945  check( z = sample_background(image,
946  x, y,
947  radius_x, radius_y,
948  nx, ny,
949  BM_METHOD),
950  "Error sampling background (x, y) = (%d, %d)", x, y);
951 
952  cpl_table_set_int (t, "X" , row, x);
953  cpl_table_set_int (t, "Y" , row, y);
954  cpl_table_set_double(t, "Z" , row, z);
955  row++;
956  }
957  }
958  cpl_table_set_size(t, row);
959  }
960 
961  /* Sampling done. Fit poly. */
962 
963  total_clipped = 0;
964  {
965  cpl_size n_clipped;
966  cpl_size deg_xy=(DEGX + 1)*(DEGY + 1);
967  do {
968  assure( cpl_table_get_nrow(t) > (DEGX + 1)*(DEGY + 1),
969  CPL_ERROR_ILLEGAL_OUTPUT,
970  "Too few sample points available (%" CPL_SIZE_FORMAT " point(s)) to make the fit "
971  "(more than %" CPL_SIZE_FORMAT " points needed). "
972  "Increase number of sample points or increase kappa",
973  cpl_table_get_nrow(t), deg_xy);
974 
975  /* Fit, calculate Zfit */
976  uves_polynomial_delete(&background);
977  check( background = uves_polynomial_regression_2d(
978  t, "X", "Y", "Z", NULL,
979  DEGX, DEGY, "Zfit", NULL, NULL, &mse,
980  NULL, NULL, -1, -1),
981  "Error fitting polynomial");
982 
983  /* Residual := Z - Zfit */
984  cpl_table_duplicate_column(t, "Residual", t, "Z");
985  cpl_table_subtract_columns(t, "Residual", "Zfit");
986 
987  /* Compute residuals w.r.t. median of Z
988  (i.e. subtract median(residual) from all residuals),
989  then get stdev based on this new mean/median value.
990  This is to make kappa sigma clipping more robust */
991 
992  cpl_table_subtract_scalar(t, "Residual",
993  cpl_table_get_column_median(t, "Residual"));
994  rmse = cpl_table_get_column_stdev(t, "Residual");
995 
996  /* One-sided kappa-sigma clipping */
997  if (KAPPA > 0)
998  {
999  check( n_clipped = uves_select_table_rows(
1000  t, "Residual", CPL_GREATER_THAN, KAPPA * rmse),
1001  "Error selecting rows");
1002  }
1003  else
1004  {
1005  n_clipped = 0;
1006  }
1007 
1008  total_clipped += n_clipped;
1009 
1010  uves_msg_debug("RMS = %f. %" CPL_SIZE_FORMAT " of %" CPL_SIZE_FORMAT " points rejected in kappa-sigma clipping",
1011  rmse, n_clipped, cpl_table_get_nrow(t));
1012 
1013  cpl_table_erase_selected(t);
1014 
1015  if (n_clipped > 0)
1016  {
1017  cpl_table_erase_column(t, "Zfit");
1018  cpl_table_erase_column(t, "Residual");
1019  }
1020 
1021  } while (n_clipped > 0);
1022  }
1023 
1024  /* Try to do some quality checking of the background subtraction.
1025  The number of rejected points (the signal) is often around 10-20 % */
1026  {
1027  double percentage =
1028  100.0 * ( (double)total_clipped ) / (total_clipped + cpl_table_get_nrow(t));
1029 
1030  if (KAPPA > 0) {
1031  uves_msg("%" CPL_SIZE_FORMAT " of %" CPL_SIZE_FORMAT " points (%.2f %%) were rejected in "
1032  "kappa-sigma clipping. RMS = %.2f ADU",
1033  total_clipped,
1034  cpl_table_get_nrow(t) + total_clipped,
1035  percentage,
1036  sqrt(mse));
1037  }
1038 
1039  /* For grid sampling: */
1040  if (orders == NULL && order_locations == NULL)
1041  {
1042  if (total_clipped == 0)
1043  {
1044  uves_msg_warning("No points rejected during background "
1045  "estimation. Background subtraction is "
1046  "uncertain. Try to decrease KAPPA "
1047  "(current value is %f)", KAPPA);
1048  }
1049  if (percentage > 40)
1050  {
1051  uves_msg_warning("%f %% of the sample points were "
1052  "rejected during "
1053  "background estimation", percentage);
1054  }
1055  }
1056  }
1057 
1058  check( subtract_background(image, NULL, background),
1059  "Error subtracting background polynomial");
1060  } /* BM_METHOD wasn't 'no' */
1061 
1062  cleanup:
1063  uves_free_table(&t);
1064  uves_polynomial_delete(&background);
1065 
1066  return cpl_error_get_code();
1067 }
1068 
1069 /*----------------------------------------------------------------------------*/
1083 /*----------------------------------------------------------------------------*/
1084 /* Recipe parameter creation code for this function
1085 / * Backsmoothx, Backsmoothy * /
1086  uves_parameter_new_range(p, uves_orderpos.preproc.backsmoothx,
1087  CPL_TYPE_INT,
1088  "Radius of window used for average filtering in the "
1089  "background subtraction (mode=smooth) step",
1090  uves_orderpos.preproc,
1091  5, 0, INT_MAX);
1092  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "backsmoothx");
1093  cpl_parameterlist_append(recipe->parameters, p);
1094 
1095  uves_parameter_new_range(p, uves_orderpos.preproc.backsmoothy,
1096  CPL_TYPE_INT,
1097  "Radius of window used for average filtering in the "
1098  "background subtraction (mode=smooth) step",
1099  uves_orderpos.preproc,
1100  30, 0, INT_MAX);
1101  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "backsmoothy");
1102  cpl_parameterlist_append(recipe->parameters, p);
1103 
1104 / * Backsmoothiter * /
1105  uves_parameter_new_range(p, uves_orderpos.preproc.backsmoothiter,
1106  CPL_TYPE_INT,
1107  "Number of iterations when estimating the background "
1108  "(mode=smooth)",
1109  uves_orderpos.preproc,
1110  10, 1, INT_MAX);
1111  cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "backsmoothiter");
1112  cpl_parameterlist_append(recipe->parameters, p);
1113 */
1114 cpl_error_code
1115 uves_backsub_smooth(cpl_image *image, int RADX, int RADY, int ITER)
1116 {
1117  cpl_image *background = NULL;
1118  int i;
1119 
1120  assure( RADX >= 0 && RADY >= 0, CPL_ERROR_ILLEGAL_INPUT,
1121  "Negative radius ((%d)x(%d))", RADX, RADY);
1122  assure( ITER >= 1, CPL_ERROR_ILLEGAL_INPUT,
1123  "Non-positive number of iterations (%d)", ITER);
1124 
1125  /* First estimate background */
1126  background = cpl_image_duplicate(image);
1127 
1128  for (i = 0; i < ITER; i++) {
1129  //uves_msg_debug("i=%d,%d ...",i, ITER);
1130  uves_msg("i = %d", i);
1131  check( lower_to_average(background,
1132  RADX, RADY), "Error smoothing image");
1133  }
1134 
1135  /* Then subtract background */
1136  check( cpl_image_subtract(image, background), "Could not subtract background image");
1137 
1138  cleanup:
1139  uves_free_image(&background);
1140 
1141  return cpl_error_get_code();
1142 }
1143 
1144 /*----------------------------------------------------------------------------*/
1163 /*----------------------------------------------------------------------------*/
1164 
1165 static double
1166 sample_background(const cpl_image *image, int x0, double y_0,
1167  int radius_x, int radius_y, int nx, int ny,
1168  background_measure_method BM_METHOD)
1169 {
1170  double result = 0;
1171  /* Use a table to calculate the median. Invalid rows are ignored */
1172  cpl_table *temp = NULL;
1173  bool found_good = false;
1174  int row;
1175  int x, y;
1176 
1177  check(
1178  (temp = cpl_table_new( (2*radius_x + 1) * (2*radius_y + 1) ),
1179  row = 0,
1180  cpl_table_new_column(temp, "Flux", CPL_TYPE_DOUBLE)),
1181  "Error allocating table");
1182 
1183  for(y = y_0 - radius_y; y <= y_0 + radius_y; y++)
1184  {
1185  for (x = x0 - radius_x; x <= x0 + radius_x; x++)
1186  {
1187  if (1 <= x && x <= nx &&
1188  1 <= y && y <= ny)
1189  {
1190  int pis_rejected;
1191  double flux = cpl_image_get(image, x, y, &pis_rejected);
1192  if( !pis_rejected )
1193  {
1194  cpl_table_set(temp, "Flux", row, flux);
1195  found_good = true;
1196  }
1197  else
1198  {
1199  cpl_table_set_invalid(temp, "Flux", row);
1200  }
1201  }
1202  else
1203  {
1204  cpl_table_set_invalid(temp, "Flux", row);
1205  }
1206 
1207  row++;
1208  }
1209  }
1210 
1211  assure( found_good, CPL_ERROR_ILLEGAL_INPUT, "No valid pixels in sample window");
1212 
1213  if (BM_METHOD == BM_MEDIAN)
1214  {
1215  result = cpl_table_get_column_median(temp, "Flux");
1216  }
1217  else if (BM_METHOD == BM_MINIMUM)
1218  {
1219  result = cpl_table_get_column_min(temp, "Flux");
1220  }
1221  else
1222  {
1223  assure( false, CPL_ERROR_UNSUPPORTED_MODE,
1224  "Unsupported background sample method: %d", BM_METHOD);
1225  }
1226 
1227  cleanup:
1228  uves_free_table(&temp);
1229  return result;
1230 }
1231 
1232 /*----------------------------------------------------------------------------*/
1241 /*----------------------------------------------------------------------------*/
1242 static int
1243 first_order(const polynomial *order_locations, int nx)
1244 {
1245  int result;
1246 
1247  result = 0;
1248  while (uves_polynomial_evaluate_2d(order_locations, 1 , result + 0.5) < 1 ||
1249  uves_polynomial_evaluate_2d(order_locations, nx, result + 0.5) < 1 )
1250  {
1251  result++;
1252  }
1253 
1254  while (uves_polynomial_evaluate_2d(order_locations, 1 , result - 0.5) >= 1 ||
1255  uves_polynomial_evaluate_2d(order_locations, nx, result - 0.5) >= 1 )
1256  {
1257  result -= 1;
1258 
1259  /* Fail cleanly even if 'order_locations' is corrupted */
1260  assure( result > -100000,
1261  CPL_ERROR_CONTINUE,
1262  "Invalid polynomial: p(x=1, order=%d) = %f p(x=%d, order=%d) = %f",
1263  result, uves_polynomial_evaluate_2d(order_locations, 1.0, result),
1264  nx, result, uves_polynomial_evaluate_2d(order_locations, nx, result));
1265  }
1266 
1267  cleanup:
1268  return result;
1269 }
1270 
1271 
1272 /*----------------------------------------------------------------------------*/
1282 /*----------------------------------------------------------------------------*/
1283 static int
1284 last_order(const polynomial *order_locations, int nx, int ny)
1285 {
1286  int result;
1287 
1288  result = 0;
1289  while (uves_polynomial_evaluate_2d(order_locations, 1 , result - 0.5) > ny ||
1290  uves_polynomial_evaluate_2d(order_locations, nx, result - 0.5) > ny )
1291  {
1292  result--;
1293  }
1294 
1295  while (uves_polynomial_evaluate_2d(order_locations, 1 , result + 1.5) <= ny ||
1296  uves_polynomial_evaluate_2d(order_locations, nx, result + 1.5) <= ny )
1297  {
1298  result += 1;
1299 
1300  /* Fail cleanly even if 'order_locations' is corrupted */
1301  assure( result < 100000,
1302  CPL_ERROR_CONTINUE,
1303  "Invalid polynomial: p(x=1, order=%d) = %f p(x=%d, order=%d) = %f",
1304  result, uves_polynomial_evaluate_2d(order_locations, 1.0, result),
1305  nx, result, uves_polynomial_evaluate_2d(order_locations, nx, result));
1306  }
1307 
1308  cleanup:
1309  return result;
1310 }
1311 
1312 /*----------------------------------------------------------------------------*/
1324 /*----------------------------------------------------------------------------*/
1325 static cpl_error_code
1326 lower_to_average(cpl_image *image, int RADX, int RADY)
1327 {
1328  cpl_image *average = NULL;
1329  double *image_data = NULL;
1330  double *average_data = NULL;
1331  int nx, ny;
1332  int x, y;
1333 
1334  passure( image != NULL, "Null image");
1335  nx = cpl_image_get_size_x(image);
1336  ny = cpl_image_get_size_y(image);
1337 
1338  /* Create smoothed image */
1339  uves_msg("Filtering...");
1340  check( average = cpl_image_duplicate(image), "Error copying image");
1341  check( uves_filter_image_average(average, RADX, RADY), "Error applying average filter");
1342  uves_msg("done");
1343 
1344  image_data = cpl_image_get_data(image);
1345  average_data = cpl_image_get_data(average);
1346  uves_msg("Lowering...");
1347  for (y = 0; y < ny; y++)
1348  {
1349  for (x = 0; x < nx; x++)
1350  {
1351  if (image_data[x + y*nx] > average_data[x + y*nx])
1352  {
1353  image_data[x + y*nx] = average_data[x + y*nx];
1354  }
1355  }
1356  }
1357  uves_msg("done");
1358 
1359  cleanup:
1360  uves_free_image(&average);
1361 
1362  return cpl_error_get_code();
1363 }
1364 
1365 /*----------------------------------------------------------------------------*/
1377 /*----------------------------------------------------------------------------*/
1378 
1379 static cpl_error_code
1380 subtract_background(cpl_image *image, cpl_image *background_im,
1381  const polynomial *background_pol)
1382 {
1383  int nx, ny;
1384  int x, y;
1385 
1386  double *image_data;
1387  double *background_data = NULL;
1388 
1389  passure(image != NULL, " ");
1390  /* Exactly one of 'background_im' and 'background_pol' must be non-NULL */
1391  passure((background_im == NULL) != (background_pol == NULL), " ");
1392 
1393  /* For efficiency, don't call cpl_image_get() */
1394  /* The following check is too strict. It can be avoided to solve PIPE-4893
1395  assure(cpl_image_count_rejected(image) == 0,
1396  CPL_ERROR_UNSUPPORTED_MODE, "Input image contains bad pixels");
1397  */
1398  assure(cpl_image_get_type(image) == CPL_TYPE_DOUBLE,
1399  CPL_ERROR_UNSUPPORTED_MODE,
1400  "Input image is of type %s. double expected",
1401  uves_tostring_cpl_type(cpl_image_get_type(image)));
1402 
1403  if (background_im != NULL)
1404  {
1405  assure(cpl_image_count_rejected(background_im) == 0,
1406  CPL_ERROR_UNSUPPORTED_MODE, "Background image contains bad pixels");
1407  assure(cpl_image_get_type(background_im) == CPL_TYPE_DOUBLE,
1408  CPL_ERROR_UNSUPPORTED_MODE,
1409  "Background image is of type %s. double expected",
1410  uves_tostring_cpl_type(cpl_image_get_type(background_im)));
1411  }
1412 
1413  image_data = cpl_image_get_data_double(image);
1414  if (background_im != NULL)
1415  {
1416  background_data = cpl_image_get_data_double(background_im);
1417  }
1418 
1419  nx = cpl_image_get_size_x(image);
1420  ny = cpl_image_get_size_y(image);
1421 
1422  for (y = 1; y <= ny; y++)
1423  {
1424  for (x = 1; x <= nx; x++)
1425  {
1426  double back;
1427  double flux, new_flux;
1428 
1429  if (background_im != NULL)
1430  {
1431  /* Slow: back = cpl_image_get(background_im, x, y, &pis_rejected); */
1432  back = background_data[(x-1) + (y-1) * nx];
1433  }
1434  else
1435  {
1436  /* Evaluate at (x,y) */
1437  back = uves_polynomial_evaluate_2d(background_pol,
1438  x,
1439  y);
1440  }
1441 
1442  /* Slow: flux = cpl_image_get(image, x, y, &pis_rejected); */
1443  flux = image_data[(x-1) + (y-1) * nx];
1444 
1445 /* Exclude these sanity checks for backwards compatibility */
1446 #if 0
1447  /* Make sure the estimated background is between zero and flux-value */
1448  if (back < 0)
1449  {
1450  back = 0.0;
1451  }
1452  if (back > flux)
1453  {
1454  back = flux;
1455  }
1456 
1457  /* Then subtract the background.
1458  * Pixel flux may be negative. Make sure the result is non-negative.
1459  */
1460  new_flux = uves_max_double(0, flux - back);
1461 #else
1462  new_flux = flux-back;
1463 #endif
1464 
1465  /* Slow: cpl_image_set(image, x, y, new_flux); */
1466  image_data[(x-1) + (y-1) * nx] = new_flux;
1467 
1468  if (background_im != NULL)
1469  {
1470  /* Slow: cpl_image_set(background_im, x, y, flux - new_flux); */
1471  background_data[(x-1) + (y-1) * nx] = flux - new_flux;
1472  }
1473  }
1474  }/* for each pixel... */
1475 
1476  cleanup:
1477  return cpl_error_get_code();
1478 }
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:3848
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:2870
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