Skip to content

Commit

Permalink
Merge pull request #264 from lsst/tickets/DM-42914
Browse files Browse the repository at this point in the history
DM-42914: Add edgeCenter and edgeCenterAll flags
  • Loading branch information
parejkoj committed Feb 16, 2024
2 parents c19132b + dc16ef9 commit 4cf4654
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 27 deletions.
7 changes: 4 additions & 3 deletions include/lsst/meas/base/PixelFlags.h
Expand Up @@ -78,9 +78,10 @@ class PixelFlagsAlgorithm : public SimpleAlgorithm {

private:
Control _ctrl;
KeyMap _centerKeys;
KeyMap _centerAllKeys;
KeyMap _anyKeys;
// Maps of mask plane name->afw table Key for the various fields we add to the schema on construction.
KeyMap _centerKeys; // Keys for bits set anywhere within the 3x3 region around the centroid.
KeyMap _centerAllKeys; // Keys for bits set on the entire 3x3 region around the centroid.
KeyMap _anyKeys; // Keys for bits set anywhere in the footprint.
afw::table::Key<afw::table::Flag> _generalFailureKey;
afw::table::Key<afw::table::Flag> _offImageKey;
};
Expand Down
21 changes: 11 additions & 10 deletions src/PixelFlags.cc
Expand Up @@ -107,7 +107,8 @@ PixelFlagsAlgorithm::PixelFlagsAlgorithm(Control const& ctrl, std::string const&
// Set all the flags that correspond to mask planes anywhere in the footprint
_anyKeys["EDGE"] = schema.addField<afw::table::Flag>(
name + "_flag_edge",
"Source is outside usable exposure region (masked EDGE or NO_DATA, or centroid off image)");
"Source is outside usable exposure region (masked EDGE or NO_DATA, or centroid off image).");
_anyKeys["NO_DATA"] = _anyKeys.at("EDGE"); // Also set edge flag for NO_DATA.
_anyKeys["INTRP"] = schema.addField<afw::table::Flag>(name + "_flag_interpolated",
"Interpolated pixel in the Source footprint");
_anyKeys["SAT"] = schema.addField<afw::table::Flag>(name + "_flag_saturated",
Expand All @@ -119,6 +120,9 @@ PixelFlagsAlgorithm::PixelFlagsAlgorithm(Control const& ctrl, std::string const&
_anyKeys["SUSPECT"] = schema.addField<afw::table::Flag>(name + "_flag_suspect",
"Source's footprint includes suspect pixels");
// Flags that correspond to mask bits which are set anywhere in the 3x3 central region of the object.
_centerKeys["EDGE"] = schema.addField<afw::table::Flag>(
name + "_flag_edgeCenter", "EDGE or NO_DATA Pixel in the 3x3 region around the centroid.");
_centerKeys["NO_DATA"] = _centerKeys.at("EDGE"); // Also set edge flag for NO_DATA.
_centerKeys["INTRP"] = schema.addField<afw::table::Flag>(
name + "_flag_interpolatedCenter", "Interpolated pixel in the 3x3 region around the centroid.");
_centerKeys["SAT"] = schema.addField<afw::table::Flag>(
Expand All @@ -131,6 +135,10 @@ PixelFlagsAlgorithm::PixelFlagsAlgorithm(Control const& ctrl, std::string const&
name + "_flag_suspectCenter", "Suspect pixel in the 3x3 region around the centroid.");

// Flags that correspond to mask bits which are set on all of the 3x3 central pixels of the object.
_centerAllKeys["EDGE"] = schema.addField<afw::table::Flag>(
name + "_flag_edgeCenterAll",
"All pixels in the 3x3 region around the centroid are marked EDGE or NO_DATA.");
_centerAllKeys["NO_DATA"] = _centerAllKeys.at("EDGE"); // Also set edge flag for NO_DATA.
_centerAllKeys["INTRP"] = schema.addField<afw::table::Flag>(
name + "_flag_interpolatedCenterAll",
"All pixels in the 3x3 region around the centroid are interpolated.");
Expand Down Expand Up @@ -203,6 +211,8 @@ void PixelFlagsAlgorithm::measure(afw::table::SourceRecord& measRecord,
if (!bbox.contains(center)) {
measRecord.set(_offImageKey, true);
measRecord.set(_anyKeys.at("EDGE"), true);
measRecord.set(_centerKeys.at("EDGE"), true);
measRecord.set(_centerAllKeys.at("EDGE"), true);
}

// Check for bits set in the source's Footprint
Expand All @@ -219,15 +229,6 @@ void PixelFlagsAlgorithm::measure(afw::table::SourceRecord& measRecord,
}
fullSpans->clippedTo(mimage.getBBox())->applyFunctor(func, *(mimage.getMask()));

// Set the EDGE flag if the bitmask has NO_DATA set
try {
if (func.getAnyBits() & MaskedImageF::Mask::getPlaneBitMask("NO_DATA")) {
measRecord.set(_anyKeys.at("EDGE"), true);
}
} catch (pex::exceptions::InvalidParameterError& err) {
throw LSST_EXCEPT(FatalAlgorithmError, err.what());
}

// update the source record for the any keys
updateFlags(_anyKeys, func, measRecord);

Expand Down
88 changes: 74 additions & 14 deletions tests/test_PixelFlags.py
Expand Up @@ -35,15 +35,17 @@ def setUp(self):
self.bbox = lsst.geom.Box2I(lsst.geom.Point2I(-20, -30),
lsst.geom.Extent2I(140, 160))
self.dataset = lsst.meas.base.tests.TestDataset(self.bbox)
self.dataset.addSource(100000.0, self.center)

def testNoFlags(self):
self.dataset.addSource(100000.0, self.center)
task = self.makeSingleFrameMeasurementTask("base_PixelFlags")
exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=0)
task.run(catalog, exposure)
record = catalog[0]
self.assertFalse(record.get("base_PixelFlags_flag"))
self.assertFalse(record.get("base_PixelFlags_flag_edge"))
self.assertFalse(record.get("base_PixelFlags_flag_edgeCenter"))
self.assertFalse(record.get("base_PixelFlags_flag_edgeCenterAll"))
self.assertFalse(record.get("base_PixelFlags_flag_interpolated"))
self.assertFalse(record.get("base_PixelFlags_flag_interpolatedCenter"))
self.assertFalse(record.get("base_PixelFlags_flag_interpolatedCenterAll"))
Expand All @@ -58,6 +60,7 @@ def testNoFlags(self):
self.assertFalse(record.get("base_PixelFlags_flag_badCenterAll"))

def testSomeFlags(self):
self.dataset.addSource(100000.0, self.center)
task = self.makeSingleFrameMeasurementTask("base_PixelFlags")
exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=0)
# one cr pixel outside the center
Expand All @@ -74,6 +77,9 @@ def testSomeFlags(self):
task.run(catalog, exposure)
record = catalog[0]

self.assertFalse(record.get("base_PixelFlags_flag_edge"))
self.assertFalse(record.get("base_PixelFlags_flag_edgeCenter"))
self.assertFalse(record.get("base_PixelFlags_flag_edgeCenterAll"))
self.assertTrue(record.get("base_PixelFlags_flag_cr"))
self.assertFalse(record.get("base_PixelFlags_flag_crCenter"))
self.assertFalse(record.get("base_PixelFlags_flag_crCenterAll"))
Expand All @@ -95,36 +101,90 @@ def testOffimageNonfinite(self):
self.dataset.addSource(100000, lsst.geom.Point2D(100, 30))
task = self.makeSingleFrameMeasurementTask("base_PixelFlags")
exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=0)
catalog[1]["slot_Centroid_x"] = np.inf
catalog[2]["slot_Centroid_x"] = -np.inf
catalog[3]["slot_Centroid_y"] = np.nan
catalog[0]["slot_Centroid_x"] = np.inf
catalog[1]["slot_Centroid_x"] = -np.inf
catalog[2]["slot_Centroid_y"] = np.nan
task.run(catalog, exposure)
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_offimage"],
np.array([False, True, True, True]))
np.array([True, True, True]))
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_edge"],
np.array([False, True, True, True]))
np.array([True, True, True]))
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_edgeCenter"],
np.array([True, True, True]))
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_edgeCenterAll"],
np.array([True, True, True]))

