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];
Extent< double, 3 > Extent3D
double allowedNonperpDeg
"allowed non-perpendicularity of x and y axes (deg)" ;
lsst::afw::geom::Point2D getPosition(lsst::afw::geom::AngleUnit unit=lsst::afw::geom::degrees) const
Return our contents in a Point2D object.
virtual bool hasDistortion() const
std::string sourceFluxField
"name of flux field in source catalog" ;
Include files required for standard LSST Exception handling.
geom::Point2D skyToPixel(geom::Angle sky1, geom::Angle sky2) const
Convert from sky coordinates (e.g. RA/dec) to pixel positions.
Point< double, 2 > Point2D
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)
boost::shared_ptr< lsst::afw::table::SimpleRecord > record
size_type size() const
Return the number of elements in the catalog.
double maxRotationDeg
"maximum allowed frame rotation (deg)" ;
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)
A coordinate class intended to represent absolute positions.
int numBrightStars
"maximum number of bright reference stars to use" ;
double maxDeterminant
"?" ;
double maxOffsetPix
"maximum allowed frame translation (pixels)" ;
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
double matchingAllowancePix
"maximum allowed distance between reference objects and sources (pixels)" ;
lsst::afw::coord::IcrsCoord IcrsCoord
AngleUnit const radians
constant with units of radians
Lightweight representation of a geometric match between two records.
Eigen::Matrix2d getCDMatrix() const
Returns the CD matrix.
int minMatchedPairs
"minimum number of matches" ;
std::string refFluxField
"name of flux field in reference catalog" ;
double degToRad(long double angleInDegrees)
#define LSST_EXCEPT(type,...)
std::vector< RecordProxy > ProxyVector
Base::const_iterator const_iterator
afw::table::Key< double > b
Extent< double, 2 > Extent2D
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
ProxyVector makeProxies(lsst::afw::table::SourceCatalog const &sourceCat, afw::image::Wcs const &distortedWcs, afw::image::Wcs const &tanWcs)
int numPointsForShape
"number of points in a matching shape" ;
Schema getSchema() const
Return the schema associated with the catalog's table.
boost::int64_t RecordId
Type used for unique IDs for records.
A FunctorKey used to get or set celestial coordiantes from a pair of Angle keys.