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()->getPsfFlux());
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) {
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) {
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();
309 ptr1 = image.begin(
true),
end = image.end(
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>
440 : afw::math::CandidateVisitor(),
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();
459 _kernel.computeImage(*_kImage,
true, xcen, ycen);
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; }
569 _psfCells.visitCandidates(&_chi2Visitor, _nStarPerCell);
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();
649 if (
true || isValid) {
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>
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) {
770 if (imCandidate == NULL) {
772 "Failed to cast SpatialCellCandidate to PsfCandidate");
781 double const xcen = imCandidate->
getXCenter();
782 double const ycen = imCandidate->
getYCenter();
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);
817 typename KImage::fast_iterator bPtr = basisImages[0]->
begin(
true);
818 for (
typename Image::fast_iterator dPtr = dataImage->begin(
true),
end = dataImage->end(
true);
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; }
845 int const _nSpatialParams;
846 int const _nComponents;
850 Eigen::MatrixXd _basisDotBasis;
854 template <
typename PixelT>
863 if (imCandidate == NULL) {
865 "Failed to cast SpatialCellCandidate to PsfCandidate");
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) {
940 img.writeFits(
"a.fits");
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;
1021 *subData->getImage() -= *kImageF;
1025 LSST_EXCEPT_ADD(e, (boost::format(
"Object at (%.2f, %.2f)") % x % y).str());
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);
1082 int const x0 = kernelImages[0]->getX0(), y0 = kernelImages[0]->getY0();
1086 for (
int i = 0; i != nKernel; ++i) {
1088 newKernel->setCtrX(x0 + static_cast<int>(newKernel->getWidth() / 2));
1089 newKernel->setCtrY(y0 + static_cast<int>(newKernel->getHeight() / 2));
1092 newKernels[i] = newKernel;
1104 template <
typename Image>
1114 int const nKernel = params.
size();
1115 assert(kernels.
size() ==
static_cast<unsigned int>(nKernel));
1118 for (
int i = 0; i != nKernel; ++i) {
1121 amp += params[i] * k->getSum();
1126 outputKernel->setCtrX(kernels[0]->getCtrX());
1127 outputKernel->setCtrY(kernels[0]->getCtrY());
1137 typedef float Pixel;
1141 geom::Point2I const&,
int const,
int const,
int const,
int const,
1142 bool const,
int const);
1143 template int countPsfCandidates<Pixel>(afw::math::SpatialCellSet
const&,
int const);
1146 afw::math::SpatialCellSet
const&,
1147 int const,
double const,
1150 afw::math::SpatialCellSet
const&,
1151 bool const,
int const,
double const,
double subtractPsf(afw::detection::Psf const &psf, ImageT *data, double x, double y, double psfFlux=std::numeric_limits< double >::quiet_NaN())
Class used by SpatialCell for spatial PSF fittig.
void setStatus(Status status)
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...
double operator()(const std::vector< double > &coeffs) const
void setAmplitude(double amplitude)
Set the best-fit amplitude.
void setErrorDef(double def)
static void setHeight(int height)
int countPsfCandidates(afw::math::SpatialCellSet const &psfCells, int const nStarPerCell=-1)
Count the number of candidates in use.
int positionToIndex(double pos)
std::vector< double > const & getEigenValues() const
double innerProduct(Image1T const &lhs, Image2T const &rhs, int const border=0)
_const_view_t::x_iterator const_x_iterator
virtual void analyze()
Generate eigenimages that are normalised and background-subtracted.
std::shared_ptr< Image > computeImage(lsst::geom::Point2D position=makeNullPoint(), image::Color color=image::Color(), ImageOwnerEnum owner=COPY) const
boost::shared_ptr< afw::table::SourceRecord > getSource() const
Return the original Source.
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.
double getVar() const
Return the variance in use when fitting this object.
void setSpatialParameters(afw::math::Kernel *kernel, std::vector< double > const &coeffs)
Fit a Kernel's spatial variability from a set of stars.
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.
Class used by SpatialCell for spatial PSF fittig.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
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...
Class for doing PCA on PSF stars.
T static_pointer_cast(T... args)
static MaskPixelT getPlaneBitMask(const std::vector< std::string > &names)
double getValue(Property const prop=NOTHING) const
std::unique_ptr< SchemaItem< U > > result
unsigned int getNKernelParameters() const
void setSpatialParameters(const std::vector< std::vector< double >> params)
#define LSST_EXCEPT(type,...)
static void setWidth(int width)
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
void processCandidate(afw::math::SpatialCellCandidate *candidate)
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...
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 Up() const
Error definition of the function.
int getNSpatialParameters() const
MinimizeChi2(evalChi2Visitor< PixelT > &chi2Visitor, afw::math::Kernel *kernel, afw::math::SpatialCellSet const &psfCells, int nStarPerCell, int nComponents, int nSpatialParams)
virtual KernelList const & getKernelList() const
afw::table::Key< double > b
ImageList const & getEigenImages() const
void visitAllCandidates(CandidateVisitor *visitor, bool const ignoreExceptions=false)
lsst::afw::image::Image< ImagePixelT > Image
#define LSST_EXCEPT_ADD(e, m)
void visitCandidates(CandidateVisitor *visitor, int const nMaxPerCell=-1, bool const ignoreExceptions=false)
Class stored in SpatialCells for spatial Psf fitting.
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...
void setChi2(double chi2)
virtual double updateBadPixels(unsigned long mask, int const ncomp)
evalChi2Visitor(afw::math::Kernel const &kernel, double lambda)