LSST Applications 26.0.0,g0265f82a02+6660c170cc,g07994bdeae+30b05a742e,g0a0026dc87+17526d298f,g0a60f58ba1+17526d298f,g0e4bf8285c+96dd2c2ea9,g0ecae5effc+c266a536c8,g1e7d6db67d+6f7cb1f4bb,g26482f50c6+6346c0633c,g2bbee38e9b+6660c170cc,g2cc88a2952+0a4e78cd49,g3273194fdb+f6908454ef,g337abbeb29+6660c170cc,g337c41fc51+9a8f8f0815,g37c6e7c3d5+7bbafe9d37,g44018dc512+6660c170cc,g4a941329ef+4f7594a38e,g4c90b7bd52+5145c320d2,g58be5f913a+bea990ba40,g635b316a6c+8d6b3a3e56,g67924a670a+bfead8c487,g6ae5381d9b+81bc2a20b4,g93c4d6e787+26b17396bd,g98cecbdb62+ed2cb6d659,g98ffbb4407+81bc2a20b4,g9ddcbc5298+7f7571301f,ga1e77700b3+99e9273977,gae46bcf261+6660c170cc,gb2715bf1a1+17526d298f,gc86a011abf+17526d298f,gcf0d15dbbd+96dd2c2ea9,gdaeeff99f8+0d8dbea60f,gdb4ec4c597+6660c170cc,ge23793e450+96dd2c2ea9,gf041782ebf+171108ac67
LSST Data Management Base Package
Loading...
Searching...
No Matches
assembleChi2Coadd.py
Go to the documentation of this file.
1# This file is part of pipe_tasks.
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
23import logging
24import numpy as np
25
26from lsst.afw.detection import Psf
27import lsst.afw.math as afwMath
28import lsst.afw.image as afwImage
29import lsst.afw.table as afwTable
30from lsst.meas.algorithms import SourceDetectionTask
31from lsst.meas.base import SkyMapIdGeneratorConfig
32import lsst.pex.config as pexConfig
33import lsst.pipe.base as pipeBase
34import lsst.pipe.base.connectionTypes as cT
35import lsst.utils as utils
36
37
38log = logging.getLogger(__name__)
39
40
41def calculateKernelSize(sigma: float, nSigmaForKernel: float = 7) -> int:
42 """Calculate the size of the smoothing kernel.
43
44 Parameters
45 ----------
46 sigma:
47 Gaussian sigma of smoothing kernel.
48 nSigmaForKernel:
49 The multiple of `sigma` to use to set the size of the kernel.
50 Note that that is the full width of the kernel bounding box
51 (so a value of 7 means 3.5 sigma on either side of center).
52 The value will be rounded up to the nearest odd integer.
53
54 Returns
55 -------
56 size:
57 Size of the smoothing kernel.
58 """
59 return (int(sigma * nSigmaForKernel + 0.5)//2)*2 + 1 # make sure it is odd
60
61
62def convolveImage(image: afwImage.Image, psf: Psf) -> afwImage.Image:
63 """Convolve an image with a psf
64
65 This methodm and the docstring, is based off the method in
67
68 We convolve the image with a Gaussian approximation to the PSF,
69 because this is separable and therefore fast. It's technically a
70 correlation rather than a convolution, but since we use a symmetric
71 Gaussian there's no difference.
72
73 Parameters
74 ----------
75 image:
76 The image to convovle.
77 psf:
78 The PSF to convolve the `image` with.
79
80 Returns
81 -------
82 convolved:
83 The result of convolving `image` with the `psf`.
84 """
85 sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius()
86 bbox = image.getBBox()
87
88 # Smooth using a Gaussian (which is separable, hence fast) of width sigma
89 # Make a SingleGaussian (separable) kernel with the 'sigma'
90 kWidth = calculateKernelSize(sigma)
91 gaussFunc = afwMath.GaussianFunction1D(sigma)
92 gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc)
93
94 convolvedImage = image.Factory(bbox)
95
96 afwMath.convolve(convolvedImage, image, gaussKernel, afwMath.ConvolutionControl())
97
98 return convolvedImage.Factory(convolvedImage, bbox, afwImage.PARENT, False)
99
100
101class AssembleChi2CoaddConnections(pipeBase.PipelineTaskConnections,
102 dimensions=("tract", "patch", "skymap"),
103 defaultTemplates={"inputCoaddName": "deep",
104 "outputCoaddName": "deepChi2"}):
105 inputCoadds = cT.Input(
106 doc="Exposure on which to run deblending",
107 name="{inputCoaddName}Coadd_calexp",
108 storageClass="ExposureF",
109 multiple=True,
110 dimensions=("tract", "patch", "band", "skymap")
111 )
112 chi2Coadd = cT.Output(
113 doc="Chi^2 exposure, produced by merging multiband coadds",
114 name="{outputCoaddName}Coadd_calexp",
115 storageClass="ExposureF",
116 dimensions=("tract", "patch", "skymap"),
117 )
118
119
120class AssembleChi2CoaddConfig(pipeBase.PipelineTaskConfig,
121 pipelineConnections=AssembleChi2CoaddConnections):
122 outputPixelatedVariance = pexConfig.Field(
123 dtype=bool,
124 default=False,
125 doc="Whether to output a pixelated variance map for the generated "
126 "chi^2 coadd, or to have a flat variance map defined by combining "
127 "the inverse variance maps of the coadds that were combined."
128 )
129
130 useUnionForMask = pexConfig.Field(
131 dtype=bool,
132 default=True,
133 doc="Whether to calculate the union of the mask plane in each band, "
134 "or the intersection of the mask plane in each band."
135 )
136
137
138class AssembleChi2CoaddTask(pipeBase.PipelineTask):
139 """Assemble a chi^2 coadd from a collection of multi-band coadds
140
141 References
142 ----------
143 .. [1] Szalay, A. S., Connolly, A. J., and Szokoly, G. P., “Simultaneous
144 Multicolor Detection of Faint Galaxies in the Hubble Deep Field”,
145 The Astronomical Journal, vol. 117, no. 1, pp. 68–74,
146 1999. doi:10.1086/300689.
147
148 .. [2] Kaiser 2001 whitepaper,
149 http://pan-starrs.ifa.hawaii.edu/project/people/kaiser/imageprocessing/im%2B%2B.pdf # noqa: E501
150
151 .. [3] https://dmtn-015.lsst.io/
152
153 .. [4] https://project.lsst.org/meetings/law/sites/lsst.org.meetings.law/files/Building%20and%20using%20coadds.pdf # noqa: E501
154 """
155 ConfigClass = AssembleChi2CoaddConfig
156 _DefaultName = "assembleChi2Coadd"
157
158 def __init__(self, initInputs, **kwargs):
159 super().__init__(initInputs=initInputs, **kwargs)
160
161 def combinedMasks(self, masks: list[afwImage.MaskX]) -> afwImage.MaskX:
162 """Combine the mask plane in each input coadd
163
164 Parameters
165 ----------
166 mMask:
167 The MultibandMask in each band.
168
169 Returns
170 -------
171 result:
172 The resulting single band mask.
173 """
174 refMask = masks[0]
175 bbox = refMask.getBBox()
176 mask = refMask.array
177 for _mask in masks[1:]:
178 if self.config.useUnionForMask:
179 mask = mask | _mask.array
180 else:
181 mask = mask & _mask.array
182 result = refMask.Factory(bbox)
183 result.array[:] = mask
184 return result
185
186 @utils.inheritDoc(pipeBase.PipelineTask)
187 def runQuantum(self, butlerQC, inputRefs, outputRefs):
188 inputs = butlerQC.get(inputRefs)
189 outputs = self.run(**inputs)
190 butlerQC.put(outputs, outputRefs)
191
192 def run(self, inputCoadds: list[afwImage.Exposure]) -> pipeBase.Struct:
193 """Assemble the chi2 coadd from the multiband coadds
194
195 Parameters
196 ----------
197 inputCoadds:
198 The coadds to combine into a single chi2 coadd.
199
200 Returns
201 -------
202 result:
203 The chi2 coadd created from the input coadds.
204 """
205 convControl = afwMath.ConvolutionControl()
206 convControl.setDoNormalize(False)
207 convControl.setDoCopyEdge(False)
208
209 # Set a reference exposure to use for creating the new coadd.
210 # It doesn't matter which exposure we use, since we just need the
211 # bounding box information and Factory to create a new expsure with
212 # the same dtype.
213 refExp = inputCoadds[0]
214 bbox = refExp.getBBox()
215
216 image = refExp.image.Factory(bbox)
217 variance_list = []
218 # Convovle the image in each band and weight by the median variance
219 for calexp in inputCoadds:
220 convolved = convolveImage(calexp.image, calexp.getPsf())
221 _variance = np.median(calexp.variance.array)
222 convolved.array[:] /= _variance
223 image += convolved
224 variance_list.append(_variance)
225
226 variance = refExp.variance.Factory(bbox)
227 if self.config.outputPixelatedVariance:
228 # Write the per pixel variance to the output coadd
229 variance.array[:] = np.sum([1/coadd.variance for coadd in inputCoadds], axis=0)
230 else:
231 # Use a flat variance in each band
232 variance.array[:] = np.sum(1/np.array(variance_list))
233 # Combine the masks planes to calculate the mask plae of the new coadd
234 mask = self.combinedMasks([coadd.mask for coadd in inputCoadds])
235 # Create the exposure
236 maskedImage = refExp.maskedImage.Factory(image, mask=mask, variance=variance)
237 chi2coadd = refExp.Factory(maskedImage, exposureInfo=refExp.getInfo())
238 chi2coadd.info.setFilter(None)
239 return pipeBase.Struct(chi2Coadd=chi2coadd)
240
241
242class DetectChi2SourcesConnections(
243 pipeBase.PipelineTaskConnections,
244 dimensions=("tract", "patch", "skymap"),
245 defaultTemplates={
246 "inputCoaddName": "deepChi2",
247 "outputCoaddName": "deepChi2"
248 }
249):
250 detectionSchema = cT.InitOutput(
251 doc="Schema of the detection catalog",
252 name="{outputCoaddName}Coadd_det_schema",
253 storageClass="SourceCatalog",
254 )
255 exposure = cT.Input(
256 doc="Exposure on which detections are to be performed",
257 name="{inputCoaddName}Coadd_calexp",
258 storageClass="ExposureF",
259 dimensions=("tract", "patch", "skymap"),
260 )
261 outputSources = cT.Output(
262 doc="Detected sources catalog",
263 name="{outputCoaddName}Coadd_det",
264 storageClass="SourceCatalog",
265 dimensions=("tract", "patch", "skymap"),
266 )
267
268
269class DetectChi2SourcesConfig(pipeBase.PipelineTaskConfig, pipelineConnections=DetectChi2SourcesConnections):
270 detection = pexConfig.ConfigurableField(
271 target=SourceDetectionTask,
272 doc="Detect sources in chi2 coadd"
273 )
274
275 idGenerator = SkyMapIdGeneratorConfig.make_field()
276
277 def setDefaults(self):
278 super().setDefaults()
279 self.detection.reEstimateBackground = False
280 self.detection.thresholdValue = 3
281
282
283class DetectChi2SourcesTask(pipeBase.PipelineTask):
284 _DefaultName = "detectChi2Sources"
285 ConfigClass = DetectChi2SourcesConfig
286
287 def __init__(self, schema=None, **kwargs):
288 # N.B. Super is used here to handle the multiple inheritance of PipelineTasks, the init tree
289 # call structure has been reviewed carefully to be sure super will work as intended.
290 super().__init__(**kwargs)
291 if schema is None:
292 schema = afwTable.SourceTable.makeMinimalSchema()
293 self.schema = schema
294 self.makeSubtask("detection", schema=self.schema)
295 self.detectionSchema = afwTable.SourceCatalog(self.schema)
296
297 def runQuantum(self, butlerQC, inputRefs, outputRefs):
298 inputs = butlerQC.get(inputRefs)
299 idGenerator = self.config.idGenerator.apply(butlerQC.quantum.dataId)
300 inputs["idFactory"] = idGenerator.make_table_id_factory()
301 inputs["expId"] = idGenerator.catalog_id
302 outputs = self.run(**inputs)
303 butlerQC.put(outputs, outputRefs)
304
305 def run(self, exposure: afwImage.Exposure, idFactory: afwTable.IdFactory, expId: int) -> pipeBase.Struct:
306 """Run detection on a chi2 exposure.
307
308 Parameters
309 ----------
310 exposure :
311 Exposure on which to detect (may be backround-subtracted and scaled,
312 depending on configuration).
313 idFactory :
314 IdFactory to set source identifiers.
315 expId :
316 Exposure identifier (integer) for RNG seed.
317
318 Returns
319 -------
320 result : `lsst.pipe.base.Struct`
321 Results as a struct with attributes:
322 ``outputSources``
323 Catalog of detections (`lsst.afw.table.SourceCatalog`).
324 """
325 table = afwTable.SourceTable.make(self.schema, idFactory)
326 # We override `doSmooth` since the chi2 coadd has already had an
327 # extra PSF convolution applied to decorrelate the images
328 # accross bands.
329 detections = self.detection.run(table, exposure, expId=expId, doSmooth=False)
330 sources = detections.sources
331 return pipeBase.Struct(outputSources=sources)
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition Exposure.h:72
A class to represent a 2-dimensional array of pixels.
Definition Image.h:51
Parameters to control convolution.
A kernel described by a pair of functions: func(x, y) = colFunc(x) * rowFunc(y)
Definition Kernel.h:860
A polymorphic functor base class for generating record IDs for a table.
Definition IdFactory.h:21
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
Convolve an Image or MaskedImage with a Kernel, setting pixels of an existing output image.
afwImage.Image convolveImage(afwImage.Image image, Psf psf)
int calculateKernelSize(float sigma, float nSigmaForKernel=7)