LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
modelPsfMatch.py
Go to the documentation of this file.
1 # LSST Data Management System
2 # Copyright 2008-2016 LSST Corporation.
3 #
4 # This product includes software developed by the
5 # LSST Project (http://www.lsst.org/).
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the LSST License Statement and
18 # the GNU General Public License along with this program. If not,
19 # see <http://www.lsstcorp.org/LegalNotices/>.
20 #
21 
22 import numpy as np
23 
24 from . import diffimLib
25 import lsst.afw.geom as afwGeom
26 import lsst.afw.image as afwImage
27 import lsst.afw.math as afwMath
28 import lsst.log as log
29 import lsst.pex.config as pexConfig
30 import lsst.pipe.base as pipeBase
31 from .makeKernelBasisList import makeKernelBasisList
32 from .psfMatch import PsfMatchTask, PsfMatchConfigAL
33 from . import utils as dituils
34 import lsst.afw.display.ds9 as ds9
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 # ds9 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 # ds9 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  Display the two images if –debug:
250 
251  .. code-block :: none
252 
253  if args.debug:
254  ds9.mtv(templateExp, frame=1, title="Example script: Input Template")
255  ds9.mtv(scienceExp, frame=2, title="Example script: Input Science Image")
256 
257  Create and run the Task:
258 
259  .. code-block :: none
260 
261  # Create the Task
262  psfMatchTask = MyModelPsfMatchTask(config=config)
263  # Run the Task
264  result = psfMatchTask.run(templateExp, scienceExp)
265 
266  And finally provide optional debugging display of the Psf-matched (via the Psf models) science image:
267 
268  .. code-block :: none
269 
270  if args.debug:
271  # See if the LSST debug has incremented the frame number; if not start with frame 3
272  try:
273  frame = debug.lsstDebug.frame + 1
274  except Exception:
275  frame = 3
276  ds9.mtv(result.psfMatchedExposure, frame=frame, title="Example script: Matched Science Image")
277 
278  """
279  ConfigClass = ModelPsfMatchConfig
280 
281  def __init__(self, *args, **kwargs):
282  """Create a ModelPsfMatchTask
283 
284  Parameters
285  ----------
286  *args
287  arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__
288  **kwargs
289  keyword arguments to be passed to lsst.ip.diffim.PsfMatchTask.__init__
290 
291  Notes
292  -----
293  Upon initialization, the kernel configuration is defined by self.config.kernel.active. This Task
294  does have a run() method, which is the default way to call the Task.
295  """
296  PsfMatchTask.__init__(self, *args, **kwargs)
297  self.kConfig = self.config.kernel.active
298 
299  @pipeBase.timeMethod
300  def run(self, exposure, referencePsfModel, kernelSum=1.0):
301  """Psf-match an exposure to a model Psf
302 
303  Parameters
304  ----------
305  exposure : `lsst.afw.image.Exposure`
306  Exposure to Psf-match to the reference Psf model;
307  it must return a valid PSF model via exposure.getPsf()
308  referencePsfModel : `lsst.afw.detection.Psf`
309  The Psf model to match to
310  kernelSum : `float`, optional
311  A multipicative factor to apply to the kernel sum (default=1.0)
312 
313  Returns
314  -------
315  result : `struct`
316  - ``psfMatchedExposure`` : the Psf-matched Exposure.
317  This has the same parent bbox, Wcs, Calib and
318  Filter as the input Exposure but no Psf.
319  In theory the Psf should equal referencePsfModel but
320  the match is likely not exact.
321  - ``psfMatchingKernel`` : the spatially varying Psf-matching kernel
322  - ``kernelCellSet`` : SpatialCellSet used to solve for the Psf-matching kernel
323  - ``referencePsfModel`` : Validated and/or modified reference model used
324 
325  Raises
326  ------
327  RuntimeError
328  if the Exposure does not contain a Psf model
329  """
330  if not exposure.hasPsf():
331  raise RuntimeError("exposure does not contain a Psf model")
332 
333  maskedImage = exposure.getMaskedImage()
334 
335  self.log.info("compute Psf-matching kernel")
336  result = self._buildCellSet(exposure, referencePsfModel)
337  kernelCellSet = result.kernelCellSet
338  referencePsfModel = result.referencePsfModel
339  fwhmScience = exposure.getPsf().computeShape().getDeterminantRadius() * sigma2fwhm
340  fwhmModel = referencePsfModel.computeShape().getDeterminantRadius() * sigma2fwhm
341 
342  basisList = makeKernelBasisList(self.kConfig, fwhmScience, fwhmModel, metadata=self.metadata)
343  spatialSolution, psfMatchingKernel, backgroundModel = self._solve(kernelCellSet, basisList)
344 
345  if psfMatchingKernel.isSpatiallyVarying():
346  sParameters = np.array(psfMatchingKernel.getSpatialParameters())
347  sParameters[0][0] = kernelSum
348  psfMatchingKernel.setSpatialParameters(sParameters)
349  else:
350  kParameters = np.array(psfMatchingKernel.getKernelParameters())
351  kParameters[0] = kernelSum
352  psfMatchingKernel.setKernelParameters(kParameters)
353 
354  self.log.info("Psf-match science exposure to reference")
355  psfMatchedExposure = afwImage.ExposureF(exposure.getBBox(), exposure.getWcs())
356  psfMatchedExposure.setFilter(exposure.getFilter())
357  psfMatchedExposure.setCalib(exposure.getCalib())
358  psfMatchedExposure.getInfo().setVisitInfo(exposure.getInfo().getVisitInfo())
359  psfMatchedExposure.setPsf(referencePsfModel)
360  psfMatchedMaskedImage = psfMatchedExposure.getMaskedImage()
361 
362  # Normalize the psf-matching kernel while convolving since its magnitude is meaningless
363  # when PSF-matching one model to another.
364  doNormalize = True
365  afwMath.convolve(psfMatchedMaskedImage, maskedImage, psfMatchingKernel, doNormalize)
366 
367  self.log.info("done")
368  return pipeBase.Struct(psfMatchedExposure=psfMatchedExposure,
369  psfMatchingKernel=psfMatchingKernel,
370  kernelCellSet=kernelCellSet,
371  metadata=self.metadata,
372  )
373 
374  def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
375  """Print diagnostic information on spatial kernel and background fit
376 
377  The debugging diagnostics are not really useful here, since the images we are matching have
378  no variance. Thus override the _diagnostic method to generate no logging information"""
379  return
380 
381  def _buildCellSet(self, exposure, referencePsfModel):
382  """Build a SpatialCellSet for use with the solve method
383 
384  Parameters
385  ----------
386  exposure : `lsst.afw.image.Exposure`
387  The science exposure that will be convolved; must contain a Psf
388  referencePsfModel : `lsst.afw.detection.Psf`
389  Psf model to match to
390 
391  Returns
392  -------
393  result : `struct`
394  - ``kernelCellSet`` : a SpatialCellSet to be used by self._solve
395  - ``referencePsfModel`` : Validated and/or modified
396  reference model used to populate the SpatialCellSet
397 
398  Notes
399  -----
400  If the reference Psf model and science Psf model have different dimensions,
401  adjust the referencePsfModel (the model to which the exposure PSF will be matched)
402  to match that of the science Psf. If the science Psf dimensions vary across the image,
403  as is common with a WarpedPsf, either pad or clip (depending on config.padPsf)
404  the dimensions to be constant.
405  """
406  sizeCellX = self.kConfig.sizeCellX
407  sizeCellY = self.kConfig.sizeCellY
408 
409  scienceBBox = exposure.getBBox()
410  # Extend for proper spatial matching kernel all the way to edge, especially for narrow strips
411  scienceBBox.grow(afwGeom.Extent2I(sizeCellX, sizeCellY))
412 
413  sciencePsfModel = exposure.getPsf()
414 
415  dimenR = referencePsfModel.getLocalKernel().getDimensions()
416  psfWidth, psfHeight = dimenR
417 
418  regionSizeX, regionSizeY = scienceBBox.getDimensions()
419  scienceX0, scienceY0 = scienceBBox.getMin()
420 
421  kernelCellSet = afwMath.SpatialCellSet(afwGeom.Box2I(scienceBBox), sizeCellX, sizeCellY)
422 
423  nCellX = regionSizeX//sizeCellX
424  nCellY = regionSizeY//sizeCellY
425 
426  if nCellX == 0 or nCellY == 0:
427  raise ValueError("Exposure dimensions=%s and sizeCell=(%s, %s). Insufficient area to match" %
428  (scienceBBox.getDimensions(), sizeCellX, sizeCellY))
429 
430  # Survey the PSF dimensions of the Spatial Cell Set
431  # to identify the minimum enclosed or maximum bounding square BBox.
432  widthList = []
433  heightList = []
434  for row in range(nCellY):
435  posY = sizeCellY*row + sizeCellY//2 + scienceY0
436  for col in range(nCellX):
437  posX = sizeCellX*col + sizeCellX//2 + scienceX0
438  widthS, heightS = sciencePsfModel.computeBBox(afwGeom.Point2D(posX, posY)).getDimensions()
439  widthList.append(widthS)
440  heightList.append(heightS)
441 
442  psfSize = max(max(heightList), max(widthList))
443 
444  if self.config.doAutoPadPsf:
445  minPsfSize = nextOddInteger(self.kConfig.kernelSize*self.config.autoPadPsfTo)
446  paddingPix = max(0, minPsfSize - psfSize)
447  else:
448  if self.config.padPsfBy % 2 != 0:
449  raise ValueError("Config padPsfBy (%i pixels) must be even number." %
450  self.config.padPsfBy)
451  paddingPix = self.config.padPsfBy
452 
453  if paddingPix > 0:
454  self.log.info("Padding Science PSF from (%s, %s) to (%s, %s) pixels" %
455  (psfSize, psfSize, paddingPix + psfSize, paddingPix + psfSize))
456  psfSize += paddingPix
457 
458  # Check that PSF is larger than the matching kernel
459  maxKernelSize = psfSize - 1
460  if maxKernelSize % 2 == 0:
461  maxKernelSize -= 1
462  if self.kConfig.kernelSize > maxKernelSize:
463  message = """
464  Kernel size (%d) too big to match Psfs of size %d.
465  Please reconfigure by setting one of the following:
466  1) kernel size to <= %d
467  2) doAutoPadPsf=True
468  3) padPsfBy to >= %s
469  """ % (self.kConfig.kernelSize, psfSize,
470  maxKernelSize, self.kConfig.kernelSize - maxKernelSize)
471  raise ValueError(message)
472 
473  dimenS = afwGeom.Extent2I(psfSize, psfSize)
474 
475  if (dimenR != dimenS):
476  try:
477  referencePsfModel = referencePsfModel.resized(psfSize, psfSize)
478  self.log.info("Adjusted dimensions of reference PSF model from %s to %s" % (dimenR, dimenS))
479  except Exception as e:
480  self.log.warn("Zero padding or clipping the reference PSF model of type %s and dimensions %s"
481  " to the science Psf dimensions %s because: %s",
482  referencePsfModel.__class__.__name__, dimenR, dimenS, e)
483  dimenR = dimenS
484 
485  policy = pexConfig.makePolicy(self.kConfig)
486  for row in range(nCellY):
487  # place at center of cell
488  posY = sizeCellY * row + sizeCellY//2 + scienceY0
489 
490  for col in range(nCellX):
491  # place at center of cell
492  posX = sizeCellX * col + sizeCellX//2 + scienceX0
493 
494  log.log("TRACE4." + self.log.getName(), log.DEBUG,
495  "Creating Psf candidate at %.1f %.1f", posX, posY)
496 
497  # reference kernel image, at location of science subimage
498  referenceMI = self._makePsfMaskedImage(referencePsfModel, posX, posY, dimensions=dimenR)
499 
500  # kernel image we are going to convolve
501  scienceMI = self._makePsfMaskedImage(sciencePsfModel, posX, posY, dimensions=dimenR)
502 
503  # The image to convolve is the science image, to the reference Psf.
504  kc = diffimLib.makeKernelCandidate(posX, posY, scienceMI, referenceMI, policy)
505  kernelCellSet.insertCandidate(kc)
506 
507  import lsstDebug
508  display = lsstDebug.Info(__name__).display
509  displaySpatialCells = lsstDebug.Info(__name__).displaySpatialCells
510  maskTransparency = lsstDebug.Info(__name__).maskTransparency
511  if not maskTransparency:
512  maskTransparency = 0
513  if display:
514  ds9.setMaskTransparency(maskTransparency)
515  if display and displaySpatialCells:
516  dituils.showKernelSpatialCells(exposure.getMaskedImage(), kernelCellSet,
517  symb="o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW, ctypeBad=ds9.RED,
518  size=4, frame=lsstDebug.frame, 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(afwGeom.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 = afwGeom.Box2I(afwGeom.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:691
A collection of SpatialCells covering an entire image.
Definition: SpatialCell.h:387
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:78
def _solve(self, kernelCellSet, basisList, returnOnExcept=False)
Definition: psfMatch.py:869
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.
An integer coordinate rectangle.
Definition: Box.h:54