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" 17 #include "lsst/pex/exceptions.h" 18 #include "lsst/afw/image/Wcs.h" 19 #include "lsst/afw/image/DistortedTanWcs.h" 20 #include "lsst/afw/geom/Angle.h" 23 namespace pexExcept = lsst::pex::exceptions;
24 namespace afwTable = lsst::afw::table;
29 afwTable::RecordId getRefId(
ProxyPair const &proxyPair) {
35 struct insertionOrderTag{};
36 typedef boost::multi_index_container<
38 boost::multi_index::indexed_by<
39 boost::multi_index::sequenced<
40 boost::multi_index::tag<insertionOrderTag>
42 boost::multi_index::ordered_unique<
43 boost::multi_index::tag<refIdTag>,
44 boost::multi_index::global_fun<
45 ProxyPair
const &, afwTable::RecordId , &getRefId
48 > MultiIndexedProxyPairList;
59 inline double absDeltaAngle(
double ang1,
double ang2) {
60 return std::fmod(std::fabs(ang1 - ang2), M_PI*2);
63 bool cmpPair(ProxyPair
const &a, ProxyPair
const &b) {
72 struct CompareProxyFlux {
75 double aFlux = a.
record->get(key);
76 double bFlux = b.
record->get(key);
77 if (std::isnan(aFlux)) {
80 if (std::isnan(bFlux)) {
86 afwTable::Key<double> key;
101 afwTable::Key<double>
const & key,
103 std::size_t startInd=0
105 if (startInd >= a.size()) {
106 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"startInd too big");
108 CompareProxyFlux cmp = {key};
110 std::sort(b.begin(), b.end(), cmp);
111 std::size_t
const endInd = std::min(startInd + num, b.size());
112 return ProxyVector(b.begin() + startInd, b.begin() + endInd);
115 std::vector<ProxyPair> searchPair(
116 std::vector<ProxyPair>
const &a,
121 std::vector<ProxyPair> v;
123 for (
size_t i = 0; i < a.size(); i++) {
124 double dd = std::fabs(a[i].distance - p.
distance);
125 double dpa = absDeltaAngle(a[i].pa, p.
pa);
126 if (dd < e && dpa < e_dpa) {
134 std::vector<ProxyPair>::iterator searchPair3(
135 std::vector<ProxyPair> &a,
142 std::vector<ProxyPair>::iterator idx = a.end();
143 double dd_min = 1.E+10;
146 for (
auto i = a.begin(); i != a.end(); ++i) {
147 double dd = std::fabs(i->distance - p.
distance);
150 absDeltaAngle(p.
pa, i->pa - dpa) < e_dpa &&
152 (i->first == q.
first)) {
158 absDeltaAngle(p.
pa, i->pa - dpa) < dpa_min) {
159 dpa_min = std::fabs(p.
pa - i->pa - dpa);
170 boost::shared_array<double>
const & coeff,
176 int ncoeff = (order + 1) * (order + 2) / 2;
180 for (
int i = 0; i <= order; i++) {
181 for (
int k = 0; k <= i; k++) {
183 *xn += coeff[n] * pow(x, j) * pow(y, k);
184 *yn += coeff[n+ncoeff] * pow(x, j) * pow(y, k);
190 boost::shared_array<double> polyfit(
195 int ncoeff = (order + 1) * (order + 2) / 2;
196 std::unique_ptr<int[]> xorder(
new int[ncoeff]);
197 std::unique_ptr<int[]> yorder(
new int[ncoeff]);
200 for (
int i = 0; i <= order; i++) {
201 for (
int k = 0; k <= i; k++) {
209 std::unique_ptr<int[]> flag(
new int[img.size()]);
210 for (
size_t k = 0; k < img.size(); k++) {
214 std::unique_ptr<double[]> a_data(
new double[ncoeff*ncoeff]);
215 std::unique_ptr<double[]> b_data(
new double[ncoeff]);
216 std::unique_ptr<double[]> c_data(
new double[ncoeff]);
218 boost::shared_array<double> coeff(
new double[ncoeff*2]);
220 for (
int loop = 0; loop < 1; loop++) {
221 for (
int i = 0; i < ncoeff; i++) {
222 for (
int j = 0; j < ncoeff; j++) {
223 a_data[i*ncoeff+j] = 0.0;
224 for (
size_t k = 0; k < img.size(); k++) {
226 a_data[i*ncoeff+j] += pow(img[k].getX(), xorder[i]) *
227 pow(img[k].getY(), yorder[i]) *
228 pow(img[k].getX(), xorder[j]) *
229 pow(img[k].getY(), yorder[j]);
233 b_data[i] = c_data[i] = 0.0;
234 for (
unsigned int k = 0; k < img.size(); k++) {
236 b_data[i] += pow(img[k].getX(), xorder[i]) *
237 pow(img[k].getY(), yorder[i]) *
239 c_data[i] += pow(img[k].getX(), xorder[i]) *
240 pow(img[k].getY(), yorder[i]) *
246 gsl_matrix_view a = gsl_matrix_view_array(a_data.get(), ncoeff, ncoeff);
247 gsl_vector_view b = gsl_vector_view_array(b_data.get(), ncoeff);
248 gsl_vector_view c = gsl_vector_view_array(c_data.get(), ncoeff);
250 std::shared_ptr<gsl_vector> x(gsl_vector_alloc(ncoeff), gsl_vector_free);
251 std::shared_ptr<gsl_vector> y(gsl_vector_alloc(ncoeff), gsl_vector_free);
255 std::shared_ptr<gsl_permutation> p(gsl_permutation_alloc(ncoeff), gsl_permutation_free);
257 gsl_linalg_LU_decomp(&a.matrix, p.get(), &s);
258 gsl_linalg_LU_solve(&a.matrix, p.get(), &b.vector, x.get());
259 gsl_linalg_LU_solve(&a.matrix, p.get(), &c.vector, y.get());
261 for (
int i = 0; i < ncoeff; i++) {
262 coeff[i] = x->data[i];
263 coeff[i+ncoeff] = y->data[i];
266 double S, Sx, Sy, Sxx, Syy;
267 S = Sx = Sy = Sxx = Syy = 0.0;
268 for (
size_t k = 0; k < img.size(); k++) {
270 double x0 = img[k].getX();
271 double y0 = img[k].getY();
273 transform(order, coeff, x0, y0, &x1, &y1);
275 Sx += (x1 - posRefCat[k].getX());
276 Sxx += (x1 - posRefCat[k].getX()) * (x1 - posRefCat[k].getX());
277 Sy += (y1 - posRefCat[k].getY());
278 Syy += (y1 - posRefCat[k].getY()) * (y1 - posRefCat[k].getY());
281 double x_sig = std::sqrt((Sxx - Sx * Sx / S) / S);
282 double y_sig = std::sqrt((Syy - Sy * Sy / S) / S);
285 for (
size_t k = 0; k < img.size(); k++) {
286 double x0 = img[k].getX();
287 double y0 = img[k].getY();
289 transform(order, coeff, x0, y0, &x1, &y1);
290 if (std::fabs(x1-posRefCat[k].getX()) > 2. * x_sig ||
291 std::fabs(y1-posRefCat[k].getY()) > 2. * y_sig) {
313 std::pair<ProxyVector::const_iterator, double> searchNearestPoint(
317 double matchingAllowancePix
319 auto minDistSq = matchingAllowancePix*matchingAllowancePix;
320 auto foundPtr = posRefCat.end();
321 for (
auto posRefPtr = posRefCat.begin(); posRefPtr != posRefCat.end(); ++posRefPtr) {
322 auto const dx = posRefPtr->getX() - x;
323 auto const dy = posRefPtr->getY() - y;
324 auto const distSq = dx*dx + dy*dy;
325 if (distSq < minDistSq) {
326 foundPtr = posRefPtr;
330 return std::make_pair(foundPtr, std::sqrt(minDistSq));
351 void addNearestMatch(
352 MultiIndexedProxyPairList &proxyPairList,
353 boost::shared_array<double> coeff,
356 double matchingAllowancePix
359 auto x0 = source.
getX();
360 auto y0 = source.
getY();
361 transform(1, coeff, x0, y0, &x1, &y1);
362 auto refObjDist = searchNearestPoint(posRefCat, x1, y1, matchingAllowancePix);
363 if (refObjDist.first == posRefCat.end()) {
367 auto existingMatch = proxyPairList.get<refIdTag>().find(refObjDist.first->record->getId());
368 if (existingMatch == proxyPairList.get<refIdTag>().end()) {
370 auto proxyPair = ProxyPair(*refObjDist.first, source);
371 proxyPairList.get<refIdTag>().insert(proxyPair);
379 if (existingMatch->distance <= refObjDist.second) {
384 proxyPairList.get<refIdTag>().erase(existingMatch);
385 auto proxyPair = ProxyPair(*refObjDist.first, source);
386 proxyPairList.get<refIdTag>().insert(proxyPair);
401 afwTable::ReferenceMatchVector FinalVerify(
402 boost::shared_array<double> coeff,
405 double matchingAllowancePix,
408 MultiIndexedProxyPairList proxyPairList;
410 for (
auto sourcePtr = sourceCat.begin(); sourcePtr != sourceCat.end(); ++sourcePtr) {
411 addNearestMatch(proxyPairList, coeff, posRefCat, *sourcePtr, matchingAllowancePix);
414 if (proxyPairList.size() > 5) {
415 for (
auto j = 0; j < 100; j++) {
416 auto prevNumMatches = proxyPairList.size();
419 srcMat.reserve(proxyPairList.size());
420 catMat.reserve(proxyPairList.size());
421 for (
auto matchPtr = proxyPairList.get<refIdTag>().begin();
422 matchPtr != proxyPairList.get<refIdTag>().end(); ++matchPtr) {
423 catMat.push_back(matchPtr->first);
424 srcMat.push_back(matchPtr->second);
426 coeff = polyfit(order, srcMat, catMat);
427 proxyPairList.clear();
429 for (ProxyVector::const_iterator sourcePtr = sourceCat.begin();
430 sourcePtr != sourceCat.end(); ++sourcePtr) {
431 addNearestMatch(proxyPairList, coeff, posRefCat, *sourcePtr, matchingAllowancePix);
433 if (proxyPairList.size() == prevNumMatches) {
438 auto matPair = afwTable::ReferenceMatchVector();
439 matPair.reserve(proxyPairList.size());
440 for (
auto proxyPairIter = proxyPairList.get<insertionOrderTag>().begin();
441 proxyPairIter != proxyPairList.get<insertionOrderTag>().end(); ++proxyPairIter) {
442 matPair.push_back(afwTable::ReferenceMatch(
443 proxyPairIter->first.record,
444 std::static_pointer_cast<afwTable::SourceRecord>(proxyPairIter->second.record),
445 proxyPairIter->distance
458 void MatchOptimisticBControl::validate()
const {
459 if (refFluxField.empty()) {
460 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"refFluxField must be specified");
462 if (sourceFluxField.empty()) {
463 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"sourceFluxField must be specified");
465 if (numBrightStars <= 0) {
466 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numBrightStars must be positive");
468 if (minMatchedPairs < 0) {
469 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"minMatchedPairs must not be negative");
471 if (matchingAllowancePix <= 0) {
472 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"matchingAllowancePix must be positive");
474 if (maxOffsetPix <= 0) {
475 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxOffsetPix must be positive");
477 if (maxRotationDeg <= 0) {
478 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxRotationRad must be positive");
480 if (allowedNonperpDeg <= 0) {
481 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"allowedNonperpDeg must be positive");
483 if (numPointsForShape <= 0) {
484 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numPointsForShape must be positive");
486 if (maxDeterminant <= 0) {
487 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxDeterminant must be positive");
492 afw::image::Wcs
const& distortedWcs,
493 afw::image::Wcs
const& tanWcs
497 r.reserve(sourceCat.size());
498 for (afwTable::SourceCatalog::const_iterator sourcePtr = sourceCat.begin();
499 sourcePtr != sourceCat.end(); ++sourcePtr) {
501 tanWcs.skyToPixel(*distortedWcs.pixelToSky(sourcePtr->getCentroid()))));
507 afw::image::Wcs
const& tanWcs
510 auto coordKey = afwTable::CoordKey(posRefCat.getSchema()[
"coord"]);
512 r.reserve(posRefCat.size());
513 for (afwTable::SimpleCatalog::const_iterator posRefPtr = posRefCat.begin();
514 posRefPtr != posRefCat.end(); ++posRefPtr) {
515 r.push_back(
RecordProxy(posRefPtr, tanWcs.skyToPixel(posRefPtr->get(coordKey))));
521 afwTable::SimpleCatalog
const &posRefCat,
522 afwTable::SourceCatalog
const &sourceCat,
524 afw::image::Wcs
const& wcs,
529 if (posRefCat.empty()) {
530 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"no entries in posRefCat");
532 if (sourceCat.empty()) {
533 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"no entries in sourceCat");
535 if (posRefBegInd < 0) {
536 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"posRefBegInd < 0");
538 if (static_cast<size_t>(posRefBegInd) >= posRefCat.size()) {
539 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"posRefBegInd too big");
541 double const maxRotationRad = afw::geom::degToRad(control.
maxRotationDeg);
543 CONST_PTR(afw::image::Wcs) tanWcs;
544 if (wcs.hasDistortion()) {
547 afw::geom::Extent2D srcCenter(0, 0);
548 for (
auto iter = sourceCat.begin(); iter != sourceCat.end(); ++iter) {
549 srcCenter += afw::geom::Extent2D(iter->getCentroid());
551 srcCenter /= sourceCat.size();
553 afw::geom::Extent3D refCenter(0, 0, 0);
554 for (
auto iter = posRefCat.begin(); iter != posRefCat.end(); ++iter) {
555 refCenter += afw::geom::Extent3D(iter->getCoord().toIcrs().getVector());
557 refCenter /= posRefCat.size();
559 tanWcs = std::make_shared<afw::image::Wcs>(
560 afw::coord::IcrsCoord(afw::geom::Point3D(refCenter)).getPosition(),
561 afw::geom::Point2D(srcCenter),
579 posRefCat.getSchema().find<
double>(control.
refFluxField).key,
580 sourceSubCat.size()+25,
583 std::cout <<
"Catalog sizes: " << sourceSubCat.size() <<
" " << posRefSubCat.size() << std::endl;
587 std::vector<ProxyPair> posRefPairList;
588 size_t const posRefCatSubSize = posRefSubCat.size();
589 for (
size_t i = 0; i < posRefCatSubSize-1; i++) {
590 for (
size_t j = i+1; j < posRefCatSubSize; j++) {
591 posRefPairList.push_back(ProxyPair(posRefSubCat[i], posRefSubCat[j]));
594 std::sort(posRefPairList.begin(), posRefPairList.end(), cmpPair);
597 std::vector<ProxyPair> sourcePairList;
598 size_t const sourceSubCatSize = sourceSubCat.size();
599 for (
size_t i = 0; i < sourceSubCatSize-1; i++) {
600 for (
size_t j = i+1; j < sourceSubCatSize; j++) {
601 sourcePairList.push_back(ProxyPair(sourceSubCat[i], sourceSubCat[j]));
604 std::sort(sourcePairList.begin(), sourcePairList.end(), cmpPair);
606 afwTable::ReferenceMatchVector matPair;
607 afwTable::ReferenceMatchVector matPairSave;
608 std::vector<afwTable::ReferenceMatchVector> matPairCand;
611 for (
size_t ii = 0; ii < sourcePairList.size(); ii++) {
612 ProxyPair p = sourcePairList[ii];
614 std::vector<ProxyPair> q = searchPair(posRefPairList, p,
620 std::vector<ProxyPair> srcMatPair;
621 std::vector<ProxyPair> catMatPair;
624 for (
size_t l = 0; l < q.size(); l++) {
627 double dpa = p.
pa - q[l].pa;
632 srcMatPair.push_back(p);
633 catMatPair.push_back(q[l]);
636 std::cout <<
"p dist: " << p.
distance <<
" pa: " << p.
pa << std::endl;
637 std::cout <<
"q dist: " << q[l].distance <<
" pa: " << q[l].pa << std::endl;
640 for (
size_t k = 0; k < sourceSubCat.size(); k++) {
641 if (p.
first == sourceSubCat[k] || p.
second == sourceSubCat[k])
continue;
643 ProxyPair pp(p.
first, sourceSubCat[k]);
645 std::vector<ProxyPair>::iterator r = searchPair3(posRefPairList, pp, q[l],
647 if (r != posRefPairList.end()) {
648 srcMatPair.push_back(pp);
649 catMatPair.push_back(*r);
651 std::cout <<
" p dist: " << pp.
distance <<
" pa: " << pp.
pa << std::endl;
652 std::cout <<
" r dist: " << (*r).distance <<
" pa: " << (*r).pa << std::endl;
654 if (srcMatPair.size() == fullShapeSize) {
660 bool goodMatch =
false;
661 if (srcMatPair.size() == fullShapeSize) {
663 for (
size_t k = 1; k < catMatPair.size(); k++) {
664 if (catMatPair[0].first != catMatPair[k].first) {
671 if (goodMatch && srcMatPair.size() == fullShapeSize) {
676 srcMat.push_back(srcMatPair[0].first);
677 catMat.push_back(catMatPair[0].first);
678 for (
size_t k = 0; k < srcMatPair.size(); k++) {
679 srcMat.push_back(srcMatPair[k].second);
680 catMat.push_back(catMatPair[k].second);
683 boost::shared_array<double> coeff = polyfit(1, srcMat, catMat);
686 for (
size_t k = 0; k < srcMat.size(); k++) {
687 std::cout <<
"circle(" << srcMat[k].getX() <<
"," 688 << srcMat[k].getY() <<
",10) # color=green" << std::endl;
689 std::cout <<
"circle(" << catMat[k].getX() <<
"," 690 << catMat[k].getY() <<
",10) # color=red" << std::endl;
691 std::cout <<
"line(" << srcMat[0].getX() <<
"," << srcMat[0].getY() <<
"," 692 << srcMat[k].getX() <<
"," << srcMat[k].getY()
693 <<
") # line=0 0 color=green" << std::endl;
694 std::cout <<
"line(" << catMat[0].getX() <<
"," << catMat[0].getY() <<
"," 695 << catMat[k].getX() <<
"," << catMat[k].getY()
696 <<
") # line=0 0 color=red" << std::endl;
704 afw::geom::Angle
const theta(std::acos((a*b+c*d)/
705 (std::sqrt(a*a+c*c)*std::sqrt(b*b+d*d))),
708 std::cout <<
"Linear fit from match:" << std::endl;
709 std::cout << coeff[0] <<
" " << coeff[1] <<
" " << coeff[2] << std::endl;
710 std::cout << coeff[3] <<
" " << coeff[4] <<
" " << coeff[5] << std::endl;
711 std::cout <<
"Determinant (max " << control.
maxDeterminant <<
"): ";
712 std::cout << coeff[1] * coeff[5] - coeff[2] * coeff[4] - 1. << std::endl;
713 std::cout <<
"Angle between axes (deg; allowed 90 +/- ";
715 std::cout << theta.asDegrees() << std::endl;
717 if (std::fabs(coeff[1] * coeff[5] - coeff[2] * coeff[4] - 1.)
723 std::cout <<
"Bad; continuing" << std::endl;
727 double x0, y0, x1, y1;
731 for (
size_t i = 0; i < sourceSubCat.size(); i++) {
732 x0 = sourceSubCat[i].getX();
733 y0 = sourceSubCat[i].getY();
734 transform(1, coeff, x0, y0, &x1, &y1);
735 auto refObjDist = searchNearestPoint(posRefSubCat, x1, y1,
737 if (refObjDist.first != posRefSubCat.end()) {
739 srcMat.push_back(sourceSubCat[i]);
740 catMat.push_back(*refObjDist.first);
742 std::cout <<
"Match: " << x0 <<
"," << y0 <<
" --> " 743 << x1 <<
"," << y1 <<
" <==> " 744 << refObjDist.first->getX() <<
"," << refObjDist.first->getY()
752 std::cout <<
"Insufficient initial matches; continuing" << std::endl;
756 coeff = polyfit(1, srcMat, catMat);
758 std::cout <<
"Coefficients from initial matching:" << std::endl;
759 for (
size_t i = 0; i < 6; ++i) {
760 std::cout << coeff[i] <<
" ";
762 std::cout << std::endl;
765 matPair = FinalVerify(coeff, posRefProxyCat, sourceProxyCat,
768 std::cout <<
"Number of matches: " << matPair.size() <<
" vs " <<
771 if (matPair.size() <=
static_cast<std::size_t
>(control.
minMatchedPairs)) {
773 std::cout <<
"Insufficient final matches; continuing" << std::endl;
775 if (matPair.size() > matPairSave.size()) {
776 matPairSave = matPair;
781 std::cout <<
"Finish" << std::endl;
783 matPairCand.push_back(matPair);
784 if (matPairCand.size() == 3) {
795 if (matPairCand.size() == 0) {
798 size_t nmatch = matPairCand[0].size();
799 afwTable::ReferenceMatchVector matPairRet = matPairCand[0];
800 for (
size_t i = 1; i < matPairCand.size(); i++) {
801 if (matPairCand[i].size() > nmatch) {
802 nmatch = matPairCand[i].size();
803 matPairRet = matPairCand[i];
ProxyVector makeProxies(afwTable::SimpleCatalog const &posRefCat, afw::image::Wcs const &tanWcs)
int numPointsForShape
"number of points in a matching shape" ;
double matchingAllowancePix
"maximum allowed distance between reference objects and sources (pixels)" ;
std::vector< RecordProxy > ProxyVector
double maxDeterminant
"?" ;
double maxOffsetPix
"maximum allowed frame translation (pixels)" ;
int numBrightStars
"maximum number of bright reference stars to use" ;
afwTable::ReferenceMatchVector matchOptimisticB(afwTable::SimpleCatalog const &posRefCat, afwTable::SourceCatalog const &sourceCat, MatchOptimisticBControl const &control, afw::image::Wcs const &wcs, int posRefBegInd, bool verbose)
std::string refFluxField
"name of flux field in reference catalog" ;
A wrapper around a SimpleRecord or SourceRecord that allows us to record a pixel position in a way th...
double maxRotationDeg
"maximum allowed frame rotation (deg)" ;
std::string sourceFluxField
"name of flux field in source catalog" ;
double allowedNonperpDeg
"allowed non-perpendicularity of x and y axes (deg)" ;
int minMatchedPairs
"minimum number of matches" ;
boost::shared_ptr< lsst::afw::table::SimpleRecord > record