def testOffimage(self):
"""Test that sources at the boundary of the image get flag_offimage
and flag_edge set appropriately.
and flag_edge[Center[All]] set appropriately.
"""
# These four will be explicitly set to values at the boundary; we
# cannot add sources off the image in TestDataset.
self.dataset.addSource(100000, lsst.geom.Point2D(20, 20))
self.dataset.addSource(100000, lsst.geom.Point2D(20, 100))
self.dataset.addSource(100000, lsst.geom.Point2D(80, 100))
self.dataset.addSource(100000, lsst.geom.Point2D(40, 100))
self.dataset.addSource(100000, lsst.geom.Point2D(80, 30))

task = self.makeSingleFrameMeasurementTask("base_PixelFlags")
exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=0)

# Mask 4 pixels at the x-edges
edgeBit = exposure.mask.getPlaneBitMask("EDGE")
exposure.mask.array[:4, :] |= edgeBit
exposure.mask.array[-4:, ] |= edgeBit
# Mask 4 pixels at the y-edges
nodataBit = exposure.mask.getPlaneBitMask("NO_DATA")
exposure.mask.array[:, :4] |= nodataBit
exposure.mask.array[:, -4:] |= nodataBit

# All of these should get edgeCenter and edgeCenterAll.
catalog[0]["slot_Centroid_x"] = -20.5 # on image
catalog[1]["slot_Centroid_x"] = -20.51 # off image
catalog[2]["slot_Centroid_y"] = 129.4 # on image
catalog[3]["slot_Centroid_y"] = 129.50 # off image

