LSSTApplications  18.0.0+106,18.0.0+50,19.0.0,19.0.0+1,19.0.0+10,19.0.0+11,19.0.0+13,19.0.0+17,19.0.0+2,19.0.0-1-g20d9b18+6,19.0.0-1-g425ff20,19.0.0-1-g5549ca4,19.0.0-1-g580fafe+6,19.0.0-1-g6fe20d0+1,19.0.0-1-g7011481+9,19.0.0-1-g8c57eb9+6,19.0.0-1-gb5175dc+11,19.0.0-1-gdc0e4a7+9,19.0.0-1-ge272bc4+6,19.0.0-1-ge3aa853,19.0.0-10-g448f008b,19.0.0-12-g6990b2c,19.0.0-2-g0d9f9cd+11,19.0.0-2-g3d9e4fb2+11,19.0.0-2-g5037de4,19.0.0-2-gb96a1c4+3,19.0.0-2-gd955cfd+15,19.0.0-3-g2d13df8,19.0.0-3-g6f3c7dc,19.0.0-4-g725f80e+11,19.0.0-4-ga671dab3b+1,19.0.0-4-gad373c5+3,19.0.0-5-ga2acb9c+2,19.0.0-5-gfe96e6c+2,w.2020.01
LSSTDataManagementBasePackage
modelPsfMatch.py
Go to the documentation of this file.
1 # This file is part of ip_diffim.
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 import numpy as np
23 
24 from . import diffimLib
25 import lsst.afw.display as afwDisplay
26 import lsst.afw.image as afwImage
27 import lsst.afw.math as afwMath
28 import lsst.geom as geom
29 import lsst.log as log
30 import lsst.pex.config as pexConfig
31 import lsst.pipe.base as pipeBase
32 from .makeKernelBasisList import makeKernelBasisList
33 from .psfMatch import PsfMatchTask, PsfMatchConfigAL
34 from . import utils as dituils
35 
36 __all__ = ("ModelPsfMatchTask", "ModelPsfMatchConfig")
37 
38 sigma2fwhm = 2.*np.sqrt(2.*np.log(2.))
39 
40 
42  nextInt = int(np.ceil(x))
43  return nextInt + 1 if nextInt%2 == 0 else nextInt
44 
45 
46 class ModelPsfMatchConfig(pexConfig.Config):
47  """Configuration for model-to-model Psf matching"""
48 
49  kernel = pexConfig.ConfigChoiceField(
50  doc="kernel type",
51  typemap=dict(
52  AL=PsfMatchConfigAL,
53  ),
54  default="AL",
55  )
56  doAutoPadPsf = pexConfig.Field(
57  dtype=bool,
58  doc=("If too small, automatically pad the science Psf? "
59  "Pad to smallest dimensions appropriate for the matching kernel dimensions, "
60  "as specified by autoPadPsfTo. If false, pad by the padPsfBy config."),
61  default=True,
62  )
63  autoPadPsfTo = pexConfig.RangeField(
64  dtype=float,
65  doc=("Minimum Science Psf dimensions as a fraction of matching kernel dimensions. "
66  "If the dimensions of the Psf to be matched are less than the "
67  "matching kernel dimensions * autoPadPsfTo, pad Science Psf to this size. "
68  "Ignored if doAutoPadPsf=False."),
69  default=1.4,
70  min=1.0,
71  max=2.0
72  )
73  padPsfBy = pexConfig.Field(
74  dtype=int,
75  doc="Pixels (even) to pad Science Psf by before matching. Ignored if doAutoPadPsf=True",
76  default=0,
77  )
78 
79  def setDefaults(self):
80  # No sigma clipping
81  self.kernel.active.singleKernelClipping = False
82  self.kernel.active.kernelSumClipping = False
83  self.kernel.active.spatialKernelClipping = False
84  self.kernel.active.checkConditionNumber = False
85 
86  # Variance is ill defined
87  self.kernel.active.constantVarianceWeighting = True
88 
89  # Do not change specified kernel size
90  self.kernel.active.scaleByFwhm = False
91 
92 
94  """Matching of two model Psfs, and application of the Psf-matching kernel to an input Exposure
95 
96  Notes
97  -----
98 
99  This Task differs from ImagePsfMatchTask in that it matches two Psf _models_, by realizing
100  them in an Exposure-sized SpatialCellSet and then inserting each Psf-image pair into KernelCandidates.
101  Because none of the pairs of sources that are to be matched should be invalid, all sigma clipping is
102  turned off in ModelPsfMatchConfig. And because there is no tracked _variance_ in the Psf images, the
103  debugging and logging QA info should be interpreted with caution.
104 
105  One item of note is that the sizes of Psf models are fixed (e.g. its defined as a 21x21 matrix). When the
106  Psf-matching kernel is being solved for, the Psf "image" is convolved with each kernel basis function,
107  leading to a loss of information around the borders.
108  This pixel loss will be problematic for the numerical
109  stability of the kernel solution if the size of the convolution kernel
110  (set by ModelPsfMatchConfig.kernelSize) is much bigger than: psfSize//2.
111  Thus the sizes of Psf-model matching kernels are typically smaller
112  than their image-matching counterparts. If the size of the kernel is too small, the convolved stars will
113  look "boxy"; if the kernel is too large, the kernel solution will be "noisy". This is a trade-off that
114  needs careful attention for a given dataset.
115 
116  The primary use case for this Task is in matching an Exposure to a
117  constant-across-the-sky Psf model for the purposes of image coaddition.
118  It is important to note that in the code, the "template" Psf is the Psf
119  that the science image gets matched to. In this sense the order of template and science image are
120  reversed, compared to ImagePsfMatchTask, which operates on the template image.
121 
122  Debug variables
123 
124  The `lsst.pipe.base.cmdLineTask.CmdLineTask` command line task interface supports a
125  flag -d/--debug to import debug.py from your PYTHONPATH. The relevant contents of debug.py
126  for this Task include:
127 
128  .. code-block:: py
129 
130  import sys
131  import lsstDebug
132  def DebugInfo(name):
133  di = lsstDebug.getInfo(name)
134  if name == "lsst.ip.diffim.psfMatch":
135  di.display = True # global
136  di.maskTransparency = 80 # mask transparency
137  di.displayCandidates = True # show all the candidates and residuals
138  di.displayKernelBasis = False # show kernel basis functions
139  di.displayKernelMosaic = True # show kernel realized across the image
140  di.plotKernelSpatialModel = False # show coefficients of spatial model
141  di.showBadCandidates = True # show the bad candidates (red) along with good (green)
142  elif name == "lsst.ip.diffim.modelPsfMatch":
143  di.display = True # global
144  di.maskTransparency = 30 # mask transparency
145  di.displaySpatialCells = True # show spatial cells before the fit
146  return di
147  lsstDebug.Info = DebugInfo
148  lsstDebug.frame = 1
149 
150  Note that if you want addional logging info, you may add to your scripts:
151 
152  .. code-block:: py
153 
154  import lsst.log.utils as logUtils
155  logUtils.traceSetAt("ip.diffim", 4)
156 
157  Examples
158  --------
159  A complete example of using ModelPsfMatchTask
160 
161  This code is modelPsfMatchTask.py in the examples directory, and can be run as e.g.
162 
163  .. code-block :: none
164 
165  examples/modelPsfMatchTask.py
166  examples/modelPsfMatchTask.py --debug
167  examples/modelPsfMatchTask.py --debug --template /path/to/templateExp.fits
168  --science /path/to/scienceExp.fits
169 
170  Create a subclass of ModelPsfMatchTask that accepts two exposures.
171  Note that the "template" exposure contains the Psf that will get matched to,
172  and the "science" exposure is the one that will be convolved:
173 
174  .. code-block :: none
175 
176  class MyModelPsfMatchTask(ModelPsfMatchTask):
177  def __init__(self, *args, **kwargs):
178  ModelPsfMatchTask.__init__(self, *args, **kwargs)
179  def run(self, templateExp, scienceExp):
180  return ModelPsfMatchTask.run(self, scienceExp, templateExp.getPsf())
181 
182  And allow the user the freedom to either run the script in default mode,
183  or point to their own images on disk. Note that these
184  images must be readable as an lsst.afw.image.Exposure:
185 
186  .. code-block :: none
187 
188  if __name__ == "__main__":
189  import argparse
190  parser = argparse.ArgumentParser(description="Demonstrate the use of ModelPsfMatchTask")
191  parser.add_argument("--debug", "-d", action="store_true", help="Load debug.py?", default=False)
192  parser.add_argument("--template", "-t", help="Template Exposure to use", default=None)
193  parser.add_argument("--science", "-s", help="Science Exposure to use", default=None)
194  args = parser.parse_args()
195 
196  We have enabled some minor display debugging in this script via the –debug option.
197  However, if you have an lsstDebug debug.py in your PYTHONPATH you will get additional
198  debugging displays. The following block checks for this script:
199 
200  .. code-block :: none
201 
202  if args.debug:
203  try:
204  import debug
205  # Since I am displaying 2 images here, set the starting frame number for the LSST debug LSST
206  debug.lsstDebug.frame = 3
207  except ImportError as e:
208  print(e, file=sys.stderr)
209 
210  Finally, we call a run method that we define below.
211  First set up a Config and modify some of the parameters.
212  In particular we don't want to "grow" the sizes of the kernel or KernelCandidates,
213  since we are operating with fixed–size images (i.e. the size of the input Psf models).
214 
215  .. code-block :: none
216 
217  def run(args):
218  #
219  # Create the Config and use sum of gaussian basis
220  #
221  config = ModelPsfMatchTask.ConfigClass()
222  config.kernel.active.scaleByFwhm = False
223 
224  Make sure the images (if any) that were sent to the script exist on disk and are readable.
225  If no images are sent, make some fake data up for the sake of this example script
226  (have a look at the code if you want more details on generateFakeData):
227 
228  .. code-block :: none
229 
230  # Run the requested method of the Task
231  if args.template is not None and args.science is not None:
232  if not os.path.isfile(args.template):
233  raise Exception("Template image %s does not exist" % (args.template))
234  if not os.path.isfile(args.science):
235  raise Exception("Science image %s does not exist" % (args.science))
236  try:
237  templateExp = afwImage.ExposureF(args.template)
238  except Exception as e:
239  raise Exception("Cannot read template image %s" % (args.template))
240  try:
241  scienceExp = afwImage.ExposureF(args.science)
242  except Exception as e:
243  raise Exception("Cannot read science image %s" % (args.science))
244  else:
245  templateExp, scienceExp = generateFakeData()
246  config.kernel.active.sizeCellX = 128
247  config.kernel.active.sizeCellY = 128
248 
249  .. code-block :: none
250 
251  if args.debug:
252  afwDisplay.Display(frame=1).mtv(templateExp, title="Example script: Input Template")
253  afwDisplay.Display(frame=2).mtv(scienceExp, title="Example script: Input Science Image")
254 
255  Create and run the Task:
256 
257  .. code-block :: none
258 
259  # Create the Task
260  psfMatchTask = MyModelPsfMatchTask(config=config)
261  # Run the Task
262  result = psfMatchTask.run(templateExp, scienceExp)
263 
264  And finally provide optional debugging display of the Psf-matched (via the Psf models) science image:
265 
266  .. code-block :: none
267 
268  if args.debug:
269  # See if the LSST debug has incremented the frame number; if not start with frame 3
270  try:
271  frame = debug.lsstDebug.frame + 1
272  except Exception:
273  frame = 3
274  afwDisplay.Display(frame=frame).mtv(result.psfMatchedExposure,
275  title="Example script: Matched Science Image")
276 
277  """
278  ConfigClass = ModelPsfMatchConfig
279 
280  def __init__(self, *args, **kwargs):
281  """Create a ModelPsfMatchTask
282 
283  Parameters
284  ----------
285  *args
286  arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__
287  **kwargs
288  keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__
289 
290  Notes
291  -----
292  Upon initialization, the kernel configuration is defined by self.config.kernel.active. This Task
293  does have a run() method, which is the default way to call the Task.
294  """
295  PsfMatchTask.__init__(self, *args, **kwargs)
296  self.kConfig = self.config.kernel.active
297 
298  @pipeBase.timeMethod
299  def run(self, exposure, referencePsfModel, kernelSum=1.0):
300  """Psf-match an exposure to a model Psf
301 
302  Parameters
303  ----------
304  exposure : `lsst.afw.image.Exposure`
305  Exposure to Psf-match to the reference Psf model;
306  it must return a valid PSF model via exposure.getPsf()
307  referencePsfModel : `lsst.afw.detection.Psf`
308  The Psf model to match to
309  kernelSum : `float`, optional
310  A multipicative factor to apply to the kernel sum (default=1.0)
311 
312  Returns
313  -------
314  result : `struct`
315  - ``psfMatchedExposure`` : the Psf-matched Exposure.
316  This has the same parent bbox, Wcs, PhotoCalib and
317  Filter as the input Exposure but no Psf.
318  In theory the Psf should equal referencePsfModel but
319  the match is likely not exact.
320  - ``psfMatchingKernel`` : the spatially varying Psf-matching kernel
321  - ``kernelCellSet`` : SpatialCellSet used to solve for the Psf-matching kernel
322  - ``referencePsfModel`` : Validated and/or modified reference model used
323 
324  Raises
325  ------
326  RuntimeError
327  if the Exposure does not contain a Psf model
328  """
329  if not exposure.hasPsf():
330  raise RuntimeError("exposure does not contain a Psf model")
331 
332  maskedImage = exposure.getMaskedImage()
333 
334  self.log.info("compute Psf-matching kernel")
335  result = self._buildCellSet(exposure, referencePsfModel)
336  kernelCellSet = result.kernelCellSet
337  referencePsfModel = result.referencePsfModel
338  fwhmScience = exposure.getPsf().computeShape().getDeterminantRadius()*sigma2fwhm
339  fwhmModel = referencePsfModel.computeShape().getDeterminantRadius()*sigma2fwhm
340 
341  basisList = makeKernelBasisList(self.kConfig, fwhmScience, fwhmModel, metadata=self.metadata)
342  spatialSolution, psfMatchingKernel, backgroundModel = self._solve(kernelCellSet, basisList)
343 
344  if psfMatchingKernel.isSpatiallyVarying():
345  sParameters = np.array(psfMatchingKernel.getSpatialParameters())
346  sParameters[0][0] = kernelSum
347  psfMatchingKernel.setSpatialParameters(sParameters)
348  else:
349  kParameters = np.array(psfMatchingKernel.getKernelParameters())
350  kParameters[0] = kernelSum
351  psfMatchingKernel.setKernelParameters(kParameters)
352 
353  self.log.info("Psf-match science exposure to reference")
354  psfMatchedExposure = afwImage.ExposureF(exposure.getBBox(), exposure.getWcs())
355  psfMatchedExposure.setFilter(exposure.getFilter())
356  psfMatchedExposure.setPhotoCalib(exposure.getPhotoCalib())
357  psfMatchedExposure.getInfo().setVisitInfo(exposure.getInfo().getVisitInfo())
358  psfMatchedExposure.setPsf(referencePsfModel)
359  psfMatchedMaskedImage = psfMatchedExposure.getMaskedImage()
360 
361  # Normalize the psf-matching kernel while convolving since its magnitude is meaningless
362  # when PSF-matching one model to another.
363  doNormalize = True
364  afwMath.convolve(psfMatchedMaskedImage, maskedImage, psfMatchingKernel, doNormalize)
365 
366  self.log.info("done")
367  return pipeBase.Struct(psfMatchedExposure=psfMatchedExposure,
368  psfMatchingKernel=psfMatchingKernel,
369  kernelCellSet=kernelCellSet,
370  metadata=self.metadata,
371  )
372 
373  def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
374  """Print diagnostic information on spatial kernel and background fit
375 
376  The debugging diagnostics are not really useful here, since the images we are matching have
377  no variance. Thus override the _diagnostic method to generate no logging information"""
378  return
379 
380  def _buildCellSet(self, exposure, referencePsfModel):
381  """Build a SpatialCellSet for use with the solve method
382 
383  Parameters
384  ----------
385  exposure : `lsst.afw.image.Exposure`
386  The science exposure that will be convolved; must contain a Psf
387  referencePsfModel : `lsst.afw.detection.Psf`
388  Psf model to match to
389 
390  Returns
391  -------
392  result : `struct`
393  - ``kernelCellSet`` : a SpatialCellSet to be used by self._solve
394  - ``referencePsfModel`` : Validated and/or modified
395  reference model used to populate the SpatialCellSet
396 
397  Notes
398  -----
399  If the reference Psf model and science Psf model have different dimensions,
400  adjust the referencePsfModel (the model to which the exposure PSF will be matched)
401  to match that of the science Psf. If the science Psf dimensions vary across the image,
402  as is common with a WarpedPsf, either pad or clip (depending on config.padPsf)
403  the dimensions to be constant.
404  """
405  sizeCellX = self.kConfig.sizeCellX
406  sizeCellY = self.kConfig.sizeCellY
407 
408  scienceBBox = exposure.getBBox()
409  # Extend for proper spatial matching kernel all the way to edge, especially for narrow strips
410  scienceBBox.grow(geom.Extent2I(sizeCellX, sizeCellY))
411 
412  sciencePsfModel = exposure.getPsf()
413 
414  dimenR = referencePsfModel.getLocalKernel().getDimensions()
415  psfWidth, psfHeight = dimenR
416 
417  regionSizeX, regionSizeY = scienceBBox.getDimensions()
418  scienceX0, scienceY0 = scienceBBox.getMin()
419 
420  kernelCellSet = afwMath.SpatialCellSet(geom.Box2I(scienceBBox), sizeCellX, sizeCellY)
421 
422  nCellX = regionSizeX//sizeCellX
423  nCellY = regionSizeY//sizeCellY
424 
425  if nCellX == 0 or nCellY == 0:
426  raise ValueError("Exposure dimensions=%s and sizeCell=(%s, %s). Insufficient area to match" %
427  (scienceBBox.getDimensions(), sizeCellX, sizeCellY))
428 
429  # Survey the PSF dimensions of the Spatial Cell Set
430  # to identify the minimum enclosed or maximum bounding square BBox.
431  widthList = []
432  heightList = []
433  for row in range(nCellY):
434  posY = sizeCellY*row + sizeCellY//2 + scienceY0
435  for col in range(nCellX):
436  posX = sizeCellX*col + sizeCellX//2 + scienceX0
437  widthS, heightS = sciencePsfModel.computeBBox(geom.Point2D(posX, posY)).getDimensions()
438  widthList.append(widthS)
439  heightList.append(heightS)
440 
441  psfSize = max(max(heightList), max(widthList))
442 
443  if self.config.doAutoPadPsf:
444  minPsfSize = nextOddInteger(self.kConfig.kernelSize*self.config.autoPadPsfTo)
445  paddingPix = max(0, minPsfSize - psfSize)
446  else:
447  if self.config.padPsfBy % 2 != 0:
448  raise ValueError("Config padPsfBy (%i pixels) must be even number." %
449  self.config.padPsfBy)
450  paddingPix = self.config.padPsfBy
451 
452  if paddingPix > 0:
453  self.log.info("Padding Science PSF from (%s, %s) to (%s, %s) pixels" %
454  (psfSize, psfSize, paddingPix + psfSize, paddingPix + psfSize))
455  psfSize += paddingPix
456 
457  # Check that PSF is larger than the matching kernel
458  maxKernelSize = psfSize - 1
459  if maxKernelSize % 2 == 0:
460  maxKernelSize -= 1
461  if self.kConfig.kernelSize > maxKernelSize:
462  message = """
463  Kernel size (%d) too big to match Psfs of size %d.
464  Please reconfigure by setting one of the following:
465  1) kernel size to <= %d
466  2) doAutoPadPsf=True
467  3) padPsfBy to >= %s
468  """ % (self.kConfig.kernelSize, psfSize,
469  maxKernelSize, self.kConfig.kernelSize - maxKernelSize)
470  raise ValueError(message)
471 
472  dimenS = geom.Extent2I(psfSize, psfSize)
473 
474  if (dimenR != dimenS):
475  try:
476  referencePsfModel = referencePsfModel.resized(psfSize, psfSize)
477  self.log.info("Adjusted dimensions of reference PSF model from %s to %s" % (dimenR, dimenS))
478  except Exception as e:
479  self.log.warn("Zero padding or clipping the reference PSF model of type %s and dimensions %s"
480  " to the science Psf dimensions %s because: %s",
481  referencePsfModel.__class__.__name__, dimenR, dimenS, e)
482  dimenR = dimenS
483 
484  ps = pexConfig.makePropertySet(self.kConfig)
485  for row in range(nCellY):
486  # place at center of cell
487  posY = sizeCellY*row + sizeCellY//2 + scienceY0
488 
489  for col in range(nCellX):
490  # place at center of cell
491  posX = sizeCellX*col + sizeCellX//2 + scienceX0
492 
493  log.log("TRACE4." + self.log.getName(), log.DEBUG,
494  "Creating Psf candidate at %.1f %.1f", posX, posY)
495 
496  # reference kernel image, at location of science subimage
497  referenceMI = self._makePsfMaskedImage(referencePsfModel, posX, posY, dimensions=dimenR)
498 
499  # kernel image we are going to convolve
500  scienceMI = self._makePsfMaskedImage(sciencePsfModel, posX, posY, dimensions=dimenR)
501 
502  # The image to convolve is the science image, to the reference Psf.
503  kc = diffimLib.makeKernelCandidate(posX, posY, scienceMI, referenceMI, ps)
504  kernelCellSet.insertCandidate(kc)
505 
506  import lsstDebug
507  display = lsstDebug.Info(__name__).display
508  displaySpatialCells = lsstDebug.Info(__name__).displaySpatialCells
509  maskTransparency = lsstDebug.Info(__name__).maskTransparency
510  if not maskTransparency:
511  maskTransparency = 0
512  if display:
513  afwDisplay.setDefaultMaskTransparency(maskTransparency)
514  if display and displaySpatialCells:
515  dituils.showKernelSpatialCells(exposure.getMaskedImage(), kernelCellSet,
516  symb="o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
517  ctypeBad=afwDisplay.RED, size=4, frame=lsstDebug.frame,
518  title="Image to be convolved")
519  lsstDebug.frame += 1
520  return pipeBase.Struct(kernelCellSet=kernelCellSet,
521  referencePsfModel=referencePsfModel,
522  )
523 
524  def _makePsfMaskedImage(self, psfModel, posX, posY, dimensions=None):
525  """Return a MaskedImage of the a PSF Model of specified dimensions
526  """
527  rawKernel = psfModel.computeKernelImage(geom.Point2D(posX, posY)).convertF()
528  if dimensions is None:
529  dimensions = rawKernel.getDimensions()
530  if rawKernel.getDimensions() == dimensions:
531  kernelIm = rawKernel
532  else:
533  # make image of proper size
534  kernelIm = afwImage.ImageF(dimensions)
535  bboxToPlace = geom.Box2I(geom.Point2I((dimensions.getX() - rawKernel.getWidth())//2,
536  (dimensions.getY() - rawKernel.getHeight())//2),
537  rawKernel.getDimensions())
538  kernelIm.assign(rawKernel, bboxToPlace)
539 
540  kernelMask = afwImage.Mask(dimensions, 0x0)
541  kernelVar = afwImage.ImageF(dimensions, 1.0)
542  return afwImage.MaskedImageF(kernelIm, kernelMask, kernelVar)
def makeKernelBasisList(config, targetFwhmPix=None, referenceFwhmPix=None, basisDegGauss=None, metadata=None)
def _buildCellSet(self, exposure, referencePsfModel)
Definition: Log.h:706
A collection of SpatialCells covering an entire image.
Definition: SpatialCell.h:387
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:77
def _solve(self, kernelCellSet, basisList, returnOnExcept=False)
Definition: psfMatch.py:880
int max
def run(self, exposure, referencePsfModel, kernelSum=1.0)
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
An integer coordinate rectangle.
Definition: Box.h:55