lsst.meas.astrom  14.0-7-g0d69b06+3
PolynomialTransform.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2016 LSST/AURA
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
28 
29 namespace lsst { namespace meas { namespace astrom {
30 
32  return compose(
33  scaled.getOutputScalingInverse(),
34  compose(scaled.getPoly(), scaled.getInputScaling())
35  );
36 }
37 
39  PolynomialTransform poly = other.getPoly();
40  // Adding 1 here accounts for the extra terms outside the sum in the SIP
41  // transform definition (see SipForwardTransform docs) - note that you can
42  // fold those terms into the sum by adding 1 from the A_10 and B_01 terms.
43  poly._xCoeffs(1, 0) += 1;
44  poly._yCoeffs(0, 1) += 1;
45  return compose(
46  other.getCdMatrix(),
47  compose(
48  poly,
50  )
51  );
52 }
53 
55  PolynomialTransform poly = other.getPoly();
56  // Account for the terms outside the sum in the SIP definition (see comment
57  // earlier in the file for more explanation).
58  poly._xCoeffs(1, 0) += 1;
59  poly._yCoeffs(0, 1) += 1;
60  return compose(
62  compose(
63  poly,
64  other._cdInverse
65  )
66  );
67 }
68 
70  _xCoeffs(),
71  _yCoeffs(),
72  _u(),
73  _v()
74 {
75  if (order < 0) {
76  throw LSST_EXCEPT(
78  "PolynomialTransform order must be >= 0"
79  );
80  }
81  // Delay allocation until after error checking.
82  _xCoeffs.reset(ndarray::Array<double,2,2>(ndarray::allocate(order + 1, order + 1)));
83  _yCoeffs.reset(ndarray::Array<double,2,2>(ndarray::allocate(order + 1, order + 1)));
84  _xCoeffs.setZero();
85  _yCoeffs.setZero();
86  _u = Eigen::VectorXd(order + 1);
87  _v = Eigen::VectorXd(order + 1);
88 }
89 
91  ndarray::Array<double const,2,0> const & xCoeffs,
92  ndarray::Array<double const,2,0> const & yCoeffs
93 ) : _xCoeffs(ndarray::copy(xCoeffs)),
94  _yCoeffs(ndarray::copy(yCoeffs)),
95  _u(_xCoeffs.rows()),
96  _v(_xCoeffs.rows())
97 {
98  if (xCoeffs.getShape() != yCoeffs.getShape()) {
99  throw LSST_EXCEPT(
101  (boost::format(
102  "X and Y coefficient matrices must have the same shape: "
103  " (%d,%d) != (%d,%d)"
104  ) % xCoeffs.getSize<0>() % xCoeffs.getSize<1>()
105  % yCoeffs.getSize<0>() % yCoeffs.getSize<1>()
106  ).str()
107  );
108  }
109  if (_xCoeffs.cols() != _xCoeffs.rows()) {
110  throw LSST_EXCEPT(
112  (boost::format(
113  "Coefficient matrices must be triangular, not trapezoidal: "
114  " %d != %d "
115  ) % _xCoeffs.rows() % _xCoeffs.cols()
116  ).str()
117  );
118  }
119 }
120 
122  _xCoeffs(ndarray::copy(other.getXCoeffs())),
123  _yCoeffs(ndarray::copy(other.getYCoeffs())),
124  _u(other._u.size()),
125  _v(other._v.size())
126 {}
127 
129  _xCoeffs(),
130  _yCoeffs(),
131  _u(),
132  _v()
133 {
134  this->swap(other);
135 }
136 
138  if (&other != this) {
139  PolynomialTransform tmp(other);
140  tmp.swap(*this);
141  }
142  return *this;
143 }
144 
146  if (&other != this) {
147  other.swap(*this);
148  }
149  return *this;
150 }
151 
153  _xCoeffs.swap(other._xCoeffs);
154  _yCoeffs.swap(other._yCoeffs);
155  _u.swap(other._u);
156  _v.swap(other._v);
157 }
158 
160  double xu = 0.0, xv = 0.0, yu = 0.0, yv = 0.0, x = 0.0, y = 0.0;
161  int const order = getOrder();
162  detail::computePowers(_u, in.getX());
163  detail::computePowers(_v, in.getY());
164  for (int p = 0; p <= order; ++p) {
165  for (int q = 0; q <= order; ++q) {
166  if (p > 0) {
167  xu += _xCoeffs(p, q) * p * _u[p - 1] * _v[q];
168  yu += _yCoeffs(p, q) * p * _u[p - 1] * _v[q];
169  }
170  if (q > 0) {
171  xv += _xCoeffs(p, q) * q * _u[p] * _v[q - 1];
172  yv += _yCoeffs(p, q) * q * _u[p] * _v[q - 1];
173  }
174  x += _xCoeffs(p, q) * _u[p] * _v[q];
175  y += _yCoeffs(p, q) * _u[p] * _v[q];
176  }
177  }
179  linear.getMatrix()(0, 0) = xu;
180  linear.getMatrix()(0, 1) = xv;
181  linear.getMatrix()(1, 0) = yu;
182  linear.getMatrix()(1, 1) = yv;
183  afw::geom::Point2D origin(x, y);
184  return afw::geom::AffineTransform(linear, origin - linear(in));
185 }
186 
188  int const order = getOrder();
189  detail::computePowers(_u, in.getX());
190  detail::computePowers(_v, in.getY());
191  double x = 0;
192  double y = 0;
193  for (int p = 0; p <= order; ++p) {
194  for (int q = 0; q <= order; ++q) {
195  x += _xCoeffs(p, q) * _u[p] * _v[q];
196  y += _yCoeffs(p, q) * _u[p] * _v[q];
197  }
198  }
199  return afw::geom::Point2D(x, y);
200 }
201 
204 }
205 
208  sipForward.getPoly(),
211  );
212  // Account for the terms outside the sum in the SIP definition (see comment
213  // earlier in the file for more explanation).
214  result._poly._xCoeffs(1, 0) += 1;
215  result._poly._yCoeffs(0, 1) += 1;
216  return result;
217 }
218 
221  sipReverse.getPoly(),
222  afw::geom::AffineTransform(sipReverse._cdInverse),
224  );
225  result._poly._xCoeffs(1, 0) += 1;
226  result._poly._yCoeffs(0, 1) += 1;
227  return result;
228 }
229 
231  PolynomialTransform const & poly,
232  afw::geom::AffineTransform const & inputScaling,
233  afw::geom::AffineTransform const & outputScalingInverse
234 ) :
235  _poly(poly),
236  _inputScaling(inputScaling),
237  _outputScalingInverse(outputScalingInverse)
238 {}
239 
241  _poly.swap(other._poly);
242  std::swap(_inputScaling, other._inputScaling);
243  std::swap(_outputScalingInverse, other._outputScalingInverse);
244 }
245 
247  return _outputScalingInverse*_poly.linearize(_inputScaling(in))*_inputScaling;
248 }
249 
251  return _outputScalingInverse(_poly(_inputScaling(in)));
252 }
253 
255  typedef afw::geom::AffineTransform AT;
256  PolynomialTransform result(t2.getOrder());
257  result._xCoeffs = t2._xCoeffs*t1[AT::XX] + t2._yCoeffs*t1[AT::XY];
258  result._yCoeffs = t2._xCoeffs*t1[AT::YX] + t2._yCoeffs*t1[AT::YY];
259  result._xCoeffs(0, 0) += t1[AT::X];
260  result._yCoeffs(0, 0) += t1[AT::Y];
261  return result;
262 }
263 
265  typedef afw::geom::AffineTransform AT;
266  int const order = t1.getOrder();
267  if (order < 1) {
268  PolynomialTransform t1a(1);
269  t1a._xCoeffs(0, 0) = t1._xCoeffs(0, 0);
270  t1a._yCoeffs(0, 0) = t1._yCoeffs(0, 0);
271  return compose(t1a, t2);
272  }
273  detail::BinomialMatrix binomial(order);
274  // For each of these, (e.g.) a[n] == pow(a, n)
275  auto const t2u = detail::computePowers(t2[AT::X], order);
276  auto const t2v = detail::computePowers(t2[AT::Y], order);
277  auto const t2uu = detail::computePowers(t2[AT::XX], order);
278  auto const t2uv = detail::computePowers(t2[AT::XY], order);
279  auto const t2vu = detail::computePowers(t2[AT::YX], order);
280  auto const t2vv = detail::computePowers(t2[AT::YY], order);
281  PolynomialTransform result(order);
282  for (int p = 0; p <= order; ++p) {
283  for (int m = 0; m <= p; ++m) {
284  for (int j = 0; j <= m; ++j) {
285  for (int q = 0; p + q <= order; ++q) {
286  for (int n = 0; n <= q; ++n) {
287  for (int k = 0; k <= n; ++k) {
288  double z = binomial(p,m) * t2u[p-m] * binomial(m,j) * t2uu[j] * t2uv[m-j] *
289  binomial(q,n) * t2v[q-n] * binomial(n,k) * t2vu[k] * t2vv[n-k];
290  result._xCoeffs(j + k, m + n - j - k) += t1._xCoeffs(p, q) * z;
291  result._yCoeffs(j + k, m + n - j - k) += t1._yCoeffs(p, q) * z;
292  } // k
293  } // n
294  } // q
295  } // j
296  } // m
297  } // p
298  return result;
299 }
300 
301 }}} // namespace lsst::meas::astrom
afw::geom::Point2D operator()(afw::geom::Point2D const &in) const
Apply the transform to a point.
afw::geom::AffineTransform linearize(afw::geom::Point2D const &in) const
Return an approximate affine transform at the given point.
PolynomialTransform const & getPoly() const
Return the polynomial component of the transform (A,B) or (AP,BP).
Definition: SipTransform.h:68
ndarray::Array< double const, 2, 2 > getXCoeffs() const
2-D polynomial coefficients that compute the output x coordinate.
Matrix const & getMatrix() const
T swap(T... args)
A class that computes binomial coefficients up to a certain power.
PolynomialTransform compose(afw::geom::AffineTransform const &t1, PolynomialTransform const &t2)
Return a PolynomialTransform that is equivalent to the composition t1(t2())
afw::geom::Point2D const & getPixelOrigin() const
Return the pixel origin (CRPIX, but zero-indexed) of the transform.
Definition: SipTransform.h:58
afw::geom::LinearTransform const & getCdMatrix() const
Return the CD matrix of the transform.
Definition: SipTransform.h:63
PolynomialTransform const & getPoly() const
Return the polynomial transform applied after the input scaling.
A transform that maps pixel coordinates to intermediate world coordinates according to the SIP conven...
Definition: SipTransform.h:150
PolynomialTransform(ndarray::Array< double const, 2, 0 > const &xCoeffs, ndarray::Array< double const, 2, 0 > const &yCoeffs)
Construct a new transform from existing coefficient arrays.
Point< double, 2 > Point2D
void swap(PolynomialTransform &other)
Lightweight swap.
PolynomialTransform & operator=(PolynomialTransform const &other)
Copy assignment.
static PolynomialTransform convert(ScaledPolynomialTransform const &other)
Convert a ScaledPolynomialTransform to an equivalent PolynomialTransform.
double x
afw::geom::AffineTransform const & getInputScaling() const
Return the first affine transform applied to input points.
A 2-d coordinate transform represented by a lazy composition of an AffineTransform, a PolynomialTransform, and another AffineTransform.
afw::geom::Point2D operator()(afw::geom::Point2D const &in) const
Apply the transform to a point.
#define LSST_EXCEPT(type,...)
A transform that maps intermediate world coordinates to pixel coordinates according to the SIP conven...
Definition: SipTransform.h:274
void swap(ScaledPolynomialTransform &other)
afw::geom::AffineTransform const & getOutputScalingInverse() const
Return the affine transform applied to points after the polynomial transform.
m
ScaledPolynomialTransform(PolynomialTransform const &poly, afw::geom::AffineTransform const &inputScaling, afw::geom::AffineTransform const &outputScalingInverse)
Construct a new ScaledPolynomialTransform from its constituents.
static ScaledPolynomialTransform convert(PolynomialTransform const &poly)
Convert a PolynomialTransform to an equivalent ScaledPolynomialTransform.
friend PolynomialTransform compose(afw::geom::AffineTransform const &t1, PolynomialTransform const &t2)
Return a PolynomialTransform that is equivalent to the composition t1(t2())
ndarray::Array< double const, 2, 2 > getYCoeffs() const
2-D polynomial coefficients that compute the output x coordinate.
int getOrder() const
Return the order of the polynomials.
void computePowers(Eigen::VectorXd &r, double x)
Fill an array with integer powers of x, so .
A 2-d coordinate transform represented by a pair of standard polynomials (one for each coordinate)...
afw::geom::AffineTransform linearize(afw::geom::Point2D const &in) const
Return an approximate affine transform at the given point.