27 #include "Eigen/Cholesky" 39 LOG_LOGGER _log =
LOG_GET(
"meas.astrom.sip");
61 indexToPQ(
int const index,
int const order)
65 for (
int decrement = order; q >= decrement && decrement > 0; --decrement) {
74 calculateCMatrix(Eigen::VectorXd
const& axis1, Eigen::VectorXd
const& axis2,
int const order)
76 int nTerms = 0.5*order*(order+1);
78 int const n = axis1.size();
79 Eigen::MatrixXd C = Eigen::MatrixXd::Zero(n, nTerms);
80 for (
int i = 0; i < n; ++i) {
81 for (
int j = 0; j < nTerms; ++j) {
83 int p = pq.first, q = pq.second;
85 assert(p + q < order);
86 C(i, j) = ::pow(axis1[i], p)*::pow(axis2[i], q);
100 leastSquaresSolve(Eigen::VectorXd b, Eigen::MatrixXd A) {
101 assert(A.rows() == b.rows());
102 Eigen::VectorXd par = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
109 template<
class MatchT>
120 _linearWcs(linearWcs.
clone()),
122 _reverseSipOrder(order+2),
123 _sipA(Eigen::MatrixXd::Zero(_sipOrder, _sipOrder)),
124 _sipB(Eigen::MatrixXd::Zero(_sipOrder, _sipOrder)),
125 _sipAp(Eigen::MatrixXd::Zero(_reverseSipOrder, _reverseSipOrder)),
126 _sipBp(Eigen::MatrixXd::Zero(_reverseSipOrder, _reverseSipOrder)),
134 str(boost::format(
"SIP forward order %d exceeds the convention limit of 9") %
137 if (_reverseSipOrder > 9) {
139 str(boost::format(
"SIP reverse order %d exceeds the convention limit of 9") %
148 _ngrid = 5*_sipOrder;
161 ptr != _matches.
end();
167 float const borderFrac = 1/::sqrt(_matches.
size());
173 _calculateForwardMatrices();
178 Eigen::MatrixXd CD = _linearWcs->getCDMatrix();
182 _calculateReverseMatrices();
189 template<
class MatchT>
197 int const nPoints = _matches.
size();
198 Eigen::VectorXd u(nPoints), v(nPoints), iwc1(nPoints), iwc2(nPoints);
203 ptr != _matches.
end();
214 u[i] = match.
second->getX() - crpix[0];
215 v[i] = match.
second->getY() - crpix[1];
218 double uMax = u.cwiseAbs().maxCoeff();
219 double vMax = v.cwiseAbs().maxCoeff();
226 Eigen::MatrixXd forwardC = calculateCMatrix(u, v, ord);
227 Eigen::VectorXd mu = leastSquaresSolve(iwc1, forwardC);
228 Eigen::VectorXd nu = leastSquaresSolve(iwc2, forwardC);
242 CD(1,0) = nu[ord]/norm;
243 CD(1,1) = nu[1]/norm;
244 CD(0,0) = mu[ord]/norm;
245 CD(0,1) = mu[1]/norm;
247 Eigen::Matrix2d CDinv = CD.inverse();
250 crpix[0] -= mu[0]*CDinv(0,0) + nu[0]*CDinv(0,1);
251 crpix[1] -= mu[0]*CDinv(1,0) + nu[0]*CDinv(1,1);
267 for(
int i=1; i< mu.rows(); ++i) {
269 int p = pq.first, q = pq.second;
271 if(p + q > 1 && p + q < ord) {
272 Eigen::Vector2d munu(2,1);
275 Eigen::Vector2d AB = CDinv*munu;
277 _sipA(p,q) = AB[0]/::pow(norm,p+q);
278 _sipB(p,q) = AB[1]/::pow(norm,p+q);
283 template<
class MatchT>
285 int const ngrid2 = _ngrid*_ngrid;
287 Eigen::VectorXd U(ngrid2), V(ngrid2);
288 Eigen::VectorXd delta1(ngrid2), delta2(ngrid2);
290 int const x0 = _bbox.
getMinX();
291 double const dx = _bbox.
getWidth()/(double)(_ngrid - 1);
292 int const y0 = _bbox.
getMinY();
293 double const dy = _bbox.
getHeight()/(double)(_ngrid - 1);
298 LOGL_DEBUG(_log,
"_calcReverseMatrices: x0,y0 %i,%i, W,H %i,%i, ngrid %i, dx,dy %g,%g, CRPIX %g,%g",
299 x0, y0, _bbox.
getWidth(), _bbox.
getHeight(), _ngrid, dx, dy, crpix[0], crpix[1]);
302 for (
int i = 0; i < _ngrid; ++i) {
303 double const y = y0 + i*dy;
304 for (
int j = 0; j < _ngrid; ++j, ++k) {
305 double const x = x0 + j*dx;
317 U[k] = xy[0] - 1 - crpix[0];
318 V[k] = xy[1] - 1 - crpix[1];
320 if ((i == 0 || i == (_ngrid-1) || i == (_ngrid/2)) &&
321 (j == 0 || j == (_ngrid-1) || j == (_ngrid/2))) {
322 LOGL_DEBUG(_log,
" x,y (%.1f, %.1f), u,v (%.1f, %.1f), U,V (%.1f, %.1f)", x, y, u, v, U[k], V[k]);
325 delta1[k] = u - U[k];
326 delta2[k] = v - V[k];
331 double UMax = U.cwiseAbs().maxCoeff();
332 double VMax = V.cwiseAbs().maxCoeff();
333 double norm = (UMax > VMax) ? UMax : VMax;
338 int const ord = _reverseSipOrder;
339 Eigen::MatrixXd reverseC = calculateCMatrix(U, V, ord);
340 Eigen::VectorXd tmpA = leastSquaresSolve(delta1, reverseC);
341 Eigen::VectorXd tmpB = leastSquaresSolve(delta2, reverseC);
343 assert(tmpA.rows() == tmpB.rows());
344 for(
int j=0; j< tmpA.rows(); ++j) {
346 int p = pq.first, q = pq.second;
348 _sipAp(p, q) = tmpA[j]/::pow(norm,p+q);
349 _sipBp(p, q) = tmpB[j]/::pow(norm,p+q);
353 template<
class MatchT>
355 assert(_newWcs.get());
359 template<
class MatchT>
361 assert(_linearWcs.get());
365 template<
class MatchT>
367 assert(_newWcs.get());
372 template<
class MatchT>
374 assert(_linearWcs.get());
379 template<
class MatchT>
386 #define INSTANTIATE(MATCH) \ 387 template class CreateWcsWithSip<MATCH>;
double getScatterInPixels() const
Compute the median separation, in pixels, between items in this object's match list.
AngleUnit constexpr radians
void include(Point2I const &point)
table::PointKey< double > crval
lsst::afw::geom::Point2D getPosition(lsst::afw::geom::AngleUnit unit=lsst::afw::geom::degrees) const
std::shared_ptr< Record2 > second
CreateWcsWithSip(std::vector< MatchT > const &matches, afw::image::Wcs const &linearWcs, int const order, afw::geom::Box2I const &bbox=afw::geom::Box2I(), int const ngrid=0)
Construct a CreateWcsWithSip.
afw::math::Statistics makeMatchStatisticsInPixels(afw::image::Wcs const &wcs, std::vector< MatchT > const &matchList, int const flags, afw::math::StatisticsControl const &sctrl=afw::math::StatisticsControl())
Compute statistics of on-detector radial separation for a match list, in pixels.
std::shared_ptr< Record1 > first
afw::geom::Angle getScatterOnSky() const
Compute the median on-sky separation between items in this object's match list.
double getValue(Property const prop=NOTHING) const
#define LSST_EXCEPT(type,...)
afw::geom::Angle getLinearScatterOnSky() const
Compute the median on-sky separation between items in this object's match list,.
Measure the distortions in an image plane and express them a SIP polynomials.
afw::math::Statistics makeMatchStatisticsInRadians(afw::image::Wcs const &wcs, std::vector< MatchT > const &matchList, int const flags, afw::math::StatisticsControl const &sctrl=afw::math::StatisticsControl())
Compute statistics of on-sky radial separation for a match list, in radians.
#define LOGL_DEBUG(logger, message...)
#define INSTANTIATE(MATCH)
double getLinearScatterInPixels() const
Compute the median radial separation between items in this object's match list.
table::PointKey< double > crpix