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,
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());
435 explicit IdSpan(
int id,
int y,
int x0,
int x1,
double good) : id(id), y(y), x0(x0), x1(x1), good(good) {}
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();
479 auto bbox = image.getBBox();
480 for (
auto const &spanIter : *
spanSet) {
481 auto y = spanIter.getY() - image.getY0();
482 if (static_cast<std::size_t>(
y + image.getY0()) <
bbox.getMinY() + margin ||
486 for (
auto x = spanIter.getMinX() - image.getX0();
x <= spanIter.getMaxX() - image.getX0(); ++
x) {
487 if (static_cast<std::size_t>(
x + image.getX0()) < (
bbox.getMinX() + margin) ||
488 static_cast<std::size_t>(
x + image.getX0()) > (
bbox.getMaxX() - margin)) {
506 foot.addPeak(
x + image.getX0(),
y + image.getY0(),
val);
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 : daf::
base::Citizen(typeid(this)), _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 : daf::
base::Citizen(typeid(this)), _footprints(new FootprintList()), _region(msk.getBBox()) {
787 switch (threshold.getType()) {
789 findFootprints<MaskPixelT, MaskPixelT, float, ThresholdBitmask_traits>(
790 _footprints.get(), _region, msk, NULL, threshold.getValue(),
791 threshold.getIncludeMultiplier(), threshold.getPolarity(), npixMin,
false);
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 : daf::
base::Citizen(typeid(this)),
811 _footprints(new FootprintList()),
812 _region(
lsst::geom::
Point2I(maskedImg.getX0(), maskedImg.getY0()),
813 lsst::geom::
Extent2I(maskedImg.getWidth(), maskedImg.getHeight())) {
816 switch (threshold.getType()) {
818 findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdPixelLevel_traits>(
820 threshold.getValue(maskedImg), threshold.getIncludeMultiplier(), threshold.getPolarity(),
824 findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdLevel_traits>(
826 threshold.getValue(maskedImg), threshold.getIncludeMultiplier(), threshold.getPolarity(),
831 if (planeName ==
"") {
838 mask->addMaskPlane(planeName);
840 MaskPixelT
const bitPlane = mask->getPlaneBitMask(planeName);
844 for (
auto const &fIter : *_footprints) {
845 fIter->getSpans()->setMask(*(maskedImg.
getMask()), bitPlane);
850 : daf::
base::Citizen(typeid(this)), _footprints(
std::
make_shared<FootprintList>()), _region(region) {}
853 : daf::
base::Citizen(typeid(this)), _footprints(new FootprintList), _region(rhs._region) {
854 _footprints->reserve(rhs._footprints->size());
855 for (FootprintSet::FootprintList::const_iterator
ptr = rhs._footprints->begin(),
856 end = rhs._footprints->end();
858 _footprints->push_back(std::make_shared<Footprint>(**
ptr));
877 FootprintControl
const ctrl(
true, isotropic);
878 FootprintSet fs = mergeFootprintSets(*
this, tGrow, rhs, rGrow, ctrl);
885 for (FootprintSet::FootprintList::iterator
ptr = _footprints->begin(),
end = _footprints->end();
887 (*ptr)->setRegion(region);
892 : daf::
base::
Citizen(typeid(this)), _footprints(new FootprintList), _region(rhs._region) {
894 FootprintSet
fs = rhs;
898 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
902 FootprintControl
const ctrl(
true, isotropic);
903 FootprintSet
fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, r, ctrl);
908 : daf::
base::Citizen(typeid(this)), _footprints(new FootprintList), _region(rhs._region) {
910 FootprintSet fs = rhs;
913 }
else if (ngrow < 0) {
914 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
918 FootprintSet fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, ngrow, ctrl);
923 : daf::
base::Citizen(typeid(this)), _footprints(new FootprintList()), _region(fs1._region) {
925 throw LSST_EXCEPT(pex::exceptions::LogicError,
"NOT IMPLEMENTED");
929 auto im = std::make_shared<image::Image<FootprintIdPixel>>(_region);
933 for (
auto const &fIter : *_footprints) {
940 fIter->getSpans()->applyFunctor(setIdImage<FootprintIdPixel>(
id), *im);
946 template <
typename ImagePixelT,
typename MaskPixelT>
948 HeavyFootprintCtrl
const *ctrl) {
949 HeavyFootprintCtrl ctrl_s = HeavyFootprintCtrl();
955 for (FootprintList::iterator
ptr = _footprints->begin(),
end = _footprints->end();
ptr !=
end; ++
ptr) {
956 ptr->reset(
new HeavyFootprint<ImagePixelT, MaskPixelT>(**
ptr, mimg, ctrl));
961 for (FootprintList::const_iterator i = _footprints->begin(); i != _footprints->end(); ++i) {
973 #define INSTANTIATE(PIXEL) \ 974 template FootprintSet::FootprintSet(image::Image<PIXEL> const &, Threshold const &, int const, \ 976 template FootprintSet::FootprintSet(image::MaskedImage<PIXEL, image::MaskPixel> const &, \ 977 Threshold const &, std::string const &, int const, bool const); \ 978 template void FootprintSet::makeHeavy(image::MaskedImage<PIXEL, image::MaskPixel> const &, \ 979 HeavyFootprintCtrl const *)
VariancePtr getVariance() const
Return a (shared_ptr to) the MaskedImage's variance.
T inplace_merge(T... args)
int getHeight() const
Return the number of rows in the image.
Use (pixels & (given mask))
int getX0() const
Return the image's column-origin.
The base class for all image classed (Image, Mask, MaskedImage, ...)
x_iterator row_begin(int y) const
Return an x_iterator to the start of the y'th row.
ImagePtr getImage() const
Return a (shared_ptr to) the MaskedImage's image.
A base class for image defects.
afw::table::CatalogT< PeakRecord > PeakCatalog
Represent a 2-dimensional array of bitmask pixels.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
A class to manipulate images, masks, and variance as a single object.
MaskPtr getMask() const
Return a (shared_ptr to) the MaskedImage's mask.
int getMinX() const noexcept
void swap(Image< PixelT > &a, Image< PixelT > &b)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Extent< int, 2 > Extent2I
int getY0() const
Return the image's row-origin.
Citizen(const std::type_info &)
void include(Point2I const &point)
Expand this to ensure that this->contains(point).
SortedCatalogT< SourceRecord > SourceCatalog
int getWidth() const
Return the number of columns in the image.
lsst::afw::detection::Footprint Footprint
int resolve_alias(const std::vector< int > &aliases, int id)
Follow a chain of aliases, returning the final resolved value.
size_type size() const
Return the number of elements in the catalog.
iterator begin() const
Return an STL compliant iterator to the start of the image.
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.
Use number of sigma given per-pixel s.d.
An integer coordinate rectangle.
A class to represent a 2-dimensional array of pixels.
int getMinY() const noexcept
#define INSTANTIATE(FROMSYS, TOSYS)
std::uint64_t FootprintIdPixel
Pixel type for FootprintSet::insertIntoImage()
T emplace_back(T... args)