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();
675 bool const isValid = min.IsValid() &&
std::isfinite(minChi2);
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...
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.