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