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,