Coverage for python/lsst/meas/extensions/scarlet/metrics.py: 46%
24 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-01 16:44 -0700
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-01 16:44 -0700
1# This file is part of meas_extensions_scarlet.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22__all__ = [
23 "DeblenderMetrics",
24 "setDeblenderMetrics",
25]
27from dataclasses import dataclass
29import numpy as np
30from lsst.scarlet.lite import Blend
33@dataclass
34class DeblenderMetrics:
35 """Metrics and measurements made on single sources.
37 Store deblender metrics to be added as attributes to a scarlet source
38 before it is converted into a `SourceRecord`.
39 TODO: When DM-34414 is finished this class will be eliminated and the
40 metrics will be added to the schema using a pipeline task that calculates
41 them from the stored deconvolved models.
43 All of the parameters are one dimensional numpy arrays,
44 with an element for each band in the observed images.
46 `maxOverlap` is useful as a metric for determining how blended a source
47 is because if it only overlaps with other sources at or below
48 the noise level, it is likely to be a mostly isolated source
49 in the deconvolved model frame.
51 `fluxOverlapFraction` is potentially more useful than the canonical
52 "blendedness" (or purity) metric because it accounts for potential
53 biases created during deblending by not weighting the overlapping
54 flux with the flux of this sources model.
56 Attributes
57 ----------
58 maxOverlap:
59 The maximum overlap that the source has with its neighbors in
60 a single pixel.
61 fluxOverlap:
62 The total flux from neighbors overlapping with the current source.
63 fluxOverlapFraction:
64 The fraction of `flux from neighbors/source flux` for a
65 given source within the source's footprint.
66 blendedness:
67 The metric for determining how blended a source is using the
68 Bosch et al. 2018 metric for "blendedness." Note that some
69 surveys use the term "purity," which is `1-blendedness`.
70 """
71 maxOverlap: np.array
72 fluxOverlap: np.array
73 fluxOverlapFraction: np.array
74 blendedness: np.array
77def setDeblenderMetrics(blend: Blend):
78 """Set metrics that can be used to evalute the deblender accuracy
80 This function calculates the `DeblenderMetrics` for each source in the
81 blend, and assigns it to that sources `metrics` property in place.
83 Parameters
84 ----------
85 blend:
86 The blend containing the sources to measure.
87 """
88 # Store the full model of the scene for comparison
89 blendModel = blend.get_model().data
90 for k, src in enumerate(blend.sources):
91 # Extract the source model in the full bounding box
92 model = src.get_model().project(bbox=blend.bbox).data
93 # The footprint is the 2D array of non-zero pixels in each band
94 footprint = np.bitwise_or.reduce(model > 0, axis=0)
95 # Calculate the metrics.
96 # See `DeblenderMetrics` for a description of each metric.
97 neighborOverlap = (blendModel-model) * footprint[None, :, :]
98 maxOverlap = np.max(neighborOverlap, axis=(1, 2))
99 fluxOverlap = np.sum(neighborOverlap, axis=(1, 2))
100 fluxModel = np.sum(model, axis=(1, 2))
101 fluxOverlapFraction = np.zeros((len(model), ), dtype=float)
102 isFinite = fluxModel > 0
103 fluxOverlapFraction[isFinite] = fluxOverlap[isFinite]/fluxModel[isFinite]
104 blendedness = 1 - np.sum(model*model, axis=(1, 2))/np.sum(blendModel*model, axis=(1, 2))
105 src.metrics = DeblenderMetrics(maxOverlap, fluxOverlap, fluxOverlapFraction, blendedness)