LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
LSSTDataManagementBasePackage
CR.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008-2016 AURA/LSST.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <https://www.lsstcorp.org/LegalNotices/>.
23  */
24 
32 #include <stdexcept>
33 #include <algorithm>
34 #include <cassert>
35 #include <string>
36 #include <typeinfo>
37 
38 #include <iostream>
39 
40 
41 #include "boost/format.hpp"
42 
43 #include "lsst/pex/exceptions.h"
44 #include "lsst/pex/logging/Trace.h"
45 #include "lsst/pex/exceptions.h"
48 #include "lsst/afw/geom.h"
49 #include "lsst/afw/detection/Psf.h"
51 #include "lsst/afw/math/Random.h"
54 
59 namespace lsst {
60 namespace afw {
61 namespace detection {
65 class IdSpan {
66 public:
67  typedef std::shared_ptr<IdSpan> Ptr;
68  typedef std::shared_ptr<const IdSpan> ConstPtr;
69 
70  explicit IdSpan(int id, int y, int x0, int x1) : id(id), y(y), x0(x0), x1(x1) {}
71  int id; /* ID for object */
72  int y; /* Row wherein IdSpan dwells */
73  int x0, x1; /* inclusive range of columns */
74 };
78 struct IdSpanCompar : public std::binary_function<const IdSpan::ConstPtr, const IdSpan::ConstPtr, bool> {
80  if (a->id < b->id) {
81  return true;
82  } else if(a->id > b->id) {
83  return false;
84  } else {
85  if (a->y < b->y) {
86  return true;
87  } else if (a->y > b->y) {
88  return false;
89  } else {
90  return (a->x0 < b->x0) ? true : false;
91  }
92  }
93  }
94 };
98 int resolve_alias(const std::vector<int>& aliases, /* list of aliases */
99  int id) { /* alias to look up */
100  int resolved = id; /* resolved alias */
101 
102  while (id != aliases[id]) {
103  resolved = id = aliases[id];
104  }
105 
106  return(resolved);
107 }
108 }}} // namespace lsst::afw::detection
109 
110 namespace lsst {
111 namespace meas {
112 namespace algorithms {
113 
114 namespace geom = lsst::afw::geom;
115 namespace math = lsst::afw::math;
116 namespace image = lsst::afw::image;
117 namespace detection = lsst::afw::detection;
118 namespace pexLogging = lsst::pex::logging;
119 
120 namespace {
121 
122 template<typename ImageT, typename MaskT>
123 void removeCR(image::MaskedImage<ImageT, MaskT> & mi, std::vector<detection::Footprint::Ptr> & CRs,
124  double const bkgd, MaskT const , MaskT const saturBit, MaskT const badMask,
125  bool const debias, bool const grow);
126 
127 template<typename ImageT>
128 bool condition_3(ImageT *estimate, double const peak,
129  double const mean_ns, double const mean_we, double const mean_swne, double const mean_nwse,
130  double const dpeak,
131  double const dmean_ns, double const dmean_we,double const dmean_swne,double const dmean_nwse,
132  double const thresH, double const thresV, double const thresD,
133  double const cond3Fac
134  );
135 
136 /************************************************************************************************************/
137 //
138 // A class to hold a detected pixel
139 template<typename ImageT>
140 struct CRPixel {
141  typedef typename std::shared_ptr<CRPixel> Ptr;
142 
143  CRPixel(int _col, int _row, ImageT _val, int _id = -1) :
144  id(_id), col(_col), row(_row), val(_val) {
145  _i = ++i;
146  }
147  ~CRPixel() {}
148 
149  bool operator< (const CRPixel& a) const {
150  return _i < a._i;
151  }
152 
153  int get_i() const {
154  return _i;
155  }
156 
157  int id; // Unique ID for cosmic ray (not cosmic ray pixel)
158  int col; // position
159  int row; // of pixel
160  ImageT val; // initial value of pixel
161 private:
162  static int i; // current value of running index
163  int mutable _i; // running index
164 };
165 
166 template<typename ImageT>
167 int CRPixel<ImageT>::i = 0;
168 
169 template<typename ImageT>
170 struct Sort_CRPixel_by_id {
171  bool operator() (CRPixel<ImageT> const & a, CRPixel<ImageT> const & b) const {
172  return a.id < b.id;
173  }
174 };
175 
176 /*****************************************************************************/
177 /*
178  * This is the code to see if a given pixel is bad
179  *
180  * Note that the pixel in question is at index 0, so its value is pt_0[0]
181  */
182 template <typename MaskedImageT>
183 bool is_cr_pixel(typename MaskedImageT::Image::Pixel *corr, // corrected value
184  typename MaskedImageT::xy_locator loc, // locator for this pixel
185  double const minSigma, // minSigma, or -threshold if negative
186  double const thresH, double const thresV, double const thresD, // for condition #3
187  double const bkgd, // unsubtracted background level
188  double const cond3Fac // fiddle factor for condition #3
189  )
190 {
191  typedef typename MaskedImageT::Image::Pixel ImagePixel;
192  //
193  // Unpack some values
194  //
195  ImagePixel const v_00 = loc.image(0, 0);
196 
197  if (v_00 < 0) {
198  return false;
199  }
200  /*
201  * condition #1 is not applied on a pixel-by-pixel basis
202  */
203  ;
204  /*
205  * condition #2
206  */
207  ImagePixel const mean_we = (loc.image(-1, 0) + loc.image( 1, 0))/2; // avgs of surrounding 8 pixels
208  ImagePixel const mean_ns = (loc.image( 0, 1) + loc.image( 0, -1))/2;
209  ImagePixel const mean_swne = (loc.image(-1, -1) + loc.image( 1, 1))/2;
210  ImagePixel const mean_nwse = (loc.image(-1, 1) + loc.image( 1, -1))/2;
211 
212  if (minSigma < 0) { /* |thres_sky_sigma| is threshold */
213  if (v_00 < -minSigma) {
214  return false;
215  }
216  } else {
217  double const thres_sky_sigma = minSigma*sqrt(loc.variance(0, 0));
218 
219  if (v_00 < mean_ns + thres_sky_sigma &&
220  v_00 < mean_we + thres_sky_sigma &&
221  v_00 < mean_swne + thres_sky_sigma &&
222  v_00 < mean_nwse + thres_sky_sigma) {
223  return false;
224  }
225  }
226 /*
227  * condition #3
228  *
229  * Note that this uses mean_ns etc. even if minSigma is negative
230  */
231  double const dv_00 = sqrt(loc.variance( 0, 0));
232  // standard deviation of means of surrounding pixels
233  double const dmean_we = sqrt(loc.variance(-1, 0) + loc.variance( 1, 0))/2;
234  double const dmean_ns = sqrt(loc.variance( 0, 1) + loc.variance( 0, -1))/2;
235  double const dmean_swne = sqrt(loc.variance(-1, -1) + loc.variance( 1, 1))/2;
236  double const dmean_nwse = sqrt(loc.variance(-1, 1) + loc.variance( 1, -1))/2;
237 
238  if (!condition_3(corr,
239  v_00 - bkgd, mean_ns - bkgd, mean_we - bkgd, mean_swne - bkgd, mean_nwse - bkgd,
240  dv_00, dmean_ns, dmean_we, dmean_swne, dmean_nwse,
241  thresH, thresV, thresD, cond3Fac)){
242  return false;
243  }
244 /*
245  * OK, it's a contaminated pixel
246  */
247  *corr += static_cast<ImagePixel>(bkgd);
248 
249  return true;
250 }
251 
252 /************************************************************************************************************/
253 //
254 // Worker routine to process the pixels adjacent to a span (including the points just
255 // to the left and just to the right)
256 //
257 template <typename MaskedImageT>
258 void checkSpanForCRs(detection::Footprint *extras, // Extra spans get added to this Footprint
259  std::vector<CRPixel<typename MaskedImageT::Image::Pixel> >& crpixels,
260  // a list of pixels containing CRs
261  int const y, // the row to process
262  int const x0, int const x1, // range of pixels in the span (inclusive)
263  MaskedImageT& image,
264  double const minSigma, // minSigma
265  double const thresH, double const thresV, double const thresD, // for cond. #3
266  double const bkgd, // unsubtracted background level
267  double const cond3Fac, // fiddle factor for condition #3
268  bool const keep // if true, don't remove the CRs
269  )
270 {
271  typedef typename MaskedImageT::Image::Pixel MImagePixel;
272  typename MaskedImageT::xy_locator loc = image.xy_at(x0 - 1, y); // locator for data
273 
274  int const imageX0 = image.getX0();
275  int const imageY0 = image.getY0();
276 
277  for (int x = x0 - 1; x <= x1 + 1; ++x) {
278  MImagePixel corr = 0; // new value for pixel
279  if (is_cr_pixel<MaskedImageT>(&corr, loc, minSigma, thresH, thresV, thresD,
280  bkgd, cond3Fac)) {
281  if (keep) {
282  crpixels.push_back(CRPixel<MImagePixel>(x + imageX0, y + imageY0, loc.image()));
283  }
284  loc.image() = corr;
285 
286  extras->addSpan(y + imageY0, x + imageX0, x + imageX0);
287  }
288  ++loc.x();
289  }
290 }
291 
292 /************************************************************************************************************/
293 /*
294  * Find the sum of the pixels in a Footprint
295  */
296 template <typename ImageT>
297 class CountsInCR : public detection::FootprintFunctor<ImageT> {
298 public:
299  CountsInCR(ImageT const& mimage, double const bkgd) :
300  detection::FootprintFunctor<ImageT>(mimage),
301  _bkgd(bkgd),
302  _sum(0.0) {}
303 
304  // method called for each pixel by apply()
305  void operator()(typename ImageT::xy_locator loc, // locator pointing at the pixel
306  int, int
307  ) {
308  _sum += *loc - _bkgd;
309  }
310 
311  virtual void reset(detection::Footprint const&) {}
312  virtual void reset() {
313  _sum = 0.0;
314  }
315 
316  double getCounts() const { return _sum; }
317 private:
318  double const _bkgd; // the Image's background level
319  typename ImageT::Pixel _sum; // the sum of all DN in the Footprint, corrected for bkgd
320 };
321 }
322 
323 /*
324  * Restore all the pixels in crpixels to their pristine state
325  */
326 template <typename ImageT>
327 static void reinstateCrPixels(
328  ImageT *image, // the image in question
329  std::vector<CRPixel<typename ImageT::Pixel> > const& crpixels // a list of pixels with CRs
330  )
331 {
332  if (crpixels.empty()) return;
333 
334  typedef typename std::vector<CRPixel<typename ImageT::Pixel> >::const_iterator crpixel_iter;
335  for (crpixel_iter crp = crpixels.begin(), end = crpixels.end(); crp < end - 1 ; ++crp) {
336  *image->at(crp->col - image->getX0(), crp->row - image->getY0()) = crp->val;
337  }
338 }
339 
345 template <typename MaskedImageT>
346 std::vector<detection::Footprint::Ptr>
347 findCosmicRays(MaskedImageT &mimage,
348  detection::Psf const &psf,
349  double const bkgd,
350  lsst::pex::policy::Policy const &policy,
351  bool const keep
352  ) {
353  typedef typename MaskedImageT::Image ImageT;
354  typedef typename ImageT::Pixel ImagePixel;
355  typedef typename MaskedImageT::Mask::Pixel MaskPixel;
356 
357  // Parse the Policy
358  double const minSigma = policy.getDouble("minSigma"); // min sigma over sky in pixel for CR candidate
359  double const minDn = policy.getDouble("min_DN"); // min number of DN in an CRs
360  double const cond3Fac = policy.getDouble("cond3_fac"); // fiddle factor for condition #3
361  double const cond3Fac2 = policy.getDouble("cond3_fac2"); // 2nd fiddle factor for condition #3
362  int const niteration = policy.getInt("niteration"); // Number of times to look for contaminated
363  // pixels near CRs
364  int const nCrPixelMax = policy.getInt("nCrPixelMax"); // maximum number of contaminated pixels
365 /*
366  * thresholds for 3rd condition
367  *
368  * Realise PSF at center of image
369  */
371  if (!kernel) {
372  throw LSST_EXCEPT(pexExcept::NotFoundError, "Psf is unable to return a kernel");
373  }
374  detection::Psf::Image psfImage = detection::Psf::Image(geom::ExtentI(kernel->getWidth(), kernel->getHeight()));
375  kernel->computeImage(psfImage, true);
376 
377  int const xc = kernel->getCtrX(); // center of PSF
378  int const yc = kernel->getCtrY();
379 
380  double const I0 = psfImage(xc, yc);
381  double const thresH = cond3Fac2*(0.5*(psfImage(xc - 1, yc) + psfImage(xc + 1, yc)))/I0; // horizontal
382  double const thresV = cond3Fac2*(0.5*(psfImage(xc, yc - 1) + psfImage(xc, yc + 1)))/I0; // vertical
383  double const thresD = cond3Fac2*(0.25*(psfImage(xc - 1, yc - 1) + psfImage(xc + 1, yc + 1) +
384  psfImage(xc - 1, yc + 1) + psfImage(xc + 1, yc - 1)))/I0; // diag
385 /*
386  * Setup desired mask planes
387  */
388  MaskPixel const badBit = mimage.getMask()->getPlaneBitMask("BAD"); // Generic bad pixels
389  MaskPixel const crBit = mimage.getMask()->getPlaneBitMask("CR"); // CR-contaminated pixels
390  MaskPixel const interpBit = mimage.getMask()->getPlaneBitMask("INTRP"); // Interpolated pixels
391  MaskPixel const saturBit = mimage.getMask()->getPlaneBitMask("SAT"); // Saturated pixels
392  MaskPixel const nodataBit = mimage.getMask()->getPlaneBitMask("NO_DATA"); // Non data pixels
393 
394  MaskPixel const badMask = (badBit | interpBit | saturBit | nodataBit); // naughty pixels
395 /*
396  * Go through the frame looking at each pixel (except the edge ones which we ignore)
397  */
398  int const ncol = mimage.getWidth();
399  int const nrow = mimage.getHeight();
400 
401  std::vector<CRPixel<ImagePixel> > crpixels; // storage for detected CR-contaminated pixels
402  typedef typename std::vector<CRPixel<ImagePixel> >::iterator crpixel_iter;
403  typedef typename std::vector<CRPixel<ImagePixel> >::reverse_iterator crpixel_riter;
404 
405  for (int j = 1; j < nrow - 1; ++j) {
406  typename MaskedImageT::xy_locator loc = mimage.xy_at(1, j); // locator for data
407 
408  for (int i = 1; i < ncol - 1; ++i, ++loc.x()) {
409  ImagePixel corr = 0;
410  if (!is_cr_pixel<MaskedImageT>(&corr, loc, minSigma,
411  thresH, thresV, thresD, bkgd, cond3Fac)) {
412  continue;
413  }
414 /*
415  * condition #4
416  */
417  if (loc.mask() & badMask) {
418  continue;
419  }
420  if ((loc.mask(-1, 1) | loc.mask(0, 1) | loc.mask(1, 1) |
421  loc.mask(-1, 0) | loc.mask(1, 0) |
422  loc.mask(-1, -1) | loc.mask(0, -1) | loc.mask(1, -1)) & interpBit) {
423  continue;
424  }
425 /*
426  * OK, it's a CR
427  *
428  * replace CR-contaminated pixels with reasonable values as we go through
429  * image, which increases the detection rate
430  */
431  crpixels.push_back(CRPixel<ImagePixel>(i + mimage.getX0(), j + mimage.getY0(), loc.image()));
432  loc.image() = corr; /* just a preliminary estimate */
433 
434  if (static_cast<int>(crpixels.size()) > nCrPixelMax) {
435  reinstateCrPixels(mimage.getImage().get(), crpixels);
436 
437  throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
438  (boost::format("Too many CR pixels (max %d)") % nCrPixelMax).str());
439  }
440  }
441  }
442 /*
443  * We've found them on a pixel-by-pixel basis, now merge those pixels
444  * into cosmic rays
445  */
446  std::vector<int> aliases; // aliases for initially disjoint parts of CRs
447  aliases.reserve(1 + crpixels.size()/2); // initial size of aliases
448 
449  std::vector<detection::IdSpan::Ptr> spans; // y:x0,x1 for objects
450  spans.reserve(aliases.capacity()); // initial size of spans
451 
452  aliases.push_back(0); // 0 --> 0
453 
459  int ncr = 0; // number of detected cosmic rays
460  if (!crpixels.empty()) {
461  int id; // id number for a CR
462  int x0 = -1, x1 = -1, y = -1; // the beginning and end column, and row of this span in a CR
463 
464  // I am dummy
465  CRPixel<ImagePixel> dummy(0, -1, 0, -1);
466  crpixels.push_back(dummy);
467  //printf("Created dummy CR: i %i, id %i, col %i, row %i, val %g\n", dummy.get_i(), dummy.id, dummy.col, dummy.row, (double)dummy.val);
468  for (crpixel_iter crp = crpixels.begin(); crp < crpixels.end() - 1 ; ++crp) {
469  //printf("Looking at CR: i %i, id %i, col %i, row %i, val %g\n", crp->get_i(), crp->id, crp->col, crp->row, (double)crp->val);
470 
471  if (crp->id < 0) { // not already assigned
472  crp->id = ++ncr; // a new CR
473  aliases.push_back(crp->id);
474  y = crp->row;
475  x0 = x1 = crp->col;
476  //printf(" Assigned ID %i; looking at row %i, start col %i\n", crp->id, crp->row, crp->col);
477  }
478  id = crp->id;
479  //printf(" Next CRpix has i=%i, id=%i, row %i, col %i\n", crp[1].get_i(), crp[1].id, crp[1].row, crp[1].col);
480 
481  if (crp[1].row == crp[0].row && crp[1].col == crp[0].col + 1) {
482  //printf(" Adjoining! Set next CRpix id = %i; x1=%i\n", crp[1].id, x1);
483  crp[1].id = id;
484  ++x1;
485  } else {
486  assert (y >= 0 && x0 >= 0 && x1 >= 0);
487  spans.push_back(detection::IdSpan::Ptr(new detection::IdSpan(id, y, x0, x1)));
488  //printf(" Not adjoining; adding span id=%i, y=%i, x = [%i, %i]\n", id, y, x0, x1);
489  }
490  }
491  }
492 
493  // At the end of this loop, all crpixel entries have been assigned an ID,
494  // except for the "dummy" entry at the end of the array.
495  if (crpixels.size() > 0) {
496  for (crpixel_iter cp = crpixels.begin(); cp != crpixels.end() - 1; cp++) {
497  assert(cp->id >= 0);
498  assert(cp->col >= 0);
499  assert(cp->row >= 0);
500  }
501  // dummy:
502  assert(crpixels[crpixels.size()-1].id == -1);
503  assert(crpixels[crpixels.size()-1].col == 0);
504  assert(crpixels[crpixels.size()-1].row == -1);
505  }
506 
507  for (std::vector<detection::IdSpan::Ptr>::iterator sp = spans.begin(), end = spans.end(); sp != end; sp++) {
508  assert((*sp)->id >= 0);
509  assert((*sp)->y >= 0);
510  assert((*sp)->x0 >= 0);
511  assert((*sp)->x1 >= (*sp)->x0);
512  for (std::vector<detection::IdSpan::Ptr>::iterator sp2 = sp + 1; sp2 != end; sp2++) {
513  assert((*sp2)->y >= (*sp)->y);
514  if ((*sp2)->y == (*sp)->y) {
515  assert((*sp2)->x0 > (*sp)->x1);
516  }
517  }
518  }
519 
520 /*
521  * See if spans touch each other
522  */
523  for (std::vector<detection::IdSpan::Ptr>::iterator sp = spans.begin(), end = spans.end();
524  sp != end; ++sp) {
525  int const y = (*sp)->y;
526  int const x0 = (*sp)->x0;
527  int const x1 = (*sp)->x1;
528 
529  // this loop will probably run for only a few steps
530  for (std::vector<detection::IdSpan::Ptr>::iterator sp2 = sp + 1; sp2 != end; ++sp2) {
531  if ((*sp2)->y == y) {
532  // on this row (but not adjoining columns, since it would have been merged into this span);
533  // keep looking.
534  continue;
535  } else if ((*sp2)->y != (y + 1)) {
536  // sp2 is more than one row below; can't be connected.
537  break;
538  } else if ((*sp2)->x0 > (x1 + 1)) {
539  // sp2 is more than one column away to the right; can't be connected
540  break;
541  } else if ((*sp2)->x1 >= (x0 - 1)) {
542  // touches
543  int r1 = detection::resolve_alias(aliases, (*sp)->id);
544  int r2 = detection::resolve_alias(aliases, (*sp2)->id);
545  aliases[r1] = r2;
546  }
547  }
548  }
549 
550 
551 
552 
553 
554 /*
555  * Resolve aliases; first alias chains, then the IDs in the spans
556  */
557  for (unsigned int i = 0; i != spans.size(); ++i) {
558  spans[i]->id = detection::resolve_alias(aliases, spans[i]->id);
559  }
560 
561 /*
562  * Sort spans by ID, so we can sweep through them once
563  */
564  if (spans.size() > 0) {
565  std::sort(spans.begin(), spans.end(), detection::IdSpanCompar());
566  }
567 
568 /*
569  * Build Footprints from spans
570  */
571  std::vector<detection::Footprint::Ptr> CRs; // our cosmic rays
572 
573  if (spans.size() > 0) {
574  int id = spans[0]->id;
575  unsigned int i0 = 0; // initial value of i
576  for (unsigned int i = i0; i <= spans.size(); ++i) { // <= size to catch the last object
577  if (i == spans.size() || spans[i]->id != id) {
579 
580  for (; i0 < i; ++i0) {
581  cr->addSpan(spans[i0]->y, spans[i0]->x0, spans[i0]->x1);
582  }
583  CRs.push_back(cr);
584  }
585 
586  if (i < spans.size()) {
587  id = spans[i]->id;
588  }
589  }
590  }
591 
592  reinstateCrPixels(mimage.getImage().get(), crpixels);
593 /*
594  * apply condition #1
595  */
596  CountsInCR<ImageT> CountDN(*mimage.getImage(), bkgd);
597  for (std::vector<detection::Footprint::Ptr>::iterator cr = CRs.begin(), end = CRs.end();
598  cr != end; ++cr) {
599  CountDN.apply(**cr); // find the sum of pixel values within the CR
600 
601  pexLogging::TTrace<10>("algorithms.CR", "CR at (%d, %d) has %g DN",
602  (*cr)->getBBox().getMinX(), (*cr)->getBBox().getMinY(), CountDN.getCounts());
603  if (CountDN.getCounts() < minDn) { /* not bright enough */
604  pexLogging::TTrace<11>("algorithms.CR", "Erasing CR");
605 
606  cr = CRs.erase(cr);
607  --cr; // back up to previous CR (we're going to increment it)
608  --end;
609  }
610  }
611  ncr = CRs.size(); /* some may have been too faint */
612 /*
613  * We've found them all, time to kill them all
614  */
615  bool const debias_values = true;
616  bool grow = false;
617  pexLogging::TTrace<2>("algorithms.CR", "Removing initial list of CRs");
618  removeCR(mimage, CRs, bkgd, crBit, saturBit, badMask, debias_values, grow);
619 #if 0 // Useful to see phase 2 in ds9; debugging only
620  (void)setMaskFromFootprintList(mimage.getMask().get(), CRs,
621  mimage.getMask()->getPlaneBitMask("DETECTED"));
622 #endif
623 /*
624  * Now that we've removed them, go through image again, examining area around
625  * each CR for extra bad pixels. Note that we set cond3Fac = 0 for this pass
626  *
627  * We iterate niteration times; niter==1 was sufficient for SDSS data, but megacam
628  * CCDs are different -- who knows for other devices?
629  */
630  bool too_many_crs = false; // we've seen too many CR pixels
631  int nextra = 0; // number of pixels added to list of CRs
632  for (int i = 0; i != niteration && !too_many_crs; ++i) {
633  pexLogging::TTrace<1>("algorithms.CR", "Starting iteration %d", i);
634  for (std::vector<detection::Footprint::Ptr>::iterator fiter = CRs.begin();
635  fiter != CRs.end(); fiter++) {
636  detection::Footprint::Ptr cr = *fiter;
637 /*
638  * Are all those `CR' pixels interpolated? If so, don't grow it
639  */
640  {
641  detection::Footprint::Ptr om = footprintAndMask(cr, mimage.getMask(), interpBit);
642  int const npix = (om) ? om->getNpix() : 0;
643 
644  if (npix == cr->getNpix()) {
645  continue;
646  }
647  }
648 /*
649  * No; some of the suspect pixels aren't interpolated
650  */
651  detection::Footprint extra; // extra pixels added to cr
652  for (detection::Footprint::SpanList::const_iterator siter = cr->getSpans().begin();
653  siter != cr->getSpans().end(); siter++) {
654  detection::Span::Ptr const span = *siter;
655 
656  /*
657  * Check the lines above and below the span. We're going to check a 3x3 region around
658  * the pixels, so we need a buffer around the edge. We check the pixels just to the
659  * left/right of the span, so the buffer needs to be 2 pixels (not just 1) in the
660  * column direction, but only 1 in the row direction.
661  */
662  int const y = span->getY() - mimage.getY0();
663  if (y < 2 || y >= nrow - 2) {
664  continue;
665  }
666  int x0 = span->getX0() - mimage.getX0();
667  int x1 = span->getX1() - mimage.getX0();
668  x0 = (x0 < 2) ? 2 : (x0 > ncol - 3) ? ncol - 3 : x0;
669  x1 = (x1 < 2) ? 2 : (x1 > ncol - 3) ? ncol - 3 : x1;
670 
671  checkSpanForCRs(&extra, crpixels, y - 1, x0, x1, mimage,
672  minSigma/2, thresH, thresV, thresD, bkgd, 0, keep);
673  checkSpanForCRs(&extra, crpixels, y, x0, x1, mimage,
674  minSigma/2, thresH, thresV, thresD, bkgd, 0, keep);
675  checkSpanForCRs(&extra, crpixels, y + 1, x0, x1, mimage,
676  minSigma/2, thresH, thresV, thresD, bkgd, 0, keep);
677  }
678 
679  if (extra.getSpans().size() > 0) { // we added some pixels
680  if (nextra + static_cast<int>(crpixels.size()) > nCrPixelMax) {
681  too_many_crs = true;
682  break;
683  }
684 
685  nextra += extra.getNpix();
686 
687  detection::Footprint::SpanList &espans = extra.getSpans();
688  for (detection::Footprint::SpanList::const_iterator siter = espans.begin();
689  siter != espans.end(); siter++) {
690  cr->addSpan(**siter);
691  }
692  cr->normalize();
693  }
694  }
695 
696  if (nextra == 0) {
697  break;
698  }
699  }
700 /*
701  * mark those pixels as CRs
702  */
703  if (!too_many_crs) {
704  (void)setMaskFromFootprintList(mimage.getMask().get(), CRs, crBit);
705  }
706 /*
707  * Maybe reinstate initial values; n.b. the same pixel may appear twice, so we want the
708  * first value stored (hence the uses of rbegin/rend)
709  *
710  * We have to do this if we decide _not_ to remove certain CRs,
711  * for example those which lie next to saturated pixels
712  */
713  if (keep || too_many_crs) {
714  if (crpixels.size() > 0) {
715  int const imageX0 = mimage.getX0();
716  int const imageY0 = mimage.getY0();
717 
718  std::sort(crpixels.begin(), crpixels.end()); // sort into birth order
719 
720  crpixel_riter rend = crpixels.rend();
721  for (crpixel_riter crp = crpixels.rbegin(); crp != rend; ++crp) {
722  if (crp->row == -1)
723  // dummy; skip it.
724  continue;
725  mimage.at(crp->col - imageX0, crp->row - imageY0).image() = crp->val;
726  }
727  }
728  } else {
729  if (true || nextra > 0) {
730  grow = true;
731  pexLogging::TTrace<2>("algorithms.CR", "Removing final list of CRs, grow = %d", grow);
732  removeCR(mimage, CRs, bkgd, crBit, saturBit, badMask, debias_values, grow);
733  }
734 /*
735  * we interpolated over all CR pixels, so set the interp bits too
736  */
737  (void)setMaskFromFootprintList(mimage.getMask().get(), CRs,
738  static_cast<MaskPixel>(crBit | interpBit));
739  }
740 
741  if (too_many_crs) { // we've cleaned up, so we can throw the exception
742  throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
743  (boost::format("Too many CR pixels (max %d)") % nCrPixelMax).str());
744  }
745 
746  return CRs;
747 }
748 
749 /*****************************************************************************/
750 namespace {
751 /*
752  * Is condition 3 true?
753  */
754 template<typename ImageT>
755 bool condition_3(ImageT *estimate, // estimate of true value of pixel
756  double const peak, // counts in central pixel (no sky)
757  double const mean_ns, // mean in NS direction (no sky)
758  double const mean_we, // " " WE " " " "
759  double const mean_swne, // " " SW-NE " " " "
760  double const mean_nwse, // " " NW-SE " " " "
761  double const dpeak, // standard deviation of peak value
762  double const dmean_ns, // s.d. of mean in NS direction
763  double const dmean_we, // " " " " WE " "
764  double const dmean_swne, // " " " " SW-NE " "
765  double const dmean_nwse, // " " " " NW-SE " "
766  double const thresH, // horizontal threshold
767  double const thresV, // vertical threshold
768  double const thresD, // diagonal threshold
769  double const cond3Fac // fiddle factor for noise
770  )
771 {
772  if (thresV*(peak - cond3Fac*dpeak) > mean_ns + cond3Fac*dmean_ns) {
773  *estimate = (ImageT)mean_ns;
774  return true;
775  }
776 
777  if (thresH*(peak - cond3Fac*dpeak) > mean_we + cond3Fac*dmean_we) {
778  *estimate = mean_we;
779  return true;
780  }
781 
782  if (thresD*(peak - cond3Fac*dpeak) > mean_swne + cond3Fac*dmean_swne) {
783  *estimate = mean_swne;
784  return true;
785  }
786 
787  if (thresD*(peak - cond3Fac*dpeak) > mean_nwse + cond3Fac*dmean_nwse) {
788  *estimate = mean_nwse;
789  return true;
790  }
791 
792  return false;
793 }
794 
795 /************************************************************************************************************/
796 /*
797  * Interpolate over a CR's pixels
798  */
799 template <typename MaskedImageT>
800 class RemoveCR : public detection::FootprintFunctor<MaskedImageT> {
801 public:
802  RemoveCR(MaskedImageT const& mimage,
803  double const bkgd,
804  typename MaskedImageT::Mask::Pixel badMask,
805  bool const debias,
807  ) : detection::FootprintFunctor<MaskedImageT>(mimage),
808  _bkgd(bkgd),
809  _ncol(mimage.getWidth()),
810  _nrow(mimage.getHeight()),
811  _badMask(badMask),
812  _debias(debias),
813  _rand(rand) {}
814 
815  // method called for each pixel by apply()
816  void operator()(typename MaskedImageT::xy_locator loc, // locator pointing at the pixel
817  int x, // column-position of pixel
818  int y // row-position of pixel
819  ) {
820  typedef typename MaskedImageT::Image::Pixel MImagePixel;
821  MImagePixel min = std::numeric_limits<MImagePixel>::max();
822  int ngood = 0; // number of good values on min
823 
824  MImagePixel const minval = _bkgd - 2*sqrt(loc.variance()); // min. acceptable pixel value after interp
825 /*
826  * W-E row
827  */
828  if (x - 2 >= 0 && x + 2 < _ncol) {
829  if ((loc.mask(-2, 0) | _badMask) || (loc.mask(-1, 0) | _badMask) ||
830  (loc.mask( 1, 0) | _badMask) || (loc.mask( 2, 0) | _badMask)) {
831  ; // estimate is contaminated
832  } else {
833  MImagePixel const v_m2 = loc.image(-2, 0);
834  MImagePixel const v_m1 = loc.image(-1, 0);
835  MImagePixel const v_p1 = loc.image( 1, 0);
836  MImagePixel const v_p2 = loc.image( 2, 0);
837 
838  MImagePixel const tmp =
839  interp::lpc_1_c1*(v_m1 + v_p1) + interp::lpc_1_c2*(v_m2 + v_p2);
840 
841  if (tmp > minval && tmp < min) {
842  min = tmp;
843  ngood++;
844  }
845  }
846  }
847 /*
848  * N-S column
849  */
850  if (y - 2 >= 0 && y + 2 < _nrow) {
851  if ((loc.mask(0, -2) | _badMask) || (loc.mask(0, -1) | _badMask) ||
852  (loc.mask(0, 1) | _badMask) || (loc.mask(0, 2) | _badMask)) {
853  ; /* estimate is contaminated */
854  } else {
855  MImagePixel const v_m2 = loc.image(0, -2);
856  MImagePixel const v_m1 = loc.image(0, -1);
857  MImagePixel const v_p1 = loc.image(0, 1);
858  MImagePixel const v_p2 = loc.image(0, 2);
859 
860  MImagePixel const tmp =
861  interp::lpc_1_c1*(v_m1 + v_p1) + interp::lpc_1_c2*(v_m2 + v_p2);
862 
863  if (tmp > minval && tmp < min) {
864  min = tmp;
865  ngood++;
866  }
867  }
868  }
869 /*
870  * SW--NE diagonal
871  */
872  if (x - 2 >= 0 && x + 2 < _ncol && y - 2 >= 0 && y + 2 < _nrow) {
873  if ((loc.mask(-2, -2) | _badMask) || (loc.mask(-1, -1) | _badMask) ||
874  (loc.mask( 1, 1) | _badMask) || (loc.mask( 2, 2) | _badMask)) {
875  ; /* estimate is contaminated */
876  } else {
877  MImagePixel const v_m2 = loc.image(-2, -2);
878  MImagePixel const v_m1 = loc.image(-1, -1);
879  MImagePixel const v_p1 = loc.image( 1, 1);
880  MImagePixel const v_p2 = loc.image( 2, 2);
881 
882  MImagePixel const tmp =
883  interp::lpc_1s2_c1*(v_m1 + v_p1) + interp::lpc_1s2_c2*(v_m2 + v_p2);
884 
885  if (tmp > minval && tmp < min) {
886  min = tmp;
887  ngood++;
888  }
889  }
890  }
891 /*
892  * SE--NW diagonal
893  */
894  if (x - 2 >= 0 && x + 2 < _ncol && y - 2 >= 0 && y + 2 < _nrow) {
895  if ((loc.mask( 2, -2) | _badMask) || (loc.mask( 1, -1) | _badMask) ||
896  (loc.mask(-1, 1) | _badMask) || (loc.mask(-2, 2) | _badMask)) {
897  ; /* estimate is contaminated */
898  } else {
899  MImagePixel const v_m2 = loc.image( 2, -2);
900  MImagePixel const v_m1 = loc.image( 1, -1);
901  MImagePixel const v_p1 = loc.image(-1, 1);
902  MImagePixel const v_p2 = loc.image(-2, 2);
903 
904  MImagePixel const tmp =
905  interp::lpc_1s2_c1*(v_m1 + v_p1) + interp::lpc_1s2_c2*(v_m2 + v_p2);
906 
907  if (tmp > minval && tmp < min) {
908  min = tmp;
909  ngood++;
910  }
911  }
912  }
913 /*
914  * Have we altogether failed to find an acceptable value? If so interpolate
915  * using the full-up interpolation code both vertically and horizontally
916  * and take the average. This can fail for large enough defects (e.g. CRs
917  * lying in bad columns), in which case the interpolator returns -1. If
918  * both directions fail, use the background value.
919  */
920  if (ngood == 0) {
921  std::pair<bool, MImagePixel const> val_h =
922  interp::singlePixel(x, y, this->getImage(), true, minval);
923  std::pair<bool, MImagePixel const> val_v =
924  interp::singlePixel(x, y, this->getImage(), false, minval);
925 
926  if (!val_h.first) {
927  if (!val_v.first) { // Still no good value. Guess wildly
928  min = _bkgd + sqrt(loc.variance())*_rand.gaussian();
929  } else {
930  min = val_v.second;
931  }
932  } else {
933  if (val_v.first) {
934  min = val_h.second;
935  } else {
936  min = (val_v.second + val_h.second)/2;
937  }
938  }
939  }
940 /*
941  * debias the minimum; If more than one uncontaminated estimate was
942  * available, estimate the bias to be simply that due to choosing the
943  * minimum of two Gaussians. In fact, even some of the "good" pixels
944  * may have some extra charge, so even if ngood > 2, still use this
945  * estimate
946  */
947  if (ngood > 0) {
948  pexLogging::TTrace<5>("algorithms.CR", "Adopted min==%g at (%d, %d) (ngood=%d)",
949  static_cast<double>(min), x, y, ngood);
950  }
951 
952  if (_debias && ngood > 1) {
953  min -= interp::min2GaussianBias*sqrt(loc.variance())*_rand.gaussian();
954  }
955 
956  loc.image() = min;
957  }
958 private:
959  double _bkgd;
960  int _ncol, _nrow;
961  typename MaskedImageT::Mask::Pixel _badMask;
962  bool _debias;
964 };
965 
966 /************************************************************************************************************/
967 /*
968  * actually remove CRs from the frame
969  */
970 template<typename ImageT, typename MaskT>
971 void removeCR(image::MaskedImage<ImageT, MaskT> & mi, // image to search
972  std::vector<detection::Footprint::Ptr> & CRs, // list of cosmic rays
973  double const bkgd, // non-subtracted background
974  MaskT const , // Bit value used to label CRs
975  MaskT const saturBit, // Bit value used to label saturated pixels
976  MaskT const badMask, // Bit mask for bad pixels
977  bool const debias, // statistically debias values?
978  bool const grow // Grow CRs?
979  )
980 {
981  lsst::afw::math::Random rand; // a random number generator
982  /*
983  * replace the values of cosmic-ray contaminated pixels with 1-dim 2nd-order weighted means Cosmic-ray
984  * contaminated pixels have already been given a mask value, crBit
985  *
986  * If there are no good options (i.e. all estimates are contaminated), try using just pixels that are not
987  * CRs; failing that, interpolate in the row- or column direction over as large a distance as is required
988  *
989  * XXX SDSS (and we) go through this list backwards; why?
990  */
991 
992  // a functor to remove a CR
993  RemoveCR<image::MaskedImage<ImageT, MaskT> > removeCR(mi, bkgd, badMask, debias, rand);
994 
995  for (std::vector<detection::Footprint::Ptr>::reverse_iterator fiter = CRs.rbegin();
996  fiter != CRs.rend(); ++fiter) {
997  detection::Footprint::Ptr cr = *fiter;
998 /*
999  * If I grow this CR does it touch saturated pixels? If so, don't
1000  * interpolate and add CR pixels to saturated mask
1001  */
1002  if (grow && cr->getNpix() < 100) {
1003  try {
1004  bool const isotropic = false; // use a slow isotropic grow?
1005  detection::Footprint::Ptr gcr = growFootprint(cr, 1, isotropic);
1006  detection::Footprint::Ptr const saturPixels = footprintAndMask(gcr, mi.getMask(), saturBit);
1007 
1008  if (saturPixels->getNpix() > 0) { // pixel is adjacent to a saturation trail
1009  setMaskFromFootprint(mi.getMask().get(), *saturPixels, saturBit);
1010 
1011  continue;
1012  }
1013  } catch(lsst::pex::exceptions::LengthError &) {
1014  continue;
1015  }
1016  }
1017 /*
1018  * OK, fix it
1019  */
1020  removeCR.apply(*cr);
1021  }
1022 }
1023 }
1024 
1025 /************************************************************************************************************/
1026 //
1027 // Explicit instantiations
1028 // \cond
1029 #define INSTANTIATE(TYPE) \
1030  template \
1031  std::vector<detection::Footprint::Ptr> \
1032  findCosmicRays(lsst::afw::image::MaskedImage<TYPE> &image, \
1033  detection::Psf const &psf, \
1034  double const bkgd, \
1035  lsst::pex::policy::Policy const& policy, \
1036  bool const keep \
1037  )
1038 
1039 INSTANTIATE(float);
1040 INSTANTIATE(double); // Why do we need double images?
1041 // \endcond
1042 }}} // namespace lsst::meas::algorithms
std::vector< Span::Ptr > SpanList
The Footprint&#39;s Span list.
Definition: Footprint.h:71
int y
std::uint16_t MaskPixel
const Span & addSpan(const int y, const int x0, const int x1)
An include file to include the header files for lsst::afw::geom.
double const min2GaussianBias
Mean value of the minimum of two N(0,1) variates.
Definition: Interp.h:60
Represent a set of pixels of an arbitrary shape and size.
Random & _rand
Definition: RandomImage.cc:45
int _i
Definition: CR.cc:163
a container for holding hierarchical configuration data in memory.
Definition: Policy.h:169
boost::shared_ptr< Footprint > footprintAndMask(boost::shared_ptr< Footprint > const &foot, typename image::Mask< MaskT >::Ptr const &mask, MaskT const bitmask)
Return a Footprint that&#39;s the intersection of a Footprint with a Mask.
definition of the Trace messaging facilities
std::shared_ptr< Footprint > Ptr
Definition: Footprint.h:67
IdSpan(int id, int y, int x0, int x1)
Definition: CR.cc:70
bool operator()(const IdSpan::ConstPtr a, const IdSpan::ConstPtr b)
Definition: CR.cc:79
#define INSTANTIATE(T)
int const x0
Definition: saturated.cc:45
bool val
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
boost::shared_ptr< Kernel const > ConstPtr
Definition: Kernel.h:139
boost::shared_ptr< Footprint > growFootprint(Footprint const &foot, int nGrow, bool isotropic=true)
std::shared_ptr< IdSpan > Ptr
Definition: CR.cc:67
ImageT::Pixel _sum
Definition: CR.cc:319
MaskPtr getMask(bool const noThrow=false) const
Return a (Ptr to) the MaskedImage&#39;s mask.
Definition: MaskedImage.h:885
int _id
Definition: Mask.cc:679
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:78
int _nrow
Definition: CR.cc:960
double x
A set of pixels in an Image.
Definition: Footprint.h:62
std::vector< std::shared_ptr< lsst::afw::detection::Footprint > > findCosmicRays(MaskedImageT &image, lsst::afw::detection::Psf const &psf, double const bkgd, lsst::pex::policy::Policy const &policy, bool const keep=false)
Find cosmic rays in an Image, and mask and remove them.
Definition: CR.cc:347
int row
Definition: CR.cc:159
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
std::shared_ptr< const IdSpan > ConstPtr
Definition: CR.cc:68
std::pair< bool, typename MaskedImageT::Image::Pixel > singlePixel(int x, int y, MaskedImageT const &image, bool horizontal, double minval)
Definition: Interp.cc:2135
Random number generator class.
MaskT setMaskFromFootprint(lsst::afw::image::Mask< MaskT > *mask, Footprint const &footprint, MaskT const bitmask)
OR bitmask into all the Mask&#39;s pixels that are in the Footprint.
int id
Definition: CR.cc:157
int resolve_alias(const std::vector< int > &aliases, int id)
Definition: CR.cc:98
boost::shared_ptr< math::Kernel const > getLocalKernel(geom::Point2D position=makeNullPoint(), image::Color color=image::Color()) const
Return a FixedKernel corresponding to the Psf image at the given point.
int getInt(const std::string &name) const
Definition: Policy.h:603
afw::table::Key< double > b
bool _debias
Definition: CR.cc:962
int _ncol
Definition: CR.cc:960
Implementation of the Class MaskedImage.
Include files required for standard LSST Exception handling.
A polymorphic base class for representing an image&#39;s Point Spread Function.
Definition: Psf.h:68
A functor class to allow users to process all the pixels in a Footprint.
image::Image< Pixel > Image
Image type returned by computeImage.
Definition: Psf.h:79
int col
Definition: CR.cc:158
MaskT setMaskFromFootprintList(lsst::afw::image::Mask< MaskT > *mask, std::vector< boost::shared_ptr< Footprint >> const &footprints, MaskT const bitmask)
OR bitmask into all the Mask&#39;s pixels which are in the set of Footprints.
double const _bkgd
Definition: CR.cc:318
double getDouble(const std::string &name) const
Definition: Policy.h:617
MaskedImageT::Mask::Pixel _badMask
Definition: CR.cc:961