7 #include "boost/scoped_array.hpp"
8 #include "boost/shared_array.hpp"
9 #include "gsl/gsl_linalg.h"
16 namespace pexExcept = lsst::pex::exceptions;
17 namespace afwTable = lsst::afw::table;
20 using namespace lsst::meas::astrom;
31 inline double deltaAngle(
double ang1,
double ang2) {
32 return std::fmod(std::fabs(ang1 - ang2), M_PI*2);
41 struct CompareProxyFlux {
44 double aFlux = a.
record->get(key);
45 double bFlux = b.
record->get(key);
65 CompareProxyFlux cmp = {key};
67 std::sort(b.begin(), b.end(), cmp);
68 std::size_t end =
std::min(start + num, b.size());
69 return ProxyVector(b.begin() + start, b.begin() + end);
72 std::vector<ProxyPair> searchPair(
73 std::vector<ProxyPair>
const &a,
78 std::vector<ProxyPair> v;
80 for (
size_t i = 0; i < a.size(); i++) {
81 double dd = std::fabs(a[i].distance - p.
distance);
82 double dpa = deltaAngle(a[i].pa, p.
pa);
83 if (dd < e && dpa < e_dpa) {
91 std::vector<ProxyPair>::iterator searchPair3(
92 std::vector<ProxyPair> &a,
99 std::vector<ProxyPair>::iterator idx = a.end();
100 double dd_min = 1.E+10;
103 for (
auto i = a.begin(); i != a.end(); ++i) {
104 double dd = std::fabs(i->distance - p.
distance);
107 deltaAngle(p.
pa, i->pa - dpa) < e_dpa &&
109 (i->first == q.
first)) {
115 deltaAngle(p.
pa, i->pa - dpa) < dpa_min) {
116 dpa_min = std::fabs(p.
pa - i->pa - dpa);
127 boost::shared_array<double>
const & coeff,
133 int ncoeff = (order + 1) * (order + 2) / 2;
137 for (
int i = 0; i <= order; i++) {
138 for (
int k = 0;
k <= i;
k++) {
140 *xn += coeff[n] * pow(x, j) * pow(y, k);
141 *yn += coeff[n+ncoeff] * pow(x, j) * pow(y, k);
147 boost::shared_array<double> polyfit(
152 int ncoeff = (order + 1) * (order + 2) / 2;
153 boost::scoped_array<int> xorder(
new int[ncoeff]);
154 boost::scoped_array<int> yorder(
new int[ncoeff]);
157 for (
int i = 0; i <= order; i++) {
158 for (
int k = 0;
k <= i;
k++) {
166 boost::scoped_array<int> flag(
new int[img.size()]);
167 for (
size_t k = 0;
k < img.size();
k++) {
171 boost::scoped_array<double> a_data(
new double[ncoeff*ncoeff]);
172 boost::scoped_array<double> b_data(
new double[ncoeff]);
173 boost::scoped_array<double> c_data(
new double[ncoeff]);
175 boost::shared_array<double> coeff(
new double[ncoeff*2]);
177 for (
int loop = 0; loop < 1; loop++) {
178 for (
int i = 0; i < ncoeff; i++) {
179 for (
int j = 0; j < ncoeff; j++) {
180 a_data[i*ncoeff+j] = 0.0;
181 for (
size_t k = 0;
k < img.size();
k++) {
183 a_data[i*ncoeff+j] += pow(img[
k].getX(), xorder[i]) *
184 pow(img[
k].getY(), yorder[i]) *
185 pow(img[
k].getX(), xorder[j]) *
186 pow(img[
k].getY(), yorder[j]);
190 b_data[i] = c_data[i] = 0.0;
191 for (
unsigned int k = 0;
k < img.size();
k++) {
193 b_data[i] += pow(img[
k].getX(), xorder[i]) *
194 pow(img[
k].getY(), yorder[i]) *
196 c_data[i] += pow(img[
k].getX(), xorder[i]) *
197 pow(img[
k].getY(), yorder[i]) *
203 gsl_matrix_view a = gsl_matrix_view_array(a_data.get(), ncoeff, ncoeff);
204 gsl_vector_view b = gsl_vector_view_array(b_data.get(), ncoeff);
205 gsl_vector_view c = gsl_vector_view_array(c_data.get(), ncoeff);
207 boost::shared_ptr<gsl_vector>
x(gsl_vector_alloc(ncoeff), gsl_vector_free);
208 boost::shared_ptr<gsl_vector>
y(gsl_vector_alloc(ncoeff), gsl_vector_free);
212 boost::shared_ptr<gsl_permutation> p(gsl_permutation_alloc(ncoeff), gsl_permutation_free);
214 gsl_linalg_LU_decomp(&a.matrix, p.get(), &s);
215 gsl_linalg_LU_solve(&a.matrix, p.get(), &b.vector, x.get());
216 gsl_linalg_LU_solve(&a.matrix, p.get(), &c.vector, y.get());
218 for (
int i = 0; i < ncoeff; i++) {
219 coeff[i] = x->data[i];
220 coeff[i+ncoeff] = y->data[i];
223 double S, Sx, Sy, Sxx, Syy;
224 S = Sx = Sy = Sxx = Syy = 0.0;
225 for (
size_t k = 0;
k < img.size();
k++) {
227 double x0 = img[
k].getX();
228 double y0 = img[
k].getY();
230 transform(order, coeff, x0, y0, &x1, &y1);
232 Sx += (x1 - posRefCat[
k].getX());
233 Sxx += (x1 - posRefCat[
k].getX()) * (x1 - posRefCat[
k].getX());
234 Sy += (y1 - posRefCat[
k].getY());
235 Syy += (y1 - posRefCat[
k].getY()) * (y1 - posRefCat[
k].getY());
238 double x_sig = std::sqrt((Sxx - Sx * Sx / S) / S);
239 double y_sig = std::sqrt((Syy - Sy * Sy / S) / S);
242 for (
size_t k = 0;
k < img.size();
k++) {
243 double x0 = img[
k].getX();
244 double y0 = img[
k].getY();
246 transform(order, coeff, x0, y0, &x1, &y1);
247 if (std::fabs(x1-posRefCat[
k].getX()) > 2. * x_sig ||
248 std::fabs(y1-posRefCat[
k].getY()) > 2. * y_sig) {
258 ProxyVector::const_iterator searchNearestPoint(
264 ProxyVector::const_iterator foundPtr = posRefCat.end();
269 for (ProxyVector::const_iterator posRefPtr = posRefCat.begin();
270 posRefPtr != posRefCat.end(); ++posRefPtr) {
271 d = std::hypot(posRefPtr->getX()-
x, posRefPtr->getY()-
y);
273 foundPtr = posRefPtr;
283 boost::shared_array<double> coeff,
286 double matchingAllowancePix,
293 double x0,
y0, x1, y1;
294 int num = 0, num_prev = -1;
295 for (ProxyVector::const_iterator sourcePtr = sourceCat.begin(); sourcePtr != sourceCat.end();
297 x0 = sourcePtr->getX();
298 y0 = sourcePtr->getY();
299 transform(1, coeff, x0, y0, &x1, &y1);
300 auto p = searchNearestPoint(posRefCat, x1, y1, matchingAllowancePix);
301 if (p != posRefCat.end()) {
303 srcMat.push_back(*sourcePtr);
304 catMat.push_back(*p);
311 coeff = polyfit(order, srcMat, catMat);
313 for (
int j = 0; j < 100; j++) {
318 for (ProxyVector::const_iterator sourcePtr = sourceCat.begin();
319 sourcePtr != sourceCat.end(); ++sourcePtr) {
320 x0 = sourcePtr->getX();
321 y0 = sourcePtr->getY();
322 transform(order, coeff, x0, y0, &x1, &y1);
323 auto p = searchNearestPoint(posRefCat, x1, y1, matchingAllowancePix);
324 if (p != posRefCat.end()) {
326 srcMat.push_back(*sourcePtr);
327 catMat.push_back(*p);
329 *p, boost::static_pointer_cast<afwTable::SourceRecord>(sourcePtr->record), 0.0));
333 if (num == num_prev)
break;
335 coeff = polyfit(order, srcMat, catMat);
346 for (
unsigned int i = 0; i < srcMat.size(); i++) {
349 catMat[i], boost::static_pointer_cast<afwTable::SourceRecord>(srcMat[i].record), 0.0)
363 void MatchOptimisticBControl::validate()
const {
364 if (refFluxField.empty()) {
365 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"refFluxField must be specified");
367 if (sourceFluxField.empty()) {
368 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"sourceFluxField must be specified");
370 if (numBrightStars <= 0) {
371 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numBrightStars must be positive");
373 if (minMatchedPairs < 0) {
374 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"minMatchedPairs must not be negative");
376 if (matchingAllowancePix <= 0) {
377 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"matchingAllowancePix must be positive");
379 if (maxOffsetPix <= 0) {
380 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxOffsetPix must be positive");
382 if (maxRotationRad <= 0) {
383 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxRotationRad must be positive");
385 if (angleDiffFrom90 <= 0) {
386 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"angleDiffFrom90 must be positive");
388 if (numPointsForShape <= 0) {
389 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numPointsForShape must be positive");
391 if (maxDeterminant <= 0) {
392 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxDeterminant must be positive");
398 r.reserve(sourceCat.
size());
400 sourcePtr != sourceCat.
end(); ++sourcePtr) {
401 r.push_back(
RecordProxy(sourcePtr, sourcePtr->getCentroid()));
408 auto hasCentroidKey = posRefCat.
getSchema().
find<afwTable::Flag>(
"hasCentroid").key;
410 r.reserve(posRefCat.
size());
412 posRefPtr != posRefCat.
end(); ++posRefPtr) {
413 if (!posRefPtr->get(hasCentroidKey)) {
414 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"unknown centroid");
416 r.push_back(
RecordProxy(posRefPtr, posRefPtr->get(centroidKey)));
442 sourceSubCat.size()+25,
445 std::cout <<
"Catalog sizes: " << sourceSubCat.size() <<
" " << posRefSubCat.size() << std::endl;
449 std::vector<ProxyPair> posRefPairList;
450 size_t const posRefCatSubSize = posRefSubCat.size();
451 for (
size_t i = 0; i < posRefCatSubSize-1; i++) {
452 for (
size_t j = i+1; j < posRefCatSubSize; j++) {
453 posRefPairList.push_back(
ProxyPair(posRefSubCat[i], posRefSubCat[j]));
458 std::sort(posRefPairList.begin(), posRefPairList.end(), cmpPair);
461 std::vector<ProxyPair> sourcePairList;
462 size_t const sourceSubCatSize = sourceSubCat.size();
463 for (
size_t i = 0; i < sourceSubCatSize-1; i++) {
464 for (
size_t j = i+1; j < sourceSubCatSize; j++) {
465 sourcePairList.push_back(
ProxyPair(sourceSubCat[i], sourceSubCat[j]));
468 std::sort(sourcePairList.begin(), sourcePairList.end(), cmpPair);
472 std::vector<afwTable::ReferenceMatchVector> matPairCand;
475 for (
size_t ii = 0; ii < sourcePairList.size(); ii++) {
478 std::vector<ProxyPair> q = searchPair(posRefPairList, p,
484 std::vector<ProxyPair> srcMatPair;
485 std::vector<ProxyPair> catMatPair;
488 for (
size_t l = 0; l < q.size(); l++) {
490 double dpa = p.
pa - q[l].pa;
495 srcMatPair.push_back(p);
496 catMatPair.push_back(q[l]);
499 std::cout <<
"p dist: " << p.
distance <<
" pa: " << p.
pa << std::endl;
500 std::cout <<
"q dist: " << q[l].distance <<
" pa: " << q[l].pa << std::endl;
503 for (
size_t k = 0;
k < sourceSubCat.size();
k++) {
504 if (p.
first == sourceSubCat[
k] || p.
second == sourceSubCat[
k])
continue;
508 std::vector<ProxyPair>::iterator r = searchPair3(posRefPairList, pp, q[l],
510 if (r != posRefPairList.end()) {
511 srcMatPair.push_back(pp);
512 catMatPair.push_back(*r);
514 std::cout <<
" p dist: " << pp.
distance <<
" pa: " << pp.
pa << std::endl;
515 std::cout <<
" r dist: " << (*r).distance <<
" pa: " << (*r).pa << std::endl;
517 if (srcMatPair.size() == fullShapeSize) {
523 bool goodMatch =
false;
524 if (srcMatPair.size() == fullShapeSize) {
526 for (
size_t k = 1;
k < catMatPair.size();
k++) {
527 if (catMatPair[0].first != catMatPair[
k].first) {
533 if (goodMatch && srcMatPair.size() == fullShapeSize) {
538 srcMat.push_back(srcMatPair[0].first);
539 catMat.push_back(catMatPair[0].first);
540 for (
size_t k = 0;
k < srcMatPair.size();
k++) {
541 srcMat.push_back(srcMatPair[
k].second);
542 catMat.push_back(catMatPair[
k].second);
545 boost::shared_array<double> coeff = polyfit(1, srcMat, catMat);
548 for (
size_t k = 0;
k < srcMat.size();
k++) {
549 std::cout <<
"circle(" << srcMat[
k].getX() <<
","
550 << srcMat[
k].getY() <<
",10) # color=green" << std::endl;
551 std::cout <<
"circle(" << catMat[
k].getX() <<
","
552 << catMat[
k].getY() <<
",10) # color=red" << std::endl;
553 std::cout <<
"line(" << srcMat[0].getX() <<
"," << srcMat[0].getY() <<
","
554 << srcMat[
k].getX() <<
"," << srcMat[
k].getY()
555 <<
") # line=0 0 color=green" << std::endl;
556 std::cout <<
"line(" << catMat[0].getX() <<
"," << catMat[0].getY() <<
","
557 << catMat[
k].getX() <<
"," << catMat[
k].getY()
558 <<
") # line=0 0 color=red" << std::endl;
566 double theta = std::acos((a*b+c*d)/(std::sqrt(a*a+c*c)*std::sqrt(b*b+d*d))) /
569 std::cout <<
"Linear fit from match:" << std::endl;
570 std::cout << coeff[0] <<
" " << coeff[1] <<
" " << coeff[2] << std::endl;
571 std::cout << coeff[3] <<
" " << coeff[4] <<
" " << coeff[5] << std::endl;
572 std::cout << coeff[1] * coeff[5] - coeff[2] * coeff[4] - 1. << std::endl;
573 std::cout << theta << std::endl;
575 if (std::fabs(coeff[1] * coeff[5] - coeff[2] * coeff[4] - 1.) > control.
maxDeterminant ||
580 std::cout <<
"Bad; continuing" << std::endl;
584 double x0,
y0, x1, y1;
588 for (
size_t i = 0; i < sourceSubCat.size(); i++) {
589 x0 = sourceSubCat[i].getX();
590 y0 = sourceSubCat[i].getY();
591 transform(1, coeff, x0, y0, &x1, &y1);
592 auto p = searchNearestPoint(posRefSubCat, x1, y1,
594 if (p != posRefSubCat.end()) {
596 srcMat.push_back(sourceSubCat[i]);
597 catMat.push_back(*p);
599 std::cout <<
"Match: " << x0 <<
"," << y0 <<
" --> "
600 << x1 <<
"," << y1 <<
601 " <==> " << p->getX() <<
"," << p->getY() << std::endl;
608 std::cout <<
"Insufficient initial matches; continuing" << std::endl;
612 coeff = polyfit(1, srcMat, catMat);
614 std::cout <<
"Coefficients from initial matching:" << std::endl;
615 for (
size_t i = 0; i < 6; ++i) {
616 std::cout << coeff[i] <<
" ";
618 std::cout << std::endl;
621 matPair = FinalVerify(coeff, posRefProxyCat, sourceProxyCat,
624 std::cout <<
"Number of matches: " << matPair.size() <<
" vs " <<
629 std::cout <<
"Insufficient final matches; continuing" << std::endl;
631 if (matPair.size() > matPairSave.size()) {
632 matPairSave = matPair;
637 std::cout <<
"Finish" << std::endl;
639 matPairCand.push_back(matPair);
640 if (matPairCand.size() == 3) {
651 if (matPairCand.size() == 0) {
654 size_t nmatch = matPairCand[0].size();
656 for (
size_t i = 1; i < matPairCand.size(); i++) {
657 if (matPairCand[i].size() > nmatch) {
658 nmatch = matPairCand[i].size();
659 matPairRet = matPairCand[i];
int numPointsForShape
"number of points in a matching shape" ;
double angleDiffFrom90
"? (deg)" ;
double matchingAllowancePix
"?" ;
std::vector< ReferenceMatch > ReferenceMatchVector
std::vector< RecordProxy > ProxyVector
afwTable::ReferenceMatchVector matchOptimisticB(afwTable::SimpleCatalog const &posRefCat, afwTable::SourceCatalog const &sourceCat, MatchOptimisticBControl const &control, int posRefBegInd, bool verbose)
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
ProxyVector makeProxies(afwTable::SourceCatalog const &sourceCat)
Schema getSchema() const
Return the schema associated with the catalog's table.
double maxDeterminant
"?" ;
Lightweight representation of a geometric match between two records.
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
double maxOffsetPix
"maximum translation allowed (pixels)" ;
#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 maxRotationRad
"maximum allowed rotation (rad)" ;
std::string sourceFluxField
"name of flux field in source catalog" ;
Base::const_iterator const_iterator
Tag types used to declare specialized field types.
Include files required for standard LSST Exception handling.
int minMatchedPairs
"minimum number of matches" ;
boost::shared_ptr< lsst::afw::table::SimpleRecord > record
size_type size() const
Return the number of elements in the catalog.