LSSTApplications  11.0-24-g0a022a1,14.0+64,15.0,15.0+1,15.0-1-g14e9bfd,15.0-1-g1eca518,15.0-1-g499c38d,15.0-1-g60afb23,15.0-1-g6668b0b,15.0-1-g788a293,15.0-1-g82223af,15.0-1-ga91101e,15.0-1-gae1598d,15.0-1-gc45031d,15.0-1-gd076f1f,15.0-1-gf4f1c34,15.0-1-gfe1617d,15.0-16-g953e39cab,15.0-2-g2010ef9,15.0-2-g33d94b3,15.0-2-g5218728,15.0-2-g947dc0d,15.0-3-g9103c06,15.0-3-ga03b4ca,15.0-3-ga659d1f3,15.0-3-ga695220+2,15.0-3-gaec6799,15.0-3-gb7a597c,15.0-3-gd5b9ff95,15.0-4-g0478fed+2,15.0-4-g45f767a,15.0-4-gff20472+2,15.0-6-ge2d9597
LSSTDataManagementBasePackage
RadialXYTransform.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
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 
25 #include <cmath>
26 #include "lsst/pex/exceptions.h"
28 #include "lsst/afw/geom/Angle.h"
29 #include <memory>
30 
31 namespace pexEx = lsst::pex::exceptions;
32 
33 namespace lsst {
34 namespace afw {
35 namespace geom {
36 
38  if (coeffs.size() == 0) {
39  // constructor called with no arguments = identity transformation
40  _coeffs.resize(2);
41  _coeffs[0] = 0.0;
42  _coeffs[1] = 1.0;
43  } else {
44  if ((coeffs.size() == 1) || (coeffs[0] != 0.0) || (coeffs[1] == 0.0)) {
45  // Discontinuous or singular transformation; presumably unintentional so throw exception
47  "invalid parameters for radial distortion: need coeffs.size() != 1, "
48  "coeffs[0]==0, coeffs[1]!=0");
49  }
50  _coeffs = coeffs;
51  }
52 
54 }
55 
61 
63  return std::make_shared<RadialXYTransform>(_coeffs);
64 }
65 
67  return std::make_shared<RadialXYTransform>(_coeffs);
68 }
69 
71 
73  return polyEvalInverse(_coeffs, _icoeffs, p);
74 }
75 
77  return polyEvalJacobian(_coeffs, p);
78 }
79 
82 }
83 
84 // --- Note: all subsequent RadialXYTransform member functions are static
85 
96  static const unsigned int maxN = 7; // degree of output polynomial + 1
97 
98  //
99  // Some sanity checks. The formulas for the inversion below assume c0 == 0 and c1 != 0
100  //
101  if (coeffs.size() <= 1 || coeffs.size() > maxN || coeffs[0] != 0.0 || coeffs[1] == 0.0)
103  "invalid parameters in RadialXYTransform::polyInvert");
104 
105  std::vector<double> c = coeffs;
106  c.resize(maxN, 0.0);
107 
108  std::vector<double> ic(maxN);
109 
110  ic[0] = 0.0;
111 
112  ic[1] = 1.0;
113  ic[1] /= c[1];
114 
115  ic[2] = -c[2];
116  ic[2] /= std::pow(c[1], 3);
117 
118  ic[3] = 2.0 * c[2] * c[2] - c[1] * c[3];
119  ic[3] /= std::pow(c[1], 5);
120 
121  ic[4] = 5.0 * c[1] * c[2] * c[3] - 5.0 * c[2] * c[2] * c[2] - c[1] * c[1] * c[4];
122  ic[4] /= std::pow(c[1], 7);
123 
124  ic[5] = 6.0 * c[1] * c[1] * c[2] * c[4] + 3.0 * c[1] * c[1] * c[3] * c[3] - c[1] * c[1] * c[1] * c[5] +
125  14.0 * std::pow(c[2], 4) - 21.0 * c[1] * c[2] * c[2] * c[3];
126  ic[5] /= std::pow(c[1], 9);
127 
128  ic[6] = 7.0 * c[1] * c[1] * c[1] * c[2] * c[5] + 84.0 * c[1] * c[2] * c[2] * c[2] * c[3] +
129  7.0 * c[1] * c[1] * c[1] * c[3] * c[4] - 28.0 * c[1] * c[1] * c[2] * c[3] * c[3] -
130  std::pow(c[1], 4) * c[6] - 28.0 * c[1] * c[1] * c[2] * c[2] * c[4] - 42.0 * std::pow(c[2], 5);
131  ic[6] /= std::pow(c[1], 11);
132 
133  return ic;
134 }
135 
136 double RadialXYTransform::polyEval(std::vector<double> const &coeffs, double x) {
137  int n = coeffs.size();
138 
139  double ret = 0.0;
140  for (int i = n - 1; i >= 0; i--) ret = ret * x + coeffs[i];
141 
142  return ret;
143 }
144 
146  double r = p.asEigen().norm();
147 
148  if (r > 0.0) {
149  double rnew = polyEval(coeffs, r);
150  return Point2D(rnew * p.getX() / r, rnew * p.getY() / r);
151  }
152 
153  if (coeffs.size() == 0 || coeffs[0] != 0.0) {
154  throw LSST_EXCEPT(pexEx::InvalidParameterError, "invalid parameters for radial distortion");
155  }
156 
157  return Point2D(0, 0);
158 }
159 
161  int n = coeffs.size();
162 
163  double ret = 0.0;
164  for (int i = n - 1; i >= 1; i--) ret = ret * x + i * coeffs[i];
165 
166  return ret;
167 }
168 
170  double r = p.asEigen().norm();
171  double rnew = polyEval(coeffs, r);
172  double rderiv = polyEvalDeriv(coeffs, r);
173  return makeAffineTransform(p.getX(), p.getY(), rnew, rderiv);
174 }
175 
177  std::vector<double> const &icoeffs, double x) {
178  static const int maxIter = 1000;
179  double tolerance = 1.0e-14 * x;
180 
181  double r = polyEval(icoeffs, x); // initial guess
182  int iter = 0;
183 
184  for (;;) {
185  double dx = x - polyEval(coeffs, r); // residual
186  if (fabs(dx) <= tolerance) return r;
187  if (iter++ > maxIter) {
189  "max iteration count exceeded in RadialXYTransform::polyEvalInverse");
190  }
191  r += dx / polyEvalDeriv(coeffs, r); // Newton-Raphson iteration
192  }
193 }
194 
196  std::vector<double> const &icoeffs, Point2D const &p) {
197  double r = p.asEigen().norm();
198 
199  if (r > 0.0) {
200  double rnew = polyEvalInverse(coeffs, icoeffs, r);
201  return Point2D(rnew * p.getX() / r, rnew * p.getY() / r);
202  }
203 
204  if (coeffs.size() == 0 || coeffs[0] != 0.0) {
205  throw LSST_EXCEPT(pexEx::InvalidParameterError, "invalid parameters for radial distortion");
206  }
207 
208  return Point2D(0, 0);
209 }
210 
212  std::vector<double> const &icoeffs,
213  Point2D const &p) {
214  double r = p.asEigen().norm();
215  double rnew = polyEvalInverse(coeffs, icoeffs, r);
216  double rderiv = 1.0 / polyEvalDeriv(coeffs, rnew);
217  return makeAffineTransform(p.getX(), p.getY(), rnew, rderiv);
218 }
219 
220 AffineTransform RadialXYTransform::makeAffineTransform(double x, double y, double rnew, double rderiv) {
221  double r = ::hypot(x, y);
222 
223  if (r <= 0.0) {
224  AffineTransform ret;
225  ret[0] = ret[3] = rderiv; // ret = rderiv * (identity)
226  return ret;
227  }
228 
229  //
230  // Note: calculation of "t" is numerically unstable as r->0, since p'(r) and p(r)/r will be
231  // nearly equal. However, detailed analysis shows that this is actually OK. The numerical
232  // instability means that the roundoff error in t is O(10^{-17}) even though t is formally O(r).
233  //
234  // Propagating through the formulas below, the AffineTransform is
235  // [rderiv*I + O(r) + O(10^{-17})] which is fine (assuming rderiv is nonzero as r->0).
236  //
237  double t = rderiv - rnew / r;
238 
239  AffineTransform ret;
240  ret[0] = (rderiv * x * x + rnew / r * y * y) / (r * r); // a00
241  ret[1] = ret[2] = t * x * y / (r * r); // a01 == a10 for this transform
242  ret[3] = (rderiv * y * y + rnew / r * x * x) / (r * r); // a11
243  ret[4] = -t * x; // v0
244  ret[5] = -t * y; // v1
245  return ret;
246 }
247 }
248 }
249 }
RadialXYTransform(std::vector< double > const &coeffs)
virtual AffineTransform linearizeReverseTransform(Point2D const &point) const
default implementation; subclass may override
std::vector< double > _coeffs
Definition: XYTransform.h:273
static std::vector< double > polyInvert(std::vector< double > const &coeffs)
These static member functions operate on polynomials represented by vector<double>.
static double polyEvalDeriv(std::vector< double > const &coeffs, double x)
static AffineTransform makeAffineTransform(double x, double y, double f, double g)
virtual std::shared_ptr< XYTransform > invert() const
returns a "deep inverse" in this sense that the forward+inverse transforms do not share state default...
Include files required for standard LSST Exception handling.
int y
Definition: SpanSet.cc:43
T resize(T... args)
RadialXYTransform & operator=(RadialXYTransform const &)
EigenVector const & asEigen() const
Return a fixed-size Eigen representation of the coordinate object.
A base class for image defects.
Definition: cameraGeom.dox:3
An affine coordinate transformation consisting of a linear transformation and an offset.
static double polyEval(std::vector< double > const &coeffs, double x)
double x
std::vector< double > _icoeffs
Definition: XYTransform.h:274
virtual Point2D reverseTransform(Point2D const &point) const
T size(T... args)
#define LSST_EXCEPT(type,...)
Create an exception with a given type and message and optionally other arguments (dependent on the ty...
Definition: Exception.h:46
virtual std::shared_ptr< XYTransform > clone() const
returns a deep copy
static double polyEvalInverse(std::vector< double > const &coeffs, std::vector< double > const &icoeffs, double x)
static AffineTransform polyEvalInverseJacobian(std::vector< double > const &coeffs, std::vector< double > const &icoeffs, Point2D const &p)
T pow(T... args)
A purely radial polynomial distortion, up to 6th order.
Definition: XYTransform.h:225
Virtual base class for 2D transforms.
Definition: XYTransform.h:49
virtual AffineTransform linearizeForwardTransform(Point2D const &point) const
linearized forward and reversed transforms
static AffineTransform polyEvalJacobian(std::vector< double > const &coeffs, Point2D const &p)
virtual Point2D forwardTransform(Point2D const &point) const
virtuals for forward and reverse transforms
afw::geom::Point2D Point2D
Definition: XYTransform.h:51