34import lsst.pipe.base.connectionTypes
as cT
38log = logging.getLogger(__name__)
42 """Calculate the size of the smoothing kernel.
47 Gaussian sigma of smoothing kernel.
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.
57 Size of the smoothing kernel.
59 return (int(sigma * nSigmaForKernel + 0.5)//2)*2 + 1
63 """Convolve an image with a psf
65 This methodm and the docstring,
is based off the method
in
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.
76 The image to convovle.
78 The PSF to convolve the `image` with.
83 The result of convolving `image`
with the `psf`.
85 sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius()
86 bbox = image.getBBox()
91 gaussFunc = afwMath.GaussianFunction1D(sigma)
94 convolvedImage = image.Factory(bbox)
98 return convolvedImage.Factory(convolvedImage, bbox, afwImage.PARENT,
False)
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",
110 dimensions=(
"tract",
"patch",
"band",
"skymap")
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"),
120class AssembleChi2CoaddConfig(pipeBase.PipelineTaskConfig,
121 pipelineConnections=AssembleChi2CoaddConnections):
122 outputPixelatedVariance = pexConfig.Field(
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."
130 useUnionForMask = pexConfig.Field(
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."
138class AssembleChi2CoaddTask(pipeBase.PipelineTask):
139 """Assemble a chi^2 coadd from a collection of multi-band coadds
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.
148 .. [2] Kaiser 2001 whitepaper,
149 http://pan-starrs.ifa.hawaii.edu/project/people/kaiser/imageprocessing/im%2B%2B.pdf
151 .. [3] https://dmtn-015.lsst.io/
153 .. [4] https://project.lsst.org/meetings/law/sites/lsst.org.meetings.law/files/Building%20
and%20using%20coadds.pdf
155 ConfigClass = AssembleChi2CoaddConfig
156 _DefaultName = "assembleChi2Coadd"
158 def __init__(self, initInputs, **kwargs):
159 super().__init__(initInputs=initInputs, **kwargs)
161 def combinedMasks(self, masks: list[afwImage.MaskX]) -> afwImage.MaskX:
162 """Combine the mask plane in each input coadd
167 The MultibandMask in each band.
172 The resulting single band mask.
175 bbox = refMask.getBBox()
177 for _mask
in masks[1:]:
178 if self.config.useUnionForMask:
179 mask = mask | _mask.array
181 mask = mask & _mask.array
182 result = refMask.Factory(bbox)
183 result.array[:] = mask
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)
193 """Assemble the chi2 coadd from the multiband coadds
198 The coadds to combine into a single chi2 coadd.
203 The chi2 coadd created from the input coadds.
206 convControl.setDoNormalize(False)
207 convControl.setDoCopyEdge(
False)
213 refExp = inputCoadds[0]
214 bbox = refExp.getBBox()
216 image = refExp.image.Factory(bbox)
219 for calexp
in inputCoadds:
221 _variance = np.median(calexp.variance.array)
222 convolved.array[:] /= _variance
224 variance_list.append(_variance)
226 variance = refExp.variance.Factory(bbox)
227 if self.config.outputPixelatedVariance:
229 variance.array[:] = np.sum([1/coadd.variance
for coadd
in inputCoadds], axis=0)
232 variance.array[:] = np.sum(1/np.array(variance_list))
234 mask = self.combinedMasks([coadd.mask
for coadd
in inputCoadds])
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)
242class DetectChi2SourcesConnections(
243 pipeBase.PipelineTaskConnections,
244 dimensions=(
"tract",
"patch",
"skymap"),
246 "inputCoaddName":
"deepChi2",
247 "outputCoaddName":
"deepChi2"
250 detectionSchema = cT.InitOutput(
251 doc=
"Schema of the detection catalog",
252 name=
"{outputCoaddName}Coadd_det_schema",
253 storageClass=
"SourceCatalog",
256 doc=
"Exposure on which detections are to be performed",
257 name=
"{inputCoaddName}Coadd_calexp",
258 storageClass=
"ExposureF",
259 dimensions=(
"tract",
"patch",
"skymap"),
261 outputSources = cT.Output(
262 doc=
"Detected sources catalog",
263 name=
"{outputCoaddName}Coadd_det",
264 storageClass=
"SourceCatalog",
265 dimensions=(
"tract",
"patch",
"skymap"),
269class DetectChi2SourcesConfig(pipeBase.PipelineTaskConfig, pipelineConnections=DetectChi2SourcesConnections):
270 detection = pexConfig.ConfigurableField(
271 target=SourceDetectionTask,
272 doc=
"Detect sources in chi2 coadd"
275 idGenerator = SkyMapIdGeneratorConfig.make_field()
277 def setDefaults(self):
278 super().setDefaults()
279 self.detection.reEstimateBackground =
False
280 self.detection.thresholdValue = 3
283class DetectChi2SourcesTask(pipeBase.PipelineTask):
284 _DefaultName =
"detectChi2Sources"
285 ConfigClass = DetectChi2SourcesConfig
287 def __init__(self, schema=None, **kwargs):
290 super().__init__(**kwargs)
292 schema = afwTable.SourceTable.makeMinimalSchema()
294 self.makeSubtask(
"detection", schema=self.schema)
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)
305 def run(self, exposure: afwImage.Exposure, idFactory:
afwTable.IdFactory, expId: int) -> pipeBase.Struct:
306 """Run detection on a chi2 exposure.
311 Exposure on which to detect (may be backround-subtracted and scaled,
312 depending on configuration).
314 IdFactory to set source identifiers.
316 Exposure identifier (integer)
for RNG seed.
320 result : `lsst.pipe.base.Struct`
321 Results
as a struct
with attributes:
325 table = afwTable.SourceTable.make(self.schema, idFactory)
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.
A class to represent a 2-dimensional array of pixels.
Parameters to control convolution.
A kernel described by a pair of functions: func(x, y) = colFunc(x) * rowFunc(y)
A polymorphic functor base class for generating record IDs for a table.
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)