8 #include "boost/scoped_array.hpp"
9 #include "boost/shared_array.hpp"
10 #include "boost/multi_index_container.hpp"
11 #include "boost/multi_index/sequenced_index.hpp"
12 #include "boost/multi_index/ordered_index.hpp"
13 #include "boost/multi_index/global_fun.hpp"
15 #include "gsl/gsl_linalg.h"
24 namespace pexExcept = lsst::pex::exceptions;
25 namespace afwTable = lsst::afw::table;
28 using namespace lsst::meas::astrom;
36 struct insertionOrderTag{};
37 typedef boost::multi_index_container<
39 boost::multi_index::indexed_by<
40 boost::multi_index::sequenced<
41 boost::multi_index::tag<insertionOrderTag>
43 boost::multi_index::ordered_unique<
44 boost::multi_index::tag<refIdTag>,
45 boost::multi_index::global_fun<
49 > MultiIndexedProxyPairList;
60 inline double absDeltaAngle(
double ang1,
double ang2) {
61 return std::fmod(std::fabs(ang1 - ang2), M_PI*2);
73 struct CompareProxyFlux {
76 double aFlux = a.
record->get(key);
77 double bFlux = b.
record->get(key);
104 std::size_t startInd=0
106 if (startInd >= a.size()) {
107 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"startInd too big");
109 CompareProxyFlux cmp = {key};
111 std::sort(b.begin(), b.end(), cmp);
112 std::size_t
const endInd = std::min(startInd + num, b.size());
113 return ProxyVector(b.begin() + startInd, b.begin() + endInd);
116 std::vector<ProxyPair> searchPair(
117 std::vector<ProxyPair>
const &a,
122 std::vector<ProxyPair> v;
124 for (
size_t i = 0; i < a.size(); i++) {
125 double dd = std::fabs(a[i].distance - p.
distance);
126 double dpa = absDeltaAngle(a[i].pa, p.
pa);
127 if (dd < e && dpa < e_dpa) {
135 std::vector<ProxyPair>::iterator searchPair3(
136 std::vector<ProxyPair> &a,
143 std::vector<ProxyPair>::iterator idx = a.end();
144 double dd_min = 1.E+10;
147 for (
auto i = a.begin(); i != a.end(); ++i) {
148 double dd = std::fabs(i->distance - p.
distance);
151 absDeltaAngle(p.
pa, i->pa - dpa) < e_dpa &&
153 (i->first == q.
first)) {
159 absDeltaAngle(p.
pa, i->pa - dpa) < dpa_min) {
160 dpa_min = std::fabs(p.
pa - i->pa - dpa);
171 boost::shared_array<double>
const & coeff,
177 int ncoeff = (order + 1) * (order + 2) / 2;
181 for (
int i = 0; i <= order; i++) {
182 for (
int k = 0; k <= i; k++) {
184 *xn += coeff[n] * pow(x, j) * pow(y, k);
185 *yn += coeff[n+ncoeff] * pow(x, j) * pow(y, k);
191 boost::shared_array<double> polyfit(
196 int ncoeff = (order + 1) * (order + 2) / 2;
197 boost::scoped_array<int> xorder(
new int[ncoeff]);
198 boost::scoped_array<int> yorder(
new int[ncoeff]);
201 for (
int i = 0; i <= order; i++) {
202 for (
int k = 0; k <= i; k++) {
210 boost::scoped_array<int> flag(
new int[img.size()]);
211 for (
size_t k = 0; k < img.size(); k++) {
215 boost::scoped_array<double> a_data(
new double[ncoeff*ncoeff]);
216 boost::scoped_array<double> b_data(
new double[ncoeff]);
217 boost::scoped_array<double> c_data(
new double[ncoeff]);
219 boost::shared_array<double> coeff(
new double[ncoeff*2]);
221 for (
int loop = 0; loop < 1; loop++) {
222 for (
int i = 0; i < ncoeff; i++) {
223 for (
int j = 0; j < ncoeff; j++) {
224 a_data[i*ncoeff+j] = 0.0;
225 for (
size_t k = 0; k < img.size(); k++) {
227 a_data[i*ncoeff+j] += pow(img[k].getX(), xorder[i]) *
228 pow(img[k].getY(), yorder[i]) *
229 pow(img[k].getX(), xorder[j]) *
230 pow(img[k].getY(), yorder[j]);
234 b_data[i] = c_data[i] = 0.0;
235 for (
unsigned int k = 0; k < img.size(); k++) {
237 b_data[i] += pow(img[k].getX(), xorder[i]) *
238 pow(img[k].getY(), yorder[i]) *
240 c_data[i] += pow(img[k].getX(), xorder[i]) *
241 pow(img[k].getY(), yorder[i]) *
247 gsl_matrix_view a = gsl_matrix_view_array(a_data.get(), ncoeff, ncoeff);
248 gsl_vector_view b = gsl_vector_view_array(b_data.get(), ncoeff);
249 gsl_vector_view c = gsl_vector_view_array(c_data.get(), ncoeff);
251 boost::shared_ptr<gsl_vector>
x(gsl_vector_alloc(ncoeff), gsl_vector_free);
252 boost::shared_ptr<gsl_vector>
y(gsl_vector_alloc(ncoeff), gsl_vector_free);
256 boost::shared_ptr<gsl_permutation> p(gsl_permutation_alloc(ncoeff), gsl_permutation_free);
258 gsl_linalg_LU_decomp(&a.matrix, p.get(), &s);
259 gsl_linalg_LU_solve(&a.matrix, p.get(), &b.vector, x.get());
260 gsl_linalg_LU_solve(&a.matrix, p.get(), &c.vector, y.get());
262 for (
int i = 0; i < ncoeff; i++) {
263 coeff[i] = x->data[i];
264 coeff[i+ncoeff] = y->data[i];
267 double S, Sx, Sy, Sxx, Syy;
268 S = Sx = Sy = Sxx = Syy = 0.0;
269 for (
size_t k = 0; k < img.size(); k++) {
271 double x0 = img[k].getX();
272 double y0 = img[k].getY();
274 transform(order, coeff, x0, y0, &x1, &y1);
276 Sx += (x1 - posRefCat[k].getX());
277 Sxx += (x1 - posRefCat[k].getX()) * (x1 - posRefCat[k].getX());
278 Sy += (y1 - posRefCat[k].getY());
279 Syy += (y1 - posRefCat[k].getY()) * (y1 - posRefCat[k].getY());
282 double x_sig = std::sqrt((Sxx - Sx * Sx / S) / S);
283 double y_sig = std::sqrt((Syy - Sy * Sy / S) / S);
286 for (
size_t k = 0; k < img.size(); k++) {
287 double x0 = img[k].getX();
288 double y0 = img[k].getY();
290 transform(order, coeff, x0, y0, &x1, &y1);
291 if (std::fabs(x1-posRefCat[k].getX()) > 2. * x_sig ||
292 std::fabs(y1-posRefCat[k].getY()) > 2. * y_sig) {
314 std::pair<ProxyVector::const_iterator, double> searchNearestPoint(
318 double matchingAllowancePix
320 auto minDistSq = matchingAllowancePix*matchingAllowancePix;
321 auto foundPtr = posRefCat.end();
322 for (
auto posRefPtr = posRefCat.begin(); posRefPtr != posRefCat.end(); ++posRefPtr) {
323 auto const dx = posRefPtr->getX() -
x;
324 auto const dy = posRefPtr->getY() -
y;
325 auto const distSq = dx*dx + dy*dy;
326 if (distSq < minDistSq) {
327 foundPtr = posRefPtr;
331 return std::make_pair(foundPtr, std::sqrt(minDistSq));
352 void addNearestMatch(
353 MultiIndexedProxyPairList &proxyPairList,
354 boost::shared_array<double> coeff,
357 double matchingAllowancePix
360 auto x0 = source.
getX();
361 auto y0 = source.
getY();
362 transform(1, coeff, x0, y0, &x1, &y1);
363 auto refObjDist = searchNearestPoint(posRefCat, x1, y1, matchingAllowancePix);
364 if (refObjDist.first == posRefCat.end()) {
368 auto existingMatch = proxyPairList.get<refIdTag>().find(refObjDist.first->record->getId());
369 if (existingMatch == proxyPairList.get<refIdTag>().end()) {
371 auto proxyPair =
ProxyPair(*refObjDist.first, source);
372 proxyPairList.get<refIdTag>().insert(proxyPair);
380 if (existingMatch->distance <= refObjDist.second) {
385 proxyPairList.get<refIdTag>().
erase(existingMatch);
386 auto proxyPair =
ProxyPair(*refObjDist.first, source);
387 proxyPairList.get<refIdTag>().insert(proxyPair);
403 boost::shared_array<double> coeff,
406 double matchingAllowancePix,
409 MultiIndexedProxyPairList proxyPairList;
411 for (
auto sourcePtr = sourceCat.begin(); sourcePtr != sourceCat.end(); ++sourcePtr) {
412 addNearestMatch(proxyPairList, coeff, posRefCat, *sourcePtr, matchingAllowancePix);
415 if (proxyPairList.size() > 5) {
416 for (
auto j = 0; j < 100; j++) {
417 auto prevNumMatches = proxyPairList.size();
420 srcMat.reserve(proxyPairList.size());
421 catMat.reserve(proxyPairList.size());
422 for (
auto matchPtr = proxyPairList.get<refIdTag>().begin();
423 matchPtr != proxyPairList.get<refIdTag>().end(); ++matchPtr) {
424 catMat.push_back(matchPtr->first);
425 srcMat.push_back(matchPtr->second);
427 coeff = polyfit(order, srcMat, catMat);
428 proxyPairList.clear();
430 for (ProxyVector::const_iterator sourcePtr = sourceCat.begin();
431 sourcePtr != sourceCat.end(); ++sourcePtr) {
432 addNearestMatch(proxyPairList, coeff, posRefCat, *sourcePtr, matchingAllowancePix);
434 if (proxyPairList.size() == prevNumMatches) {
440 matPair.reserve(proxyPairList.size());
441 for (
auto proxyPairIter = proxyPairList.get<insertionOrderTag>().begin();
442 proxyPairIter != proxyPairList.get<insertionOrderTag>().end(); ++proxyPairIter) {
444 proxyPairIter->first.record,
445 boost::static_pointer_cast<afwTable::SourceRecord>(proxyPairIter->second.record),
446 proxyPairIter->distance
459 void MatchOptimisticBControl::validate()
const {
460 if (refFluxField.empty()) {
461 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"refFluxField must be specified");
463 if (sourceFluxField.empty()) {
464 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"sourceFluxField must be specified");
466 if (numBrightStars <= 0) {
467 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numBrightStars must be positive");
469 if (minMatchedPairs < 0) {
470 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"minMatchedPairs must not be negative");
472 if (matchingAllowancePix <= 0) {
473 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"matchingAllowancePix must be positive");
475 if (maxOffsetPix <= 0) {
476 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxOffsetPix must be positive");
478 if (maxRotationDeg <= 0) {
479 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxRotationRad must be positive");
481 if (allowedNonperpDeg <= 0) {
482 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"allowedNonperpDeg must be positive");
484 if (numPointsForShape <= 0) {
485 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numPointsForShape must be positive");
487 if (maxDeterminant <= 0) {
488 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxDeterminant must be positive");
498 r.reserve(sourceCat.
size());
500 sourcePtr != sourceCat.
end(); ++sourcePtr) {
513 r.reserve(posRefCat.
size());
515 posRefPtr != posRefCat.
end(); ++posRefPtr) {
530 if (posRefBegInd < 0) {
531 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"posRefBegInd < 0");
533 if (posRefBegInd >= posRefCat.
size()) {
534 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"posRefBegInd too big");
546 srcCenter /= sourceCat.
size();
552 refCenter /= posRefCat.
size();
554 tanWcs = boost::make_shared<afw::image::Wcs>(
575 sourceSubCat.size()+25,
578 std::cout <<
"Catalog sizes: " << sourceSubCat.size() <<
" " << posRefSubCat.size() << std::endl;
582 std::vector<ProxyPair> posRefPairList;
583 size_t const posRefCatSubSize = posRefSubCat.size();
584 for (
size_t i = 0; i < posRefCatSubSize-1; i++) {
585 for (
size_t j = i+1; j < posRefCatSubSize; j++) {
586 posRefPairList.push_back(
ProxyPair(posRefSubCat[i], posRefSubCat[j]));
589 std::sort(posRefPairList.begin(), posRefPairList.end(), cmpPair);
592 std::vector<ProxyPair> sourcePairList;
593 size_t const sourceSubCatSize = sourceSubCat.size();
594 for (
size_t i = 0; i < sourceSubCatSize-1; i++) {
595 for (
size_t j = i+1; j < sourceSubCatSize; j++) {
596 sourcePairList.push_back(
ProxyPair(sourceSubCat[i], sourceSubCat[j]));
599 std::sort(sourcePairList.begin(), sourcePairList.end(), cmpPair);
603 std::vector<afwTable::ReferenceMatchVector> matPairCand;
606 for (
size_t ii = 0; ii < sourcePairList.size(); ii++) {
609 std::vector<ProxyPair> q = searchPair(posRefPairList, p,
615 std::vector<ProxyPair> srcMatPair;
616 std::vector<ProxyPair> catMatPair;
619 for (
size_t l = 0; l < q.size(); l++) {
622 double dpa = p.
pa - q[l].pa;
627 srcMatPair.push_back(p);
628 catMatPair.push_back(q[l]);
631 std::cout <<
"p dist: " << p.
distance <<
" pa: " << p.
pa << std::endl;
632 std::cout <<
"q dist: " << q[l].distance <<
" pa: " << q[l].pa << std::endl;
635 for (
size_t k = 0; k < sourceSubCat.size(); k++) {
636 if (p.
first == sourceSubCat[k] || p.
second == sourceSubCat[k])
continue;
640 std::vector<ProxyPair>::iterator r = searchPair3(posRefPairList, pp, q[l],
642 if (r != posRefPairList.end()) {
643 srcMatPair.push_back(pp);
644 catMatPair.push_back(*r);
646 std::cout <<
" p dist: " << pp.
distance <<
" pa: " << pp.
pa << std::endl;
647 std::cout <<
" r dist: " << (*r).distance <<
" pa: " << (*r).pa << std::endl;
649 if (srcMatPair.size() == fullShapeSize) {
655 bool goodMatch =
false;
656 if (srcMatPair.size() == fullShapeSize) {
658 for (
size_t k = 1; k < catMatPair.size(); k++) {
659 if (catMatPair[0].first != catMatPair[k].first) {
666 if (goodMatch && srcMatPair.size() == fullShapeSize) {
671 srcMat.push_back(srcMatPair[0].first);
672 catMat.push_back(catMatPair[0].first);
673 for (
size_t k = 0; k < srcMatPair.size(); k++) {
674 srcMat.push_back(srcMatPair[k].second);
675 catMat.push_back(catMatPair[k].second);
678 boost::shared_array<double> coeff = polyfit(1, srcMat, catMat);
681 for (
size_t k = 0; k < srcMat.size(); k++) {
682 std::cout <<
"circle(" << srcMat[k].getX() <<
","
683 << srcMat[k].getY() <<
",10) # color=green" << std::endl;
684 std::cout <<
"circle(" << catMat[k].getX() <<
","
685 << catMat[k].getY() <<
",10) # color=red" << std::endl;
686 std::cout <<
"line(" << srcMat[0].getX() <<
"," << srcMat[0].getY() <<
","
687 << srcMat[k].getX() <<
"," << srcMat[k].getY()
688 <<
") # line=0 0 color=green" << std::endl;
689 std::cout <<
"line(" << catMat[0].getX() <<
"," << catMat[0].getY() <<
","
690 << catMat[k].getX() <<
"," << catMat[k].getY()
691 <<
") # line=0 0 color=red" << std::endl;
700 (std::sqrt(a*a+c*c)*std::sqrt(b*b+d*d))),
703 std::cout <<
"Linear fit from match:" << std::endl;
704 std::cout << coeff[0] <<
" " << coeff[1] <<
" " << coeff[2] << std::endl;
705 std::cout << coeff[3] <<
" " << coeff[4] <<
" " << coeff[5] << std::endl;
706 std::cout <<
"Determinant (max " << control.
maxDeterminant <<
"): ";
707 std::cout << coeff[1] * coeff[5] - coeff[2] * coeff[4] - 1. << std::endl;
708 std::cout <<
"Angle between axes (deg; allowed 90 +/- ";
710 std::cout << theta.
asDegrees() << std::endl;
712 if (std::fabs(coeff[1] * coeff[5] - coeff[2] * coeff[4] - 1.)
718 std::cout <<
"Bad; continuing" << std::endl;
722 double x0,
y0, x1, y1;
726 for (
size_t i = 0; i < sourceSubCat.size(); i++) {
727 x0 = sourceSubCat[i].getX();
728 y0 = sourceSubCat[i].getY();
729 transform(1, coeff, x0, y0, &x1, &y1);
730 auto refObjDist = searchNearestPoint(posRefSubCat, x1, y1,
732 if (refObjDist.first != posRefSubCat.end()) {
734 srcMat.push_back(sourceSubCat[i]);
735 catMat.push_back(*refObjDist.first);
737 std::cout <<
"Match: " << x0 <<
"," << y0 <<
" --> "
738 << x1 <<
"," << y1 <<
" <==> "
739 << refObjDist.first->getX() <<
"," << refObjDist.first->getY()
747 std::cout <<
"Insufficient initial matches; continuing" << std::endl;
751 coeff = polyfit(1, srcMat, catMat);
753 std::cout <<
"Coefficients from initial matching:" << std::endl;
754 for (
size_t i = 0; i < 6; ++i) {
755 std::cout << coeff[i] <<
" ";
757 std::cout << std::endl;
760 matPair = FinalVerify(coeff, posRefProxyCat, sourceProxyCat,
763 std::cout <<
"Number of matches: " << matPair.size() <<
" vs " <<
768 std::cout <<
"Insufficient final matches; continuing" << std::endl;
770 if (matPair.size() > matPairSave.size()) {
771 matPairSave = matPair;
776 std::cout <<
"Finish" << std::endl;
778 matPairCand.push_back(matPair);
779 if (matPairCand.size() == 3) {
790 if (matPairCand.size() == 0) {
793 size_t nmatch = matPairCand[0].size();
795 for (
size_t i = 1; i < matPairCand.size(); i++) {
796 if (matPairCand[i].size() > nmatch) {
797 nmatch = matPairCand[i].size();
798 matPairRet = matPairCand[i];
AngleUnit const radians
constant with units of radians
int numPointsForShape
"number of points in a matching shape" ;
Extent< double, 2 > Extent2D
geom::Point2D skyToPixel(geom::Angle sky1, geom::Angle sky2) const
Convert from sky coordinates (e.g. RA/dec) to pixel positions.
double matchingAllowancePix
"maximum allowed distance between reference objects and sources (pixels)" ;
std::vector< ReferenceMatch > ReferenceMatchVector
Implementation of the WCS standard for a any projection.
boost::shared_ptr< coord::Coord > pixelToSky(double pix1, double pix2) const
Convert from pixel position to sky coordinates (e.g. RA/dec)
lsst::afw::coord::IcrsCoord IcrsCoord
lsst::afw::table::ReferenceMatchVector matchOptimisticB(lsst::afw::table::SimpleCatalog const &posRefCat, lsst::afw::table::SourceCatalog const &sourceCat, MatchOptimisticBControl const &control, afw::image::Wcs const &wcs, int posRefBegInd=0, bool verbose=false)
std::vector< RecordProxy > ProxyVector
Extent< double, 3 > Extent3D
A coordinate class intended to represent absolute positions.
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Schema getSchema() const
Return the schema associated with the catalog's table.
double maxDeterminant
"?" ;
Lightweight representation of a geometric match between two records.
Eigen::Matrix2d getCDMatrix() const
Returns the CD matrix.
virtual bool hasDistortion() const
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
double maxOffsetPix
"maximum allowed frame translation (pixels)" ;
double degToRad(long double angleInDegrees)
#define LSST_EXCEPT(type,...)
int numBrightStars
"maximum number of bright reference stars to use" ;
std::string refFluxField
"name of flux field in reference catalog" ;
afw::table::Key< double > b
double maxRotationDeg
"maximum allowed frame rotation (deg)" ;
Point< double, 2 > Point2D
ProxyVector makeProxies(lsst::afw::table::SourceCatalog const &sourceCat, afw::image::Wcs const &distortedWcs, afw::image::Wcs const &tanWcs)
std::string sourceFluxField
"name of flux field in source catalog" ;
Base::const_iterator const_iterator
boost::int64_t RecordId
Type used for unique IDs for records.
double allowedNonperpDeg
"allowed non-perpendicularity of x and y axes (deg)" ;
int minMatchedPairs
"minimum number of matches" ;
lsst::afw::geom::Point2D getPosition(lsst::afw::geom::AngleUnit unit=lsst::afw::geom::degrees) const
Return our contents in a Point2D object.
boost::shared_ptr< lsst::afw::table::SimpleRecord > record
Include files required for standard LSST Exception handling.
A FunctorKey used to get or set celestial coordiantes from a pair of Angle keys.
size_type size() const
Return the number of elements in the catalog.