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,
235 SetPcaImageVisitor<PixelT> importStarVisitor(&imagePca);
236 bool const ignoreExceptions =
true;
237 psfCells.
visitCandidates(&importStarVisitor, nStarPerCell, ignoreExceptions);
246 double deltaLim = 10.0;
251 for (
int i = 0; i != niter; ++i) {
252 int const ncomp = (i == 0) ? 0 :
255 if (i > 0 && delta < deltaLim) {
264 int const nEigen =
static_cast<int>(eigenValues.
size());
266 int const ncomp = (nEigenComponents <= 0 || nEigen < nEigenComponents) ? nEigen : nEigenComponents;
272 for (
int k = 0; k != ncomp; ++k) {
273 ImageT
const& im = *eigenImages[k]->getImage();
276 if (bkg_border > im.getWidth()) {
277 bkg_border = im.getWidth() / 2;
279 if (bkg_border > im.getHeight()) {
280 bkg_border = im.getHeight() / 2;
285 for (
int i = 0; i != bkg_border; ++i) {
287 ptrB = im.row_begin(i), ptrT = im.row_begin(im.getHeight() - i - 1);
288 for (
int j = 0; j != im.getWidth(); ++j, ++ptrB, ++ptrT) {
289 sum += *ptrB + *ptrT;
292 for (
int i = bkg_border; i < im.getHeight() - bkg_border; ++i) {
295 ptrL = im.row_begin(i), ptrR = im.row_begin(i) + im.getWidth() - bkg_border;
296 for (
int j = 0; j != bkg_border; ++j, ++ptrL, ++ptrR) {
297 sum += *ptrL + *ptrR;
300 sum /= 2*(bkg_border*im.getWidth() + bkg_border*(im.getHeight() - 2*bkg_border));
302 *eigenImages[k] -= sum;
312 for (
int i = 0; i != ncomp; ++i) {
317 ImageT&
image = *eigenImages[i]->getImage();
323 ptr1 = image.begin(
true), end = image.end(
true); ptr1 != end; ++ptr0, ++ptr1) {
324 *ptr1 = *ptr1 / sum - *ptr0;
336 spatialFunction->setParameter(0, 1.0);
337 spatialFunctionList.
push_back(spatialFunction);
350 template<
typename PixelT>
352 int const nStarPerCell)
354 countVisitor<PixelT> counter;
357 return counter.getN();
367 template<
typename ModelImageT,
typename DataImageT>
369 fitKernel(ModelImageT
const& mImage,
370 DataImageT
const& data,
372 bool detected =
true,
375 assert(data.getDimensions() == mImage.getDimensions());
380 double sumMM = 0.0, sumMD = 0.0, sumDD = 0.0;
382 for (
int y = 0; y != data.getHeight(); ++y) {
383 typename ModelImageT::x_iterator mptr = mImage.row_begin(y);
384 for (
typename DataImageT::x_iterator ptr = data.row_begin(y), end = data.row_end(y);
385 ptr != end; ++ptr, ++mptr) {
386 double const m = (*mptr)[0];
387 double const d = ptr.image();
388 double const var = ptr.variance() + lambda*d;
389 if (detected && !(ptr.mask() & DETECTED)) {
392 if (ptr.mask() & BAD) {
396 double const iVar = 1.0/var;
412 double const amp = sumMD/sumMM;
413 double const chi2 = (sumDD - 2*amp*sumMD + amp*amp*sumMM)/(npix - 1);
420 int y = data.getHeight()/2;
421 int x = data.getWidth()/2;
424 for (
int ii = -hsize; ii <= hsize; ++ii) {
425 for (
int jj = -hsize; jj <= hsize; ++jj) {
426 printf(
"%7.1f ", data.at(x + jj, y - ii).image());
429 for (
int jj = -hsize; jj <= hsize; ++jj) {
430 printf(
"%7.1f ", amp*(*(mImage.at(x + jj, y - ii)))[0]);
434 printf(
"%g %.1f\n", amp, chi2);
447 template<
typename PixelT>
459 _chi2(0.0), _kernel(kernel), _lambda(lambda),
460 _kImage(
std::shared_ptr<KImage>(new KImage(kernel.getDimensions()))) {
470 if (imCandidate == NULL) {
472 "Failed to cast SpatialCellCandidate to PsfCandidate");
475 double const xcen = imCandidate->
getSource()->getX();
476 double const ycen = imCandidate->
getSource()->getY();
490 double dchi2 = result.first;
491 double const amp = result.second;
508 double mutable _chi2;
526 assert (nComponents*nSpatialParams == static_cast<long>(coeffs.
size()));
530 for (
int i = 0; i != nComponents; ++i) {
533 coeffs.
begin() + (i + 1)*nSpatialParams, kCoeffs[i].begin());
544 Eigen::VectorXd
const& vec
550 assert (nComponents*nSpatialParams == vec.size());
554 for (
int i = 0; i != nComponents; ++i) {
556 for (
int j = 0; j != nSpatialParams; ++j) {
557 spatialCoeffs[j] = vec[i*nSpatialParams + j];
568 template<
typename PixelT>
578 _chi2Visitor(chi2Visitor),
581 _nStarPerCell(nStarPerCell),
582 _nComponents(nComponents),
583 _nSpatialParams(nSpatialParams) {}
591 double Up()
const {
return _errorDef; }
597 _psfCells.visitCandidates(&_chi2Visitor, _nStarPerCell);
599 return _chi2Visitor.getValue();
618 template<
typename PixelT>
623 int const nStarPerCell,
624 double const tolerance,
637 coeffs.
assign(nComponents*nSpatialParams, 0.0);
640 stepSize.
assign(nComponents*nSpatialParams, 100);
644 ROOT::Minuit2::MnUserParameters fitPar;
646 paramNames.
reserve(nComponents*nSpatialParams);
648 for (
int i = 0, c = 0; c != nComponents; ++c) {
650 for (
int s = 0; s != nSpatialParams; ++s, ++i) {
651 paramNames.
push_back((boost::format(
"C%d:%d") % c % s).str());
652 fitPar.Add(paramNames[i].c_str(), coeffs[i], stepSize[i]);
659 MinimizeChi2<PixelT> minimizerFunc(getChi2, kernel, psfCells, nStarPerCell, nComponents, nSpatialParams);
661 double const errorDef = 1.0;
666 ROOT::Minuit2::MnMigrad migrad(minimizerFunc, fitPar);
671 ROOT::Minuit2::FunctionMinimum min =
672 migrad(maxFnCalls, tolerance/(1e-4*errorDef));
674 float minChi2 = min.Fval();
677 if (
true || isValid) {
678 for (
int i = 0; i != nComponents*nSpatialParams; ++i) {
679 coeffs[i] = min.UserState().Value(i);
685 #if 0 // Estimate errors; we don't really need this 686 ROOT::Minuit2::MnMinos minos(minimizerFunc, min);
687 for (
int i = 0, c = 0; c != nComponents; ++c) {
688 for (
int s = 0; s != nSpatialParams; ++s, ++i) {
689 char const *
name = paramNames[i].c_str();
690 printf(
"%s %g", name, min.UserState().Value(name));
691 if (isValid && !fitPar.Parameter(fitPar.Index(name)).IsFixed()) {
692 printf(
" (%g+%g)\n", minos(i).
first, minos(i).
second);
748 template<
typename PixelT>
765 _A((_nComponents-1)*_nSpatialParams, (_nComponents-1)*_nSpatialParams),
766 _b((_nComponents-1)*_nSpatialParams),
767 _basisDotBasis(_nComponents, _nComponents)
769 _basisImgs.resize(_nComponents);
777 for (
int i = 0; i != _nComponents; ++i) {
779 kernels[i]->computeImage(*_basisImgs[i],
false);
785 for (
int i = 1; i != _nComponents; ++i) {
786 for (
int j = i; j != _nComponents; ++j) {
787 _basisDotBasis(i, j) = _basisDotBasis(j, i) =
788 afwImage::innerProduct(*_basisImgs[i], *_basisImgs[j],
799 if (imCandidate == NULL) {
801 "Failed to cast SpatialCellCandidate to PsfCandidate");
810 double const xcen = imCandidate->
getXCenter();
811 double const ycen = imCandidate->
getYCenter();
812 double const dx = afwImage::positionToIndex(xcen,
true).second;
813 double const dy = afwImage::positionToIndex(ycen,
true).second;
829 double const amp = ret.second.first;
832 double const var = imCandidate->
getVar();
833 double const ivar = 1/(var + _tau2);
837 for (
int ic = 1; ic != _nComponents; ++ic) {
846 typename KImage::fast_iterator bPtr = basisImages[0]->
begin(
true);
847 for (
typename Image::fast_iterator dPtr = dataImage->begin(
true), end = dataImage->end(
true);
848 dPtr != end; ++dPtr, ++bPtr) {
849 *dPtr = *dPtr / amp - *bPtr;
852 for (
int i = 0, ic = 1; ic != _nComponents; ++ic) {
853 double const basisDotData = afwImage::innerProduct(*basisImages[ic], *dataImage,
855 for (
int is = 0; is != _nSpatialParams; ++is, ++i) {
856 _b(i) += ivar*params[ic][is]*basisDotData;
858 for (
int j = i, jc = ic; jc != _nComponents; ++jc) {
859 for (
int js = (i == j) ? is : 0; js != _nSpatialParams; ++js, ++j) {
860 _A(i, j) += ivar*params[ic][is]*params[jc][js]*_basisDotBasis(ic, jc);
868 Eigen::MatrixXd
const& getA()
const {
return _A; }
869 Eigen::VectorXd
const& getB()
const {
return _b; }
874 int const _nSpatialParams;
875 int const _nComponents;
879 Eigen::MatrixXd _basisDotBasis;
884 template<
typename PixelT>
892 if (imCandidate == NULL) {
894 "Failed to cast SpatialCellCandidate to PsfCandidate");
897 afwMath::MAX).getValue());
904 template<
typename PixelT>
909 bool const doNonLinearFit,
910 int const nStarPerCell,
911 double const tolerance,
915 if (doNonLinearFit) {
916 return fitSpatialKernelFromPsfCandidates<PixelT>(kernel, psfCells, nStarPerCell, tolerance);
919 double const tau = 0;
925 "Failed to cast Kernel to LinearCombinationKernel while building spatial PSF model");
931 setAmplitudeVisitor<PixelT> setAmplitude;
937 FillABVisitor<PixelT> getAB(*lcKernel, tau);
945 Eigen::MatrixXd
const& A = getAB.getA();
946 Eigen::VectorXd
const&
b = getAB.getB();
947 Eigen::VectorXd x0(b.size());
953 x0(0) =
b(0)/A(0, 0);
956 x0 = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
965 for (
int j = 0; j < b.size(); ++j) {
966 for (
int i = 0; i < b.size(); ++i) {
970 img.writeFits(
"a.fits");
973 for (
int i = 0; i != 6; ++i) {
974 double xcen = 25;
double ycen = 35 + 35*i;
975 std::cout <<
"x, y " << xcen <<
" , " << ycen <<
" b " 976 << (x[3] + xcen*x[4] + ycen*x[5])/(x[0] + xcen*x[1] + ycen*x[2]) <<
std::endl;
1005 template<
typename MaskedImageT>
1031 double lambda = 0.0;
1038 chi2 = result.first;
1039 amp = result.second;
1042 amp = psfFlux/afwMath::makeStatistics(*kImage, afwMath::SUM).
getValue();
1051 *subData->getImage() -= *kImageF;
1055 LSST_EXCEPT_ADD(e, (boost::format(
"Object at (%.2f, %.2f)") % x % y).str());
1066 template<
typename Image>
1077 int const nKernel = kernels.
size();
1081 "Your kernel must have at least one component");
1090 Image
const& subImage(Image(image, bbox, afwImage::PARENT,
false));
1096 Eigen::MatrixXd A(nKernel, nKernel);
1097 Eigen::VectorXd
b(nKernel);
1099 for (
int i = 0; i != nKernel; ++i) {
1100 b(i) = afwImage::innerProduct(*kernelImages[i], *subImage.getImage());
1102 for (
int j = i; j != nKernel; ++j) {
1103 A(i, j) = A(j, i) = afwImage::innerProduct(*kernelImages[i], *kernelImages[j]);
1106 Eigen::VectorXd x(nKernel);
1109 x(0) =
b(0)/A(0, 0);
1111 x = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
1115 int const x0 = kernelImages[0]->getX0(), y0 = kernelImages[0]->getY0();
1119 for (
int i = 0; i != nKernel; ++i) {
1121 newKernel->setCtrX(x0 + static_cast<int>(newKernel->getWidth()/2));
1122 newKernel->setCtrY(y0 + static_cast<int>(newKernel->getHeight()/2));
1125 newKernels[i] = newKernel;
1138 template<
typename Image>
1150 int const nKernel = params.
size();
1151 assert(kernels.
size() ==
static_cast<unsigned int>(nKernel));
1154 for (
int i = 0; i != nKernel; ++i) {
1157 amp += params[i] * k->getSum();
1162 outputKernel->setCtrX(kernels[0]->getCtrX());
1163 outputKernel->setCtrY(kernels[0]->getCtrY());
1174 typedef float Pixel;
1180 int const,
bool const,
int const);
1182 int countPsfCandidates<Pixel>(afwMath::SpatialCellSet
const&,
int const);
1186 fitSpatialKernelFromPsfCandidates<Pixel>(
afwMath::Kernel *, afwMath::SpatialCellSet
const&,
1187 int const,
double const,
double const);
1190 fitSpatialKernelFromPsfCandidates<Pixel>(afwMath::Kernel *, afwMath::SpatialCellSet
const&,
bool const,
1191 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...
std::shared_ptr< ImageT > offsetImage(ImageT const &image, float dx, float dy, std::string const &algorithmName="lanczos5", unsigned int buffer=0)
boost::shared_ptr< afw::table::SourceRecord > getSource() const
Return the original Source.
virtual KernelList const & getKernelList() const
double getVar() const
Return the variance in use when fitting this object.
SpatialFunctionPtr getSpatialFunction(unsigned int index) const
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.
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)
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)
virtual double updateBadPixels(unsigned long mask, int const ncomp)
double Up() const
Error definition of the function.
int getNSpatialParameters() 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...
static MaskPixelT getPlaneBitMask(const std::vector< std::string > &names)
#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)
int countPsfCandidates(lsst::afw::math::SpatialCellSet const &psfCells, int const nStarPerCell=-1)
Count the number of candidates in use.