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);
101 imCandidate->getXCenter() % imCandidate->getYCenter()));
107 imCandidate->getXCenter() % imCandidate->getYCenter()));
110 _imagePca->addImage(im, imCandidate->getSource()->getPsfInstFlux());
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) {
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;
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,
2-dimensional weighted sum of Chebyshev polynomials of the first kind.
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)
Set the candidate's 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.
x_iterator fast_iterator
A fast STL compliant iterator for contiguous images N.b.
void setErrorDef(double def)
static void setHeight(int height)
Set the height of the image that getImage should return.
int countPsfCandidates(afw::math::SpatialCellSet const &psfCells, int const nStarPerCell=-1)
Count the number of candidates in use.
A floating-point coordinate rectangle geometry.
float Pixel
Typedefs to be used for pixel values.
int positionToIndex(double pos)
Convert image position to nearest integer index.
std::vector< double > const & getEigenValues() const
Return Eigen values.
#define CONST_PTR(...)
A shared pointer to a const object.
A coordinate class intended to represent absolute positions.
A class to contain the data, WCS, and other information needed to describe an image of the sky...
double innerProduct(Image1T const &lhs, Image2T const &rhs, int const border=0)
Calculate the inner product of two images.
Reports attempts to exceed implementation-defined length limits for some classes. ...
_const_view_t::x_iterator const_x_iterator
A const iterator for traversing the pixels in a row.
float getXCenter() const
Return the object's column-centre.
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
Return an Image of the PSF, in a form that can be compared directly with star images.
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.
A collection of SpatialCells covering an entire image.
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.
A base class for image defects.
Class used by SpatialCell for spatial PSF fittig.
Class for doing PCA on PSF stars.
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)
A kernel that is a linear combination of fixed basis kernels.
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...
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
A class to manipulate images, masks, and variance as a single object.
T static_pointer_cast(T... args)
static MaskPixelT getPlaneBitMask(const std::vector< std::string > &names)
Return the bitmask corresponding to a vector of plane names OR'd together.
Reports errors in the logical structure of the program.
A coordinate class intended to represent offsets and dimensions.
double getValue(Property const prop=NOTHING) const
Return the value of the desired property (if specified in the constructor)
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.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
static void setWidth(int width)
Set the width of the image that getImage should return.
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.
float getYCenter() const
Return the object's row-centre.
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
Return the number of spatial parameters (0 if not spatially varying)
MinimizeChi2(evalChi2Visitor< PixelT > &chi2Visitor, afw::math::Kernel *kernel, afw::math::SpatialCellSet const &psfCells, int nStarPerCell, int nComponents, int nSpatialParams)
virtual KernelList const & getKernelList() const
Get the fixed basis kernels.
ImageList const & getEigenImages() const
Return Eigen images.
void visitAllCandidates(CandidateVisitor *visitor, bool const ignoreExceptions=false)
Call the visitor's processCandidate method for every Candidate in the SpatialCellSet.
Base class for candidate objects in a SpatialCell.
lsst::afw::image::Image< ImagePixelT > Image
find sum of pixels in the image
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
A polymorphic base class for representing an image's Point Spread Function.
#define LSST_EXCEPT_ADD(e, m)
Add the current location and a message to an existing exception before rethrowing it...
Kernels are used for convolution with MaskedImages and (eventually) Images.
An integer coordinate rectangle.
void visitCandidates(CandidateVisitor *visitor, int const nMaxPerCell=-1, bool const ignoreExceptions=false)
Call the visitor's processCandidate method for each Candidate in the SpatialCellSet.
A class to represent a 2-dimensional array of pixels.
Reports when the result of an operation cannot be represented by the destination type.
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)
Set the candidate's chi^2.
virtual double updateBadPixels(unsigned long mask, int const ncomp)
Update the bad pixels (i.e.
A kernel created from an Image.
Reports errors that are due to events beyond the control of the program.
evalChi2Visitor(afw::math::Kernel const &kernel, double lambda)