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"
57 namespace algorithms {
61 int const WARP_BUFFER(1);
65 template <
typename PixelT>
66 class SetPcaImageVisitor :
public afw::math::CandidateVisitor {
67 typedef afw::image::Image<PixelT> ImageT;
68 typedef afw::image::MaskedImage<PixelT> MaskedImageT;
69 typedef afw::image::Exposure<PixelT> ExposureT;
72 explicit SetPcaImageVisitor(PsfImagePca<MaskedImageT>* imagePca,
73 unsigned int const mask = 0x0
75 :
afw::math::CandidateVisitor(), _imagePca(imagePca) {
80 void processCandidate(afw::math::SpatialCellCandidate* candidate) {
81 PsfCandidate<PixelT>* imCandidate =
dynamic_cast<PsfCandidate<PixelT>*
>(candidate);
82 if (imCandidate == NULL) {
84 "Failed to cast SpatialCellCandidate to PsfCandidate");
94 afw::math::StatisticsControl sctrl;
95 sctrl.setNanSafe(
false);
101 imCandidate->getXCenter() % imCandidate->getYCenter()));
106 str(
boost::format(
"Variance of Image at %d, %d contains NaN") %
107 imCandidate->getXCenter() % imCandidate->getYCenter()));
110 _imagePca->addImage(im, imCandidate->getSource()->getPsfInstFlux());
117 PsfImagePca<MaskedImageT>* _imagePca;
122 template <
typename PixelT>
123 class countVisitor :
public afw::math::CandidateVisitor {
124 typedef afw::image::MaskedImage<PixelT> MaskedImage;
125 typedef afw::image::Exposure<PixelT> Exposure;
128 explicit countVisitor() :
afw::math::CandidateVisitor(), _n(0) {}
130 void reset() { _n = 0; }
133 void processCandidate(afw::math::SpatialCellCandidate* candidate) {
134 PsfCandidate<PixelT>* imCandidate =
dynamic_cast<PsfCandidate<PixelT>*
>(candidate);
135 if (imCandidate == NULL) {
137 "Failed to cast SpatialCellCandidate to PsfCandidate");
141 imCandidate->getMaskedImage();
150 double getN()
const {
return _n; }
160 template <
typename ImageT>
162 afw::math::LinearCombinationKernel
const& kernel,
166 unsigned int const nKernel = kernels.
size();
172 ImageT scratch(kernel.getDimensions());
173 for (
unsigned int i = 0; i != nKernel; ++i) {
174 kernels[i]->computeImage(scratch,
false);
191 template <
typename PixelT>
197 int const nEigenComponents,
198 int const spatialOrder,
200 int const nStarPerCell,
201 bool const constantWeight,
220 SetPcaImageVisitor<PixelT> importStarVisitor(&imagePca);
221 bool const ignoreExceptions =
true;
222 psfCells.
visitCandidates(&importStarVisitor, nStarPerCell, ignoreExceptions);
231 double deltaLim = 10.0;
236 for (
int i = 0; i != niter; ++i) {
241 if (i > 0 && delta < deltaLim) {
250 int const nEigen =
static_cast<int>(eigenValues.
size());
252 int const ncomp = (nEigenComponents <= 0 || nEigen < nEigenComponents) ? nEigen : nEigenComponents;
258 for (
int k = 0; k != ncomp; ++k) {
259 ImageT
const& im = *eigenImages[k]->getImage();
262 if (bkg_border > im.getWidth()) {
263 bkg_border = im.getWidth() / 2;
265 if (bkg_border > im.getHeight()) {
266 bkg_border = im.getHeight() / 2;
271 for (
int i = 0; i != bkg_border; ++i) {
272 typename ImageT::const_x_iterator ptrB = im.row_begin(i),
273 ptrT = im.row_begin(im.getHeight() - i - 1);
274 for (
int j = 0; j != im.getWidth(); ++j, ++ptrB, ++ptrT) {
275 sum += *ptrB + *ptrT;
278 for (
int i = bkg_border; i < im.getHeight() - bkg_border; ++i) {
280 typename ImageT::const_x_iterator ptrL = im.row_begin(i),
281 ptrR = im.row_begin(i) + im.getWidth() - bkg_border;
282 for (
int j = 0; j != bkg_border; ++j, ++ptrL, ++ptrR) {
283 sum += *ptrL + *ptrR;
286 sum /= 2 * (bkg_border * im.getWidth() + bkg_border * (im.getHeight() - 2 * bkg_border));
288 *eigenImages[k] -= sum;
298 for (
int i = 0; i != ncomp; ++i) {
303 ImageT&
image = *eigenImages[i]->getImage();
308 for (
typename ImageT::fast_iterator ptr0 = eigenImages[0]->getImage()->begin(
true),
310 ptr1 !=
end; ++ptr0, ++ptr1) {
311 *ptr1 = *ptr1 / sum - *ptr0;
323 spatialFunctionList.
push_back(spatialFunction);
336 template <
typename PixelT>
338 countVisitor<PixelT> counter;
341 return counter.getN();
351 template <
typename ModelImageT,
typename DataImageT>
353 DataImageT
const&
data,
355 bool detected =
true,
358 assert(
data.getDimensions() == mImage.getDimensions());
363 double sumMM = 0.0, sumMD = 0.0, sumDD = 0.0;
365 for (
int y = 0;
y !=
data.getHeight(); ++
y) {
366 typename ModelImageT::x_iterator mptr = mImage.row_begin(
y);
369 double const m = (*mptr)[0];
370 double const d =
ptr.image();
371 double const var =
ptr.variance() + lambda * d;
372 if (detected && !(
ptr.mask() & DETECTED)) {
375 if (
ptr.mask() & BAD) {
379 double const iVar = 1.0 / var;
381 sumMM +=
m *
m * iVar;
382 sumMD +=
m * d * iVar;
383 sumDD += d * d * iVar;
395 double const amp = sumMD / sumMM;
396 double const chi2 = (sumDD - 2 * amp * sumMD + amp * amp * sumMM) / (npix - 1);
403 int y =
data.getHeight()/2;
404 int x =
data.getWidth()/2;
407 for (
int ii = -hsize; ii <= hsize; ++ii) {
408 for (
int jj = -hsize; jj <= hsize; ++jj) {
412 for (
int jj = -hsize; jj <= hsize; ++jj) {
413 printf(
"%7.1f ", amp*(*(mImage.at(
x + jj,
y - ii)))[0]);
417 printf(
"%g %.1f\n", amp, chi2);
430 template <
typename PixelT>
444 _kImage(
std::shared_ptr<
KImage>(new
KImage(kernel.getDimensions()))) {}
451 if (imCandidate == NULL) {
453 "Failed to cast SpatialCellCandidate to PsfCandidate");
456 double const xcen = imCandidate->
getSource()->getX();
457 double const ycen = imCandidate->
getSource()->getY();
469 fitKernel(*_kImage, *
data, _lambda,
false, imCandidate->
getSource()->getId());
471 double dchi2 =
result.first;
472 double const amp =
result.second;
489 double mutable _chi2;
504 assert(nComponents * nSpatialParams ==
static_cast<long>(coeffs.
size()));
508 for (
int i = 0; i != nComponents; ++i) {
525 assert(nComponents * nSpatialParams == vec.size());
529 for (
int i = 0; i != nComponents; ++i) {
531 for (
int j = 0; j != nSpatialParams; ++j) {
532 spatialCoeffs[j] = vec[i * nSpatialParams + j];
543 template <
typename PixelT>
550 _chi2Visitor(chi2Visitor),
553 _nStarPerCell(nStarPerCell),
554 _nComponents(nComponents),
555 _nSpatialParams(nSpatialParams) {}
563 double Up()
const {
return _errorDef; }
571 return _chi2Visitor.getValue();
591 template <
typename PixelT>
595 int const nStarPerCell,
596 double const tolerance,
609 coeffs.
assign(nComponents * nSpatialParams, 0.0);
612 stepSize.
assign(nComponents * nSpatialParams, 100);
616 ROOT::Minuit2::MnUserParameters fitPar;
618 paramNames.
reserve(nComponents * nSpatialParams);
620 for (
int i = 0, c = 0; c != nComponents; ++c) {
622 for (
int s = 0; s != nSpatialParams; ++s, ++i) {
624 fitPar.Add(paramNames[i].c_str(), coeffs[i], stepSize[i]);
631 MinimizeChi2<PixelT> minimizerFunc(getChi2, kernel, psfCells, nStarPerCell, nComponents, nSpatialParams);
633 double const errorDef = 1.0;
638 ROOT::Minuit2::MnMigrad migrad(minimizerFunc, fitPar);
643 ROOT::Minuit2::FunctionMinimum
min =
644 migrad(maxFnCalls, tolerance / (1e-4 * errorDef));
646 float minChi2 =
min.Fval();
650 for (
int i = 0; i != nComponents * nSpatialParams; ++i) {
651 coeffs[i] =
min.UserState().Value(i);
657 #if 0 // Estimate errors; we don't really need this
658 ROOT::Minuit2::MnMinos minos(minimizerFunc,
min);
659 for (
int i = 0, c = 0; c != nComponents; ++c) {
660 for (
int s = 0; s != nSpatialParams; ++s, ++i) {
661 char const *
name = paramNames[i].c_str();
662 printf(
"%s %g",
name,
min.UserState().Value(
name));
663 if (
isValid && !fitPar.Parameter(fitPar.Index(
name)).IsFixed()) {
664 printf(
" (%g+%g)\n", minos(i).
first, minos(i).
second);
720 template <
typename PixelT>
732 :
afw::math::CandidateVisitor(),
735 _nSpatialParams(_kernel.getNSpatialParameters()),
736 _nComponents(_kernel.getNKernelParameters()),
738 _A((_nComponents - 1) * _nSpatialParams, (_nComponents - 1) * _nSpatialParams),
739 _b((_nComponents - 1) * _nSpatialParams),
740 _basisDotBasis(_nComponents, _nComponents) {
741 _basisImgs.resize(_nComponents);
749 for (
int i = 0; i != _nComponents; ++i) {
751 kernels[i]->computeImage(*_basisImgs[i],
false);
757 for (
int i = 1; i != _nComponents; ++i) {
758 for (
int j = i; j != _nComponents; ++j) {
768 void processCandidate(afw::math::SpatialCellCandidate* candidate) {
769 PsfCandidate<PixelT>* imCandidate =
dynamic_cast<PsfCandidate<PixelT>*
>(candidate);
770 if (imCandidate == NULL) {
772 "Failed to cast SpatialCellCandidate to PsfCandidate");
777 data = imCandidate->getMaskedImage(_kernel.getWidth(), _kernel.getHeight());
781 double const xcen = imCandidate->getXCenter();
782 double const ycen = imCandidate->getYCenter();
787 double const amp = imCandidate->getAmplitude();
800 double const amp = ret.second.first;
803 double const var = imCandidate->getVar();
804 double const ivar = 1 / (var + _tau2);
808 for (
int ic = 1; ic != _nComponents; ++ic) {
809 params[ic] = _kernel.getSpatialFunction(ic)->getDFuncDParameters(xcen, ycen);
819 dPtr !=
end; ++dPtr, ++bPtr) {
820 *dPtr = *dPtr / amp - *bPtr;
823 for (
int i = 0, ic = 1; ic != _nComponents; ++ic) {
826 for (
int is = 0; is != _nSpatialParams; ++is, ++i) {
827 _b(i) += ivar * params[ic][is] * basisDotData;
829 for (
int j = i, jc = ic; jc != _nComponents; ++jc) {
830 for (
int js = (i == j) ? is : 0; js != _nSpatialParams; ++js, ++j) {
831 _A(i, j) += ivar * params[ic][is] * params[jc][js] * _basisDotBasis(ic, jc);
839 Eigen::MatrixXd
const& getA()
const {
return _A; }
840 Eigen::VectorXd
const& getB()
const {
return _b; }
843 afw::math::LinearCombinationKernel
const& _kernel;
845 int const _nSpatialParams;
846 int const _nComponents;
850 Eigen::MatrixXd _basisDotBasis;
854 template <
typename PixelT>
855 class setAmplitudeVisitor :
public afw::math::CandidateVisitor {
856 typedef afw::image::MaskedImage<PixelT> MaskedImage;
857 typedef afw::image::Exposure<PixelT> Exposure;
861 void processCandidate(afw::math::SpatialCellCandidate* candidate) {
862 PsfCandidate<PixelT>* imCandidate =
dynamic_cast<PsfCandidate<PixelT>*
>(candidate);
863 if (imCandidate == NULL) {
865 "Failed to cast SpatialCellCandidate to PsfCandidate");
867 imCandidate->setAmplitude(
875 template <
typename PixelT>
879 bool const doNonLinearFit,
880 int const nStarPerCell,
881 double const tolerance,
884 if (doNonLinearFit) {
885 return fitSpatialKernelFromPsfCandidates<PixelT>(kernel, psfCells, nStarPerCell, tolerance);
888 double const tau = 0;
895 "Failed to cast Kernel to LinearCombinationKernel while building spatial PSF model");
901 setAmplitudeVisitor<PixelT> setAmplitude;
907 FillABVisitor<PixelT> getAB(*lcKernel, tau);
915 Eigen::MatrixXd
const& A = getAB.getA();
916 Eigen::VectorXd
const&
b = getAB.getB();
917 Eigen::VectorXd x0(
b.size());
923 x0(0) =
b(0) / A(0, 0);
926 x0 = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(
b);
935 for (
int j = 0; j <
b.size(); ++j) {
936 for (
int i = 0; i <
b.size(); ++i) {
943 for (
int i = 0; i != 6; ++i) {
944 double xcen = 25;
double ycen = 35 + 35*i;
945 std::cout <<
"x, y " << xcen <<
" , " << ycen <<
" b "
946 << (
x[3] + xcen*
x[4] + ycen*
x[5])/(
x[0] + xcen*
x[1] + ycen*
x[2]) <<
std::endl;
975 template <
typename MaskedImageT>
1001 double lambda = 0.0;
1018 new typename MaskedImageT::Image(*kImage,
true));
1021 *subData->getImage() -= *kImageF;
1036 template <
typename Image>
1045 int const nKernel = kernels.
size();
1063 Eigen::MatrixXd A(nKernel, nKernel);
1064 Eigen::VectorXd
b(nKernel);
1066 for (
int i = 0; i != nKernel; ++i) {
1069 for (
int j = i; j != nKernel; ++j) {
1073 Eigen::VectorXd
x(nKernel);
1076 x(0) =
b(0) / A(0, 0);
1078 x = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(
b);
1086 for (
int i = 0; i != nKernel; ++i) {
1088 newKernel->setCtr(xy0 + newKernel->getDimensions() / 2);
1091 newKernels[i] = newKernel;
1103 template <
typename Image>
1113 int const nKernel = params.
size();
1114 assert(kernels.
size() ==
static_cast<unsigned int>(nKernel));
1117 for (
int i = 0; i != nKernel; ++i) {
1120 amp += params[i] * k->getSum();
1125 outputKernel->setCtr(kernels[0]->getCtr());
1135 typedef float Pixel;
1139 geom::Point2I const&,
int const,
int const,
int const,
int const,
1140 bool const,
int const);
1145 int const,
double const,
1149 bool const,
int const,
double const,