MUSE Pipeline Reference Manual  2.1.1
muse_image.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) 2005-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 <cpl.h>
30 #include <string.h>
31 
32 #include "muse_image.h"
33 
34 #include "muse_quadrants.h"
35 #include "muse_quality.h"
36 #include "muse_pfits.h"
37 #include "muse_utils.h"
38 #include "muse_wcs.h"
39 
40 /*----------------------------------------------------------------------------*/
47 /*----------------------------------------------------------------------------*/
48 
51 /*---------------------------------------------------------------------------*/
64 /*---------------------------------------------------------------------------*/
65 muse_image *
67 {
68  /* calloc automatically NULLs out the four components of the structure */
69  muse_image *image = (muse_image *)cpl_calloc(1, sizeof(muse_image));
70  return image;
71 } /* muse_image_new() */
72 
73 /*---------------------------------------------------------------------------*/
83 /*---------------------------------------------------------------------------*/
84 void
86 {
87  /* if image does not exists at all, we don't need to do anything */
88  if (!aImage) {
89  return;
90  }
91 
92  /* checks for the existence of the sub-images *
93  * are done in the CPL functions */
94  cpl_image_delete(aImage->data);
95  aImage->data = NULL;
96  cpl_image_delete(aImage->dq);
97  aImage->dq = NULL;
98  cpl_image_delete(aImage->stat);
99  aImage->stat = NULL;
100 
101  /* delete the FITS header, too */
102  cpl_propertylist_delete(aImage->header);
103  aImage->header = NULL;
104  cpl_free(aImage);
105 } /* muse_image_delete() */
106 
107 /*---------------------------------------------------------------------------*/
115 /*---------------------------------------------------------------------------*/
116 static muse_image *
117 muse_image_load_internal(const char *aFilename, unsigned char aIFU,
118  const char *aID)
119 {
120  muse_image *image = muse_image_new();
121 
122  /* Load the primary FITS header first. This is not critical, but *
123  * a good first test to see, if the file is really there. */
124  image->header = cpl_propertylist_load(aFilename, 0);
125  if (!image->header) {
126  cpl_error_set_message(aID, cpl_error_get_code(), "Loading primary FITS "
127  "header of \"%s\" did not succeed", aFilename);
128  muse_image_delete(image);
129  return NULL;
130  }
131 
132  /* now load the image data from the there extensions */
133  char extname[KEYWORD_LENGTH];
134  if (aIFU) {
135  /* first set the EXTNAME keyword in the output header */
136  snprintf(extname, KEYWORD_LENGTH, "CHAN%02hhu", aIFU);
137  cpl_propertylist_update_string(image->header, "EXTNAME", extname);
138  /* then set up the extension name to search for */
139  snprintf(extname, KEYWORD_LENGTH, "CHAN%02hhu.%s", aIFU, EXTNAME_DATA);
140  } else {
141  snprintf(extname, KEYWORD_LENGTH, "%s", EXTNAME_DATA);
142  }
143  int extension = cpl_fits_find_extension(aFilename, extname);
144  image->data = cpl_image_load(aFilename, CPL_TYPE_FLOAT, 0, extension);
145  if (!image->data) {
146  cpl_error_set_message(aID, MUSE_ERROR_READ_DATA, "Could not load extension "
147  "%s from \"%s\"", extname, aFilename);
148  muse_image_delete(image);
149  return NULL;
150  }
151  /* get BUNIT back from the header of this data extension */
152  cpl_propertylist *hdata = cpl_propertylist_load(aFilename, extension);
153  if (cpl_propertylist_has(hdata, "BUNIT")) { /* backward compatibility */
154  cpl_propertylist_append_string(image->header, "BUNIT",
155  muse_pfits_get_bunit(hdata));
156  cpl_propertylist_set_comment(image->header, "BUNIT",
157  cpl_propertylist_get_comment(hdata, "BUNIT"));
158  } else {
159  cpl_msg_warning(aID, "No BUNIT given in extension %d [%s] of \"%s\"!",
160  extension, extname, aFilename);
161  }
162  /* When reading from extensions, we also need to merge in all *
163  * HIERARCH keywords from the data extension and add the EXTNAME. */
164  if (aIFU) {
165  cpl_propertylist_erase_regexp(hdata, "^ESO ", 1);
166  cpl_propertylist_append(image->header, hdata);
167  }
168  cpl_propertylist_delete(hdata);
169 
170  if (aIFU) {
171  snprintf(extname, KEYWORD_LENGTH, "CHAN%02hhu.%s", aIFU, EXTNAME_DQ);
172  } else {
173  snprintf(extname, KEYWORD_LENGTH, "%s", EXTNAME_DQ);
174  }
175  extension = cpl_fits_find_extension(aFilename, extname);
176  image->dq = cpl_image_load(aFilename, CPL_TYPE_INT, 0, extension);
177  if (!image->dq) {
178  cpl_error_set_message(aID, MUSE_ERROR_READ_DQ, "Could not load extension "
179  "%s from \"%s\"", extname, aFilename);
180  muse_image_delete(image);
181  return NULL;
182  }
183 
184  if (aIFU) {
185  snprintf(extname, KEYWORD_LENGTH, "CHAN%02hhu.%s", aIFU, EXTNAME_STAT);
186  } else {
187  snprintf(extname, KEYWORD_LENGTH, "%s", EXTNAME_STAT);
188  }
189  extension = cpl_fits_find_extension(aFilename, extname);
190  image->stat = cpl_image_load(aFilename, CPL_TYPE_FLOAT, 0, extension);
191  if (!image->stat) {
192  cpl_error_set_message(aID, MUSE_ERROR_READ_STAT, "Could not load extension "
193  "%s from \"%s\"", extname, aFilename);
194  muse_image_delete(image);
195  return NULL;
196  }
197 
198  return image;
199 } /* muse_image_load_internal() */
200 
201 /*---------------------------------------------------------------------------*/
226 /*---------------------------------------------------------------------------*/
227 muse_image *
228 muse_image_load(const char *aFilename)
229 {
230  return muse_image_load_internal(aFilename, 0, __func__);
231 } /* muse_image_load() */
232 
233 /*---------------------------------------------------------------------------*/
260 /*---------------------------------------------------------------------------*/
261 muse_image *
262 muse_image_load_from_extensions(const char *aFilename, unsigned char aIFU)
263 {
264  return muse_image_load_internal(aFilename, aIFU, __func__);
265 } /* muse_image_load_from_extensions() */
266 
267 /*---------------------------------------------------------------------------*/
290 /*---------------------------------------------------------------------------*/
291 muse_image *
292 muse_image_load_from_raw(const char *aFilename, int aExtension)
293 {
294  muse_image *mImage = muse_image_new();
295 
296  cpl_errorstate prestate = cpl_errorstate_get();
297  /* Raw images are always integer. *
298  * The cast to float happens implicitely on loading */
299  mImage->data = cpl_image_load(aFilename, CPL_TYPE_FLOAT, 0, aExtension);
300  char *channel = NULL;
301  if (!mImage->data) {
302  muse_image_delete(mImage);
303 
304  cpl_propertylist *header = cpl_propertylist_load(aFilename, aExtension);
305  if (!header) {
306  cpl_msg_error(__func__, "Image \"%s\" (extension %d) could not be read: %s",
307  aFilename, aExtension, cpl_error_get_message());
308  return NULL;
309  }
310  /* check if the header contains DET.CHIP.LIVE=F; if so, *
311  * set a special error to not fail the whole recipe */
312  cpl_boolean live = muse_pfits_get_chip_live(header);
313  channel = cpl_strdup(muse_pfits_get_extname(header));
314  cpl_propertylist_delete(header);
315  if (live) { /* something is wrong with the file! */
316  cpl_msg_error(__func__, "Image \"%s[%s]\" (extension %d) could not be read "
317  "although chip is alive: %s", aFilename, channel, aExtension,
318  cpl_error_get_message());
319  cpl_free(channel);
320  return NULL;
321  }
322  cpl_msg_warning(__func__, "Image \"%s[%s]\" (extension %d) could not be read,"
323  " but chip is dead: %s", aFilename, channel, aExtension,
324  cpl_error_get_message());
325  cpl_errorstate_set(prestate);
326  cpl_error_set_message(__func__, MUSE_ERROR_CHIP_NOT_LIVE, "Image \"%s[%s]\" "
327  "(extension %d) is dead", aFilename, channel,
328  aExtension);
329  cpl_free(channel);
330  return NULL;
331  } /* if data not loaded */
332 
333  /* Create an empty DQ extension, signifying a CCD without flaws. *
334  * As a first guess this should be adequate. */
335  mImage->dq = cpl_image_new(cpl_image_get_size_x(mImage->data),
336  cpl_image_get_size_y(mImage->data),
337  CPL_TYPE_INT); /* is initialized to 0 */
338 
339  /* create STAT extension filled with zeros; this must be replaced later *
340  * by the caller of this function */
341  mImage->stat = cpl_image_new(cpl_image_get_size_x(mImage->data),
342  cpl_image_get_size_y(mImage->data),
343  CPL_TYPE_FLOAT); /* is initialized to 0 */
344 
345  /* load and merge primary and extension header */
346  mImage->header = cpl_propertylist_load(aFilename, 0);
347  if (aExtension > 0) {
348  cpl_propertylist *extHeader = cpl_propertylist_load(aFilename, aExtension);
349  /* leave EXTNAME in place but remove other stuff related to extensions: */
350  cpl_propertylist_copy_property_regexp(mImage->header, extHeader,
351  "^XTENSION$|"
352  "^CHECKSUM$|"
353  "^DATASUM$", 1);
354  cpl_propertylist_delete(extHeader);
355  }
356  prestate = cpl_errorstate_get();
357  channel = cpl_strdup(muse_pfits_get_extname(mImage->header));
358  if (!cpl_errorstate_is_equal(prestate)) { /* ignore missing EXTNAME */
359  cpl_errorstate_set(prestate);
360  }
361  /* make sure that the unit is correct */
362  cpl_propertylist_update_string(mImage->header, "BUNIT", "adu");
363  cpl_propertylist_set_comment(mImage->header, "BUNIT",
364  "DATA is in analog-to-digital units");
365  cpl_msg_info(__func__, "loaded \"%s[%s]\" (extension %d)", aFilename,
366  channel ? channel : "0", aExtension);
367  cpl_free(channel);
368 
369  return mImage;
370 } /* muse_image_load_from_raw() */
371 
372 /*----------------------------------------------------------------------------*/
403 /*----------------------------------------------------------------------------*/
404 cpl_error_code
405 muse_image_save(muse_image *aImage, const char *aFilename)
406 {
407  cpl_ensure_code(aImage && aImage->data && aFilename, CPL_ERROR_NULL_INPUT);
408  cpl_ensure_code(cpl_propertylist_has(aImage->header, "BUNIT"),
409  CPL_ERROR_INCOMPATIBLE_INPUT);
410 
411  /* BUNIT should only be saved in the image extension(s), *
412  * so save a header without it */
413  cpl_propertylist *header = cpl_propertylist_duplicate(aImage->header);
414  cpl_propertylist_erase(header, "BUNIT");
415  /* do not save the WCS keywords, either */
416  cpl_propertylist_erase_regexp(header, MUSE_WCS_KEYS, 0);
417 
418  /* Save the FITS header first, as the primary header of the file. *
419  * The extensions will contain only minimal image headers. */
420  cpl_error_code error = cpl_propertylist_save(header, aFilename, CPL_IO_CREATE);
421  cpl_propertylist_delete(header);
422  if (error != CPL_ERROR_NONE) {
423  cpl_msg_error(__func__, "Could not save header: %s",
424  cpl_error_get_message());
425  return error;
426  }
427 
428  /* minimal header information for the extensions to add *
429  * extension name information */
430  cpl_propertylist *extheader = cpl_propertylist_new();
431 
432  /* copy WCS information to minimal extension header */
433  cpl_propertylist_copy_property_regexp(extheader, aImage->header,
434  MUSE_WCS_KEYS, 0);
435 
436  cpl_propertylist_append_string(extheader, "EXTNAME", EXTNAME_DATA);
437  cpl_propertylist_set_comment(extheader, "EXTNAME", EXTNAME_DATA_COMMENT);
438  const char *unit = muse_pfits_get_bunit(aImage->header),
439  *ucomment = cpl_propertylist_get_comment(aImage->header, "BUNIT");
440  cpl_propertylist_append_string(extheader, "BUNIT", unit);
441  cpl_propertylist_set_comment(extheader, "BUNIT", ucomment);
442  muse_utils_set_hduclass(extheader, "DATA", EXTNAME_DATA,
443  aImage->dq ? EXTNAME_DQ : NULL,
444  aImage->stat ? EXTNAME_STAT : NULL);
445  error = cpl_image_save(aImage->data, aFilename, CPL_TYPE_FLOAT,
446  extheader, CPL_IO_EXTEND);
447  if (error != CPL_ERROR_NONE) {
448  cpl_msg_error(__func__, "Could not append data image: %s",
449  cpl_error_get_message());
450  cpl_propertylist_delete(extheader);
451  return error;
452  } /* if error */
453 
454  if (aImage->dq) {
455  cpl_propertylist_set_string(extheader, "EXTNAME", EXTNAME_DQ);
456  cpl_propertylist_set_comment(extheader, "EXTNAME", EXTNAME_DQ_COMMENT);
457  cpl_propertylist_erase(extheader, "BUNIT"); /* no unit for data quality */
458  muse_utils_set_hduclass(extheader, "QUALITY", EXTNAME_DATA, EXTNAME_DQ,
459  aImage->stat ? EXTNAME_STAT : NULL);
460  error = cpl_image_save(aImage->dq, aFilename, CPL_TYPE_INT,
461  extheader, CPL_IO_EXTEND);
462  if (error != CPL_ERROR_NONE) {
463  cpl_msg_error(__func__, "Could not append dq image: %s",
464  cpl_error_get_message());
465  cpl_propertylist_delete(extheader);
466  return error;
467  } /* if error */
468  } /* if dq */
469 
470  if (aImage->stat) {
471  cpl_propertylist_set_string(extheader, "EXTNAME", EXTNAME_STAT);
472  cpl_propertylist_set_comment(extheader, "EXTNAME", EXTNAME_STAT_COMMENT);
473  char *ustat = cpl_sprintf("(%s)**2", unit); /* variance in squared units */
474  cpl_propertylist_append_string(extheader, "BUNIT", ustat);
475  cpl_free(ustat);
476  muse_utils_set_hduclass(extheader, "ERROR", EXTNAME_DATA,
477  aImage->dq ? EXTNAME_DQ : NULL, EXTNAME_STAT);
478  error = cpl_image_save(aImage->stat, aFilename, CPL_TYPE_FLOAT,
479  extheader, CPL_IO_EXTEND);
480  if (error != CPL_ERROR_NONE) {
481  cpl_msg_error(__func__, "Could not append stat image: %s",
482  cpl_error_get_message());
483  cpl_propertylist_delete(extheader);
484  return error;
485  } /* if error */
486  } /* if stat */
487  cpl_propertylist_delete(extheader);
488 
489  return CPL_ERROR_NONE;
490 } /* muse_image_save() */
491 
492 /*----------------------------------------------------------------------------*/
508 /*----------------------------------------------------------------------------*/
509 muse_image *
511 {
512  cpl_ensure(aImage, CPL_ERROR_NULL_INPUT, NULL);
513 
514  muse_image *image = muse_image_new();
515  image->data = cpl_image_duplicate(aImage->data);
516  image->dq = cpl_image_duplicate(aImage->dq);
517  image->stat = cpl_image_duplicate(aImage->stat);
518  image->header = cpl_propertylist_duplicate(aImage->header);
519  if (!image->data || !image->dq || !image->stat || !image->header) {
520  muse_image_delete(image);
521  return NULL;
522  }
523  return image;
524 } /* muse_image_duplicate() */
525 
526 /*---------------------------------------------------------------------------*/
542 /*---------------------------------------------------------------------------*/
543 static int
544 muse_image_dq_merge(cpl_image *aDQ1, cpl_image *aDQ2)
545 {
546  cpl_ensure(aDQ1, CPL_ERROR_NULL_INPUT, -1);
547  cpl_ensure(aDQ2, CPL_ERROR_NULL_INPUT, -2);
548 
549  /* get the output dq image buffer as normal int */
550  int *dq1 = cpl_image_get_data_int(aDQ1);
551  /* get the subtract dq image buffer as const because we don't want to change it */
552  const int *dq2 = cpl_image_get_data_int_const(aDQ2);
553  if (!dq1 || !dq2) {
554  return cpl_error_get_code();
555  }
556 
557  int i,
558  nx = cpl_image_get_size_x(aDQ1),
559  ny = cpl_image_get_size_y(aDQ1);
560  for (i = 0; i < nx; i++) {
561  int j;
562  for (j = 0; j < ny; j++) {
563  /* If the second pixel is bad somehow, merge it with the dq flag *
564  * of the first image. The output pixel gets a combined dq status. */
565  if (dq2[i + j*nx]) {
566  dq1[i + j*nx] |= dq2[i + j*nx];
567  }
568  } /* for j */
569  } /* for i */
570 
571  return 0;
572 } /* muse_image_dq_merge() */
573 
574 /*---------------------------------------------------------------------------*/
591 /*---------------------------------------------------------------------------*/
592 int
594 {
595  cpl_ensure(aImage, CPL_ERROR_NULL_INPUT, -1);
596  cpl_ensure(aSubtract, CPL_ERROR_NULL_INPUT, -2);
597 
598  cpl_error_code rc = cpl_image_subtract(aImage->data, aSubtract->data);
599  if (rc != CPL_ERROR_NONE) {
600  cpl_msg_error(__func__, "failure while subtracting data extension");
601  return rc;
602  }
603 
604  rc = cpl_image_add(aImage->stat, aSubtract->stat);
605  if (rc != CPL_ERROR_NONE) {
606  cpl_msg_error(__func__, "failure for stat extension");
607  return rc;
608  }
609 
610  rc = muse_image_dq_merge(aImage->dq, aSubtract->dq);
611  if (rc) {
612  cpl_msg_error(__func__, "failure for dq extension");
613  return rc;
614  }
615 
616  return 0;
617 } /* muse_image_subtract() */
618 
619 /*---------------------------------------------------------------------------*/
637 /*---------------------------------------------------------------------------*/
638 int
640 {
641  cpl_ensure(aImage, CPL_ERROR_NULL_INPUT, -1);
642  cpl_ensure(aDivisor, CPL_ERROR_NULL_INPUT, -2);
643 
644  /* Gaussian error propagation for a division:
645  * variance_out = (variance_in + in^2 * variance_divisor / divisor^2 ) / divisor^2
646  * ^------------- term2 -------------^
647  */
648  cpl_image *term2 = cpl_image_power_create(aImage->data, 2);
649 
650  cpl_error_code rc = cpl_image_divide(aImage->data, aDivisor->data);
651  if (rc != CPL_ERROR_NONE) {
652  cpl_msg_error(__func__, "failure while dividing data extension");
653  cpl_image_delete(term2);
654  return rc;
655  }
656 
657  cpl_image *divsq = cpl_image_power_create(aDivisor->data, 2);
658  rc = cpl_image_multiply(term2, aDivisor->stat);
659 
660  /* first access to stat, better check error */
661  if (rc != CPL_ERROR_NONE) {
662  cpl_msg_error(__func__, "failure while accessing stat extension of divisor");
663  cpl_image_delete(term2);
664  cpl_image_delete(divsq);
665  return rc;
666  }
667  cpl_image_divide(term2, divsq);
668  rc = cpl_image_add(aImage->stat, term2);
669  if (rc != CPL_ERROR_NONE) {
670  cpl_msg_error(__func__, "failure while accessing stat extension of image");
671  cpl_image_delete(term2);
672  cpl_image_delete(divsq);
673  return rc;
674  }
675  cpl_image_delete(term2);
676  cpl_image_divide(aImage->stat, divsq);
677  cpl_image_delete(divsq);
678 
679  rc = muse_image_dq_merge(aImage->dq, aDivisor->dq);
680  if (rc) {
681  cpl_msg_error(__func__, "failure for dq extension");
682  return rc;
683  }
684  return 0;
685 } /* muse_image_divide() */
686 
687 /*---------------------------------------------------------------------------*/
701 /*---------------------------------------------------------------------------*/
702 int
703 muse_image_scale(muse_image *aImage, double aScale)
704 {
705  cpl_ensure(aImage, CPL_ERROR_NULL_INPUT, -1);
706 
707  /* scale the data extension */
708  cpl_error_code rc = cpl_image_multiply_scalar(aImage->data, aScale);
709  if (rc != CPL_ERROR_NONE) {
710  cpl_msg_error(__func__, "failure while scaling data extension");
711  return rc;
712  }
713 
714  /* scale the stat extension (squared as this is the variance) */
715  rc = cpl_image_multiply_scalar(aImage->stat, aScale*aScale);
716  if (rc != CPL_ERROR_NONE) {
717  cpl_msg_error(__func__, "failure while scaling stat extension");
718  return rc;
719  }
720 
721  return 0;
722 } /* muse_image_scale() */
723 
724 /*---------------------------------------------------------------------------*/
745 /*---------------------------------------------------------------------------*/
746 int
748 {
749  cpl_ensure(aImage, CPL_ERROR_NULL_INPUT, -1);
750  cpl_ensure(aBias, CPL_ERROR_NULL_INPUT, -2);
751  int nx = cpl_image_get_size_x(aImage->stat),
752  ny = cpl_image_get_size_y(aImage->stat),
753  nxbias = cpl_image_get_size_x(aBias->stat),
754  nybias = cpl_image_get_size_y(aBias->stat);
755  cpl_ensure(nx == nxbias && ny == nybias, CPL_ERROR_INCOMPATIBLE_INPUT, -3);
756 
757  /* the easiest is, to remove the existing stat extension and *
758  * create a new image in its place (otherwise one would need *
759  * to check that it is still zero everywhere */
760  cpl_image_delete(aImage->stat);
761  aImage->stat = cpl_image_subtract_create(aImage->data, aBias->data);
762  float *pixstat = cpl_image_get_data_float(aImage->stat);
763 
764  /* now loop through all quadrants and their pixels and divide *
765  * by the gain value */
766  unsigned char n;
767  for (n = 1; n <= 4; n++) {
768  double gain = muse_pfits_get_gain(aImage->header, n); /* in count/adu */
769  cpl_size *window = muse_quadrants_get_window(aImage, n);
770 
771  int i;
772  for (i = window[0] - 1; i < window[1]; i++) {
773  int j;
774  for (j = window[2] - 1; j < window[3]; j++) {
775  pixstat[i + j*nx] /= gain;
776  /* if the photon variance turns out to be too low, we have to reset *
777  * it, because variance cannot take a negative or zero value */
778  if (pixstat[i + j*nx] <= 0.0) {
779  /* set it to the smallest value possible for 32bit float data */
780  pixstat[i + j*nx] = FLT_MIN;
781  }
782  } /* for j (y pixels) */
783  } /* for i (x pixels) */
784  cpl_free(window);
785  } /* for n (quadrants) */
786 
787  return 0;
788 } /* muse_image_variance_create() */
789 
790 /*---------------------------------------------------------------------------*/
809 /*---------------------------------------------------------------------------*/
810 cpl_error_code
812 {
813  cpl_ensure_code(aImage && aImage->header, CPL_ERROR_NULL_INPUT);
814  cpl_ensure_code(cpl_propertylist_has(aImage->header, "BUNIT") &&
815  !strncmp(muse_pfits_get_bunit(aImage->header), "adu", 4),
816  CPL_ERROR_INCOMPATIBLE_INPUT);
817  int nx = cpl_image_get_size_x(aImage->data);
818  float *data = cpl_image_get_data_float(aImage->data),
819  *stat = cpl_image_get_data_float(aImage->stat);
820  cpl_ensure_code(data && stat, CPL_ERROR_ILLEGAL_INPUT);
821 
822  unsigned char n;
823  for (n = 1; n <= 4; n++) {
824  double gain = muse_pfits_get_gain(aImage->header, n); /* gain in count/adu */
825  cpl_size *w = muse_quadrants_get_window(aImage, n);
826 #if 0
827  cpl_msg_debug(__func__, "looping %"CPL_SIZE_FORMAT"...%"CPL_SIZE_FORMAT
828  " %"CPL_SIZE_FORMAT"...%"CPL_SIZE_FORMAT"",
829  w[0], w[1], w[2], w[3]);
830 #endif
831  /* loop through this window and multiply counts by gain to get count */
832  int i;
833  for (i = w[0] - 1; i < w[1]; i++) {
834  int j;
835  for (j = w[2] - 1; j < w[3]; j++) {
836  data[i + j*nx] *= gain;
837  stat[i + j*nx] *= gain*gain;
838  } /* for j (window y pixels) */
839  } /* for i (window x pixels) */
840  cpl_free(w);
841  } /* for n (quadrants) */
842  cpl_propertylist_update_string(aImage->header, "BUNIT", "count");
843  cpl_propertylist_set_comment(aImage->header, "BUNIT", "DATA is in electrons");
844  return CPL_ERROR_NONE;
845 } /* muse_image_adu_to_count() */
846 
847 /*---------------------------------------------------------------------------*/
861 /*---------------------------------------------------------------------------*/
862 cpl_error_code
864 {
865  cpl_ensure_code(aImage && aImage->data && aImage->dq, CPL_ERROR_NULL_INPUT);
866  int nx = cpl_image_get_size_x(aImage->data),
867  ny = cpl_image_get_size_y(aImage->data);
868  const int *dq = cpl_image_get_data_int_const(aImage->dq);
869  int i, j;
870  for (i = 0; i < nx; i++) {
871  for (j = 0; j < ny; j++) {
872  if (!dq[i + j*nx]) { /* dq == 0 means bad */
873  continue;
874  }
875  cpl_image_reject(aImage->data, i+1, j+1);
876  if (aImage->stat) {
877  cpl_image_reject(aImage->stat, i+1, j+1);
878  }
879  } /* for j (columns) */
880  } /* for i (rows) */
881 
882  return CPL_ERROR_NONE;
883 } /* muse_image_reject_from_dq() */
884 
885 /*---------------------------------------------------------------------------*/
902 /*---------------------------------------------------------------------------*/
903 cpl_error_code
905 {
906  cpl_ensure_code(aImage && aImage->data && aImage->dq, CPL_ERROR_NULL_INPUT);
907 
908  /* get pointers to the 2 mandatory components */
909  int *pdq = cpl_image_get_data_int(aImage->dq);
910  float *pdata = cpl_image_get_data_float(aImage->data),
911  *pstat = NULL;
912  /* get the pointers to optional stat component as well, if possible */
913  if (aImage->stat) {
914  pstat = cpl_image_get_data_float(aImage->stat);
915  }
916  int i, nx = cpl_image_get_size_x(aImage->data),
917  ny = cpl_image_get_size_y(aImage->data);
918  for (i = 0; i < nx; i++) {
919  int j;
920  for (j = 0; j < ny; j++) {
921  if (pdq[i + j*nx] == EURO3D_GOODPIXEL) {
922  continue; /* nothing to do for good pixels */
923  }
924  /* set bad pixels of any type to not-a-number in both extensions */
925  pdata[i + j*nx] = NAN; /* supposed to be quiet NaN */
926  if (pstat) {
927  pstat[i + j*nx] = NAN;
928  }
929  } /* for j (y direction) */
930  } /* for i (x direction) */
931 
932  /* deallocate DQ and set it to NULL to be able to check for it */
933  cpl_image_delete(aImage->dq);
934  aImage->dq = NULL;
935 
936  return CPL_ERROR_NONE;
937 } /* muse_image_dq_to_nan() */
938 
void muse_image_delete(muse_image *aImage)
Deallocate memory associated to a muse_image object.
Definition: muse_image.c:85
cpl_boolean muse_pfits_get_chip_live(const cpl_propertylist *aHeaders)
find out if the CCD was active (live)
Definition: muse_pfits.c:639
int muse_image_divide(muse_image *aImage, muse_image *aDivisor)
Divide a muse_image by another with correct treatment of bad pixels and variance. ...
Definition: muse_image.c:639
const char * muse_pfits_get_extname(const cpl_propertylist *aHeaders)
find out the extension name
Definition: muse_pfits.c:205
cpl_size * muse_quadrants_get_window(const muse_image *aImage, unsigned char aQuadrant)
Determine the data window of a given quadrant on the CCD.
int muse_image_scale(muse_image *aImage, double aScale)
Scale a muse_image with correct treatment of variance.
Definition: muse_image.c:703
double muse_pfits_get_gain(const cpl_propertylist *aHeaders, unsigned char aQuadrant)
find the detector gain (in units of count/adu)
Definition: muse_pfits.c:686
cpl_image * data
the data extension
Definition: muse_image.h:46
muse_image * muse_image_load_from_raw(const char *aFilename, int aExtension)
Load raw image into the data extension of a MUSE image.
Definition: muse_image.c:292
static muse_image * muse_image_load_internal(const char *aFilename, unsigned char aIFU, const char *aID)
Load the three extensions and the FITS headers of a MUSE image.
Definition: muse_image.c:117
muse_image * muse_image_duplicate(const muse_image *aImage)
Duplicate the three image extensions and the FITS headers of a MUSE image.
Definition: muse_image.c:510
cpl_image * stat
the statistics extension
Definition: muse_image.h:64
int muse_image_subtract(muse_image *aImage, muse_image *aSubtract)
Subtract a muse_image from another with correct treatment of bad pixels and variance.
Definition: muse_image.c:593
const char * muse_pfits_get_bunit(const cpl_propertylist *aHeaders)
find out the unit string
Definition: muse_pfits.c:223
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:40
cpl_propertylist * header
the FITS header
Definition: muse_image.h:72
cpl_error_code muse_utils_set_hduclass(cpl_propertylist *aHeader, const char *aClass2, const char *aExtData, const char *aExtDQ, const char *aExtStat)
Set HDU headers for the ESO FITS data format.
Definition: muse_utils.c:2611
int muse_image_variance_create(muse_image *aImage, muse_image *aBias)
Create the photon noise-based variance in the stat extension.
Definition: muse_image.c:747
cpl_image * dq
the data quality extension
Definition: muse_image.h:56
#define MUSE_WCS_KEYS
regular expression for WCS properties
Definition: muse_wcs.h:48
cpl_error_code muse_image_save(muse_image *aImage, const char *aFilename)
Save the three image extensions and the FITS headers of a MUSE image to a file.
Definition: muse_image.c:405
muse_image * muse_image_load(const char *aFilename)
Load the three extensions and the FITS headers of a MUSE image from a file.
Definition: muse_image.c:228
cpl_error_code muse_image_reject_from_dq(muse_image *aImage)
Reject pixels of a muse_image depending on its DQ data.
Definition: muse_image.c:863
muse_image * muse_image_new(void)
Allocate memory for a new muse_image object.
Definition: muse_image.c:66
cpl_error_code muse_image_adu_to_count(muse_image *aImage)
Convert the data units from raw adu to count (= electron) units.
Definition: muse_image.c:811
muse_image * muse_image_load_from_extensions(const char *aFilename, unsigned char aIFU)
Load the three extensions and the FITS headers of a MUSE image from extensions of a merged file...
Definition: muse_image.c:262
cpl_error_code muse_image_dq_to_nan(muse_image *aImage)
Convert pixels flagged in the DQ extension to NANs in DATA (and STAT, if present).
Definition: muse_image.c:904