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) {
280 spanList.
push_back(geom::Span(dy, 0, 0));
282 for (
auto dy = -1; dy >= -bottom; --dy) {
283 spanList.
push_back(geom::Span(dy, 0, 0));
285 spanList.
push_back(geom::Span(0, -lLimit, rLimit));
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 (FootprintList::iterator
ptr = fs.getFootprints()->begin(),
end = fs.getFootprints()->end();
350 foot->getSpans()->applyFunctor(idFinder, *idImage);
355 idptr != idend; ++idptr) {
356 unsigned int indx = *idptr;
357 if ((indx & lhsIdMask) > 0) {
359 lhsFootprintIndxs.
insert(i);
363 OldIdMap::iterator mapPtr = overwrittenIds.find(indx);
364 if (mapPtr != overwrittenIds.end()) {
369 lhsFootprintIndxs.
insert((*
ptr & lhsIdMask) - 1);
377 rhsFootprintIndxs.
insert(i);
381 OldIdMap::iterator mapPtr = overwrittenIds.find(indx);
382 if (mapPtr != overwrittenIds.end()) {
401 assert(i < lhsFootprints.size());
402 PeakCatalog const &oldPeaks = lhsFootprints[i]->getPeaks();
404 int const nold = peaks.
size();
405 peaks.insert(peaks.end(), oldPeaks.begin(), oldPeaks.end());
410 peaks.getInternal().end(), SortPeaks());
416 assert(i < rhsFootprints.size());
417 PeakCatalog const &oldPeaks = rhsFootprints[i]->getPeaks();
419 int const nold = peaks.
size();
420 peaks.insert(peaks.end(), oldPeaks.begin(), oldPeaks.end());
423 peaks.getInternal().end(), SortPeaks());
444 struct IdSpanCompare {
445 bool operator()(IdSpan
const &
a, IdSpan
const &
b)
const {
448 }
else if (
a.id >
b.id) {
451 return (
a.y <
b.y) ? true :
false;
462 while (
id != aliases[
id]) {
463 resolved =
id = aliases[
id];
472 template <
typename ImageT>
475 auto spanSet = foot.getSpans();
476 if (spanSet->size() == 0) {
480 for (
auto const &spanIter : *spanSet) {
481 auto y = spanIter.getY() -
image.getY0();
486 for (
auto x = spanIter.getMinX() -
image.getX0();
x <= spanIter.getMaxX() -
image.getX0(); ++
x) {
511 template <
typename ImageT>
512 class FindMaxInFootprint {
514 explicit FindMaxInFootprint(
bool polarity)
515 : _polarity(polarity),
518 _min(
std::numeric_limits<double>::
max()),
519 _max(-
std::numeric_limits<double>::
max()) {}
537 void addRecord(
Footprint &foot)
const { foot.addPeak(_x, _y, _polarity ? _max : _min); }
545 template <
typename ImageT,
typename ThresholdT>
547 findPeaksInFootprint(img, polarity, foot->getPeaks(), *foot, 1);
552 std::stable_sort(foot->getPeaks().getInternal().begin(), foot->getPeaks().getInternal().end(),
555 if (foot->getPeaks().empty()) {
556 FindMaxInFootprint<typename ImageT::Pixel> maxFinder(polarity);
557 foot->getSpans()->applyFunctor(maxFinder,
ndarray::ndImage(img.getArray(), img.getXY0()));
558 maxFinder.addRecord(*foot);
563 template <
typename ImageT>
572 template <
typename ImagePixelT,
typename IterT>
573 static inline bool inFootprint(ImagePixelT pixVal, IterT,
bool polarity,
double thresholdVal,
574 ThresholdLevel_traits) {
575 return (polarity ? pixVal : -pixVal) >= thresholdVal;
578 template <
typename ImagePixelT,
typename IterT>
579 static inline bool inFootprint(ImagePixelT pixVal, IterT var,
bool polarity,
double thresholdVal,
580 ThresholdPixelLevel_traits) {
581 return (polarity ? pixVal : -pixVal) >= thresholdVal * ::sqrt(*var);
584 template <
typename ImagePixelT,
typename IterT>
585 static inline bool inFootprint(ImagePixelT pixVal, IterT,
bool,
double thresholdVal,
586 ThresholdBitmask_traits) {
587 return (pixVal &
static_cast<long>(thresholdVal));
593 template <
typename IterT>
594 static inline IterT advancePtr(IterT varPtr, Threshold_traits) {
598 template <
typename IterT>
599 static inline IterT advancePtr(IterT varPtr, ThresholdPixelLevel_traits) {
607 template <
typename ImagePixelT,
typename MaskPixelT,
typename VariancePixelT,
typename ThresholdTraitT>
608 static void findFootprints(
613 double const footprintThreshold,
614 double const includeThresholdMultiplier,
624 double includeThreshold = footprintThreshold * includeThresholdMultiplier;
626 int const row0 = img.
getY0();
627 int const col0 = img.
getX0();
642 aliases.
reserve(1 + height / 20);
655 for (
int y = 0;
y != height; ++
y) {
656 if (idc == id1.begin() + 1) {
657 idc = id2.
begin() + 1;
658 idp = id1.
begin() + 1;
660 idc = id1.
begin() + 1;
661 idp = id2.
begin() + 1;
666 bool good = (includeThresholdMultiplier == 1.0);
669 x_var_iterator varPtr = (var == NULL) ? NULL : var->
row_begin(
y);
670 for (
int x = 0;
x < width; ++
x, ++pixPtr, varPtr = advancePtr(varPtr, ThresholdTraitT())) {
671 ImagePixelT
const pixVal = *pixPtr;
673 if (isBadPixel(pixVal) ||
674 !inFootprint(pixVal, varPtr, polarity, footprintThreshold, ThresholdTraitT())) {
682 if (idc[
x - 1] != 0) {
684 }
else if (idp[
x - 1] != 0) {
686 }
else if (idp[
x] != 0) {
688 }
else if (idp[
x + 1] != 0) {
703 if (idp[
x + 1] != 0 && idp[
x + 1] !=
id) {
706 idc[
x] =
id = idp[
x + 1];
709 if (!good && inFootprint(pixVal, varPtr, polarity, includeThreshold, ThresholdTraitT())) {
722 for (
unsigned int i = 0; i < spans.
size(); i++) {
728 if (spans.
size() > 0) {
735 if (spans.
size() > 0) {
738 for (
unsigned int i = 0; i <= spans.
size(); i++) {
739 if (i == spans.
size() || spans[i].id !=
id) {
742 for (; i0 < i; i0++) {
743 good |= spans[i0].good;
745 geom::Span(spans[i0].
y + row0, spans[i0].x0 + col0, spans[i0].x1 + col0));
747 auto tempSpanSet = std::make_shared<geom::SpanSet>(
std::move(tempSpanList));
748 auto fp = std::make_shared<Footprint>(tempSpanSet, _region);
750 if (good && fp->getArea() >=
static_cast<std::size_t>(npixMin)) {
751 _footprints->push_back(fp);
755 if (i < spans.
size()) {
764 typedef FootprintSet::FootprintList::iterator fiterator;
765 for (fiterator
ptr = _footprints->begin(),
end = _footprints->end();
ptr !=
end; ++
ptr) {
766 findPeaks(*
ptr, img, polarity, ThresholdTraitT());
771 template <
typename ImagePixelT>
773 int const npixMin,
bool const setPeaks)
774 : _footprints(new FootprintList()), _region(img.getBBox()) {
775 typedef float VariancePixelT;
777 findFootprints<ImagePixelT, image::MaskPixel, VariancePixelT, ThresholdLevel_traits>(
778 _footprints.get(), _region, img, NULL, threshold.getValue(img), threshold.getIncludeMultiplier(),
779 threshold.getPolarity(), npixMin, setPeaks);
784 template <
typename MaskPixelT>
786 : _footprints(new FootprintList()), _region(msk.getBBox()) {
787 switch (threshold.getType()) {
788 case Threshold::BITMASK:
789 findFootprints<MaskPixelT, MaskPixelT, float, ThresholdBitmask_traits>(
790 _footprints.get(), _region, msk, NULL, threshold.getValue(),
791 threshold.getIncludeMultiplier(), threshold.getPolarity(), npixMin,
false);
794 case Threshold::VALUE:
795 findFootprints<MaskPixelT, MaskPixelT, float, ThresholdLevel_traits>(
796 _footprints.get(), _region, msk, NULL, threshold.getValue(),
797 threshold.getIncludeMultiplier(), threshold.getPolarity(), npixMin,
false);
801 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
802 "You must specify a numerical threshold value with a Mask");
806 template <
typename ImagePixelT,
typename MaskPixelT>
808 Threshold
const &threshold,
std::string const &planeName,
int const npixMin,
810 : _footprints(new FootprintList()),
815 switch (threshold.getType()) {
816 case Threshold::PIXEL_STDEV:
817 findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdPixelLevel_traits>(
819 threshold.getValue(maskedImg), threshold.getIncludeMultiplier(), threshold.getPolarity(),
823 findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdLevel_traits>(
825 threshold.getValue(maskedImg), threshold.getIncludeMultiplier(), threshold.getPolarity(),
830 if (planeName ==
"") {
837 mask->addMaskPlane(planeName);
839 MaskPixelT
const bitPlane =
mask->getPlaneBitMask(planeName);
843 for (
auto const &fIter : *_footprints) {
844 fIter->getSpans()->setMask(*(maskedImg.
getMask()), bitPlane);
849 : _footprints(
std::
make_shared<FootprintList>()), _region(region) {}
851 FootprintSet::FootprintSet(FootprintSet
const &rhs) : _footprints(new FootprintList), _region(rhs._region) {
852 _footprints->reserve(rhs._footprints->size());
853 for (FootprintSet::FootprintList::const_iterator
ptr = rhs._footprints->begin(),
854 end = rhs._footprints->end();
856 _footprints->push_back(std::make_shared<Footprint>(**
ptr));
861 FootprintSet::FootprintSet(FootprintSet &&rhs) : FootprintSet(rhs) {}
864 FootprintSet tmp(rhs);
872 FootprintSet::~FootprintSet() =
default;
874 void FootprintSet::merge(FootprintSet
const &rhs,
int tGrow,
int rGrow,
bool isotropic) {
875 FootprintControl
const ctrl(
true, isotropic);
876 FootprintSet fs = mergeFootprintSets(*
this, tGrow, rhs, rGrow, ctrl);
883 for (FootprintSet::FootprintList::iterator
ptr = _footprints->begin(),
end = _footprints->end();
885 (*ptr)->setRegion(region);
889 FootprintSet::FootprintSet(FootprintSet
const &rhs,
int r,
bool isotropic)
890 : _footprints(new FootprintList), _region(rhs._region) {
892 FootprintSet fs = rhs;
896 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
897 (
boost::format(
"I cannot grow by negative numbers: %d") % r).str());
900 FootprintControl
const ctrl(
true, isotropic);
901 FootprintSet fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, r, ctrl);
905 FootprintSet::FootprintSet(FootprintSet
const &rhs,
int ngrow, FootprintControl
const &ctrl)
906 : _footprints(new FootprintList), _region(rhs._region) {
908 FootprintSet fs = rhs;
911 }
else if (ngrow < 0) {
912 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
913 str(
boost::format(
"I cannot grow by negative numbers: %d") % ngrow));
916 FootprintSet fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, ngrow, ctrl);
920 FootprintSet::FootprintSet(FootprintSet
const &fs1, FootprintSet
const &fs2,
bool const)
921 : _footprints(new FootprintList()), _region(fs1._region) {
923 throw LSST_EXCEPT(pex::exceptions::LogicError,
"NOT IMPLEMENTED");
927 auto im = std::make_shared<image::Image<FootprintIdPixel>>(_region);
931 for (
auto const &fIter : *_footprints) {
933 fIter->getSpans()->applyFunctor(setIdImage<FootprintIdPixel>(
id), *im);
939 template <
typename ImagePixelT,
typename MaskPixelT>
941 HeavyFootprintCtrl
const *ctrl) {
942 HeavyFootprintCtrl ctrl_s = HeavyFootprintCtrl();
948 for (FootprintList::iterator
ptr = _footprints->begin(),
end = _footprints->end();
ptr !=
end; ++
ptr) {
949 ptr->reset(
new HeavyFootprint<ImagePixelT, MaskPixelT>(**
ptr, mimg, ctrl));
954 for (FootprintList::const_iterator i = _footprints->begin(); i != _footprints->end(); ++i) {
966 #define INSTANTIATE(PIXEL) \
967 template FootprintSet::FootprintSet(image::Image<PIXEL> const &, Threshold const &, int const, \
969 template FootprintSet::FootprintSet(image::MaskedImage<PIXEL, image::MaskPixel> const &, \
970 Threshold const &, std::string const &, int const, bool const); \
971 template void FootprintSet::makeHeavy(image::MaskedImage<PIXEL, image::MaskPixel> const &, \
972 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.