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/geom/Angle.h" 22 namespace pexExcept = lsst::pex::exceptions;
23 namespace afwTable = lsst::afw::table;
28 afwTable::RecordId getRefId(
ProxyPair const &proxyPair) {
34 struct insertionOrderTag{};
35 typedef boost::multi_index_container<
37 boost::multi_index::indexed_by<
38 boost::multi_index::sequenced<
39 boost::multi_index::tag<insertionOrderTag>
41 boost::multi_index::ordered_unique<
42 boost::multi_index::tag<refIdTag>,
43 boost::multi_index::global_fun<
44 ProxyPair
const &, afwTable::RecordId , &getRefId
47 > MultiIndexedProxyPairList;
58 inline double absDeltaAngle(
double ang1,
double ang2) {
59 return std::fmod(std::fabs(ang1 - ang2), M_PI*2);
62 bool cmpPair(ProxyPair
const &a, ProxyPair
const &b) {
71 struct CompareProxyFlux {
74 double aFlux = a.
record->get(key);
75 double bFlux = b.
record->get(key);
76 if (std::isnan(aFlux)) {
79 if (std::isnan(bFlux)) {
85 afwTable::Key<double> key;
100 afwTable::Key<double>
const & key,
102 std::size_t startInd=0
104 if (startInd >= a.size()) {
105 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"startInd too big");
107 CompareProxyFlux cmp = {key};
109 std::sort(b.begin(), b.end(), cmp);
110 std::size_t
const endInd = std::min(startInd + num, b.size());
111 return ProxyVector(b.begin() + startInd, b.begin() + endInd);
114 std::vector<ProxyPair> searchPair(
115 std::vector<ProxyPair>
const &a,
120 std::vector<ProxyPair> v;
122 for (
size_t i = 0; i < a.size(); i++) {
123 double dd = std::fabs(a[i].distance - p.
distance);
124 double dpa = absDeltaAngle(a[i].pa, p.
pa);
125 if (dd < e && dpa < e_dpa) {
133 std::vector<ProxyPair>::iterator searchPair3(
134 std::vector<ProxyPair> &a,
141 std::vector<ProxyPair>::iterator idx = a.end();
142 double dd_min = 1.E+10;
145 for (
auto i = a.begin(); i != a.end(); ++i) {
146 double dd = std::fabs(i->distance - p.
distance);
149 absDeltaAngle(p.
pa, i->pa - dpa) < e_dpa &&
151 (i->first == q.
first)) {
157 absDeltaAngle(p.
pa, i->pa - dpa) < dpa_min) {
158 dpa_min = std::fabs(p.
pa - i->pa - dpa);
169 boost::shared_array<double>
const & coeff,
175 int ncoeff = (order + 1) * (order + 2) / 2;
179 for (
int i = 0; i <= order; i++) {
180 for (
int k = 0; k <= i; k++) {
182 *xn += coeff[n] * pow(x, j) * pow(y, k);
183 *yn += coeff[n+ncoeff] * pow(x, j) * pow(y, k);
189 boost::shared_array<double> polyfit(
194 int ncoeff = (order + 1) * (order + 2) / 2;
195 std::unique_ptr<int[]> xorder(
new int[ncoeff]);
196 std::unique_ptr<int[]> yorder(
new int[ncoeff]);
199 for (
int i = 0; i <= order; i++) {
200 for (
int k = 0; k <= i; k++) {
208 std::unique_ptr<int[]> flag(
new int[img.size()]);
209 for (
size_t k = 0; k < img.size(); k++) {
213 std::unique_ptr<double[]> a_data(
new double[ncoeff*ncoeff]);
214 std::unique_ptr<double[]> b_data(
new double[ncoeff]);
215 std::unique_ptr<double[]> c_data(
new double[ncoeff]);
217 boost::shared_array<double> coeff(
new double[ncoeff*2]);
219 for (
int loop = 0; loop < 1; loop++) {
220 for (
int i = 0; i < ncoeff; i++) {
221 for (
int j = 0; j < ncoeff; j++) {
222 a_data[i*ncoeff+j] = 0.0;
223 for (
size_t k = 0; k < img.size(); k++) {
225 a_data[i*ncoeff+j] += pow(img[k].getX(), xorder[i]) *
226 pow(img[k].getY(), yorder[i]) *
227 pow(img[k].getX(), xorder[j]) *
228 pow(img[k].getY(), yorder[j]);
232 b_data[i] = c_data[i] = 0.0;
233 for (
unsigned int k = 0; k < img.size(); k++) {
235 b_data[i] += pow(img[k].getX(), xorder[i]) *
236 pow(img[k].getY(), yorder[i]) *
238 c_data[i] += pow(img[k].getX(), xorder[i]) *
239 pow(img[k].getY(), yorder[i]) *
245 gsl_matrix_view a = gsl_matrix_view_array(a_data.get(), ncoeff, ncoeff);
246 gsl_vector_view b = gsl_vector_view_array(b_data.get(), ncoeff);
247 gsl_vector_view c = gsl_vector_view_array(c_data.get(), ncoeff);
249 std::shared_ptr<gsl_vector> x(gsl_vector_alloc(ncoeff), gsl_vector_free);
250 std::shared_ptr<gsl_vector> y(gsl_vector_alloc(ncoeff), gsl_vector_free);
254 std::shared_ptr<gsl_permutation> p(gsl_permutation_alloc(ncoeff), gsl_permutation_free);
256 gsl_linalg_LU_decomp(&a.matrix, p.get(), &s);
257 gsl_linalg_LU_solve(&a.matrix, p.get(), &b.vector, x.get());
258 gsl_linalg_LU_solve(&a.matrix, p.get(), &c.vector, y.get());
260 for (
int i = 0; i < ncoeff; i++) {
261 coeff[i] = x->data[i];
262 coeff[i+ncoeff] = y->data[i];
265 double S, Sx, Sy, Sxx, Syy;
266 S = Sx = Sy = Sxx = Syy = 0.0;
267 for (
size_t k = 0; k < img.size(); k++) {
269 double x0 = img[k].getX();
270 double y0 = img[k].getY();
272 transform(order, coeff, x0, y0, &x1, &y1);
274 Sx += (x1 - posRefCat[k].getX());
275 Sxx += (x1 - posRefCat[k].getX()) * (x1 - posRefCat[k].getX());
276 Sy += (y1 - posRefCat[k].getY());
277 Syy += (y1 - posRefCat[k].getY()) * (y1 - posRefCat[k].getY());
280 double x_sig = std::sqrt((Sxx - Sx * Sx / S) / S);
281 double y_sig = std::sqrt((Syy - Sy * Sy / S) / S);
284 for (
size_t k = 0; k < img.size(); k++) {
285 double x0 = img[k].getX();
286 double y0 = img[k].getY();
288 transform(order, coeff, x0, y0, &x1, &y1);
289 if (std::fabs(x1-posRefCat[k].getX()) > 2. * x_sig ||
290 std::fabs(y1-posRefCat[k].getY()) > 2. * y_sig) {
312 std::pair<ProxyVector::const_iterator, double> searchNearestPoint(
316 double matchingAllowancePix
318 auto minDistSq = matchingAllowancePix*matchingAllowancePix;
319 auto foundPtr = posRefCat.end();
320 for (
auto posRefPtr = posRefCat.begin(); posRefPtr != posRefCat.end(); ++posRefPtr) {
321 auto const dx = posRefPtr->getX() - x;
322 auto const dy = posRefPtr->getY() - y;
323 auto const distSq = dx*dx + dy*dy;
324 if (distSq < minDistSq) {
325 foundPtr = posRefPtr;
329 return std::make_pair(foundPtr, std::sqrt(minDistSq));
350 void addNearestMatch(
351 MultiIndexedProxyPairList &proxyPairList,
352 boost::shared_array<double> coeff,
355 double matchingAllowancePix
358 auto x0 = source.
getX();
359 auto y0 = source.
getY();
360 transform(1, coeff, x0, y0, &x1, &y1);
361 auto refObjDist = searchNearestPoint(posRefCat, x1, y1, matchingAllowancePix);
362 if (refObjDist.first == posRefCat.end()) {
366 auto existingMatch = proxyPairList.get<refIdTag>().find(refObjDist.first->record->getId());
367 if (existingMatch == proxyPairList.get<refIdTag>().end()) {
369 auto proxyPair = ProxyPair(*refObjDist.first, source);
370 proxyPairList.get<refIdTag>().insert(proxyPair);
378 if (existingMatch->distance <= refObjDist.second) {
383 proxyPairList.get<refIdTag>().erase(existingMatch);
384 auto proxyPair = ProxyPair(*refObjDist.first, source);
385 proxyPairList.get<refIdTag>().insert(proxyPair);
400 afwTable::ReferenceMatchVector FinalVerify(
401 boost::shared_array<double> coeff,
404 double matchingAllowancePix,
407 MultiIndexedProxyPairList proxyPairList;
409 for (
auto sourcePtr = sourceCat.begin(); sourcePtr != sourceCat.end(); ++sourcePtr) {
410 addNearestMatch(proxyPairList, coeff, posRefCat, *sourcePtr, matchingAllowancePix);
413 if (proxyPairList.size() > 5) {
414 for (
auto j = 0; j < 100; j++) {
415 auto prevNumMatches = proxyPairList.size();
418 srcMat.reserve(proxyPairList.size());
419 catMat.reserve(proxyPairList.size());
420 for (
auto matchPtr = proxyPairList.get<refIdTag>().begin();
421 matchPtr != proxyPairList.get<refIdTag>().end(); ++matchPtr) {
422 catMat.push_back(matchPtr->first);
423 srcMat.push_back(matchPtr->second);
425 coeff = polyfit(order, srcMat, catMat);
426 proxyPairList.clear();
428 for (ProxyVector::const_iterator sourcePtr = sourceCat.begin();
429 sourcePtr != sourceCat.end(); ++sourcePtr) {
430 addNearestMatch(proxyPairList, coeff, posRefCat, *sourcePtr, matchingAllowancePix);
432 if (proxyPairList.size() == prevNumMatches) {
437 auto matPair = afwTable::ReferenceMatchVector();
438 matPair.reserve(proxyPairList.size());
439 for (
auto proxyPairIter = proxyPairList.get<insertionOrderTag>().begin();
440 proxyPairIter != proxyPairList.get<insertionOrderTag>().end(); ++proxyPairIter) {
441 matPair.push_back(afwTable::ReferenceMatch(
442 proxyPairIter->first.record,
443 std::static_pointer_cast<afwTable::SourceRecord>(proxyPairIter->second.record),
444 proxyPairIter->distance
457 void MatchOptimisticBControl::validate()
const {
458 if (refFluxField.empty()) {
459 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"refFluxField must be specified");
461 if (sourceFluxField.empty()) {
462 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"sourceFluxField must be specified");
464 if (numBrightStars <= 0) {
465 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numBrightStars must be positive");
467 if (minMatchedPairs < 0) {
468 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"minMatchedPairs must not be negative");
470 if (matchingAllowancePix <= 0) {
471 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"matchingAllowancePix must be positive");
473 if (maxOffsetPix <= 0) {
474 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxOffsetPix must be positive");
476 if (maxRotationDeg <= 0) {
477 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxRotationRad must be positive");
479 if (allowedNonperpDeg <= 0) {
480 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"allowedNonperpDeg must be positive");
482 if (numPointsForShape <= 0) {
483 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numPointsForShape must be positive");
485 if (maxDeterminant <= 0) {
486 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxDeterminant must be positive");
491 afw::image::Wcs
const& distortedWcs,
492 afw::image::Wcs
const& tanWcs
496 r.reserve(sourceCat.size());
497 for (afwTable::SourceCatalog::const_iterator sourcePtr = sourceCat.begin();
498 sourcePtr != sourceCat.end(); ++sourcePtr) {
500 tanWcs.skyToPixel(*distortedWcs.pixelToSky(sourcePtr->getCentroid()))));
506 afw::image::Wcs
const& tanWcs
509 auto coordKey = afwTable::CoordKey(posRefCat.getSchema()[
"coord"]);
511 r.reserve(posRefCat.size());
512 for (afwTable::SimpleCatalog::const_iterator posRefPtr = posRefCat.begin();
513 posRefPtr != posRefCat.end(); ++posRefPtr) {
514 r.push_back(
RecordProxy(posRefPtr, tanWcs.skyToPixel(posRefPtr->get(coordKey))));
520 afwTable::SimpleCatalog
const &posRefCat,
521 afwTable::SourceCatalog
const &sourceCat,
523 afw::image::Wcs
const& wcs,
528 if (posRefCat.empty()) {
529 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"no entries in posRefCat");
531 if (sourceCat.empty()) {
532 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"no entries in sourceCat");
534 if (posRefBegInd < 0) {
535 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"posRefBegInd < 0");
537 if (static_cast<size_t>(posRefBegInd) >= posRefCat.size()) {
538 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"posRefBegInd too big");
540 double const maxRotationRad = afw::geom::degToRad(control.
maxRotationDeg);
542 CONST_PTR(afw::image::Wcs) tanWcs;
543 if (wcs.hasDistortion()) {
546 afw::geom::Extent2D srcCenter(0, 0);
547 for (
auto iter = sourceCat.begin(); iter != sourceCat.end(); ++iter) {
548 srcCenter += afw::geom::Extent2D(iter->getCentroid());
550 srcCenter /= sourceCat.size();
552 afw::geom::Extent3D refCenter(0, 0, 0);
553 for (
auto iter = posRefCat.begin(); iter != posRefCat.end(); ++iter) {
554 refCenter += afw::geom::Extent3D(iter->getCoord().toIcrs().getVector());
556 refCenter /= posRefCat.size();
558 tanWcs = std::make_shared<afw::image::Wcs>(
559 afw::coord::IcrsCoord(afw::geom::Point3D(refCenter)).getPosition(),
560 afw::geom::Point2D(srcCenter),
578 posRefCat.getSchema().find<
double>(control.
refFluxField).key,
579 sourceSubCat.size()+25,
582 std::cout <<
"Catalog sizes: " << sourceSubCat.size() <<
" " << posRefSubCat.size() << std::endl;
586 std::vector<ProxyPair> posRefPairList;
587 size_t const posRefCatSubSize = posRefSubCat.size();
588 for (
size_t i = 0; i < posRefCatSubSize-1; i++) {
589 for (
size_t j = i+1; j < posRefCatSubSize; j++) {
590 posRefPairList.push_back(ProxyPair(posRefSubCat[i], posRefSubCat[j]));
593 std::sort(posRefPairList.begin(), posRefPairList.end(), cmpPair);
596 std::vector<ProxyPair> sourcePairList;
597 size_t const sourceSubCatSize = sourceSubCat.size();
598 for (
size_t i = 0; i < sourceSubCatSize-1; i++) {
599 for (
size_t j = i+1; j < sourceSubCatSize; j++) {
600 sourcePairList.push_back(ProxyPair(sourceSubCat[i], sourceSubCat[j]));
603 std::sort(sourcePairList.begin(), sourcePairList.end(), cmpPair);
605 afwTable::ReferenceMatchVector matPair;
606 afwTable::ReferenceMatchVector matPairSave;
607 std::vector<afwTable::ReferenceMatchVector> matPairCand;
610 for (
size_t ii = 0; ii < sourcePairList.size(); ii++) {
611 ProxyPair p = sourcePairList[ii];
613 std::vector<ProxyPair> q = searchPair(posRefPairList, p,
619 std::vector<ProxyPair> srcMatPair;
620 std::vector<ProxyPair> catMatPair;
623 for (
size_t l = 0; l < q.size(); l++) {
626 double dpa = p.
pa - q[l].pa;
631 srcMatPair.push_back(p);
632 catMatPair.push_back(q[l]);
635 std::cout <<
"p dist: " << p.
distance <<
" pa: " << p.
pa << std::endl;
636 std::cout <<
"q dist: " << q[l].distance <<
" pa: " << q[l].pa << std::endl;
639 for (
size_t k = 0; k < sourceSubCat.size(); k++) {
640 if (p.
first == sourceSubCat[k] || p.
second == sourceSubCat[k])
continue;
642 ProxyPair pp(p.
first, sourceSubCat[k]);
644 std::vector<ProxyPair>::iterator r = searchPair3(posRefPairList, pp, q[l],
646 if (r != posRefPairList.end()) {
647 srcMatPair.push_back(pp);
648 catMatPair.push_back(*r);
650 std::cout <<
" p dist: " << pp.
distance <<
" pa: " << pp.
pa << std::endl;
651 std::cout <<
" r dist: " << (*r).distance <<
" pa: " << (*r).pa << std::endl;
653 if (srcMatPair.size() == fullShapeSize) {
659 bool goodMatch =
false;
660 if (srcMatPair.size() == fullShapeSize) {
662 for (
size_t k = 1; k < catMatPair.size(); k++) {
663 if (catMatPair[0].first != catMatPair[k].first) {
670 if (goodMatch && srcMatPair.size() == fullShapeSize) {
675 srcMat.push_back(srcMatPair[0].first);
676 catMat.push_back(catMatPair[0].first);
677 for (
size_t k = 0; k < srcMatPair.size(); k++) {
678 srcMat.push_back(srcMatPair[k].second);
679 catMat.push_back(catMatPair[k].second);
682 boost::shared_array<double> coeff = polyfit(1, srcMat, catMat);
685 for (
size_t k = 0; k < srcMat.size(); k++) {
686 std::cout <<
"circle(" << srcMat[k].getX() <<
"," 687 << srcMat[k].getY() <<
",10) # color=green" << std::endl;
688 std::cout <<
"circle(" << catMat[k].getX() <<
"," 689 << catMat[k].getY() <<
",10) # color=red" << std::endl;
690 std::cout <<
"line(" << srcMat[0].getX() <<
"," << srcMat[0].getY() <<
"," 691 << srcMat[k].getX() <<
"," << srcMat[k].getY()
692 <<
") # line=0 0 color=green" << std::endl;
693 std::cout <<
"line(" << catMat[0].getX() <<
"," << catMat[0].getY() <<
"," 694 << catMat[k].getX() <<
"," << catMat[k].getY()
695 <<
") # line=0 0 color=red" << std::endl;
703 afw::geom::Angle
const theta(std::acos((a*b+c*d)/
704 (std::sqrt(a*a+c*c)*std::sqrt(b*b+d*d))),
707 std::cout <<
"Linear fit from match:" << std::endl;
708 std::cout << coeff[0] <<
" " << coeff[1] <<
" " << coeff[2] << std::endl;
709 std::cout << coeff[3] <<
" " << coeff[4] <<
" " << coeff[5] << std::endl;
710 std::cout <<
"Determinant (max " << control.
maxDeterminant <<
"): ";
711 std::cout << coeff[1] * coeff[5] - coeff[2] * coeff[4] - 1. << std::endl;
712 std::cout <<
"Angle between axes (deg; allowed 90 +/- ";
714 std::cout << theta.asDegrees() << std::endl;
716 if (std::fabs(coeff[1] * coeff[5] - coeff[2] * coeff[4] - 1.)
722 std::cout <<
"Bad; continuing" << std::endl;
726 double x0, y0, x1, y1;
730 for (
size_t i = 0; i < sourceSubCat.size(); i++) {
731 x0 = sourceSubCat[i].getX();
732 y0 = sourceSubCat[i].getY();
733 transform(1, coeff, x0, y0, &x1, &y1);
734 auto refObjDist = searchNearestPoint(posRefSubCat, x1, y1,
736 if (refObjDist.first != posRefSubCat.end()) {
738 srcMat.push_back(sourceSubCat[i]);
739 catMat.push_back(*refObjDist.first);
741 std::cout <<
"Match: " << x0 <<
"," << y0 <<
" --> " 742 << x1 <<
"," << y1 <<
" <==> " 743 << refObjDist.first->getX() <<
"," << refObjDist.first->getY()
751 std::cout <<
"Insufficient initial matches; continuing" << std::endl;
755 coeff = polyfit(1, srcMat, catMat);
757 std::cout <<
"Coefficients from initial matching:" << std::endl;
758 for (
size_t i = 0; i < 6; ++i) {
759 std::cout << coeff[i] <<
" ";
761 std::cout << std::endl;
764 matPair = FinalVerify(coeff, posRefProxyCat, sourceProxyCat,
767 std::cout <<
"Number of matches: " << matPair.size() <<
" vs " <<
770 if (matPair.size() <=
static_cast<std::size_t
>(control.
minMatchedPairs)) {
772 std::cout <<
"Insufficient final matches; continuing" << std::endl;
774 if (matPair.size() > matPairSave.size()) {
775 matPairSave = matPair;
780 std::cout <<
"Finish" << std::endl;
782 matPairCand.push_back(matPair);
783 if (matPairCand.size() == 3) {
794 if (matPairCand.size() == 0) {
797 size_t nmatch = matPairCand[0].size();
798 afwTable::ReferenceMatchVector matPairRet = matPairCand[0];
799 for (
size_t i = 1; i < matPairCand.size(); i++) {
800 if (matPairCand[i].size() > nmatch) {
801 nmatch = matPairCand[i].size();
802 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