26 #include "ndarray/eigen.h"
35 namespace lsst {
namespace afw {
namespace math {
62 _toChebyshevRange(makeChebyshevRangeTransform(geom::Box2D(bbox))),
63 _coefficients(coefficients)
69 _toChebyshevRange(makeChebyshevRangeTransform(geom::Box2D(bbox)))
81 ndarray::Array<double,1,1>
const & t,
84 int const n = t.getSize<0>();
91 for (
int i = 2; i <
n; ++i) {
92 t[i] = 2.0*x*t[i-1] - t[i-2];
100 ndarray::Array<double,2,2> makeMatrix(
101 ndarray::Array<double const,1>
const & x,
102 ndarray::Array<double const,1>
const &
y,
104 Packer
const & packer,
107 int const nPoints = x.getSize<0>();
108 ndarray::Array<double,1,1> tx = ndarray::allocate(packer.nx);
109 ndarray::Array<double,1,1> ty = ndarray::allocate(packer.ny);
110 ndarray::Array<double,2,2> out = ndarray::allocate(nPoints, packer.size);
113 for (
int p = 0; p < nPoints; ++p) {
115 evaluateBasis1d(tx, sxy.getX());
116 evaluateBasis1d(ty, sxy.getY());
117 packer.pack(out[p], tx, ty);
126 ndarray::Array<double,2,2> makeMatrix(
129 Packer
const & packer,
133 ndarray::Array<double,2,2> tx = ndarray::allocate(bbox.
getWidth(), packer.nx);
143 ndarray::Array<double,2,2> out = ndarray::allocate(bbox.
getArea(),packer.size);
144 ndarray::Array<double,2,2>::Iterator outIter = out.begin();
145 ndarray::Array<double,1,1> ty = ndarray::allocate(ctrl.orderY + 1);
151 for (
int j = 0; j < bbox.
getWidth(); ++j, ++outIter) {
153 packer.pack(*outIter, tx[j], ty);
162 afw::geom::Box2I const & bbox,
163 ndarray::Array<
double const,1> const & x,
164 ndarray::Array<
double const,1> const & y,
165 ndarray::Array<
double const,1> const & z,
172 Packer
const packer(ctrl);
174 ndarray::Array<double,2,2> matrix = makeMatrix(x, y, result->_toChebyshevRange, packer, ctrl);
178 result->_coefficients = packer.unpack(lstsq.
getSolution());
183 afw::geom::Box2I const & bbox,
184 ndarray::Array<
double const,1> const & x,
185 ndarray::Array<
double const,1> const & y,
186 ndarray::Array<
double const,1> const & z,
187 ndarray::Array<
double const,1> const &
w,
194 Packer
const packer(ctrl);
196 ndarray::Array<double,2,2> matrix = makeMatrix(x, y, result->_toChebyshevRange, packer, ctrl);
199 matrix.asEigen<Eigen::ArrayXpr>().colwise() *= w.asEigen<Eigen::ArrayXpr>();
200 ndarray::Array<double,1,1> wz = ndarray::copy(z);
201 wz.asEigen<Eigen::ArrayXpr>() *= w.asEigen<Eigen::ArrayXpr>();
205 result->_coefficients = packer.unpack(lstsq.
getSolution());
209 template <
typename T>
211 image::Image<T> const & img,
219 Packer
const packer(ctrl);
220 ndarray::Array<double,2,2> matrix = makeMatrix(bbox, result->_toChebyshevRange, packer, ctrl);
222 ndarray::Array<double,2,2> imgCopy = ndarray::allocate(img.getArray().getShape());
223 imgCopy.deep() = img.getArray();
224 ndarray::Array<double const,1,1> z = ndarray::flatten<1>(imgCopy);
228 result->_coefficients = packer.unpack(lstsq.getSolution());
235 if (static_cast<std::size_t>(ctrl.orderX) >= _coefficients.getSize<1>()) {
237 pex::exceptions::LengthError,
239 % ctrl.orderX % (_coefficients.getSize<1>() - 1)).str()
242 if (static_cast<std::size_t>(ctrl.orderY) >= _coefficients.getSize<0>()) {
244 pex::exceptions::LengthError,
246 % ctrl.orderY % (_coefficients.getSize<0>() - 1)).str()
249 ndarray::Array<double,2,2>
coefficients = ndarray::allocate(ctrl.orderY + 1, ctrl.orderX + 1);
251 = _coefficients[ndarray::view(0, ctrl.orderY + 1)(0, ctrl.orderX + 1)];
252 if (ctrl.triangular) {
254 ndarray::Array<double,1,1> packed = ndarray::allocate(packer.size);
255 packer.pack(packed, coefficients);
256 packer.unpack(coefficients, packed);
258 return std::make_shared<ChebyshevBoundedField>(getBBox(),
coefficients);
262 return std::make_shared<ChebyshevBoundedField>(bbox, _coefficients);
274 template <
typename CoeffGetter>
275 double evaluateFunction1d(
280 double b_kp2=0.0, b_kp1=0.0;
281 for (
int k = (size - 1); k > 0; --k) {
282 double b_k = g[k] + 2*x*b_kp1 - b_kp2;
286 return g[0] + x*b_kp1 - b_kp2;
294 struct RecursionArrayImitator {
296 double operator[](
int i)
const {
300 RecursionArrayImitator(ndarray::Array<double const,2,2>
const & coefficients_,
double x_) :
312 return evaluateFunction1d(RecursionArrayImitator(
_coefficients, p.getX()),
320 struct PersistenceHelper {
327 PersistenceHelper(
int nx,
int ny) :
329 orderX(
schema.addField<int>(
"order_x",
"maximum Chebyshev function order in x")),
330 bboxMin(table::PointKey<int>::addFields(
331 schema,
"bbox_min",
"lower-left corner of bounding box",
"pixel")),
332 bboxMax(table::PointKey<int>::addFields(
333 schema,
"bbox_max",
"upper-right corner of bounding box",
"pixel")),
335 schema.addField< table::Array<double> >(
336 "coefficients",
"Chebyshev function coefficients, ordered by y then x", nx*ny
341 PersistenceHelper(table::Schema
const & s) :
351 class ChebyshevBoundedFieldFactory :
public table::io::PersistableFactory {
354 virtual PTR(table::io::Persistable)
355 read(InputArchive const & archive, CatalogVector const & catalogs)
const {
358 table::BaseRecord
const & record = catalogs.front().front();
359 PersistenceHelper
const keys(record.getSchema());
360 geom::Box2I bbox(record.get(keys.bboxMin), record.get(keys.bboxMax));
361 int nx = record.get(keys.orderX) + 1;
362 int ny = keys.coefficients.getSize() / nx;
364 ndarray::Array<double,2,2>
coefficients = ndarray::allocate(ny, nx);
365 ndarray::flatten<1>(
coefficients) = record.get(keys.coefficients);
366 return std::make_shared<ChebyshevBoundedField>(bbox,
coefficients);
369 ChebyshevBoundedFieldFactory(std::string
const &
name) : afw::table::io::PersistableFactory(name) {}
373 std::string getChebyshevBoundedFieldPersistenceName() {
return "ChebyshevBoundedField"; }
375 ChebyshevBoundedFieldFactory registration(getChebyshevBoundedFieldPersistenceName());
380 return getChebyshevBoundedFieldPersistenceName();
384 return "lsst.afw.math";
392 record->set(keys.bboxMin,
getBBox().getMin());
393 record->set(keys.bboxMax,
getBBox().getMax());
394 (*record)[keys.coefficients].deep() = ndarray::flatten<1>(
_coefficients);
399 return std::make_shared<ChebyshevBoundedField>(getBBox(), ndarray::copy(getCoefficients()*scale));
406 #define INSTANTIATE(T) \
407 template PTR(ChebyshevBoundedField) ChebyshevBoundedField::fit( \
408 image::Image<T> const & image, \
409 Control const & ctrl \
Extent< int, N > truncate(Extent< double, N > const &input)
Return the component-wise truncation (round towards zero).
table::PointKey< int > bboxMax
table::Key< std::string > name
ndarray::Array< double const, 2, 2 > _coefficients
An object passed to Persistable::write to allow it to persist itself.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
A custom container class for records, based on std::vector.
afw::table::Schema schema
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
virtual std::string getPythonModule() const
Return the fully-qualified Python module that should be imported to guarantee that its factory is reg...
double getCenterX() const
A control object used when fitting ChebyshevBoundedField to data (see ChebyshevBoundedField::fit) ...
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
virtual void write(OutputArchiveHandle &handle) const
Write the object to one or more catalogs.
Solver for linear least-squares problems.
double getCenterY() const
static LeastSquares fromDesignMatrix(ndarray::Array< T1, 2, C1 > const &design, ndarray::Array< T2, 1, C2 > const &data, Factorization factorization=NORMAL_EIGENSYSTEM)
Initialize from the design matrix and data vector given as ndarrays.
int computeSize() const
Return the number of nonzero coefficients in the Chebyshev function defined by this object...
A coordinate class intended to represent absolute positions.
geom::Box2I getBBox() const
Return the bounding box that defines the region where the field is valid.
An integer coordinate rectangle.
table::Key< table::Array< Kernel::Pixel > > image
boost::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
metadata import lsst afw display as afwDisplay n
geom::AffineTransform _toChebyshevRange
Use the normal equations with a symmetric Eigensystem decomposition.
virtual double evaluate(geom::Point2D const &position) const
Evaluate the field at the given point.
table::PointKey< int > bboxMin
virtual std::string getPersistenceName() const
Return the unique name used to persist this object and look up its factory.
#define LSST_EXCEPT(type,...)
Create an exception with a given type and message and optionally other arguments (dependent on the ty...
Base class for all records.
A BoundedField based on 2-d Chebyshev polynomials of the first kind.
ChebyshevBoundedField(afw::geom::Box2I const &bbox, ndarray::Array< double const, 2, 2 > const &coefficients)
Initialize the field from its bounding box an coefficients.
An abstract base class for 2-d functions defined on an integer bounding boxes.
ndarray::Array< double const, 1, 1 > getSolution()
Return the vector solution to the least squares problem.
A floating-point coordinate rectangle geometry.
ndarray::Array< double const, 2, 2 > coefficients
A helper class ChebyshevBoundedField, for mapping trapezoidal matrices to 1-d arrays.