31import lsst.pipe.base.connectionTypes
as cT
32import lsst.utils
as utils
35log = logging.getLogger(__name__)
39 """Calculate the size of the smoothing kernel.
44 Gaussian sigma of smoothing kernel.
46 The multiple of `sigma` to use to set the size of the kernel.
47 Note that that is the full width of the kernel bounding box
48 (so a value of 7 means 3.5 sigma on either side of center).
49 The value will be rounded up to the nearest odd integer.
54 Size of the smoothing kernel.
56 return (int(sigma * nSigmaForKernel + 0.5)//2)*2 + 1
60 """Convolve an image with a psf
62 This methodm and the docstring,
is based off the method
in
65 We convolve the image
with a Gaussian approximation to the PSF,
66 because this
is separable
and therefore fast. It
's technically a
67 correlation rather than a convolution, but since we use a symmetric
68 Gaussian there's no difference.
73 The image to convovle.
75 The PSF to convolve the `image` with.
80 The result of convolving `image`
with the `psf`.
82 sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius()
83 bbox = image.getBBox()
88 gaussFunc = afwMath.GaussianFunction1D(sigma)
89 gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc)
91 convolvedImage = image.Factory(bbox)
93 afwMath.convolve(convolvedImage, image, gaussKernel, afwMath.ConvolutionControl())
95 return convolvedImage.Factory(convolvedImage, bbox, afwImage.PARENT,
False)
99 dimensions=(
"tract",
"patch",
"band",
"skymap"),
100 defaultTemplates={
"inputCoaddName":
"deep",
101 "outputCoaddName":
"deep"}):
102 inputCoadds = cT.Input(
103 doc=
"Exposure on which to run deblending",
104 name=
"{inputCoaddName}Coadd_calexp",
105 storageClass=
"ExposureF",
107 dimensions=(
"tract",
"patch",
"band",
"skymap")
109 chi2Coadd = cT.Output(
110 doc=
"Chi^2 exposure, produced by merging multiband coadds",
111 name=
"{outputCoaddName}Chi2Coadd",
112 storageClass=
"ExposureF",
113 dimensions=(
"tract",
"patch",
"skymap"),
117class AssembleChi2CoaddConfig(pipeBase.PipelineTaskConfig,
118 pipelineConnections=AssembleChi2CoaddConnections):
119 outputPixelatedVariance = pexConfig.Field(
122 doc=
"Whether to output a pixelated variance map for the generated "
123 "chi^2 coadd, or to have a flat variance map defined by combining "
124 "the inverse variance maps of the coadds that were combined."
127 useUnionForMask = pexConfig.Field(
130 doc=
"Whether to calculate the union of the mask plane in each band, "
131 "or the intersection of the mask plane in each band."
135class AssembleChi2CoaddTask(pipeBase.Task):
136 """Assemble a chi^2 (Kaiser) coadd from a collection of multi-band coadds
138 See Kaiser 2001 for more information.
140 ConfigClass = AssembleChi2CoaddConfig
141 _DefaultName = "assembleChi2Coadd"
143 def __init__(self, *args, **kwargs):
144 super().__init__(*args, **kwargs)
146 def combinedMasks(self, masks: list[afwImage.MaskX]) -> afwImage.MaskX:
147 """Combine the mask plane in each input coadd
152 The MultibandMask in each band.
157 The resulting single band mask.
160 bbox = refMask.getBBox()
162 for _mask
in masks[1:]:
163 if self.config.useUnionForMask:
164 mask = mask | _mask.array
166 mask = mask & _mask.array
167 result = refMask.Factory(bbox)
168 result.array[:] = mask
171 @utils.inheritDoc(pipeBase.PipelineTask)
172 def runQuantum(self, butlerQC, inputRefs, outputRefs):
173 inputs = butlerQC.get(inputRefs)
174 outputs = self.run(**inputs)
175 butlerQC.put(outputs, outputRefs)
177 def run(self, inputCoadds: list[afwImage.Exposure]) -> pipeBase.Struct:
178 """Assemble the chi2 coadd from the multiband coadds
183 The coadds to combine into a single chi2 coadd.
188 The chi2 coadd created from the input coadds.
190 convControl = afwMath.ConvolutionControl()
191 convControl.setDoNormalize(False)
192 convControl.setDoCopyEdge(
False)
198 refExp = inputCoadds[0]
199 bbox = refExp.getBBox()
201 image = refExp.image.Factory(bbox)
204 for calexp
in inputCoadds:
206 _variance = np.median(calexp.variance.array)
207 convolved.array[:] /= _variance
209 variance_list.append(_variance)
211 variance = refExp.variance.Factory(bbox)
212 if self.config.outputPixelatedVariance:
214 variance.array[:] = np.sum([1/coadd.variance
for coadd
in inputCoadds], axis=0)
217 variance.array[:] = np.sum(1/np.array(variance_list))
219 mask = self.combinedMasks([coadd.mask
for coadd
in inputCoadds])
221 maskedImage = refExp.maskedImage.Factory(image, mask=mask, variance=variance)
222 chi2coadd = refExp.Factory(maskedImage, exposureInfo=refExp.getInfo())
223 return pipeBase.Struct(chi2Coadd=chi2coadd)
afwImage.Image convolveImage(afwImage.Image image, Psf psf)
int calculateKernelSize(float sigma, float nSigmaForKernel=7)