task.run(catalog, exposure)

np.testing.assert_array_equal(catalog["base_PixelFlags_flag_offimage"],
np.array([False, True, False, True]))
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_edge"],
np.array([False, True, False, True]))
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_edgeCenter"],
np.array([True, True, True, True]))
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_edgeCenterAll"],
np.array([True, True, True, True]))

def testEdgeFlags(self):
"""Test that edge/edgeCenter/edgeCenterAll are set appropriately.
"""
# Half of the Central 3x3 pixels are on the masked EDGE; no offimage or
# edgeCenterAll, but edge and edgeCenter.
self.dataset.addSource(100000, lsst.geom.Point2D(115, 30))
# Central 3x3 pixels all masked EDGE; no offimage but all edge flags.
self.dataset.addSource(100000, lsst.geom.Point2D(10, 127))

task = self.makeSingleFrameMeasurementTask("base_PixelFlags")
exposure, catalog = self.dataset.realize(10.0, task.schema, randomSeed=0)
catalog[1]["slot_Centroid_x"] = -20.5 # on image
catalog[2]["slot_Centroid_x"] = -20.51 # off image
catalog[3]["slot_Centroid_y"] = 129.4 # on image
catalog[4]["slot_Centroid_y"] = 129.50 # off image

# Mask 4 pixels at the x-edges
edgeBit = exposure.mask.getPlaneBitMask("EDGE")
exposure.mask.array[:4, :] |= edgeBit
exposure.mask.array[-4:, ] |= edgeBit
# Mask 4 pixels at the y-edges
nodataBit = exposure.mask.getPlaneBitMask("NO_DATA")
exposure.mask.array[:, :4] |= nodataBit
exposure.mask.array[:, -4:] |= nodataBit

task.run(catalog, exposure)

np.testing.assert_array_equal(catalog["base_PixelFlags_flag_offimage"],
np.array([False, False, True, False, True]))
np.array([False, False]))
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_edge"],
np.array([False, False, True, False, True]))
np.array([True, True]))
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_edgeCenter"],
np.array([True, True]))
np.testing.assert_array_equal(catalog["base_PixelFlags_flag_edgeCenterAll"],
np.array([False, True]))


class TestMemory(lsst.utils.tests.MemoryTestCase):
Expand Down

0 comments on commit 4cf4654

Please sign in to comment.