LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
KernelSolution.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
11 #include <iterator>
12 #include <cmath>
13 #include <algorithm>
14 #include <limits>
15 
16 #include "boost/shared_ptr.hpp"
17 #include "boost/timer.hpp"
18 
19 #include "Eigen/Core"
20 #include "Eigen/Cholesky"
21 #include "Eigen/QR"
22 #include "Eigen/LU"
23 #include "Eigen/Eigenvalues"
24 #include "Eigen/SVD"
25 
26 #include "lsst/afw/math.h"
27 #include "lsst/afw/geom.h"
28 #include "lsst/afw/image.h"
29 #include "lsst/afw/detection.h"
30 #include "lsst/afw/detection/FootprintArray.cc"
32 #include "lsst/pex/logging/Trace.h"
33 
36 
37 #include "ndarray.h"
38 #include "ndarray/eigen.h"
39 
40 #define DEBUG_MATRIX 0
41 #define DEBUG_MATRIX2 0
42 
43 namespace afwDet = lsst::afw::detection;
44 namespace afwMath = lsst::afw::math;
45 namespace afwGeom = lsst::afw::geom;
46 namespace afwImage = lsst::afw::image;
47 namespace pexLog = lsst::pex::logging;
48 namespace pexExcept = lsst::pex::exceptions;
49 
50 namespace lsst {
51 namespace ip {
52 namespace diffim {
53 
54  /* Unique identifier for solution */
56 
58  boost::shared_ptr<Eigen::MatrixXd> mMat,
59  boost::shared_ptr<Eigen::VectorXd> bVec,
60  bool fitForBackground
61  ) :
62  _id(++_SolutionId),
63  _mMat(mMat),
64  _bVec(bVec),
65  _aVec(),
66  _solvedBy(NONE),
67  _fitForBackground(fitForBackground)
68  {};
69 
71  bool fitForBackground
72  ) :
73  _id(++_SolutionId),
74  _mMat(),
75  _bVec(),
76  _aVec(),
77  _solvedBy(NONE),
78  _fitForBackground(fitForBackground)
79  {};
80 
82  _id(++_SolutionId),
83  _mMat(),
84  _bVec(),
85  _aVec(),
86  _solvedBy(NONE),
87  _fitForBackground(true)
88  {};
89 
91  solve(*_mMat, *_bVec);
92  }
93 
95  return getConditionNumber(*_mMat, conditionType);
96  }
97 
98  double KernelSolution::getConditionNumber(Eigen::MatrixXd mMat,
99  ConditionNumberType conditionType) {
100  switch (conditionType) {
101  case EIGENVALUE:
102  {
103  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eVecValues(mMat);
104  Eigen::VectorXd eValues = eVecValues.eigenvalues();
105  double eMax = eValues.maxCoeff();
106  double eMin = eValues.minCoeff();
107  pexLog::TTrace<5>("lsst.ip.diffim.KernelSolution.getConditionNumber",
108  "EIGENVALUE eMax / eMin = %.3e", eMax / eMin);
109  return (eMax / eMin);
110  break;
111  }
112  case SVD:
113  {
114  Eigen::VectorXd sValues = mMat.jacobiSvd().singularValues();
115  double sMax = sValues.maxCoeff();
116  double sMin = sValues.minCoeff();
117  pexLog::TTrace<5>("lsst.ip.diffim.KernelSolution.getConditionNumber",
118  "SVD eMax / eMin = %.3e", sMax / sMin);
119  return (sMax / sMin);
120  break;
121  }
122  default:
123  {
124  throw LSST_EXCEPT(pexExcept::InvalidParameterError,
125  "Undefined ConditionNumberType : only EIGENVALUE, SVD allowed.");
126  break;
127  }
128  }
129  }
130 
131  void KernelSolution::solve(Eigen::MatrixXd mMat,
132  Eigen::VectorXd bVec) {
133 
134  if (DEBUG_MATRIX) {
135  std::cout << "M " << std::endl;
136  std::cout << mMat << std::endl;
137  std::cout << "B " << std::endl;
138  std::cout << bVec << std::endl;
139  }
140 
141  Eigen::VectorXd aVec = Eigen::VectorXd::Zero(bVec.size());
142 
143  boost::timer t;
144  t.restart();
145 
146  pexLog::TTrace<4>("lsst.ip.difim.KernelSolution.solve",
147  "Solving for kernel");
148  _solvedBy = LU;
149  Eigen::FullPivLU<Eigen::MatrixXd> lu(mMat);
150  if (lu.isInvertible()) {
151  aVec = lu.solve(bVec);
152  } else {
153  pexLog::TTrace<5>("lsst.ip.diffim.KernelSolution.solve",
154  "Unable to determine kernel via LU");
155  /* LAST RESORT */
156  try {
157 
158  _solvedBy = EIGENVECTOR;
159  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eVecValues(mMat);
160  Eigen::MatrixXd const& rMat = eVecValues.eigenvectors();
161  Eigen::VectorXd eValues = eVecValues.eigenvalues();
162 
163  for (int i = 0; i != eValues.rows(); ++i) {
164  if (eValues(i) != 0.0) {
165  eValues(i) = 1.0/eValues(i);
166  }
167  }
168 
169  aVec = rMat * eValues.asDiagonal() * rMat.transpose() * bVec;
170  } catch (pexExcept::Exception& e) {
171 
172  _solvedBy = NONE;
173  pexLog::TTrace<5>("lsst.ip.diffim.KernelSolution.solve",
174  "Unable to determine kernel via eigen-values");
175 
176  throw LSST_EXCEPT(pexExcept::Exception, "Unable to determine kernel solution");
177  }
178  }
179 
180  double time = t.elapsed();
181  pexLog::TTrace<5>("lsst.ip.diffim.KernelSolution.solve",
182  "Compute time for matrix math : %.2f s", time);
183 
184  if (DEBUG_MATRIX) {
185  std::cout << "A " << std::endl;
186  std::cout << aVec << std::endl;
187  }
188 
189  _aVec = boost::shared_ptr<Eigen::VectorXd>(new Eigen::VectorXd(aVec));
190  }
191 
192  /*******************************************************************************************************/
193 
194  template <typename InputT>
196  lsst::afw::math::KernelList const& basisList,
197  bool fitForBackground
198  )
199  :
200  KernelSolution(fitForBackground),
201  _cMat(),
202  _iVec(),
203  _ivVec(),
204  _kernel(),
205  _background(0.0),
206  _kSum(0.0)
207  {
208  std::vector<double> kValues(basisList.size());
209  _kernel = boost::shared_ptr<afwMath::Kernel>(
210  new afwMath::LinearCombinationKernel(basisList, kValues)
211  );
212  };
213 
214  template <typename InputT>
216  if (_solvedBy == KernelSolution::NONE) {
217  throw LSST_EXCEPT(pexExcept::Exception, "Kernel not solved; cannot return solution");
218  }
219  return _kernel;
220  }
221 
222  template <typename InputT>
224  if (_solvedBy == KernelSolution::NONE) {
225  throw LSST_EXCEPT(pexExcept::Exception, "Kernel not solved; cannot return image");
226  }
229  );
230  (void)_kernel->computeImage(*image, false);
231  return image;
232  }
233 
234  template <typename InputT>
236  if (_solvedBy == KernelSolution::NONE) {
237  throw LSST_EXCEPT(pexExcept::Exception, "Kernel not solved; cannot return background");
238  }
239  return _background;
240  }
241 
242  template <typename InputT>
244  if (_solvedBy == KernelSolution::NONE) {
245  throw LSST_EXCEPT(pexExcept::Exception, "Kernel not solved; cannot return ksum");
246  }
247  return _kSum;
248  }
249 
250  template <typename InputT>
251  std::pair<boost::shared_ptr<lsst::afw::math::Kernel>, double>
253  if (_solvedBy == KernelSolution::NONE) {
254  throw LSST_EXCEPT(pexExcept::Exception, "Kernel not solved; cannot return solution");
255  }
256 
257  return std::make_pair(_kernel, _background);
258  }
259 
260  template <typename InputT>
262  lsst::afw::image::Image<InputT> const &templateImage,
263  lsst::afw::image::Image<InputT> const &scienceImage,
265  ) {
266 
267  afwMath::Statistics varStats = afwMath::makeStatistics(varianceEstimate, afwMath::MIN);
268  if (varStats.getValue(afwMath::MIN) < 0.0) {
270  "Error: variance less than 0.0");
271  }
272  if (varStats.getValue(afwMath::MIN) == 0.0) {
274  "Error: variance equals 0.0, cannot inverse variance weight");
275  }
276 
277  lsst::afw::math::KernelList basisList =
278  boost::dynamic_pointer_cast<afwMath::LinearCombinationKernel>(_kernel)->getKernelList();
279 
280  unsigned int const nKernelParameters = basisList.size();
281  unsigned int const nBackgroundParameters = _fitForBackground ? 1 : 0;
282  unsigned int const nParameters = nKernelParameters + nBackgroundParameters;
283 
284  std::vector<boost::shared_ptr<afwMath::Kernel> >::const_iterator kiter = basisList.begin();
285 
286  /* Ignore buffers around edge of convolved images :
287  *
288  * If the kernel has width 5, it has center pixel 2. The first good pixel
289  * is the (5-2)=3rd pixel, which is array index 2, and ends up being the
290  * index of the central pixel.
291  *
292  * You also have a buffer of unusable pixels on the other side, numbered
293  * width-center-1. The last good usable pixel is N-width+center+1.
294  *
295  * Example : the kernel is width = 5, center = 2
296  *
297  * ---|---|-c-|---|---|
298  *
299  * the image is width = N
300  * convolve this with the kernel, and you get
301  *
302  * |-x-|-x-|-g-|---|---| ... |---|---|-g-|-x-|-x-|
303  *
304  * g = first/last good pixel
305  * x = bad
306  *
307  * the first good pixel is the array index that has the value "center", 2
308  * the last good pixel has array index N-(5-2)+1
309  * eg. if N = 100, you want to use up to index 97
310  * 100-3+1 = 98, and the loops use i < 98, meaning the last
311  * index you address is 97.
312  */
313 
314  /* NOTE - we are accessing particular elements of Eigen arrays using
315  these coordinates, therefore they need to be in LOCAL coordinates.
316  This was written before ndarray unification.
317  */
318  afwGeom::Box2I goodBBox = (*kiter)->shrinkBBox(templateImage.getBBox(afwImage::LOCAL));
319  unsigned int const startCol = goodBBox.getMinX();
320  unsigned int const startRow = goodBBox.getMinY();
321  // endCol/Row is one past the index of the last good col/row
322  unsigned int endCol = goodBBox.getMaxX() + 1;
323  unsigned int endRow = goodBBox.getMaxY() + 1;
324 
325  boost::timer t;
326  t.restart();
327 
328  /* Eigen representation of input images; only the pixels that are unconvolved in cimage below */
329  Eigen::MatrixXd eigenTemplate = imageToEigenMatrix(templateImage).block(startRow,
330  startCol,
331  endRow-startRow,
332  endCol-startCol);
333  Eigen::MatrixXd eigenScience = imageToEigenMatrix(scienceImage).block(startRow,
334  startCol,
335  endRow-startRow,
336  endCol-startCol);
337  Eigen::MatrixXd eigeniVariance = imageToEigenMatrix(varianceEstimate).block(
338  startRow, startCol, endRow-startRow, endCol-startCol
339  ).array().inverse().matrix();
340 
341  /* Resize into 1-D for later usage */
342  eigenTemplate.resize(eigenTemplate.rows()*eigenTemplate.cols(), 1);
343  eigenScience.resize(eigenScience.rows()*eigenScience.cols(), 1);
344  eigeniVariance.resize(eigeniVariance.rows()*eigeniVariance.cols(), 1);
345 
346  /* Holds image convolved with basis function */
347  afwImage::Image<PixelT> cimage(templateImage.getDimensions());
348 
349  /* Holds eigen representation of image convolved with all basis functions */
350  std::vector<boost::shared_ptr<Eigen::MatrixXd> > convolvedEigenList(nKernelParameters);
351 
352  /* Iterators over convolved image list and basis list */
353  typename std::vector<boost::shared_ptr<Eigen::MatrixXd> >::iterator eiter =
354  convolvedEigenList.begin();
355  /* Create C_i in the formalism of Alard & Lupton */
356  for (kiter = basisList.begin(); kiter != basisList.end(); ++kiter, ++eiter) {
357  afwMath::convolve(cimage, templateImage, **kiter, false); /* cimage stores convolved image */
358 
359  boost::shared_ptr<Eigen::MatrixXd> cMat (
360  new Eigen::MatrixXd(imageToEigenMatrix(cimage).block(startRow,
361  startCol,
362  endRow-startRow,
363  endCol-startCol))
364  );
365  cMat->resize(cMat->rows()*cMat->cols(), 1);
366  *eiter = cMat;
367 
368  }
369 
370  double time = t.elapsed();
371  pexLog::TTrace<5>("lsst.ip.diffim.StaticKernelSolution.build",
372  "Total compute time to do basis convolutions : %.2f s", time);
373  t.restart();
374 
375  /*
376  Load matrix with all values from convolvedEigenList : all images
377  (eigeniVariance, convolvedEigenList) must be the same size
378  */
379  Eigen::MatrixXd cMat(eigenTemplate.col(0).size(), nParameters);
380  typename std::vector<boost::shared_ptr<Eigen::MatrixXd> >::iterator eiterj =
381  convolvedEigenList.begin();
382  typename std::vector<boost::shared_ptr<Eigen::MatrixXd> >::iterator eiterE =
383  convolvedEigenList.end();
384  for (unsigned int kidxj = 0; eiterj != eiterE; eiterj++, kidxj++) {
385  cMat.col(kidxj) = (*eiterj)->col(0);
386  }
387  /* Treat the last "image" as all 1's to do the background calculation. */
388  if (_fitForBackground)
389  cMat.col(nParameters-1).fill(1.);
390 
391  _cMat.reset(new Eigen::MatrixXd(cMat));
392  _ivVec.reset(new Eigen::VectorXd(eigeniVariance.col(0)));
393  _iVec.reset(new Eigen::VectorXd(eigenScience.col(0)));
394 
395  /* Make these outside of solve() so I can check condition number */
396  _mMat.reset(new Eigen::MatrixXd((*_cMat).transpose() * ((*_ivVec).asDiagonal() * (*_cMat))));
397  _bVec.reset(new Eigen::VectorXd((*_cMat).transpose() * ((*_ivVec).asDiagonal() * (*_iVec))));
398  }
399 
400  template <typename InputT>
402  pexLog::TTrace<5>("lsst.ip.diffim.StaticKernelSolution.solve",
403  "mMat is %d x %d; bVec is %d; cMat is %d x %d; vVec is %d; iVec is %d",
404  (*_mMat).rows(), (*_mMat).cols(), (*_bVec).size(),
405  (*_cMat).rows(), (*_cMat).cols(), (*_ivVec).size(), (*_iVec).size());
406 
407  /* If I put this here I can't check for condition number before solving */
408  /*
409  _mMat.reset(new Eigen::MatrixXd((*_cMat).transpose() * ((*_ivVec).asDiagonal() * (*_cMat))));
410  _bVec.reset(new Eigen::VectorXd((*_cMat).transpose() * ((*_ivVec).asDiagonal() * (*_iVec))));
411  */
412 
413  if (DEBUG_MATRIX) {
414  std::cout << "C" << std::endl;
415  std::cout << (*_cMat) << std::endl;
416  std::cout << "iV" << std::endl;
417  std::cout << (*_ivVec) << std::endl;
418  std::cout << "I" << std::endl;
419  std::cout << (*_iVec) << std::endl;
420  }
421 
422  try {
424  } catch (pexExcept::Exception &e) {
425  LSST_EXCEPT_ADD(e, "Unable to solve static kernel matrix");
426  throw e;
427  }
428  /* Turn matrices into _kernel and _background */
429  _setKernel();
430  }
431 
432  template <typename InputT>
434  if (_solvedBy == KernelSolution::NONE) {
435  throw LSST_EXCEPT(pexExcept::Exception, "Kernel not solved; cannot make solution");
436  }
437 
438  unsigned int const nParameters = _aVec->size();
439  unsigned int const nBackgroundParameters = _fitForBackground ? 1 : 0;
440  unsigned int const nKernelParameters =
441  boost::dynamic_pointer_cast<afwMath::LinearCombinationKernel>(_kernel)->getKernelList().size();
442  if (nParameters != (nKernelParameters + nBackgroundParameters))
443  throw LSST_EXCEPT(pexExcept::Exception, "Mismatched sizes in kernel solution");
444 
445  /* Fill in the kernel results */
446  std::vector<double> kValues(nKernelParameters);
447  for (unsigned int idx = 0; idx < nKernelParameters; idx++) {
448  if (std::isnan((*_aVec)(idx))) {
450  str(boost::format("Unable to determine kernel solution %d (nan)") % idx));
451  }
452  kValues[idx] = (*_aVec)(idx);
453  }
454  _kernel->setKernelParameters(kValues);
455 
458  );
459  _kSum = _kernel->computeImage(*image, false);
460 
461  if (_fitForBackground) {
462  if (std::isnan((*_aVec)(nParameters-1))) {
464  str(boost::format("Unable to determine background solution %d (nan)") %
465  (nParameters-1)));
466  }
467  _background = (*_aVec)(nParameters-1);
468  }
469  }
470 
471 
472  template <typename InputT>
474  throw LSST_EXCEPT(pexExcept::Exception, "Uncertainty calculation not supported");
475 
476  /* Estimate of parameter uncertainties comes from the inverse of the
477  * covariance matrix (noise spectrum).
478  * N.R. 15.4.8 to 15.4.15
479  *
480  * Since this is a linear problem no need to use Fisher matrix
481  * N.R. 15.5.8
482  *
483  * Although I might be able to take advantage of the solution above.
484  * Since this now works and is not the rate limiting step, keep as-is for DC3a.
485  *
486  * Use Cholesky decomposition again.
487  * Cholkesy:
488  * Cov = L L^t
489  * Cov^(-1) = (L L^t)^(-1)
490  * = (L^T)^-1 L^(-1)
491  *
492  * Code would be:
493  *
494  * Eigen::MatrixXd cov = (*_mMat).transpose() * (*_mMat);
495  * Eigen::LLT<Eigen::MatrixXd> llt = cov.llt();
496  * Eigen::MatrixXd error2 = llt.matrixL().transpose().inverse()*llt.matrixL().inverse();
497  */
498  }
499 
500  /*******************************************************************************************************/
501 
502  template <typename InputT>
504  lsst::afw::math::KernelList const& basisList,
505  bool fitForBackground
506  ) :
507  StaticKernelSolution<InputT>(basisList, fitForBackground)
508  {};
509 
510  template <typename InputT>
512  lsst::afw::image::Image<InputT> const &templateImage,
513  lsst::afw::image::Image<InputT> const &scienceImage,
516  ) {
517 
518  afwMath::Statistics varStats = afwMath::makeStatistics(varianceEstimate, afwMath::MIN);
519  if (varStats.getValue(afwMath::MIN) < 0.0) {
521  "Error: variance less than 0.0");
522  }
523  if (varStats.getValue(afwMath::MIN) == 0.0) {
525  "Error: variance equals 0.0, cannot inverse variance weight");
526  }
527 
528  /* Full footprint of all input images */
529  afwDet::Footprint::Ptr fullFp(new afwDet::Footprint(templateImage.getBBox()));
530 
531  afwMath::KernelList basisList =
532  boost::dynamic_pointer_cast<afwMath::LinearCombinationKernel>(this->_kernel)->getKernelList();
533  std::vector<boost::shared_ptr<afwMath::Kernel> >::const_iterator kiter = basisList.begin();
534 
535  /* Only BAD pixels marked in this mask */
536  afwImage::MaskPixel bitMask =
540 
541  /* Create a Footprint that contains all the masked pixels set above */
543  afwDet::FootprintSet maskFpSet(pixelMask, threshold, true);
544 
545  /* And spread it by the kernel half width */
546  int growPix = (*kiter)->getCtr().getX();
547  afwDet::FootprintSet maskedFpSetGrown(maskFpSet, growPix, true);
548 
549 #if 0
550  for (typename afwDet::FootprintSet::FootprintList::iterator
551  ptr = maskedFpSetGrown.getFootprints()->begin(),
552  end = maskedFpSetGrown.getFootprints()->end();
553  ptr != end;
554  ++ptr) {
555 
556  afwDet::setMaskFromFootprint(finalMask,
557  (**ptr),
559  }
560 #endif
561 
562  afwImage::Mask<afwImage::MaskPixel> finalMask(pixelMask.getDimensions());
564  *(maskedFpSetGrown.getFootprints()),
566  pixelMask.writeFits("pixelmask.fits");
567  finalMask.writeFits("finalmask.fits");
568 
569 
570  ndarray::Array<int, 1, 1> maskArray =
571  ndarray::allocate(ndarray::makeVector(fullFp->getArea()));
572  afwDet::flattenArray(*fullFp, finalMask.getArray(),
573  maskArray, templateImage.getXY0()); /* Need to fake the XY0 */
574  ndarray::EigenView<int, 1, 1> maskEigen(maskArray);
575 
576  ndarray::Array<InputT, 1, 1> arrayTemplate =
577  ndarray::allocate(ndarray::makeVector(fullFp->getArea()));
578  afwDet::flattenArray(*fullFp, templateImage.getArray(),
579  arrayTemplate, templateImage.getXY0());
580  ndarray::EigenView<InputT, 1, 1> eigenTemplate0(arrayTemplate);
581 
582  ndarray::Array<InputT, 1, 1> arrayScience =
583  ndarray::allocate(ndarray::makeVector(fullFp->getArea()));
584  afwDet::flattenArray(*fullFp, scienceImage.getArray(),
585  arrayScience, scienceImage.getXY0());
586  ndarray::EigenView<InputT, 1, 1> eigenScience0(arrayScience);
587 
589  ndarray::allocate(ndarray::makeVector(fullFp->getArea()));
590  afwDet::flattenArray(*fullFp, varianceEstimate.getArray(),
591  arrayVariance, varianceEstimate.getXY0());
592  ndarray::EigenView<afwImage::VariancePixel, 1, 1> eigenVariance0(arrayVariance);
593 
594  int nGood = 0;
595  for (int i = 0; i < maskEigen.size(); i++) {
596  if (maskEigen(i) == 0.0)
597  nGood += 1;
598  }
599 
600  Eigen::VectorXd eigenTemplate(nGood);
601  Eigen::VectorXd eigenScience(nGood);
602  Eigen::VectorXd eigenVariance(nGood);
603  int nUsed = 0;
604  for (int i = 0; i < maskEigen.size(); i++) {
605  if (maskEigen(i) == 0.0) {
606  eigenTemplate(nUsed) = eigenTemplate0(i);
607  eigenScience(nUsed) = eigenScience0(i);
608  eigenVariance(nUsed) = eigenVariance0(i);
609  nUsed += 1;
610  }
611  }
612 
613 
614  boost::timer t;
615  t.restart();
616 
617  unsigned int const nKernelParameters = basisList.size();
618  unsigned int const nBackgroundParameters = this->_fitForBackground ? 1 : 0;
619  unsigned int const nParameters = nKernelParameters + nBackgroundParameters;
620 
621  /* Holds image convolved with basis function */
622  afwImage::Image<InputT> cimage(templateImage.getDimensions());
623 
624  /* Holds eigen representation of image convolved with all basis functions */
625  std::vector<boost::shared_ptr<Eigen::VectorXd> >
626  convolvedEigenList(nKernelParameters);
627 
628  /* Iterators over convolved image list and basis list */
629  typename std::vector<boost::shared_ptr<Eigen::VectorXd> >::iterator eiter =
630  convolvedEigenList.begin();
631 
632  /* Create C_i in the formalism of Alard & Lupton */
633  for (kiter = basisList.begin(); kiter != basisList.end(); ++kiter, ++eiter) {
634  afwMath::convolve(cimage, templateImage, **kiter, false); /* cimage stores convolved image */
635 
637  ndarray::allocate(ndarray::makeVector(fullFp->getArea()));
638  afwDet::flattenArray(*fullFp, cimage.getArray(),
639  arrayC, cimage.getXY0());
640  ndarray::EigenView<InputT, 1, 1> eigenC0(arrayC);
641 
642  Eigen::VectorXd eigenC(nGood);
643  int nUsed = 0;
644  for (int i = 0; i < maskEigen.size(); i++) {
645  if (maskEigen(i) == 0.0) {
646  eigenC(nUsed) = eigenC0(i);
647  nUsed += 1;
648  }
649  }
650 
651  boost::shared_ptr<Eigen::VectorXd>
652  eigenCptr (new Eigen::VectorXd(eigenC));
653 
654  *eiter = eigenCptr;
655  }
656  double time = t.elapsed();
657  pexLog::TTrace<5>("lsst.ip.diffim.StaticKernelSolution.buildWithMask",
658  "Total compute time to do basis convolutions : %.2f s", time);
659  t.restart();
660 
661  /* Load matrix with all convolved images */
662  Eigen::MatrixXd cMat(eigenTemplate.size(), nParameters);
663  typename std::vector<boost::shared_ptr<Eigen::VectorXd> >::iterator eiterj =
664  convolvedEigenList.begin();
665  typename std::vector<boost::shared_ptr<Eigen::VectorXd> >::iterator eiterE =
666  convolvedEigenList.end();
667  for (unsigned int kidxj = 0; eiterj != eiterE; eiterj++, kidxj++) {
668  cMat.block(0, kidxj, eigenTemplate.size(), 1) =
669  Eigen::MatrixXd(**eiterj).block(0, 0, eigenTemplate.size(), 1);
670  }
671  /* Treat the last "image" as all 1's to do the background calculation. */
672  if (this->_fitForBackground)
673  cMat.col(nParameters-1).fill(1.);
674 
675  this->_cMat.reset(new Eigen::MatrixXd(cMat));
676  //this->_ivVec.reset(new Eigen::VectorXd((eigenVariance.template cast<double>()).cwise().inverse()));
677  //this->_iVec.reset(new Eigen::VectorXd(eigenScience.template cast<double>()));
678  this->_ivVec.reset(new Eigen::VectorXd(eigenVariance.array().inverse().matrix()));
679  this->_iVec.reset(new Eigen::VectorXd(eigenScience));
680 
681  /* Make these outside of solve() so I can check condition number */
682  this->_mMat.reset(
683  new Eigen::MatrixXd(this->_cMat->transpose() * this->_ivVec->asDiagonal() * *(this->_cMat))
684  );
685  this->_bVec.reset(
686  new Eigen::VectorXd(this->_cMat->transpose() * this->_ivVec->asDiagonal() * *(this->_iVec))
687  );
688  }
689 
690 
691  template <typename InputT>
693  lsst::afw::image::Image<InputT> const &templateImage,
694  lsst::afw::image::Image<InputT> const &scienceImage,
697  ) {
698 
699  afwMath::Statistics varStats = afwMath::makeStatistics(varianceEstimate, afwMath::MIN);
700  if (varStats.getValue(afwMath::MIN) < 0.0) {
702  "Error: variance less than 0.0");
703  }
704  if (varStats.getValue(afwMath::MIN) == 0.0) {
706  "Error: variance equals 0.0, cannot inverse variance weight");
707  }
708 
709  lsst::afw::math::KernelList basisList =
710  boost::dynamic_pointer_cast<afwMath::LinearCombinationKernel>(this->_kernel)->getKernelList();
711  std::vector<boost::shared_ptr<afwMath::Kernel> >::const_iterator kiter = basisList.begin();
712 
713  /* Only BAD pixels marked in this mask */
715  afwImage::MaskPixel bitMask =
719  sMask &= bitMask;
720  /* TBD: Need to figure out a way to spread this; currently done in Python */
721 
722  unsigned int const nKernelParameters = basisList.size();
723  unsigned int const nBackgroundParameters = this->_fitForBackground ? 1 : 0;
724  unsigned int const nParameters = nKernelParameters + nBackgroundParameters;
725 
726  /* NOTE - we are accessing particular elements of Eigen arrays using
727  these coordinates, therefore they need to be in LOCAL coordinates.
728  This was written before ndarray unification.
729  */
730  /* Ignore known EDGE pixels for speed */
731  afwGeom::Box2I shrunkLocalBBox = (*kiter)->shrinkBBox(templateImage.getBBox(afwImage::LOCAL));
732  pexLog::TTrace<5>("lsst.ip.diffim.MaskedKernelSolution.build",
733  "Limits of good pixels after convolution: %d,%d -> %d,%d (local)",
734  shrunkLocalBBox.getMinX(), shrunkLocalBBox.getMinY(),
735  shrunkLocalBBox.getMaxX(), shrunkLocalBBox.getMaxY());
736 
737  /* Subimages are addressed in raw pixel coordinates */
738  unsigned int startCol = shrunkLocalBBox.getMinX();
739  unsigned int startRow = shrunkLocalBBox.getMinY();
740  unsigned int endCol = shrunkLocalBBox.getMaxX();
741  unsigned int endRow = shrunkLocalBBox.getMaxY();
742  /* Needed for Eigen block slicing operation */
743  endCol += 1;
744  endRow += 1;
745 
746  boost::timer t;
747  t.restart();
748 
749  /* Eigen representation of input images; only the pixels that are unconvolved in cimage below */
750  Eigen::MatrixXi eMask = maskToEigenMatrix(sMask).block(startRow,
751  startCol,
752  endRow-startRow,
753  endCol-startCol);
754 
755  Eigen::MatrixXd eigenTemplate = imageToEigenMatrix(templateImage).block(startRow,
756  startCol,
757  endRow-startRow,
758  endCol-startCol);
759  Eigen::MatrixXd eigenScience = imageToEigenMatrix(scienceImage).block(startRow,
760  startCol,
761  endRow-startRow,
762  endCol-startCol);
763  Eigen::MatrixXd eigeniVariance = imageToEigenMatrix(varianceEstimate).block(
764  startRow, startCol, endRow-startRow, endCol-startCol
765  ).array().inverse().matrix();
766 
767  /* Resize into 1-D for later usage */
768  eMask.resize(eMask.rows()*eMask.cols(), 1);
769  eigenTemplate.resize(eigenTemplate.rows()*eigenTemplate.cols(), 1);
770  eigenScience.resize(eigenScience.rows()*eigenScience.cols(), 1);
771  eigeniVariance.resize(eigeniVariance.rows()*eigeniVariance.cols(), 1);
772 
773  /* Do the masking, slowly for now... */
774  Eigen::MatrixXd maskedEigenTemplate(eigenTemplate.rows(), 1);
775  Eigen::MatrixXd maskedEigenScience(eigenScience.rows(), 1);
776  Eigen::MatrixXd maskedEigeniVariance(eigeniVariance.rows(), 1);
777  int nGood = 0;
778  for (int i = 0; i < eMask.rows(); i++) {
779  if (eMask(i, 0) == 0) {
780  maskedEigenTemplate(nGood, 0) = eigenTemplate(i, 0);
781  maskedEigenScience(nGood, 0) = eigenScience(i, 0);
782  maskedEigeniVariance(nGood, 0) = eigeniVariance(i, 0);
783  nGood += 1;
784  }
785  }
786  /* Can't resize() since all values are lost; use blocks */
787  eigenTemplate = maskedEigenTemplate.block(0, 0, nGood, 1);
788  eigenScience = maskedEigenScience.block(0, 0, nGood, 1);
789  eigeniVariance = maskedEigeniVariance.block(0, 0, nGood, 1);
790 
791 
792  /* Holds image convolved with basis function */
793  afwImage::Image<InputT> cimage(templateImage.getDimensions());
794 
795  /* Holds eigen representation of image convolved with all basis functions */
796  std::vector<boost::shared_ptr<Eigen::MatrixXd> > convolvedEigenList(nKernelParameters);
797 
798  /* Iterators over convolved image list and basis list */
799  typename std::vector<boost::shared_ptr<Eigen::MatrixXd> >::iterator eiter =
800  convolvedEigenList.begin();
801  /* Create C_i in the formalism of Alard & Lupton */
802  for (kiter = basisList.begin(); kiter != basisList.end(); ++kiter, ++eiter) {
803  afwMath::convolve(cimage, templateImage, **kiter, false); /* cimage stores convolved image */
804 
805  Eigen::MatrixXd cMat = imageToEigenMatrix(cimage).block(startRow,
806  startCol,
807  endRow-startRow,
808  endCol-startCol);
809  cMat.resize(cMat.rows() * cMat.cols(), 1);
810 
811  /* Do masking */
812  Eigen::MatrixXd maskedcMat(cMat.rows(), 1);
813  int nGood = 0;
814  for (int i = 0; i < eMask.rows(); i++) {
815  if (eMask(i, 0) == 0) {
816  maskedcMat(nGood, 0) = cMat(i, 0);
817  nGood += 1;
818  }
819  }
820  cMat = maskedcMat.block(0, 0, nGood, 1);
821  boost::shared_ptr<Eigen::MatrixXd> cMatPtr (new Eigen::MatrixXd(cMat));
822  *eiter = cMatPtr;
823  }
824 
825  double time = t.elapsed();
826  pexLog::TTrace<5>("lsst.ip.diffim.StaticKernelSolution.build",
827  "Total compute time to do basis convolutions : %.2f s", time);
828  t.restart();
829 
830  /*
831  Load matrix with all values from convolvedEigenList : all images
832  (eigeniVariance, convolvedEigenList) must be the same size
833  */
834  Eigen::MatrixXd cMat(eigenTemplate.col(0).size(), nParameters);
835  typename std::vector<boost::shared_ptr<Eigen::MatrixXd> >::iterator eiterj =
836  convolvedEigenList.begin();
837  typename std::vector<boost::shared_ptr<Eigen::MatrixXd> >::iterator eiterE =
838  convolvedEigenList.end();
839  for (unsigned int kidxj = 0; eiterj != eiterE; eiterj++, kidxj++) {
840  cMat.col(kidxj) = (*eiterj)->col(0);
841  }
842  /* Treat the last "image" as all 1's to do the background calculation. */
843  if (this->_fitForBackground)
844  cMat.col(nParameters-1).fill(1.);
845 
846  this->_cMat.reset(new Eigen::MatrixXd(cMat));
847  this->_ivVec.reset(new Eigen::VectorXd(eigeniVariance.col(0)));
848  this->_iVec.reset(new Eigen::VectorXd(eigenScience.col(0)));
849 
850  /* Make these outside of solve() so I can check condition number */
851  this->_mMat.reset(
852  new Eigen::MatrixXd(this->_cMat->transpose() * this->_ivVec->asDiagonal() * *(this->_cMat))
853  );
854  this->_bVec.reset(
855  new Eigen::VectorXd(this->_cMat->transpose() * this->_ivVec->asDiagonal() * *(this->_iVec))
856  );
857 
858  }
859 
860  /* NOTE - this was written before the ndarray unification. I am rewriting
861  buildSingleMask to use the ndarray stuff */
862  template <typename InputT>
864  lsst::afw::image::Image<InputT> const &templateImage,
865  lsst::afw::image::Image<InputT> const &scienceImage,
867  lsst::afw::geom::Box2I maskBox
868  ) {
869 
870  afwMath::Statistics varStats = afwMath::makeStatistics(varianceEstimate, afwMath::MIN);
871  if (varStats.getValue(afwMath::MIN) < 0.0) {
873  "Error: variance less than 0.0");
874  }
875  if (varStats.getValue(afwMath::MIN) == 0.0) {
877  "Error: variance equals 0.0, cannot inverse variance weight");
878  }
879 
880  lsst::afw::math::KernelList basisList =
881  boost::dynamic_pointer_cast<afwMath::LinearCombinationKernel>(this->_kernel)->getKernelList();
882 
883  unsigned int const nKernelParameters = basisList.size();
884  unsigned int const nBackgroundParameters = this->_fitForBackground ? 1 : 0;
885  unsigned int const nParameters = nKernelParameters + nBackgroundParameters;
886 
887  std::vector<boost::shared_ptr<afwMath::Kernel> >::const_iterator kiter = basisList.begin();
888 
889  /*
890  NOTE : If we are using these views in Afw's Image space, we need to
891  make sure and compensate for the XY0 of the image:
892 
893  afwGeom::Box2I fullBBox = templateImage.getBBox();
894  int maskStartCol = maskBox.getMinX();
895  int maskStartRow = maskBox.getMinY();
896  int maskEndCol = maskBox.getMaxX();
897  int maskEndRow = maskBox.getMaxY();
898 
899 
900  If we are going to be doing the slicing in Eigen matrices derived from
901  the images, ignore the XY0.
902 
903  afwGeom::Box2I fullBBox = templateImage.getBBox(afwImage::LOCAL);
904 
905  int maskStartCol = maskBox.getMinX() - templateImage.getX0();
906  int maskStartRow = maskBox.getMinY() - templateImage.getY0();
907  int maskEndCol = maskBox.getMaxX() - templateImage.getX0();
908  int maskEndRow = maskBox.getMaxY() - templateImage.getY0();
909 
910  */
911 
912 
913  afwGeom::Box2I shrunkBBox = (*kiter)->shrinkBBox(templateImage.getBBox());
914 
915  pexLog::TTrace<5>("lsst.ip.diffim.MaskedKernelSolution.build",
916  "Limits of good pixels after convolution: %d,%d -> %d,%d",
917  shrunkBBox.getMinX(), shrunkBBox.getMinY(),
918  shrunkBBox.getMaxX(), shrunkBBox.getMaxY());
919 
920  unsigned int const startCol = shrunkBBox.getMinX();
921  unsigned int const startRow = shrunkBBox.getMinY();
922  unsigned int const endCol = shrunkBBox.getMaxX();
923  unsigned int const endRow = shrunkBBox.getMaxY();
924 
925  /* NOTE: no endCol/endRow += 1 for block slicing, since we are doing the
926  slicing using Afw, not Eigen
927 
928  Eigen arrays have index 0,0 in the upper right, while LSST images
929  have 0,0 in the lower left. The y-axis is flipped. When translating
930  Images to Eigen matrices in ipDiffim::imageToEigenMatrix this is
931  accounted for. However, we still need to be aware of this fact if
932  addressing subregions of an Eigen matrix. This is why the slicing is
933  done in Afw, its cleaner.
934 
935  Please see examples/maskedKernel.cc for elaboration on some of the
936  tests done to make sure this indexing gets done right.
937 
938  */
939 
940 
941  /* Inner limits; of mask box */
942  int maskStartCol = maskBox.getMinX();
943  int maskStartRow = maskBox.getMinY();
944  int maskEndCol = maskBox.getMaxX();
945  int maskEndRow = maskBox.getMaxY();
946 
947  /*
948 
949  |---------------------------|
950  | Kernel Boundary |
951  | |---------------------| |
952  | | Top | |
953  | |......_________......| |
954  | | | | | |
955  | | L | Box | R | |
956  | | | | | |
957  | |......---------......| |
958  | | Bottom | |
959  | |---------------------| |
960  | |
961  |---------------------------|
962 
963  4 regions we want to extract from the pixels: top bottom left right
964 
965  */
966  afwGeom::Box2I tBox = afwGeom::Box2I(afwGeom::Point2I(startCol, maskEndRow + 1),
967  afwGeom::Point2I(endCol, endRow));
968 
969  afwGeom::Box2I bBox = afwGeom::Box2I(afwGeom::Point2I(startCol, startRow),
970  afwGeom::Point2I(endCol, maskStartRow - 1));
971 
972  afwGeom::Box2I lBox = afwGeom::Box2I(afwGeom::Point2I(startCol, maskStartRow),
973  afwGeom::Point2I(maskStartCol - 1, maskEndRow));
974 
975  afwGeom::Box2I rBox = afwGeom::Box2I(afwGeom::Point2I(maskEndCol + 1, maskStartRow),
976  afwGeom::Point2I(endCol, maskEndRow));
977 
978  pexLog::TTrace<5>("lsst.ip.diffim.MaskedKernelSolution.build",
979  "Upper good pixel region: %d,%d -> %d,%d",
980  tBox.getMinX(), tBox.getMinY(), tBox.getMaxX(), tBox.getMaxY());
981  pexLog::TTrace<5>("lsst.ip.diffim.MaskedKernelSolution.build",
982  "Bottom good pixel region: %d,%d -> %d,%d",
983  bBox.getMinX(), bBox.getMinY(), bBox.getMaxX(), bBox.getMaxY());
984  pexLog::TTrace<5>("lsst.ip.diffim.MaskedKernelSolution.build",
985  "Left good pixel region: %d,%d -> %d,%d",
986  lBox.getMinX(), lBox.getMinY(), lBox.getMaxX(), lBox.getMaxY());
987  pexLog::TTrace<5>("lsst.ip.diffim.MaskedKernelSolution.build",
988  "Right good pixel region: %d,%d -> %d,%d",
989  rBox.getMinX(), rBox.getMinY(), rBox.getMaxX(), rBox.getMaxY());
990 
991  std::vector<afwGeom::Box2I> boxArray;
992  boxArray.push_back(tBox);
993  boxArray.push_back(bBox);
994  boxArray.push_back(lBox);
995  boxArray.push_back(rBox);
996 
997  int totalSize = tBox.getWidth() * tBox.getHeight();
998  totalSize += bBox.getWidth() * bBox.getHeight();
999  totalSize += lBox.getWidth() * lBox.getHeight();
1000  totalSize += rBox.getWidth() * rBox.getHeight();
1001 
1002  Eigen::MatrixXd eigenTemplate(totalSize, 1);
1003  Eigen::MatrixXd eigenScience(totalSize, 1);
1004  Eigen::MatrixXd eigeniVariance(totalSize, 1);
1005  eigenTemplate.setZero();
1006  eigenScience.setZero();
1007  eigeniVariance.setZero();
1008 
1009  boost::timer t;
1010  t.restart();
1011 
1012  int nTerms = 0;
1013  typename std::vector<afwGeom::Box2I>::iterator biter = boxArray.begin();
1014  for (; biter != boxArray.end(); ++biter) {
1015  int area = (*biter).getWidth() * (*biter).getHeight();
1016 
1017  afwImage::Image<InputT> siTemplate(templateImage, *biter);
1018  afwImage::Image<InputT> siScience(scienceImage, *biter);
1019  afwImage::Image<InputT> sVarEstimate(varianceEstimate, *biter);
1020 
1021  Eigen::MatrixXd eTemplate = imageToEigenMatrix(siTemplate);
1022  Eigen::MatrixXd eScience = imageToEigenMatrix(siScience);
1023  Eigen::MatrixXd eiVarEstimate = imageToEigenMatrix(sVarEstimate).array().inverse().matrix();
1024 
1025  eTemplate.resize(area, 1);
1026  eScience.resize(area, 1);
1027  eiVarEstimate.resize(area, 1);
1028 
1029  eigenTemplate.block(nTerms, 0, area, 1) = eTemplate.block(0, 0, area, 1);
1030  eigenScience.block(nTerms, 0, area, 1) = eScience.block(0, 0, area, 1);
1031  eigeniVariance.block(nTerms, 0, area, 1) = eiVarEstimate.block(0, 0, area, 1);
1032 
1033  nTerms += area;
1034  }
1035 
1036  afwImage::Image<InputT> cimage(templateImage.getDimensions());
1037 
1038  std::vector<boost::shared_ptr<Eigen::MatrixXd> > convolvedEigenList(nKernelParameters);
1039  typename std::vector<boost::shared_ptr<Eigen::MatrixXd> >::iterator eiter =
1040  convolvedEigenList.begin();
1041  /* Create C_i in the formalism of Alard & Lupton */
1042  for (kiter = basisList.begin(); kiter != basisList.end(); ++kiter, ++eiter) {
1043  afwMath::convolve(cimage, templateImage, **kiter, false); /* cimage stores convolved image */
1044  Eigen::MatrixXd cMat(totalSize, 1);
1045  cMat.setZero();
1046 
1047  int nTerms = 0;
1048  typename std::vector<afwGeom::Box2I>::iterator biter = boxArray.begin();
1049  for (; biter != boxArray.end(); ++biter) {
1050  int area = (*biter).getWidth() * (*biter).getHeight();
1051 
1052  afwImage::Image<InputT> csubimage(cimage, *biter);
1053  Eigen::MatrixXd esubimage = imageToEigenMatrix(csubimage);
1054  esubimage.resize(area, 1);
1055  cMat.block(nTerms, 0, area, 1) = esubimage.block(0, 0, area, 1);
1056 
1057  nTerms += area;
1058  }
1059 
1060  boost::shared_ptr<Eigen::MatrixXd> cMatPtr (new Eigen::MatrixXd(cMat));
1061  *eiter = cMatPtr;
1062 
1063  }
1064 
1065  double time = t.elapsed();
1066  pexLog::TTrace<5>("lsst.ip.diffim.MaskedKernelSolution.build",
1067  "Total compute time to do basis convolutions : %.2f s", time);
1068  t.restart();
1069 
1070  /*
1071  Load matrix with all values from convolvedEigenList : all images
1072  (eigeniVariance, convolvedEigenList) must be the same size
1073  */
1074  Eigen::MatrixXd cMat(eigenTemplate.col(0).size(), nParameters);
1075  typename std::vector<boost::shared_ptr<Eigen::MatrixXd> >::iterator eiterj =
1076  convolvedEigenList.begin();
1077  typename std::vector<boost::shared_ptr<Eigen::MatrixXd> >::iterator eiterE =
1078  convolvedEigenList.end();
1079  for (unsigned int kidxj = 0; eiterj != eiterE; eiterj++, kidxj++) {
1080  cMat.col(kidxj) = (*eiterj)->col(0);
1081  }
1082  /* Treat the last "image" as all 1's to do the background calculation. */
1083  if (this->_fitForBackground)
1084  cMat.col(nParameters-1).fill(1.);
1085 
1086  this->_cMat.reset(new Eigen::MatrixXd(cMat));
1087  this->_ivVec.reset(new Eigen::VectorXd(eigeniVariance.col(0)));
1088  this->_iVec.reset(new Eigen::VectorXd(eigenScience.col(0)));
1089 
1090  /* Make these outside of solve() so I can check condition number */
1091  this->_mMat.reset(
1092  new Eigen::MatrixXd(this->_cMat->transpose() * this->_ivVec->asDiagonal() * *(this->_cMat))
1093  );
1094  this->_bVec.reset(
1095  new Eigen::VectorXd(this->_cMat->transpose() * this->_ivVec->asDiagonal() * *(this->_iVec))
1096  );
1097 
1098  }
1099  /*******************************************************************************************************/
1100 
1101 
1102  template <typename InputT>
1104  lsst::afw::math::KernelList const& basisList,
1105  bool fitForBackground,
1106  boost::shared_ptr<Eigen::MatrixXd> hMat,
1108  )
1109  :
1110  StaticKernelSolution<InputT>(basisList, fitForBackground),
1111  _hMat(hMat),
1112  _policy(policy)
1113  {};
1114 
1115  template <typename InputT>
1117  Eigen::MatrixXd vMat = (this->_cMat)->jacobiSvd().matrixV();
1118  Eigen::MatrixXd vMatvMatT = vMat * vMat.transpose();
1119 
1120  /* Find pseudo inverse of mMat, which may be ill conditioned */
1121  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eVecValues(*(this->_mMat));
1122  Eigen::MatrixXd const& rMat = eVecValues.eigenvectors();
1123  Eigen::VectorXd eValues = eVecValues.eigenvalues();
1124  double eMax = eValues.maxCoeff();
1125  for (int i = 0; i != eValues.rows(); ++i) {
1126  if (eValues(i) == 0.0) {
1127  eValues(i) = 0.0;
1128  }
1129  else if ((eMax / eValues(i)) > maxCond) {
1130  pexLog::TTrace<5>("lsst.ip.diffim.RegularizedKernelSolution.estimateRisk",
1131  "Truncating eValue %d; %.5e / %.5e = %.5e vs. %.5e",
1132  i, eMax, eValues(i), eMax / eValues(i), maxCond);
1133  eValues(i) = 0.0;
1134  }
1135  else {
1136  eValues(i) = 1.0 / eValues(i);
1137  }
1138  }
1139  Eigen::MatrixXd mInv = rMat * eValues.asDiagonal() * rMat.transpose();
1140 
1141  std::vector<double> lambdas = _createLambdaSteps();
1142  std::vector<double> risks;
1143  for (unsigned int i = 0; i < lambdas.size(); i++) {
1144  double l = lambdas[i];
1145  Eigen::MatrixXd mLambda = *(this->_mMat) + l * (*_hMat);
1146 
1147  try {
1148  KernelSolution::solve(mLambda, *(this->_bVec));
1149  } catch (pexExcept::Exception &e) {
1150  LSST_EXCEPT_ADD(e, "Unable to solve regularized kernel matrix");
1151  throw e;
1152  }
1153  Eigen::VectorXd term1 = (this->_aVec->transpose() * vMatvMatT * *(this->_aVec));
1154  if (term1.size() != 1)
1155  throw LSST_EXCEPT(pexExcept::Exception, "Matrix size mismatch");
1156 
1157  double term2a = (vMatvMatT * mLambda.inverse()).trace();
1158 
1159  Eigen::VectorXd term2b = (this->_aVec->transpose() * (mInv * *(this->_bVec)));
1160  if (term2b.size() != 1)
1161  throw LSST_EXCEPT(pexExcept::Exception, "Matrix size mismatch");
1162 
1163  double risk = term1(0) + 2 * (term2a - term2b(0));
1164  pexLog::TTrace<6>("lsst.ip.diffim.RegularizedKernelSolution.estimateRisk",
1165  "Lambda = %.3f, Risk = %.5e",
1166  l, risk);
1167  pexLog::TTrace<7>("lsst.ip.diffim.RegularizedKernelSolution.estimateRisk",
1168  "%.5e + 2 * (%.5e - %.5e)",
1169  term1(0), term2a, term2b(0));
1170  risks.push_back(risk);
1171  }
1172  std::vector<double>::iterator it = min_element(risks.begin(), risks.end());
1173  int index = distance(risks.begin(), it);
1174  pexLog::TTrace<5>("lsst.ip.diffim.RegularizedKernelSolution.estimateRisk",
1175  "Minimum Risk = %.3e at lambda = %.3e", risks[index], lambdas[index]);
1176 
1177  return lambdas[index];
1178 
1179  }
1180 
1181  template <typename InputT>
1182  boost::shared_ptr<Eigen::MatrixXd> RegularizedKernelSolution<InputT>::getM(bool includeHmat) {
1183  if (includeHmat == true) {
1184  return (boost::shared_ptr<Eigen::MatrixXd>(
1185  new Eigen::MatrixXd(*(this->_mMat) + _lambda * (*_hMat))
1186  ));
1187  }
1188  else {
1189  return this->_mMat;
1190  }
1191  }
1192 
1193  template <typename InputT>
1195 
1196  pexLog::TTrace<5>("lsst.ip.diffim.RegularizedKernelSolution.solve",
1197  "cMat is %d x %d; vVec is %d; iVec is %d; hMat is %d x %d",
1198  (*this->_cMat).rows(), (*this->_cMat).cols(), (*this->_ivVec).size(),
1199  (*this->_iVec).size(), (*_hMat).rows(), (*_hMat).cols());
1200 
1201  if (DEBUG_MATRIX2) {
1202  std::cout << "ID: " << (this->_id) << std::endl;
1203  std::cout << "C:" << std::endl;
1204  std::cout << (*this->_cMat) << std::endl;
1205  std::cout << "Sigma^{-1}:" << std::endl;
1206  std::cout << Eigen::MatrixXd((*this->_ivVec).asDiagonal()) << std::endl;
1207  std::cout << "Y:" << std::endl;
1208  std::cout << (*this->_iVec) << std::endl;
1209  std::cout << "H:" << std::endl;
1210  std::cout << (*_hMat) << std::endl;
1211  }
1212 
1213 
1214  this->_mMat.reset(
1215  new Eigen::MatrixXd(this->_cMat->transpose() * this->_ivVec->asDiagonal() * *(this->_cMat))
1216  );
1217  this->_bVec.reset(
1218  new Eigen::VectorXd(this->_cMat->transpose() * this->_ivVec->asDiagonal() * *(this->_iVec))
1219  );
1220 
1221 
1222  /* See N.R. 18.5
1223 
1224  Matrix equation to solve is Y = C a + N
1225  Y = vectorized version of I (I = image to not convolve)
1226  C_i = K_i (x) R (R = image to convolve)
1227  a = kernel coefficients
1228  N = noise
1229 
1230  If we reweight everything by the inverse square root of the noise
1231  covariance, we get a linear model with the identity matrix for
1232  the noise. The problem can then be solved using least squares,
1233  with the normal equations
1234 
1235  C^T Y = C^T C a
1236 
1237  or
1238 
1239  b = M a
1240 
1241  with
1242 
1243  b = C^T Y
1244  M = C^T C
1245  a = (C^T C)^{-1} C^T Y
1246 
1247 
1248  We can regularize the least square problem
1249 
1250  Y = C a + lambda a^T H a (+ N, which can be ignored)
1251 
1252  or the normal equations
1253 
1254  (C^T C + lambda H) a = C^T Y
1255 
1256 
1257  The solution to the regularization of the least squares problem is
1258 
1259  a = (C^T C + lambda H)^{-1} C^T Y
1260 
1261  The approximation to Y is
1262 
1263  C a = C (C^T C + lambda H)^{-1} C^T Y
1264 
1265  with smoothing matrix
1266 
1267  S = C (C^T C + lambda H)^{-1} C^T
1268 
1269  */
1270 
1271  std::string lambdaType = _policy.getString("lambdaType");
1272  if (lambdaType == "absolute") {
1273  _lambda = _policy.getDouble("lambdaValue");
1274  }
1275  else if (lambdaType == "relative") {
1276  _lambda = this->_mMat->trace() / this->_hMat->trace();
1277  _lambda *= _policy.getDouble("lambdaScaling");
1278  }
1279  else if (lambdaType == "minimizeBiasedRisk") {
1280  double tol = _policy.getDouble("maxConditionNumber");
1281  _lambda = estimateRisk(tol);
1282  }
1283  else if (lambdaType == "minimizeUnbiasedRisk") {
1284  _lambda = estimateRisk(std::numeric_limits<double>::max());
1285  }
1286  else {
1287  throw LSST_EXCEPT(pexExcept::Exception, "lambdaType in Policy not recognized");
1288  }
1289 
1290  pexLog::TTrace<5>("lsst.ip.diffim.KernelCandidate.build",
1291  "Applying kernel regularization with lambda = %.2e", _lambda);
1292 
1293 
1294  try {
1295  KernelSolution::solve(*(this->_mMat) + _lambda * (*_hMat), *(this->_bVec));
1296  } catch (pexExcept::Exception &e) {
1297  LSST_EXCEPT_ADD(e, "Unable to solve static kernel matrix");
1298  throw e;
1299  }
1300  /* Turn matrices into _kernel and _background */
1302  }
1303 
1304  template <typename InputT>
1306  std::vector<double> lambdas;
1307 
1308  std::string lambdaStepType = _policy.getString("lambdaStepType");
1309  if (lambdaStepType == "linear") {
1310  double lambdaLinMin = _policy.getDouble("lambdaLinMin");
1311  double lambdaLinMax = _policy.getDouble("lambdaLinMax");
1312  double lambdaLinStep = _policy.getDouble("lambdaLinStep");
1313  for (double l = lambdaLinMin; l <= lambdaLinMax; l += lambdaLinStep) {
1314  lambdas.push_back(l);
1315  }
1316  }
1317  else if (lambdaStepType == "log") {
1318  double lambdaLogMin = _policy.getDouble("lambdaLogMin");
1319  double lambdaLogMax = _policy.getDouble("lambdaLogMax");
1320  double lambdaLogStep = _policy.getDouble("lambdaLogStep");
1321  for (double l = lambdaLogMin; l <= lambdaLogMax; l += lambdaLogStep) {
1322  lambdas.push_back(pow(10, l));
1323  }
1324  }
1325  else {
1326  throw LSST_EXCEPT(pexExcept::Exception, "lambdaStepType in Policy not recognized");
1327  }
1328  return lambdas;
1329  }
1330 
1331  /*******************************************************************************************************/
1332 
1334  lsst::afw::math::KernelList const& basisList,
1335  lsst::afw::math::Kernel::SpatialFunctionPtr spatialKernelFunction,
1338  ) :
1339  KernelSolution(),
1340  _spatialKernelFunction(spatialKernelFunction),
1341  _constantFirstTerm(false),
1342  _kernel(),
1343  _background(background),
1344  _kSum(0.0),
1345  _policy(policy),
1346  _nbases(0),
1347  _nkt(0),
1348  _nbt(0),
1349  _nt(0) {
1350 
1351  bool isAlardLupton = _policy.getString("kernelBasisSet") == "alard-lupton";
1352  bool usePca = _policy.getBool("usePcaForSpatialKernel");
1353  if (isAlardLupton || usePca) {
1354  _constantFirstTerm = true;
1355  }
1356  this->_fitForBackground = _policy.getBool("fitForBackground");
1357 
1358  _nbases = basisList.size();
1359  _nkt = _spatialKernelFunction->getParameters().size();
1360  _nbt = _fitForBackground ? _background->getParameters().size() : 0;
1361  _nt = 0;
1362  if (_constantFirstTerm) {
1363  _nt = (_nbases - 1) * _nkt + 1 + _nbt;
1364  } else {
1365  _nt = _nbases * _nkt + _nbt;
1366  }
1367 
1368  boost::shared_ptr<Eigen::MatrixXd> mMat (new Eigen::MatrixXd(_nt, _nt));
1369  boost::shared_ptr<Eigen::VectorXd> bVec (new Eigen::VectorXd(_nt));
1370  (*mMat).setZero();
1371  (*bVec).setZero();
1372 
1373  this->_mMat = mMat;
1374  this->_bVec = bVec;
1375 
1378  );
1379 
1380  pexLog::TTrace<5>("lsst.ip.diffim.SpatialKernelSolution",
1381  "Initializing with size %d %d %d and constant first term = %s",
1382  _nkt, _nbt, _nt,
1383  _constantFirstTerm ? "true" : "false");
1384 
1385  }
1386 
1387  void SpatialKernelSolution::addConstraint(float xCenter, float yCenter,
1388  boost::shared_ptr<Eigen::MatrixXd> qMat,
1389  boost::shared_ptr<Eigen::VectorXd> wVec) {
1390 
1391  pexLog::TTrace<8>("lsst.ip.diffim.SpatialKernelSolution.addConstraint",
1392  "Adding candidate at %f, %f", xCenter, yCenter);
1393 
1394  /* Calculate P matrices */
1395  /* Pure kernel terms */
1396  Eigen::VectorXd pK(_nkt);
1397  std::vector<double> paramsK = _spatialKernelFunction->getParameters();
1398  for (int idx = 0; idx < _nkt; idx++) { paramsK[idx] = 0.0; }
1399  for (int idx = 0; idx < _nkt; idx++) {
1400  paramsK[idx] = 1.0;
1401  _spatialKernelFunction->setParameters(paramsK);
1402  pK(idx) = (*_spatialKernelFunction)(xCenter, yCenter); /* Assume things don't vary over stamp */
1403  paramsK[idx] = 0.0;
1404  }
1405  Eigen::MatrixXd pKpKt = (pK * pK.transpose());
1406 
1407  Eigen::VectorXd pB;
1408  Eigen::MatrixXd pBpBt;
1409  Eigen::MatrixXd pKpBt;
1410  if (_fitForBackground) {
1411  pB = Eigen::VectorXd(_nbt);
1412 
1413  /* Pure background terms */
1414  std::vector<double> paramsB = _background->getParameters();
1415  for (int idx = 0; idx < _nbt; idx++) { paramsB[idx] = 0.0; }
1416  for (int idx = 0; idx < _nbt; idx++) {
1417  paramsB[idx] = 1.0;
1418  _background->setParameters(paramsB);
1419  pB(idx) = (*_background)(xCenter, yCenter); /* Assume things don't vary over stamp */
1420  paramsB[idx] = 0.0;
1421  }
1422  pBpBt = (pB * pB.transpose());
1423 
1424  /* Cross terms */
1425  pKpBt = (pK * pB.transpose());
1426  }
1427 
1428  if (DEBUG_MATRIX) {
1429  std::cout << "Spatial weights" << std::endl;
1430  std::cout << "pKpKt " << pKpKt << std::endl;
1431  if (_fitForBackground) {
1432  std::cout << "pBpBt " << pBpBt << std::endl;
1433  std::cout << "pKpBt " << pKpBt << std::endl;
1434  }
1435  }
1436 
1437  if (DEBUG_MATRIX) {
1438  std::cout << "Spatial matrix inputs" << std::endl;
1439  std::cout << "M " << (*qMat) << std::endl;
1440  std::cout << "B " << (*wVec) << std::endl;
1441  }
1442 
1443  /* first index to start the spatial blocks; default=0 for non-constant first term */
1444  int m0 = 0;
1445  /* how many rows/cols to adjust the matrices/vectors; default=0 for non-constant first term */
1446  int dm = 0;
1447  /* where to start the background terms; this is always true */
1448  int mb = _nt - _nbt;
1449 
1450  if (_constantFirstTerm) {
1451  m0 = 1; /* we need to manually fill in the first (non-spatial) terms below */
1452  dm = _nkt-1; /* need to shift terms due to lack of spatial variation in first term */
1453 
1454  (*_mMat)(0, 0) += (*qMat)(0,0);
1455  for(int m2 = 1; m2 < _nbases; m2++) {
1456  (*_mMat).block(0, m2*_nkt-dm, 1, _nkt) += (*qMat)(0,m2) * pK.transpose();
1457  }
1458  (*_bVec)(0) += (*wVec)(0);
1459 
1460  if (_fitForBackground) {
1461  (*_mMat).block(0, mb, 1, _nbt) += (*qMat)(0,_nbases) * pB.transpose();
1462  }
1463  }
1464 
1465  /* Fill in the spatial blocks */
1466  for(int m1 = m0; m1 < _nbases; m1++) {
1467  /* Diagonal kernel-kernel term; only use upper triangular part of pKpKt */
1468  (*_mMat).block(m1*_nkt-dm, m1*_nkt-dm, _nkt, _nkt) +=
1469  (pKpKt * (*qMat)(m1,m1)).triangularView<Eigen::Upper>();
1470 
1471  /* Kernel-kernel terms */
1472  for(int m2 = m1+1; m2 < _nbases; m2++) {
1473  (*_mMat).block(m1*_nkt-dm, m2*_nkt-dm, _nkt, _nkt) += (*qMat)(m1,m2) * pKpKt;
1474  }
1475 
1476  if (_fitForBackground) {
1477  /* Kernel cross terms with background */
1478  (*_mMat).block(m1*_nkt-dm, mb, _nkt, _nbt) += (*qMat)(m1,_nbases) * pKpBt;
1479  }
1480 
1481  /* B vector */
1482  (*_bVec).segment(m1*_nkt-dm, _nkt) += (*wVec)(m1) * pK;
1483  }
1484 
1485  if (_fitForBackground) {
1486  /* Background-background terms only */
1487  (*_mMat).block(mb, mb, _nbt, _nbt) +=
1488  (pBpBt * (*qMat)(_nbases,_nbases)).triangularView<Eigen::Upper>();
1489  (*_bVec).segment(mb, _nbt) += (*wVec)(_nbases) * pB;
1490  }
1491 
1492  if (DEBUG_MATRIX) {
1493  std::cout << "Spatial matrix outputs" << std::endl;
1494  std::cout << "mMat " << (*_mMat) << std::endl;
1495  std::cout << "bVec " << (*_bVec) << std::endl;
1496  }
1497 
1498  }
1499 
1502  throw LSST_EXCEPT(pexExcept::Exception, "Kernel not solved; cannot return image");
1503  }
1505  new afwImage::Image<afwMath::Kernel::Pixel>(_kernel->getDimensions())
1506  );
1507  (void)_kernel->computeImage(*image, false, pos[0], pos[1]);
1508  return image;
1509  }
1510 
1512  /* Fill in the other half of mMat */
1513  for (int i = 0; i < _nt; i++) {
1514  for (int j = i+1; j < _nt; j++) {
1515  (*_mMat)(j,i) = (*_mMat)(i,j);
1516  }
1517  }
1518 
1519  try {
1521  } catch (pexExcept::Exception &e) {
1522  LSST_EXCEPT_ADD(e, "Unable to solve spatial kernel matrix");
1523  throw e;
1524  }
1525  /* Turn matrices into _kernel and _background */
1526  _setKernel();
1527  }
1528 
1532  throw LSST_EXCEPT(pexExcept::Exception, "Kernel not solved; cannot return solution");
1533  }
1534 
1535  return std::make_pair(_kernel, _background);
1536  }
1537 
1539  /* Report on condition number */
1540  double cNumber = this->getConditionNumber(EIGENVALUE);
1541 
1542  if (_nkt == 1) {
1543  /* Not spatially varying; this fork is a specialization for convolution speed--up */
1544 
1545  /* Set the basis coefficients */
1546  std::vector<double> kCoeffs(_nbases);
1547  for (int i = 0; i < _nbases; i++) {
1548  if (std::isnan((*_aVec)(i))) {
1549  throw LSST_EXCEPT(
1551  str(boost::format(
1552  "I. Unable to determine spatial kernel solution %d (nan). Condition number = %.3e") % i % cNumber));
1553  }
1554  kCoeffs[i] = (*_aVec)(i);
1555  }
1556  lsst::afw::math::KernelList basisList =
1557  boost::dynamic_pointer_cast<afwMath::LinearCombinationKernel>(_kernel)->getKernelList();
1558  _kernel.reset(
1559  new afwMath::LinearCombinationKernel(basisList, kCoeffs)
1560  );
1561  }
1562  else {
1563 
1564  /* Set the kernel coefficients */
1565  std::vector<std::vector<double> > kCoeffs;
1566  kCoeffs.reserve(_nbases);
1567  for (int i = 0, idx = 0; i < _nbases; i++) {
1568  kCoeffs.push_back(std::vector<double>(_nkt));
1569 
1570  /* Deal with the possibility the first term doesn't vary spatially */
1571  if ((i == 0) && (_constantFirstTerm)) {
1572  if (std::isnan((*_aVec)(idx))) {
1573  throw LSST_EXCEPT(
1575  str(boost::format(
1576  "II. Unable to determine spatial kernel solution %d (nan). Condition number = %.3e") % idx % cNumber));
1577  }
1578  kCoeffs[i][0] = (*_aVec)(idx++);
1579  }
1580  else {
1581  for (int j = 0; j < _nkt; j++) {
1582  if (std::isnan((*_aVec)(idx))) {
1583  throw LSST_EXCEPT(
1585  str(boost::format(
1586  "III. Unable to determine spatial kernel solution %d (nan). Condition number = %.3e") % idx % cNumber));
1587  }
1588  kCoeffs[i][j] = (*_aVec)(idx++);
1589  }
1590  }
1591  }
1592  _kernel->setSpatialParameters(kCoeffs);
1593  }
1594 
1595  /* Set kernel Sum */
1596  ImageT::Ptr image (new ImageT(_kernel->getDimensions()));
1597  _kSum = _kernel->computeImage(*image, false);
1598 
1599  /* Set the background coefficients */
1600  std::vector<double> bgCoeffs(_fitForBackground ? _nbt : 1);
1601  if (_fitForBackground) {
1602  for (int i = 0; i < _nbt; i++) {
1603  int idx = _nt - _nbt + i;
1604  if (std::isnan((*_aVec)(idx))) {
1606  str(boost::format(
1607  "Unable to determine spatial background solution %d (nan)") % (idx)));
1608  }
1609  bgCoeffs[i] = (*_aVec)(idx);
1610  }
1611  }
1612  else {
1613  bgCoeffs[0] = 0.;
1614  }
1615  _background->setParameters(bgCoeffs);
1616  }
1617 
1618 /***********************************************************************************************************/
1619 //
1620 // Explicit instantiations
1621 //
1622  typedef float InputT;
1623 
1624  template class StaticKernelSolution<InputT>;
1625  template class MaskedKernelSolution<InputT>;
1626  template class RegularizedKernelSolution<InputT>;
1627 
1628 }}} // end of namespace lsst::ip::diffim
An include file to include the public header files for lsst::afw::math.
void _setKernel()
Set kernel after solution.
double getValue(Property const prop=NOTHING) const
Return the value of the desired property (if specified in the constructor)
Definition: Statistics.cc:950
void flattenArray(Footprint const &fp, ndarray::Array< T, N, C > const &src, ndarray::Array< U, N-1, D > const &dest, lsst::afw::geom::Point2I const &xy0=lsst::afw::geom::Point2I())
Flatten the first two dimensions of an array.
boost::uint16_t MaskPixel
An include file to include the header files for lsst::afw::geom.
boost::shared_ptr< lsst::afw::math::Function2< double > > SpatialFunctionPtr
Definition: Kernel.h:143
Eigen matrix objects that present a view into an ndarray::Array.
int64_t _id
Eigen::MatrixXi maskToEigenMatrix(lsst::afw::image::Mask< lsst::afw::image::MaskPixel > const &mask)
Eigen3 view into an ndarray::Array.
Definition: eigen.h:232
virtual lsst::afw::image::Image< lsst::afw::math::Kernel::Pixel >::Ptr makeKernelImage()
a container for holding hierarchical configuration data in memory.
Definition: Policy.h:169
definition of the Trace messaging facilities
SpatialKernelSolution(lsst::afw::math::KernelList const &basisList, lsst::afw::math::Kernel::SpatialFunctionPtr spatialKernelFunction, lsst::afw::math::Kernel::SpatialFunctionPtr background, lsst::pex::policy::Policy policy)
lsst::afw::math::Kernel::Ptr _kernel
Derived single-object convolution kernel.
int getMinY() const
Definition: Box.h:125
A Threshold is used to pass a threshold value to detection algorithms.
Definition: Threshold.h:44
virtual lsst::afw::math::Kernel::Ptr getKernel()
KernelSolvedBy _solvedBy
Type of algorithm used to make solution.
StaticKernelSolution(lsst::afw::math::KernelList const &basisList, bool fitForBackground)
boost::shared_ptr< Footprint > Ptr
Definition: Footprint.h:75
iterator begin() const
Definition: Image.cc:325
int getMaxX() const
Definition: Box.h:128
int isnan(T t)
Definition: ieee.h:110
boost::shared_ptr< Image< PixelT > > Ptr
Definition: Image.h:418
def trace
Definition: log.py:96
iterator end() const
Return an STL compliant iterator to the end of the image.
Definition: Image.cc:331
boost::shared_ptr< Eigen::MatrixXd > getM()
boost::shared_ptr< Kernel > Ptr
Definition: Kernel.h:141
An integer coordinate rectangle.
Definition: Box.h:53
Declaration of classes to store the solution for convolution kernels.
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
void addConstraint(float xCenter, float yCenter, boost::shared_ptr< Eigen::MatrixXd > qMat, boost::shared_ptr< Eigen::VectorXd > wVec)
An include file to include the header files for lsst::afw::image.
double max
Definition: attributes.cc:218
virtual void build(lsst::afw::image::Image< InputT > const &templateImage, lsst::afw::image::Image< InputT > const &scienceImage, lsst::afw::image::Image< lsst::afw::image::VariancePixel > const &varianceEstimate)
int getHeight() const
Definition: Box.h:155
void _setKernel()
Set kernel after solution.
Use (pixels &amp; (given mask))
Definition: Threshold.h:49
double _kSum
Derived kernel sum.
static MaskPixelT getPlaneBitMask(const std::vector< std::string > &names)
Return the bitmask corresponding to a vector of plane names OR&#39;d together.
Definition: Mask.cc:859
static int _SolutionId
Unique identifier for solution.
void _setKernelUncertainty()
Not implemented.
bool _fitForBackground
Background terms included in fit.
An include file to include the header files for lsst::afw::detection.
lsst::afw::image::Image< lsst::afw::math::Kernel::Pixel > ImageT
afwMath::LinearCombinationKernel const & _kernel
virtual double getConditionNumber(ConditionNumberType conditionType)
lsst::afw::image::Image< lsst::afw::math::Kernel::Pixel >::Ptr makeKernelImage(lsst::afw::geom::Point2D const &pos)
A kernel that is a linear combination of fixed basis kernels.
Definition: Kernel.h:814
geom::Extent2I const getDimensions() const
Return the Kernel&#39;s dimensions (width, height)
Definition: Kernel.h:226
Vector< T, N > makeVector(T v1, T v2,..., T vN)
Variadic constructor for Vector.
Eigen::MatrixXd imageToEigenMatrix(lsst::afw::image::Image< PixelT > const &img)
Turns a 2-d Image into a 2-d Eigen Matrix.
void setKernelParameters(std::vector< double > const &params)
Set the kernel parameters of a spatially invariant kernel.
Definition: Kernel.h:412
boost::shared_ptr< Eigen::VectorXd > _bVec
Derived least squares B vector.
virtual void buildOrig(lsst::afw::image::Image< InputT > const &templateImage, lsst::afw::image::Image< InputT > const &scienceImage, lsst::afw::image::Image< lsst::afw::image::VariancePixel > const &varianceEstimate, lsst::afw::image::Mask< lsst::afw::image::MaskPixel > pixelMask)
lsst::afw::math::Kernel::SpatialFunctionPtr _spatialKernelFunction
Spatial function for Kernel.
int getMaxY() const
Definition: Box.h:129
int getMinX() const
Definition: Box.h:124
A set of pixels in an Image.
Definition: Footprint.h:70
int _nbt
Number of background terms.
bool _constantFirstTerm
Is the first term constant.
estimate sample minimum
Definition: Statistics.h:76
lsst::pex::policy::Policy _policy
Policy to control processing.
detail::SimpleInitializer< N > allocate(Vector< int, N > const &shape)
Create an expression that allocates uninitialized memory for an array.
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
A set of Footprints, associated with a MaskedImage.
Definition: FootprintSet.h:53
lsst::afw::math::LinearCombinationKernel::Ptr _kernel
Spatial convolution kernel.
boost::shared_ptr< Eigen::VectorXd > _aVec
Derived least squares solution matrix.
MaskedKernelSolution(lsst::afw::math::KernelList const &basisList, bool fitForBackground)
lsst::afw::math::Kernel::SpatialFunctionPtr _background
Spatial background model.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
Convolve an Image or MaskedImage with a Kernel, setting pixels of an existing output image...
RegularizedKernelSolution(lsst::afw::math::KernelList const &basisList, bool fitForBackground, boost::shared_ptr< Eigen::MatrixXd > hMat, lsst::pex::policy::Policy policy)
geom::Box2I getBBox(ImageOrigin origin=PARENT) const
Definition: Image.h:377
int _nkt
Number of kernel terms.
void writeFits(std::string const &fileName, boost::shared_ptr< lsst::daf::base::PropertySet const > metadata=boost::shared_ptr< lsst::daf::base::PropertySet >(), std::string const &mode="w") const
Write a mask to a regular FITS file.
boost::shared_ptr< Eigen::MatrixXd > _mMat
Derived least squares M matrix.
std::pair< lsst::afw::math::LinearCombinationKernel::Ptr, lsst::afw::math::Kernel::SpatialFunctionPtr > getSolutionPair()
MaskT setMaskFromFootprint(lsst::afw::image::Mask< MaskT > *mask, Footprint const &footprint, MaskT const bitmask)
OR bitmask into all the Mask&#39;s pixels that are in the Footprint.
int _nbases
Number of basis functions.
bool getBool(const std::string &name) const
Definition: Policy.h:589
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
Definition: Statistics.cc:1023
virtual void buildSingleMaskOrig(lsst::afw::image::Image< InputT > const &templateImage, lsst::afw::image::Image< InputT > const &scienceImage, lsst::afw::image::Image< lsst::afw::image::VariancePixel > const &varianceEstimate, lsst::afw::geom::Box2I maskBox)
virtual std::pair< boost::shared_ptr< lsst::afw::math::Kernel >, double > getSolutionPair()
virtual void buildWithMask(lsst::afw::image::Image< InputT > const &templateImage, lsst::afw::image::Image< InputT > const &scienceImage, lsst::afw::image::Image< lsst::afw::image::VariancePixel > const &varianceEstimate, lsst::afw::image::Mask< lsst::afw::image::MaskPixel > const &pixelMask)
geom::Point2I getXY0() const
Definition: Image.h:264
std::vector< boost::shared_ptr< Kernel > > KernelList
Definition: Kernel.h:542
const std::string getString(const std::string &name) const
Definition: Policy.h:631
#define LSST_EXCEPT_ADD(e, m)
Definition: Exception.h:51
geom::Extent2I getDimensions() const
Return the image&#39;s size; useful for passing to constructors.
Definition: Image.h:298
A class to represent a 2-dimensional array of pixels.
Definition: PSF.h:43
int getWidth() const
Definition: Box.h:154
#define DEBUG_MATRIX
#define DEBUG_MATRIX2
Image Subtraction helper functions.
boost::shared_ptr< LinearCombinationKernel > Ptr
Definition: Kernel.h:818
boost::shared_ptr< FootprintList > getFootprints()
Definition: FootprintSet.h:168
MaskT setMaskFromFootprintList(lsst::afw::image::Mask< MaskT > *mask, std::vector< boost::shared_ptr< Footprint >> const &footprints, MaskT const bitmask)
OR bitmask into all the Mask&#39;s pixels which are in the set of Footprints.
double computeImage(lsst::afw::image::Image< Pixel > &image, bool doNormalize, double x=0.0, double y=0.0) const
Compute an image (pixellized representation of the kernel) in place.
Definition: Kernel.cc:94