27 #include "Eigen/Cholesky"
41 namespace except = lsst::pex::exceptions;
42 namespace afwCoord = lsst::afw::coord;
43 namespace afwGeom = lsst::afw::geom;
45 namespace afwDet = lsst::afw::detection;
46 namespace afwMath = lsst::afw::math;
47 namespace afwTable = lsst::afw::table;
48 namespace pexLog = lsst::pex::logging;
56 indexToPQ(
int const index,
int const order)
60 for (
int decrement = order; q >= decrement && decrement > 0; --decrement) {
65 return std::make_pair(p, q);
69 calculateCMatrix(Eigen::VectorXd
const& axis1, Eigen::VectorXd
const& axis2,
int const order)
71 int nTerms = 0.5*order*(order+1);
73 int const n = axis1.size();
74 Eigen::MatrixXd C = Eigen::MatrixXd::Zero(n, nTerms);
75 for (
int i = 0; i < n; ++i) {
76 for (
int j = 0; j < nTerms; ++j) {
77 std::pair<int, int> pq = indexToPQ(j, order);
78 int p = pq.first, q = pq.second;
80 assert(p + q < order);
81 C(i, j) = ::pow(axis1[i], p)*::pow(axis2[i], q);
95 leastSquaresSolve(Eigen::VectorXd
b, Eigen::MatrixXd A) {
96 assert(A.rows() == b.rows());
97 Eigen::VectorXd par = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
104 template<
class MatchT>
106 std::vector<MatchT>
const & matches,
112 _log(pexLog::Log(pexLog::Log::getDefaultLog(),
"meas.astrom.sip")),
116 _linearWcs(linearWcs.clone()),
118 _reverseSipOrder(order+2),
119 _sipA(Eigen::MatrixXd::Zero(_sipOrder, _sipOrder)),
120 _sipB(Eigen::MatrixXd::Zero(_sipOrder, _sipOrder)),
121 _sipAp(Eigen::MatrixXd::Zero(_reverseSipOrder, _reverseSipOrder)),
122 _sipBp(Eigen::MatrixXd::Zero(_reverseSipOrder, _reverseSipOrder)),
126 throw LSST_EXCEPT(except::OutOfRangeError,
"SIP must be at least 2nd order");
130 str(
boost::format(
"SIP forward order %d exceeds the convention limit of 9") %
135 str(
boost::format(
"SIP reverse order %d exceeds the convention limit of 9") %
140 throw LSST_EXCEPT(except::LengthError,
"Number of matches less than requested sip order");
156 typename std::vector<MatchT>::const_iterator ptr =
_matches.begin();
163 float const borderFrac = 1/::sqrt(
_matches.size());
174 Eigen::MatrixXd CD =
_linearWcs->getCDMatrix();
185 template<
class MatchT>
193 int const nPoints = _matches.size();
194 Eigen::VectorXd u(nPoints), v(nPoints), iwc1(nPoints), iwc2(nPoints);
198 typename std::vector<MatchT>::const_iterator ptr = _matches.begin();
199 ptr != _matches.end();
210 u[i] = match.
second->getX() - crpix[0];
211 v[i] = match.
second->getY() - crpix[1];
214 double uMax = u.cwiseAbs().maxCoeff();
215 double vMax = v.cwiseAbs().maxCoeff();
216 double norm = std::max(uMax, vMax);
222 Eigen::MatrixXd forwardC = calculateCMatrix(u, v, ord);
223 Eigen::VectorXd mu = leastSquaresSolve(iwc1, forwardC);
224 Eigen::VectorXd nu = leastSquaresSolve(iwc2, forwardC);
232 assert ((indexToPQ(0, ord) == std::pair<int, int>(0, 0)));
233 assert ((indexToPQ(1, ord) == std::pair<int, int>(0, 1)));
234 assert ((indexToPQ(ord, ord) == std::pair<int, int>(1, 0)));
238 CD(1,0) = nu[ord]/
norm;
239 CD(1,1) = nu[1]/
norm;
240 CD(0,0) = mu[ord]/
norm;
241 CD(0,1) = mu[1]/
norm;
243 Eigen::Matrix2d CDinv = CD.inverse();
246 crpix[0] -= mu[0]*CDinv(0,0) + nu[0]*CDinv(0,1);
247 crpix[1] -= mu[0]*CDinv(1,0) + nu[0]*CDinv(1,1);
263 for(
int i=1; i< mu.rows(); ++i) {
264 std::pair<int, int> pq = indexToPQ(i, ord);
265 int p = pq.first, q = pq.second;
267 if(p + q > 1 && p + q < ord) {
268 Eigen::Vector2d munu(2,1);
271 Eigen::Vector2d AB = CDinv*munu;
273 _sipA(p,q) = AB[0]/::pow(norm,p+q);
274 _sipB(p,q) = AB[1]/::pow(norm,p+q);
279 template<
class MatchT>
281 int const ngrid2 = _ngrid*_ngrid;
283 Eigen::VectorXd U(ngrid2), V(ngrid2);
284 Eigen::VectorXd delta1(ngrid2), delta2(ngrid2);
294 _log.debugf(
"_calcReverseMatrices: x0,y0 %i,%i, W,H %i,%i, ngrid %i, dx,dy %g,%g, CRPIX %g,%g",
298 for (
int i = 0; i < _ngrid; ++i) {
299 double const y = y0 + i*dy;
300 for (
int j = 0; j < _ngrid; ++j, ++k) {
301 double const x = x0 + j*dx;
313 U[k] = xy[0] - 1 - crpix[0];
314 V[k] = xy[1] - 1 - crpix[1];
316 if ((i == 0 || i == (_ngrid-1) || i == (_ngrid/2)) &&
317 (j == 0 || j == (_ngrid-1) || j == (_ngrid/2))) {
318 _log.debugf(
" x,y (%.1f, %.1f), u,v (%.1f, %.1f), U,V (%.1f, %.1f)", x, y, u, v, U[k], V[k]);
321 delta1[k] = u - U[k];
322 delta2[k] = v - V[k];
327 double UMax = U.cwiseAbs().maxCoeff();
328 double VMax = V.cwiseAbs().maxCoeff();
329 double norm = (UMax > VMax) ? UMax : VMax;
334 int const ord = _reverseSipOrder;
335 Eigen::MatrixXd reverseC = calculateCMatrix(U, V, ord);
336 Eigen::VectorXd tmpA = leastSquaresSolve(delta1, reverseC);
337 Eigen::VectorXd tmpB = leastSquaresSolve(delta2, reverseC);
339 assert(tmpA.rows() == tmpB.rows());
340 for(
int j=0; j< tmpA.rows(); ++j) {
341 std::pair<int, int> pq = indexToPQ(j, ord);
342 int p = pq.first, q = pq.second;
344 _sipAp(p, q) = tmpA[j]/::pow(norm,p+q);
345 _sipBp(p, q) = tmpB[j]/::pow(norm,p+q);
349 template<
class MatchT>
351 assert(_newWcs.get());
352 return _getScatterPixels(*_newWcs, _matches);
355 template<
class MatchT>
357 assert(_linearWcs.get());
358 return _getScatterPixels(*_linearWcs, _matches);
361 template<
class MatchT>
364 std::vector<MatchT>
const & matches) {
365 std::vector<double>
val;
366 val.reserve(matches.size());
369 typename std::vector<MatchT>::const_iterator ptr = matches.begin();
370 ptr != matches.end();
376 double imgX = src->getX();
377 double imgY = src->getY();
381 val.push_back(::hypot(imgX - catX, imgY - catY));
387 template<
class MatchT>
389 assert(_newWcs.get());
390 return _getScatterSky(*_newWcs, _matches);
393 template<
class MatchT>
395 assert(_linearWcs.get());
396 return _getScatterSky(*_linearWcs, _matches);
399 template<
class MatchT>
402 std::vector<MatchT>
const & matches) {
403 std::vector<double>
val;
404 val.reserve(matches.size());
407 typename std::vector<MatchT>::const_iterator ptr = matches.begin();
408 ptr != matches.end();
419 assert(val.size() > 0);
424 template<
class MatchT>
431 #define INSTANTIATE(MATCH) \
432 template class CreateWcsWithSip<MATCH>;
int const _reverseSipOrder
double getValue(Property const prop=NOTHING) const
Return the value of the desired property (if specified in the constructor)
afw::geom::Point2D _getCrvalAsGeomPoint()
double getX() const
Return the centroid slot x coordinate.
void grow(int buffer)
Increase the size of the box by the given buffer amount in all directions.
afw::geom::Angle getLinearScatterOnSky()
boost::shared_ptr< Wcs > Ptr
geom::Point2D skyToPixel(geom::Angle sky1, geom::Angle sky2) const
Convert from sky coordinates (e.g. RA/dec) to pixel positions.
double getY() const
Return the centroid slot y coordinate.
table::PointKey< double > crval
boost::shared_ptr< afw::image::Wcs const > _linearWcs
Implementation of the WCS standard for a any projection.
double getLinearScatterInPixels()
boost::shared_ptr< coord::Coord > pixelToSky(double pix1, double pix2) const
Convert from pixel position to sky coordinates (e.g. RA/dec)
double getScatterInPixels()
An integer coordinate rectangle.
table::Key< table::Array< Kernel::Pixel > > image
double _getScatterPixels(afw::image::Wcs const &wcs, std::vector< MatchT > const &matches)
virtual Fk5Coord toFk5(double const epoch) const
Convert ourself to Fk5 (ie. a no-op): RA, Dec (precess to new epoch)
boost::shared_ptr< Record1 > first
Implementation of the WCS standard for the special case of the Gnomonic (tangent plane) projection...
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)
Constructor.
Lightweight representation of a geometric match between two records.
geom::Box2I const & _bbox
void _calculateForwardMatrices()
void _calculateReverseMatrices()
void include(Point2I const &point)
Expand this to ensure that this->contains(point).
A class to handle Fk5 coordinates (inherits from Coord)
#define LSST_EXCEPT(type,...)
bool isEmpty() const
Return true if the box contains no points.
boost::shared_ptr< Record2 > second
boost::shared_ptr< afw::image::TanWcs > _newWcs
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
Compute Image Statistics.
afw::table::Key< double > b
afw::geom::Angle _getScatterSky(afw::image::Wcs const &wcs, std::vector< MatchT > const &matches)
Record class that contains measurements made on a single exposure.
#define INSTANTIATE(MATCH)
Record class that must contain a unique ID field and a celestial coordinate field.
A class to handle Icrs coordinates (inherits from Coord)
lsst::afw::geom::Point2D getPosition(lsst::afw::geom::AngleUnit unit=lsst::afw::geom::degrees) const
Return our contents in a Point2D object.
afw::geom::Angle getScatterOnSky()
std::vector< MatchT > const _matches
lsst::afw::geom::Angle angularSeparation(Coord const &c) const
compute the angular separation between two Coords
table::PointKey< double > crpix