40#include "boost/format.hpp"
80 }
else if (
a->id >
b->id) {
85 }
else if (
a->y >
b->y) {
88 return (
a->x0 <
b->x0) ? true :
false;
100 while (
id != aliases[
id]) {
101 resolved =
id = aliases[
id];
112namespace algorithms {
116template <
typename ImageT,
typename MaskT>
117void removeCR(afw::image::MaskedImage<ImageT, MaskT> &mi,
119 MaskT
const saturBit, MaskT
const badMask,
bool const debias,
bool const grow);
121template <
typename ImageT>
122bool condition_3(ImageT *estimate,
double const peak,
double const mean_ns,
double const mean_we,
123 double const mean_swne,
double const mean_nwse,
double const dpeak,
double const dmean_ns,
124 double const dmean_we,
double const dmean_swne,
double const dmean_nwse,
double const thresH,
125 double const thresV,
double const thresD,
double const cond3Fac);
130template <
typename ImageT>
134 CRPixel(
int _col,
int _row, ImageT _val,
int _id = -1) :
id(_id),
col(_col),
row(_row),
val(_val) {
139 bool operator<(
const CRPixel &
a)
const {
return _i <
a._i; }
141 int get_i()
const {
return _i; }
152template <
typename ImageT>
153int CRPixel<ImageT>::i = 0;
155template <
typename ImageT>
156struct Sort_CRPixel_by_id {
157 bool operator()(CRPixel<ImageT>
const &
a, CRPixel<ImageT>
const &
b)
const {
return a.id <
b.id; }
166template <
typename MaskedImageT>
167bool is_cr_pixel(
typename MaskedImageT::Image::Pixel *corr,
168 typename MaskedImageT::xy_locator loc,
169 double const minSigma,
170 double const thresH,
double const thresV,
double const thresD,
172 double const cond3Fac
174 typedef typename MaskedImageT::Image::Pixel ImagePixel;
178 ImagePixel
const v_00 = loc.image(0, 0);
190 ImagePixel
const mean_we = (loc.image(-1, 0) + loc.image(1, 0)) / 2;
191 ImagePixel
const mean_ns = (loc.image(0, 1) + loc.image(0, -1)) / 2;
192 ImagePixel
const mean_swne = (loc.image(-1, -1) + loc.image(1, 1)) / 2;
193 ImagePixel
const mean_nwse = (loc.image(-1, 1) + loc.image(1, -1)) / 2;
196 if (v_00 < -minSigma) {
200 double const thres_sky_sigma = minSigma *
sqrt(loc.variance(0, 0));
202 if (v_00 < mean_ns + thres_sky_sigma && v_00 < mean_we + thres_sky_sigma &&
203 v_00 < mean_swne + thres_sky_sigma && v_00 < mean_nwse + thres_sky_sigma) {
212 double const dv_00 =
sqrt(loc.variance(0, 0));
214 double const dmean_we =
sqrt(loc.variance(-1, 0) + loc.variance(1, 0)) / 2;
215 double const dmean_ns =
sqrt(loc.variance(0, 1) + loc.variance(0, -1)) / 2;
216 double const dmean_swne =
sqrt(loc.variance(-1, -1) + loc.variance(1, 1)) / 2;
217 double const dmean_nwse =
sqrt(loc.variance(-1, 1) + loc.variance(1, -1)) / 2;
219 if (!condition_3(corr, v_00 - bkgd, mean_ns - bkgd, mean_we - bkgd, mean_swne - bkgd, mean_nwse - bkgd,
220 dv_00, dmean_ns, dmean_we, dmean_swne, dmean_nwse, thresH, thresV, thresD, cond3Fac)) {
226 *corr +=
static_cast<ImagePixel
>(bkgd);
236template <
typename MaskedImageT>
237void checkSpanForCRs(afw::detection::Footprint *extras,
238 std::vector<CRPixel<typename MaskedImageT::Image::Pixel>> &crpixels,
241 int const x0,
int const x1,
243 double const minSigma,
244 double const thresH,
double const thresV,
double const thresD,
246 double const cond3Fac,
249 typedef typename MaskedImageT::Image::Pixel MImagePixel;
250 typename MaskedImageT::xy_locator loc =
image.xy_at(x0 - 1,
y);
252 int const imageX0 =
image.getX0();
253 int const imageY0 =
image.getY0();
255 for (
int x = x0 - 1;
x <= x1 + 1; ++
x) {
256 MImagePixel corr = 0;
257 if (is_cr_pixel<MaskedImageT>(&corr, loc, minSigma, thresH, thresV, thresD, bkgd, cond3Fac)) {
259 crpixels.push_back(CRPixel<MImagePixel>(
x + imageX0,
y + imageY0, loc.image()));
264 spanList.push_back(afw::geom::Span(
y + imageY0,
x + imageX0,
x + imageX0));
265 extras->setSpans(std::make_shared<afw::geom::SpanSet>(
std::move(spanList)));
275template <
typename ImageT>
278 CountsInCR(
double const bkgd) : _bkgd(bkgd), _sum(0.0) {}
282 virtual void reset(afw::detection::Footprint
const &) {}
283 virtual void reset() { _sum = 0.0; }
285 double getCounts()
const {
return _sum; }
296template <
typename ImageT>
297static void reinstateCrPixels(
299 std::vector<CRPixel<typename ImageT::Pixel>>
const &crpixels
301 if (crpixels.empty())
return;
304 for (crpixel_iter crp = crpixels.begin(),
end = crpixels.end(); crp <
end - 1; ++crp) {
305 *
image->at(crp->col -
image->getX0(), crp->row -
image->getY0()) = crp->val;
314template <
typename MaskedImageT>
316 MaskedImageT &mimage,
322 typedef typename MaskedImageT::Image ImageT;
323 typedef typename ImageT::Pixel ImagePixel;
324 typedef typename MaskedImageT::Mask::Pixel MaskPixel;
327 double const minSigma = ps.
getAsDouble(
"minSigma");
329 double const cond3Fac = ps.
getAsDouble(
"cond3_fac");
330 double const cond3Fac2 = ps.
getAsDouble(
"cond3_fac2");
331 int const niteration = ps.
getAsInt(
"niteration");
333 int const nCrPixelMax = ps.
getAsInt(
"nCrPixelMax");
345 kernel->computeImage(psfImage,
true);
347 int const xc = kernel->getCtr().getX();
348 int const yc = kernel->getCtr().getY();
350 double const I0 = psfImage(xc, yc);
351 double const thresH =
352 cond3Fac2 * (0.5 * (psfImage(xc - 1, yc) + psfImage(xc + 1, yc))) / I0;
353 double const thresV = cond3Fac2 * (0.5 * (psfImage(xc, yc - 1) + psfImage(xc, yc + 1))) / I0;
354 double const thresD = cond3Fac2 *
355 (0.25 * (psfImage(xc - 1, yc - 1) + psfImage(xc + 1, yc + 1) +
356 psfImage(xc - 1, yc + 1) + psfImage(xc + 1, yc - 1))) /
361 MaskPixel
const badBit = mimage.getMask()->getPlaneBitMask(
"BAD");
362 MaskPixel
const crBit = mimage.getMask()->getPlaneBitMask(
"CR");
363 MaskPixel
const interpBit = mimage.getMask()->getPlaneBitMask(
"INTRP");
364 MaskPixel
const saturBit = mimage.getMask()->getPlaneBitMask(
"SAT");
365 MaskPixel
const nodataBit = mimage.getMask()->getPlaneBitMask(
"NO_DATA");
367 MaskPixel
const badMask = (badBit | interpBit | saturBit | nodataBit);
371 int const ncol = mimage.getWidth();
372 int const nrow = mimage.getHeight();
378 for (
int j = 1; j < nrow - 1; ++j) {
379 typename MaskedImageT::xy_locator loc = mimage.xy_at(1, j);
381 for (
int i = 1; i < ncol - 1; ++i, ++loc.x()) {
383 if (!is_cr_pixel<MaskedImageT>(&corr, loc, minSigma, thresH, thresV, thresD, bkgd, cond3Fac)) {
389 if (loc.mask() & badMask) {
392 if ((loc.mask(-1, 1) | loc.mask(0, 1) | loc.mask(1, 1) | loc.mask(-1, 0) | loc.mask(1, 0) |
393 loc.mask(-1, -1) | loc.mask(0, -1) | loc.mask(1, -1)) &
403 crpixels.
push_back(CRPixel<ImagePixel>(i + mimage.getX0(), j + mimage.getY0(), loc.image()));
406 if (
static_cast<int>(crpixels.
size()) > nCrPixelMax) {
407 reinstateCrPixels(mimage.getImage().get(), crpixels);
410 (boost::format(
"Too many CR pixels (max %d)") % nCrPixelMax).str());
432 if (!crpixels.
empty()) {
434 int x0 = -1, x1 = -1,
y = -1;
437 CRPixel<ImagePixel> dummy(0, -1, 0, -1);
441 for (crpixel_iter crp = crpixels.
begin(); crp < crpixels.
end() - 1; ++crp) {
456 if (crp[1].
row == crp[0].
row && crp[1].
col == crp[0].
col + 1) {
461 assert(
y >= 0 && x0 >= 0 && x1 >= 0);
470 if (crpixels.
size() > 0) {
471 for (crpixel_iter cp = crpixels.
begin(); cp != crpixels.
end() - 1; cp++) {
473 assert(cp->col >= 0);
474 assert(cp->row >= 0);
477 assert(crpixels[crpixels.
size() - 1].id == -1);
478 assert(crpixels[crpixels.
size() - 1].col == 0);
479 assert(crpixels[crpixels.
size() - 1].row == -1);
484 assert((*sp)->id >= 0);
485 assert((*sp)->y >= 0);
486 assert((*sp)->x0 >= 0);
487 assert((*sp)->x1 >= (*sp)->x0);
489 assert((*sp2)->y >= (*sp)->y);
490 if ((*sp2)->y == (*sp)->y) {
491 assert((*sp2)->x0 > (*sp)->x1);
501 int const y = (*sp)->y;
502 int const x0 = (*sp)->x0;
503 int const x1 = (*sp)->x1;
507 if ((*sp2)->y ==
y) {
511 }
else if ((*sp2)->y != (
y + 1)) {
514 }
else if ((*sp2)->x0 > (x1 + 1)) {
517 }
else if ((*sp2)->x1 >= (x0 - 1)) {
529 for (
unsigned int i = 0; i != spans.
size(); ++i) {
536 if (spans.
size() > 0) {
545 if (spans.
size() > 0) {
546 int id = spans[0]->id;
548 for (
unsigned int i = i0; i <= spans.
size(); ++i) {
549 if (i == spans.
size() || spans[i]->id !=
id) {
554 for (; i0 < i; ++i0) {
557 cr->setSpans(std::make_shared<afw::geom::SpanSet>(
std::move(spanList)));
561 if (i < spans.
size()) {
567 reinstateCrPixels(mimage.getImage().get(), crpixels);
571 CountsInCR<typename ImageT::Pixel> CountDN(bkgd);
575 (*cr)->getSpans()->applyFunctor(CountDN, *mimage.getImage());
577 LOGL_DEBUG(
"TRACE4.lsst.algorithms.CR",
"CR at (%d, %d) has %g DN", (*cr)->getBBox().getMinX(),
578 (*cr)->getBBox().getMinY(), CountDN.getCounts());
579 if (CountDN.getCounts() < minDn) {
580 LOGL_DEBUG(
"TRACE5.lsst.algorithms.CR",
"Erasing CR");
592 bool const debias_values =
true;
594 LOGL_DEBUG(
"TRACE2.lsst.algorithms.CR",
"Removing initial list of CRs");
595 removeCR(mimage, CRs, bkgd, crBit, saturBit, badMask, debias_values, grow);
597 (void)setMaskFromFootprintList(mimage.getMask().get(), CRs,
598 mimage.getMask()->getPlaneBitMask(
"DETECTED"));
607 bool too_many_crs =
false;
609 for (
int i = 0; i != niteration && !too_many_crs; ++i) {
610 LOGL_DEBUG(
"TRACE1.lsst.algorithms.CR",
"Starting iteration %d", i);
612 fiter != CRs.
end(); fiter++) {
622 auto om = std::make_shared<afw::detection::Footprint>();
623 int const npix = (om) ? om->getArea() : 0;
625 if (
static_cast<std::size_t>(npix) == cr->getArea()) {
633 for (
auto siter = cr->getSpans()->begin(); siter != cr->getSpans()->end(); siter++) {
634 auto const span = siter;
642 int const y = span->getY() - mimage.getY0();
643 if (y < 2 || y >= nrow - 2) {
646 int x0 = span->getX0() - mimage.getX0();
647 int x1 = span->getX1() - mimage.getX0();
648 x0 = (x0 < 2) ? 2 : (x0 > ncol - 3) ? ncol - 3 : x0;
649 x1 = (x1 < 2) ? 2 : (x1 > ncol - 3) ? ncol - 3 : x1;
651 checkSpanForCRs(&extra, crpixels,
y - 1, x0, x1, mimage, minSigma / 2, thresH, thresV, thresD,
653 checkSpanForCRs(&extra, crpixels,
y, x0, x1, mimage, minSigma / 2, thresH, thresV, thresD,
655 checkSpanForCRs(&extra, crpixels,
y + 1, x0, x1, mimage, minSigma / 2, thresH, thresV, thresD,
660 if (nextra +
static_cast<int>(crpixels.
size()) > nCrPixelMax) {
668 for (
auto const &spn : (*extra.
getSpans())) {
671 cr->setSpans(std::make_shared<afw::geom::SpanSet>(
std::move(tmpSpanList)));
683 for (
auto const &foot : CRs) {
684 foot->getSpans()->setMask(*mimage.getMask().get(), crBit);
694 if (keep || too_many_crs) {
695 if (crpixels.
size() > 0) {
696 int const imageX0 = mimage.getX0();
697 int const imageY0 = mimage.getY0();
701 crpixel_riter rend = crpixels.
rend();
702 for (crpixel_riter crp = crpixels.
rbegin(); crp != rend; ++crp) {
706 mimage.at(crp->col - imageX0, crp->row - imageY0).image() = crp->val;
710 if (
true || nextra > 0) {
712 LOGL_DEBUG(
"TRACE2.lsst.algorithms.CR",
"Removing final list of CRs, grow = %d", grow);
713 removeCR(mimage, CRs, bkgd, crBit, saturBit, badMask, debias_values, grow);
718 for (
auto const &foot : CRs) {
719 foot->getSpans()->setMask(*mimage.getMask().get(),
static_cast<MaskPixel
>(crBit | interpBit));
725 (boost::format(
"Too many CR pixels (max %d)") % nCrPixelMax).str());
736template <
typename ImageT>
737bool condition_3(ImageT *estimate,
739 double const mean_ns,
740 double const mean_we,
741 double const mean_swne,
742 double const mean_nwse,
744 double const dmean_ns,
745 double const dmean_we,
746 double const dmean_swne,
747 double const dmean_nwse,
751 double const cond3Fac
753 if (thresV * (peak - cond3Fac * dpeak) > mean_ns + cond3Fac * dmean_ns) {
754 *estimate = (ImageT)mean_ns;
758 if (thresH * (peak - cond3Fac * dpeak) > mean_we + cond3Fac * dmean_we) {
763 if (thresD * (peak - cond3Fac * dpeak) > mean_swne + cond3Fac * dmean_swne) {
764 *estimate = mean_swne;
768 if (thresD * (peak - cond3Fac * dpeak) > mean_nwse + cond3Fac * dmean_nwse) {
769 *estimate = mean_nwse;
780template <
typename MaskedImageT>
783 RemoveCR(MaskedImageT
const &mimage,
double const bkgd,
typename MaskedImageT::Mask::Pixel badMask,
784 bool const debias, afw::math::Random &rand)
787 _ncol(mimage.getWidth()),
788 _nrow(mimage.getHeight()),
794 int const &
x = point.getX();
795 int const &
y = point.getY();
796 typename MaskedImageT::xy_locator loc = _image.xy_at(
x - _image.getX0(),
y - _image.getY0());
797 typedef typename MaskedImageT::Image::Pixel MImagePixel;
801 MImagePixel
const minval =
802 _bkgd - 2 *
sqrt(loc.variance());
806 if (
x - 2 >= 0 &&
x + 2 < _ncol) {
807 if ((loc.mask(-2, 0) | _badMask) || (loc.mask(-1, 0) | _badMask) || (loc.mask(1, 0) | _badMask) ||
808 (loc.mask(2, 0) | _badMask)) {
811 MImagePixel
const v_m2 = loc.image(-2, 0);
812 MImagePixel
const v_m1 = loc.image(-1, 0);
813 MImagePixel
const v_p1 = loc.image(1, 0);
814 MImagePixel
const v_p2 = loc.image(2, 0);
818 if (tmp > minval && tmp <
min) {
827 if (
y - 2 >= 0 &&
y + 2 < _nrow) {
828 if ((loc.mask(0, -2) | _badMask) || (loc.mask(0, -1) | _badMask) || (loc.mask(0, 1) | _badMask) ||
829 (loc.mask(0, 2) | _badMask)) {
832 MImagePixel
const v_m2 = loc.image(0, -2);
833 MImagePixel
const v_m1 = loc.image(0, -1);
834 MImagePixel
const v_p1 = loc.image(0, 1);
835 MImagePixel
const v_p2 = loc.image(0, 2);
839 if (tmp > minval && tmp <
min) {
848 if (
x - 2 >= 0 &&
x + 2 < _ncol &&
y - 2 >= 0 &&
y + 2 < _nrow) {
849 if ((loc.mask(-2, -2) | _badMask) || (loc.mask(-1, -1) | _badMask) ||
850 (loc.mask(1, 1) | _badMask) || (loc.mask(2, 2) | _badMask)) {
853 MImagePixel
const v_m2 = loc.image(-2, -2);
854 MImagePixel
const v_m1 = loc.image(-1, -1);
855 MImagePixel
const v_p1 = loc.image(1, 1);
856 MImagePixel
const v_p2 = loc.image(2, 2);
858 MImagePixel
const tmp =
861 if (tmp > minval && tmp <
min) {
870 if (
x - 2 >= 0 &&
x + 2 < _ncol &&
y - 2 >= 0 &&
y + 2 < _nrow) {
871 if ((loc.mask(2, -2) | _badMask) || (loc.mask(1, -1) | _badMask) ||
872 (loc.mask(-1, 1) | _badMask) || (loc.mask(-2, 2) | _badMask)) {
875 MImagePixel
const v_m2 = loc.image(2, -2);
876 MImagePixel
const v_m1 = loc.image(1, -1);
877 MImagePixel
const v_p1 = loc.image(-1, 1);
878 MImagePixel
const v_p2 = loc.image(-2, 2);
880 MImagePixel
const tmp =
883 if (tmp > minval && tmp <
min) {
910 min = (val_v.second + val_h.second) / 2;
922 LOGL_DEBUG(
"TRACE3.lsst.algorithms.CR",
"Adopted min==%g at (%d, %d) (ngood=%d)",
923 static_cast<double>(
min),
x,
y, ngood);
926 if (_debias && ngood > 1) {
934 MaskedImageT
const &_image;
937 typename MaskedImageT::Mask::Pixel _badMask;
939 afw::math::Random &
_rand;
946template <
typename ImageT,
typename MaskT>
947void removeCR(afw::image::MaskedImage<ImageT, MaskT> &mi,
951 MaskT
const saturBit,
956 afw::math::Random
rand;
968 RemoveCR<afw::image::MaskedImage<ImageT, MaskT>> removeCR(mi, bkgd, badMask, debias, rand);
971 fiter != CRs.rend(); ++fiter) {
1002 cr->getSpans()->applyFunctor(removeCR, 1);
1011#define INSTANTIATE(TYPE) \
1012 template std::vector<std::shared_ptr<afw::detection::Footprint>> findCosmicRays( \
1013 afw::image::MaskedImage<TYPE> &image, afw::detection::Psf const &psf, double const bkgd, \
1014 daf::base::PropertySet const &ps, bool const keep)
#define INSTANTIATE(FROMSYS, TOSYS)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
LSST DM logging module built on log4cxx.
#define LOGL_DEBUG(logger, message...)
Log a debug-level message using a varargs/printf style interface.
run-length code for part of object
std::shared_ptr< IdSpan > Ptr
std::shared_ptr< const IdSpan > ConstPtr
IdSpan(int id, int y, int x0, int x1)
A polymorphic base class for representing an image's Point Spread Function.
image::Image< Pixel > Image
Image type returned by computeImage.
A range of pixels within one row of an Image.
Class for storing generic metadata.
int getAsInt(std::string const &name) const
Get the last value for a bool/char/short/int property name (possibly hierarchical).
double getAsDouble(std::string const &name) const
Get the last value for any arithmetic property name (possibly hierarchical).
Reports attempts to exceed implementation-defined length limits for some classes.
Reports attempts to access elements using an invalid key.
int resolve_alias(const std::vector< int > &aliases, int id)
Follow a chain of aliases, returning the final resolved value.
double const min2GaussianBias
Mean value of the minimum of two N(0,1) variates.
double const lpc_1_c1
LPC coefficients for sigma = 1, S/N = infty.
double const lpc_1s2_c1
LPC coefficients for sigma = 1/sqrt(2), S/N = infty.
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,...
std::vector< std::shared_ptr< afw::detection::Footprint > > findCosmicRays(MaskedImageT &image, afw::detection::Psf const &psf, double const bkgd, daf::base::PropertySet const &ps, bool const keep=false)
Find cosmic rays in an Image, and mask and remove them.
comparison functor; sort by ID, then by row (y), then by column range start (x0)
bool operator()(const IdSpan::ConstPtr a, const IdSpan::ConstPtr b)