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"
61int const WARP_BUFFER(1);
65template <
typename PixelT>
66class 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;
122template <
typename PixelT>
123class 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; }
160template <
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);
191template <
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);
336template <
typename PixelT>
338 countVisitor<PixelT> counter;
341 return counter.getN();
351template <
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);
430template <
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];
543template <
typename PixelT>
550 _chi2Visitor(chi2Visitor),
553 _nStarPerCell(nStarPerCell),
554 _nComponents(nComponents),
555 _nSpatialParams(nSpatialParams) {}
563 double Up()
const {
return _errorDef; }
571 return _chi2Visitor.getValue();
591template <
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);
717template <
typename PixelT>
729 :
afw::math::CandidateVisitor(),
732 _nSpatialParams(_kernel.getNSpatialParameters()),
733 _nComponents(_kernel.getNKernelParameters()),
735 _A((_nComponents - 1) * _nSpatialParams, (_nComponents - 1) * _nSpatialParams),
736 _b((_nComponents - 1) * _nSpatialParams),
737 _basisDotBasis(_nComponents, _nComponents) {
738 _basisImgs.resize(_nComponents);
746 for (
int i = 0; i != _nComponents; ++i) {
748 kernels[i]->computeImage(*_basisImgs[i],
false);
754 for (
int i = 1; i != _nComponents; ++i) {
755 for (
int j = i; j != _nComponents; ++j) {
765 void processCandidate(afw::math::SpatialCellCandidate* candidate) {
766 PsfCandidate<PixelT>* imCandidate =
dynamic_cast<PsfCandidate<PixelT>*
>(candidate);
767 if (imCandidate == NULL) {
769 "Failed to cast SpatialCellCandidate to PsfCandidate");
774 data = imCandidate->getMaskedImage(_kernel.getWidth(), _kernel.getHeight());
778 double const xcen = imCandidate->getXCenter();
779 double const ycen = imCandidate->getYCenter();
784 double const amp = imCandidate->getAmplitude();
797 double const amp = ret.second.first;
800 double const var = imCandidate->getVar();
801 double const ivar = 1 / (var + _tau2);
805 for (
int ic = 1; ic != _nComponents; ++ic) {
806 params[ic] = _kernel.getSpatialFunction(ic)->getDFuncDParameters(xcen, ycen);
816 dPtr !=
end; ++dPtr, ++bPtr) {
817 *dPtr = *dPtr / amp - *bPtr;
820 for (
int i = 0, ic = 1; ic != _nComponents; ++ic) {
823 for (
int is = 0; is != _nSpatialParams; ++is, ++i) {
824 _b(i) += ivar * params[ic][is] * basisDotData;
826 for (
int j = i, jc = ic; jc != _nComponents; ++jc) {
827 for (
int js = (i == j) ? is : 0; js != _nSpatialParams; ++js, ++j) {
828 _A(i, j) += ivar * params[ic][is] * params[jc][js] * _basisDotBasis(ic, jc);
836 Eigen::MatrixXd
const& getA()
const {
return _A; }
837 Eigen::VectorXd
const& getB()
const {
return _b; }
840 afw::math::LinearCombinationKernel
const& _kernel;
842 int const _nSpatialParams;
843 int const _nComponents;
847 Eigen::MatrixXd _basisDotBasis;
851template <
typename PixelT>
852class setAmplitudeVisitor :
public afw::math::CandidateVisitor {
853 typedef afw::image::MaskedImage<PixelT> MaskedImage;
854 typedef afw::image::Exposure<PixelT> Exposure;
858 void processCandidate(afw::math::SpatialCellCandidate* candidate) {
859 PsfCandidate<PixelT>* imCandidate =
dynamic_cast<PsfCandidate<PixelT>*
>(candidate);
860 if (imCandidate == NULL) {
862 "Failed to cast SpatialCellCandidate to PsfCandidate");
864 imCandidate->setAmplitude(
875template <
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;
975template <
typename MaskedImageT>
997 new MaskedImageT(*
data,
bbox, afw::image::PARENT,
false));
1001 double lambda = 0.0;
1018 new typename MaskedImageT::Image(*kImage,
true));
1021 *subData->getImage() -= *kImageF;
1036template <
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;
1103template <
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());
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,
#define LSST_EXCEPT_ADD(e, m)
Add the current location and a message to an existing exception before rethrowing it.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Class used by SpatialCell for spatial PSF fittig.
Class used by SpatialCell for spatial PSF fittig.
A polymorphic base class for representing an image's Point Spread Function.
A class to contain the data, WCS, and other information needed to describe an image of the sky.
x_iterator fast_iterator
A fast STL compliant iterator for contiguous images N.b.
A class to represent a 2-dimensional array of pixels.
void writeFits(std::string const &fileName, daf::base::PropertySet const *metadata=nullptr, std::string const &mode="w") const
Write an image to a regular FITS file.
virtual double updateBadPixels(unsigned long mask, int const ncomp)
Update the bad pixels (i.e.
ImageList const & getEigenImages() const
Return Eigen images.
std::vector< double > const & getEigenValues() const
Return Eigen values.
Represent a 2-dimensional array of bitmask pixels.
A class to manipulate images, masks, and variance as a single object.
CandidateVisitor()=default
2-dimensional weighted sum of Chebyshev polynomials of the first kind.
A kernel created from an Image.
void setParameter(unsigned int ind, double newValue)
Set one function parameter without range checking.
Kernels are used for convolution with MaskedImages and (eventually) Images.
unsigned int getNKernelParameters() const
Return the number of kernel parameters (0 if none)
void setSpatialParameters(const std::vector< std::vector< double > > params)
Set the parameters of all spatial functions.
int getNSpatialParameters() const
Return the number of spatial parameters (0 if not spatially varying)
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.
A kernel that is a linear combination of fixed basis kernels.
virtual KernelList const & getKernelList() const
Get the fixed basis kernels.
Base class for candidate objects in a SpatialCell.
void setStatus(Status status)
Set the candidate's status.
static void setWidth(int width)
Set the width of the image that getImage should return.
static void setHeight(int height)
Set the height of the image that getImage should return.
void setChi2(double chi2)
Set the candidate's chi^2.
A collection of SpatialCells covering an entire image.
void visitAllCandidates(CandidateVisitor *visitor, bool const ignoreExceptions=false)
Call the visitor's processCandidate method for every Candidate in the SpatialCellSet.
void visitCandidates(CandidateVisitor *visitor, int const nMaxPerCell=-1, bool const ignoreExceptions=false)
Call the visitor's processCandidate method for each Candidate in the SpatialCellSet.
double getValue(Property const prop=NOTHING) const
Return the value of the desired property (if specified in the constructor)
A floating-point coordinate rectangle geometry.
An integer coordinate rectangle.
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)
Reports attempts to exceed implementation-defined length limits for some classes.
Reports errors in the logical structure of the program.
Reports when the result of an operation cannot be represented by the destination type.
Reports errors that are due to events beyond the control of the program.
Class for doing PCA on PSF stars.
double innerProduct(Image1T const &lhs, Image2T const &rhs, int const border=0)
Calculate the inner product of two images.
int positionToIndex(double pos)
Convert image position to nearest integer index.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
@ MAX
estimate sample maximum
@ SUM
find sum of pixels in the image
std::shared_ptr< ImageT > offsetImage(ImageT const &image, float dx, float dy, std::string const &algorithmName="lanczos5", unsigned int buffer=0)
Return an image offset by (dx, dy) using the specified algorithm.
std::vector< std::shared_ptr< Kernel > > KernelList
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())
g2d::python::Image< double > Image