35 # include "Minuit2/FCNBase.h" 36 # include "Minuit2/FunctionMinimum.h" 37 # include "Minuit2/MnMigrad.h" 38 # include "Minuit2/MnMinos.h" 39 # include "Minuit2/MnPrint.h" 43 #include "Eigen/Cholesky" 46 #include "lsst/afw/detection/Footprint.h" 47 #include "lsst/afw/math/SpatialCell.h" 48 #include "lsst/afw/math/FunctionLibrary.h" 49 #include "lsst/afw/geom/Point.h" 50 #include "lsst/afw/geom/Box.h" 58 namespace afwMath = lsst::afw::math;
62 namespace algorithms {
66 int const WARP_BUFFER(1);
67 std::string
const WARP_ALGORITHM(
"lanczos5");
71 template<
typename PixelT>
72 class SetPcaImageVisitor :
public afwMath::CandidateVisitor {
74 typedef afwImage::MaskedImage<PixelT> MaskedImageT;
75 typedef afwImage::Exposure<PixelT> ExposureT;
77 explicit SetPcaImageVisitor(
78 PsfImagePca<MaskedImageT> *imagePca,
79 unsigned int const mask=0x0
81 afwMath::CandidateVisitor(),
88 void processCandidate(afwMath::SpatialCellCandidate *candidate) {
89 PsfCandidate<PixelT> *imCandidate =
dynamic_cast<PsfCandidate<PixelT> *
>(candidate);
90 if (imCandidate == NULL) {
91 throw LSST_EXCEPT(lsst::pex::exceptions::LogicError,
92 "Failed to cast SpatialCellCandidate to PsfCandidate");
96 std::shared_ptr<MaskedImageT> im = imCandidate->getOffsetImage(WARP_ALGORITHM,
104 afwMath::StatisticsControl sctrl;
105 sctrl.setNanSafe(
false);
107 if (!std::isfinite(afwMath::makeStatistics(*im->getImage(),
108 afwMath::MAX, sctrl).getValue())) {
109 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
110 str(boost::format(
"Image at %d, %d contains NaN")
111 % imCandidate->getXCenter() % imCandidate->getYCenter()));
114 if (!std::isfinite(afwMath::makeStatistics(*im->getVariance(),
115 afwMath::MAX, sctrl).getValue())) {
116 throw LSST_EXCEPT(lsst::pex::exceptions::RuntimeError,
117 str(boost::format(
"Variance of Image at %d, %d contains NaN")
118 % imCandidate->getXCenter() % imCandidate->getYCenter()));
121 _imagePca->addImage(im, imCandidate->getSource()->getPsfFlux());
122 }
catch(lsst::pex::exceptions::LengthError &) {
127 PsfImagePca<MaskedImageT> *_imagePca;
132 template<
typename PixelT>
133 class countVisitor :
public afwMath::CandidateVisitor {
134 typedef afwImage::MaskedImage<PixelT> MaskedImage;
135 typedef afwImage::Exposure<PixelT> Exposure;
137 explicit countVisitor() : afwMath::CandidateVisitor(), _n(0) {}
144 void processCandidate(afwMath::SpatialCellCandidate *candidate) {
145 PsfCandidate<PixelT> *imCandidate =
dynamic_cast<PsfCandidate<PixelT> *
>(candidate);
146 if (imCandidate == NULL) {
147 throw LSST_EXCEPT(lsst::pex::exceptions::LogicError,
148 "Failed to cast SpatialCellCandidate to PsfCandidate");
152 imCandidate->getMaskedImage();
153 }
catch(lsst::pex::exceptions::LengthError &) {
161 double getN()
const {
return _n; }
172 template<
typename ImageT>
173 std::vector<std::shared_ptr<ImageT>> offsetKernel(
174 afwMath::LinearCombinationKernel
const& kernel,
178 afwMath::KernelList kernels = kernel.getKernelList();
179 unsigned int const nKernel = kernels.size();
180 std::vector<std::shared_ptr<ImageT>> kernelImages(nKernel);
182 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
183 "Kernel has no components");
186 ImageT scratch(kernel.getDimensions());
187 for (
unsigned int i = 0; i != nKernel; ++i) {
188 kernels[i]->computeImage(scratch,
false);
189 kernelImages[i] = afwMath::offsetImage(scratch, dx, dy, WARP_ALGORITHM, WARP_BUFFER);
205 template<
typename PixelT>
207 afwMath::SpatialCellSet
const& psfCells,
208 lsst::afw::geom::Extent2I
const& dims,
210 int const nEigenComponents,
211 int const spatialOrder,
213 int const nStarPerCell,
214 bool const constantWeight,
219 typedef typename afwImage::MaskedImage<PixelT> MaskedImageT;
220 typedef typename afwImage::Exposure<PixelT> ExposureT;
236 SetPcaImageVisitor<PixelT> importStarVisitor(&imagePca);
237 bool const ignoreExceptions =
true;
238 psfCells.visitCandidates(&importStarVisitor, nStarPerCell, ignoreExceptions);
247 double deltaLim = 10.0;
248 lsst::afw::image::MaskPixel
const BAD = afwImage::Mask<>::getPlaneBitMask(
"BAD");
249 lsst::afw::image::MaskPixel
const CR = afwImage::Mask<>::getPlaneBitMask(
"CR");
250 lsst::afw::image::MaskPixel
const INTRP = afwImage::Mask<>::getPlaneBitMask(
"INTRP");
252 for (
int i = 0; i != niter; ++i) {
253 int const ncomp = (i == 0) ? 0 :
254 ((nEigenComponents == 0) ? imagePca.getEigenImages().size() : nEigenComponents);
255 double delta = imagePca.updateBadPixels(BAD | CR | INTRP, ncomp);
256 if (i > 0 && delta < deltaLim) {
263 std::vector<std::shared_ptr<MaskedImageT>> eigenImages = imagePca.getEigenImages();
264 std::vector<double> eigenValues = imagePca.getEigenValues();
265 int const nEigen =
static_cast<int>(eigenValues.size());
267 int const ncomp = (nEigenComponents <= 0 || nEigen < nEigenComponents) ? nEigen : nEigenComponents;
273 for (
int k = 0; k != ncomp; ++k) {
274 ImageT
const& im = *eigenImages[k]->getImage();
277 if (bkg_border > im.getWidth()) {
278 bkg_border = im.getWidth() / 2;
280 if (bkg_border > im.getHeight()) {
281 bkg_border = im.getHeight() / 2;
286 for (
int i = 0; i != bkg_border; ++i) {
287 typename ImageT::const_x_iterator
288 ptrB = im.row_begin(i), ptrT = im.row_begin(im.getHeight() - i - 1);
289 for (
int j = 0; j != im.getWidth(); ++j, ++ptrB, ++ptrT) {
290 sum += *ptrB + *ptrT;
293 for (
int i = bkg_border; i < im.getHeight() - bkg_border; ++i) {
295 typename ImageT::const_x_iterator
296 ptrL = im.row_begin(i), ptrR = im.row_begin(i) + im.getWidth() - bkg_border;
297 for (
int j = 0; j != bkg_border; ++j, ++ptrL, ++ptrR) {
298 sum += *ptrL + *ptrR;
301 sum /= 2*(bkg_border*im.getWidth() + bkg_border*(im.getHeight() - 2*bkg_border));
303 *eigenImages[k] -= sum;
309 afwMath::KernelList kernelList;
310 std::vector<afwMath::Kernel::SpatialFunctionPtr> spatialFunctionList;
311 afwGeom::Box2D
const range = afwGeom::Box2D(afwGeom::Point2D(xy0), afwGeom::Extent2D(dims));
313 for (
int i = 0; i != ncomp; ++i) {
318 ImageT&
image = *eigenImages[i]->getImage();
319 double sum = std::accumulate(image.begin(
true), image.end(
true), 0.0);
323 for (
typename ImageT::fast_iterator ptr0 = eigenImages[0]->getImage()->begin(
true),
324 ptr1 = image.begin(
true), end = image.end(
true); ptr1 != end; ++ptr0, ++ptr1) {
325 *ptr1 = *ptr1 / sum - *ptr0;
330 kernelList.push_back(std::shared_ptr<afwMath::Kernel>(
new afwMath::FixedKernel(
334 afwMath::Kernel::SpatialFunctionPtr
336 spatialFunction(
new afwMath::Chebyshev1Function2<double>(spatialOrder, range));
337 spatialFunction->setParameter(0, 1.0);
338 spatialFunctionList.push_back(spatialFunction);
341 std::shared_ptr<afwMath::LinearCombinationKernel>
342 psf(
new afwMath::LinearCombinationKernel(kernelList, spatialFunctionList));
344 return std::make_pair(psf, eigenValues);
351 template<
typename PixelT>
353 int const nStarPerCell)
355 countVisitor<PixelT> counter;
356 psfCells.visitCandidates(&counter, nStarPerCell);
358 return counter.getN();
368 template<
typename ModelImageT,
typename DataImageT>
369 std::pair<double, double>
370 fitKernel(ModelImageT
const& mImage,
371 DataImageT
const& data,
373 bool detected =
true,
376 assert(data.getDimensions() == mImage.getDimensions());
378 int const DETECTED = afwImage::Mask<>::getPlaneBitMask(
"DETECTED");
379 int const BAD = afwImage::Mask<>::getPlaneBitMask(
"CR") | afwImage::Mask<>::getPlaneBitMask(
"BAD");
381 double sumMM = 0.0, sumMD = 0.0, sumDD = 0.0;
383 for (
int y = 0; y != data.getHeight(); ++y) {
384 typename ModelImageT::x_iterator mptr = mImage.row_begin(y);
385 for (
typename DataImageT::x_iterator ptr = data.row_begin(y), end = data.row_end(y);
386 ptr != end; ++ptr, ++mptr) {
387 double const m = (*mptr)[0];
388 double const d = ptr.image();
389 double const var = ptr.variance() + lambda*d;
390 if (detected && !(ptr.mask() & DETECTED)) {
393 if (ptr.mask() & BAD) {
397 double const iVar = 1.0/var;
407 throw LSST_EXCEPT(lsst::pex::exceptions::RangeError,
"No good pixels");
410 throw LSST_EXCEPT(lsst::pex::exceptions::RangeError,
"sum(data*data)/var == 0");
413 double const amp = sumMD/sumMM;
414 double const chi2 = (sumDD - 2*amp*sumMD + amp*amp*sumMM)/(npix - 1);
421 int y = data.getHeight()/2;
422 int x = data.getWidth()/2;
425 for (
int ii = -hsize; ii <= hsize; ++ii) {
426 for (
int jj = -hsize; jj <= hsize; ++jj) {
427 printf(
"%7.1f ", data.at(x + jj, y - ii).image());
430 for (
int jj = -hsize; jj <= hsize; ++jj) {
431 printf(
"%7.1f ", amp*(*(mImage.at(x + jj, y - ii)))[0]);
435 printf(
"%g %.1f\n", amp, chi2);
439 return std::make_pair(chi2, amp);
448 template<
typename PixelT>
451 typedef afwImage::MaskedImage<PixelT> MaskedImage;
452 typedef afwImage::Exposure<PixelT> Exposure;
459 afwMath::CandidateVisitor(),
460 _chi2(0.0), _kernel(kernel), _lambda(lambda),
461 _kImage(
std::shared_ptr<KImage>(new KImage(kernel.getDimensions()))) {
471 if (imCandidate == NULL) {
472 throw LSST_EXCEPT(lsst::pex::exceptions::LogicError,
473 "Failed to cast SpatialCellCandidate to PsfCandidate");
476 double const xcen = imCandidate->
getSource()->getX();
477 double const ycen = imCandidate->
getSource()->getY();
479 _kernel.computeImage(*_kImage,
true, xcen, ycen);
480 std::shared_ptr<MaskedImage const> data;
483 }
catch(lsst::pex::exceptions::LengthError &) {
488 std::pair<double, double> result = fitKernel(*_kImage, *data, _lambda,
false,
491 double dchi2 = result.first;
492 double const amp = result.second;
494 imCandidate->setChi2(dchi2);
498 }
catch(lsst::pex::exceptions::RangeError &e) {
499 imCandidate->setStatus(afwMath::SpatialCellCandidate::BAD);
500 imCandidate->setChi2(std::numeric_limits<double>::quiet_NaN());
501 imCandidate->
setAmplitude(std::numeric_limits<double>::quiet_NaN());
509 double mutable _chi2;
510 afwMath::Kernel
const& _kernel;
512 std::shared_ptr<KImage>
mutable _kImage;
521 std::vector<double>
const& coeffs
524 int const nComponents = kernel->getNKernelParameters();
525 int const nSpatialParams = kernel->getNSpatialParameters();
527 assert (nComponents*nSpatialParams == static_cast<long>(coeffs.size()));
529 std::vector<std::vector<double> > kCoeffs;
530 kCoeffs.reserve(nComponents);
531 for (
int i = 0; i != nComponents; ++i) {
532 kCoeffs.push_back(std::vector<double>(nSpatialParams));
533 std::copy(coeffs.begin() + i*nSpatialParams,
534 coeffs.begin() + (i + 1)*nSpatialParams, kCoeffs[i].begin());
537 kernel->setSpatialParameters(kCoeffs);
545 Eigen::VectorXd
const& vec
548 int const nComponents = kernel->getNKernelParameters();
549 int const nSpatialParams = kernel->getNSpatialParameters();
551 assert (nComponents*nSpatialParams == vec.size());
553 std::vector<std::vector<double> > kCoeffs;
554 kCoeffs.reserve(nComponents);
555 for (
int i = 0; i != nComponents; ++i) {
556 std::vector<double> spatialCoeffs(nSpatialParams);
557 for (
int j = 0; j != nSpatialParams; ++j) {
558 spatialCoeffs[j] = vec[i*nSpatialParams + j];
560 kCoeffs.push_back(spatialCoeffs);
563 kernel->setSpatialParameters(kCoeffs);
569 template<
typename PixelT>
573 afwMath::Kernel *kernel,
574 afwMath::SpatialCellSet
const& psfCells,
579 _chi2Visitor(chi2Visitor),
582 _nStarPerCell(nStarPerCell),
583 _nComponents(nComponents),
584 _nSpatialParams(nSpatialParams) {}
592 double Up()
const {
return _errorDef; }
598 _psfCells.visitCandidates(&_chi2Visitor, _nStarPerCell);
600 return _chi2Visitor.getValue();
608 afwMath::Kernel *_kernel;
609 afwMath::SpatialCellSet
const& _psfCells;
619 template<
typename PixelT>
620 std::pair<bool, double>
622 afwMath::Kernel *kernel,
623 afwMath::SpatialCellSet
const& psfCells,
624 int const nStarPerCell,
625 double const tolerance,
630 int const nComponents = kernel->getNKernelParameters();
631 int const nSpatialParams = kernel->getNSpatialParameters();
639 std::vector<double> coeffs;
640 coeffs.assign(nComponents*nSpatialParams, 0.0);
642 std::vector<double> stepSize;
643 stepSize.assign(nComponents*nSpatialParams, 100);
647 ROOT::Minuit2::MnUserParameters fitPar;
648 std::vector<std::string> paramNames;
649 paramNames.reserve(nComponents*nSpatialParams);
651 for (
int i = 0, c = 0; c != nComponents; ++c) {
653 for (
int s = 0; s != nSpatialParams; ++s, ++i) {
654 paramNames.push_back((boost::format(
"C%d:%d") % c % s).str());
655 fitPar.Add(paramNames[i].c_str(), coeffs[i], stepSize[i]);
662 MinimizeChi2<PixelT> minimizerFunc(getChi2, kernel, psfCells, nStarPerCell, nComponents, nSpatialParams);
664 double const errorDef = 1.0;
669 ROOT::Minuit2::MnMigrad migrad(minimizerFunc, fitPar);
674 ROOT::Minuit2::FunctionMinimum min =
675 migrad(maxFnCalls, tolerance/(1e-4*errorDef));
677 float minChi2 = min.Fval();
678 bool const isValid = min.IsValid() && std::isfinite(minChi2);
680 if (
true || isValid) {
681 for (
int i = 0; i != nComponents*nSpatialParams; ++i) {
682 coeffs[i] = min.UserState().Value(i);
688 #if 0 // Estimate errors; we don't really need this 689 ROOT::Minuit2::MnMinos minos(minimizerFunc, min);
690 for (
int i = 0, c = 0; c != nComponents; ++c) {
691 for (
int s = 0; s != nSpatialParams; ++s, ++i) {
692 char const *name = paramNames[i].c_str();
693 printf(
"%s %g", name, min.UserState().Value(name));
694 if (isValid && !fitPar.Parameter(fitPar.Index(name)).IsFixed()) {
695 printf(
" (%g+%g)\n", minos(i).first, minos(i).second);
705 psfCells.visitAllCandidates(&getChi2,
true);
707 return std::make_pair(isValid, minChi2);
751 template<
typename PixelT>
752 class FillABVisitor :
public afwMath::CandidateVisitor {
754 typedef afwImage::MaskedImage<PixelT> MaskedImage;
755 typedef afwImage::Exposure<PixelT> Exposure;
759 explicit FillABVisitor(afwMath::LinearCombinationKernel
const& kernel,
762 afwMath::CandidateVisitor(),
765 _nSpatialParams(_kernel.getNSpatialParameters()),
766 _nComponents(_kernel.getNKernelParameters()),
768 _A((_nComponents-1)*_nSpatialParams, (_nComponents-1)*_nSpatialParams),
769 _b((_nComponents-1)*_nSpatialParams),
770 _basisDotBasis(_nComponents, _nComponents)
772 _basisImgs.resize(_nComponents);
779 afwMath::KernelList
const& kernels = _kernel.getKernelList();
780 for (
int i = 0; i != _nComponents; ++i) {
781 _basisImgs[i] = std::shared_ptr<KImage>(
new KImage(kernels[i]->getDimensions()));
782 kernels[i]->computeImage(*_basisImgs[i],
false);
788 for (
int i = 1; i != _nComponents; ++i) {
789 for (
int j = i; j != _nComponents; ++j) {
790 _basisDotBasis(i, j) = _basisDotBasis(j, i) =
791 afwImage::innerProduct(*_basisImgs[i], *_basisImgs[j],
800 void processCandidate(afwMath::SpatialCellCandidate *candidate) {
802 if (imCandidate == NULL) {
803 throw LSST_EXCEPT(lsst::pex::exceptions::LogicError,
804 "Failed to cast SpatialCellCandidate to PsfCandidate");
807 CONST_PTR(MaskedImage) data;
809 data = imCandidate->
getMaskedImage(_kernel.getWidth(), _kernel.getHeight());
810 }
catch(lsst::pex::exceptions::LengthError &) {
813 double const xcen = imCandidate->getXCenter();
814 double const ycen = imCandidate->getYCenter();
815 double const dx = afwImage::positionToIndex(xcen,
true).second;
816 double const dy = afwImage::positionToIndex(ycen,
true).second;
830 std::pair<std::shared_ptr<afwMath::Kernel>, std::pair<double, double> > ret =
832 double const amp = ret.second.first;
835 double const var = imCandidate->
getVar();
836 double const ivar = 1/(var + _tau2);
839 std::vector<std::vector<double> > params(_nComponents);
840 for (
int ic = 1; ic != _nComponents; ++ic) {
841 params[ic] = _kernel.getSpatialFunction(ic)->getDFuncDParameters(xcen, ycen);
844 std::vector<std::shared_ptr<KImage>> basisImages = offsetKernel<KImage>(_kernel, dx, dy);
848 std::shared_ptr<Image> dataImage(
new Image(*data->getImage(),
true));
849 typename KImage::fast_iterator bPtr = basisImages[0]->begin(
true);
850 for (
typename Image::fast_iterator dPtr = dataImage->begin(
true), end = dataImage->end(
true);
851 dPtr != end; ++dPtr, ++bPtr) {
852 *dPtr = *dPtr / amp - *bPtr;
855 for (
int i = 0, ic = 1; ic != _nComponents; ++ic) {
856 double const basisDotData = afwImage::innerProduct(*basisImages[ic], *dataImage,
858 for (
int is = 0; is != _nSpatialParams; ++is, ++i) {
859 _b(i) += ivar*params[ic][is]*basisDotData;
861 for (
int j = i, jc = ic; jc != _nComponents; ++jc) {
862 for (
int js = (i == j) ? is : 0; js != _nSpatialParams; ++js, ++j) {
863 _A(i, j) += ivar*params[ic][is]*params[jc][js]*_basisDotBasis(ic, jc);
871 Eigen::MatrixXd
const& getA()
const {
return _A; }
872 Eigen::VectorXd
const& getB()
const {
return _b; }
875 afwMath::LinearCombinationKernel
const& _kernel;
877 int const _nSpatialParams;
878 int const _nComponents;
879 std::vector<std::shared_ptr<KImage>> _basisImgs;
882 Eigen::MatrixXd _basisDotBasis;
887 template<
typename PixelT>
888 class setAmplitudeVisitor :
public afwMath::CandidateVisitor {
889 typedef afwImage::MaskedImage<PixelT> MaskedImage;
890 typedef afwImage::Exposure<PixelT> Exposure;
893 void processCandidate(afwMath::SpatialCellCandidate *candidate) {
895 if (imCandidate == NULL) {
896 throw LSST_EXCEPT(lsst::pex::exceptions::LogicError,
897 "Failed to cast SpatialCellCandidate to PsfCandidate");
900 afwMath::MAX).getValue());
907 template<
typename PixelT>
908 std::pair<bool, double>
910 afwMath::Kernel *kernel,
911 afwMath::SpatialCellSet
const& psfCells,
912 bool const doNonLinearFit,
913 int const nStarPerCell,
914 double const tolerance,
918 if (doNonLinearFit) {
919 return fitSpatialKernelFromPsfCandidates<PixelT>(kernel, psfCells, nStarPerCell, tolerance);
922 double const tau = 0;
924 afwMath::LinearCombinationKernel
const* lcKernel =
925 dynamic_cast<afwMath::LinearCombinationKernel const*
>(kernel);
927 throw LSST_EXCEPT(lsst::pex::exceptions::LogicError,
928 "Failed to cast Kernel to LinearCombinationKernel while building spatial PSF model");
934 setAmplitudeVisitor<PixelT> setAmplitude;
935 psfCells.visitAllCandidates(&setAmplitude,
true);
940 FillABVisitor<PixelT> getAB(*lcKernel, tau);
944 psfCells.visitCandidates(&getAB, nStarPerCell,
true);
948 Eigen::MatrixXd
const& A = getAB.getA();
949 Eigen::VectorXd
const&
b = getAB.getB();
950 Eigen::VectorXd x0(b.size());
956 x0(0) =
b(0)/A(0, 0);
959 x0 = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
963 std::cout <<
"A " << A << std::endl;
964 std::cout <<
"b " << b.transpose() << std::endl;
965 std::cout <<
"x " << x.transpose() << std::endl;
968 for (
int j = 0; j < b.size(); ++j) {
969 for (
int i = 0; i < b.size(); ++i) {
973 img.writeFits(
"a.fits");
976 for (
int i = 0; i != 6; ++i) {
977 double xcen = 25;
double ycen = 35 + 35*i;
978 std::cout <<
"x, y " << xcen <<
" , " << ycen <<
" b " 979 << (x[3] + xcen*x[4] + ycen*x[5])/(x[0] + xcen*x[1] + ycen*x[2]) << std::endl;
985 Eigen::VectorXd x(kernel->getNKernelParameters() * kernel->getNSpatialParameters());
987 std::fill(x.data() + 1, x.data() + kernel->getNSpatialParameters(), 0.0);
988 std::copy(x0.data(), x0.data() + x0.size(), x.data() + kernel->getNSpatialParameters());
999 psfCells.visitAllCandidates(&getChi2,
true);
1001 return std::make_pair(
true, getChi2.
getValue());
1008 template<
typename MaskedImageT>
1016 if (std::isnan(x + y)) {
1017 return std::numeric_limits<double>::quiet_NaN();
1023 std::shared_ptr<afwDetection::Psf::Image> kImage = psf.computeImage(afwGeom::PointD(x, y));
1028 afwGeom::BoxI bbox = kImage->getBBox();
1030 std::shared_ptr<MaskedImageT> subData(
new MaskedImageT(*data, bbox, afwImage::PARENT,
false));
1034 double lambda = 0.0;
1039 if (std::isnan(psfFlux)) {
1040 std::pair<double, double> result = fitKernel(*kImage, *subData, lambda,
true);
1041 chi2 = result.first;
1042 amp = result.second;
1044 chi2 = std::numeric_limits<double>::quiet_NaN();
1045 amp = psfFlux/afwMath::makeStatistics(*kImage, afwMath::SUM).getValue();
1050 std::shared_ptr<typename MaskedImageT::Image>
1051 kImageF(
new typename MaskedImageT::Image(*kImage,
true));
1054 *subData->getImage() -= *kImageF;
1057 }
catch(lsst::pex::exceptions::RangeError &e) {
1058 LSST_EXCEPT_ADD(e, (boost::format(
"Object at (%.2f, %.2f)") % x % y).str());
1069 template<
typename Image>
1070 std::pair<std::vector<double>, afwMath::KernelList>
1072 afwMath::LinearCombinationKernel
const& kernel,
1074 afwGeom::Point2D
const& pos
1079 afwMath::KernelList kernels = kernel.getKernelList();
1080 int const nKernel = kernels.size();
1083 throw LSST_EXCEPT(lsst::pex::exceptions::LengthError,
1084 "Your kernel must have at least one component");
1091 std::vector<std::shared_ptr<KernelT>> kernelImages = offsetKernel<KernelT>(kernel, pos[0], pos[1]);
1092 afwGeom::BoxI bbox(kernelImages[0]->getBBox());
1093 Image
const& subImage(Image(image, bbox, afwImage::PARENT,
false));
1099 Eigen::MatrixXd A(nKernel, nKernel);
1100 Eigen::VectorXd
b(nKernel);
1102 for (
int i = 0; i != nKernel; ++i) {
1103 b(i) = afwImage::innerProduct(*kernelImages[i], *subImage.getImage());
1105 for (
int j = i; j != nKernel; ++j) {
1106 A(i, j) = A(j, i) = afwImage::innerProduct(*kernelImages[i], *kernelImages[j]);
1109 Eigen::VectorXd x(nKernel);
1112 x(0) =
b(0)/A(0, 0);
1114 x = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
1118 int const x0 = kernelImages[0]->getX0(), y0 = kernelImages[0]->getY0();
1120 afwMath::KernelList newKernels(nKernel);
1121 std::vector<double> params(nKernel);
1122 for (
int i = 0; i != nKernel; ++i) {
1123 std::shared_ptr<afwMath::Kernel> newKernel(
new afwMath::FixedKernel(*kernelImages[i]));
1124 newKernel->setCtrX(x0 + static_cast<int>(newKernel->getWidth()/2));
1125 newKernel->setCtrY(y0 + static_cast<int>(newKernel->getHeight()/2));
1128 newKernels[i] = newKernel;
1131 return std::make_pair(params, newKernels);
1141 template<
typename Image>
1142 std::pair<std::shared_ptr<afwMath::Kernel>, std::pair<double, double> >
1144 afwMath::LinearCombinationKernel
const& kernel,
1146 afwGeom::Point2D
const& pos
1149 std::pair<std::vector<double>, afwMath::KernelList>
const fit =
1151 std::vector<double> params = fit.first;
1152 afwMath::KernelList kernels = fit.second;
1153 int const nKernel = params.size();
1154 assert(kernels.size() ==
static_cast<unsigned int>(nKernel));
1157 for (
int i = 0; i != nKernel; ++i) {
1158 std::shared_ptr<afwMath::Kernel> base = kernels[i];
1159 std::shared_ptr<afwMath::FixedKernel> k = std::static_pointer_cast<afwMath::FixedKernel>(base);
1160 amp += params[i] * k->getSum();
1163 std::shared_ptr<afwMath::Kernel> outputKernel(
new afwMath::LinearCombinationKernel(kernels, params));
1165 outputKernel->setCtrX(kernels[0]->getCtrX());
1166 outputKernel->setCtrY(kernels[0]->getCtrY());
1168 return std::make_pair(outputKernel, std::make_pair(amp, chisq));
1177 typedef float Pixel;
1180 std::pair<std::shared_ptr<afwMath::LinearCombinationKernel>, std::vector<double> >
1181 createKernelFromPsfCandidates<Pixel>(afwMath::SpatialCellSet
const&, afwGeom::Extent2I
const&,
1183 int const,
bool const,
int const);
1185 int countPsfCandidates<Pixel>(afwMath::SpatialCellSet
const&,
int const);
1188 std::pair<bool, double>
1189 fitSpatialKernelFromPsfCandidates<Pixel>(afwMath::Kernel *, afwMath::SpatialCellSet
const&,
1190 int const,
double const,
double const);
1192 std::pair<bool, double>
1193 fitSpatialKernelFromPsfCandidates<Pixel>(afwMath::Kernel *, afwMath::SpatialCellSet
const&,
bool const,
1194 int const,
double const,
double const);
1197 double subtractPsf(afwDetection::Psf
const&, afwImage::MaskedImage<float> *,
double,
double,
double);
1200 std::pair<std::vector<double>, afwMath::KernelList>
1202 afwImage::MaskedImage<Pixel>
const&, afwGeom::Point2D
const&);
1205 std::pair<std::shared_ptr<afwMath::Kernel>, std::pair<double, double> >
1207 afwImage::MaskedImage<Pixel>
const&, afwGeom::Point2D
const&);
Class used by SpatialCell for spatial PSF fittig.
boost::shared_ptr< afw::image::MaskedImage< PixelT > > getOffsetImage(std::string const algorithm, unsigned int buffer) const
Return an offset version of the image of the source.
double operator()(const std::vector< double > &coeffs) const
void setAmplitude(double amplitude)
Set the best-fit amplitude.
void setErrorDef(double def)
void processCandidate(afwMath::SpatialCellCandidate *candidate)
int countPsfCandidates(lsst::afw::math::SpatialCellSet const &psfCells, int const nStarPerCell=-1)
boost::shared_ptr< afw::image::MaskedImage< PixelT > const > getMaskedImage() const
Return the image at the position of the Source, without any sub-pixel shifts to put the centre of the...
double subtractPsf(lsst::afw::detection::Psf const &psf, ImageT *data, double x, double y, double psfFlux=std::numeric_limits< double >::quiet_NaN())
MinimizeChi2(evalChi2Visitor< PixelT > &chi2Visitor, afwMath::Kernel *kernel, afwMath::SpatialCellSet const &psfCells, int nStarPerCell, int nComponents, int nSpatialParams)
virtual void analyze()
Generate eigenimages that are normalised and background-subtracted.
evalChi2Visitor(afwMath::Kernel const &kernel, double lambda)
std::pair< std::vector< double >, lsst::afw::math::KernelList > fitKernelParamsToImage(lsst::afw::math::LinearCombinationKernel const &kernel, Image const &image, lsst::afw::geom::Point2D const &pos)
std::pair< bool, double > fitSpatialKernelFromPsfCandidates(lsst::afw::math::Kernel *kernel, lsst::afw::math::SpatialCellSet const &psfCells, int const nStarPerCell=-1, double const tolerance=1e-5, double const lambda=0.0)
boost::shared_ptr< afw::table::SourceRecord > getSource() const
Return the original Source.
double getVar() const
Return the variance in use when fitting this object.
double getAmplitude() const
Return the best-fit amplitude.
A class to pass around to all our PsfCandidates to evaluate the PSF fit's X^2.
std::pair< std::shared_ptr< lsst::afw::math::LinearCombinationKernel >, std::vector< double > > createKernelFromPsfCandidates(lsst::afw::math::SpatialCellSet const &psfCells, lsst::afw::geom::Extent2I const &dims, lsst::afw::geom::Point2I const &xy0, int const nEigenComponents, int const spatialOrder, int const ksize, int const nStarPerCell=-1, bool const constantWeight=true, int const border=3)
Class used by SpatialCell for spatial PSF fittig.
Class for doing PCA on PSF stars.
void setSpatialParameters(afwMath::Kernel *kernel, std::vector< double > const &coeffs)
Fit a Kernel's spatial variability from a set of stars.
double Up() const
Error definition of the function.
afw::table::Key< double > b
std::pair< std::shared_ptr< lsst::afw::math::Kernel >, std::pair< double, double > > fitKernelToImage(lsst::afw::math::LinearCombinationKernel const &kernel, Image const &image, lsst::afw::geom::Point2D const &pos)
Class stored in SpatialCells for spatial Psf fitting.