MUSE Pipeline Reference Manual  0.18.1
muse_artifacts.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-2014 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 <math.h>
31 
32 #include "muse_artifacts.h"
33 
34 #include "muse_quality.h"
35 
36 /*----------------------------------------------------------------------------*/
43 /*----------------------------------------------------------------------------*/
44 
47 /*----------------------------------------------------------------------------*/
66 /*----------------------------------------------------------------------------*/
67 static int
68 muse_cosmics_dcr_subframe(muse_image *aImage,
69  int aX1, int aX2, int aY1, int aY2,
70  float aThres, unsigned short aDebug)
71 {
72  float *data = cpl_image_get_data_float(aImage->data);
73  int *dq = cpl_image_get_data_int(aImage->dq);
74 
75  /* compute mean and standard deviation for good pixels, */
76  double dsum = 0., dsq = 0.,
77  min = FLT_MAX, max = -FLT_MAX;
78  int i, ngood = 0,
79  nx = cpl_image_get_size_x(aImage->data),
80  ny = cpl_image_get_size_y(aImage->data);
81  for (i = aX1 - 1; i < aX2 && i < nx; i++) {
82  int j;
83  for (j = aY1 - 1; j < aY2 && j < ny; j++) {
84  if (dq[i + j*nx]) { /* exclude bad pixels */
85  continue;
86  }
87  dsum += data[i + j*nx];
88  dsq += data[i + j*nx]*data[i + j*nx];
89  ngood++;
90  if (data[i + j*nx] < min) {
91  min = data[i + j*nx];
92  }
93  if (data[i + j*nx] > max) {
94  max = data[i + j*nx];
95  }
96  } /* for j */
97  } /* for i */
98  /* sigma = sqrt((sum(ci^2) - (sum(ci))^2 / n) / n) as defined by Pych */
99  double mean = dsum / ngood, /* simple average */
100  sigma = sqrt((dsq - dsum*dsum / ngood) / ngood),
101  lo = mean - sigma * aThres,
102  hi = mean + sigma * aThres;
103  if (aDebug > 1) {
104  printf(" stats(1): %f+/-%f -> limits=%f...%f, extremes=%f...%f\n",
105  mean, sigma, lo, hi, min, max);
106  fflush(stdout);
107  }
108 
109  /* compute mean and standard deviation for good pixels between the limits *
110  * at the same time create the histogram of good points, even outside limits */
111 #define HIST_BIN_WIDTH 1. /* which of each histogram bin in counts */
112  long hlength = lround((max - min) / HIST_BIN_WIDTH + 1);
113  int *hist = cpl_calloc(hlength, sizeof(int));
114  dsum = 0.;
115  dsq = 0.;
116  ngood = 0;
117  for (i = aX1 - 1; i < aX2 && i < nx; i++) {
118  int j;
119  for (j = aY1 - 1; j < aY2 && j < ny; j++) {
120  if (dq[i + j*nx]) { /* exclude bad pixels */
121  continue;
122  }
123  /* only good pixels into histogram, add to the appropriate bin */
124  hist[lround((data[i + j*nx] - min) / HIST_BIN_WIDTH)] += 1;
125  if (data[i + j*nx] > lo && data[i + j*nx] < hi) {
126  /* add only good pixels that are between thresholds into statistics */
127  dsum += data[i + j*nx];
128  dsq += data[i + j*nx]*data[i + j*nx];
129  ngood++;
130  } /* if between limits */
131  } /* for j */
132  } /* for i */
133  mean = dsum / ngood;
134  sigma = sqrt((dsq - dsum*dsum / ngood) / ngood);
135 
136  /* Find the mode of the distribution of counts *
137  * (i.e., the peak of the histogram). */
138  cpl_array *histogram = cpl_array_wrap_int(hist, hlength);
139  cpl_size nmaxpos; /* the mode of the histogram distribution */
140  cpl_array_get_maxpos(histogram, &nmaxpos);
141  if (aDebug > 1) {
142  printf(" stats(2): %f+/-%f, histogram: length=%ld, peak=%.0f at %f "
143  "(%"CPL_SIZE_FORMAT")\n", mean, sigma, hlength,
144  cpl_array_get_max(histogram), (double)nmaxpos * HIST_BIN_WIDTH + min,
145  nmaxpos);
146  if (aDebug > 2) {
147  printf(" histogram:\n entry value number\n");
148  for (i = 0; i < hlength; i++) {
149  printf(" %7d %10.3f %8d\n", i, i * HIST_BIN_WIDTH + min, hist[i]);
150  }
151  }
152  fflush(stdout);
153  }
154 
155  /* Find the first gap that is wider than a threshold, which is the standard *
156  deviation multiplied by an arbitrary number (usually 3.0). */
157  double gapwidth = sigma * aThres;
158 
159  for (i = nmaxpos ; i < hlength; i++) {
160  if (hist[i] == 0) {
161  /* we found the start of a gap, search the end of it */
162  int j = i + 1;
163  while (j < hlength && !hist[j]) {
164  j++;
165  }
166  if (j >= hlength) {
167  /* there is no gap (left) in this histogram, we are done */
168  cpl_array_delete(histogram); /* clean up array and the data buffer */
169  return 0;
170  }
171  double gap = (double)(j - i) * HIST_BIN_WIDTH;
172  if (gap > gapwidth) {
173  /* found the right gap, starting at i */
174  break;
175  }
176  i = j; /* else jump to the end of the gap */
177  } /* if gap */
178  } /* for i */
179  cpl_array_delete(histogram); /* clean up array and the data buffer */
180 
181  /* if we are at the end of the histogram, we *
182  * haven't found a gap of the necessary size */
183  if (i >= hlength) {
184  return 0;
185  }
186 
187  /* If such a gap exists, flag pixels with counts lying *
188  * above the gap as affected by cosmic rays. */
189  hi = i * HIST_BIN_WIDTH + min;
190  if (aDebug > 1) {
191  printf(" flagging pixels above %.3f counts (%d of %ld in histogram)\n",
192  hi, i+1, hlength);
193  fflush(stdout);
194  }
195  int ncr = 0;
196  for (i = aX1 - 1; i < aX2 && i < nx; i++) {
197  int j;
198  for (j = aY1 - 1; j < aY2 && j < ny; j++) {
199  if (!dq[i + j*nx] && data[i + j*nx] > hi) { /* flag only good pixels */
200  dq[i + j*nx] |= EURO3D_COSMICRAY;
201  ncr++;
202  } /* if good pixel above gap */
203  } /* for j */
204  } /* for i */
205 
206  return ncr;
207 } /* muse_cosmics_dcr_subframe() */
208 
209 /*----------------------------------------------------------------------------*/
263 /*----------------------------------------------------------------------------*/
264 int
265 muse_cosmics_dcr(muse_image *aImage, unsigned int aXBox, unsigned int aYBox,
266  unsigned int aPasses, float aThres)
267 {
268  cpl_ensure(aImage, CPL_ERROR_NULL_INPUT, -1);
269  cpl_ensure(aThres > 0, CPL_ERROR_ILLEGAL_INPUT, -2);
270  cpl_ensure(aPasses > 0, CPL_ERROR_ILLEGAL_INPUT, -3);
271  int nx = cpl_image_get_size_x(aImage->data),
272  ny = cpl_image_get_size_y(aImage->data);
273  cpl_ensure(aXBox <= (unsigned int)nx, CPL_ERROR_ILLEGAL_INPUT, -4);
274  cpl_ensure(aYBox <= (unsigned int)ny, CPL_ERROR_ILLEGAL_INPUT, -5);
275 
276  if (aXBox * aYBox < 100) {
277  cpl_msg_warning(__func__, "Boxes containing more than 100 pixels are "
278  "recommended for DCR!");
279  }
280  cpl_msg_debug(__func__, "Cosmic ray rejection using DCR: subframe %dx%d "
281  "(%d pixels/subframe), %d passes, threshold %.3f sigma)",
282  aXBox, aYBox, aXBox * aYBox, aPasses, aThres);
283 
284  /* init for extra debug output based on environment variable */
285  char *dodebug = getenv("MUSE_DEBUG_DCR");
286  unsigned short debug = dodebug ? atoi(dodebug) : 0,
287  sfdebug = debug;
288 
289  unsigned npass, ncr = 0;
290  for (npass = 1; npass <= aPasses; npass++) {
291  /* keep track of top/rightmost pixel covered by standard boxes */
292  int imax = 0, jmax = 0;
293 
294  /* run CR operation in standard boxes, offsetting by half a box size */
295  unsigned i;
296  for (i = 1; i <= nx - aXBox + 1; i+=aXBox/2) {
297  unsigned j;
298  for (j = 1; j <= ny - aYBox + 1; j+=aYBox/2) {
299  if (i + aXBox > (unsigned)imax) {
300  imax = i + aXBox;
301  }
302  if (j + aYBox > (unsigned)jmax) {
303  jmax = j + aYBox;
304  }
305  if (debug) {
306  printf("subframe [%d:%d,%d:%d] (standard)\n",
307  i, i + aXBox, j, j + aYBox);
308  fflush(stdout);
309  }
310  int npx = muse_cosmics_dcr_subframe(aImage, i, i + aXBox, j, j + aYBox,
311  aThres, debug);
312  ncr += npx;
313  if (debug && npx) {
314  printf("%8d affected pixels\n", npx);
315  fflush(stdout);
316  }
317  } /* for j (box positions in y) */
318 
319  if (jmax < ny) {
320  /* run CR operation in boxes at upper edge of the image */
321  if (debug) {
322  printf("subframe [%d:%d,%d:%d] (upper)\n",
323  i, i + aXBox, ny - aYBox + 1, ny);
324  fflush(stdout);
325  }
326  int npx = muse_cosmics_dcr_subframe(aImage, i, i + aXBox, ny - aYBox + 1, ny,
327  aThres, debug);
328  ncr += npx;
329  if (debug && npx) {
330  printf("%8d affected pixels\n", npx);
331  fflush(stdout);
332  }
333  }
334  } /* for i (box positions in x) */
335  if (sfdebug) {
336  printf("standard subframe coverage to [%d,%d] (image has %dx%d)\n",
337  imax, jmax, nx, ny);
338  fflush(stdout);
339  sfdebug = 0; /* only need this output once */
340  }
341 
342  if (imax < nx) {
343  /* run CR operation in boxes at right edge of the image */
344  unsigned j;
345  for (j = 1; j <= ny - aYBox + 1; j+=aYBox/2) {
346  if (debug) {
347  printf("subframe [%d:%d,%d:%d] (right)\n",
348  nx - aXBox + 1, nx, j, j + aYBox);
349  fflush(stdout);
350  }
351  int npx = muse_cosmics_dcr_subframe(aImage, nx - aXBox + 1, nx, j, j + aYBox,
352  aThres, debug);
353  ncr += npx;
354  if (debug && npx) {
355  printf("%8d affected pixels\n", npx);
356  fflush(stdout);
357  }
358  } /* for j (box positions in y) */
359  }
360 
361  if (imax < nx && jmax < ny) {
362  /* run CR operation in box at upper right corner of the image */
363  if (debug) {
364  printf("subframe [%d:%d,%d:%d] (corner)\n",
365  nx - aXBox + 1, nx, ny - aXBox + 1, ny);
366  fflush(stdout);
367  }
368  int npx = muse_cosmics_dcr_subframe(aImage, nx - aXBox + 1, nx, ny - aXBox + 1, ny,
369  aThres, debug);
370  ncr += npx;
371  if (debug && npx) {
372  printf("%8d affected pixels\n", npx);
373  fflush(stdout);
374  }
375  }
376 
377  cpl_msg_debug(__func__, "%d pixels found after pass %d", ncr, npass);
378  } /* for npass (passes) */
379 
380  return ncr;
381 } /* muse_cosmics_dcr() */
382 
cpl_image * data
the data extension
Definition: muse_image.h:47
int muse_cosmics_dcr(muse_image *aImage, unsigned int aXBox, unsigned int aYBox, unsigned int aPasses, float aThres)
Quickly mark cosmic rays in an image using the DCR algorithm.
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:41
cpl_image * dq
the data quality extension
Definition: muse_image.h:57