27 #include "Eigen/Cholesky" 39 LOG_LOGGER _log =
LOG_GET(
"meas.astrom.sip");
56 double const MAX_DISTANCE_CRPIX_TO_BBOXCTR = 1000;
63 indexToPQ(
int const index,
int const order)
67 for (
int decrement = order; q >= decrement && decrement > 0; --decrement) {
76 calculateCMatrix(Eigen::VectorXd
const& axis1, Eigen::VectorXd
const& axis2,
int const order)
78 int nTerms = 0.5*order*(order+1);
80 int const n = axis1.size();
81 Eigen::MatrixXd C = Eigen::MatrixXd::Zero(n, nTerms);
82 for (
int i = 0; i < n; ++i) {
83 for (
int j = 0; j < nTerms; ++j) {
85 int p = pq.first, q = pq.second;
87 assert(p + q < order);
88 C(i, j) = ::pow(axis1[i], p)*::pow(axis2[i], q);
102 leastSquaresSolve(Eigen::VectorXd b, Eigen::MatrixXd A) {
103 assert(A.rows() == b.rows());
104 Eigen::VectorXd par = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
111 template<
class MatchT>
122 _linearWcs(
std::make_shared<afw::geom::SkyWcs>(linearWcs)),
124 _reverseSipOrder(order+2),
125 _sipA(Eigen::MatrixXd::Zero(_sipOrder, _sipOrder)),
126 _sipB(Eigen::MatrixXd::Zero(_sipOrder, _sipOrder)),
127 _sipAp(Eigen::MatrixXd::Zero(_reverseSipOrder, _reverseSipOrder)),
128 _sipBp(Eigen::MatrixXd::Zero(_reverseSipOrder, _reverseSipOrder)),
136 str(boost::format(
"SIP forward order %d exceeds the convention limit of 9") %
139 if (_reverseSipOrder > 9) {
141 str(boost::format(
"SIP reverse order %d exceeds the convention limit of 9") %
150 _ngrid = 5*_sipOrder;
163 ptr != _matches.
end();
169 float const borderFrac = 1/::sqrt(_matches.
size());
176 auto const initialCrpix = _linearWcs->getPixelOrigin();
178 if (
std::hypot(initialCrpix[0] - bboxCenter[0], initialCrpix[1] - bboxCenter[1]) >
179 MAX_DISTANCE_CRPIX_TO_BBOXCTR) {
181 "_linearWcs crpix = %d, %d farther than %f from bbox center; shifting to center %d, %d",
182 initialCrpix[0], initialCrpix[1], MAX_DISTANCE_CRPIX_TO_BBOXCTR, bboxCenter[0],
184 _linearWcs = _linearWcs->getTanWcs(bboxCenter);
188 _calculateForwardMatrices();
191 auto const crval = _linearWcs->getSkyOrigin();
192 auto const crpix = _linearWcs->getPixelOrigin();
193 Eigen::MatrixXd cdMatrix = _linearWcs->getCdMatrix();
197 _calculateReverseMatrices();
203 template<
class MatchT>
211 int const nPoints = _matches.
size();
212 Eigen::VectorXd u(nPoints), v(nPoints), iwc1(nPoints), iwc2(nPoints);
218 ptr != _matches.
end();
224 auto c = match.
first->getCoord();
229 u[i] = match.
second->getX() - crpix[0];
230 v[i] = match.
second->getY() - crpix[1];
233 double uMax = u.cwiseAbs().maxCoeff();
234 double vMax = v.cwiseAbs().maxCoeff();
241 Eigen::MatrixXd forwardC = calculateCMatrix(u, v, ord);
242 Eigen::VectorXd mu = leastSquaresSolve(iwc1, forwardC);
243 Eigen::VectorXd nu = leastSquaresSolve(iwc2, forwardC);
257 CD(1,0) = nu[ord]/norm;
258 CD(1,1) = nu[1]/norm;
259 CD(0,0) = mu[ord]/norm;
260 CD(0,1) = mu[1]/norm;
262 Eigen::Matrix2d CDinv = CD.inverse();
265 crpix[0] -= mu[0]*CDinv(0,0) + nu[0]*CDinv(0,1);
266 crpix[1] -= mu[0]*CDinv(1,0) + nu[0]*CDinv(1,1);
268 auto const crval = _linearWcs->getSkyOrigin();
282 for(
int i=1; i< mu.rows(); ++i) {
284 int p = pq.first, q = pq.second;
286 if(p + q > 1 && p + q < ord) {
287 Eigen::Vector2d munu(2,1);
290 Eigen::Vector2d AB = CDinv*munu;
292 _sipA(p,q) = AB[0]/::pow(norm,p+q);
293 _sipB(p,q) = AB[1]/::pow(norm,p+q);
298 template<
class MatchT>
300 int const ngrid2 = _ngrid*_ngrid;
302 Eigen::VectorXd U(ngrid2), V(ngrid2);
303 Eigen::VectorXd delta1(ngrid2), delta2(ngrid2);
305 int const x0 = _bbox.
getMinX();
306 double const dx = _bbox.
getWidth()/(double)(_ngrid - 1);
307 int const y0 = _bbox.
getMinY();
308 double const dy = _bbox.
getHeight()/(double)(_ngrid - 1);
313 LOGL_DEBUG(_log,
"_calcReverseMatrices: x0,y0 %i,%i, W,H %i,%i, ngrid %i, dx,dy %g,%g, CRPIX %g,%g",
314 x0, y0, _bbox.
getWidth(), _bbox.
getHeight(), _ngrid, dx, dy, crpix[0], crpix[1]);
316 auto tanWcs = _newWcs->getTanWcs(_newWcs->getPixelOrigin());
317 auto applySipAB = afwGeom::makeWcsPairTransform(*_newWcs, *tanWcs);
319 for (
int i = 0; i < _ngrid; ++i) {
320 double const y = y0 + i*dy;
322 beforeSipABPoints.
reserve(_ngrid);
323 for (
int j = 0; j < _ngrid; ++j) {
324 double const x = x0 + j*dx;
327 auto const afterSipABPoints = applySipAB->applyForward(beforeSipABPoints);
328 for (
int j = 0; j < _ngrid; ++j, ++k) {
329 double const x = x0 + j*dx;
337 U[k] = xy[0] - crpix[0];
338 V[k] = xy[1] - crpix[1];
340 if ((i == 0 || i == (_ngrid-1) || i == (_ngrid/2)) &&
341 (j == 0 || j == (_ngrid-1) || j == (_ngrid/2))) {
342 LOGL_DEBUG(_log,
" x,y (%.1f, %.1f), u,v (%.1f, %.1f), U,V (%.1f, %.1f)", x, y, u, v, U[k], V[k]);
345 delta1[k] = u - U[k];
346 delta2[k] = v - V[k];
351 double UMax = U.cwiseAbs().maxCoeff();
352 double VMax = V.cwiseAbs().maxCoeff();
353 double norm = (UMax > VMax) ? UMax : VMax;
358 int const ord = _reverseSipOrder;
359 Eigen::MatrixXd reverseC = calculateCMatrix(U, V, ord);
360 Eigen::VectorXd tmpA = leastSquaresSolve(delta1, reverseC);
361 Eigen::VectorXd tmpB = leastSquaresSolve(delta2, reverseC);
363 assert(tmpA.rows() == tmpB.rows());
364 for(
int j=0; j< tmpA.rows(); ++j) {
366 int p = pq.first, q = pq.second;
368 _sipAp(p, q) = tmpA[j]/::pow(norm,p+q);
369 _sipBp(p, q) = tmpB[j]/::pow(norm,p+q);
373 template<
class MatchT>
375 assert(_newWcs.get());
379 template<
class MatchT>
381 assert(_linearWcs.get());
385 template<
class MatchT>
387 assert(_newWcs.get());
392 template<
class MatchT>
394 assert(_linearWcs.get());
400 #define INSTANTIATE(MATCH) \ 401 template class CreateWcsWithSip<MATCH>;
double getScatterInPixels() const
Compute the median separation, in pixels, between items in this object's match list.
AngleUnit constexpr radians
CreateWcsWithSip(std::vector< MatchT > const &matches, afw::geom::SkyWcs const &linearWcs, int const order, afw::geom::Box2I const &bbox=afw::geom::Box2I(), int const ngrid=0)
Construct a CreateWcsWithSip.
void include(Point2I const &point)
table::PointKey< double > crpix
table::PointKey< double > crval
std::shared_ptr< Record2 > second
std::shared_ptr< RecordT > src
afw::math::Statistics makeMatchStatisticsInPixels(afw::geom::SkyWcs 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.
Point2D const getCenter() const
std::shared_ptr< Record1 > first
std::shared_ptr< TransformPoint2ToSpherePoint > getIntermediateWorldCoordsToSky(SkyWcs const &wcs, bool simplify=true)
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::math::Statistics makeMatchStatisticsInRadians(afw::geom::SkyWcs 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.
afw::geom::Angle getLinearScatterOnSky() const
Compute the median on-sky separation between items in this object's match list,.
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Measure the distortions in an image plane and express them a SIP polynomials.
#define LOGL_DEBUG(logger, message...)
#define INSTANTIATE(MATCH)
std::shared_ptr< SkyWcs > makeTanSipWcs(Point2D const &crpix, SpherePoint const &crval, Eigen::Matrix2d const &cdMatrix, Eigen::MatrixXd const &sipA, Eigen::MatrixXd const &sipB)
double getLinearScatterInPixels() const
Compute the median radial separation between items in this object's match list.
T emplace_back(T... args)