MUSE Pipeline Reference Manual  2.1.1
muse_twilight.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set sw=2 sts=2 et cin: */
3 /*
4  * This file is part of the MUSE Instrument Pipeline
5  * Copyright (C) 2014-2015 European Southern Observatory
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 /*----------------------------------------------------------------------------*
27  * Includes *
28  *----------------------------------------------------------------------------*/
29 #include <string.h>
30 
31 #include <muse.h>
32 #include "muse_twilight_z.h"
33 
34 /*---------------------------------------------------------------------------*
35  * Functions code *
36  *---------------------------------------------------------------------------*/
37 
38 /*---------------------------------------------------------------------------*/
45 /*---------------------------------------------------------------------------*/
46 static cpl_error_code
47 muse_twilight_qc_header(muse_image *aImage, muse_imagelist *aList)
48 {
49  cpl_ensure_code(aImage && aList, CPL_ERROR_NULL_INPUT);
50  unsigned char ifu = muse_utils_get_ifu(aImage->header);
51  cpl_msg_debug(__func__, "Adding QC keywords for IFU %hhu", ifu);
52 
53  unsigned stats = CPL_STATS_MEDIAN | CPL_STATS_MEAN | CPL_STATS_STDEV
54  | CPL_STATS_MIN | CPL_STATS_MAX;
55 
56  /* write the image statistics for input skyflat field exposures */
57  unsigned int k;
58  for (k = 0; k < muse_imagelist_get_size(aList); k++) {
59  char *keyword = cpl_sprintf(QC_TWILIGHTm_PREFIXi, ifu, k+1);
61  aImage->header, keyword, stats);
62  cpl_free(keyword);
63  keyword = cpl_sprintf(QC_TWILIGHTm_PREFIXi" "QC_BASIC_NSATURATED, ifu, k+1);
64  int nsaturated = cpl_propertylist_get_int(muse_imagelist_get(aList, k)->header,
65  MUSE_HDR_TMP_NSAT);
66  cpl_propertylist_update_int(aImage->header, keyword, nsaturated);
67  cpl_free(keyword);
68  } /* for k (all images in list) */
69 
70  /* properties of the resulting master frame */
71  char *kw = cpl_sprintf(QC_TWILIGHTm_MASTER_PREFIX, ifu);
72  stats |= CPL_STATS_FLUX;
73  muse_basicproc_stats_append_header(aImage->data, aImage->header, kw, stats);
74  cpl_free(kw);
75  return CPL_ERROR_NONE;
76 } /* muse_twilight_qc_header() */
77 
78 /*----------------------------------------------------------------------------*/
125 /*----------------------------------------------------------------------------*/
126 static muse_datacube *
127 muse_twilight_reconstruct(muse_processing *aProcessing,
128  muse_twilight_params_t *aParams,
129  muse_basicproc_params *aBPars,
130  muse_combinepar *aCPars,
131  muse_resampling_params *aRPars)
132 {
133  cpl_ensure(aProcessing && aParams, CPL_ERROR_NULL_INPUT, NULL);
134  double lmin = aParams->lambdamin,
135  lmax = aParams->lambdamax;
136  cpl_ensure(lmax > lmin, CPL_ERROR_ILLEGAL_INPUT, NULL);
137  /* the geometry table is common for all IFUs */
138  cpl_table *geo = muse_processing_load_ctable(aProcessing, MUSE_TAG_GEOMETRY_TABLE, 0);
139  cpl_ensure(geo, CPL_ERROR_FILE_NOT_FOUND, NULL);
140 
141  muse_pixtable **pts = cpl_calloc(kMuseNumIFUs, sizeof(muse_pixtable));
142  unsigned char nifu;
143  #pragma omp parallel for default(none) /* as req. by Ralf */ \
144  shared(aBPars, aCPars, aProcessing, geo, pts)
145  for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
146  cpl_table *trace = muse_processing_load_ctable(aProcessing, MUSE_TAG_TRACE_TABLE, nifu),
147  *wavecal = muse_processing_load_ctable(aProcessing, MUSE_TAG_WAVECAL_TABLE, nifu);
148  if (!trace || !wavecal) {
149  cpl_msg_warning(__func__, "Calibrations could not be loaded for IFU "
150  "%2hhu:%s%s", nifu, !trace ? " "MUSE_TAG_TRACE_TABLE : "",
151  !wavecal ? " "MUSE_TAG_WAVECAL_TABLE : "");
152  cpl_table_delete(trace);
153  cpl_table_delete(wavecal);
154  continue; /* skip this IFU */
155  }
156  /* load and pre-process all raw files */
157  cpl_msg_debug(__func__, "load raw files of IFU %2hhu", nifu);
158  muse_imagelist *images = muse_basicproc_load(aProcessing, nifu, aBPars);
159  if (!images) {
160  cpl_msg_warning(__func__, "Basic processing failed for IFU %2hhu: %s",
161  nifu, cpl_error_get_message());
162  cpl_table_delete(trace);
163  cpl_table_delete(wavecal);
164  continue;
165  }
166  /* search for ILLUM exposure(s) in input list, *
167  * prepare them and remove all from the list */
168  cpl_table *tattached = muse_basicproc_get_illum(images, trace, wavecal, geo);
169  /* combine the raw exposures into a master image */
170  cpl_msg_debug(__func__, "combine raw files of IFU %2hhu", nifu);
171  muse_image *masterimage = muse_combine_images(aCPars, images);
172  muse_twilight_qc_header(masterimage, images);
173  muse_imagelist_delete(images);
174  if (!masterimage) {
175  cpl_msg_warning(__func__, "Combining individual images failed for IFU %2hhu: %s",
176  nifu, cpl_error_get_message());
177  cpl_table_delete(tattached);
178  cpl_table_delete(trace);
179  cpl_table_delete(wavecal);
180  continue;
181  }
182 
183  /* apply bad-pixel interpolation on this image, so that the *
184  * per-IFU integrated fluxes are independent of bad pixels */
185  int nbad = muse_quality_image_reject_using_dq(masterimage->data,
186  masterimage->dq,
187  masterimage->stat);
188  cpl_detector_interpolate_rejected(masterimage->data);
189  /* doing this for the variance is ugly but we cannot do much else */
190  cpl_detector_interpolate_rejected(masterimage->stat);
191  /* still leave them marked in the DQ extension */
192  cpl_msg_debug(__func__, "interpolated over %d bad pixels in IFU %2hhu",
193  nbad, nifu);
194 
195  /* create pixel table for this master image */
196  cpl_msg_debug(__func__, "create pixel table for IFU %2hhu", nifu);
197  pts[nifu - 1] = muse_pixtable_create(masterimage, trace, wavecal, geo);
198  cpl_table_delete(trace);
199  cpl_table_delete(wavecal);
200  /* propagate the QC parameters from the image header */
201  if (pts[nifu - 1]) {
202  cpl_propertylist_copy_property_regexp(pts[nifu - 1]->header,
203  masterimage->header,
204  QC_TWILIGHT_REGEXP, 0);
205  if (tattached) { /* test before, to not throw an error */
206  muse_basicproc_apply_illum(pts[nifu - 1], tattached);
207  } /* if attached flat given */
208  } /* if pixel table created */
209  muse_image_delete(masterimage);
210  cpl_table_delete(tattached);
211 
212  /* in AO mode we also need to mask the NaD notch filter range */
213  if (muse_pfits_get_mode(pts[nifu - 1]->header) > MUSE_MODE_WFM_NONAO_N) {
214  muse_basicproc_mask_notch_filter(pts[nifu - 1], nifu);
215  } /* if AO mode */
216  } /* for nifu (all IFUs) */
217  cpl_table_delete(geo);
218 
219  muse_pixtable *pt = pts[0];
220  muse_pixtable_restrict_wavelength(pt, lmin, lmax);
221  if (muse_pixtable_get_nrow(pt) < 1) {
222  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT,
223  "After cutting to %.2f..%.2f Angstrom in wavelength "
224  "no pixels for cube reconstruction are left!", lmin,
225  lmax);
226  for (nifu = 1; nifu <= kMuseNumIFUs; nifu++) {
227  muse_pixtable_delete(pts[nifu - 1]);
228  } /* for nifu (all IFUs) */
229  cpl_free(pts);
230  return NULL;
231  } /* if */
232 
233  cpl_array *scales = NULL;
234  if (getenv("MUSE_TWILIGHT_SCALES")) {
235  scales = muse_cplarray_new_from_delimited_string(getenv("MUSE_TWILIGHT_SCALES"), ",");
236  double scale = atof(cpl_array_get_string(scales, 0));
237  cpl_table_divide_scalar(pt->table, MUSE_PIXTABLE_DATA, scale);
238  cpl_table_divide_scalar(pt->table, MUSE_PIXTABLE_STAT, scale*scale);
239  int nscales = cpl_array_get_size(scales);
240  if (nscales != kMuseNumIFUs) {
241  cpl_msg_warning(__func__, "%d external scales found instead of %d! Not "
242  "using them.", nscales, kMuseNumIFUs);
243  cpl_array_delete(scales);
244  scales = NULL;
245  } else {
246  cpl_msg_info(__func__, "Using %d external scales.", nscales);
247  }
248 #if 1
249  cpl_array_dump(scales, 0, 30, stdout);
250  fflush(stdout);
251 #endif
252  } /* if */
253 
254  double fluxref = cpl_table_get_column_mean(pt->table, MUSE_PIXTABLE_DATA)
256  cpl_msg_debug(__func__, "pixel table of IFU 1, fluxref = %e, %"CPL_SIZE_FORMAT
257  " rows", fluxref, muse_pixtable_get_nrow(pt));
258  /* merge all pixel tables a la muse_pixtable_load_merge_channels() *
259  * but first restricting the wavelength range and with flux *
260  * correction using the data we have in the table */
261  cpl_propertylist_append_double(pt->header, MUSE_HDR_FLAT_FLUX_SKY"1", fluxref);
262  char *kw = cpl_sprintf(QC_TWILIGHTm_INTFLUX, (unsigned char)1);
263  cpl_propertylist_append_float(pt->header, kw, fluxref);
264  cpl_free(kw);
265  for (nifu = 2; nifu <= kMuseNumIFUs; nifu++) {
266  if (!pts[nifu - 1]) {
267  continue;
268  } /* if */
269  muse_pixtable_restrict_wavelength(pts[nifu - 1], lmin, lmax);
270  if (muse_pixtable_get_nrow(pts[nifu - 1]) < 1) {
271  continue;
272  } /* if */
273  double flux = cpl_table_get_column_mean(pts[nifu - 1]->table, MUSE_PIXTABLE_DATA)
274  * muse_pixtable_get_nrow(pts[nifu - 1]),
275  scale = 1.;
276  if (scales) {
277  scale = atof(cpl_array_get_string(scales, nifu - 1));
278  /* also recomputed the flux that is then saved in the header */
279  flux = scale * fluxref;
280  } else {
281  scale = flux / fluxref;
282  } /* else */
283  kw = cpl_sprintf(MUSE_HDR_FLAT_FLUX_SKY"%hhu", nifu);
284  cpl_propertylist_append_double(pt->header, kw, flux);
285  cpl_free(kw);
286  cpl_msg_debug(__func__, "merging pixel table of IFU %2hhu, flux = %e, scale = %f",
287  nifu, flux, scale);
288  cpl_table_divide_scalar(pts[nifu - 1]->table, MUSE_PIXTABLE_DATA, scale);
289  cpl_table_divide_scalar(pts[nifu - 1]->table, MUSE_PIXTABLE_STAT, scale*scale);
290  cpl_table_insert(pt->table, pts[nifu - 1]->table, muse_pixtable_get_nrow(pt));
291  /* again, propagate the QC parameters to the merged pixel table */
292  cpl_propertylist_copy_property_regexp(pt->header, pts[nifu - 1]->header,
293  QC_TWILIGHT_REGEXP, 0);
294  /* add the pixel-table based flux as QC as well */
295  kw = cpl_sprintf(QC_TWILIGHTm_INTFLUX, nifu);
296  cpl_propertylist_append_float(pt->header, kw, flux);
297  cpl_free(kw);
298  muse_pixtable_delete(pts[nifu - 1]);
299  } /* for nifu (all IFUs) */
300  cpl_free(pts);
301  /* reset limits in the header, necessary due to manual operation on table data */
303  cpl_array_delete(scales);
304 
305  /* now just reconstruct the cube and collapse over the full range */
306  muse_datacube *cube = muse_resampling_cube(pt, aRPars, NULL);
308  if (!cube) {
309  return NULL;
310  }
311  /* also create a corresponding white-light image */
312  muse_image *white = muse_datacube_collapse(cube, NULL);
313  cube->recimages = muse_imagelist_new();
314  cube->recnames = cpl_array_new(1, CPL_TYPE_STRING);
315  muse_imagelist_set(cube->recimages, white, 0);
316  cpl_array_set_string(cube->recnames, 0, "white");
317 
318  /* want NANs instead of zeros, and save space */
320 
321  return cube;
322 } /* muse_twilight_reconstruct() */
323 
324 /*----------------------------------------------------------------------------*/
348 /*----------------------------------------------------------------------------*/
349 static cpl_image *
350 muse_twilight_image_normalize_and_fit(muse_image *aImage,
351  unsigned int aXOrder,
352  unsigned int aYOrder,
353  const cpl_mask *aBPM,
354  const cpl_mask *aVign)
355 {
356  cpl_ensure(aImage && aImage->data, CPL_ERROR_NULL_INPUT, NULL);
357 
358  /* reject values from the white-light mask, and extra infinite pixels */
359  if (aBPM) {
360  cpl_image_reject_from_mask(aImage->data, aBPM);
361  }
362  cpl_image_reject_value(aImage->data, CPL_VALUE_NOTFINITE | CPL_VALUE_ZERO);
363 
364  /* filter the image to remove outliers (like strongly deviant slices) */
365  /* use a 5x7 median filter */
366  cpl_image *filtered = cpl_image_duplicate(aImage->data);
367  cpl_mask *mask = cpl_mask_new(5, 7);
368  cpl_mask_not(mask);
369  cpl_errorstate prestate = cpl_errorstate_get();
370  cpl_image_filter_mask(filtered, aImage->data, mask, CPL_FILTER_MEDIAN,
371  CPL_BORDER_FILTER);
372  cpl_mask_delete(mask);
373  if (!cpl_errorstate_is_equal(prestate)) {
374  cpl_msg_warning(__func__, "filtering cube plane failed: %s",
375  cpl_error_get_message());
376  }
377 #if 0
378  /* now also use a wide (~10 pixel FWHM) 2D Gaussian to smooth the output even more */
379  cpl_image *filtered2 = cpl_image_duplicate(filtered);
380  cpl_matrix *gauss = muse_matrix_new_gaussian_2d(7, 7, 10. * CPL_MATH_SIG_FWHM);
381  cpl_image_filter(filtered2, filtered, gauss, CPL_FILTER_LINEAR,
382  CPL_BORDER_FILTER);
383  cpl_matrix_delete(gauss);
384  if (!cpl_errorstate_is_equal(prestate)) {
385  cpl_msg_warning(__func__, "filtering 2 failed: %s", cpl_error_get_message());
386  } else {
387  cpl_image_reject_from_mask(filtered2, bpm2);
388  cpl_image_fill_rejected(filtered2, 1.);
389  muse_processing_save_cimage(aProcessing, -1, filtered2, wmuse->header,
390  "TWILIGHT_IMAGE2_SMOOTHED" /* XXX */);
391  cpl_detector_interpolate_rejected(filtered2);
392  muse_processing_save_cimage(aProcessing, -1, filtered2, wmuse->header,
393  "TWILIGHT_IMAGE2_INTERPOLATED" /* XXX */);
394  }
395  cpl_image_delete(filtered2);
396 #endif
397 
398  /* compute the mean of the good pixels and use that to normalize the image */
399  double mean = cpl_image_get_mean(filtered);
400  cpl_msg_debug(__func__, "mean = %f", mean);
401  cpl_image_multiply_scalar(filtered, 1. / mean);
402 
403  /* mask out the vignetted area (if given), before applying the fit */
404  cpl_mask *bpm = cpl_image_get_bpm(filtered);
405  if (aVign) {
406  cpl_mask_or(bpm, aVign);
407  }
408  cpl_image *fit = muse_utils_image_fit_polynomial(filtered,
409  aXOrder, aYOrder);
410  /* normalize again */
411  mean = cpl_image_get_mean(fit);
412  cpl_msg_debug(__func__, "mean(fit) = %f", mean);
413  cpl_image_multiply_scalar(fit, 1. / mean);
414  cpl_image_delete(filtered);
415  return fit;
416 } /* muse_twilight_image_normalize_and_fit() */
417 
418 /*---------------------------------------------------------------------------*/
425 /*---------------------------------------------------------------------------*/
426 int
427 muse_twilight_compute(muse_processing *aProcessing,
428  muse_twilight_params_t *aParams)
429 {
430  muse_datacube *cube = NULL,
431  *cubefit = cpl_calloc(1, sizeof(muse_datacube));
432  cubefit->data = cpl_imagelist_new();
433  if (getenv("MUSE_TWILIGHT_SKIP")) {
434  char *fn = getenv("MUSE_TWILIGHT_SKIP");
435  cube = muse_datacube_load(fn);
436  cpl_msg_warning(__func__, "Skipping active: loaded previously generated "
437  "cube from \"%s\"", fn);
438  /* ensure that the first collapse image is "white" */
439  if (!cube->recimages) { /* no reconstructed images at all */
440  /* create the white-light image */
441  cube->recimages = muse_imagelist_new();
442  cube->recnames = cpl_array_new(1, CPL_TYPE_STRING);
443  muse_image *white = muse_datacube_collapse(cube, NULL);
444  muse_imagelist_set(cube->recimages, white, 0);
445  cpl_array_set_string(cube->recnames, 0, "white");
446  } else if (!cube->recnames ||
447  (cube->recnames &&
448  strncmp("white", cpl_array_get_string(cube->recnames, 0), 6))) {
449  /* replace first reconstructed image with white-light */
450  muse_image *white = muse_datacube_collapse(cube, NULL);
451  muse_imagelist_set(cube->recimages, white, 0);
452  cpl_array_set_string(cube->recnames, 0, "white");
453  } /* else */
454 
455  /* add something to the usedframes in aProcessing for saving below to work */
456  cpl_frameset *frames = muse_frameset_find_tags(aProcessing->inframes,
457  aProcessing->intags, -1, 0);
458  cpl_size iframe, nframes = cpl_frameset_get_size(frames);
459  for (iframe = 0; iframe < nframes; iframe++) {
460  cpl_frame *frame = cpl_frameset_get_position(frames, iframe);
461  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_RAW, 1);
462  } /* for iframe (all raw inputs) */
463  cpl_frameset_delete(frames);
464  cpl_msg_warning(__func__, "Skipping active: faked used frames using only "
465  "raw inputs:");
466  cpl_frameset_dump(aProcessing->usedframes, stdout);
467  fflush(stdout);
468  cubefit->header = cpl_propertylist_duplicate(cube->header);
469  } /* if skip */
470 
471  cpl_error_code rc = CPL_ERROR_NONE;
472  if (!cube) { /* skipping did not happen */
474  "muse.muse_twilight");
475  muse_combinepar *cpars = muse_combinepar_new(aProcessing->parameters,
476  "muse.muse_twilight");
477  muse_resampling_type resample
480  rp->crtype = muse_postproc_get_cr_type(aParams->crtype_s);
481  rp->crsigma = aParams->crsigma;
482  rp->dlambda = aParams->dlambda;
483  rp->pfx = 1.;
484  rp->pfy = 1.;
485  rp->pfl = 1.;
486  cube = muse_twilight_reconstruct(aProcessing, aParams, bpars, cpars, rp);
488  muse_combinepar_delete(cpars);
490  if (!cube) {
491  muse_datacube_delete(cubefit);
492  return -1;
493  }
494  /* duplicate the cube header and its WCS before saving destroys the WCS */
495  cubefit->header = cpl_propertylist_duplicate(cube->header);
496  rc = muse_processing_save_cube(aProcessing, -1, cube,
497  MUSE_TAG_CUBE_SKYFLAT,
499  /* the QC parameters are saved with the unprocessed cube, now delete them */
500  cpl_propertylist_erase_regexp(cubefit->header, QC_TWILIGHT_REGEXP, 0);
501  } /* if no skipping */
502 
503  /* now mark empty areas in the white-light image as bad; just *
504  * access the first reconstructed image, since the white-light *
505  * image is the only one created by muse_twilight_reconstruct() */
506  muse_image *wmuse = muse_imagelist_get(cube->recimages, 0);
507  if (!wmuse) {
508  muse_datacube_delete(cube);
509  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_OUTPUT,
510  "White-light image is missing!");
511  muse_datacube_delete(cubefit);
512  return -1;
513  }
514  cpl_image *white = cpl_image_duplicate(wmuse->data);
515  cpl_image_reject_value(white, CPL_VALUE_NOTFINITE | CPL_VALUE_ZERO);
516  cpl_mask *bpm = cpl_image_get_bpm(white),
517  *bpm2 = cpl_mask_duplicate(bpm);
518  /* force a rejection of the vertical border pixels */
519  cpl_mask *border = cpl_mask_new(1, cpl_mask_get_size_y(bpm2));
520  cpl_mask_not(border);
521  cpl_mask_copy(bpm2, border, 1, 1);
522  cpl_mask_copy(bpm2, border, cpl_mask_get_size_x(bpm2), 1);
523  cpl_mask_delete(border);
524  /* use dilation to make the mask wider, i.e. to *
525  * exclude more of the (usually bad) border */
526  cpl_mask *kernel = cpl_mask_new(5, 1);
527  cpl_mask_not(kernel);
528  cpl_mask_filter(bpm2, bpm, kernel, CPL_FILTER_DILATION, CPL_BORDER_NOP);
529  cpl_mask_delete(kernel);
530  bpm = cpl_image_set_bpm(white, bpm2);
531  cpl_mask_delete(bpm);
532  bpm = bpm2; /* easier handle to remember */
533 
534  /* apply a mask, if one was given, on top of the one that the image already has */
535  muse_mask *maskimage = muse_processing_load_mask(aProcessing,
536  MUSE_TAG_VIGN_MASK);
537  /* adapt the mask to the size of the actual cube */
538  if (maskimage) {
539  cpl_mask *mask = muse_cplmask_adapt_to_image(maskimage->mask,
540  cpl_imagelist_get(cube->data, 0));
541  cpl_mask_delete(maskimage->mask);
542  maskimage->mask = mask;
543  }
544 #if 0 // only for smoothing in wavelength direction (see below)
545  /* also compute the wavelength of each plane */
546  double crpix3 = muse_pfits_get_crpix(cube->header, 3),
547  crval3 = muse_pfits_get_crval(cube->header, 3),
548  cd33 = muse_pfits_get_cd(cube->header, 3, 3);
549  cpl_propertylist *pbla = cpl_propertylist_new();
550  cpl_propertylist_copy_property_regexp(pbla, cube->header, "^C", 0);
551  cpl_propertylist_dump(pbla, stdout);
552  fflush(stdout);
553  cpl_msg_debug(__func__, "%f %f %f", crpix3, crval3, cd33);
554  cpl_vector *pos = cpl_vector_new(npl);
555 #endif
556  /* and we can normalize, fit, and then normalize again, every plane in the cube */
557  muse_image *image = muse_image_new();
558  int ipl, npl = cpl_imagelist_get_size(cube->data);
559  for (ipl = 0; ipl < npl; ipl++) {
560  image->data = cpl_imagelist_get(cube->data, ipl);
561  image->stat = cpl_imagelist_get(cube->stat, ipl);
562  cpl_mask *vignmask = maskimage ? maskimage->mask : NULL;
563  cpl_image *fit = muse_twilight_image_normalize_and_fit(image,
564  aParams->xorder,
565  aParams->yorder,
566  bpm, vignmask);
567  cpl_imagelist_set(cubefit->data, fit, ipl);
568 #if 0 // only for smoothing in wavelength direction (see below)
569  /* wavelength of this plane */
570  cpl_vector_set(pos, ipl, ipl+1);//(ipl + 1. - crpix3) * cd33 + crval3);
571 #endif
572  } /* for ipl (all cube image planes) */
573  image->data = NULL;
574  image->stat = NULL;
575  muse_image_delete(image);
576  muse_datacube_delete(cube);
577 
578 #if 0 // XXX try to smooth with a fit in wavelenth direction?
579  cpl_image *error = cpl_image_new(cpl_image_get_size_x(white),
580  cpl_image_get_size_y(white),
581  CPL_TYPE_DOUBLE);
582  cpl_imagelist *cubefit2 = cpl_fit_imagelist_polynomial(pos, cubefit->data,
583  0, 2, CPL_FALSE,
584  CPL_TYPE_DOUBLE, error);
585  cpl_vector_dump(pos, stdout);
586  fflush(stdout);
587  cpl_vector_delete(pos);
588  muse_processing_save_cimage(aProcessing, -1, error, wmuse->header, "ERROR_IMAGE");
589  cpl_image_delete(error);
590  /* evaluate the fits and construct a new cube out of them */
591  cpl_imagelist_save(cubefit2, "cubefit2.fits", CPL_TYPE_UNSPECIFIED, NULL, CPL_IO_CREATE);
592  cpl_imagelist_delete(cubefit2);
593 #endif
594 
595 #if 0 /* extra debug output for the globally fitted cube, without vignetted area */
596  rc = muse_processing_save_cube(aProcessing, -1, cubefit,
597  "TWILIGHT_CUBE_FIT", MUSE_CUBE_TYPE_FITS);
598 #endif
599 
600  muse_image *whitefit = muse_datacube_collapse(cubefit, NULL);
601  cpl_image_delete(whitefit->dq);
602  whitefit->dq = NULL;
603  cpl_propertylist_update_string(whitefit->header, "OBJECT",
604  "white from global fit");
605  cubefit->recimages = muse_imagelist_new();
606  muse_imagelist_set(cubefit->recimages, whitefit, 0);
607  cubefit->recnames = cpl_array_new(1, CPL_TYPE_STRING);
608  cpl_array_set_string(cubefit->recnames, 0, "white_global_fit");
609 
610  /* normalize the non-smoothed white-light image and divide it by *
611  * the smoothed version; then fit the masked (vignetted) area */
612  double mean = cpl_image_get_mean(white);
613  cpl_image_divide_scalar(white, mean);
614  cpl_image *wdiv = cpl_image_divide_create(white, whitefit->data);
615  /* append to reconstructed images */
616  image = muse_image_new();
617  image->header = cpl_propertylist_new();
618  cpl_propertylist_append_string(image->header, "OBJECT", "normalized "
619  "white-light divided by white global fit");
620  cpl_propertylist_append_string(image->header, "BUNIT", "1"); /* unitless */
621  image->data = wdiv;
622  image->dq = cpl_image_new_from_mask(cpl_image_get_bpm(wdiv));
623  muse_imagelist_set(cubefit->recimages, image,
624  muse_imagelist_get_size(cubefit->recimages));
625  cpl_array_set_size(cubefit->recnames,
626  cpl_array_get_size(cubefit->recnames) + 1);
627  cpl_array_set_string(cubefit->recnames, cpl_array_get_size(cubefit->recnames) - 1,
628  "white_div_global");
629  /* keep mask due to non-rectangular FOV around */
630  cpl_mask *fovmask = cpl_mask_duplicate(cpl_image_get_bpm(white));
631  if (maskimage) {
632  bpm = cpl_image_get_bpm(white);
633  cpl_mask_or(bpm, maskimage->mask);
634  } /* if maskimage */
635  cpl_image_delete(white);
636 
637  /* now invert the mask image (we need to fit the region that it contains!) */
638  if (maskimage) {
639  /* create mask of only the vignetted area */
640  cpl_mask *vignmask = cpl_mask_duplicate(fovmask);
641  cpl_mask_not(maskimage->mask);
642  cpl_mask_or(vignmask, maskimage->mask);
643  cpl_image *wdiv2 = cpl_image_duplicate(wdiv);
644  cpl_image_reject_from_mask(wdiv2, vignmask);
645 
646  /* search for pixels strongly deviating from unity and reject them */
647  if (aParams->vignmaskedges > 0.) {
648  cpl_msg_info(__func__, "Excluding strong edges (stronger than %.1f%%) "
649  "from the vignetted area", aParams->vignmaskedges * 100.);
650  cpl_image *wdivX = cpl_image_duplicate(wdiv2);
651  cpl_mask *wdivXmask = cpl_mask_duplicate(cpl_image_get_bpm(wdivX));
652  cpl_image_fill_rejected(wdivX, 1.);
653  cpl_image_accept_all(wdivX);
654  int i, nx = cpl_image_get_size_x(wdivX),
655  j, ny = cpl_image_get_size_y(wdivX);
656  /* first column-wise */
657  for (i = 1; i <= nx; i++) {
658  for (j = 2; j <= ny; j++) {
659  int err;
660  double v1 = cpl_image_get(wdivX, i, j - 1, &err),
661  v2 = cpl_image_get(wdivX, i, j, &err);
662  if (fabs(v2 - v1) < aParams->vignmaskedges) {
663  continue;
664  }
665  /* large difference found */
666  cpl_msg_debug(__func__, "%03d %03d v1 %f V2 %f --> delta %f",
667  i, j, v1, v2, fabs(v2 - v1));
668  double dv1 = fabs(v1 - 1.),
669  dv2 = fabs(v2 - 1.);
670  if (dv1 > dv2) {
671  cpl_mask_set(wdivXmask, i, j - 1, CPL_BINARY_1);
672  } else {
673  cpl_mask_set(wdivXmask, i, j, CPL_BINARY_1);
674  }
675  }
676  }
677  /* now row-wise */
678  for (j = 1; j <= ny; j++) {
679  for (i = 2; i <= nx; i++) {
680  int err;
681  double v1 = cpl_image_get(wdivX, i - 1, j, &err),
682  v2 = cpl_image_get(wdivX, i, j, &err);
683  if (fabs(v2 - v1) < aParams->vignmaskedges) {
684  continue;
685  }
686  /* large difference found */
687  cpl_msg_debug(__func__, "%03d %03d v1 %f V2 %f --> delta %f",
688  i, j, v1, v2, fabs(v2 - v1));
689  double dv1 = fabs(v1 - 1.),
690  dv2 = fabs(v2 - 1.);
691  if (dv1 > dv2) {
692  cpl_mask_set(wdivXmask, i - 1, j, CPL_BINARY_1);
693  } else {
694  cpl_mask_set(wdivXmask, i, j, CPL_BINARY_1);
695  }
696  }
697  }
698  /* transfer the edited mask to the divided image for the smoothing */
699  cpl_image_reject_from_mask(wdiv2, wdivXmask);
700 
701  /* append to reconstructed images, before throwing away */
702  image = muse_image_new();
703  image->header = cpl_propertylist_new();
704  cpl_propertylist_append_string(image->header, "OBJECT",
705  "white-light divided by white global, mask excl edges");
706  cpl_propertylist_append_string(image->header, "BUNIT", "1"); /* unitless */
707  image->data = wdivX;
708  image->dq = cpl_image_new_from_mask(wdivXmask);
709  cpl_mask_delete(wdivXmask);
710  muse_imagelist_set(cubefit->recimages, image,
711  muse_imagelist_get_size(cubefit->recimages));
712  cpl_array_set_size(cubefit->recnames,
713  cpl_array_get_size(cubefit->recnames) + 1);
714  cpl_array_set_string(cubefit->recnames, cpl_array_get_size(cubefit->recnames) - 1,
715  "white_div_global_excl_edges");
716  } /* if vignmaskedges positive */
717 
718  /* do the smoothing in one of the several possible ways */
719  cpl_image *fit = NULL;
720  if (aParams->vignsmooth == MUSE_TWILIGHT_PARAM_VIGNSMOOTH_GAUSSIAN) {
721  /* use Gaussian filter to smooth the vignetted area */
722  double fwhm = (aParams->vignxpar + aParams->vignypar) / 2.;
723  cpl_msg_info(__func__, "Running %.1f pix FWHM Gaussian filter...", fwhm);
724  fit = cpl_image_duplicate(wdiv2);
725  /* create odd halfwidth of about fwhm */
726  int hw = lround(fwhm);
727  hw = hw % 2 ? hw : hw + 1;
728  cpl_msg_debug(__func__, "using width 2 x %d + 1 for Gaussian matrix", hw);
729  cpl_matrix *gauss = muse_matrix_new_gaussian_2d(hw, hw,
730  fwhm * CPL_MATH_SIG_FWHM);
731  cpl_errorstate prestate = cpl_errorstate_get();
732  cpl_image_filter(fit, wdiv2, gauss, CPL_FILTER_LINEAR, CPL_BORDER_FILTER);
733  cpl_matrix_delete(gauss);
734  cpl_image_reject_from_mask(fit, cpl_image_get_bpm(wdiv2)); /* re-reject! */
735  /* again, replace values less than 1 */
736  cpl_image_threshold(fit, 1., FLT_MAX, 1., FLT_MAX);
737  if (!cpl_errorstate_is_equal(prestate)) {
738  cpl_msg_warning(__func__, "filtering vignetted area failed: %s",
739  cpl_error_get_message());
740  }
741 #if 0 /* test of trying to run the filtering on a larger area which is *
742  * set to one towards the center of the field; implementation not *
743  * finished but only tested with a hand-edited wider mask */
744  cpl_msg_info(__func__, "Running Gaussian filter on extended region...");
745  /* use Gaussian filter to smooth the vignetted area */
746  fit = cpl_image_duplicate(wdiv2);
747  //cpl_mask_save(cpl_image_get_bpm(wdiv2), "masktest.fits", NULL, CPL_IO_CREATE);
748  /* threshold the rest of the image */
749  cpl_image *wdiv3 = cpl_image_duplicate(wdiv2);
750  cpl_image_fill_rejected(wdiv3, 1.);
751  cpl_mask *masktest = cpl_mask_load("masktest3.fits", 0, 0);
752  cpl_image_reject_from_mask(wdiv3, masktest);
753  cpl_mask_delete(masktest);
754  /* extend the vignetted area to the top and left */
755  /* find top pixel in each column */
756  /* extend it from there 5 pix to the left and 5 pix to the top */
757  cpl_matrix *gauss = muse_matrix_new_gaussian_2d(7, 7,
758  10. * CPL_MATH_SIG_FWHM);
759  cpl_errorstate prestate = cpl_errorstate_get();
760  cpl_image_filter(fit, wdiv3, gauss, CPL_FILTER_LINEAR, CPL_BORDER_FILTER);
761  cpl_image_delete(wdiv3);
762  cpl_matrix_delete(gauss);
763  cpl_image_reject_from_mask(fit, cpl_image_get_bpm(wdiv2)); /* re-reject! */
764  /* again, replace values less than 1 */
765  cpl_image_threshold(fit, 1., FLT_MAX, 1., FLT_MAX);
766  if (!cpl_errorstate_is_equal(prestate)) {
767  cpl_msg_warning(__func__, "filtering vignetted area failed: %s",
768  cpl_error_get_message());
769  }
770 #endif
771  } else if (aParams->vignsmooth == MUSE_TWILIGHT_PARAM_VIGNSMOOTH_MEDIAN) {
772  cpl_msg_info(__func__, "Running %dx%d median filter...",
773  aParams->vignxpar, aParams->vignypar);
774  fit = cpl_image_duplicate(wdiv2);
775  cpl_mask *mask = cpl_mask_new(aParams->vignxpar, aParams->vignypar);
776  cpl_mask_not(mask);
777  cpl_errorstate prestate = cpl_errorstate_get();
778  cpl_image_filter_mask(fit, wdiv2, mask, CPL_FILTER_MEDIAN,
779  CPL_BORDER_FILTER);
780  cpl_mask_delete(mask);
781  /* again, replace values less than 1 */
782  cpl_image_threshold(fit, 1., FLT_MAX, 1., FLT_MAX);
783  if (!cpl_errorstate_is_equal(prestate)) {
784  cpl_msg_warning(__func__, "filtering vignetted ared failed: %s",
785  cpl_error_get_message());
786  }
787  } else { /* MUSE_TWILIGHT_PARAM_VIGNSMOOTH_POLYFIT */
788  cpl_msg_info(__func__, "Running polyfit...");
789  /* since we want to correct only vignetting, everything in this image *
790  * should be one or above, so that we should replace lower values */
791  cpl_image *wdiv3 = cpl_image_duplicate(wdiv2);
792  cpl_image_threshold(wdiv3, 1., FLT_MAX, 1., FLT_MAX);
793 
794  /* do the fit */
795  fit = muse_utils_image_fit_polynomial(wdiv3, aParams->vignxpar,
796  aParams->vignypar);
797  cpl_image_delete(wdiv3);
798  /* again, replace values less than 1 */
799  cpl_image_threshold(fit, 1., FLT_MAX, 1., FLT_MAX);
800  } /* else */
801 
802  /* set the original rejection mask again */
803  cpl_image_reject_from_mask(fit, vignmask);
804  /* fill everything but the fitted area with ones, so that we *
805  * don't add rubbish when now dividing by this vignetting fix */
806  cpl_image_fill_rejected(fit, 1.);
807 
808  /* append to reconstructed images */
809  image = muse_image_new();
810  image->header = cpl_propertylist_new();
811  cpl_propertylist_append_string(image->header, "OBJECT", "vignetting fit");
812  /* no sensible unit for this image */
813  cpl_propertylist_append_string(image->header, "BUNIT", "");
814  image->data = fit;
815  image->dq = cpl_image_new_from_mask(cpl_image_get_bpm(fit));
816  muse_imagelist_set(cubefit->recimages, image,
817  muse_imagelist_get_size(cubefit->recimages));
818  cpl_array_set_size(cubefit->recnames,
819  cpl_array_get_size(cubefit->recnames) + 1);
820  cpl_array_set_string(cubefit->recnames, cpl_array_get_size(cubefit->recnames) - 1,
821  "vign_fit");
822 
823  /* divide by the result to assess how well it worked, and append to recimages */
824  image = muse_image_new();
825  image->header = cpl_propertylist_new();
826  cpl_propertylist_append_string(image->header, "OBJECT",
827  "white image corrected by vignetting fit");
828  /* unit again like white image */
829  cpl_propertylist_append_string(image->header, "BUNIT", "count");
830  image->data = cpl_image_divide_create(wdiv2, fit);
831  image->dq = cpl_image_new_from_mask(cpl_image_get_bpm(image->data));
832  muse_imagelist_set(cubefit->recimages, image,
833  muse_imagelist_get_size(cubefit->recimages));
834  cpl_array_set_size(cubefit->recnames,
835  cpl_array_get_size(cubefit->recnames) + 1);
836  cpl_array_set_string(cubefit->recnames, cpl_array_get_size(cubefit->recnames) - 1,
837  "white_cor");
838  cpl_image_delete(wdiv2);
839 
840  /* apply the vignetting fit on top of the twilight cube */
841  cpl_msg_info(__func__, "Combining smooth field of view and vignetted corner"
842  " in %d planes", npl);
843  for (ipl = 0; ipl < npl; ipl++) {
844  cpl_image *cimage = cpl_imagelist_get(cubefit->data, ipl);
845  // XXX the best here would be to restrict the mask to the illuminated
846  // area instead of using the full image plane!
847  cpl_image_accept_all(cimage);
848  double mean1 = cpl_image_get_mean(cimage);
849  cpl_image_multiply(cimage, fit);
850  /* since the image multiplication merges the bad pixel masks, we need *
851  * to reset them again before computing the normalization factor */
852  cpl_image_accept_all(cimage);
853  double mean2 = cpl_image_get_mean(cimage);
854  cpl_image_multiply_scalar(cimage, 1. / mean2);
855  double mean3 = cpl_image_get_mean(cimage);
856  if (fabs(mean3 - 1.) > FLT_EPSILON) {
857  cpl_msg_warning(__func__, "normalization failed in plane %d: mean("
858  "plane) = %f -> %f -> %f", ipl + 1, mean1, mean2, mean3);
859  } /* if */
860  } /* for ipl (all cube image planes) */
861  cpl_mask_delete(vignmask);
862  } /* if maskimage */
863  cpl_mask_delete(fovmask);
864  muse_mask_delete(maskimage);
865 
866  rc = muse_processing_save_cube(aProcessing, -1, cubefit,
867  MUSE_TAG_TWILIGHT_CUBE,
869  muse_datacube_delete(cubefit);
870 
871  return rc == CPL_ERROR_NONE ? 0 : -1;
872 } /* muse_twilight_compute() */
double crsigma
Sigma rejection factor to use for cosmic ray rejection during final resampling. A zero or negative va...
int muse_processing_save_cimage(muse_processing *aProcessing, int aIFU, cpl_image *aImage, cpl_propertylist *aHeader, const char *aTag)
Save a computed FITS image to disk.
muse_imagelist * muse_basicproc_load(muse_processing *aProcessing, unsigned char aIFU, muse_basicproc_params *aBPars)
Load the raw input files from disk and do basic processing.
Structure definition of a MUSE datacube.
Definition: muse_datacube.h:48
Structure definition for a collection of muse_images.
void muse_image_delete(muse_image *aImage)
Deallocate memory associated to a muse_image object.
Definition: muse_image.c:85
double muse_pfits_get_cd(const cpl_propertylist *aHeaders, unsigned int aAxisI, unsigned int aAxisJ)
find out the WCS coordinate at the reference point
Definition: muse_pfits.c:446
double muse_pfits_get_crval(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS coordinate at the reference point
Definition: muse_pfits.c:423
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
Definition: muse_utils.c:98
cpl_image * data
the data extension
Definition: muse_image.h:46
cpl_size muse_pixtable_get_nrow(const muse_pixtable *aPixtable)
get the number of rows within the pixel table
void muse_mask_delete(muse_mask *aMask)
Deallocate memory associated to a muse_mask object.
Definition: muse_mask.c:78
muse_datacube * muse_datacube_load(const char *aFilename)
Load header, DATA and optionally STAT and DQ extensions as well as the reconstructed images of a MUSE...
muse_resampling_crstats_type muse_postproc_get_cr_type(const char *aCRTypeString)
Select correct cosmic ray rejection type for crtype string.
muse_datacube * muse_resampling_cube(muse_pixtable *aPixtable, muse_resampling_params *aParams, muse_pixgrid **aGrid)
Resample a pixel table onto a regular grid structure representing a FITS NAXIS=3 datacube.
cpl_image * stat
the statistics extension
Definition: muse_image.h:64
void muse_datacube_delete(muse_datacube *aCube)
Deallocate memory associated to a muse_datacube object.
void muse_imagelist_delete(muse_imagelist *aList)
Free the memory of the MUSE image list.
const char * crtype_s
Type of statistics used for detection of cosmic rays during final resampling. "iraf" uses the varianc...
muse_basicproc_params * muse_basicproc_params_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new structure of basic processing parameters.
muse_image * muse_combine_images(muse_combinepar *aCPars, muse_imagelist *aImages)
Combine several images into one.
Definition: muse_combine.c:741
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:40
cpl_array * recnames
the reconstructed image filter names
Definition: muse_datacube.h:71
cpl_table * table
The pixel table.
void muse_basicproc_params_delete(muse_basicproc_params *aBPars)
Free a structure of basic processing parameters.
cpl_propertylist * header
the FITS header
Definition: muse_image.h:72
#define MUSE_PIXTABLE_DATA
Definition: muse_pixtable.h:48
unsigned int muse_imagelist_get_size(muse_imagelist *aList)
Return the number of stored images.
cpl_frameset * usedframes
void muse_combinepar_delete(muse_combinepar *aCPars)
Clear the combination parameters.
Definition: muse_combine.c:715
muse_resampling_crstats_type crtype
cpl_image * dq
the data quality extension
Definition: muse_image.h:56
cpl_error_code muse_pixtable_restrict_wavelength(muse_pixtable *aPixtable, double aLow, double aHigh)
Restrict a pixel table to a certain wavelength range.
double muse_pfits_get_crpix(const cpl_propertylist *aHeaders, unsigned int aAxis)
find out the WCS reference point
Definition: muse_pfits.c:401
cpl_array * muse_cplarray_new_from_delimited_string(const char *aString, const char *aDelim)
Convert a delimited string into an array of strings.
Structure definition of MUSE pixel table.
cpl_error_code muse_datacube_convert_dq(muse_datacube *aCube)
Convert the DQ extension of a datacube to NANs in DATA and STAT.
muse_image * muse_imagelist_get(muse_imagelist *aList, unsigned int aIdx)
Get the muse_image of given list index.
int vignxpar
Parameter used by the vignetting smoothing: x order for polyfit (default, recommended 4)...
double vignmaskedges
Pixels on edges stronger than this fraction in the normalized image are excluded from the fit to the ...
int vignypar
Parameter used by the vignetting smoothing: y order for polyfit (default, recommended 4)...
muse_resampling_params * muse_resampling_params_new(muse_resampling_type aMethod)
Create the resampling parameters structure.
Structure to hold the parameters of the muse_twilight recipe.
cpl_error_code muse_processing_save_cube(muse_processing *aProcessing, int aIFU, void *aCube, const char *aTag, muse_cube_type aType)
Save a MUSE datacube to disk.
double dlambda
Sampling for twilight reconstruction, this should result in planes of equal wavelength coverage...
int vignsmooth
Type of smoothing to use for the vignetted region given by the VIGNETTING_MASK; gaussian uses (vignxp...
muse_combinepar * muse_combinepar_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new set of combination parameters.
Definition: muse_combine.c:672
double lambdamin
Minimum wavelength for twilight reconstruction.
cpl_imagelist * data
the cube containing the actual data values
Definition: muse_datacube.h:76
void muse_processing_append_used(muse_processing *aProcessing, cpl_frame *aFrame, cpl_frame_group aGroup, int aDuplicate)
Add a frame to the set of used frames.
int yorder
Polynomial order to use in y direction to fit the full field of view.
muse_mask * muse_processing_load_mask(muse_processing *aProcessing, const char *aTag)
Load a mask file and its FITS header.
int xorder
Polynomial order to use in x direction to fit the full field of view.
muse_pixtable * muse_pixtable_create(muse_image *aImage, cpl_table *aTrace, cpl_table *aWave, cpl_table *aGeoTable)
Create the pixel table for one CCD.
cpl_error_code muse_basicproc_stats_append_header(cpl_image *aImage, cpl_propertylist *aHeader, const char *aPrefix, unsigned aStats)
Compute image statistics of an image and add them to a header.
cpl_table * muse_basicproc_get_illum(muse_imagelist *aImages, cpl_table *aTrace, cpl_table *aWave, cpl_table *aGeo)
Get an illum/attached flat-field from an imagelist and prepare it for use.
cpl_propertylist * header
the FITS header
Definition: muse_datacube.h:57
cpl_table * muse_processing_load_ctable(muse_processing *aProcessing, const char *aTag, unsigned char aIFU)
Load a CPL table according to its tag and IFU/channel number.
muse_resampling_type muse_postproc_get_resampling_type(const char *aResampleString)
Select correct resampling type for resample string.
#define MUSE_PIXTABLE_STAT
Definition: muse_pixtable.h:50
cpl_image * muse_utils_image_fit_polynomial(const cpl_image *aImage, unsigned short aXOrder, unsigned short aYOrder)
Create a smooth version of a 2D image by fitting it with a 2D polynomial.
Definition: muse_utils.c:1147
Handling of "mask" files.
Definition: muse_mask.h:43
cpl_array * intags
muse_imagelist * muse_imagelist_new(void)
Create a new (empty) MUSE image list.
cpl_frameset * inframes
cpl_error_code muse_basicproc_mask_notch_filter(muse_pixtable *aPT, unsigned char aIFU)
Mask the range of the NaD notch filter in the given pixel table.
Structure of basic processing parameters.
muse_resampling_type
Resampling types.
Resampling parameters.
void muse_resampling_params_delete(muse_resampling_params *aParams)
Delete a resampling parameters structure.
muse_image * muse_datacube_collapse(muse_datacube *aCube, const muse_table *aFilter)
Integrate a FITS NAXIS=3 datacube along the wavelength direction.
muse_image * muse_image_new(void)
Allocate memory for a new muse_image object.
Definition: muse_image.c:66
cpl_matrix * muse_matrix_new_gaussian_2d(int aXHalfwidth, int aYHalfwidth, double aSigma)
Create a matrix that contains a normalized 2D Gaussian.
Definition: muse_utils.c:1099
cpl_error_code muse_basicproc_apply_illum(muse_pixtable *aPT, cpl_table *aAttached)
Apply an illum/attached flat-field to a pixel table.
double lambdamax
Maximum wavelength for twilight reconstruction.
cpl_mask * mask
The mask data.
Definition: muse_mask.h:49
void muse_pixtable_delete(muse_pixtable *aPixtable)
Deallocate memory associated to a pixel table object.
cpl_error_code muse_imagelist_set(muse_imagelist *aList, muse_image *aImage, unsigned int aIdx)
Set the muse_image of given list index.
cpl_error_code muse_pixtable_compute_limits(muse_pixtable *aPixtable)
(Re-)Compute the limits of the coordinate columns of a pixel table.
int muse_quality_image_reject_using_dq(cpl_image *aData, cpl_image *aDQ, cpl_image *aStat)
Reject pixels of one or two images on a DQ image.
Definition: muse_quality.c:628
muse_ins_mode muse_pfits_get_mode(const cpl_propertylist *aHeaders)
find out the observation mode
Definition: muse_pfits.c:1352
muse_imagelist * recimages
the reconstructed image data
Definition: muse_datacube.h:64
cpl_mask * muse_cplmask_adapt_to_image(const cpl_mask *aMask, const cpl_image *aImage)
Adapt mask with masked region in one quadrant to size of an image.
cpl_parameterlist * parameters
cpl_imagelist * stat
the cube containing the data variance
Definition: muse_datacube.h:86
cpl_propertylist * header
The FITS header.
cpl_frameset * muse_frameset_find_tags(const cpl_frameset *aFrames, const cpl_array *aTags, unsigned char aIFU, cpl_boolean aInvert)
return frameset containing data from an IFU/channel with the given tag(s)
Definition: muse_utils.c:253
const char * resample_s
The resampling technique to use for the final output cube. (as string)