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);
100 str(boost::format(
"Image at %d, %d contains NaN") %
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;
322 spatialFunction->setParameter(0, 1.0);
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);
367 for (
typename DataImageT::x_iterator ptr =
data.row_begin(y), end =
data.row_end(y); ptr != end;
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) {
409 printf(
"%7.1f ",
data.at(x + jj, y - ii).image());
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) {
623 paramNames.
push_back((boost::format(
"C%d:%d") % c % s).str());
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);
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;
1008 chi2 = result.first;
1009 amp = result.second;
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,
table::Key< std::string > name
afw::table::Key< double > b
#define LSST_EXCEPT_ADD(e, m)
#define LSST_EXCEPT(type,...)
Class for doing PCA on PSF stars.
Class used by SpatialCell for spatial PSF fittig.
Class used by SpatialCell for spatial PSF fittig.
void writeFits(std::string const &fileName, std::shared_ptr< lsst::daf::base::PropertySet const > metadata=std::shared_ptr< lsst::daf::base::PropertySet const >(), std::string const &mode="w") const
virtual double updateBadPixels(unsigned long mask, int const ncomp)
std::vector< double > const & getEigenValues() const
ImageList const & getEigenImages() const
static MaskPixelT getPlaneBitMask(const std::vector< std::string > &names)
CandidateVisitor()=default
unsigned int getNKernelParameters() const
int getNSpatialParameters() const
double computeImage(lsst::afw::image::Image< Pixel > &image, bool doNormalize, double x=0.0, double y=0.0) const
void setSpatialParameters(const std::vector< std::vector< double >> params)
virtual KernelList const & getKernelList() const
void setStatus(Status status)
static void setWidth(int width)
static void setHeight(int height)
void setChi2(double chi2)
void visitAllCandidates(CandidateVisitor *visitor, bool const ignoreExceptions=false)
void visitCandidates(CandidateVisitor *visitor, int const nMaxPerCell=-1, bool const ignoreExceptions=false)
double getValue(Property const prop=NOTHING) const
double operator()(const std::vector< double > &coeffs) const
MinimizeChi2(evalChi2Visitor< PixelT > &chi2Visitor, afw::math::Kernel *kernel, afw::math::SpatialCellSet const &psfCells, int nStarPerCell, int nComponents, int nSpatialParams)
double Up() const
Error definition of the function.
void setErrorDef(double def)
Class stored in SpatialCells for spatial Psf fitting.
static int getBorderWidth()
Return the number of pixels being ignored around the candidate image's edge.
void setAmplitude(double amplitude)
Set the best-fit amplitude.
std::shared_ptr< afw::table::SourceRecord > getSource() const
Return the original Source.
std::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.
virtual void analyze()
Generate eigenimages that are normalised and background-subtracted.
A class to pass around to all our PsfCandidates to evaluate the PSF fit's X^2.
evalChi2Visitor(afw::math::Kernel const &kernel, double lambda)
void processCandidate(afw::math::SpatialCellCandidate *candidate)
double innerProduct(Image1T const &lhs, Image2T const &rhs, int const border=0)
int positionToIndex(double pos)
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
std::shared_ptr< ImageT > offsetImage(ImageT const &image, float dx, float dy, std::string const &algorithmName="lanczos5", unsigned int buffer=0)
std::vector< std::shared_ptr< Kernel > > KernelList
Point< double, 2 > Point2D
std::pair< std::shared_ptr< afw::math::LinearCombinationKernel >, std::vector< double > > createKernelFromPsfCandidates(afw::math::SpatialCellSet const &psfCells, geom::Extent2I const &dims, 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)
Return a Kernel pointer and a list of eigenvalues resulting from analysing the provided SpatialCellSe...
int countPsfCandidates(afw::math::SpatialCellSet const &psfCells, int const nStarPerCell=-1)
Count the number of candidates in use.
std::pair< std::vector< double >, afw::math::KernelList > fitKernelParamsToImage(afw::math::LinearCombinationKernel const &kernel, Image const &image, geom::Point2D const &pos)
Fit a LinearCombinationKernel to an Image, allowing the coefficients of the components to vary.
std::pair< std::shared_ptr< afw::math::Kernel >, std::pair< double, double > > fitKernelToImage(afw::math::LinearCombinationKernel const &kernel, Image const &image, geom::Point2D const &pos)
Fit a LinearCombinationKernel to an Image, allowing the coefficients of the components to vary.
std::pair< bool, double > fitSpatialKernelFromPsfCandidates(afw::math::Kernel *kernel, afw::math::SpatialCellSet const &psfCells, int const nStarPerCell=-1, double const tolerance=1e-5, double const lambda=0.0)
Fit spatial kernel using full-nonlinear optimization to estimate candidate amplitudes.
void setSpatialParameters(afw::math::Kernel *kernel, std::vector< double > const &coeffs)
Fit a Kernel's spatial variability from a set of stars.
double subtractPsf(afw::detection::Psf const &psf, ImageT *data, double x, double y, double psfFlux=std::numeric_limits< double >::quiet_NaN())