Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tickets/dm 8108 #79

Merged
merged 2 commits into from
Apr 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions python/lsst/meas/base/noiseReplacer.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def __init__(self, config, exposure, footprints, noiseImage=None, exposureId=Non
# the Image, not the MaskedImage.
noiseFp.insert(im)
# Also set the OTHERDET bit
afwDet.setMaskFromFootprint(mask, fp, self.otherbitmask)
fp.spans.setMask(mask, self.otherbitmask)

def insertSource(self, id):
"""!
Expand All @@ -200,8 +200,8 @@ def insertSource(self, id):
usedid = self.footprints[usedid][0]
fp = self.heavies[usedid]
fp.insert(im)
afwDet.setMaskFromFootprint(mask, fp, self.thisbitmask)
afwDet.clearMaskFromFootprint(mask, fp, self.otherbitmask)
fp.spans.setMask(mask, self.thisbitmask)
fp.spans.clearMask(mask, self.otherbitmask)

def removeSource(self, id):
"""!
Expand All @@ -227,8 +227,8 @@ def removeSource(self, id):
fp = self.heavyNoise[usedid]
fp.insert(im)
# Clear the THISDET mask plane.
afwDet.clearMaskFromFootprint(mask, fp, self.thisbitmask)
afwDet.setMaskFromFootprint(mask, fp, self.otherbitmask)
fp.spans.clearMask(mask, self.thisbitmask)
fp.spans.setMask(mask, self.otherbitmask)

def end(self):
"""!
Expand Down
3 changes: 2 additions & 1 deletion python/lsst/meas/base/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ def measure(self, measRecord, exposure):
# pixels around the object to get decent statistics
aperture = lsst.afw.geom.ellipses.Ellipse(measRecord.getShape(), measRecord.getCentroid())
aperture.scale(self.config.scale)
foot = lsst.afw.detection.Footprint(aperture)
ellipse = lsst.afw.geom.SpanSet.fromShape(aperture)
foot = lsst.afw.detection.Footprint(ellipse)
foot.clipTo(exposure.getBBox(lsst.afw.image.PARENT))
# Filter out any pixels which have mask bits set corresponding to the planes to be excluded
# (defined in config.mask)
Expand Down
18 changes: 6 additions & 12 deletions python/lsst/meas/base/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,19 +446,13 @@ def realize(self, noise, schema):
parent = catalog.find(record.getParent())
footprint = parent.getFootprint()
parentFluxArrayNoNoise = np.zeros(footprint.getArea(), dtype=np.float32)
lsst.afw.detection.flattenArray(
footprint,
self.exposure.getMaskedImage().getImage().getArray(),
parentFluxArrayNoNoise,
self.exposure.getXY0()
)
footprint.spans.flatten(parentFluxArrayNoNoise,
self.exposure.getMaskedImage().getImage().getArray(),
self.exposure.getXY0())
parentFluxArrayNoisy = np.zeros(footprint.getArea(), dtype=np.float32)
lsst.afw.detection.flattenArray(
footprint,
exposure.getMaskedImage().getImage().getArray(),
parentFluxArrayNoisy,
exposure.getXY0()
)
footprint.spans.flatten(parentFluxArrayNoisy,
exposure.getMaskedImage().getImage().getArray(),
exposure.getXY0())
oldHeavy = record.getFootprint()
fraction = (oldHeavy.getImageArray() / parentFluxArrayNoNoise)
# n.b. this isn't a copy ctor - it's a copy from a vanilla Footprint, so it doesn't copy
Expand Down
7 changes: 3 additions & 4 deletions src/Blendedness.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,15 @@ double computeOldBlendedness(
// Iterate over all the spans in the child HeavyFootprint,
// along with iterators for the child pixels (from the HeavyFootprint)
// and parent pixels (from the Exposure).
typedef afw::detection::Footprint::SpanList::const_iterator SpanIter;
typedef afw::image::Image<float>::const_x_iterator ParentPixIter;
typedef ndarray::Array<float const,1,1>::Iterator ChildPixIter;
SpanIter spanIter = childHeavy->getSpans().begin();
SpanIter const spanEnd = childHeavy->getSpans().end();
auto spanIter = childHeavy->getSpans()->begin();
auto const spanEnd = childHeavy->getSpans()->end();
ChildPixIter childPixIter = childHeavy->getImageArray().begin();
double cp = 0.0; // child.dot(parent)
double cc = 0.0; // child.dot(child)
while (spanIter != spanEnd) {
afw::geom::Span const & span = **spanIter;
afw::geom::Span const & span = *spanIter;
ParentPixIter parentPixIter = parentImage.x_at(
span.getBeginX() - parentImage.getX0(),
span.getY() - parentImage.getY0()
Expand Down
2 changes: 0 additions & 2 deletions src/GaussianFlux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
#include "lsst/afw/detection/Psf.h"
#include "lsst/afw/geom/Box.h"
#include "lsst/afw/geom/ellipses/Ellipse.h"
#include "lsst/afw/detection/FootprintArray.h"
#include "lsst/afw/detection/FootprintArray.cc"
#include "lsst/afw/table/Source.h"
#include "lsst/meas/base/GaussianFlux.h"
#include "lsst/meas/base/SdssShape.h"
Expand Down
35 changes: 16 additions & 19 deletions src/PixelFlags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,26 @@
#include "ndarray/eigen.h"

#include "lsst/afw/detection/Psf.h"
#include "lsst/afw/detection/FootprintFunctor.h"
#include "lsst/afw/table/Source.h"
#include "lsst/afw/geom/Point.h"
#include "lsst/afw/geom/SpanSet.h"
#include "lsst/meas/base/PixelFlags.h"

namespace lsst { namespace meas { namespace base {
namespace {
template <typename MaskedImageT>
class FootprintBits : public afw::detection::FootprintFunctor<MaskedImageT> {
class FootprintBits {
public:
explicit FootprintBits(MaskedImageT const& mimage) :
afw::detection::FootprintFunctor<MaskedImageT>(mimage), _bits(0)
{}
explicit FootprintBits() : _bits(0) {}

/// \brief Reset everything for a new Footprint
void reset() {
_bits = 0x0;
}
virtual void reset(afw::detection::Footprint const&) {}

/// \brief method called for each pixel by apply()
void operator()(typename MaskedImageT::xy_locator loc, ///< locator pointing at the pixel
int x, ///< column-position of pixel
int y ///< row-position of pixel
) {
_bits |= loc.mask(0, 0);

void operator()(lsst::afw::geom::Point2I const & point,
typename MaskedImageT::Mask::Pixel const & value) {
_bits |= value;
}

/// Return the union of the bits set anywhere in the Footprint
Expand Down Expand Up @@ -118,7 +113,7 @@ PixelFlagsAlgorithm::PixelFlagsAlgorithm(
_centerKeys[i] = schema.addField<afw::table::Flag>(name + "_flag_" + maskName + "Center",
"Source center is close to "+ i + " pixels");
}

for (auto const & i: _ctrl.masksFpAnywhere) {
std::string maskName(i);
std::transform(maskName.begin(), maskName.end(), maskName.begin(), ::tolower);
Expand All @@ -133,7 +128,7 @@ void PixelFlagsAlgorithm::measure(
) const {

MaskedImageF mimage = exposure.getMaskedImage();
FootprintBits<MaskedImageF> func(mimage);
FootprintBits<MaskedImageF> func;

// Check if the measRecord has a valid centroid key, i.e. it was centroided
afw::geom::Point2D center;
Expand Down Expand Up @@ -174,7 +169,7 @@ void PixelFlagsAlgorithm::measure(

// Check for bits set in the source's Footprint
afw::detection::Footprint const & footprint(*measRecord.getFootprint());
func.apply(footprint);
footprint.getSpans()->clippedTo(mimage.getBBox())->applyFunctor(func, *(mimage.getMask()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new syntax seems much more clumsy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new syntax exists for two reasons. 1. Most of this sort of work is attempted to be part of the new SpanSet class, such that in many places people can just use SpanSets, and not carry around the extra bits of footprints. I attempted to change the minimal amount in the stack to ensure maximum compatibility, and thus I did not go all in on where SpanSets could be used exclusively. Future code would probably not be used in this manor. 2. Previously the footprint functors silently dropped locations that were in the footprint but outside images, arrays, etc. This is a dangerous thing, but was commonly exploited by code that dilated the footprint such that the bounds were outside the image. For better safety it is now required that the programmer explicitly acknowledge that they wanted these pixels dropped with a clippedTo call.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, that's fair.


// Set the EDGE flag if the bitmask has NO_DATA set
try {
Expand All @@ -192,16 +187,18 @@ void PixelFlagsAlgorithm::measure(
afw::geom::Point2I llc(afw::image::positionToIndex(center.getX()) - 1,
afw::image::positionToIndex(center.getY()) - 1);

afw::detection::Footprint const middle(afw::geom::BoxI(llc, afw::geom::ExtentI(3))); // central 3x3
func.apply(middle);
func.reset();
auto spans = std::make_shared<afw::geom::SpanSet>(afw::geom::Box2I(llc, afw::geom::ExtentI(3)));
afw::detection::Footprint const middle(spans); // central 3x3
middle.getSpans()->clippedTo(mimage.getBBox())->applyFunctor(func, *(mimage.getMask()));

// Update the flags which have to do with the center of the footprint
updateFlags(_centerKeys, func, measRecord);

}

void PixelFlagsAlgorithm::fail(afw::table::SourceRecord & measRecord, MeasurementError * error) const {
measRecord.set(_generalFailureKey, true);
}

}}} // namespace lsst::meas::base

29 changes: 10 additions & 19 deletions src/PsfFlux.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

// -*- lsst-c++ -*-
/*
* LSST Data Management System
Expand Down Expand Up @@ -28,9 +29,8 @@

#include "lsst/afw/table/Source.h"
#include "lsst/afw/detection/Psf.h"
#include "lsst/afw/detection/FootprintArray.h"
#include "lsst/afw/detection/FootprintArray.cc"
#include "lsst/log/Log.h"
#include "lsst/afw/geom/SpanSet.h"
#include "lsst/meas/base/PsfFlux.h"

namespace lsst { namespace meas { namespace base {
Expand Down Expand Up @@ -88,7 +88,8 @@ void PsfFluxAlgorithm::measure(
_flagHandler.setValue(measRecord, FAILURE.number, true); // if we had a suspect flag, we'd set that instead
_flagHandler.setValue(measRecord, EDGE.number, true);
}
afw::detection::Footprint fitRegion(fitBBox);
auto fitRegionSpans = std::make_shared<afw::geom::SpanSet>(fitBBox);
afw::detection::Footprint fitRegion(fitRegionSpans);
if (!_ctrl.badMaskPlanes.empty()) {
afw::image::MaskPixel badBits = 0x0;
for (
Expand All @@ -98,7 +99,8 @@ void PsfFluxAlgorithm::measure(
) {
badBits |= exposure.getMaskedImage().getMask()->getPlaneBitMask(*i);
}
fitRegion.intersectMask(*exposure.getMaskedImage().getMask(), badBits);
fitRegion.setSpans(fitRegion.getSpans()->intersectNot(*exposure.getMaskedImage().getMask(),
badBits)->clippedTo(exposure.getMaskedImage().getMask()->getBBox()));
}
if (fitRegion.getArea() == 0) {
throw LSST_EXCEPT(
Expand All @@ -110,25 +112,14 @@ void PsfFluxAlgorithm::measure(
typedef afw::detection::Psf::Pixel PsfPixel;
typedef afw::image::MaskedImage<float>::Variance::Pixel VarPixel;
ndarray::EigenView<PsfPixel,1,1,Eigen::ArrayXpr> model(
afw::detection::flattenArray(
fitRegion,
psfImage->getArray(),
psfImage->getXY0()
)
fitRegion.getSpans()->flatten(psfImage->getArray(), psfImage->getXY0())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an improvement.

);
ndarray::EigenView<float,1,1,Eigen::ArrayXpr> data(
afw::detection::flattenArray(
fitRegion,
exposure.getMaskedImage().getImage()->getArray(),
exposure.getXY0()
)
fitRegion.getSpans()->flatten(exposure.getMaskedImage().getImage()->getArray(), exposure.getXY0())
);
ndarray::EigenView<VarPixel,1,1,Eigen::ArrayXpr> variance(
afw::detection::flattenArray(
fitRegion,
exposure.getMaskedImage().getVariance()->getArray(),
exposure.getXY0()
)
fitRegion.getSpans()->flatten(exposure.getMaskedImage().getVariance()->getArray(),
exposure.getXY0())
);
PsfPixel alpha = model.matrix().squaredNorm();
FluxResult result;
Expand Down
6 changes: 4 additions & 2 deletions tests/centroid.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ def do_testAstrometry(self, alg, bkgd, control):
im.set(x, y, (1010,))

source = table.addNew()
foot = afwDetection.Footprint(exp.getBBox(afwImage.LOCAL))
spans = afwGeom.SpanSet(exp.getBBox(afwImage.LOCAL))
foot = afwDetection.Footprint(spans)
foot.addPeak(x + x0, y + y0, 1010)
source.setFootprint(foot)
centroider.measure(source, exp)
Expand Down Expand Up @@ -145,7 +146,8 @@ def mySetup(self):
measCat = afwTable.SourceCatalog(schema)

source = measCat.addNew()
fp = afwDetection.Footprint(self.exp.getBBox(afwImage.LOCAL))
spans = afwGeom.SpanSet(self.exp.getBBox(afwImage.LOCAL))
fp = afwDetection.Footprint(spans)
fp.addPeak(50, 50, 1000.0)
source.setFootprint(fp)
# Then run the default SFM task. Results not checked
Expand Down
4 changes: 3 additions & 1 deletion tests/testInputCount.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ def testInputCounts(self, showPlot=False):

# Add simulated sources to the measurement catalog.
for src in sources:
foot = afwDetection.Footprint(afwGeom.Point2I(src.pos), 1.0)
spans = afwGeom.SpanSet.fromShape(1)
spans = spans.shiftedBy(int(src.pos.getX()), int(src.pos.getY()))
foot = afwDetection.Footprint(spans)
peak = foot.getPeaks().addNew()
peak.setFx(src.pos[0])
peak.setFy(src.pos[1])
Expand Down
12 changes: 9 additions & 3 deletions tests/testMeasureSources.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,9 @@ def testPixelFlags(self):
(20, 80, ['edge']),
(30, 30, ['clipped']),
]:
foot = afwDetection.Footprint(afwGeom.Point2I(afwGeom.Point2D(x + x0, y + y0)), 5)
spans = afwGeom.SpanSet.fromShape(5).shiftedBy(x + x0,
y + y0)
foot = afwDetection.Footprint(spans)
source = cat.makeRecord()
source.setFootprint(foot)
source.set("centroid_x", x+x0)
Expand All @@ -276,7 +278,9 @@ def testPixelFlags(self):
source.set("centroid_x", float("NAN"))
source.set("centroid_y", 40)
source.set("centroid_flag", True)
source.setFootprint(afwDetection.Footprint(afwGeom.Point2I(afwGeom.Point2D(x + x0, y + y0)), 5))
tmpSpanSet = afwGeom.SpanSet.fromShape(5).shiftedBy(x + x0,
y + y0)
source.setFootprint(afwDetection.Footprint(tmpSpanSet))
with self.assertRaises(lsst.pex.exceptions.RuntimeError):
plugin.measure(source, exp)
# Test that if there is no center and centroider that the object should look at the footprint
Expand All @@ -286,7 +290,9 @@ def testPixelFlags(self):
with self.assertRaises(lsst.pex.exceptions.RuntimeError):
plugin.measure(source, exp)
# The second test will raise an error because no peaks are present
source.setFootprint(afwDetection.Footprint(afwGeom.Point2I(afwGeom.Point2D(x + x0, y + y0)), 5))
tmpSpanSet2 = afwGeom.SpanSet.fromShape(5).shiftedBy(x + x0,
y + y0)
source.setFootprint(afwDetection.Footprint(tmpSpanSet2))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah the joys of 79 characters...

with self.assertRaises(lsst.pex.exceptions.RuntimeError):
plugin.measure(source, exp)
# The final test should pass because it detects a peak, we are reusing the location of the
Expand Down
3 changes: 2 additions & 1 deletion tests/testNoiseReplacer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#
# LSST Data Management System
# Copyright 2008-2017 LSST Corporation.
Expand Down Expand Up @@ -49,7 +50,7 @@ def measure(self, measRecord, exposure):
footprint = measRecord.getFootprint()
fullArray = exposure.getMaskedImage().getImage().getArray()
insideArray = np.zeros(footprint.getArea(), dtype=fullArray.dtype)
lsst.afw.detection.flattenArray(footprint, fullArray, insideArray, exposure.getXY0())
footprint.spans.flatten(insideArray, fullArray, exposure.getXY0())
insideFlux = float(insideArray.sum())
outsideFlux = float(fullArray.sum()) - insideFlux
measRecord.set(self.insideKey, insideFlux)
Expand Down
4 changes: 3 additions & 1 deletion tests/testSdssCentroid.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from lsst.meas.base.tests import (AlgorithmTestCase, CentroidTransformTestCase,
SingleFramePluginTransformSetupHelper)
import lsst.afw.geom as afwGeom
import lsst.utils.tests

# n.b. Some tests here depend on the noise realization in the test data
Expand Down Expand Up @@ -124,7 +125,8 @@ def testEdge(self):
# we also need to install a smaller footprint, or NoiseReplacer complains before we even get to
# measuring the centriod
record = catalog[0]
newFootprint = lsst.afw.detection.Footprint(bbox)
spanSet = afwGeom.SpanSet(bbox)
newFootprint = lsst.afw.detection.Footprint(spanSet)
peak = record.getFootprint().getPeaks()[0]
newFootprint.addPeak(peak.getFx(), peak.getFy(), peak.getPeakValue())
record.setFootprint(newFootprint)
Expand Down
4 changes: 3 additions & 1 deletion tests/testVariance.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ def setUp(self):
task = measBase.SingleFrameMeasurementTask(schema, config=config)
catalog = afwTable.SourceCatalog(schema)

foot = afwDetection.Footprint(afwGeom.Point2I(center), width)
spans = afwGeom.SpanSet.fromShape(int(width))
spans = spans.shiftedBy(int(center.getX()), int(center.getY()))
foot = afwDetection.Footprint(spans)
peak = foot.getPeaks().addNew()
peak.setIx(int(center.getX()))
peak.setIy(int(center.getY()))
Expand Down
19 changes: 11 additions & 8 deletions tests/test_undeblended.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,19 @@ def testUndeblendedMeasurement(self):
parent = cat.addNew()
parent.set("centroid_x", x0 + xCenter)
parent.set("centroid_y", y0 + yCenter)
parent.setFootprint(afwDetection.Footprint(afwGeom.Point2I(x0 + xCenter, y0 + yCenter), radius))
spanSetParent = afwGeom.SpanSet.fromShape(int(radius))
spanSetParent = spanSetParent.shiftedBy(x0 + xCenter, y0 + yCenter)
parent.setFootprint(afwDetection.Footprint(spanSetParent))

# First child is bright, dominating the blend
child1 = cat.addNew()
child1.set("centroid_x", parent.get("centroid_x"))
child1.set("centroid_y", parent.get("centroid_y"))
child1.setParent(parent.getId())
image.set(xCenter, yCenter, (flux1, 0, 0))
foot1 = afwDetection.Footprint(afwGeom.Point2I(x0 + xCenter, y0 + yCenter), 0.1)
spanSetChild1 = afwGeom.SpanSet.fromShape(1)
spanSetChild1 = spanSetChild1.shiftedBy(x0 + xCenter, y0 + yCenter)
foot1 = afwDetection.Footprint(spanSetChild1)
child1.setFootprint(afwDetection.HeavyFootprintF(foot1, image))

# Second child is fainter, but we want to be able to measure it!
Expand All @@ -107,14 +111,13 @@ def testUndeblendedMeasurement(self):
child2.set("centroid_y", parent.get("centroid_y") + yOffset)
child2.setParent(parent.getId())
image.set(xCenter + xOffset, yCenter + yOffset, (flux2, 0, 0))
foot2 = afwDetection.Footprint(afwGeom.Point2I(x0 + xCenter + xOffset, y0 + yCenter + yOffset), 0.1)
spanSetChild2 = afwGeom.SpanSet.fromShape(1)
tmpPoint = (x0 + xCenter + xOffset, y0 + yCenter + yOffset)
spanSetChild2 = spanSetChild2.shiftedBy(*tmpPoint)
foot2 = afwDetection.Footprint(spanSetChild2)
child2.setFootprint(afwDetection.HeavyFootprintF(foot2, image))

spans = []
for ss in foot1.getSpans():
spans.append(ss)
for ss in foot2.getSpans():
spans.append(ss)
spans = foot1.spans.union(foot2.spans)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an improvement.

bbox = afwGeom.Box2I()
bbox.include(foot1.getBBox())
bbox.include(foot2.getBBox())
Expand Down