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)
72 for (
int i = 1; i <= order; ++i) {
76 int const n = axis1.size();
77 Eigen::MatrixXd C = Eigen::MatrixXd::Zero(n, nTerms);
78 for (
int i = 0; i < n; ++i) {
79 for (
int j = 0; j < nTerms; ++j) {
80 std::pair<int, int> pq = indexToPQ(j, order);
81 int p = pq.first, q = pq.second;
83 assert(p + q < order);
84 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>
111 std::vector<MatchT>
const & matches,
117 _log(pexLog::Log(pexLog::Log::getDefaultLog(),
"meas.astrom.sip")),
121 _linearWcs(linearWcs.clone()),
123 _reverseSipOrder(order+2),
124 _sipA(Eigen::MatrixXd::Zero(_sipOrder, _sipOrder)),
125 _sipB(Eigen::MatrixXd::Zero(_sipOrder, _sipOrder)),
126 _sipAp(Eigen::MatrixXd::Zero(_reverseSipOrder, _reverseSipOrder)),
127 _sipBp(Eigen::MatrixXd::Zero(_reverseSipOrder, _reverseSipOrder)),
131 throw LSST_EXCEPT(except::OutOfRangeError,
"SIP must be at least 2nd order");
135 str(
boost::format(
"SIP forward order %d exceeds the convention limit of 9") %
140 str(
boost::format(
"SIP reverse order %d exceeds the convention limit of 9") %
145 throw LSST_EXCEPT(except::LengthError,
"Number of matches less than requested sip order");
161 typename std::vector<MatchT>::const_iterator ptr =
_matches.begin();
168 float const borderFrac = 1/::sqrt(
_matches.size());
179 Eigen::MatrixXd CD =
_linearWcs->getCDMatrix();
190 template<
class MatchT>
198 int const nPoints =
_matches.size();
199 Eigen::VectorXd u(nPoints), v(nPoints), iwc1(nPoints), iwc2(nPoints);
203 typename std::vector<MatchT>::const_iterator ptr =
_matches.begin();
215 u[i] = match.
second->getX() - crpix[0];
216 v[i] = match.
second->getY() - crpix[1];
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)));
242 Eigen::Matrix2d CDinv = CD.inverse();
245 crpix[0] -= mu[0]*CDinv(0,0) + nu[0]*CDinv(0,1);
246 crpix[1] -= mu[0]*CDinv(1,0) + nu[0]*CDinv(1,1);
262 for(
int i=1; i< mu.rows(); ++i) {
263 std::pair<int, int> pq = indexToPQ(i, ord);
264 int p = pq.first, q = pq.second;
266 if(p + q > 1 && p + q < ord) {
267 Eigen::Vector2d munu(2,1);
270 Eigen::Vector2d AB = CDinv*munu;
277 template<
class MatchT>
279 int const ngrid2 = _ngrid*_ngrid;
281 Eigen::VectorXd U(ngrid2), V(ngrid2);
282 Eigen::VectorXd delta1(ngrid2), delta2(ngrid2);
292 _log.debugf(
"_calcReverseMatrices: x0,y0 %i,%i, W,H %i,%i, ngrid %i, dx,dy %g,%g, CRPIX %g,%g",
296 for (
int i = 0; i < _ngrid; ++i) {
297 double const y = y0 + i*
dy;
298 for (
int j = 0; j < _ngrid; ++j, ++
k) {
299 double const x = x0 + j*
dx;
311 U[
k] = xy[0] - 1 - crpix[0];
312 V[
k] = xy[1] - 1 - crpix[1];
314 if ((i == 0 || i == (_ngrid-1) || i == (_ngrid/2)) &&
315 (j == 0 || j == (_ngrid-1) || j == (_ngrid/2))) {
316 _log.debugf(
" x,y (%.1f, %.1f), u,v (%.1f, %.1f), U,V (%.1f, %.1f)", x, y, u, v, U[k], V[k]);
319 delta1[
k] = u - U[
k];
320 delta2[
k] = v - V[
k];
325 int const ord = _reverseSipOrder;
326 Eigen::MatrixXd reverseC = calculateCMatrix(U, V, ord);
327 Eigen::VectorXd tmpA = leastSquaresSolve(delta1, reverseC);
328 Eigen::VectorXd tmpB = leastSquaresSolve(delta2, reverseC);
330 assert(tmpA.rows() == tmpB.rows());
331 for(
int j=0; j< tmpA.rows(); ++j) {
332 std::pair<int, int> pq = indexToPQ(j, ord);
333 int p = pq.first, q = pq.second;
334 _sipAp(p, q) = tmpA[j];
335 _sipBp(p, q) = tmpB[j];
339 template<
class MatchT>
341 assert(_newWcs.get());
342 return _getScatterPixels(*_newWcs,
_matches);
345 template<
class MatchT>
347 assert(_linearWcs.get());
348 return _getScatterPixels(*_linearWcs,
_matches);
351 template<
class MatchT>
354 std::vector<MatchT>
const & matches) {
355 std::vector<double>
val;
356 val.reserve(matches.size());
359 typename std::vector<MatchT>::const_iterator ptr = matches.begin();
360 ptr != matches.end();
366 double imgX = src->getX();
367 double imgY = src->getY();
371 val.push_back(::hypot(imgX - catX, imgY - catY));
377 template<
class MatchT>
379 assert(_newWcs.get());
380 return _getScatterSky(*_newWcs,
_matches);
383 template<
class MatchT>
385 assert(_linearWcs.get());
386 return _getScatterSky(*_linearWcs,
_matches);
389 template<
class MatchT>
392 std::vector<MatchT>
const & matches) {
393 std::vector<double>
val;
394 val.reserve(matches.size());
397 typename std::vector<MatchT>::const_iterator ptr = matches.begin();
398 ptr != matches.end();
409 assert(val.size() > 0);
414 template<
class MatchT>
421 #define INSTANTIATE(MATCH) \
422 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.
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()
table::Key< table::Point< double > > crval
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()
table::Key< table::Point< double > > crpix
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