Coverage for python/lsst/meas/extensions/scarlet/metrics.py: 46%

24 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-05-04 03:03 -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/>. 

21 

22__all__ = [ 

23 "DeblenderMetrics", 

24 "setDeblenderMetrics", 

25] 

26 

27from dataclasses import dataclass 

28 

29import numpy as np 

30from lsst.scarlet.lite import Blend 

31 

32 

33@dataclass 

34class DeblenderMetrics: 

35 """Metrics and measurements made on single sources. 

36 

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. 

42 

43 All of the parameters are one dimensional numpy arrays, 

44 with an element for each band in the observed images. 

45 

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. 

50 

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. 

55 

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 

75 

76 

77def setDeblenderMetrics(blend: Blend): 

78 """Set metrics that can be used to evalute the deblender accuracy 

79 

80 This function calculates the `DeblenderMetrics` for each source in the 

81 blend, and assigns it to that sources `metrics` property in place. 

82 

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)