LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
psfMatch.py
Go to the documentation of this file.
1 #from builtins import str
2 from builtins import range
3 #
4 # LSST Data Management System
5 # Copyright 2008-2016 LSST Corporation.
6 #
7 # This product includes software developed by the
8 # LSST Project (http://www.lsst.org/).
9 #
10 # This program is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation, either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the LSST License Statement and
21 # the GNU General Public License along with this program. If not,
22 # see <http://www.lsstcorp.org/LegalNotices/>.
23 #
24 import time
25 import numpy as num
26 import lsst.afw.image as afwImage
27 import lsst.pex.config as pexConfig
28 import lsst.afw.math as afwMath
29 import lsst.afw.display.ds9 as ds9
30 import lsst.log as log
31 import lsst.pipe.base as pipeBase
32 from lsst.meas.algorithms import SubtractBackgroundConfig
33 from . import utils as diUtils
34 from . import diffimLib
35 
36 
37 class DetectionConfig(pexConfig.Config):
38  """!Configuration for detecting sources on images for building a PSF-matching kernel
39 
40  Configuration for turning detected lsst.afw.detection.FootPrints into an acceptable
41  (unmasked, high signal-to-noise, not too large or not too small) list of
42  lsst.ip.diffim.KernelSources that are used to build the Psf-matching kernel"""
43 
44  detThreshold = pexConfig.Field(
45  dtype=float,
46  doc="Value of footprint detection threshold",
47  default=10.0,
48  check=lambda x: x >= 3.0
49  )
50  detThresholdType = pexConfig.ChoiceField(
51  dtype=str,
52  doc="Type of detection threshold",
53  default="pixel_stdev",
54  allowed={
55  "value": "Use counts as the detection threshold type",
56  "stdev": "Use standard deviation of image plane",
57  "variance": "Use variance of image plane",
58  "pixel_stdev": "Use stdev derived from variance plane"
59  }
60  )
61  detOnTemplate = pexConfig.Field(
62  dtype=bool,
63  doc="""If true run detection on the template (image to convolve);
64  if false run detection on the science image""",
65  default=True
66  )
67  badMaskPlanes = pexConfig.ListField(
68  dtype=str,
69  doc="""Mask planes that lead to an invalid detection.
70  Options: NO_DATA EDGE SAT BAD CR INTRP""",
71  default=("NO_DATA", "EDGE", "SAT")
72  )
73  fpNpixMin = pexConfig.Field(
74  dtype=int,
75  doc="Minimum number of pixels in an acceptable Footprint",
76  default=5,
77  check=lambda x: x >= 5
78  )
79  fpNpixMax = pexConfig.Field(
80  dtype=int,
81  doc="""Maximum number of pixels in an acceptable Footprint;
82  too big and the subsequent convolutions become unwieldy""",
83  default=500,
84  check=lambda x: x <= 500
85  )
86  fpGrowKernelScaling = pexConfig.Field(
87  dtype=float,
88  doc="""If config.scaleByFwhm, grow the footprint based on
89  the final kernelSize. Each footprint will be
90  2*fpGrowKernelScaling*kernelSize x
91  2*fpGrowKernelScaling*kernelSize. With the value
92  of 1.0, the remaining pixels in each KernelCandiate
93  after convolution by the basis functions will be
94  equal to the kernel size itself.""",
95  default=1.0,
96  check=lambda x: x >= 1.0
97  )
98  fpGrowPix = pexConfig.Field(
99  dtype=int,
100  doc="""Growing radius (in pixels) for each raw detection
101  footprint. The smaller the faster; however the
102  kernel sum does not converge if the stamp is too
103  small; and the kernel is not constrained at all if
104  the stamp is the size of the kernel. The grown stamp
105  is 2 * fpGrowPix pixels larger in each dimension.
106  This is overridden by fpGrowKernelScaling if scaleByFwhm""",
107  default=30,
108  check=lambda x: x >= 10
109  )
110  scaleByFwhm = pexConfig.Field(
111  dtype=bool,
112  doc="Scale fpGrowPix by input Fwhm?",
113  default=True,
114  )
115 
116 
117 class PsfMatchConfig(pexConfig.Config):
118  """!Base configuration for Psf-matching
119 
120  The base configuration of the Psf-matching kernel, and of the warping, detection,
121  and background modeling subTasks."""
122 
123  warpingConfig = pexConfig.ConfigField("Config for warping exposures to a common alignment",
124  afwMath.warper.WarperConfig)
125  detectionConfig = pexConfig.ConfigField("Controlling the detection of sources for kernel building",
126  DetectionConfig)
127  afwBackgroundConfig = pexConfig.ConfigField("Controlling the Afw background fitting",
128  SubtractBackgroundConfig)
129 
130  useAfwBackground = pexConfig.Field(
131  dtype=bool,
132  doc="Use afw background subtraction instead of ip_diffim",
133  default=False,
134  )
135  fitForBackground = pexConfig.Field(
136  dtype=bool,
137  doc="Include terms (including kernel cross terms) for background in ip_diffim",
138  default=False,
139  )
140  kernelBasisSet = pexConfig.ChoiceField(
141  dtype=str,
142  doc="Type of basis set for PSF matching kernel.",
143  default="alard-lupton",
144  allowed={
145  "alard-lupton" : """Alard-Lupton sum-of-gaussians basis set,
146  * The first term has no spatial variation
147  * The kernel sum is conserved
148  * You may want to turn off 'usePcaForSpatialKernel'""",
149  "delta-function" : """Delta-function kernel basis set,
150  * You may enable the option useRegularization
151  * You should seriously consider usePcaForSpatialKernel, which will also
152  enable kernel sum conservation for the delta function kernels"""
153  }
154  )
155  kernelSize = pexConfig.Field(
156  dtype=int,
157  doc="""Number of rows/columns in the convolution kernel; should be odd-valued.
158  Modified by kernelSizeFwhmScaling if scaleByFwhm = true""",
159  default=21,
160  )
161  scaleByFwhm = pexConfig.Field(
162  dtype=bool,
163  doc="Scale kernelSize, alardGaussians by input Fwhm",
164  default=True,
165  )
166  kernelSizeFwhmScaling = pexConfig.Field(
167  dtype=float,
168  doc="""How much to scale the kernel size based on the largest AL Sigma""",
169  default=6.0,
170  check=lambda x: x >= 1.0
171  )
172  kernelSizeMin = pexConfig.Field(
173  dtype=int,
174  doc="""Minimum Kernel Size""",
175  default=21,
176  )
177  kernelSizeMax = pexConfig.Field(
178  dtype=int,
179  doc="""Maximum Kernel Size""",
180  default=35,
181  )
182  spatialModelType = pexConfig.ChoiceField(
183  dtype=str,
184  doc="Type of spatial functions for kernel and background",
185  default="chebyshev1",
186  allowed={
187  "chebyshev1": "Chebyshev polynomial of the first kind",
188  "polynomial": "Standard x,y polynomial",
189  }
190  )
191  spatialKernelOrder = pexConfig.Field(
192  dtype=int,
193  doc="Spatial order of convolution kernel variation",
194  default=2,
195  check=lambda x: x >= 0
196  )
197  spatialBgOrder = pexConfig.Field(
198  dtype=int,
199  doc="Spatial order of differential background variation",
200  default=1,
201  check=lambda x: x >= 0
202  )
203  sizeCellX = pexConfig.Field(
204  dtype=int,
205  doc="Size (rows) in pixels of each SpatialCell for spatial modeling",
206  default=128,
207  check=lambda x: x >= 32
208  )
209  sizeCellY = pexConfig.Field(
210  dtype=int,
211  doc="Size (columns) in pixels of each SpatialCell for spatial modeling",
212  default=128,
213  check=lambda x: x >= 32
214  )
215  nStarPerCell = pexConfig.Field(
216  dtype=int,
217  doc="Number of KernelCandidates in each SpatialCell to use in the spatial fitting",
218  default=3,
219  check=lambda x: x >= 1
220  )
221  maxSpatialIterations = pexConfig.Field(
222  dtype=int,
223  doc="Maximum number of iterations for rejecting bad KernelCandidates in spatial fitting",
224  default=3,
225  check=lambda x: x >= 1 and x <= 5
226  )
227  usePcaForSpatialKernel = pexConfig.Field(
228  dtype=bool,
229  doc="""Use Pca to reduce the dimensionality of the kernel basis sets.
230  This is particularly useful for delta-function kernels.
231  Functionally, after all Cells have their raw kernels determined, we run
232  a Pca on these Kernels, re-fit the Cells using the eigenKernels and then
233  fit those for spatial variation using the same technique as for Alard-Lupton kernels.
234  If this option is used, the first term will have no spatial variation and the
235  kernel sum will be conserved.""",
236  default=False,
237  )
238  subtractMeanForPca = pexConfig.Field(
239  dtype=bool,
240  doc="Subtract off the mean feature before doing the Pca",
241  default=True,
242  )
243  numPrincipalComponents = pexConfig.Field(
244  dtype=int,
245  doc="""Number of principal components to use for Pca basis, including the
246  mean kernel if requested.""",
247  default=5,
248  check=lambda x: x >= 3
249  )
250  singleKernelClipping = pexConfig.Field(
251  dtype=bool,
252  doc="Do sigma clipping on each raw kernel candidate",
253  default=True,
254  )
255  kernelSumClipping = pexConfig.Field(
256  dtype=bool,
257  doc="Do sigma clipping on the ensemble of kernel sums",
258  default=True,
259  )
260  spatialKernelClipping = pexConfig.Field(
261  dtype=bool,
262  doc="Do sigma clipping after building the spatial model",
263  default=True,
264  )
265  checkConditionNumber = pexConfig.Field(
266  dtype=bool,
267  doc="""Test for maximum condition number when inverting a kernel matrix.
268  Anything above maxConditionNumber is not used and the candidate is set as BAD.
269  Also used to truncate inverse matrix in estimateBiasedRisk. However,
270  if you are doing any deconvolution you will want to turn this off, or use
271  a large maxConditionNumber""",
272  default=False,
273  )
274  badMaskPlanes = pexConfig.ListField(
275  dtype=str,
276  doc="""Mask planes to ignore when calculating diffim statistics
277  Options: NO_DATA EDGE SAT BAD CR INTRP""",
278  default=("NO_DATA", "EDGE", "SAT")
279  )
280  candidateResidualMeanMax = pexConfig.Field(
281  dtype=float,
282  doc="""Rejects KernelCandidates yielding bad difference image quality.
283  Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
284  Represents average over pixels of (image/sqrt(variance)).""",
285  default=0.25,
286  check=lambda x: x >= 0.0
287  )
288  candidateResidualStdMax = pexConfig.Field(
289  dtype=float,
290  doc="""Rejects KernelCandidates yielding bad difference image quality.
291  Used by BuildSingleKernelVisitor, AssessSpatialKernelVisitor.
292  Represents stddev over pixels of (image/sqrt(variance)).""",
293  default=1.50,
294  check=lambda x: x >= 0.0
295  )
296  useCoreStats = pexConfig.Field(
297  dtype=bool,
298  doc="""Use the core of the footprint for the quality statistics, instead of the entire footprint.
299  WARNING: if there is deconvolution we probably will need to turn this off""",
300  default=False,
301  )
302  candidateCoreRadius = pexConfig.Field(
303  dtype=int,
304  doc="""Radius for calculation of stats in 'core' of KernelCandidate diffim.
305  Total number of pixels used will be (2*radius)**2.
306  This is used both for 'core' diffim quality as well as ranking of
307  KernelCandidates by their total flux in this core""",
308  default=3,
309  check=lambda x: x >= 1
310  )
311  maxKsumSigma = pexConfig.Field(
312  dtype=float,
313  doc="""Maximum allowed sigma for outliers from kernel sum distribution.
314  Used to reject variable objects from the kernel model""",
315  default=3.0,
316  check=lambda x: x >= 0.0
317  )
318  maxConditionNumber = pexConfig.Field(
319  dtype=float,
320  doc="Maximum condition number for a well conditioned matrix",
321  default=5.0e7,
322  check=lambda x: x >= 0.0
323  )
324  conditionNumberType = pexConfig.ChoiceField(
325  dtype=str,
326  doc="Use singular values (SVD) or eigen values (EIGENVALUE) to determine condition number",
327  default="EIGENVALUE",
328  allowed={
329  "SVD": "Use singular values",
330  "EIGENVALUE": "Use eigen values (faster)",
331  }
332  )
333  maxSpatialConditionNumber = pexConfig.Field(
334  dtype=float,
335  doc="Maximum condition number for a well conditioned spatial matrix",
336  default=1.0e10,
337  check=lambda x: x >= 0.0
338  )
339  iterateSingleKernel = pexConfig.Field(
340  dtype=bool,
341  doc="""Remake KernelCandidate using better variance estimate after first pass?
342  Primarily useful when convolving a single-depth image, otherwise not necessary.""",
343  default=False,
344  )
345  constantVarianceWeighting = pexConfig.Field(
346  dtype=bool,
347  doc="""Use constant variance weighting in single kernel fitting?
348  In some cases this is better for bright star residuals.""",
349  default=True,
350  )
351  calculateKernelUncertainty = pexConfig.Field(
352  dtype=bool,
353  doc="""Calculate kernel and background uncertainties for each kernel candidate?
354  This comes from the inverse of the covariance matrix.
355  Warning: regularization can cause problems for this step.""",
356  default=False,
357  )
358  useBicForKernelBasis = pexConfig.Field(
359  dtype=bool,
360  doc="""Use Bayesian Information Criterion to select the number of bases going into the kernel""",
361  default=False,
362  )
363 
364 
366  """!The parameters specific to the "Alard-Lupton" (sum-of-Gaussian) Psf-matching basis"""
367 
368  def setDefaults(self):
369  PsfMatchConfig.setDefaults(self)
370  self.kernelBasisSet = "alard-lupton"
371  self.maxConditionNumber = 5.0e7
372 
373  alardNGauss = pexConfig.Field(
374  dtype=int,
375  doc="Number of Gaussians in alard-lupton basis",
376  default=3,
377  check=lambda x: x >= 1
378  )
379  alardDegGauss = pexConfig.ListField(
380  dtype=int,
381  doc="Polynomial order of spatial modification of Gaussians. Must in number equal alardNGauss",
382  default=(4, 2, 2),
383  )
384  alardSigGauss = pexConfig.ListField(
385  dtype=float,
386  doc="""Sigma in pixels of Gaussians (FWHM = 2.35 sigma). Must in number equal alardNGauss""",
387  default=(0.7, 1.5, 3.0),
388  )
389  alardGaussBeta = pexConfig.Field(
390  dtype=float,
391  doc="""Default scale factor between Gaussian sigmas """,
392  default=2.0,
393  check=lambda x: x >= 0.0,
394  )
395  alardMinSig = pexConfig.Field(
396  dtype=float,
397  doc="""Minimum Sigma (pixels) for Gaussians""",
398  default=0.7,
399  check=lambda x: x >= 0.25
400  )
401  alardDegGaussDeconv = pexConfig.Field(
402  dtype=int,
403  doc="""Degree of spatial modification of ALL gaussians in AL basis during deconvolution""",
404  default=3,
405  check=lambda x: x >= 1
406  )
407  alardMinSigDeconv = pexConfig.Field(
408  dtype=float,
409  doc="""Minimum Sigma (pixels) for Gaussians during deconvolution;
410  make smaller than alardMinSig as this is only indirectly used""",
411  default=0.4,
412  check=lambda x: x >= 0.25
413  )
414  alardNGaussDeconv = pexConfig.Field(
415  dtype=int,
416  doc="Number of Gaussians in AL basis during deconvolution",
417  default=3,
418  check=lambda x: x >= 1
419  )
420 
421 
423  """!The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis"""
424 
425  def setDefaults(self):
426  PsfMatchConfig.setDefaults(self)
427  self.kernelBasisSet = "delta-function"
428  self.maxConditionNumber = 5.0e6
430  self.subtractMeanForPca = True
431  self.useBicForKernelBasis = False
432 
433  useRegularization = pexConfig.Field(
434  dtype=bool,
435  doc="Use regularization to smooth the delta function kernels",
436  default=True,
437  )
438  regularizationType = pexConfig.ChoiceField(
439  dtype=str,
440  doc="Type of regularization.",
441  default="centralDifference",
442  allowed={
443  "centralDifference": "Penalize second derivative using 2-D stencil of central finite difference",
444  "forwardDifference": "Penalize first, second, third derivatives using forward finite differeces"
445  }
446  )
447  centralRegularizationStencil = pexConfig.ChoiceField(
448  dtype=int,
449  doc="Type of stencil to approximate central derivative (for centralDifference only)",
450  default=9,
451  allowed={
452  5: "5-point stencil including only adjacent-in-x,y elements",
453  9: "9-point stencil including diagonal elements"
454  }
455  )
456  forwardRegularizationOrders = pexConfig.ListField(
457  dtype=int,
458  doc="Array showing which order derivatives to penalize (for forwardDifference only)",
459  default=(1, 2),
460  itemCheck=lambda x: (x > 0) and (x < 4)
461  )
462  regularizationBorderPenalty = pexConfig.Field(
463  dtype=float,
464  doc="Value of the penalty for kernel border pixels",
465  default=3.0,
466  check=lambda x: x >= 0.0
467  )
468  lambdaType = pexConfig.ChoiceField(
469  dtype=str,
470  doc="How to choose the value of the regularization strength",
471  default="absolute",
472  allowed={
473  "absolute": "Use lambdaValue as the value of regularization strength",
474  "relative": "Use lambdaValue as fraction of the default regularization strength (N.R. 18.5.8)",
475  "minimizeBiasedRisk": "Minimize biased risk estimate",
476  "minimizeUnbiasedRisk": "Minimize unbiased risk estimate",
477  }
478  )
479  lambdaValue = pexConfig.Field(
480  dtype=float,
481  doc="Value used for absolute determinations of regularization strength",
482  default=0.2,
483  )
484  lambdaScaling = pexConfig.Field(
485  dtype=float,
486  doc="Fraction of the default lambda strength (N.R. 18.5.8) to use. 1e-4 or 1e-5",
487  default=1e-4,
488  )
489  lambdaStepType = pexConfig.ChoiceField(
490  dtype=str,
491  doc="""If a scan through lambda is needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
492  use log or linear steps""",
493  default="log",
494  allowed={
495  "log": "Step in log intervals; e.g. lambdaMin, lambdaMax, lambdaStep = -1.0, 2.0, 0.1",
496  "linear": "Step in linear intervals; e.g. lambdaMin, lambdaMax, lambdaStep = 0.1, 100, 0.1",
497  }
498  )
499  lambdaMin = pexConfig.Field(
500  dtype=float,
501  doc="""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
502  start at this value. If lambdaStepType = log:linear, suggest -1:0.1""",
503  default=-1.0,
504  )
505  lambdaMax = pexConfig.Field(
506  dtype=float,
507  doc="""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
508  stop at this value. If lambdaStepType = log:linear, suggest 2:100""",
509  default=2.0,
510  )
511  lambdaStep = pexConfig.Field(
512  dtype=float,
513  doc="""If scan through lambda needed (minimizeBiasedRisk, minimizeUnbiasedRisk),
514  step in these increments. If lambdaStepType = log:linear, suggest 0.1:0.1""",
515  default=0.1,
516  )
517 
518 
519 ## \addtogroup LSST_task_documentation
520 ## \{
521 ## \page PsfMatchTask
522 ## \ref PsfMatchTask_ "PsfMatchTask"
523 ## \copybrief PsfMatchTask
524 ## \}
525 
526 class PsfMatchTask(pipeBase.Task):
527  """!
528 
529 \anchor PsfMatchTask_
530 
531 \brief Base class for Psf Matching; should not be called directly
532 
533 \section ip_diffim_psfmatch_Contents Contents
534 
535  - \ref ip_diffim_psfmatch_Purpose
536  - \ref ip_diffim_psfmatch_Initialize
537  - \ref ip_diffim_psfmatch_IO
538  - \ref ip_diffim_psfmatch_Config
539  - \ref ip_diffim_psfmatch_Metadata
540  - \ref ip_diffim_psfmatch_Debug
541  - \ref ip_diffim_psfmatch_Example
542 
543 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
544 
545 \section ip_diffim_psfmatch_Purpose Description
546 
547 PsfMatchTask is a base class that implements the core functionality for matching the
548 Psfs of two images using a spatially varying Psf-matching lsst.afw.math.LinearCombinationKernel.
549 The Task requires the user to provide an instance of an lsst.afw.math.SpatialCellSet,
550 filled with lsst.ip.diffim.KernelCandidate instances, and an lsst.afw.math.KernelList
551 of basis shapes that will be used for the decomposition. If requested, the Task
552 also performs background matching and returns the differential background model as an
553 lsst.afw.math.Kernel.SpatialFunction.
554 
555 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
556 
557 \section ip_diffim_psfmatch_Initialize Task initialization
558 
559 \copydoc \_\_init\_\_
560 
561 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
562 
563 \section ip_diffim_psfmatch_IO Invoking the Task
564 
565 As a base class, this Task is not directly invoked. However, run() methods that are
566 implemented on derived classes will make use of the core _solve() functionality,
567 which defines a sequence of lsst.afw.math.CandidateVisitor classes that iterate
568 through the KernelCandidates, first building up a per-candidate solution and then
569 building up a spatial model from the ensemble of candidates. Sigma clipping is
570 performed using the mean and standard deviation of all kernel sums (to reject
571 variable objects), on the per-candidate substamp diffim residuals
572 (to indicate a bad choice of kernel basis shapes for that particular object),
573 and on the substamp diffim residuals using the spatial kernel fit (to indicate a bad
574 choice of spatial kernel order, or poor constraints on the spatial model). The
575 _diagnostic() method logs information on the quality of the spatial fit, and also
576 modifies the Task metadata.
577 
578 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
579 
580 \section ip_diffim_psfmatch_Config Configuration parameters
581 
582 See \ref PsfMatchConfig, \ref PsfMatchConfigAL, \ref PsfMatchConfigDF, and \ref DetectionConfig.
583 
584 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
585 
586 \section ip_diffim_psfmatch_Metadata Quantities set in Metadata
587 
588 
589 <DL>
590 <DT> spatialConditionNum <DD> Condition number of the spatial kernel fit;
591  via \link lsst.ip.diffim.PsfMatchTask._diagnostic PsfMatchTask._diagnostic \endlink </DD> </DT>
592 <DT> spatialKernelSum <DD> Kernel sum (10^{-0.4 * &Delta; zeropoint}) of the spatial Psf-matching kernel;
593  via \link lsst.ip.diffim.PsfMatchTask._diagnostic PsfMatchTask._diagnostic \endlink </DD> </DT>
594 
595 <DT> ALBasisNGauss <DD> If using sum-of-Gaussian basis, the number of gaussians used;
596  via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
597  generateAlardLuptonBasisList\endlink </DD> </DT>
598 <DT> ALBasisDegGauss <DD> If using sum-of-Gaussian basis, the degree of spatial variation of the Gaussians;
599  via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
600  generateAlardLuptonBasisList\endlink </DD> </DT>
601 <DT> ALBasisSigGauss <DD> If using sum-of-Gaussian basis, the widths (sigma) of the Gaussians;
602  via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
603  generateAlardLuptonBasisList\endlink </DD> </DT>
604 <DT> ALKernelSize <DD> If using sum-of-Gaussian basis, the kernel size;
605  via \link lsst.ip.diffim.makeKernelBasisList.generateAlardLuptonBasisList
606  generateAlardLuptonBasisList\endlink </DD> </DT>
607 
608 <DT> NFalsePositivesTotal <DD> Total number of diaSources;
609  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
610 <DT> NFalsePositivesRefAssociated <DD> Number of diaSources that associate with the reference catalog;
611  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
612 <DT> NFalsePositivesRefAssociated <DD> Number of diaSources that associate with the source catalog;
613  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
614 <DT> NFalsePositivesUnassociated <DD> Number of diaSources that are orphans;
615  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
616 <DT> metric_MEAN <DD> Mean value of substamp diffim quality metrics across all KernelCandidates,
617  for both the per-candidate (LOCAL) and SPATIAL residuals;
618  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
619 <DT> metric_MEDIAN <DD> Median value of substamp diffim quality metrics across all KernelCandidates,
620  for both the per-candidate (LOCAL) and SPATIAL residuals;
621  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
622 <DT> metric_STDEV <DD> Standard deviation of substamp diffim quality metrics across all KernelCandidates,
623  for both the per-candidate (LOCAL) and SPATIAL residuals;
624  via \link lsst.ip.diffim.KernelCandidateQa.aggregate KernelCandidateQa.aggregate\endlink </DD> </DT>
625 </DL>
626 
627 
628 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
629 
630 \section ip_diffim_psfmatch_Debug Debug variables
631 
632 
633 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
634 flag \c -d/--debug to import \b debug.py from your \c PYTHONPATH. The relevant contents of debug.py
635 for this Task include:
636 
637 \code{.py}
638  import sys
639  import lsstDebug
640  def DebugInfo(name):
641  di = lsstDebug.getInfo(name)
642  if name == "lsst.ip.diffim.psfMatch":
643  di.display = True # enable debug output
644  di.maskTransparency = 80 # ds9 mask transparency
645  di.displayCandidates = True # show all the candidates and residuals
646  di.displayKernelBasis = False # show kernel basis functions
647  di.displayKernelMosaic = True # show kernel realized across the image
648  di.plotKernelSpatialModel = False # show coefficients of spatial model
649  di.showBadCandidates = True # show the bad candidates (red) along with good (green)
650  return di
651  lsstDebug.Info = DebugInfo
652  lsstDebug.frame = 1
653 
654 \endcode
655 
656 Note that if you want addional logging info, you may add to your scripts:
657 \code{.py}
658 import lsst.log.utils as logUtils
659 logUtils.traceSetAt("ip.diffim", 4)
660 \endcode
661 
662 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
663 
664 \section ip_diffim_psfmatch_Example Example code
665 
666 As a base class, there is no example code for PsfMatchTask.
667 However, see \link lsst.ip.diffim.imagePsfMatch.ImagePsfMatchTask ImagePsfMatchTask\endlink,
668 \link lsst.ip.diffim.snapPsfMatch.SnapPsfMatchTask SnapPsfMatchTask\endlink, and
669 \link lsst.ip.diffim.modelPsfMatch.ModelPsfMatchTask ModelPsfMatchTask\endlink.
670 
671 #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
672 
673  """
674  ConfigClass = PsfMatchConfig
675  _DefaultName = "psfMatch"
676 
677  def __init__(self, *args, **kwargs):
678  """!Create the psf-matching Task
679 
680  \param *args arguments to be passed to lsst.pipe.base.task.Task.__init__
681  \param **kwargs keyword arguments to be passed to lsst.pipe.base.task.Task.__init__
682 
683  The initialization sets the Psf-matching kernel configuration using the value of
684  self.config.kernel.active. If the kernel is requested with regularization to moderate
685  the bias/variance tradeoff, currently only used when a delta function kernel basis
686  is provided, it creates a regularization matrix stored as member variable
687  self.hMat.
688  """
689  pipeBase.Task.__init__(self, *args, **kwargs)
690  self.kConfig = self.config.kernel.active
691 
692  #
693  if 'useRegularization' in self.kConfig:
694  self.useRegularization = self.kConfig.useRegularization
695  else:
696  self.useRegularization = False
697 
698  if self.useRegularization:
699  self.hMat = diffimLib.makeRegularizationMatrix(pexConfig.makePolicy(self.kConfig))
700 
701  def _diagnostic(self, kernelCellSet, spatialSolution, spatialKernel, spatialBg):
702  """!Provide logging diagnostics on quality of spatial kernel fit
703 
704  @param kernelCellSet: Cellset that contains the KernelCandidates used in the fitting
705  @param spatialSolution: KernelSolution of best-fit
706  @param spatialKernel: Best-fit spatial Kernel model
707  @param spatialBg: Best-fit spatial background model
708 
709  """
710  # What is the final kernel sum
711  kImage = afwImage.ImageD(spatialKernel.getDimensions())
712  kSum = spatialKernel.computeImage(kImage, False)
713  self.log.info("Final spatial kernel sum %.3f" % (kSum))
714 
715  # Look at how well conditioned the matrix is
716  conditionNum = spatialSolution.getConditionNumber(
717  getattr(diffimLib.KernelSolution, self.kConfig.conditionNumberType))
718  self.log.info("Spatial model condition number %.3e" % (conditionNum))
719 
720  if conditionNum < 0.0:
721  self.log.warn("Condition number is negative (%.3e)" % (conditionNum))
722  if conditionNum > self.kConfig.maxSpatialConditionNumber:
723  self.log.warn("Spatial solution exceeds max condition number (%.3e > %.3e)" % (
724  conditionNum, self.kConfig.maxSpatialConditionNumber))
725 
726  self.metadata.set("spatialConditionNum", conditionNum)
727  self.metadata.set("spatialKernelSum", kSum)
728 
729  # Look at how well the solution is constrained
730  nBasisKernels = spatialKernel.getNBasisKernels()
731  nKernelTerms = spatialKernel.getNSpatialParameters()
732  if nKernelTerms == 0: # order 0
733  nKernelTerms = 1
734 
735  # Not fit for
736  nBgTerms = spatialBg.getNParameters()
737  if nBgTerms == 1:
738  if spatialBg.getParameters()[0] == 0.0:
739  nBgTerms = 0
740 
741  nGood = 0
742  nBad = 0
743  nTot = 0
744  for cell in kernelCellSet.getCellList():
745  for cand in cell.begin(False): # False = include bad candidates
746  cand = diffimLib.KernelCandidateF.cast(cand)
747  nTot += 1
748  if cand.getStatus() == afwMath.SpatialCellCandidate.GOOD:
749  nGood += 1
750  if cand.getStatus() == afwMath.SpatialCellCandidate.BAD:
751  nBad += 1
752 
753  self.log.info("Doing stats of kernel candidates used in the spatial fit.")
754 
755  # Counting statistics
756  if nBad > 2*nGood:
757  self.log.warn("Many more candidates rejected than accepted; %d total, %d rejected, %d used" % (
758  nTot, nBad, nGood))
759  else:
760  self.log.info("%d candidates total, %d rejected, %d used" % (nTot, nBad, nGood))
761 
762  # Some judgements on the quality of the spatial models
763  if nGood < nKernelTerms:
764  self.log.warn("Spatial kernel model underconstrained; %d candidates, %d terms, %d bases" % (
765  nGood, nKernelTerms, nBasisKernels))
766  self.log.warn("Consider lowering the spatial order")
767  elif nGood <= 2*nKernelTerms:
768  self.log.warn("Spatial kernel model poorly constrained; %d candidates, %d terms, %d bases" % (
769  nGood, nKernelTerms, nBasisKernels))
770  self.log.warn("Consider lowering the spatial order")
771  else:
772  self.log.info("Spatial kernel model well constrained; %d candidates, %d terms, %d bases" % (
773  nGood, nKernelTerms, nBasisKernels))
774 
775  if nGood < nBgTerms:
776  self.log.warn("Spatial background model underconstrained; %d candidates, %d terms" % (
777  nGood, nBgTerms))
778  self.log.warn("Consider lowering the spatial order")
779  elif nGood <= 2*nBgTerms:
780  self.log.warn("Spatial background model poorly constrained; %d candidates, %d terms" % (
781  nGood, nBgTerms))
782  self.log.warn("Consider lowering the spatial order")
783  else:
784  self.log.info("Spatial background model appears well constrained; %d candidates, %d terms" % (
785  nGood, nBgTerms))
786 
787  def _displayDebug(self, kernelCellSet, spatialKernel, spatialBackground):
788  """!Provide visualization of the inputs and ouputs to the Psf-matching code
789 
790  @param kernelCellSet: the SpatialCellSet used in determining the matching kernel and background
791  @param spatialKernel: spatially varying Psf-matching kernel
792  @param spatialBackground: spatially varying background-matching function
793 
794  """
795  import lsstDebug
796  displayCandidates = lsstDebug.Info(__name__).displayCandidates
797  displayKernelBasis = lsstDebug.Info(__name__).displayKernelBasis
798  displayKernelMosaic = lsstDebug.Info(__name__).displayKernelMosaic
799  plotKernelSpatialModel = lsstDebug.Info(__name__).plotKernelSpatialModel
800  showBadCandidates = lsstDebug.Info(__name__).showBadCandidates
801  maskTransparency = lsstDebug.Info(__name__).maskTransparency
802  if not maskTransparency:
803  maskTransparency = 0
804  ds9.setMaskTransparency(maskTransparency)
805 
806  if displayCandidates:
807  diUtils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
808  frame=lsstDebug.frame,
809  showBadCandidates=showBadCandidates)
810  lsstDebug.frame += 1
811  diUtils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
812  frame=lsstDebug.frame,
813  showBadCandidates=showBadCandidates,
814  kernels=True)
815  lsstDebug.frame += 1
816  diUtils.showKernelCandidates(kernelCellSet, kernel=spatialKernel, background=spatialBackground,
817  frame=lsstDebug.frame,
818  showBadCandidates=showBadCandidates,
819  resids=True)
820  lsstDebug.frame += 1
821 
822  if displayKernelBasis:
823  diUtils.showKernelBasis(spatialKernel, frame=lsstDebug.frame)
824  lsstDebug.frame += 1
825 
826  if displayKernelMosaic:
827  diUtils.showKernelMosaic(kernelCellSet.getBBox(), spatialKernel, frame=lsstDebug.frame)
828  lsstDebug.frame += 1
829 
830  if plotKernelSpatialModel:
831  diUtils.plotKernelSpatialModel(spatialKernel, kernelCellSet, showBadCandidates=showBadCandidates)
832 
833  def _createPcaBasis(self, kernelCellSet, nStarPerCell, policy):
834  """!Create Principal Component basis
835 
836  If a principal component analysis is requested, typically when using a delta function basis,
837  perform the PCA here and return a new basis list containing the new principal components.
838 
839  @param kernelCellSet: a SpatialCellSet containing KernelCandidates, from which components are derived
840  @param nStarPerCell: the number of stars per cell to visit when doing the PCA
841  @param policy: input policy controlling the single kernel visitor
842 
843  @return
844  - nRejectedPca: number of KernelCandidates rejected during PCA loop
845  - spatialBasisList: basis list containing the principal shapes as Kernels
846 
847  """
848  nComponents = self.kConfig.numPrincipalComponents
849  imagePca = diffimLib.KernelPcaD()
850  importStarVisitor = diffimLib.KernelPcaVisitorF(imagePca)
851  kernelCellSet.visitCandidates(importStarVisitor, nStarPerCell)
852  if self.kConfig.subtractMeanForPca:
853  importStarVisitor.subtractMean()
854  imagePca.analyze()
855 
856  eigenValues = imagePca.getEigenValues()
857  pcaBasisList = importStarVisitor.getEigenKernels()
858 
859  eSum = num.sum(eigenValues)
860  if eSum == 0.0:
861  raise RuntimeError("Eigenvalues sum to zero")
862  for j in range(len(eigenValues)):
863  log.log("TRACE5." + self.log.getName() + "._solve", log.DEBUG,
864  "Eigenvalue %d : %f (%f)", j, eigenValues[j], eigenValues[j]/eSum)
865 
866  nToUse = min(nComponents, len(eigenValues))
867  trimBasisList = afwMath.KernelList()
868  for j in range(nToUse):
869  # Check for NaNs?
870  kimage = afwImage.ImageD(pcaBasisList[j].getDimensions())
871  pcaBasisList[j].computeImage(kimage, False)
872  if not (True in num.isnan(kimage.getArray())):
873  trimBasisList.push_back(pcaBasisList[j])
874 
875  # Put all the power in the first kernel, which will not vary spatially
876  spatialBasisList = diffimLib.renormalizeKernelList(trimBasisList)
877 
878  # New Kernel visitor for this new basis list (no regularization explicitly)
879  singlekvPca = diffimLib.BuildSingleKernelVisitorF(spatialBasisList, policy)
880  singlekvPca.setSkipBuilt(False)
881  kernelCellSet.visitCandidates(singlekvPca, nStarPerCell)
882  singlekvPca.setSkipBuilt(True)
883  nRejectedPca = singlekvPca.getNRejected()
884 
885  return nRejectedPca, spatialBasisList
886 
887  def _buildCellSet(self, *args):
888  """!Fill a SpatialCellSet with KernelCandidates for the Psf-matching process;
889  override in derived classes"""
890  return
891 
892  @pipeBase.timeMethod
893  def _solve(self, kernelCellSet, basisList, returnOnExcept=False):
894  """!Solve for the PSF matching kernel
895 
896  @param kernelCellSet: a SpatialCellSet to use in determining the matching kernel
897  (typically as provided by _buildCellSet)
898  @param basisList: list of Kernels to be used in the decomposition of the spatially varying kernel
899  (typically as provided by makeKernelBasisList)
900  @param returnOnExcept: if True then return (None, None) if an error occurs, else raise the exception
901 
902  @return
903  - psfMatchingKernel: PSF matching kernel
904  - backgroundModel: differential background model
905 
906  Raise Exception if unable to determine PSF matching kernel and returnOnExcept False
907  """
908 
909  import lsstDebug
910  display = lsstDebug.Info(__name__).display
911 
912  maxSpatialIterations = self.kConfig.maxSpatialIterations
913  nStarPerCell = self.kConfig.nStarPerCell
914  usePcaForSpatialKernel = self.kConfig.usePcaForSpatialKernel
915 
916  # Visitor for the single kernel fit
917  policy = pexConfig.makePolicy(self.kConfig)
918  if self.useRegularization:
919  singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy, self.hMat)
920  else:
921  singlekv = diffimLib.BuildSingleKernelVisitorF(basisList, policy)
922 
923  # Visitor for the kernel sum rejection
924  ksv = diffimLib.KernelSumVisitorF(policy)
925 
926  # Main loop
927  t0 = time.time()
928  try:
929  totalIterations = 0
930  thisIteration = 0
931  while (thisIteration < maxSpatialIterations):
932 
933  # Make sure there are no uninitialized candidates as active occupants of Cell
934  nRejectedSkf = -1
935  while (nRejectedSkf != 0):
936  log.log("TRACE1." + self.log.getName() + "._solve", log.DEBUG,
937  "Building single kernels...")
938  kernelCellSet.visitCandidates(singlekv, nStarPerCell)
939  nRejectedSkf = singlekv.getNRejected()
940  log.log("TRACE1." + self.log.getName() + "._solve", log.DEBUG,
941  "Iteration %d, rejected %d candidates due to initial kernel fit",
942  thisIteration, nRejectedSkf)
943 
944  # Reject outliers in kernel sum
945  ksv.resetKernelSum()
946  ksv.setMode(diffimLib.KernelSumVisitorF.AGGREGATE)
947  kernelCellSet.visitCandidates(ksv, nStarPerCell)
948  ksv.processKsumDistribution()
949  ksv.setMode(diffimLib.KernelSumVisitorF.REJECT)
950  kernelCellSet.visitCandidates(ksv, nStarPerCell)
951 
952  nRejectedKsum = ksv.getNRejected()
953  log.log("TRACE1." + self.log.getName() + "._solve", log.DEBUG,
954  "Iteration %d, rejected %d candidates due to kernel sum",
955  thisIteration, nRejectedKsum)
956 
957  # Do we jump back to the top without incrementing thisIteration?
958  if nRejectedKsum > 0:
959  totalIterations += 1
960  continue
961 
962  # At this stage we can either apply the spatial fit to
963  # the kernels, or we run a PCA, use these as a *new*
964  # basis set with lower dimensionality, and then apply
965  # the spatial fit to these kernels
966 
967  if (usePcaForSpatialKernel):
968  log.log("TRACE0." + self.log.getName() + "._solve", log.DEBUG,
969  "Building Pca basis")
970 
971  nRejectedPca, spatialBasisList = self._createPcaBasis(kernelCellSet, nStarPerCell, policy)
972  log.log("TRACE1." + self.log.getName() + "._solve", log.DEBUG,
973  "Iteration %d, rejected %d candidates due to Pca kernel fit",
974  thisIteration, nRejectedPca)
975 
976  # We don't want to continue on (yet) with the
977  # spatial modeling, because we have bad objects
978  # contributing to the Pca basis. We basically
979  # need to restart from the beginning of this loop,
980  # since the cell-mates of those objects that were
981  # rejected need their original Kernels built by
982  # singleKernelFitter.
983 
984  # Don't count against thisIteration
985  if (nRejectedPca > 0):
986  totalIterations += 1
987  continue
988  else:
989  spatialBasisList = basisList
990 
991  # We have gotten on to the spatial modeling part
992  regionBBox = kernelCellSet.getBBox()
993  spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
994  kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
995  spatialkv.solveLinearEquation()
996  log.log("TRACE2." + self.log.getName() + "._solve", log.DEBUG,
997  "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
998  spatialKernel, spatialBackground = spatialkv.getSolutionPair()
999 
1000  # Check the quality of the spatial fit (look at residuals)
1001  assesskv = diffimLib.AssessSpatialKernelVisitorF(spatialKernel, spatialBackground, policy)
1002  kernelCellSet.visitCandidates(assesskv, nStarPerCell)
1003  nRejectedSpatial = assesskv.getNRejected()
1004  nGoodSpatial = assesskv.getNGood()
1005  log.log("TRACE1." + self.log.getName() + "._solve", log.DEBUG,
1006  "Iteration %d, rejected %d candidates due to spatial kernel fit",
1007  thisIteration, nRejectedSpatial)
1008  log.log("TRACE1." + self.log.getName() + "._solve", log.DEBUG,
1009  "%d candidates used in fit", nGoodSpatial)
1010 
1011  # If only nGoodSpatial == 0, might be other candidates in the cells
1012  if nGoodSpatial == 0 and nRejectedSpatial == 0:
1013  raise RuntimeError("No kernel candidates for spatial fit")
1014 
1015  if nRejectedSpatial == 0:
1016  # Nothing rejected, finished with spatial fit
1017  break
1018 
1019  # Otherwise, iterate on...
1020  thisIteration += 1
1021 
1022  # Final fit if above did not converge
1023  if (nRejectedSpatial > 0) and (thisIteration == maxSpatialIterations):
1024  log.log("TRACE1." + self.log.getName() + "._solve", log.DEBUG, "Final spatial fit")
1025  if (usePcaForSpatialKernel):
1026  nRejectedPca, spatialBasisList = self._createPcaBasis(kernelCellSet, nStarPerCell, policy)
1027  regionBBox = kernelCellSet.getBBox()
1028  spatialkv = diffimLib.BuildSpatialKernelVisitorF(spatialBasisList, regionBBox, policy)
1029  kernelCellSet.visitCandidates(spatialkv, nStarPerCell)
1030  spatialkv.solveLinearEquation()
1031  log.log("TRACE2." + self.log.getName() + "._solve", log.DEBUG,
1032  "Spatial kernel built with %d candidates", spatialkv.getNCandidates())
1033  spatialKernel, spatialBackground = spatialkv.getSolutionPair()
1034 
1035  spatialSolution = spatialkv.getKernelSolution()
1036 
1037  except Exception as e:
1038  self.log.error("ERROR: Unable to calculate psf matching kernel")
1039 
1040  log.log("TRACE1." + self.log.getName() + "._solve", log.DEBUG, str(e))
1041  raise e
1042 
1043  t1 = time.time()
1044  log.log("TRACE0." + self.log.getName() + "._solve", log.DEBUG,
1045  "Total time to compute the spatial kernel : %.2f s", (t1 - t0))
1046 
1047  if display:
1048  self._displayDebug(kernelCellSet, spatialKernel, spatialBackground)
1049 
1050  self._diagnostic(kernelCellSet, spatialSolution, spatialKernel, spatialBackground)
1051 
1052  return spatialSolution, spatialKernel, spatialBackground
1053 
1054 PsfMatch = PsfMatchTask
Configuration for detecting sources on images for building a PSF-matching kernel. ...
Definition: psfMatch.py:37
The parameters specific to the &quot;Alard-Lupton&quot; (sum-of-Gaussian) Psf-matching basis.
Definition: psfMatch.py:365
Base configuration for Psf-matching.
Definition: psfMatch.py:117
The parameters specific to the delta-function (one basis per-pixel) Psf-matching basis.
Definition: psfMatch.py:422
Base class for Psf Matching; should not be called directly.
Definition: psfMatch.py:526
def _displayDebug
Provide visualization of the inputs and ouputs to the Psf-matching code.
Definition: psfMatch.py:787
def _diagnostic
Provide logging diagnostics on quality of spatial kernel fit.
Definition: psfMatch.py:701
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
def __init__
Create the psf-matching Task.
Definition: psfMatch.py:677
def _buildCellSet
Fill a SpatialCellSet with KernelCandidates for the Psf-matching process; override in derived classes...
Definition: psfMatch.py:887
def _createPcaBasis
Create Principal Component basis.
Definition: psfMatch.py:833
def _solve
Solve for the PSF matching kernel.
Definition: psfMatch.py:893
std::vector< boost::shared_ptr< Kernel > > KernelList
Definition: Kernel.h:539