206 def _buildCellSet(self, exposure, referencePsfModel):
207 """Build a SpatialCellSet for use with the solve method
208
209 Parameters
210 ----------
211 exposure : `lsst.afw.image.Exposure`
212 The science exposure that will be convolved; must contain a Psf
213 referencePsfModel : `lsst.afw.detection.Psf`
214 Psf model to match to
215
216 Returns
217 -------
218 result : `struct`
219 - ``kernelCellSet`` : a SpatialCellSet to be used by self._solve
220 - ``referencePsfModel`` : Validated and/or modified
221 reference model used to populate the SpatialCellSet
222
223 Notes
224 -----
225 If the reference Psf model and science Psf model have different dimensions,
226 adjust the referencePsfModel (the model to which the exposure PSF will be matched)
227 to match that of the science Psf. If the science Psf dimensions vary across the image,
228 as is common with a WarpedPsf, either pad or clip (depending on config.padPsf)
229 the dimensions to be constant.
230 """
231 sizeCellX = self.kConfig.sizeCellX
232 sizeCellY = self.kConfig.sizeCellY
233
234 scienceBBox = exposure.getBBox()
235
237
238 sciencePsfModel = exposure.getPsf()
239
240 dimenR = referencePsfModel.getLocalKernel(scienceBBox.getCenter()).getDimensions()
241
242 regionSizeX, regionSizeY = scienceBBox.getDimensions()
243 scienceX0, scienceY0 = scienceBBox.getMin()
244
246
247 nCellX = regionSizeX//sizeCellX
248 nCellY = regionSizeY//sizeCellY
249
250 if nCellX == 0 or nCellY == 0:
251 raise ValueError("Exposure dimensions=%s and sizeCell=(%s, %s). Insufficient area to match" %
252 (scienceBBox.getDimensions(), sizeCellX, sizeCellY))
253
254
255
256 widthList = []
257 heightList = []
258 for row in range(nCellY):
259 posY = sizeCellY*row + sizeCellY//2 + scienceY0
260 for col in range(nCellX):
261 posX = sizeCellX*col + sizeCellX//2 + scienceX0
262 widthS, heightS = sciencePsfModel.computeBBox(
geom.Point2D(posX, posY)).getDimensions()
263 widthList.append(widthS)
264 heightList.append(heightS)
265
266 psfSize =
max(
max(heightList),
max(widthList))
267
268 if self.config.doAutoPadPsf:
269 minPsfSize = nextOddInteger(self.kConfig.kernelSize*self.config.autoPadPsfTo)
270 paddingPix =
max(0, minPsfSize - psfSize)
271 else:
272 if self.config.padPsfBy % 2 != 0:
273 raise ValueError("Config padPsfBy (%i pixels) must be even number." %
274 self.config.padPsfBy)
275 paddingPix = self.config.padPsfBy
276
277 if paddingPix > 0:
278 self.log.debug("Padding Science PSF from (%d, %d) to (%d, %d) pixels",
279 psfSize, psfSize, paddingPix + psfSize, paddingPix + psfSize)
280 psfSize += paddingPix
281
282
283 maxKernelSize = psfSize - 1
284 if maxKernelSize % 2 == 0:
285 maxKernelSize -= 1
286 if self.kConfig.kernelSize > maxKernelSize:
287 message = """
288 Kernel size (%d) too big to match Psfs of size %d.
289 Please reconfigure by setting one of the following:
290 1) kernel size to <= %d
291 2) doAutoPadPsf=True
292 3) padPsfBy to >= %s
293 """ % (self.kConfig.kernelSize, psfSize,
294 maxKernelSize, self.kConfig.kernelSize - maxKernelSize)
295 raise ValueError(message)
296
298
299 if (dimenR != dimenS):
300 try:
301 referencePsfModel = referencePsfModel.resized(psfSize, psfSize)
302 self.log.info("Adjusted dimensions of reference PSF model from %s to %s", dimenR, dimenS)
303 except Exception as e:
304 self.log.warning("Zero padding or clipping the reference PSF model of type %s and dimensions"
305 " %s to the science Psf dimensions %s because: %s",
306 referencePsfModel.__class__.__name__, dimenR, dimenS, e)
307 dimenR = dimenS
308
309 ps = pexConfig.makePropertySet(self.kConfig)
310 for row in range(nCellY):
311
312 posY = sizeCellY*row + sizeCellY//2 + scienceY0
313
314 for col in range(nCellX):
315
316 posX = sizeCellX*col + sizeCellX//2 + scienceX0
317
318 getTraceLogger(self.log, 4).debug("Creating Psf candidate at %.1f %.1f", posX, posY)
319
320
321 referenceMI = self._makePsfMaskedImage(referencePsfModel, posX, posY, dimensions=dimenR)
322
323
324 scienceMI = self._makePsfMaskedImage(sciencePsfModel, posX, posY, dimensions=dimenR)
325
326
327 kc = diffimLib.makeKernelCandidate(posX, posY, scienceMI, referenceMI, ps)
328 kernelCellSet.insertCandidate(kc)
329
330 import lsstDebug
332 displaySpatialCells =
lsstDebug.Info(__name__).displaySpatialCells
334 if not maskTransparency:
335 maskTransparency = 0
336 if display:
337 afwDisplay.setDefaultMaskTransparency(maskTransparency)
338 if display and displaySpatialCells:
339 dituils.showKernelSpatialCells(exposure.getMaskedImage(), kernelCellSet,
340 symb="o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
341 ctypeBad=afwDisplay.RED, size=4, frame=lsstDebug.frame,
342 title="Image to be convolved")
343 lsstDebug.frame += 1
344 return pipeBase.Struct(kernelCellSet=kernelCellSet,
345 referencePsfModel=referencePsfModel,
346 )
347
A collection of SpatialCells covering an entire image.
An integer coordinate rectangle.