Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 

22import numpy as np 

23from scarlet.source import PointSource, ExtendedSource, MultiComponentSource 

24 

25import lsst.afw.image as afwImage 

26from lsst.afw.geom import SpanSet 

27from lsst.geom import Point2I 

28import lsst.log 

29import lsst.afw.detection as afwDet 

30 

31__all__ = ["init_source", "morphToHeavy", "modelToHeavy"] 

32 

33logger = lsst.log.Log.getLogger("meas.deblender.deblend") 

34 

35 

36def init_source(frame, peak, observation, bbox, 

37 symmetric=False, monotonic=True, 

38 thresh=5, components=1): 

39 """Initialize a Source 

40 

41 The user can specify the number of desired components 

42 for the modeled source. If scarlet cannot initialize a 

43 model with the desired number of components it continues 

44 to attempt initialization of one fewer component until 

45 it finds a model that can be initialized. 

46 

47 It is possible that scarlet will be unable to initialize a 

48 source with the desired number of components, for example 

49 a two component source might have degenerate components, 

50 a single component source might not have enough signal in 

51 the joint coadd (all bands combined together into 

52 single signal-to-noise weighted image for initialization) 

53 to initialize, and a true spurious detection will not have 

54 enough signal to initialize as a point source. 

55 If all of the models fail, including a `PointSource` model, 

56 then this source is skipped. 

57 

58 Parameters 

59 ---------- 

60 frame : `LsstFrame` 

61 The model frame for the scene 

62 peak : `PeakRecord` 

63 Record for a peak in the parent `PeakCatalog` 

64 observation : `LsstObservation` 

65 The images, psfs, etc, of the observed data. 

66 bbox : `lsst.geom.Box2I` 

67 The bounding box of the parent footprint. 

68 symmetric : `bool` 

69 Whether or not the object is symmetric 

70 monotonic : `bool` 

71 Whether or not the object has flux monotonically 

72 decreasing from its center 

73 thresh : `float` 

74 Fraction of the background to use as a threshold for 

75 each pixel in the initialization 

76 components : int 

77 The number of components for the source. 

78 If `components=0` then a `PointSource` model is used. 

79 """ 

80 assert components <= 2 

81 xmin = bbox.getMinX() 

82 ymin = bbox.getMinY() 

83 center = np.array([peak.getIy()-ymin, peak.getIx()-xmin], dtype=int) 

84 

85 while components > 1: 

86 try: 

87 source = MultiComponentSource(frame, center, observation, symmetric=symmetric, 

88 monotonic=monotonic, thresh=thresh) 

89 if (np.any([np.any(np.isnan(c.sed)) for c in source.components]) or 

90 np.any([np.all(c.sed <= 0) for c in source.components])): 

91 logger.warning("Could not initialize") 

92 raise ValueError("Could not initialize source") 

93 break 

94 except Exception: 

95 # If the MultiComponentSource failed to initialize 

96 # try an ExtendedSource 

97 components -= 1 

98 

99 if components == 1: 

100 try: 

101 source = ExtendedSource(frame, center, observation, thresh=thresh, 

102 symmetric=symmetric, monotonic=monotonic) 

103 if np.any(np.isnan(source.sed)) or np.all(source.sed <= 0) or np.sum(source.morph) == 0: 

104 logger.warning("Could not initialize") 

105 raise ValueError("Could not initialize source") 

106 except Exception: 

107 # If the source is too faint for background detection, 

108 # initialize it as a PointSource 

109 components -= 1 

110 

111 if components == 0: 

112 try: 

113 source = PointSource(frame, center, observation) 

114 except Exception: 

115 # None of the models worked to initialize the source, 

116 # so skip this source 

117 return None 

118 

119 source.detectedPeak = peak 

120 return source 

121 

122 

123def morphToHeavy(source, peakSchema, xy0=Point2I()): 

124 """Convert the morphology to a `HeavyFootprint` 

125 

126 Parameters 

127 ---------- 

128 source : `scarlet.Component` 

129 The scarlet source with a morphology to convert to 

130 a `HeavyFootprint`. 

131 peakSchema : `lsst.daf.butler.Schema` 

132 The schema for the `PeakCatalog` of the `HeavyFootprint`. 

133 xy0 : `tuple` 

134 `(x,y)` coordinates of the bounding box containing the 

135 `HeavyFootprint`. 

136 

137 Returns 

138 ------- 

139 heavy : `lsst.afw.detection.HeavyFootprint` 

140 """ 

141 mask = afwImage.MaskX(np.array(source.morph > 0, dtype=np.int32), xy0=xy0) 

142 ss = SpanSet.fromMask(mask) 

143 

144 if len(ss) == 0: 

145 return None 

146 

147 tfoot = afwDet.Footprint(ss, peakSchema=peakSchema) 

148 cy, cx = source.pixel_center 

149 xmin, ymin = xy0 

150 # HeavyFootprints are not defined for 64 bit floats 

151 morph = source.morph.astype(np.float32) 

152 peakFlux = morph[cy, cx] 

153 tfoot.addPeak(cx+xmin, cy+ymin, peakFlux) 

154 timg = afwImage.ImageF(morph, xy0=xy0) 

155 timg = timg[tfoot.getBBox()] 

156 heavy = afwDet.makeHeavyFootprint(tfoot, afwImage.MaskedImageF(timg)) 

157 return heavy 

158 

159 

160def modelToHeavy(source, filters, xy0=Point2I(), observation=None, dtype=np.float32): 

161 """Convert the model to a `MultibandFootprint` 

162 

163 Parameters 

164 ---------- 

165 source : `scarlet.Component` 

166 The source to convert to a `HeavyFootprint`. 

167 filters : `iterable` 

168 A "list" of names for each filter. 

169 xy0 : `lsst.geom.Point2I` 

170 `(x,y)` coordinates of the bounding box containing the 

171 `HeavyFootprint`. 

172 observation : `scarlet.Observation` 

173 The scarlet observation, used to convolve the image with 

174 the origin PSF. If `observation`` is `None` then the 

175 `HeavyFootprint` will exist in the model frame. 

176 dtype : `numpy.dtype` 

177 The data type for the returned `HeavyFootprint`. 

178 

179 Returns 

180 ------- 

181 mHeavy : `lsst.detection.MultibandFootprint` 

182 The multi-band footprint containing the model for the source. 

183 """ 

184 if observation is not None: 

185 model = observation.render(source.get_model()).astype(dtype) 

186 else: 

187 model = source.get_model().astype(dtype) 

188 mHeavy = afwDet.MultibandFootprint.fromArrays(filters, model, xy0=xy0) 

189 peakCat = afwDet.PeakCatalog(source.detectedPeak.table) 

190 peakCat.append(source.detectedPeak) 

191 for footprint in mHeavy: 

192 footprint.setPeakCatalog(peakCat) 

193 return mHeavy