LSSTApplications  11.0-24-g0a022a1,14.0+77,15.0,15.0+1
LSSTDataManagementBasePackage
PhotometryTransfo.cc
Go to the documentation of this file.
1 #include "ndarray.h"
2 #include "Eigen/Core"
3 
5 
6 #include "lsst/jointcal/Point.h"
8 
9 namespace lsst {
10 namespace jointcal {
11 
12 // ------------------ PhotometryTransfoChebyshev helpers ---------------------------------------------------
13 
14 namespace {
15 
16 // To evaluate a 1-d Chebyshev function without needing to have workspace, we use the
17 // Clenshaw algorith, which is like going through the recurrence relation in reverse.
18 // The CoeffGetter argument g is something that behaves like an array, providing access
19 // to the coefficients.
20 template <typename CoeffGetter>
21 double evaluateFunction1d(CoeffGetter g, double x, int size) {
22  double b_kp2 = 0.0, b_kp1 = 0.0;
23  for (int k = (size - 1); k > 0; --k) {
24  double b_k = g[k] + 2 * x * b_kp1 - b_kp2;
25  b_kp2 = b_kp1;
26  b_kp1 = b_k;
27  }
28  return g[0] + x * b_kp1 - b_kp2;
29 }
30 
31 // This class imitates a 1-d array, by running evaluateFunction1d on a nested dimension;
32 // this lets us reuse the logic in evaluateFunction1d for both dimensions. Essentially,
33 // we run evaluateFunction1d on a column of coefficients to evaluate T_i(x), then pass
34 // the result of that to evaluateFunction1d with the results as the "coefficients" associated
35 // with the T_j(y) functions.
36 struct RecursionArrayImitator {
37  double operator[](int i) const {
38  return evaluateFunction1d(coefficients[i], x, coefficients.getSize<1>());
39  }
40 
41  RecursionArrayImitator(ndarray::Array<double const, 2, 2> const &coefficients_, double x_)
42  : coefficients(coefficients_), x(x_) {}
43 
44  ndarray::Array<double const, 2, 2> coefficients;
45  double x;
46 };
47 
48 // Compute an affine transform that maps an arbitrary box to [-1,1]x[-1,1]
49 afw::geom::AffineTransform makeChebyshevRangeTransform(afw::geom::Box2D const &bbox) {
50  return afw::geom::AffineTransform(
51  afw::geom::LinearTransform::makeScaling(2.0 / bbox.getWidth(), 2.0 / bbox.getHeight()),
52  afw::geom::Extent2D(-(2.0 * bbox.getCenterX()) / bbox.getWidth(),
53  -(2.0 * bbox.getCenterY()) / bbox.getHeight()));
54 }
55 
56 // Initialize a "unit" Chebyshev
57 ndarray::Array<double, 2, 2> _identityChebyshev(size_t degree) {
58  ndarray::Array<double, 2, 2> coeffs = ndarray::allocate(ndarray::makeVector(degree + 1, degree + 1));
59  coeffs.deep() = 0.0;
60  coeffs[0][0] = 1;
61  return coeffs;
62 }
63 } // namespace
64 
66  : _bbox(bbox),
67  _toChebyshevRange(makeChebyshevRangeTransform(bbox)),
68  _coefficients(_identityChebyshev(degree)),
69  _degree(degree),
70  _nParameters((degree + 1) * (degree + 2) / 2) {}
71 
73  afw::geom::Box2D const &bbox)
74  : _bbox(bbox),
75  _toChebyshevRange(makeChebyshevRangeTransform(bbox)),
76  _coefficients(coefficients),
77  _degree(coefficients.size() - 1),
78  _nParameters((_degree + 1) * (_degree + 2) / 2) {}
79 
80 double PhotometryTransfoChebyshev::transform(double x, double y, double instFlux) const {
82  return instFlux * evaluateFunction1d(RecursionArrayImitator(_coefficients, p.getX()), p.getY(),
83  _coefficients.getSize<0>());
84 }
85 
86 void PhotometryTransfoChebyshev::offsetParams(Eigen::VectorXd const &delta) {
87  // NOTE: the indexing in this method and computeParameterDerivatives must be kept consistent!
88  Eigen::VectorXd::Index k = 0;
89  for (ndarray::Size j = 0; j <= _degree; ++j) {
90  ndarray::Size const iMax = _degree - j; // to save re-computing `i+j <= degree` every inner step.
91  for (ndarray::Size i = 0; i <= iMax; ++i, ++k) {
92  _coefficients[j][i] -= delta[k];
93  }
94  }
95 }
96 
97 namespace {
98 // The integral of T_n(x) over [-1,1]:
99 // https://en.wikipedia.org/wiki/Chebyshev_polynomials#Differentiation_and_integration
100 double integrateTn(int n) {
101  if (n % 2 == 1)
102  return 0;
103  else
104  return 2.0 / (1.0 - static_cast<double>(n * n));
105 }
106 } // namespace
107 
109  double result = 0;
110  double determinant = _bbox.getArea() / 4.0;
111  for (ndarray::Size j = 0; j < _coefficients.getSize<0>(); j++) {
112  for (ndarray::Size i = 0; i < _coefficients.getSize<1>(); i++) {
113  result += _coefficients[j][i] * integrateTn(i) * integrateTn(j);
114  }
115  }
116  return result * determinant;
117 }
118 
119 double PhotometryTransfoChebyshev::mean() const { return integrate() / _bbox.getArea(); }
120 
121 void PhotometryTransfoChebyshev::computeParameterDerivatives(double x, double y, double instFlux,
122  Eigen::Ref<Eigen::VectorXd> derivatives) const {
124  // Algorithm: compute all the individual components recursively (since we'll need them anyway),
125  // then combine them into the final answer vectors.
126  Eigen::VectorXd Tnx(_degree + 1);
127  Eigen::VectorXd Tmy(_degree + 1);
128  Tnx[0] = 1;
129  Tmy[0] = 1;
130  if (_degree >= 1) {
131  Tnx[1] = p.getX();
132  Tmy[1] = p.getY();
133  }
134  for (ndarray::Size i = 2; i <= _degree; ++i) {
135  Tnx[i] = 2 * p.getX() * Tnx[i - 1] - Tnx[i - 2];
136  Tmy[i] = 2 * p.getY() * Tmy[i - 1] - Tmy[i - 2];
137  }
138 
139  // NOTE: the indexing in this method and offsetParams must be kept consistent!
140  Eigen::VectorXd::Index k = 0;
141  for (ndarray::Size j = 0; j <= _degree; ++j) {
142  ndarray::Size const iMax = _degree - j; // to save re-computing `i+j <= degree` every inner step.
143  for (ndarray::Size i = 0; i <= iMax; ++i, ++k) {
144  derivatives[k] = instFlux * Tmy[j] * Tnx[i];
145  }
146  }
147 }
148 
150  Eigen::VectorXd parameters(_nParameters);
151  // NOTE: the indexing in this method and offsetParams must be kept consistent!
152  Eigen::VectorXd::Index k = 0;
153  for (ndarray::Size j = 0; j <= _degree; ++j) {
154  for (ndarray::Size i = 0; i + j <= _degree; ++i, ++k) {
155  parameters[k] = _coefficients[j][i];
156  }
157  }
158 
159  return parameters;
160 }
161 
162 } // namespace jointcal
163 } // namespace lsst
Eigen::VectorXd getParameters() const override
Get a copy of the parameters of this model, in the same order as offsetParams.
Extent< double, 2 > Extent2D
Definition: Extent.h:383
static LinearTransform makeScaling(double s)
ndarray::Array< double, 2, 2 > _coefficients
table::Key< table::Array< int > > degree
Definition: PsfexPsf.cc:346
A base class for image defects.
Definition: cameraGeom.dox:3
geom::Box2I const & _bbox
Definition: fits_io_mpl.h:84
afw::geom::AffineTransform _toChebyshevRange
void computeParameterDerivatives(double x, double y, double instFlux, Eigen::Ref< Eigen::VectorXd > derivatives) const override
Compute the derivatives with respect to the parameters (i.e.
void offsetParams(Eigen::VectorXd const &delta) override
Offset the parameters by some (negative) amount during fitting.
double x
ndarray::Array< double const, 2, 2 > coefficients
table::Box2IKey bbox
double getArea() const
Definition: Box.h:356
A floating-point coordinate rectangle geometry.
Definition: Box.h:266
PhotometryTransfoChebyshev(size_t degree, afw::geom::Box2D const &bbox)
Create an identity (a_0,0==1) Chebyshev transfo with terms up to degree in (x*y). ...
double transform(double x, double y, double instFlux) const override
Apply the transform to instFlux at (x,y), put result in flux.