LSSTApplications  16.0-10-g4f78f78+16,16.0-10-gc1446dd+42,16.0-11-g09ed895+1,16.0-13-g7649090,16.0-14-g0a28612+1,16.0-14-g6c7ed55+16,16.0-15-ga29f190+1,16.0-16-g89065d4+14,16.0-16-gd8e3590+16,16.0-16-ge6a35c8+6,16.0-17-g7e0e4ff+10,16.0-17-ga3d2e9f,16.0-19-gb830ed4e+16,16.0-2-g0febb12+21,16.0-2-g9d5294e+61,16.0-2-ga8830df+5,16.0-24-gc1c7f52+9,16.0-25-g07af9f2+1,16.0-3-ge00e371+21,16.0-36-g07840cb1,16.0-4-g18f3627+5,16.0-4-g5f3a788+20,16.0-4-ga3eb747+10,16.0-4-gabf74b7+16,16.0-4-gade8416+9,16.0-4-gb13d127+5,16.0-5-g6a53317+21,16.0-5-gb3f8a4b+74,16.0-5-gef99c9f+12,16.0-6-g9321be7+4,16.0-6-gcbc7b31+22,16.0-6-gf49912c+16,16.0-63-gae20905ba,16.0-7-gd2eeba5+31,16.0-8-g21fd5fe+16,16.0-8-g3a9f023+12,16.0-8-g4734f7a,16.0-9-g85d1a16+16,16.0-9-gf5c1f43,master-g07ce7b41a7,w.2018.48
LSSTDataManagementBasePackage
PhotometryTransfo.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 /*
3  * This file is part of jointcal.
4  *
5  * Developed for the LSST Data Management System.
6  * This product includes software developed by the LSST Project
7  * (https://www.lsst.org).
8  * See the COPYRIGHT file at the top-level directory of this distribution
9  * for details of code ownership.
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <https://www.gnu.org/licenses/>.
23  */
24 
25 #include "ndarray.h"
26 #include "Eigen/Core"
27 
29 
30 #include "lsst/jointcal/Point.h"
32 
33 namespace lsst {
34 namespace jointcal {
35 
36 // ------------------ PhotometryTransfoChebyshev helpers ---------------------------------------------------
37 
38 namespace {
39 
40 // To evaluate a 1-d Chebyshev function without needing to have workspace, we use the
41 // Clenshaw algorith, which is like going through the recurrence relation in reverse.
42 // The CoeffGetter argument g is something that behaves like an array, providing access
43 // to the coefficients.
44 template <typename CoeffGetter>
45 double evaluateFunction1d(CoeffGetter g, double x, int size) {
46  double b_kp2 = 0.0, b_kp1 = 0.0;
47  for (int k = (size - 1); k > 0; --k) {
48  double b_k = g[k] + 2 * x * b_kp1 - b_kp2;
49  b_kp2 = b_kp1;
50  b_kp1 = b_k;
51  }
52  return g[0] + x * b_kp1 - b_kp2;
53 }
54 
55 // This class imitates a 1-d array, by running evaluateFunction1d on a nested dimension;
56 // this lets us reuse the logic in evaluateFunction1d for both dimensions. Essentially,
57 // we run evaluateFunction1d on a column of coefficients to evaluate T_i(x), then pass
58 // the result of that to evaluateFunction1d with the results as the "coefficients" associated
59 // with the T_j(y) functions.
60 struct RecursionArrayImitator {
61  double operator[](int i) const {
62  return evaluateFunction1d(coefficients[i], x, coefficients.getSize<1>());
63  }
64 
65  RecursionArrayImitator(ndarray::Array<double const, 2, 2> const &coefficients_, double x_)
66  : coefficients(coefficients_), x(x_) {}
67 
68  ndarray::Array<double const, 2, 2> coefficients;
69  double x;
70 };
71 
72 // Compute an affine transform that maps an arbitrary box to [-1,1]x[-1,1]
73 afw::geom::AffineTransform makeChebyshevRangeTransform(afw::geom::Box2D const &bbox) {
74  return afw::geom::AffineTransform(
75  afw::geom::LinearTransform::makeScaling(2.0 / bbox.getWidth(), 2.0 / bbox.getHeight()),
76  afw::geom::Extent2D(-(2.0 * bbox.getCenterX()) / bbox.getWidth(),
77  -(2.0 * bbox.getCenterY()) / bbox.getHeight()));
78 }
79 
80 // Initialize a "unit" Chebyshev
81 ndarray::Array<double, 2, 2> _initializeChebyshev(size_t order, bool identity) {
82  ndarray::Array<double, 2, 2> coeffs = ndarray::allocate(ndarray::makeVector(order + 1, order + 1));
83  coeffs.deep() = 0.0;
84  if (identity) {
85  coeffs[0][0] = 1;
86  }
87  return coeffs;
88 }
89 } // namespace
90 
92  bool identity)
93  : _bbox(bbox),
94  _toChebyshevRange(makeChebyshevRangeTransform(bbox)),
95  _coefficients(_initializeChebyshev(order, identity)),
96  _order(order),
97  _nParameters((order + 1) * (order + 2) / 2) {}
98 
100  afw::geom::Box2D const &bbox)
101  : _bbox(bbox),
102  _toChebyshevRange(makeChebyshevRangeTransform(bbox)),
103  _coefficients(coefficients),
104  _order(coefficients.size() - 1),
105  _nParameters((_order + 1) * (_order + 2) / 2) {}
106 
107 void PhotometryTransfoChebyshev::offsetParams(Eigen::VectorXd const &delta) {
108  // NOTE: the indexing in this method and computeParameterDerivatives must be kept consistent!
109  Eigen::VectorXd::Index k = 0;
110  for (ndarray::Size j = 0; j <= _order; ++j) {
111  ndarray::Size const iMax = _order - j; // to save re-computing `i+j <= order` every inner step.
112  for (ndarray::Size i = 0; i <= iMax; ++i, ++k) {
113  _coefficients[j][i] -= delta[k];
114  }
115  }
116 }
117 
118 namespace {
119 // The integral of T_n(x) over [-1,1]:
120 // https://en.wikipedia.org/wiki/Chebyshev_polynomials#Differentiation_and_integration
121 double integrateTn(int n) {
122  if (n % 2 == 1)
123  return 0;
124  else
125  return 2.0 / (1.0 - static_cast<double>(n * n));
126 }
127 } // namespace
128 
129 double PhotometryTransfoChebyshev::integrate() const {
130  double result = 0;
131  double determinant = _bbox.getArea() / 4.0;
132  for (ndarray::Size j = 0; j < _coefficients.getSize<0>(); j++) {
133  for (ndarray::Size i = 0; i < _coefficients.getSize<1>(); i++) {
134  result += _coefficients[j][i] * integrateTn(i) * integrateTn(j);
135  }
136  }
137  return result * determinant;
138 }
139 
140 double PhotometryTransfoChebyshev::mean() const { return integrate() / _bbox.getArea(); }
141 
143  Eigen::VectorXd parameters(_nParameters);
144  // NOTE: the indexing in this method and offsetParams must be kept consistent!
145  Eigen::VectorXd::Index k = 0;
146  for (ndarray::Size j = 0; j <= _order; ++j) {
147  ndarray::Size const iMax = _order - j; // to save re-computing `i+j <= order` every inner step.
148  for (ndarray::Size i = 0; i <= iMax; ++i, ++k) {
149  parameters[k] = _coefficients[j][i];
150  }
151  }
152 
153  return parameters;
154 }
155 
156 double PhotometryTransfoChebyshev::computeChebyshev(double x, double y) const {
157  afw::geom::Point2D p = _toChebyshevRange(afw::geom::Point2D(x, y));
158  return evaluateFunction1d(RecursionArrayImitator(_coefficients, p.getX()), p.getY(),
159  _coefficients.getSize<0>());
160 }
161 
163  Eigen::Ref<Eigen::VectorXd> derivatives) const {
164  afw::geom::Point2D p = _toChebyshevRange(afw::geom::Point2D(x, y));
165  // Algorithm: compute all the individual components recursively (since we'll need them anyway),
166  // then combine them into the final answer vectors.
167  Eigen::VectorXd Tnx(_order + 1);
168  Eigen::VectorXd Tmy(_order + 1);
169  Tnx[0] = 1;
170  Tmy[0] = 1;
171  if (_order >= 1) {
172  Tnx[1] = p.getX();
173  Tmy[1] = p.getY();
174  }
175  for (ndarray::Size i = 2; i <= _order; ++i) {
176  Tnx[i] = 2 * p.getX() * Tnx[i - 1] - Tnx[i - 2];
177  Tmy[i] = 2 * p.getY() * Tmy[i - 1] - Tmy[i - 2];
178  }
179 
180  // NOTE: the indexing in this method and offsetParams must be kept consistent!
181  Eigen::VectorXd::Index k = 0;
182  for (ndarray::Size j = 0; j <= _order; ++j) {
183  ndarray::Size const iMax = _order - j; // to save re-computing `i+j <= order` every inner step.
184  for (ndarray::Size i = 0; i <= iMax; ++i, ++k) {
185  derivatives[k] = Tmy[j] * Tnx[i];
186  }
187  }
188 }
189 
190 } // namespace jointcal
191 } // namespace lsst
double x
ndarray::Array< double const, 2, 2 > coefficients
Eigen::VectorXd getParameters() const override
Get a copy of the parameters of this model, in the same order as offsetParams.
A floating-point coordinate rectangle geometry.
Definition: Box.h:294
py::object result
Definition: schema.cc:284
int y
Definition: SpanSet.cc:49
A base class for image defects.
Definition: cameraGeom.dox:3
table::Box2IKey bbox
Definition: Detector.cc:166
static LinearTransform makeScaling(double s) noexcept
double getArea() const noexcept
Definition: Box.h:400
PhotometryTransfoChebyshev(size_t order, afw::geom::Box2D const &bbox, bool identity)
Create a Chebyshev transfo with terms up to order in (x*y).
void offsetParams(Eigen::VectorXd const &delta) override
Offset the parameters by some (negative) amount during fitting.
void computeChebyshevDerivatives(double x, double y, Eigen::Ref< Eigen::VectorXd > derivatives) const
Set the derivatives of this polynomial at x,y.
Extent< double, 2 > Extent2D
Definition: Extent.h:400
double computeChebyshev(double x, double y) const
Return the value of this polynomial at x,y.