49 #include "boost/format.hpp"
67 struct Threshold_traits {};
68 struct ThresholdLevel_traits :
public Threshold_traits {
70 struct ThresholdPixelLevel_traits :
public Threshold_traits {
72 struct ThresholdBitmask_traits :
public Threshold_traits {
75 template <
typename PixelT>
78 explicit setIdImage(
std::uint64_t const id,
bool overwriteId =
false,
long const idMask = 0x0)
81 _withSetReplace(false),
82 _overwriteId(overwriteId),
87 pex::exceptions::InvalidParameterError,
88 str(boost::format(
"Id 0x%x sets bits in the protected mask 0x%x") % _id % _idMask));
93 long const idMask = 0x0)
96 _withSetReplace(true),
97 _overwriteId(overwriteId),
99 _pos(oldIds->
begin()) {
102 pex::exceptions::InvalidParameterError,
103 str(boost::format(
"Id 0x%x sets bits in the protected mask 0x%x") % _id % _idMask));
109 auto val = input & ~_idMask;
111 if (val != 0 && _withSetReplace) {
112 _pos = _oldIds->insert(_pos, val);
115 input = (input & _idMask) + _id;
124 bool _withSetReplace;
134 template <
typename T>
135 inline bool isBadPixel(T) {
140 inline bool isBadPixel(
float val) {
145 inline bool isBadPixel(
double val) {
152 int nbit(
unsigned long i) {
166 template <
typename T>
167 class FindIdsInFootprint {
169 explicit FindIdsInFootprint() : _ids(), _old(0) {}
197 if (
a->getPeakValue() !=
b->getPeakValue()) {
198 return (
a->getPeakValue() >
b->getPeakValue());
201 if (
a->getIx() !=
b->getIx()) {
202 return (
a->getIx() <
b->getIx());
205 return (
a->getIy() <
b->getIy());
211 FootprintSet mergeFootprintSets(FootprintSet
const &lhs,
213 FootprintSet
const &rhs,
215 FootprintControl
const &ctrl
219 bool const circular = ctrl.isCircular().first && ctrl.isCircular().second;
220 bool const isotropic = ctrl.isIsotropic().second;
222 bool const left = ctrl.isLeft().first && ctrl.isLeft().second;
223 bool const right = ctrl.isRight().first && ctrl.isRight().second;
224 bool const up = ctrl.isUp().first && ctrl.isUp().second;
225 bool const down = ctrl.isDown().first && ctrl.isDown().second;
228 if (region != rhs.getRegion()) {
229 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
230 boost::format(
"The two FootprintSets must have the same region").str());
233 auto idImage = std::make_shared<image::Image<IdPixelT>>(region);
237 FootprintList
const &lhsFootprints = *lhs.getFootprints();
238 FootprintList
const &rhsFootprints = *rhs.getFootprints();
239 int const nLhs = lhsFootprints.size();
240 int const nRhs = rhsFootprints.size();
245 int const lhsIdNbit = nbit(nLhs);
246 int const lhsIdMask = (lhsIdNbit == 0) ? 0x0 : (1 << lhsIdNbit) - 1;
250 (boost::format(
"%d + %d footprints need too many bits; change IdPixelT typedef") %
260 OldIdMap overwrittenIds;
262 auto grower = [&circular, &up, &down, &
left, &
right, &isotropic](
266 auto tmpFoot = std::make_shared<Footprint>(foot->getSpans()->dilated(amount,
element),
270 int top = up ? amount : 0;
271 int bottom = down ? amount : 0;
272 int lLimit =
left ? amount : 0;
273 int rLimit =
right ? amount : 0;
275 auto yRange = top + bottom + 1;
279 for (
auto dy = 1; dy <= top; ++dy) {
282 for (
auto dy = -1; dy >= -bottom; --dy) {
286 geom::SpanSet structure(
std::move(spanList));
288 std::make_shared<Footprint>(foot->getSpans()->dilated(structure), foot->getRegion());
294 for (FootprintList::const_iterator ptr = lhsFootprints.begin(),
end = lhsFootprints.end(); ptr !=
end;
298 if (rLhs > 0 && foot->getArea() > 0) {
299 foot = grower(foot, rLhs);
304 ->clippedTo(idImage->getBBox())
305 ->applyFunctor(setIdImage<IdPixelT>(
id, &overwritten,
true), *idImage);
307 if (!overwritten.
empty()) {
308 overwrittenIds.insert(overwrittenIds.end(),
std::make_pair(
id, overwritten));
313 id = (1 << lhsIdNbit);
314 for (FootprintList::const_iterator ptr = rhsFootprints.begin(),
end = rhsFootprints.end(); ptr !=
end;
315 ++ptr,
id += (1 << lhsIdNbit)) {
318 if (rRhs > 0 && foot->getArea() > 0) {
319 foot = grower(foot, rRhs);
324 ->clippedTo(idImage->getBBox())
325 ->applyFunctor(setIdImage<IdPixelT>(
id, &overwritten,
true, lhsIdMask), *idImage);
327 if (!overwritten.
empty()) {
328 overwrittenIds.insert(overwrittenIds.end(),
std::make_pair(
id, overwritten));
332 FootprintSet fs(*idImage, Threshold(1), 1,
false);
344 FindIdsInFootprint<IdPixelT> idFinder;
345 for (
const auto& foot : *fs.getFootprints()) {
347 foot->getSpans()->applyFunctor(idFinder, *idImage);
351 for (
unsigned int indx : idFinder.getIds()) {
352 if ((indx & lhsIdMask) > 0) {
354 lhsFootprintIndxs.
insert(i);
358 OldIdMap::iterator mapPtr = overwrittenIds.find(indx);
359 if (mapPtr != overwrittenIds.end()) {
362 for (
unsigned long ptr : overwritten) {
363 lhsFootprintIndxs.
insert((ptr & lhsIdMask) - 1);
371 rhsFootprintIndxs.
insert(i);
375 OldIdMap::iterator mapPtr = overwrittenIds.find(indx);
376 if (mapPtr != overwrittenIds.end()) {
379 for (
unsigned long ptr : overwritten) {
380 rhsFootprintIndxs.
insert(ptr - 1);
391 for (
unsigned long i : lhsFootprintIndxs) {
392 assert(i < lhsFootprints.size());
393 PeakCatalog const &oldPeaks = lhsFootprints[i]->getPeaks();
395 int const nold = peaks.
size();
396 peaks.insert(peaks.end(), oldPeaks.begin(), oldPeaks.end());
401 peaks.getInternal().end(), SortPeaks());
404 for (
unsigned long i : rhsFootprintIndxs) {
405 assert(i < rhsFootprints.size());
406 PeakCatalog const &oldPeaks = rhsFootprints[i]->getPeaks();
408 int const nold = peaks.
size();
409 peaks.insert(peaks.end(), oldPeaks.begin(), oldPeaks.end());
412 peaks.getInternal().end(), SortPeaks());
424 explicit IdSpan(
int id,
int y,
int x0,
int x1,
double good) :
id(
id),
y(
y), x0(x0), x1(x1), good(good) {}
433 struct IdSpanCompare {
434 bool operator()(IdSpan
const &
a, IdSpan
const &
b)
const {
437 }
else if (
a.id >
b.id) {
440 return (
a.y <
b.y) ? true :
false;
451 while (
id != aliases[
id]) {
452 resolved =
id = aliases[
id];
461 template <
typename ImageT>
464 auto spanSet = foot.getSpans();
465 if (spanSet->size() == 0) {
469 for (
auto const &spanIter : *spanSet) {
470 auto y = spanIter.getY() -
image.getY0();
475 for (
auto x = spanIter.getMinX() -
image.getX0();
x <= spanIter.getMaxX() -
image.getX0(); ++
x) {
495 foot.addPeak(
x +
image.getX0(),
y +
image.getY0(), val);
500 template <
typename ImageT>
501 class FindMaxInFootprint {
503 explicit FindMaxInFootprint(
bool polarity)
504 : _polarity(polarity),
507 _min(
std::numeric_limits<double>::
max()),
508 _max(-
std::numeric_limits<double>::
max()) {}
526 void addRecord(
Footprint &foot)
const { foot.addPeak(_x, _y, _polarity ? _max : _min); }
534 template <
typename ImageT,
typename ThresholdT>
536 findPeaksInFootprint(img, polarity, foot->getPeaks(), *foot, 1);
541 std::stable_sort(foot->getPeaks().getInternal().begin(), foot->getPeaks().getInternal().end(),
544 if (foot->getPeaks().empty()) {
545 FindMaxInFootprint<typename ImageT::Pixel> maxFinder(polarity);
546 foot->getSpans()->applyFunctor(maxFinder,
ndarray::ndImage(img.getArray(), img.getXY0()));
547 maxFinder.addRecord(*foot);
552 template <
typename ImageT>
561 template <
typename ImagePixelT,
typename IterT>
562 static inline bool inFootprint(ImagePixelT pixVal, IterT,
bool polarity,
double thresholdVal,
563 ThresholdLevel_traits) {
564 return (polarity ? pixVal : -pixVal) >= thresholdVal;
567 template <
typename ImagePixelT,
typename IterT>
568 static inline bool inFootprint(ImagePixelT pixVal, IterT var,
bool polarity,
double thresholdVal,
569 ThresholdPixelLevel_traits) {
570 return (polarity ? pixVal : -pixVal) >= thresholdVal * ::sqrt(*var);
573 template <
typename ImagePixelT,
typename IterT>
574 static inline bool inFootprint(ImagePixelT pixVal, IterT,
bool,
double thresholdVal,
575 ThresholdBitmask_traits) {
576 return (pixVal &
static_cast<long>(thresholdVal));
582 template <
typename IterT>
583 static inline IterT advancePtr(IterT varPtr, Threshold_traits) {
587 template <
typename IterT>
588 static inline IterT advancePtr(IterT varPtr, ThresholdPixelLevel_traits) {
596 template <
typename ImagePixelT,
typename MaskPixelT,
typename VariancePixelT,
typename ThresholdTraitT>
597 static void findFootprints(
602 double const footprintThreshold,
603 double const includeThresholdMultiplier,
613 double includeThreshold = footprintThreshold * includeThresholdMultiplier;
615 int const row0 = img.
getY0();
616 int const col0 = img.
getX0();
631 aliases.
reserve(1 + height / 20);
644 for (
int y = 0;
y != height; ++
y) {
645 if (idc == id1.begin() + 1) {
646 idc = id2.
begin() + 1;
647 idp = id1.
begin() + 1;
649 idc = id1.
begin() + 1;
650 idp = id2.
begin() + 1;
655 bool good = (includeThresholdMultiplier == 1.0);
658 x_var_iterator varPtr = (var ==
nullptr) ?
nullptr : var->
row_begin(
y);
659 for (
int x = 0;
x < width; ++
x, ++pixPtr, varPtr = advancePtr(varPtr, ThresholdTraitT())) {
660 ImagePixelT
const pixVal = *pixPtr;
662 if (isBadPixel(pixVal) ||
663 !inFootprint(pixVal, varPtr, polarity, footprintThreshold, ThresholdTraitT())) {
671 if (idc[
x - 1] != 0) {
673 }
else if (idp[
x - 1] != 0) {
675 }
else if (idp[
x] != 0) {
677 }
else if (idp[
x + 1] != 0) {
692 if (idp[
x + 1] != 0 && idp[
x + 1] !=
id) {
693 aliases[resolve_alias(aliases, idp[
x + 1])] = resolve_alias(aliases,
id);
695 idc[
x] =
id = idp[
x + 1];
698 if (!good && inFootprint(pixVal, varPtr, polarity, includeThreshold, ThresholdTraitT())) {
711 for (
auto & span : spans) {
712 span.id = resolve_alias(aliases, span.id);
717 if (spans.size() > 0) {
718 std::sort(spans.begin(), spans.end(), IdSpanCompare());
724 if (spans.size() > 0) {
727 for (
unsigned int i = 0; i <= spans.size(); i++) {
728 if (i == spans.size() || spans[i].id !=
id) {
731 for (; i0 < i; i0++) {
732 good |= spans[i0].good;
733 tempSpanList.
emplace_back(spans[i0].
y + row0, spans[i0].x0 + col0, spans[i0].x1 + col0);
735 auto tempSpanSet = std::make_shared<geom::SpanSet>(
std::move(tempSpanList));
736 auto fp = std::make_shared<Footprint>(tempSpanSet, _region);
738 if (good && fp->getArea() >=
static_cast<std::size_t>(npixMin)) {
739 _footprints->push_back(fp);
743 if (i < spans.size()) {
752 using fiterator = FootprintSet::FootprintList::iterator;
753 for (
auto const &_footprint : *_footprints) {
754 findPeaks(_footprint, img, polarity, ThresholdTraitT());
759 template <
typename ImagePixelT>
761 int const npixMin,
bool const setPeaks)
762 : _footprints(new FootprintList()), _region(img.getBBox()) {
763 using VariancePixelT = float;
765 findFootprints<ImagePixelT, image::MaskPixel, VariancePixelT, ThresholdLevel_traits>(
766 _footprints.get(), _region, img,
nullptr, threshold.getValue(img), threshold.getIncludeMultiplier(),
767 threshold.getPolarity(), npixMin, setPeaks);
772 template <
typename MaskPixelT>
774 : _footprints(new FootprintList()), _region(msk.getBBox()) {
775 switch (threshold.getType()) {
776 case Threshold::BITMASK:
777 findFootprints<MaskPixelT, MaskPixelT, float, ThresholdBitmask_traits>(
778 _footprints.get(), _region, msk,
nullptr, threshold.getValue(),
779 threshold.getIncludeMultiplier(), threshold.getPolarity(), npixMin,
false);
782 case Threshold::VALUE:
783 findFootprints<MaskPixelT, MaskPixelT, float, ThresholdLevel_traits>(
784 _footprints.get(), _region, msk,
nullptr, threshold.getValue(),
785 threshold.getIncludeMultiplier(), threshold.getPolarity(), npixMin,
false);
789 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
790 "You must specify a numerical threshold value with a Mask");
794 template <
typename ImagePixelT,
typename MaskPixelT>
796 Threshold
const &threshold,
std::string const &planeName,
int const npixMin,
798 : _footprints(new FootprintList()),
799 _region(
lsst::geom::
Point2I(maskedImg.getX0(), maskedImg.getY0()),
800 lsst::geom::
Extent2I(maskedImg.getWidth(), maskedImg.getHeight())) {
803 switch (threshold.getType()) {
804 case Threshold::PIXEL_STDEV:
805 findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdPixelLevel_traits>(
807 threshold.getValue(maskedImg), threshold.getIncludeMultiplier(), threshold.getPolarity(),
811 findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdLevel_traits>(
813 threshold.getValue(maskedImg), threshold.getIncludeMultiplier(), threshold.getPolarity(),
818 if (planeName ==
"") {
825 mask->addMaskPlane(planeName);
827 MaskPixelT
const bitPlane =
mask->getPlaneBitMask(planeName);
831 for (
auto const &fIter : *_footprints) {
832 fIter->getSpans()->setMask(*(maskedImg.
getMask()), bitPlane);
837 : _footprints(
std::
make_shared<FootprintList>()), _region(region) {}
839 FootprintSet::FootprintSet(FootprintSet
const &rhs) : _footprints(new FootprintList), _region(rhs._region) {
840 _footprints->reserve(rhs._footprints->size());
841 for (FootprintSet::FootprintList::const_iterator ptr = rhs._footprints->begin(),
842 end = rhs._footprints->end();
844 _footprints->push_back(std::make_shared<Footprint>(**ptr));
849 FootprintSet::FootprintSet(FootprintSet &&rhs) : FootprintSet(rhs) {}
852 FootprintSet tmp(rhs);
860 FootprintSet::~FootprintSet() =
default;
862 void FootprintSet::merge(FootprintSet
const &rhs,
int tGrow,
int rGrow,
bool isotropic) {
863 FootprintControl
const ctrl(
true, isotropic);
864 FootprintSet fs = mergeFootprintSets(*
this, tGrow, rhs, rGrow, ctrl);
871 for (
auto const &ptr : *_footprints) {
872 ptr->setRegion(region);
876 FootprintSet::FootprintSet(FootprintSet
const &rhs,
int r,
bool isotropic)
877 : _footprints(new FootprintList), _region(rhs._region) {
879 FootprintSet fs = rhs;
883 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
884 (boost::format(
"I cannot grow by negative numbers: %d") % r).str());
887 FootprintControl
const ctrl(
true, isotropic);
888 FootprintSet fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, r, ctrl);
892 FootprintSet::FootprintSet(FootprintSet
const &rhs,
int ngrow, FootprintControl
const &ctrl)
893 : _footprints(new FootprintList), _region(rhs._region) {
895 FootprintSet fs = rhs;
898 }
else if (ngrow < 0) {
899 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
900 str(boost::format(
"I cannot grow by negative numbers: %d") % ngrow));
903 FootprintSet fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, ngrow, ctrl);
907 FootprintSet::FootprintSet(FootprintSet
const &fs1, FootprintSet
const &fs2,
bool const)
908 : _footprints(new FootprintList()), _region(fs1._region) {
910 throw LSST_EXCEPT(pex::exceptions::LogicError,
"NOT IMPLEMENTED");
914 auto im = std::make_shared<image::Image<FootprintIdPixel>>(_region);
918 for (
auto const &fIter : *_footprints) {
920 fIter->getSpans()->applyFunctor(setIdImage<FootprintIdPixel>(
id), *im);
926 template <
typename ImagePixelT,
typename MaskPixelT>
928 HeavyFootprintCtrl
const *ctrl) {
929 HeavyFootprintCtrl ctrl_s = HeavyFootprintCtrl();
935 for (FootprintList::iterator ptr = _footprints->begin(),
end = _footprints->end(); ptr !=
end; ++ptr) {
936 ptr->reset(
new HeavyFootprint<ImagePixelT, MaskPixelT>(**ptr, mimg, ctrl));
941 for (
auto const &i : *_footprints) {
953 #define INSTANTIATE(PIXEL) \
954 template FootprintSet::FootprintSet(image::Image<PIXEL> const &, Threshold const &, int const, \
956 template FootprintSet::FootprintSet(image::MaskedImage<PIXEL, image::MaskPixel> const &, \
957 Threshold const &, std::string const &, int const, bool const); \
958 template void FootprintSet::makeHeavy(image::MaskedImage<PIXEL, image::MaskPixel> const &, \
959 HeavyFootprintCtrl const *)
#define INSTANTIATE(FROMSYS, TOSYS)
#define LSST_EXCEPT(type,...)
The base class for all image classed (Image, Mask, MaskedImage, ...)
int getX0() const
Return the image's column-origin.
int getWidth() const
Return the number of columns in the image.
int getY0() const
Return the image's row-origin.
int getHeight() const
Return the number of rows in the image.
x_iterator row_begin(int y) const
Return an x_iterator to the start of the y'th row.
A class to represent a 2-dimensional array of pixels.
Represent a 2-dimensional array of bitmask pixels.
A class to manipulate images, masks, and variance as a single object.
VariancePtr getVariance() const
Return a (shared_ptr to) the MaskedImage's variance.
MaskPtr getMask() const
Return a (shared_ptr to) the MaskedImage's mask.
ImagePtr getImage() const
Return a (shared_ptr to) the MaskedImage's image.
size_type size() const
Return the number of elements in the catalog.
void swap(PolymorphicValue &lhs, PolymorphicValue &rhs) noexcept
Swap specialization for PolymorphicValue.
int getMinY() const noexcept
void include(Point2I const &point)
int getMinX() const noexcept
T emplace_back(T... args)
T inplace_merge(T... args)
afw::table::CatalogT< PeakRecord > PeakCatalog
std::uint64_t FootprintIdPixel
Pixel type for FootprintSet::insertIntoImage()
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
FilterProperty & operator=(FilterProperty const &)=default
lsst::afw::detection::Footprint Footprint
SortedCatalogT< SourceRecord > SourceCatalog
A base class for image defects.
details::ImageNdGetter< T, inA, inB > ndImage(ndarray::Array< T, inA, inB > const &array, lsst::geom::Point2I xy0=lsst::geom::Point2I())
Marks a ndarray to be interpreted as an image when applying a functor from a SpanSet.