41 #include "boost/format.hpp"
43 #include "lsst/pex/exceptions.h"
44 #include "lsst/log/Log.h"
45 #include "lsst/pex/exceptions.h"
46 #include "lsst/afw/detection/Footprint.h"
47 #include "lsst/afw/geom.h"
48 #include "lsst/afw/detection/Psf.h"
49 #include "lsst/afw/image/MaskedImage.h"
50 #include "lsst/afw/math/Random.h"
66 typedef std::shared_ptr<IdSpan>
Ptr;
67 typedef std::shared_ptr<const IdSpan>
ConstPtr;
69 explicit IdSpan(
int id,
int y,
int x0,
int x1) : id(id), y(y), x0(x0), x1(x1) {}
77 struct IdSpanCompar :
public std::binary_function<const IdSpan::ConstPtr, const IdSpan::ConstPtr, bool> {
81 }
else if(a->id > b->id) {
86 }
else if (a->y > b->y) {
89 return (a->x0 < b->x0) ?
true :
false;
101 while (
id != aliases[
id]) {
102 resolved =
id = aliases[
id];
111 namespace algorithms {
113 namespace geom = lsst::afw::geom;
114 namespace math = lsst::afw::math;
115 namespace image = lsst::afw::image;
116 namespace detection = lsst::afw::detection;
120 template<
typename ImageT,
typename MaskT>
121 void removeCR(image::MaskedImage<ImageT, MaskT> & mi, std::vector<std::shared_ptr<detection::Footprint>> & CRs,
122 double const bkgd, MaskT
const , MaskT
const saturBit, MaskT
const badMask,
123 bool const debias,
bool const grow);
125 template<
typename ImageT>
126 bool condition_3(ImageT *estimate,
double const peak,
127 double const mean_ns,
double const mean_we,
double const mean_swne,
double const mean_nwse,
129 double const dmean_ns,
double const dmean_we,
double const dmean_swne,
double const dmean_nwse,
130 double const thresH,
double const thresV,
double const thresD,
131 double const cond3Fac
137 template<
typename ImageT>
139 typedef typename std::shared_ptr<CRPixel> Ptr;
141 CRPixel(
int _col,
int _row, ImageT _val,
int _id = -1) :
147 bool operator< (
const CRPixel& a)
const {
164 template<
typename ImageT>
165 int CRPixel<ImageT>::i = 0;
167 template<
typename ImageT>
168 struct Sort_CRPixel_by_id {
169 bool operator() (CRPixel<ImageT>
const & a, CRPixel<ImageT>
const &
b)
const {
180 template <
typename MaskedImageT>
181 bool is_cr_pixel(
typename MaskedImageT::Image::Pixel *corr,
182 typename MaskedImageT::xy_locator loc,
183 double const minSigma,
184 double const thresH,
double const thresV,
double const thresD,
186 double const cond3Fac
189 typedef typename MaskedImageT::Image::Pixel ImagePixel;
193 ImagePixel
const v_00 = loc.image(0, 0);
205 ImagePixel
const mean_we = (loc.image(-1, 0) + loc.image( 1, 0))/2;
206 ImagePixel
const mean_ns = (loc.image( 0, 1) + loc.image( 0, -1))/2;
207 ImagePixel
const mean_swne = (loc.image(-1, -1) + loc.image( 1, 1))/2;
208 ImagePixel
const mean_nwse = (loc.image(-1, 1) + loc.image( 1, -1))/2;
211 if (v_00 < -minSigma) {
215 double const thres_sky_sigma = minSigma*sqrt(loc.variance(0, 0));
217 if (v_00 < mean_ns + thres_sky_sigma &&
218 v_00 < mean_we + thres_sky_sigma &&
219 v_00 < mean_swne + thres_sky_sigma &&
220 v_00 < mean_nwse + thres_sky_sigma) {
229 double const dv_00 = sqrt(loc.variance( 0, 0));
231 double const dmean_we = sqrt(loc.variance(-1, 0) + loc.variance( 1, 0))/2;
232 double const dmean_ns = sqrt(loc.variance( 0, 1) + loc.variance( 0, -1))/2;
233 double const dmean_swne = sqrt(loc.variance(-1, -1) + loc.variance( 1, 1))/2;
234 double const dmean_nwse = sqrt(loc.variance(-1, 1) + loc.variance( 1, -1))/2;
236 if (!condition_3(corr,
237 v_00 - bkgd, mean_ns - bkgd, mean_we - bkgd, mean_swne - bkgd, mean_nwse - bkgd,
238 dv_00, dmean_ns, dmean_we, dmean_swne, dmean_nwse,
239 thresH, thresV, thresD, cond3Fac)){
245 *corr +=
static_cast<ImagePixel
>(bkgd);
255 template <
typename MaskedImageT>
256 void checkSpanForCRs(detection::Footprint *extras,
257 std::vector<CRPixel<typename MaskedImageT::Image::Pixel> >& crpixels,
260 int const x0,
int const x1,
262 double const minSigma,
263 double const thresH,
double const thresV,
double const thresD,
265 double const cond3Fac,
269 typedef typename MaskedImageT::Image::Pixel MImagePixel;
270 typename MaskedImageT::xy_locator loc = image.xy_at(x0 - 1, y);
272 int const imageX0 = image.getX0();
273 int const imageY0 = image.getY0();
275 for (
int x = x0 - 1; x <= x1 + 1; ++x) {
276 MImagePixel corr = 0;
277 if (is_cr_pixel<MaskedImageT>(&corr, loc, minSigma, thresH, thresV, thresD,
280 crpixels.push_back(CRPixel<MImagePixel>(x + imageX0, y + imageY0, loc.image()));
284 std::vector<geom::Span> spanList(extras->getSpans()->begin(),
285 extras->getSpans()->end());
286 spanList.push_back(geom::Span(y + imageY0, x + imageX0, x +imageX0));
287 extras->setSpans(std::make_shared<geom::SpanSet>(std::move(spanList)));
297 template <
typename ImageT>
300 CountsInCR(
double const bkgd) :
308 virtual void reset(detection::Footprint
const&) {}
309 virtual void reset() {
313 double getCounts()
const {
return _sum; }
323 template <
typename ImageT>
324 static void reinstateCrPixels(
326 std::vector<CRPixel<typename ImageT::Pixel> >
const& crpixels
329 if (crpixels.empty())
return;
331 typedef typename std::vector<CRPixel<typename ImageT::Pixel> >::const_iterator crpixel_iter;
332 for (crpixel_iter crp = crpixels.begin(), end = crpixels.end(); crp < end - 1 ; ++crp) {
333 *image->at(crp->col - image->getX0(), crp->row - image->getY0()) = crp->val;
342 template <
typename MaskedImageT>
343 std::vector<std::shared_ptr<detection::Footprint>>
345 detection::Psf
const &psf,
347 lsst::pex::policy::Policy
const &policy,
350 typedef typename MaskedImageT::Image ImageT;
351 typedef typename ImageT::Pixel ImagePixel;
352 typedef typename MaskedImageT::Mask::Pixel MaskPixel;
355 double const minSigma = policy.getDouble(
"minSigma");
356 double const minDn = policy.getDouble(
"min_DN");
357 double const cond3Fac = policy.getDouble(
"cond3_fac");
358 double const cond3Fac2 = policy.getDouble(
"cond3_fac2");
359 int const niteration = policy.getInt(
"niteration");
361 int const nCrPixelMax = policy.getInt(
"nCrPixelMax");
367 std::shared_ptr<lsst::afw::math::Kernel const> kernel = psf.getLocalKernel();
369 throw LSST_EXCEPT(pexExcept::NotFoundError,
"Psf is unable to return a kernel");
371 detection::Psf::Image psfImage = detection::Psf::Image(geom::ExtentI(kernel->getWidth(), kernel->getHeight()));
372 kernel->computeImage(psfImage,
true);
374 int const xc = kernel->getCtrX();
375 int const yc = kernel->getCtrY();
377 double const I0 = psfImage(xc, yc);
378 double const thresH = cond3Fac2*(0.5*(psfImage(xc - 1, yc) + psfImage(xc + 1, yc)))/I0;
379 double const thresV = cond3Fac2*(0.5*(psfImage(xc, yc - 1) + psfImage(xc, yc + 1)))/I0;
380 double const thresD = cond3Fac2*(0.25*(psfImage(xc - 1, yc - 1) + psfImage(xc + 1, yc + 1) +
381 psfImage(xc - 1, yc + 1) + psfImage(xc + 1, yc - 1)))/I0;
385 MaskPixel
const badBit = mimage.getMask()->getPlaneBitMask(
"BAD");
386 MaskPixel
const crBit = mimage.getMask()->getPlaneBitMask(
"CR");
387 MaskPixel
const interpBit = mimage.getMask()->getPlaneBitMask(
"INTRP");
388 MaskPixel
const saturBit = mimage.getMask()->getPlaneBitMask(
"SAT");
389 MaskPixel
const nodataBit = mimage.getMask()->getPlaneBitMask(
"NO_DATA");
391 MaskPixel
const badMask = (badBit | interpBit | saturBit | nodataBit);
395 int const ncol = mimage.getWidth();
396 int const nrow = mimage.getHeight();
398 std::vector<CRPixel<ImagePixel> > crpixels;
399 typedef typename std::vector<CRPixel<ImagePixel> >::iterator crpixel_iter;
400 typedef typename std::vector<CRPixel<ImagePixel> >::reverse_iterator crpixel_riter;
402 for (
int j = 1; j < nrow - 1; ++j) {
403 typename MaskedImageT::xy_locator loc = mimage.xy_at(1, j);
405 for (
int i = 1; i < ncol - 1; ++i, ++loc.x()) {
407 if (!is_cr_pixel<MaskedImageT>(&corr, loc, minSigma,
408 thresH, thresV, thresD, bkgd, cond3Fac)) {
414 if (loc.mask() & badMask) {
417 if ((loc.mask(-1, 1) | loc.mask(0, 1) | loc.mask(1, 1) |
418 loc.mask(-1, 0) | loc.mask(1, 0) |
419 loc.mask(-1, -1) | loc.mask(0, -1) | loc.mask(1, -1)) & interpBit) {
428 crpixels.push_back(CRPixel<ImagePixel>(i + mimage.getX0(), j + mimage.getY0(), loc.image()));
431 if (static_cast<int>(crpixels.size()) > nCrPixelMax) {
432 reinstateCrPixels(mimage.getImage().get(), crpixels);
434 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
435 (boost::format(
"Too many CR pixels (max %d)") % nCrPixelMax).str());
443 std::vector<int> aliases;
444 aliases.reserve(1 + crpixels.size()/2);
446 std::vector<detection::IdSpan::Ptr> spans;
447 spans.reserve(aliases.capacity());
449 aliases.push_back(0);
457 if (!crpixels.empty()) {
459 int x0 = -1, x1 = -1, y = -1;
462 CRPixel<ImagePixel> dummy(0, -1, 0, -1);
463 crpixels.push_back(dummy);
465 for (crpixel_iter crp = crpixels.begin(); crp < crpixels.end() - 1 ; ++crp) {
470 aliases.push_back(crp->id);
478 if (crp[1].
row == crp[0].
row && crp[1].
col == crp[0].
col + 1) {
483 assert (y >= 0 && x0 >= 0 && x1 >= 0);
492 if (crpixels.size() > 0) {
493 for (crpixel_iter cp = crpixels.begin(); cp != crpixels.end() - 1; cp++) {
495 assert(cp->col >= 0);
496 assert(cp->row >= 0);
499 assert(crpixels[crpixels.size()-1].id == -1);
500 assert(crpixels[crpixels.size()-1].col == 0);
501 assert(crpixels[crpixels.size()-1].row == -1);
504 for (std::vector<detection::IdSpan::Ptr>::iterator sp = spans.begin(), end = spans.end(); sp != end; sp++) {
505 assert((*sp)->id >= 0);
506 assert((*sp)->y >= 0);
507 assert((*sp)->x0 >= 0);
508 assert((*sp)->x1 >= (*sp)->x0);
509 for (std::vector<detection::IdSpan::Ptr>::iterator sp2 = sp + 1; sp2 != end; sp2++) {
510 assert((*sp2)->y >= (*sp)->y);
511 if ((*sp2)->y == (*sp)->y) {
512 assert((*sp2)->x0 > (*sp)->x1);
520 for (std::vector<detection::IdSpan::Ptr>::iterator sp = spans.begin(), end = spans.end();
522 int const y = (*sp)->y;
523 int const x0 = (*sp)->x0;
524 int const x1 = (*sp)->x1;
527 for (std::vector<detection::IdSpan::Ptr>::iterator sp2 = sp + 1; sp2 != end; ++sp2) {
528 if ((*sp2)->y == y) {
532 }
else if ((*sp2)->y != (y + 1)) {
535 }
else if ((*sp2)->x0 > (x1 + 1)) {
538 }
else if ((*sp2)->x1 >= (x0 - 1)) {
554 for (
unsigned int i = 0; i != spans.size(); ++i) {
561 if (spans.size() > 0) {
568 std::vector<std::shared_ptr<detection::Footprint>> CRs;
570 if (spans.size() > 0) {
571 int id = spans[0]->id;
573 for (
unsigned int i = i0; i <= spans.size(); ++i) {
574 if (i == spans.size() || spans[i]->id !=
id) {
575 std::shared_ptr<detection::Footprint> cr(
new detection::Footprint());
577 std::vector<geom::Span> spanList;
578 spanList.reserve(i - i0);
579 for (; i0 < i; ++i0) {
580 spanList.push_back(geom::Span(spans[i0]->y, spans[i0]->x0, spans[i0]->x1));
582 cr->setSpans(std::make_shared<geom::SpanSet>(std::move(spanList)));
586 if (i < spans.size()) {
592 reinstateCrPixels(mimage.getImage().get(), crpixels);
596 CountsInCR<typename ImageT::Pixel> CountDN(bkgd);
597 for (std::vector<std::shared_ptr<detection::Footprint>>::iterator cr = CRs.begin(), end = CRs.end();
600 (*cr)->getSpans()->applyFunctor(CountDN, *mimage.getImage());
602 LOGL_DEBUG(
"TRACE4.algorithms.CR",
"CR at (%d, %d) has %g DN",
603 (*cr)->getBBox().getMinX(), (*cr)->getBBox().getMinY(), CountDN.getCounts());
604 if (CountDN.getCounts() < minDn) {
605 LOGL_DEBUG(
"TRACE5.algorithms.CR",
"Erasing CR");
617 bool const debias_values =
true;
619 LOGL_DEBUG(
"TRACE2.algorithms.CR",
"Removing initial list of CRs");
620 removeCR(mimage, CRs, bkgd, crBit, saturBit, badMask, debias_values, grow);
621 #if 0 // Useful to see phase 2 in ds9; debugging only
622 (void)setMaskFromFootprintList(mimage.getMask().get(), CRs,
623 mimage.getMask()->getPlaneBitMask(
"DETECTED"));
632 bool too_many_crs =
false;
634 for (
int i = 0; i != niteration && !too_many_crs; ++i) {
635 LOGL_DEBUG(
"TRACE1.algorithms.CR",
"Starting iteration %d", i);
636 for (std::vector<std::shared_ptr<detection::Footprint>>::iterator fiter = CRs.begin();
637 fiter != CRs.end(); fiter++) {
638 std::shared_ptr<detection::Footprint> cr = *fiter;
646 auto om = std::make_shared<detection::Footprint>();
647 int const npix = (om) ? om->getArea() : 0;
649 if (static_cast<std::size_t>(npix) == cr->getArea()) {
656 detection::Footprint extra;
657 for (
auto siter = cr->getSpans()->begin();
658 siter != cr->getSpans()->end(); siter++) {
659 auto const span = siter;
667 int const y = span->getY() - mimage.getY0();
668 if (y < 2 || y >= nrow - 2) {
671 int x0 = span->getX0() - mimage.getX0();
672 int x1 = span->getX1() - mimage.getX0();
673 x0 = (x0 < 2) ? 2 : (x0 > ncol - 3) ? ncol - 3 : x0;
674 x1 = (x1 < 2) ? 2 : (x1 > ncol - 3) ? ncol - 3 : x1;
676 checkSpanForCRs(&extra, crpixels, y - 1, x0, x1, mimage,
677 minSigma/2, thresH, thresV, thresD, bkgd, 0, keep);
678 checkSpanForCRs(&extra, crpixels, y, x0, x1, mimage,
679 minSigma/2, thresH, thresV, thresD, bkgd, 0, keep);
680 checkSpanForCRs(&extra, crpixels, y + 1, x0, x1, mimage,
681 minSigma/2, thresH, thresV, thresD, bkgd, 0, keep);
684 if (extra.getSpans()->size() > 0) {
685 if (nextra + static_cast<int>(crpixels.size()) > nCrPixelMax) {
690 nextra += extra.getArea();
692 std::vector<geom::Span> tmpSpanList(cr->getSpans()->begin(),
693 cr->getSpans()->end());
694 for (
auto const & spn : (*extra.getSpans())) {
695 tmpSpanList.push_back(spn);
697 cr->setSpans(std::make_shared<geom::SpanSet>(std::move(tmpSpanList)));
709 for (
auto const & foot : CRs) {
710 foot->getSpans()->setMask(*mimage.getMask().get(), crBit);
720 if (keep || too_many_crs) {
721 if (crpixels.size() > 0) {
722 int const imageX0 = mimage.getX0();
723 int const imageY0 = mimage.getY0();
725 std::sort(crpixels.begin(), crpixels.end());
727 crpixel_riter rend = crpixels.rend();
728 for (crpixel_riter crp = crpixels.rbegin(); crp != rend; ++crp) {
732 mimage.at(crp->col - imageX0, crp->row - imageY0).image() = crp->val;
736 if (
true || nextra > 0) {
738 LOGL_DEBUG(
"TRACE2.algorithms.CR",
"Removing final list of CRs, grow = %d", grow);
739 removeCR(mimage, CRs, bkgd, crBit, saturBit, badMask, debias_values, grow);
744 for (
auto const & foot : CRs) {
745 foot->getSpans()->setMask(*mimage.getMask().get(),
static_cast<MaskPixel
>(crBit | interpBit));
750 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
751 (boost::format(
"Too many CR pixels (max %d)") % nCrPixelMax).str());
762 template<
typename ImageT>
763 bool condition_3(ImageT *estimate,
765 double const mean_ns,
766 double const mean_we,
767 double const mean_swne,
768 double const mean_nwse,
770 double const dmean_ns,
771 double const dmean_we,
772 double const dmean_swne,
773 double const dmean_nwse,
777 double const cond3Fac
780 if (thresV*(peak - cond3Fac*dpeak) > mean_ns + cond3Fac*dmean_ns) {
781 *estimate = (ImageT)mean_ns;
785 if (thresH*(peak - cond3Fac*dpeak) > mean_we + cond3Fac*dmean_we) {
790 if (thresD*(peak - cond3Fac*dpeak) > mean_swne + cond3Fac*dmean_swne) {
791 *estimate = mean_swne;
795 if (thresD*(peak - cond3Fac*dpeak) > mean_nwse + cond3Fac*dmean_nwse) {
796 *estimate = mean_nwse;
807 template <
typename MaskedImageT>
810 RemoveCR(MaskedImageT
const& mimage,
812 typename MaskedImageT::Mask::Pixel badMask,
814 lsst::afw::math::Random& rand
817 _ncol(mimage.getWidth()),
818 _nrow(mimage.getHeight()),
824 int const & x = point.getX();
825 int const & y = point.getY();
826 typename MaskedImageT::xy_locator loc =
_image.xy_at(x -
_image.getX0(),
828 typedef typename MaskedImageT::Image::Pixel MImagePixel;
829 MImagePixel min = std::numeric_limits<MImagePixel>::max();
832 MImagePixel
const minval =
_bkgd - 2*sqrt(loc.variance());
836 if (x - 2 >= 0 && x + 2 <
_ncol) {
841 MImagePixel
const v_m2 = loc.image(-2, 0);
842 MImagePixel
const v_m1 = loc.image(-1, 0);
843 MImagePixel
const v_p1 = loc.image( 1, 0);
844 MImagePixel
const v_p2 = loc.image( 2, 0);
846 MImagePixel
const tmp =
849 if (tmp > minval && tmp < min) {
858 if (y - 2 >= 0 && y + 2 <
_nrow) {
863 MImagePixel
const v_m2 = loc.image(0, -2);
864 MImagePixel
const v_m1 = loc.image(0, -1);
865 MImagePixel
const v_p1 = loc.image(0, 1);
866 MImagePixel
const v_p2 = loc.image(0, 2);
868 MImagePixel
const tmp =
871 if (tmp > minval && tmp < min) {
880 if (x - 2 >= 0 && x + 2 <
_ncol && y - 2 >= 0 && y + 2 <
_nrow) {
885 MImagePixel
const v_m2 = loc.image(-2, -2);
886 MImagePixel
const v_m1 = loc.image(-1, -1);
887 MImagePixel
const v_p1 = loc.image( 1, 1);
888 MImagePixel
const v_p2 = loc.image( 2, 2);
890 MImagePixel
const tmp =
893 if (tmp > minval && tmp < min) {
902 if (x - 2 >= 0 && x + 2 <
_ncol && y - 2 >= 0 && y + 2 <
_nrow) {
907 MImagePixel
const v_m2 = loc.image( 2, -2);
908 MImagePixel
const v_m1 = loc.image( 1, -1);
909 MImagePixel
const v_p1 = loc.image(-1, 1);
910 MImagePixel
const v_p2 = loc.image(-2, 2);
912 MImagePixel
const tmp =
915 if (tmp > minval && tmp < min) {
929 std::pair<bool, MImagePixel const> val_h =
931 std::pair<bool, MImagePixel const> val_v =
936 min =
_bkgd + sqrt(loc.variance())*
_rand.gaussian();
944 min = (val_v.second + val_h.second)/2;
956 LOGL_DEBUG(
"TRACE3.algorithms.CR",
"Adopted min==%g at (%d, %d) (ngood=%d)",
957 static_cast<double>(min), x, y, ngood);
979 template<
typename ImageT,
typename MaskT>
980 void removeCR(image::MaskedImage<ImageT, MaskT> & mi,
981 std::vector<std::shared_ptr<detection::Footprint>> & CRs,
984 MaskT
const saturBit,
990 lsst::afw::math::Random rand;
1002 RemoveCR<image::MaskedImage<ImageT, MaskT> > removeCR(mi, bkgd, badMask, debias, rand);
1004 for (std::vector<std::shared_ptr<detection::Footprint>>::reverse_iterator fiter = CRs.rbegin();
1005 fiter != CRs.rend(); ++fiter) {
1006 std::shared_ptr<detection::Footprint> cr = *fiter;
1035 cr->getSpans()->applyFunctor(removeCR, 1);
1044 #define INSTANTIATE(TYPE) \
1046 std::vector<std::shared_ptr<detection::Footprint>> \
1047 findCosmicRays(lsst::afw::image::MaskedImage<TYPE> &image, \
1048 detection::Psf const &psf, \
1049 double const bkgd, \
1050 lsst::pex::policy::Policy const& policy, \
#define INSTANTIATE(TYPE)
double const min2GaussianBias
Mean value of the minimum of two N(0,1) variates.
MaskedImageT const & _image
IdSpan(int id, int y, int x0, int x1)
bool operator()(const IdSpan::ConstPtr a, const IdSpan::ConstPtr b)
std::shared_ptr< IdSpan > Ptr
comparison functor; sort by ID, then by row (y), then by column range start (x0)
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)
std::shared_ptr< const IdSpan > ConstPtr
lsst::afw::math::Random & _rand
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...
int resolve_alias(const std::vector< int > &aliases, int id)
Follow a chain of aliases, returning the final resolved value.
afw::table::Key< double > b
double const lpc_1s2_c1
LPC coefficients for sigma = 1/sqrt(2), S/N = infty.
double const lpc_1_c1
LPC coefficients for sigma = 1, S/N = infty.
run-length code for part of object
MaskedImageT::Mask::Pixel _badMask