MUSE Pipeline Reference Manual  2.1.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 > 2) {
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  if (!isfinite(mean) || !isfinite(sigma) || !isfinite(lo) || !isfinite(hi) ||
109  min == FLT_MAX || max == -FLT_MAX) {
110  if (aDebug > 0) {
111  printf("No good pixels found or statistics are infinite!\n"
112  " stats: %f+/-%f -> limits=%f...%f, extremes=%f...%f\n",
113  mean, sigma, lo, hi, min, max);
114  fflush(stdout);
115  }
116  return 0;
117  }
118 
119  /* compute mean and standard deviation for good pixels between the limits *
120  * at the same time create the histogram of good points, even outside limits */
121 #define HIST_BIN_WIDTH 1. /* which of each histogram bin in counts */
122  long hlength = lround((max - min) / HIST_BIN_WIDTH + 1);
123  int *hist = cpl_calloc(hlength, sizeof(int));
124  dsum = 0.;
125  dsq = 0.;
126  ngood = 0;
127  for (i = aX1 - 1; i < aX2 && i < nx; i++) {
128  int j;
129  for (j = aY1 - 1; j < aY2 && j < ny; j++) {
130  if (dq[i + j*nx]) { /* exclude bad pixels */
131  continue;
132  }
133  /* only good pixels into histogram, add to the appropriate bin */
134  hist[lround((data[i + j*nx] - min) / HIST_BIN_WIDTH)] += 1;
135  if (data[i + j*nx] > lo && data[i + j*nx] < hi) {
136  /* add only good pixels that are between thresholds into statistics */
137  dsum += data[i + j*nx];
138  dsq += data[i + j*nx]*data[i + j*nx];
139  ngood++;
140  } /* if between limits */
141  } /* for j */
142  } /* for i */
143  mean = dsum / ngood;
144  sigma = sqrt((dsq - dsum*dsum / ngood) / ngood);
145 
146  /* Find the mode of the distribution of counts *
147  * (i.e., the peak of the histogram). */
148  cpl_array *histogram = cpl_array_wrap_int(hist, hlength);
149  cpl_size nmaxpos; /* the mode of the histogram distribution */
150  cpl_array_get_maxpos(histogram, &nmaxpos);
151  if (aDebug > 2) {
152  printf(" stats(2): %f+/-%f, histogram: length=%ld, peak=%.0f at %f "
153  "(%"CPL_SIZE_FORMAT")\n", mean, sigma, hlength,
154  cpl_array_get_max(histogram), (double)nmaxpos * HIST_BIN_WIDTH + min,
155  nmaxpos);
156  if (aDebug > 3) {
157  printf(" histogram:\n entry value number\n");
158  for (i = 0; i < hlength; i++) {
159  printf(" %7d %10.3f %8d\n", i, i * HIST_BIN_WIDTH + min, hist[i]);
160  }
161  }
162  fflush(stdout);
163  }
164 
165  /* Find the first gap that is wider than a threshold, which is the standard *
166  deviation multiplied by an arbitrary number (usually 3.0). */
167  double gapwidth = sigma * aThres;
168 
169  for (i = nmaxpos ; i < hlength; i++) {
170  if (hist[i] == 0) {
171  /* we found the start of a gap, search the end of it */
172  int j = i + 1;
173  while (j < hlength && !hist[j]) {
174  j++;
175  }
176  if (j >= hlength) {
177  /* there is no gap (left) in this histogram, we are done */
178  cpl_array_delete(histogram); /* clean up array and the data buffer */
179  return 0;
180  }
181  double gap = (double)(j - i) * HIST_BIN_WIDTH;
182  if (gap > gapwidth) {
183  /* found the right gap, starting at i */
184  break;
185  }
186  i = j; /* else jump to the end of the gap */
187  } /* if gap */
188  } /* for i */
189  cpl_array_delete(histogram); /* clean up array and the data buffer */
190 
191  /* if we are at the end of the histogram, we *
192  * haven't found a gap of the necessary size */
193  if (i >= hlength) {
194  return 0;
195  }
196 
197  /* If such a gap exists, flag pixels with counts lying *
198  * above the gap as affected by cosmic rays. */
199  hi = i * HIST_BIN_WIDTH + min;
200  if (aDebug > 2) {
201  printf(" flagging pixels above %.3f counts (%d of %ld in histogram)\n",
202  hi, i+1, hlength);
203  fflush(stdout);
204  }
205  int ncr = 0;
206  for (i = aX1 - 1; i < aX2 && i < nx; i++) {
207  int j;
208  for (j = aY1 - 1; j < aY2 && j < ny; j++) {
209  if (!dq[i + j*nx] && data[i + j*nx] > hi) { /* flag only good pixels */
210  dq[i + j*nx] |= EURO3D_COSMICRAY;
211  ncr++;
212  } /* if good pixel above gap */
213  } /* for j */
214  } /* for i */
215 
216  return ncr;
217 } /* muse_cosmics_dcr_subframe() */
218 
219 /*----------------------------------------------------------------------------*/
273 /*----------------------------------------------------------------------------*/
274 int
275 muse_cosmics_dcr(muse_image *aImage, unsigned int aXBox, unsigned int aYBox,
276  unsigned int aPasses, float aThres)
277 {
278  cpl_ensure(aImage, CPL_ERROR_NULL_INPUT, -1);
279  cpl_ensure(aThres > 0, CPL_ERROR_ILLEGAL_INPUT, -2);
280  cpl_ensure(aPasses > 0, CPL_ERROR_ILLEGAL_INPUT, -3);
281  int nx = cpl_image_get_size_x(aImage->data),
282  ny = cpl_image_get_size_y(aImage->data);
283  cpl_ensure(aXBox <= (unsigned int)nx, CPL_ERROR_ILLEGAL_INPUT, -4);
284  cpl_ensure(aYBox <= (unsigned int)ny, CPL_ERROR_ILLEGAL_INPUT, -5);
285 
286  if (aXBox * aYBox < 100) {
287  cpl_msg_warning(__func__, "Boxes containing more than 100 pixels are "
288  "recommended for DCR!");
289  }
290  /* init for extra debug output based on environment variable */
291  char *dodebug = getenv("MUSE_DEBUG_DCR");
292  unsigned short debug = dodebug ? atoi(dodebug) : 0,
293  sfdebug = debug > 1;
294  if (debug) {
295  cpl_msg_debug(__func__, "Cosmic ray rejection using DCR: subframe %dx%d "
296  "(%d pixels/subframe), %d passes, threshold %.3f sigma)",
297  aXBox, aYBox, aXBox * aYBox, aPasses, aThres);
298  }
299 
300  unsigned npass, ncr = 0;
301  for (npass = 1; npass <= aPasses; npass++) {
302  /* keep track of top/rightmost pixel covered by standard boxes */
303  int imax = 0, jmax = 0;
304  /* check detection in this pass */
305  unsigned ncrpass = 0;
306 
307  /* run CR operation in standard boxes, offsetting by half a box size */
308  unsigned i;
309  for (i = 1; i <= nx - aXBox + 1; i+=aXBox/2) {
310  unsigned j;
311  for (j = 1; j <= ny - aYBox + 1; j+=aYBox/2) {
312  if (i + aXBox > (unsigned)imax) {
313  imax = i + aXBox;
314  }
315  if (j + aYBox > (unsigned)jmax) {
316  jmax = j + aYBox;
317  }
318  if (debug > 1) {
319  printf("subframe [%d:%d,%d:%d] (standard)\n",
320  i, i + aXBox, j, j + aYBox);
321  fflush(stdout);
322  }
323  int npx = muse_cosmics_dcr_subframe(aImage, i, i + aXBox, j, j + aYBox,
324  aThres, debug);
325  ncrpass += npx;
326  if (debug > 1 && npx) {
327  printf("%8d affected pixels\n", npx);
328  fflush(stdout);
329  }
330  } /* for j (box positions in y) */
331 
332  if (jmax < ny) {
333  /* run CR operation in boxes at upper edge of the image */
334  if (debug > 1) {
335  printf("subframe [%d:%d,%d:%d] (upper)\n",
336  i, i + aXBox, ny - aYBox + 1, ny);
337  fflush(stdout);
338  }
339  int npx = muse_cosmics_dcr_subframe(aImage, i, i + aXBox, ny - aYBox + 1, ny,
340  aThres, debug);
341  ncrpass += npx;
342  if (debug > 1 && npx) {
343  printf("%8d affected pixels\n", npx);
344  fflush(stdout);
345  }
346  }
347  } /* for i (box positions in x) */
348  if (sfdebug) {
349  printf("standard subframe coverage to [%d,%d] (image has %dx%d)\n",
350  imax, jmax, nx, ny);
351  fflush(stdout);
352  sfdebug = 0; /* only need this output once */
353  }
354 
355  if (imax < nx) {
356  /* run CR operation in boxes at right edge of the image */
357  unsigned j;
358  for (j = 1; j <= ny - aYBox + 1; j+=aYBox/2) {
359  if (debug > 1) {
360  printf("subframe [%d:%d,%d:%d] (right)\n",
361  nx - aXBox + 1, nx, j, j + aYBox);
362  fflush(stdout);
363  }
364  int npx = muse_cosmics_dcr_subframe(aImage, nx - aXBox + 1, nx, j, j + aYBox,
365  aThres, debug);
366  ncrpass += npx;
367  if (debug > 1 && npx) {
368  printf("%8d affected pixels\n", npx);
369  fflush(stdout);
370  }
371  } /* for j (box positions in y) */
372  } /* if imax < nx */
373 
374  if (imax < nx && jmax < ny) {
375  /* run CR operation in box at upper right corner of the image */
376  if (debug > 1) {
377  printf("subframe [%d:%d,%d:%d] (corner)\n",
378  nx - aXBox + 1, nx, ny - aXBox + 1, ny);
379  fflush(stdout);
380  }
381  int npx = muse_cosmics_dcr_subframe(aImage, nx - aXBox + 1, nx, ny - aXBox + 1, ny,
382  aThres, debug);
383  ncrpass += npx;
384  if (debug > 1 && npx) {
385  printf("%8d affected pixels\n", npx);
386  fflush(stdout);
387  }
388  } /* if imax < nx and jmax < ny */
389 
390  ncr += ncrpass;
391  if (debug) {
392  cpl_msg_debug(__func__, "%d (%d new) pixels found after pass %d", ncr,
393  ncrpass, npass);
394  } /* if debug */
395 
396  if (!ncrpass) {
397  break; /* stop early, if already the current pass didn't find new CRs */
398  } /* if */
399  } /* for npass (passes) */
400 
401  return ncr;
402 } /* muse_cosmics_dcr() */
403 
cpl_image * data
the data extension
Definition: muse_image.h:46
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:40
cpl_image * dq
the data quality extension
Definition: muse_image.h:56