48 auto workspace =
basis.makeWorkspace();
49 Eigen::MatrixXd matrix = Eigen::MatrixXd::Zero(input.
size(),
basis.size());
50 Eigen::VectorXd xRhs(input.
size());
51 Eigen::VectorXd yRhs(input.
size());
52 for (
int i = 0; i < matrix.rows(); ++i) {
53 basis.fill(input[i], matrix.row(i), workspace);
54 auto rhs = output[i] - input[i];
60 auto decomp = matrix.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV);
61 if (svdThreshold >= 0) {
62 decomp.setThreshold(svdThreshold);
73 if (shape.getX() <= 0 || shape.getY() <= 0) {
75 pex::exceptions::InvalidParameterError,
76 "Grid shape must be positive." 80 points.
reserve(shape.getX()*shape.getY());
81 double const dx = bbox.getWidth()/shape.getX();
82 double const dy = bbox.getHeight()/shape.getY();
83 for (
int iy = 0; iy < shape.getY(); ++iy) {
84 double const y = bbox.getMinY() + iy*dy;
85 for (
int ix = 0; ix < shape.getX(); ++ix) {
94 LSST_THROW_IF_NE(coeffs.getSize<0>(), coeffs.getSize<1>(), pex::exceptions::InvalidParameterError,
95 "Coefficient matrix must be square (%d != %d).");
97 Eigen::VectorXd packed(
basis.size());
98 for (
auto const & i :
basis.getIndices()) {
99 packed[i.flat] = coeffs[i.nx][i.ny];
128 a(a_),
b(b_), ap(ap_), bp(bp_)
132 "A and B polynomials must have the same order (%d != %d).");
135 "A and AP polynomials must have the same order (%d != %d).");
138 "A and BP polynomials must have the same order (%d != %d).");
146 return dpix +
Extent2D(
a(dpix, ws),
b(dpix, ws));
150 return siwc +
Extent2D(ap(siwc, ws), bp(siwc, ws));
161 dpix1(makeGrid(parent._bbox, shape)),
162 siwc(parent._pixelToIwc->applyForward(dpix1))
167 if (parent._useInverse) {
170 dpix2 = parent._pixelToIwc->applyInverse(
siwc);
188 if (basis.
size() > parent._grid->dpix1.size()) {
191 (
boost::format(
"Number of parameters (%d) is larger than number of data points (%d)")
192 % (2*basis.
size()) % (2*parent._grid->dpix1.size())).str()
196 Box2D boxFwd(parent._bbox);
197 boxFwd.shift(-parent._crpix);
198 auto fwd = fitSipOneDirection(order, boxFwd, svdThreshold, parent._grid->dpix1, parent._grid->siwc);
201 for (
auto const & point : parent._grid->siwc) {
204 auto inv = fitSipOneDirection(order, boxInv, svdThreshold, parent._grid->siwc, parent._grid->dpix2);
206 return std::make_unique<Solution>(fwd.first, fwd.second, inv.first, inv.second);
212 Eigen::Matrix2d
const &
cd,
219 _useInverse(useInverse),
220 _pixelToIwc(
std::move(pixelToIwc)),
224 _grid(new
Grid(gridShape, *this)),
225 _solution(
Solution::
fit(order, svdThreshold, *this))
231 Eigen::Matrix2d
const &
cd,
234 ndarray::Array<double const, 2>
const &
a,
235 ndarray::Array<double const, 2>
const &
b,
236 ndarray::Array<double const, 2>
const & ap,
237 ndarray::Array<double const, 2>
const & bp,
240 _useInverse(useInverse),
241 _pixelToIwc(
std::move(pixelToIwc)),
245 _grid(new
Grid(gridShape, *this)),
248 makePolynomialFromCoeffMatrix(a),
249 makePolynomialFromCoeffMatrix(b),
250 makePolynomialFromCoeffMatrix(ap),
251 makePolynomialFromCoeffMatrix(bp)
259 return _solution->a.getBasis().getOrder();
263 return _solution->a[_solution->a.getBasis().index(p, q)];
267 return _solution->b[_solution->a.getBasis().index(p, q)];
271 return _solution->ap[_solution->a.getBasis().index(p, q)];
275 return _solution->bp[_solution->a.getBasis().index(p, q)];
280 template <
typename F>
281 Eigen::MatrixXd makeCoefficientMatrix(
std::size_t size, F getter) {
282 Eigen::MatrixXd
result = Eigen::MatrixXd::Zero(size, size);
285 result(p, q) = getter(p, q);
294 return makeCoefficientMatrix(
295 _solution->a.getBasis().size(),
296 [
this](
int p,
int q) {
return getA(p, q); }
301 return makeCoefficientMatrix(
302 _solution->b.getBasis().size(),
303 [
this](
int p,
int q) {
return getB(p, q); }
308 return makeCoefficientMatrix(
309 _solution->ap.getBasis().size(),
310 [
this](
int p,
int q) {
return getAP(p, q); }
315 return makeCoefficientMatrix(
316 _solution->bp.getBasis().size(),
317 [
this](
int p,
int q) {
return getBP(p, q); }
323 auto ws = _solution->makeWorkspace();
324 return cd(_solution->applyForward(pix - _crpix, ws));
328 auto ws = _solution->makeWorkspace();
332 for (
auto const & point : pix) {
333 iwc.
push_back(
cd(_solution->applyForward(point - _crpix, ws)));
339 auto ws = _solution->makeWorkspace();
340 return _solution->applyInverse(_cdInv(iwc), ws) + _crpix;
344 auto ws = _solution->makeWorkspace();
347 for (
auto const & point : iwc) {
348 pix.
push_back(_solution->applyInverse(_cdInv(point), ws) + _crpix);
363 _grid = std::make_unique<Grid>(shape, *
this);
380 auto ws = _solution->makeWorkspace();
381 for (
std::size_t i = 0; i < _grid->dpix1.size(); ++i) {
382 auto siwc2 = _solution->applyForward(_grid->dpix1[i], ws);
383 auto dpix2 = _solution->applyInverse(_grid->siwc[i], ws);
384 maxDiff.first =
std::max(maxDiff.first, (_grid->siwc[i] - siwc2).computeNorm());
385 maxDiff.second =
std::max(maxDiff.second, (_grid->dpix2[i] - dpix2).computeNorm());
Eigen::MatrixXd getA() const noexcept
Return the coefficients of the forward transform polynomial.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
double getHeight() const noexcept
1-d interval accessors
Point2D applyForward(Point2D const &dpix, Workspace &ws) const
static std::unique_ptr< Solution > fit(int order_, double svdThreshold, SipApproximation const &parent)
SipApproximation(std::shared_ptr< TransformPoint2ToPoint2 > pixelToIwc, Point2D const &crpix, Eigen::Matrix2d const &cd, Box2D const &bbox, Extent2I const &gridShape, int order, bool useInverse=true, double svdThreshold=-1)
Construct a new approximation by fitting on a grid of points.
A fitter and results class for approximating a general Transform in a form compatible with FITS WCS p...
double getWidth() const noexcept
1-d interval accessors
table::Key< table::Array< double > > cd
A floating-point coordinate rectangle geometry.
Low-level polynomials (including special polynomials) in C++.
poly::PolynomialFunction2dYX bp
#define LSST_THROW_IF_NE(N1, N2, EXC_CLASS, MSG)
Check whether the given values are equal, and throw an LSST Exception if they are not...
Point2D applyInverse(Point2D const &siwc, Workspace &ws) const
void include(Point2D const &point) noexcept
Expand this to ensure that this->contains(point).
table::PointKey< double > crpix
~SipApproximation() noexcept
poly::PolynomialFunction2dYX a
Point2D applyForward(Point2D const &pix) const
Convert a point from pixels to intermediate world coordinates.
A 2-d function defined by a series expansion and its coefficients.
std::size_t size() const noexcept
Return the number of basis functions.
Workspace makeWorkspace() const
PolynomialFunction1d simplified(ScaledPolynomialFunction1d const &f)
Calculate the standard polynomial function that is equivalent to a scaled standard polynomial functio...
Function2d< Basis > makeFunction2d(Basis const &basis, Eigen::VectorXd const &coefficients)
Create a Function2d of the appropriate type from a Basis2d and an Eigen object containing coefficient...
poly::PolynomialFunction2dYX b
Eigen::MatrixXd getB() const noexcept
Return the coefficients of the forward transform polynomial.
std::vector< Point2D > dpix2
poly::PolynomialFunction2dYX ap
typename Basis::Workspace Workspace
Type returned by makeWorkspace().
Grid(Extent2I const &shape_, SipApproximation const &parent)
A base class for image defects.
Eigen::MatrixXd getBP() const noexcept
Return the coefficients of the reverse transform polynomial.
Point2D applyInverse(Point2D const &iwcs) const
Convert a point from intermediate world coordinates to pixels.
std::vector< Point2D > siwc
Reports errors in the logical structure of the program.
Eigen::MatrixXd getAP() const noexcept
Return the coefficients of the reverse transform polynomial.
Extent2D getGridStep() const noexcept
Return the distance between grid points in pixels.
table::Key< table::Array< double > > basis
void refineGrid(int factor=2)
Update the grid by making it finer by a given integer factor.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
std::pair< double, double > computeMaxDeviation() const noexcept
Return the maximum deviation of the solution from the exact transform on the current grid...
Extent2I getGridShape() const noexcept
Return the number of grid points in x and y.
ScaledPolynomialBasis2d< PackingOrder::YX > ScaledPolynomialBasis2dYX
A Basis2d for scaled standard polynomials, ordered via PackingOrder::YX.
A Basis2d formed from the product of a Basis1d for each of x and y, truncated at the sum of their ord...
Reports invalid arguments.
void fit(int order, double svdThreshold=-1)
Obtain a new solution at the given order with the current grid.
PolynomialFunction2d< PackingOrder::YX > PolynomialFunction2dYX
A Function2d for standard polynomials, ordered via PackingOrder::YX.
std::vector< Point2D > dpix1
void updateGrid(Extent2I const &shape)
Update the grid to the given number of points in x and y.
poly::PolynomialFunction2dYX::Workspace Workspace
int getOrder() const noexcept
Return the polynomial order of the current solution (same for forward and reverse).
Solution(poly::PolynomialFunction2dYX const &a_, poly::PolynomialFunction2dYX const &b_, poly::PolynomialFunction2dYX const &ap_, poly::PolynomialFunction2dYX const &bp_)
T emplace_back(T... args)