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" 62 namespace algorithms {
66 int const WARP_BUFFER(1);
71 template<
typename PixelT>
77 explicit SetPcaImageVisitor(
78 PsfImagePca<MaskedImageT> *imagePca,
79 unsigned int const mask=0x0
89 PsfCandidate<PixelT> *imCandidate =
dynamic_cast<PsfCandidate<PixelT> *
>(candidate);
90 if (imCandidate == NULL) {
92 "Failed to cast SpatialCellCandidate to PsfCandidate");
110 str(boost::format(
"Image at %d, %d contains NaN")
111 % imCandidate->getXCenter() % imCandidate->getYCenter()));
114 if (!
std::isfinite(afwMath::makeStatistics(*im->getVariance(),
117 str(boost::format(
"Variance of Image at %d, %d contains NaN")
118 % imCandidate->getXCenter() % imCandidate->getYCenter()));
121 _imagePca->addImage(im, imCandidate->getSource()->getPsfFlux());
127 PsfImagePca<MaskedImageT> *_imagePca;
132 template<
typename PixelT>
137 explicit countVisitor() :
afwMath::CandidateVisitor(), _n(0) {}
145 PsfCandidate<PixelT> *imCandidate =
dynamic_cast<PsfCandidate<PixelT> *
>(candidate);
146 if (imCandidate == NULL) {
148 "Failed to cast SpatialCellCandidate to PsfCandidate");
152 imCandidate->getMaskedImage();
161 double getN()
const {
return _n; }
172 template<
typename ImageT>
179 unsigned int const nKernel = kernels.
size();
183 "Kernel has no components");
187 for (
unsigned int i = 0; i != nKernel; ++i) {
188 kernels[i]->computeImage(scratch,
false);
205 template<
typename PixelT>
210 int const nEigenComponents,
211 int const spatialOrder,
213 int const nStarPerCell,
214 bool const constantWeight,
236 SetPcaImageVisitor<PixelT> importStarVisitor(&imagePca);
237 bool const ignoreExceptions =
true;
238 psfCells.
visitCandidates(&importStarVisitor, nStarPerCell, ignoreExceptions);
247 double deltaLim = 10.0;
252 for (
int i = 0; i != niter; ++i) {
253 int const ncomp = (i == 0) ? 0 :
256 if (i > 0 && delta < deltaLim) {
265 int const nEigen =
static_cast<int>(eigenValues.
size());
267 int const ncomp = (nEigenComponents <= 0 || nEigen < nEigenComponents) ? nEigen : nEigenComponents;
273 for (
int k = 0; k != ncomp; ++k) {
274 ImageT
const& im = *eigenImages[k]->getImage();
277 if (bkg_border > im.getWidth()) {
278 bkg_border = im.getWidth() / 2;
280 if (bkg_border > im.getHeight()) {
281 bkg_border = im.getHeight() / 2;
286 for (
int i = 0; i != bkg_border; ++i) {
288 ptrB = im.row_begin(i), ptrT = im.row_begin(im.getHeight() - i - 1);
289 for (
int j = 0; j != im.getWidth(); ++j, ++ptrB, ++ptrT) {
290 sum += *ptrB + *ptrT;
293 for (
int i = bkg_border; i < im.getHeight() - bkg_border; ++i) {
296 ptrL = im.row_begin(i), ptrR = im.row_begin(i) + im.getWidth() - bkg_border;
297 for (
int j = 0; j != bkg_border; ++j, ++ptrL, ++ptrR) {
298 sum += *ptrL + *ptrR;
301 sum /= 2*(bkg_border*im.getWidth() + bkg_border*(im.getHeight() - 2*bkg_border));
303 *eigenImages[k] -= sum;
313 for (
int i = 0; i != ncomp; ++i) {
318 ImageT&
image = *eigenImages[i]->getImage();
324 ptr1 = image.begin(
true),
end = image.end(
true); ptr1 !=
end; ++ptr0, ++ptr1) {
325 *ptr1 = *ptr1 / sum - *ptr0;
337 spatialFunction->setParameter(0, 1.0);
338 spatialFunctionList.
push_back(spatialFunction);
351 template<
typename PixelT>
353 int const nStarPerCell)
355 countVisitor<PixelT> counter;
358 return counter.getN();
368 template<
typename ModelImageT,
typename DataImageT>
370 fitKernel(ModelImageT
const& mImage,
371 DataImageT
const& data,
373 bool detected =
true,
376 assert(data.getDimensions() == mImage.getDimensions());
381 double sumMM = 0.0, sumMD = 0.0, sumDD = 0.0;
383 for (
int y = 0;
y != data.getHeight(); ++
y) {
384 typename ModelImageT::x_iterator mptr = mImage.row_begin(
y);
385 for (
typename DataImageT::x_iterator ptr = data.row_begin(
y),
end = data.row_end(
y);
386 ptr !=
end; ++ptr, ++mptr) {
387 double const m = (*mptr)[0];
388 double const d = ptr.image();
389 double const var = ptr.variance() + lambda*d;
390 if (detected && !(ptr.mask() & DETECTED)) {
393 if (ptr.mask() & BAD) {
397 double const iVar = 1.0/var;
413 double const amp = sumMD/sumMM;
414 double const chi2 = (sumDD - 2*amp*sumMD + amp*amp*sumMM)/(npix - 1);
421 int y = data.getHeight()/2;
422 int x = data.getWidth()/2;
425 for (
int ii = -hsize; ii <= hsize; ++ii) {
426 for (
int jj = -hsize; jj <= hsize; ++jj) {
427 printf(
"%7.1f ", data.at(x + jj, y - ii).image());
430 for (
int jj = -hsize; jj <= hsize; ++jj) {
431 printf(
"%7.1f ", amp*(*(mImage.at(x + jj, y - ii)))[0]);
435 printf(
"%g %.1f\n", amp, chi2);
448 template<
typename PixelT>
460 _chi2(0.0), _kernel(kernel), _lambda(lambda),
461 _kImage(
std::shared_ptr<KImage>(new KImage(kernel.getDimensions()))) {
471 if (imCandidate == NULL) {
473 "Failed to cast SpatialCellCandidate to PsfCandidate");
476 double const xcen = imCandidate->
getSource()->getX();
477 double const ycen = imCandidate->
getSource()->getY();
491 double dchi2 = result.first;
492 double const amp = result.second;
509 double mutable _chi2;
527 assert (nComponents*nSpatialParams == static_cast<long>(coeffs.
size()));
531 for (
int i = 0; i != nComponents; ++i) {
534 coeffs.
begin() + (i + 1)*nSpatialParams, kCoeffs[i].begin());
545 Eigen::VectorXd
const& vec
551 assert (nComponents*nSpatialParams == vec.size());
555 for (
int i = 0; i != nComponents; ++i) {
557 for (
int j = 0; j != nSpatialParams; ++j) {
558 spatialCoeffs[j] = vec[i*nSpatialParams + j];
569 template<
typename PixelT>
579 _chi2Visitor(chi2Visitor),
582 _nStarPerCell(nStarPerCell),
583 _nComponents(nComponents),
584 _nSpatialParams(nSpatialParams) {}
592 double Up()
const {
return _errorDef; }
598 _psfCells.visitCandidates(&_chi2Visitor, _nStarPerCell);
600 return _chi2Visitor.getValue();
619 template<
typename PixelT>
624 int const nStarPerCell,
625 double const tolerance,
640 coeffs.
assign(nComponents*nSpatialParams, 0.0);
643 stepSize.
assign(nComponents*nSpatialParams, 100);
647 ROOT::Minuit2::MnUserParameters fitPar;
649 paramNames.
reserve(nComponents*nSpatialParams);
651 for (
int i = 0, c = 0; c != nComponents; ++c) {
653 for (
int s = 0; s != nSpatialParams; ++s, ++i) {
654 paramNames.
push_back((boost::format(
"C%d:%d") % c % s).str());
655 fitPar.Add(paramNames[i].c_str(), coeffs[i], stepSize[i]);
662 MinimizeChi2<PixelT> minimizerFunc(getChi2, kernel, psfCells, nStarPerCell, nComponents, nSpatialParams);
664 double const errorDef = 1.0;
669 ROOT::Minuit2::MnMigrad migrad(minimizerFunc, fitPar);
674 ROOT::Minuit2::FunctionMinimum
min =
675 migrad(maxFnCalls, tolerance/(1e-4*errorDef));
677 float minChi2 = min.Fval();
678 bool const isValid = min.IsValid() &&
std::isfinite(minChi2);
680 if (
true || isValid) {
681 for (
int i = 0; i != nComponents*nSpatialParams; ++i) {
682 coeffs[i] = min.UserState().Value(i);
688 #if 0 // Estimate errors; we don't really need this 689 ROOT::Minuit2::MnMinos minos(minimizerFunc, min);
690 for (
int i = 0, c = 0; c != nComponents; ++c) {
691 for (
int s = 0; s != nSpatialParams; ++s, ++i) {
692 char const *
name = paramNames[i].c_str();
693 printf(
"%s %g", name, min.UserState().Value(name));
694 if (isValid && !fitPar.Parameter(fitPar.Index(name)).IsFixed()) {
695 printf(
" (%g+%g)\n", minos(i).
first, minos(i).
second);
751 template<
typename PixelT>
768 _A((_nComponents-1)*_nSpatialParams, (_nComponents-1)*_nSpatialParams),
769 _b((_nComponents-1)*_nSpatialParams),
770 _basisDotBasis(_nComponents, _nComponents)
772 _basisImgs.resize(_nComponents);
780 for (
int i = 0; i != _nComponents; ++i) {
782 kernels[i]->computeImage(*_basisImgs[i],
false);
788 for (
int i = 1; i != _nComponents; ++i) {
789 for (
int j = i; j != _nComponents; ++j) {
790 _basisDotBasis(i, j) = _basisDotBasis(j, i) =
791 afwImage::innerProduct(*_basisImgs[i], *_basisImgs[j],
802 if (imCandidate == NULL) {
804 "Failed to cast SpatialCellCandidate to PsfCandidate");
813 double const xcen = imCandidate->
getXCenter();
814 double const ycen = imCandidate->
getYCenter();
815 double const dx = afwImage::positionToIndex(xcen,
true).second;
816 double const dy = afwImage::positionToIndex(ycen,
true).second;
832 double const amp = ret.second.first;
835 double const var = imCandidate->
getVar();
836 double const ivar = 1/(var + _tau2);
840 for (
int ic = 1; ic != _nComponents; ++ic) {
849 typename KImage::fast_iterator bPtr = basisImages[0]->
begin(
true);
850 for (
typename Image::fast_iterator dPtr = dataImage->begin(
true),
end = dataImage->end(
true);
851 dPtr !=
end; ++dPtr, ++bPtr) {
852 *dPtr = *dPtr / amp - *bPtr;
855 for (
int i = 0, ic = 1; ic != _nComponents; ++ic) {
856 double const basisDotData = afwImage::innerProduct(*basisImages[ic], *dataImage,
858 for (
int is = 0; is != _nSpatialParams; ++is, ++i) {
859 _b(i) += ivar*params[ic][is]*basisDotData;
861 for (
int j = i, jc = ic; jc != _nComponents; ++jc) {
862 for (
int js = (i == j) ? is : 0; js != _nSpatialParams; ++js, ++j) {
863 _A(i, j) += ivar*params[ic][is]*params[jc][js]*_basisDotBasis(ic, jc);
871 Eigen::MatrixXd
const& getA()
const {
return _A; }
872 Eigen::VectorXd
const& getB()
const {
return _b; }
877 int const _nSpatialParams;
878 int const _nComponents;
882 Eigen::MatrixXd _basisDotBasis;
887 template<
typename PixelT>
895 if (imCandidate == NULL) {
897 "Failed to cast SpatialCellCandidate to PsfCandidate");
900 afwMath::MAX).getValue());
907 template<
typename PixelT>
912 bool const doNonLinearFit,
913 int const nStarPerCell,
914 double const tolerance,
918 if (doNonLinearFit) {
919 return fitSpatialKernelFromPsfCandidates<PixelT>(kernel, psfCells, nStarPerCell, tolerance);
922 double const tau = 0;
928 "Failed to cast Kernel to LinearCombinationKernel while building spatial PSF model");
934 setAmplitudeVisitor<PixelT> setAmplitude;
940 FillABVisitor<PixelT> getAB(*lcKernel, tau);
948 Eigen::MatrixXd
const& A = getAB.getA();
949 Eigen::VectorXd
const&
b = getAB.getB();
950 Eigen::VectorXd x0(b.size());
956 x0(0) =
b(0)/A(0, 0);
959 x0 = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
968 for (
int j = 0; j < b.size(); ++j) {
969 for (
int i = 0; i < b.size(); ++i) {
973 img.writeFits(
"a.fits");
976 for (
int i = 0; i != 6; ++i) {
977 double xcen = 25;
double ycen = 35 + 35*i;
978 std::cout <<
"x, y " << xcen <<
" , " << ycen <<
" b " 979 << (
x[3] + xcen*
x[4] + ycen*
x[5])/(
x[0] + xcen*
x[1] + ycen*
x[2]) <<
std::endl;
1008 template<
typename MaskedImageT>
1034 double lambda = 0.0;
1041 chi2 = result.first;
1042 amp = result.second;
1045 amp = psfFlux/afwMath::makeStatistics(*kImage, afwMath::SUM).
getValue();
1054 *subData->getImage() -= *kImageF;
1058 LSST_EXCEPT_ADD(e, (boost::format(
"Object at (%.2f, %.2f)") % x % y).str());
1069 template<
typename Image>
1080 int const nKernel = kernels.
size();
1084 "Your kernel must have at least one component");
1093 Image
const& subImage(Image(image, bbox, afwImage::PARENT,
false));
1099 Eigen::MatrixXd A(nKernel, nKernel);
1100 Eigen::VectorXd
b(nKernel);
1102 for (
int i = 0; i != nKernel; ++i) {
1103 b(i) = afwImage::innerProduct(*kernelImages[i], *subImage.getImage());
1105 for (
int j = i; j != nKernel; ++j) {
1106 A(i, j) = A(j, i) = afwImage::innerProduct(*kernelImages[i], *kernelImages[j]);
1109 Eigen::VectorXd
x(nKernel);
1112 x(0) =
b(0)/A(0, 0);
1114 x = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
1118 int const x0 = kernelImages[0]->getX0(), y0 = kernelImages[0]->getY0();
1122 for (
int i = 0; i != nKernel; ++i) {
1124 newKernel->setCtrX(x0 + static_cast<int>(newKernel->getWidth()/2));
1125 newKernel->setCtrY(y0 + static_cast<int>(newKernel->getHeight()/2));
1128 newKernels[i] = newKernel;
1141 template<
typename Image>
1153 int const nKernel = params.
size();
1154 assert(kernels.
size() ==
static_cast<unsigned int>(nKernel));
1157 for (
int i = 0; i != nKernel; ++i) {
1160 amp += params[i] * k->getSum();
1165 outputKernel->setCtrX(kernels[0]->getCtrX());
1166 outputKernel->setCtrY(kernels[0]->getCtrY());
1177 typedef float Pixel;
1183 int const,
bool const,
int const);
1185 int countPsfCandidates<Pixel>(afwMath::SpatialCellSet
const&,
int const);
1189 fitSpatialKernelFromPsfCandidates<Pixel>(
afwMath::Kernel *, afwMath::SpatialCellSet
const&,
1190 int const,
double const,
double const);
1193 fitSpatialKernelFromPsfCandidates<Pixel>(afwMath::Kernel *, afwMath::SpatialCellSet
const&,
bool const,
1194 int const,
double const,
double const);
Class used by SpatialCell for spatial PSF fittig.
void setStatus(Status status)
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 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)
std::pair< bool, double > fitSpatialKernelFromPsfCandidates(lsst::afw::math::Kernel *kernel, lsst::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.
std::vector< double > const & getEigenValues() const
void processCandidate(afwMath::SpatialCellCandidate *candidate)
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...
double subtractPsf(lsst::afw::detection::Psf const &psf, ImageT *data, double x, double y, double psfFlux=std::numeric_limits< double >::quiet_NaN())
MinimizeChi2(evalChi2Visitor< PixelT > &chi2Visitor, afwMath::Kernel *kernel, afwMath::SpatialCellSet const &psfCells, int nStarPerCell, int nComponents, int nSpatialParams)
_const_view_t::x_iterator const_x_iterator
std::shared_ptr< lsst::afw::math::Function2< double > > SpatialFunctionPtr
virtual void analyze()
Generate eigenimages that are normalised and background-subtracted.
evalChi2Visitor(afwMath::Kernel const &kernel, double lambda)
std::pair< std::vector< double >, lsst::afw::math::KernelList > fitKernelParamsToImage(lsst::afw::math::LinearCombinationKernel const &kernel, Image const &image, lsst::afw::geom::Point2D const &pos)
Fit a LinearCombinationKernel to an Image, allowing the coefficients of the components to vary...
boost::shared_ptr< afw::table::SourceRecord > getSource() const
Return the original Source.
double getVar() const
Return the variance in use when fitting this object.
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.
std::pair< std::shared_ptr< lsst::afw::math::LinearCombinationKernel >, std::vector< double > > createKernelFromPsfCandidates(lsst::afw::math::SpatialCellSet const &psfCells, lsst::afw::geom::Extent2I const &dims, lsst::afw::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...
Class used by SpatialCell for spatial PSF fittig.
Class for doing PCA on PSF stars.
SpatialFunctionPtr getSpatialFunction(unsigned int index) const
double computeImage(lsst::afw::image::Image< Pixel > &image, bool doNormalize, double x=0.0, double y=0.0) const
T static_pointer_cast(T... args)
static MaskPixelT getPlaneBitMask(const std::vector< std::string > &names)
double getValue(Property const prop=NOTHING) const
void setSpatialParameters(afwMath::Kernel *kernel, std::vector< double > const &coeffs)
Fit a Kernel's spatial variability from a set of stars.
unsigned int getNKernelParameters() const
void setSpatialParameters(const std::vector< std::vector< double >> params)
#define LSST_EXCEPT(type,...)
void setNanSafe(bool isNanSafe)
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)
double Up() const
Error definition of the function.
int getNSpatialParameters() const
virtual KernelList const & getKernelList() const
afw::table::Key< double > b
ImageList const & getEigenImages() const
void visitAllCandidates(CandidateVisitor *visitor, bool const ignoreExceptions=false)
std::shared_ptr< Image > computeImage(geom::Point2D position=makeNullPoint(), image::Color color=image::Color(), ImageOwnerEnum owner=COPY) const
lsst::afw::image::Image< ImagePixelT > Image
std::pair< std::shared_ptr< lsst::afw::math::Kernel >, std::pair< double, double > > fitKernelToImage(lsst::afw::math::LinearCombinationKernel const &kernel, Image const &image, lsst::afw::geom::Point2D const &pos)
Fit a LinearCombinationKernel to an Image, allowing the coefficients of the components to vary...
#define LSST_EXCEPT_ADD(e, m)
geom::Extent2I const getDimensions() const
void visitCandidates(CandidateVisitor *visitor, int const nMaxPerCell=-1, bool const ignoreExceptions=false)
Class stored in SpatialCells for spatial Psf fitting.
void setChi2(double chi2)
virtual double updateBadPixels(unsigned long mask, int const ncomp)
int countPsfCandidates(lsst::afw::math::SpatialCellSet const &psfCells, int const nStarPerCell=-1)
Count the number of candidates in use.