51#include "boost/format.hpp"
69struct Threshold_traits {};
70struct ThresholdLevel_traits :
public Threshold_traits {
72struct ThresholdPixelLevel_traits :
public Threshold_traits {
74struct ThresholdBitmask_traits :
public Threshold_traits {
77template <
typename PixelT>
80 explicit setIdImage(
std::uint64_t const id,
bool overwriteId =
false,
long const idMask = 0x0)
83 _withSetReplace(false),
84 _overwriteId(overwriteId),
89 pex::exceptions::InvalidParameterError,
90 str(boost::format(
"Id 0x%x sets bits in the protected mask 0x%x") % _id % _idMask));
95 long const idMask = 0x0)
98 _withSetReplace(true),
99 _overwriteId(overwriteId),
101 _pos(oldIds->
begin()) {
104 pex::exceptions::InvalidParameterError,
105 str(boost::format(
"Id 0x%x sets bits in the protected mask 0x%x") % _id % _idMask));
111 auto val = input & ~_idMask;
113 if (val != 0 && _withSetReplace) {
114 _pos = _oldIds->insert(_pos, val);
117 input = (input & _idMask) + _id;
126 bool _withSetReplace;
137inline bool isBadPixel(T) {
142inline bool isBadPixel(
float val) {
147inline bool isBadPixel(
double val) {
154int nbit(
unsigned long i) {
169class FindIdsInFootprint {
171 explicit FindIdsInFootprint() : _ids(), _old(0) {}
199 if (
a->getPeakValue() !=
b->getPeakValue()) {
200 return (
a->getPeakValue() >
b->getPeakValue());
203 if (
a->getIx() !=
b->getIx()) {
204 return (
a->getIx() <
b->getIx());
207 return (
a->getIy() <
b->getIy());
213FootprintSet mergeFootprintSets(FootprintSet
const &lhs,
215 FootprintSet
const &rhs,
217 FootprintControl
const &ctrl
221 bool const circular = ctrl.isCircular().first && ctrl.isCircular().second;
222 bool const isotropic = ctrl.isIsotropic().second;
224 bool const left = ctrl.isLeft().first && ctrl.isLeft().second;
225 bool const right = ctrl.isRight().first && ctrl.isRight().second;
226 bool const up = ctrl.isUp().first && ctrl.isUp().second;
227 bool const down = ctrl.isDown().first && ctrl.isDown().second;
230 if (region != rhs.getRegion()) {
231 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
232 boost::format(
"The two FootprintSets must have the same region").
str());
235 auto idImage = std::make_shared<image::Image<IdPixelT>>(region);
239 FootprintList
const &lhsFootprints = *lhs.getFootprints();
240 FootprintList
const &rhsFootprints = *rhs.getFootprints();
241 int const nLhs = lhsFootprints.size();
242 int const nRhs = rhsFootprints.size();
247 int const lhsIdNbit = nbit(nLhs);
248 int const lhsIdMask = (lhsIdNbit == 0) ? 0x0 : (1 << lhsIdNbit) - 1;
252 (boost::format(
"%d + %d footprints need too many bits; change IdPixelT typedef") %
262 OldIdMap overwrittenIds;
264 auto grower = [&circular, &up, &down, &
left, &
right, &isotropic](
268 auto tmpFoot = std::make_shared<Footprint>(foot->getSpans()->dilated(amount,
element),
272 int top = up ? amount : 0;
273 int bottom = down ? amount : 0;
274 int lLimit =
left ? amount : 0;
275 int rLimit =
right ? amount : 0;
277 auto yRange = top + bottom + 1;
281 for (
auto dy = 1; dy <= top; ++dy) {
284 for (
auto dy = -1; dy >= -bottom; --dy) {
288 geom::SpanSet structure(
std::move(spanList));
290 std::make_shared<Footprint>(foot->getSpans()->dilated(structure), foot->getRegion());
296 for (FootprintList::const_iterator ptr = lhsFootprints.begin(),
end = lhsFootprints.end(); ptr !=
end;
300 if (rLhs > 0 && foot->getArea() > 0) {
301 foot = grower(foot, rLhs);
306 ->clippedTo(idImage->getBBox())
307 ->applyFunctor(setIdImage<IdPixelT>(
id, &overwritten,
true), *idImage);
309 if (!overwritten.
empty()) {
310 overwrittenIds.insert(overwrittenIds.end(),
std::make_pair(
id, overwritten));
315 id = (1 << lhsIdNbit);
316 for (FootprintList::const_iterator ptr = rhsFootprints.begin(),
end = rhsFootprints.end(); ptr !=
end;
317 ++ptr,
id += (1 << lhsIdNbit)) {
320 if (rRhs > 0 && foot->getArea() > 0) {
321 foot = grower(foot, rRhs);
326 ->clippedTo(idImage->getBBox())
327 ->applyFunctor(setIdImage<IdPixelT>(
id, &overwritten,
true, lhsIdMask), *idImage);
329 if (!overwritten.
empty()) {
330 overwrittenIds.insert(overwrittenIds.end(),
std::make_pair(
id, overwritten));
334 FootprintSet fs(*idImage, Threshold(1), 1,
false);
346 FindIdsInFootprint<IdPixelT> idFinder;
347 for (
const auto& foot : *fs.getFootprints()) {
349 foot->getSpans()->applyFunctor(idFinder, *idImage);
353 for (
unsigned int indx : idFinder.getIds()) {
354 if ((indx & lhsIdMask) > 0) {
356 lhsFootprintIndxs.
insert(i);
360 OldIdMap::iterator mapPtr = overwrittenIds.find(indx);
361 if (mapPtr != overwrittenIds.end()) {
364 for (
unsigned long ptr : overwritten) {
365 lhsFootprintIndxs.
insert((ptr & lhsIdMask) - 1);
373 rhsFootprintIndxs.
insert(i);
377 OldIdMap::iterator mapPtr = overwrittenIds.find(indx);
378 if (mapPtr != overwrittenIds.end()) {
381 for (
unsigned long ptr : overwritten) {
382 rhsFootprintIndxs.
insert(ptr - 1);
393 for (
unsigned long i : lhsFootprintIndxs) {
394 assert(i < lhsFootprints.size());
395 PeakCatalog const &oldPeaks = lhsFootprints[i]->getPeaks();
397 int const nold = peaks.
size();
398 peaks.insert(peaks.end(), oldPeaks.begin(), oldPeaks.end());
403 peaks.getInternal().end(), SortPeaks());
406 for (
unsigned long i : rhsFootprintIndxs) {
407 assert(i < rhsFootprints.size());
408 PeakCatalog const &oldPeaks = rhsFootprints[i]->getPeaks();
410 int const nold = peaks.
size();
411 peaks.insert(peaks.end(), oldPeaks.begin(), oldPeaks.end());
414 peaks.getInternal().end(), SortPeaks());
426 explicit IdSpan(
int id,
int y,
int x0,
int x1,
double good) :
id(
id),
y(
y), x0(x0), x1(x1), good(good) {}
435struct IdSpanCompare {
436 bool operator()(IdSpan
const &
a, IdSpan
const &
b)
const {
439 }
else if (
a.id >
b.id) {
442 return (
a.y <
b.y) ? true :
false;
453 while (
id != aliases[
id]) {
454 resolved =
id = aliases[
id];
463template <
typename ImageT>
466 auto spanSet = foot.getSpans();
467 if (spanSet->size() == 0) {
471 for (
auto const &spanIter : *spanSet) {
472 auto y = spanIter.getY() -
image.getY0();
477 for (
auto x = spanIter.getMinX() -
image.getX0();
x <= spanIter.getMaxX() -
image.getX0(); ++
x) {
497 foot.addPeak(
x +
image.getX0(),
y +
image.getY0(), val);
502template <
typename ImageT>
503class FindMaxInFootprint {
505 explicit FindMaxInFootprint(
bool polarity)
506 : _polarity(polarity),
509 _min(
std::numeric_limits<double>::
max()),
510 _max(-
std::numeric_limits<double>::
max()) {}
528 void addRecord(
Footprint &foot)
const { foot.addPeak(_x, _y, _polarity ? _max : _min); }
536template <
typename ImageT,
typename ThresholdT>
538 findPeaksInFootprint(img, polarity, foot->getPeaks(), *foot, 1);
543 std::stable_sort(foot->getPeaks().getInternal().begin(), foot->getPeaks().getInternal().end(),
546 if (foot->getPeaks().empty()) {
547 FindMaxInFootprint<typename ImageT::Pixel> maxFinder(polarity);
548 foot->getSpans()->applyFunctor(maxFinder,
ndarray::ndImage(img.getArray(), img.getXY0()));
549 maxFinder.addRecord(*foot);
554template <
typename ImageT>
563template <
typename ImagePixelT,
typename IterT>
564static inline bool inFootprint(ImagePixelT pixVal, IterT,
bool polarity,
double thresholdVal,
565 ThresholdLevel_traits) {
566 return (polarity ? pixVal : -pixVal) >= thresholdVal;
569template <
typename ImagePixelT,
typename IterT>
570static inline bool inFootprint(ImagePixelT pixVal, IterT var,
bool polarity,
double thresholdVal,
571 ThresholdPixelLevel_traits) {
572 return (polarity ? pixVal : -pixVal) >= thresholdVal * ::sqrt(*var);
575template <
typename ImagePixelT,
typename IterT>
576static inline bool inFootprint(ImagePixelT pixVal, IterT,
bool,
double thresholdVal,
577 ThresholdBitmask_traits) {
578 return (pixVal &
static_cast<long>(thresholdVal));
584template <
typename IterT>
585static inline IterT advancePtr(IterT varPtr, Threshold_traits) {
589template <
typename IterT>
590static inline IterT advancePtr(IterT varPtr, ThresholdPixelLevel_traits) {
598template <
typename ImagePixelT,
typename MaskPixelT,
typename VariancePixelT,
typename ThresholdTraitT>
599static void findFootprints(
604 double const footprintThreshold,
605 double const includeThresholdMultiplier,
615 double includeThreshold = footprintThreshold * includeThresholdMultiplier;
617 int const row0 = img.
getY0();
618 int const col0 = img.
getX0();
633 aliases.
reserve(1 + height / 20);
646 for (
int y = 0;
y != height; ++
y) {
647 if (idc == id1.begin() + 1) {
648 idc = id2.
begin() + 1;
649 idp = id1.
begin() + 1;
651 idc = id1.
begin() + 1;
652 idp = id2.
begin() + 1;
657 bool good = (includeThresholdMultiplier == 1.0);
660 x_var_iterator varPtr = (var ==
nullptr) ?
nullptr : var->
row_begin(
y);
661 for (
int x = 0;
x < width; ++
x, ++pixPtr, varPtr = advancePtr(varPtr, ThresholdTraitT())) {
662 ImagePixelT
const pixVal = *pixPtr;
664 if (isBadPixel(pixVal) ||
665 !inFootprint(pixVal, varPtr, polarity, footprintThreshold, ThresholdTraitT())) {
673 if (idc[
x - 1] != 0) {
675 }
else if (idp[
x - 1] != 0) {
677 }
else if (idp[
x] != 0) {
679 }
else if (idp[
x + 1] != 0) {
694 if (idp[
x + 1] != 0 && idp[
x + 1] !=
id) {
695 aliases[resolve_alias(aliases, idp[
x + 1])] = resolve_alias(aliases,
id);
697 idc[
x] =
id = idp[
x + 1];
700 if (!good && inFootprint(pixVal, varPtr, polarity, includeThreshold, ThresholdTraitT())) {
713 for (
auto & span : spans) {
714 span.id = resolve_alias(aliases, span.id);
719 if (spans.size() > 0) {
720 std::sort(spans.begin(), spans.end(), IdSpanCompare());
726 if (spans.size() > 0) {
729 for (
unsigned int i = 0; i <= spans.size(); i++) {
730 if (i == spans.size() || spans[i].id !=
id) {
733 for (; i0 < i; i0++) {
734 good |= spans[i0].good;
735 tempSpanList.
emplace_back(spans[i0].
y + row0, spans[i0].x0 + col0, spans[i0].x1 + col0);
737 auto tempSpanSet = std::make_shared<geom::SpanSet>(
std::move(tempSpanList));
738 auto fp = std::make_shared<Footprint>(tempSpanSet, _region);
740 if (good && fp->getArea() >=
static_cast<std::size_t>(npixMin)) {
741 _footprints->push_back(fp);
745 if (i < spans.size()) {
754 using fiterator = FootprintSet::FootprintList::iterator;
755 for (
auto const &_footprint : *_footprints) {
756 findPeaks(_footprint, img, polarity, ThresholdTraitT());
761template <
typename ImagePixelT>
763 int const npixMin,
bool const setPeaks)
764 : _footprints(new FootprintList()), _region(img.getBBox()) {
765 using VariancePixelT = float;
767 findFootprints<ImagePixelT, image::MaskPixel, VariancePixelT, ThresholdLevel_traits>(
768 _footprints.get(), _region, img,
nullptr, threshold.getValue(img), threshold.getIncludeMultiplier(),
769 threshold.getPolarity(), npixMin, setPeaks);
774template <
typename MaskPixelT>
776 : _footprints(new FootprintList()), _region(msk.getBBox()) {
777 switch (threshold.getType()) {
778 case Threshold::BITMASK:
779 findFootprints<MaskPixelT, MaskPixelT, float, ThresholdBitmask_traits>(
780 _footprints.get(), _region, msk,
nullptr, threshold.getValue(),
781 threshold.getIncludeMultiplier(), threshold.getPolarity(), npixMin,
false);
784 case Threshold::VALUE:
785 findFootprints<MaskPixelT, MaskPixelT, float, ThresholdLevel_traits>(
786 _footprints.get(), _region, msk,
nullptr, threshold.getValue(),
787 threshold.getIncludeMultiplier(), threshold.getPolarity(), npixMin,
false);
791 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
792 "You must specify a numerical threshold value with a Mask");
796template <
typename ImagePixelT,
typename MaskPixelT>
798 Threshold
const &threshold,
std::string const &planeName,
int const npixMin,
800 : _footprints(new FootprintList()),
801 _region(
lsst::geom::
Point2I(maskedImg.getX0(), maskedImg.getY0()),
802 lsst::geom::
Extent2I(maskedImg.getWidth(), maskedImg.getHeight())) {
805 switch (threshold.getType()) {
806 case Threshold::PIXEL_STDEV:
807 findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdPixelLevel_traits>(
809 threshold.getValue(maskedImg), threshold.getIncludeMultiplier(), threshold.getPolarity(),
813 findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdLevel_traits>(
815 threshold.getValue(maskedImg), threshold.getIncludeMultiplier(), threshold.getPolarity(),
820 if (planeName ==
"") {
827 mask->addMaskPlane(planeName);
829 MaskPixelT
const bitPlane =
mask->getPlaneBitMask(planeName);
833 for (
auto const &fIter : *_footprints) {
834 fIter->getSpans()->setMask(*(maskedImg.
getMask()), bitPlane);
839 : _footprints(
std::
make_shared<FootprintList>()), _region(region) {}
841FootprintSet::FootprintSet(FootprintSet
const &rhs) : _footprints(new FootprintList), _region(rhs._region) {
842 _footprints->reserve(rhs._footprints->size());
843 for (FootprintSet::FootprintList::const_iterator ptr = rhs._footprints->begin(),
844 end = rhs._footprints->end();
846 _footprints->push_back(std::make_shared<Footprint>(**ptr));
851FootprintSet::FootprintSet(FootprintSet &&rhs) : FootprintSet(rhs) {}
854 FootprintSet tmp(rhs);
862FootprintSet::~FootprintSet() =
default;
864void FootprintSet::merge(FootprintSet
const &rhs,
int tGrow,
int rGrow,
bool isotropic) {
865 FootprintControl
const ctrl(
true, isotropic);
866 FootprintSet fs = mergeFootprintSets(*
this, tGrow, rhs, rGrow, ctrl);
873 for (
auto const &ptr : *_footprints) {
874 ptr->setRegion(region);
878FootprintSet::FootprintSet(FootprintSet
const &rhs,
int r,
bool isotropic)
879 : _footprints(new FootprintList), _region(rhs._region) {
881 FootprintSet fs = rhs;
885 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
886 (boost::format(
"I cannot grow by negative numbers: %d") % r).
str());
889 FootprintControl
const ctrl(
true, isotropic);
890 FootprintSet fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, r, ctrl);
894FootprintSet::FootprintSet(FootprintSet
const &rhs,
int ngrow, FootprintControl
const &ctrl)
895 : _footprints(new FootprintList), _region(rhs._region) {
897 FootprintSet fs = rhs;
900 }
else if (ngrow < 0) {
901 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
902 str(boost::format(
"I cannot grow by negative numbers: %d") % ngrow));
905 FootprintSet fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, ngrow, ctrl);
909FootprintSet::FootprintSet(FootprintSet
const &fs1, FootprintSet
const &fs2,
bool const)
910 : _footprints(new FootprintList()), _region(fs1._region) {
912 throw LSST_EXCEPT(pex::exceptions::LogicError,
"NOT IMPLEMENTED");
916 auto im = std::make_shared<image::Image<FootprintIdPixel>>(_region);
920 for (
auto const &fIter : *_footprints) {
922 fIter->getSpans()->applyFunctor(setIdImage<FootprintIdPixel>(
id), *im);
928template <
typename ImagePixelT,
typename MaskPixelT>
930 HeavyFootprintCtrl
const *ctrl) {
931 HeavyFootprintCtrl ctrl_s = HeavyFootprintCtrl();
937 for (FootprintList::iterator ptr = _footprints->begin(),
end = _footprints->end(); ptr !=
end; ++ptr) {
938 ptr->reset(
new HeavyFootprint<ImagePixelT, MaskPixelT>(**ptr, mimg, ctrl));
943 for (
auto const &i : *_footprints) {
955#define INSTANTIATE(PIXEL) \
956 template FootprintSet::FootprintSet(image::Image<PIXEL> const &, Threshold const &, int const, \
958 template FootprintSet::FootprintSet(image::MaskedImage<PIXEL, image::MaskPixel> const &, \
959 Threshold const &, std::string const &, int const, bool const); \
960 template void FootprintSet::makeHeavy(image::MaskedImage<PIXEL, image::MaskPixel> const &, \
961 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
Extent< int, 2 > Extent2I
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.