51 #include "boost/format.hpp"
69 struct Threshold_traits {};
70 struct ThresholdLevel_traits :
public Threshold_traits {
72 struct ThresholdPixelLevel_traits :
public Threshold_traits {
74 struct ThresholdBitmask_traits :
public Threshold_traits {
77 template <
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;
136 template <
typename T>
137 inline bool isBadPixel(
T) {
142 inline bool isBadPixel(
float val) {
147 inline bool isBadPixel(
double val) {
154 int nbit(
unsigned long i) {
168 template <
typename T>
169 class 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());
213 FootprintSet 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) {
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());
435 struct 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];
463 template <
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) {
502 template <
typename ImageT>
503 class 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); }
536 template <
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);
554 template <
typename ImageT>
563 template <
typename ImagePixelT,
typename IterT>
564 static inline bool inFootprint(ImagePixelT pixVal, IterT,
bool polarity,
double thresholdVal,
565 ThresholdLevel_traits) {
566 return (polarity ? pixVal : -pixVal) >= thresholdVal;
569 template <
typename ImagePixelT,
typename IterT>
570 static inline bool inFootprint(ImagePixelT pixVal, IterT var,
bool polarity,
double thresholdVal,
571 ThresholdPixelLevel_traits) {
572 return (polarity ? pixVal : -pixVal) >= thresholdVal * ::sqrt(*var);
575 template <
typename ImagePixelT,
typename IterT>
576 static inline bool inFootprint(ImagePixelT pixVal, IterT,
bool,
double thresholdVal,
577 ThresholdBitmask_traits) {
578 return (pixVal &
static_cast<long>(thresholdVal));
584 template <
typename IterT>
585 static inline IterT advancePtr(IterT varPtr, Threshold_traits) {
589 template <
typename IterT>
590 static inline IterT advancePtr(IterT varPtr, ThresholdPixelLevel_traits) {
598 template <
typename ImagePixelT,
typename MaskPixelT,
typename VariancePixelT,
typename ThresholdTraitT>
599 static 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) {
697 idc[
x] =
id = idp[
x + 1];
700 if (!good && inFootprint(pixVal, varPtr, polarity, includeThreshold, ThresholdTraitT())) {
713 for (
auto & span : spans) {
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());
761 template <
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);
774 template <
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");
796 template <
typename ImagePixelT,
typename MaskPixelT>
798 Threshold
const &threshold,
std::string const &planeName,
int const npixMin,
800 : _footprints(new FootprintList()),
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) {}
841 FootprintSet::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));
851 FootprintSet::FootprintSet(FootprintSet &&rhs) : FootprintSet(rhs) {}
854 FootprintSet tmp(rhs);
862 FootprintSet::~FootprintSet() =
default;
864 void 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);
878 FootprintSet::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);
894 FootprintSet::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);
909 FootprintSet::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);
928 template <
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,...)
Create an exception with a given type.
IdSpan(int id, int y, int x0, int x1)
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.
An integer coordinate rectangle.
int getMinY() const noexcept
void include(Point2I const &point)
Expand this to ensure that this->contains(point).
int getMinX() const noexcept
T emplace_back(T... args)
T inplace_merge(T... args)
int resolve_alias(const std::vector< int > &aliases, int id)
Follow a chain of aliases, returning the final resolved value.
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
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
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.