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"
30using namespace lsst::meas::astrom;
36struct insertionOrderTag {};
37typedef boost::multi_index_container<
39 boost::multi_index::indexed_by<
40 boost::multi_index::sequenced<boost::multi_index::tag<insertionOrderTag> >,
41 boost::multi_index::ordered_unique<
42 boost::multi_index::tag<refIdTag>,
43 boost::multi_index::global_fun<ProxyPair const &, afwTable::RecordId, &getRefId> > > >
44 MultiIndexedProxyPairList;
55inline double absDeltaAngle(
double ang1,
double ang2) {
return std::fmod(
std::fabs(ang1 - ang2),
M_PI * 2); }
57bool cmpPair(ProxyPair
const &a, ProxyPair
const &b) {
return a.
distance >
b.distance; }
64struct CompareProxyFlux {
65 bool operator()(RecordProxy
const &a, RecordProxy
const &b)
const {
66 double aFlux = a.
record->get(key);
67 double bFlux =
b.record->get(key);
77 afwTable::Key<double> key;
92 if (startInd >= a.
size()) {
93 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"startInd too big");
95 CompareProxyFlux cmp = {key};
102std::vector<ProxyPair> searchPair(std::vector<ProxyPair>
const &a, ProxyPair
const &p,
double e,
104 std::vector<ProxyPair> v;
106 for (
size_t i = 0; i < a.
size(); i++) {
108 double dpa = absDeltaAngle(a[i].pa, p.
pa);
109 if (dd < e && dpa < e_dpa) {
117std::vector<ProxyPair>::iterator searchPair3(std::vector<ProxyPair> &a, ProxyPair
const &p,
118 ProxyPair
const &q,
double e,
double dpa,
double e_dpa = 0.02) {
119 std::vector<ProxyPair>::iterator idx = a.
end();
120 double dd_min = 1.E+10;
123 for (
auto i = a.
begin(); i != a.
end(); ++i) {
126 if (dd < e && absDeltaAngle(p.
pa, i->pa - dpa) < e_dpa && dd < dd_min && (i->first == q.
first)) {
131 if (dd < e && absDeltaAngle(p.
pa, i->pa - dpa) < dpa_min) {
141void transform(
int order, boost::shared_array<double>
const &coeff,
double x,
double y,
double *xn,
143 int ncoeff = (
order + 1) * (order + 2) / 2;
147 for (
int i = 0; i <=
order; i++) {
148 for (
int k = 0; k <= i; k++) {
150 *xn += coeff[n] *
pow(x, j) *
pow(y, k);
151 *yn += coeff[n + ncoeff] *
pow(x, j) *
pow(y, k);
158 int ncoeff = (
order + 1) * (order + 2) / 2;
159 std::unique_ptr<int[]> xorder(
new int[ncoeff]);
160 std::unique_ptr<int[]> yorder(
new int[ncoeff]);
163 for (
int i = 0; i <=
order; i++) {
164 for (
int k = 0; k <= i; k++) {
172 std::unique_ptr<int[]> flag(
new int[img.
size()]);
173 for (
size_t k = 0; k < img.
size(); k++) {
177 std::unique_ptr<double[]> a_data(
new double[ncoeff * ncoeff]);
178 std::unique_ptr<double[]> b_data(
new double[ncoeff]);
179 std::unique_ptr<double[]> c_data(
new double[ncoeff]);
181 boost::shared_array<double> coeff(
new double[ncoeff * 2]);
183 for (
int loop = 0; loop < 1; loop++) {
184 for (
int i = 0; i < ncoeff; i++) {
185 for (
int j = 0; j < ncoeff; j++) {
186 a_data[i * ncoeff + j] = 0.0;
187 for (
size_t k = 0; k < img.
size(); k++) {
189 a_data[i * ncoeff + j] +=
190 pow(img[k].getX(), xorder[i]) *
pow(img[k].getY(), yorder[i]) *
191 pow(img[k].getX(), xorder[j]) *
pow(img[k].getY(), yorder[j]);
195 b_data[i] = c_data[i] = 0.0;
196 for (
unsigned int k = 0; k < img.
size(); k++) {
198 b_data[i] +=
pow(img[k].getX(), xorder[i]) *
pow(img[k].getY(), yorder[i]) *
200 c_data[i] +=
pow(img[k].getX(), xorder[i]) *
pow(img[k].getY(), yorder[i]) *
206 gsl_matrix_view a = gsl_matrix_view_array(a_data.get(), ncoeff, ncoeff);
207 gsl_vector_view
b = gsl_vector_view_array(b_data.get(), ncoeff);
208 gsl_vector_view c = gsl_vector_view_array(c_data.get(), ncoeff);
210 std::shared_ptr<gsl_vector>
x(gsl_vector_alloc(ncoeff), gsl_vector_free);
211 std::shared_ptr<gsl_vector>
y(gsl_vector_alloc(ncoeff), gsl_vector_free);
215 std::shared_ptr<gsl_permutation> p(gsl_permutation_alloc(ncoeff), gsl_permutation_free);
217 gsl_linalg_LU_decomp(&a.matrix, p.get(), &s);
218 gsl_linalg_LU_solve(&a.matrix, p.get(), &
b.vector,
x.get());
219 gsl_linalg_LU_solve(&a.matrix, p.get(), &c.vector,
y.get());
221 for (
int i = 0; i < ncoeff; i++) {
222 coeff[i] =
x->data[i];
223 coeff[i + ncoeff] =
y->data[i];
226 double S, Sx, Sy, Sxx, Syy;
227 S = Sx = Sy = Sxx = Syy = 0.0;
228 for (
size_t k = 0; k < img.
size(); k++) {
230 double x0 = img[k].getX();
231 double y0 = img[k].getY();
233 transform(order, coeff, x0, y0, &x1, &y1);
235 Sx += (x1 - posRefCat[k].getX());
236 Sxx += (x1 - posRefCat[k].getX()) * (x1 - posRefCat[k].getX());
237 Sy += (y1 - posRefCat[k].getY());
238 Syy += (y1 - posRefCat[k].getY()) * (y1 - posRefCat[k].getY());
241 double x_sig =
std::sqrt((Sxx - Sx * Sx / S) / S);
242 double y_sig =
std::sqrt((Syy - Sy * Sy / S) / S);
245 for (
size_t k = 0; k < img.
size(); k++) {
246 double x0 = img[k].getX();
247 double y0 = img[k].getY();
249 transform(order, coeff, x0, y0, &x1, &y1);
250 if (
std::fabs(x1 - posRefCat[k].getX()) > 2. * x_sig ||
251 std::fabs(y1 - posRefCat[k].getY()) > 2. * y_sig) {
272std::pair<ProxyVector::const_iterator, double> searchNearestPoint(
ProxyVector const &posRefCat,
double x,
273 double y,
double matchingAllowancePix) {
274 auto minDistSq = matchingAllowancePix * matchingAllowancePix;
275 auto foundPtr = posRefCat.
end();
276 for (
auto posRefPtr = posRefCat.
begin(); posRefPtr != posRefCat.
end(); ++posRefPtr) {
277 auto const dx = posRefPtr->getX() -
x;
278 auto const dy = posRefPtr->getY() -
y;
279 auto const distSq = dx * dx + dy * dy;
280 if (distSq < minDistSq) {
281 foundPtr = posRefPtr;
306void addNearestMatch(MultiIndexedProxyPairList &proxyPairList, boost::shared_array<double> coeff,
307 ProxyVector const &posRefCat, RecordProxy
const &source,
double matchingAllowancePix) {
312 auto refObjDist = searchNearestPoint(posRefCat, x1, y1, matchingAllowancePix);
313 if (refObjDist.first == posRefCat.
end()) {
317 auto existingMatch = proxyPairList.get<refIdTag>().
find(refObjDist.first->record->getId());
318 if (existingMatch == proxyPairList.get<refIdTag>().end()) {
320 auto proxyPair = ProxyPair(*refObjDist.first, source);
321 proxyPairList.get<refIdTag>().insert(proxyPair);
329 if (existingMatch->distance <= refObjDist.second) {
334 proxyPairList.get<refIdTag>().
erase(existingMatch);
335 auto proxyPair = ProxyPair(*refObjDist.first, source);
336 proxyPairList.get<refIdTag>().insert(proxyPair);
352 ProxyVector const &sourceCat,
double matchingAllowancePix,
354 MultiIndexedProxyPairList proxyPairList;
357 addNearestMatch(proxyPairList, coeff, posRefCat, *sourcePtr, matchingAllowancePix);
360 if (proxyPairList.size() > 5) {
361 for (
auto j = 0; j < 100; j++) {
362 auto prevNumMatches = proxyPairList.size();
365 srcMat.reserve(proxyPairList.size());
366 catMat.reserve(proxyPairList.size());
367 for (
auto matchPtr = proxyPairList.get<refIdTag>().begin();
368 matchPtr != proxyPairList.get<refIdTag>().end(); ++matchPtr) {
369 catMat.push_back(matchPtr->first);
370 srcMat.push_back(matchPtr->second);
372 coeff = polyfit(order, srcMat, catMat);
373 proxyPairList.clear();
375 for (ProxyVector::const_iterator sourcePtr =
sourceCat.begin(); sourcePtr !=
sourceCat.end();
377 addNearestMatch(proxyPairList, coeff, posRefCat, *sourcePtr, matchingAllowancePix);
379 if (proxyPairList.size() == prevNumMatches) {
385 matPair.reserve(proxyPairList.size());
386 for (
auto proxyPairIter = proxyPairList.get<insertionOrderTag>().begin();
387 proxyPairIter != proxyPairList.get<insertionOrderTag>().end(); ++proxyPairIter) {
389 proxyPairIter->first.record,
391 proxyPairIter->distance));
405 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"refFluxField must be specified");
408 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"sourceFluxField must be specified");
411 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numBrightStars must be positive");
414 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"minMatchedPairs must not be negative");
417 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"matchingAllowancePix must be positive");
420 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxOffsetPix must be positive");
423 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxRotationRad must be positive");
426 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"allowedNonperpDeg must be positive");
429 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"numPointsForShape must be positive");
432 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"maxDeterminant must be positive");
439 r.reserve(sourceCat.size());
451 r.reserve(posRefCat.
size());
465 if (posRefCat.
empty()) {
466 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"no entries in posRefCat");
468 if (sourceCat.empty()) {
469 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"no entries in sourceCat");
471 if (posRefBegInd < 0) {
472 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"posRefBegInd < 0");
474 if (
static_cast<size_t>(posRefBegInd) >= posRefCat.
size()) {
475 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
"posRefBegInd too big");
482 for (
auto iter = sourceCat.begin(); iter != sourceCat.end(); ++iter) {
485 srcCenter /= sourceCat.size();
488 for (
auto iter = posRefCat.
begin(); iter != posRefCat.
end(); ++iter) {
489 refCenter += iter->getCoord().getVector();
491 refCenter /= posRefCat.
size();
501 selectPoint(sourceProxyCat, sourceCat.getSchema().find<
double>(control.
sourceFluxField).key,
508 sourceSubCat.
size() + 25, posRefBegInd);
515 size_t const posRefCatSubSize = posRefSubCat.
size();
516 for (
size_t i = 0; i < posRefCatSubSize - 1; i++) {
517 for (
size_t j = i + 1; j < posRefCatSubSize; j++) {
525 size_t const sourceSubCatSize = sourceSubCat.
size();
526 for (
size_t i = 0; i < sourceSubCatSize - 1; i++) {
527 for (
size_t j = i + 1; j < sourceSubCatSize; j++) {
538 for (
size_t ii = 0; ii < sourcePairList.
size(); ii++) {
550 for (
size_t l = 0; l < q.
size(); l++) {
553 double dpa = p.
pa - q[l].pa;
566 for (
size_t k = 0; k < sourceSubCat.
size(); k++) {
567 if (p.
first == sourceSubCat[k] || p.
second == sourceSubCat[k])
continue;
571 std::vector<ProxyPair>::iterator r = searchPair3(
573 if (r != posRefPairList.
end()) {
580 if (srcMatPair.
size() == fullShapeSize) {
586 bool goodMatch =
false;
587 if (srcMatPair.
size() == fullShapeSize) {
589 for (
size_t k = 1; k < catMatPair.
size(); k++) {
590 if (catMatPair[0].first != catMatPair[k].first) {
597 if (goodMatch && srcMatPair.
size() == fullShapeSize) {
603 for (
size_t k = 0; k < srcMatPair.
size(); k++) {
608 boost::shared_array<double> coeff = polyfit(1, srcMat, catMat);
611 for (
size_t k = 0; k < srcMat.
size(); k++) {
612 std::cout <<
"circle(" << srcMat[k].getX() <<
"," << srcMat[k].getY()
614 std::cout <<
"circle(" << catMat[k].getX() <<
"," << catMat[k].getY()
616 std::cout <<
"line(" << srcMat[0].getX() <<
"," << srcMat[0].getY() <<
","
617 << srcMat[k].getX() <<
"," << srcMat[k].getY()
618 <<
") # line=0 0 color=green" <<
std::endl;
619 std::cout <<
"line(" << catMat[0].getX() <<
"," << catMat[0].getY() <<
","
620 << catMat[k].getX() <<
"," << catMat[k].getY()
621 <<
") # line=0 0 color=red" <<
std::endl;
638 std::cout <<
"Angle between axes (deg; allowed 90 +/- ";
651 double x0, y0, x1, y1;
655 for (
size_t i = 0; i < sourceSubCat.
size(); i++) {
656 x0 = sourceSubCat[i].getX();
657 y0 = sourceSubCat[i].getY();
658 transform(1, coeff, x0, y0, &x1, &y1);
661 if (refObjDist.first != posRefSubCat.
end()) {
666 std::cout <<
"Match: " << x0 <<
"," << y0 <<
" --> " << x1 <<
"," << y1
667 <<
" <==> " << refObjDist.first->getX() <<
","
668 << refObjDist.first->getY() <<
std::endl;
679 coeff = polyfit(1, srcMat, catMat);
682 for (
size_t i = 0; i < 6; ++i) {
688 matPair = FinalVerify(coeff, posRefProxyCat, sourceProxyCat,
691 std::cout <<
"Number of matches: " << matPair.
size() <<
" vs "
698 if (matPair.
size() > matPairSave.
size()) {
699 matPairSave = matPair;
707 if (matPairCand.
size() == 3) {
718 if (matPairCand.
size() == 0) {
721 size_t nmatch = matPairCand[0].
size();
723 for (
size_t i = 1; i < matPairCand.
size(); i++) {
724 if (matPairCand[i].size() > nmatch) {
725 nmatch = matPairCand[i].
size();
726 matPairRet = matPairCand[i];
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
This file declares a class for representing vectors in ℝ³.
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
lsst::geom::SpherePoint pixelToSky(lsst::geom::Point2D const &pixel) const
Compute sky position(s) from pixel position(s)
lsst::geom::Point2D skyToPixel(lsst::geom::SpherePoint const &sky) const
Compute pixel position(s) from sky position(s)
size_type size() const
Return the number of elements in the catalog.
bool empty() const
Return true if the catalog has no records.
iterator begin()
Iterator access.
Schema getSchema() const
Return the schema associated with the catalog's table.
A FunctorKey used to get or set celestial coordinates from a pair of lsst::geom::Angle keys.
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
typename Base::const_iterator const_iterator
A class representing an angle.
constexpr double asDegrees() const noexcept
Return an Angle's value in degrees.
Point in an unspecified spherical coordinate system.
Vector3d is a vector in ℝ³ with components stored in double precision.
const char * source()
Source function that allows astChannel to source from a Stream.
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
std::vector< ReferenceMatch > ReferenceMatchVector
SortedCatalogT< SimpleRecord > SimpleCatalog
Match< SimpleRecord, SourceRecord > ReferenceMatch
std::int64_t RecordId
Type used for unique IDs for records.
Extent< double, 2 > Extent2D
AngleUnit constexpr radians
constant with units of radians
constexpr double degToRad(double x) noexcept
Point< double, 2 > Point2D
std::vector< RecordProxy > ProxyVector
ProxyVector makeProxies(afw::table::SourceCatalog const &sourceCat, afw::geom::SkyWcs const &distortedWcs, afw::geom::SkyWcs const &tanWcs)
decltype(sizeof(void *)) size_t
T static_pointer_cast(T... args)
std::string refFluxField
"name of flux field in reference catalog" ;
double maxRotationDeg
"maximum allowed frame rotation (deg)" ;
double matchingAllowancePix
"maximum allowed distance between reference objects and sources (pixels)" ;
int numPointsForShape
"number of points in a matching shape" ;
double maxDeterminant
"?" ;
int minMatchedPairs
"minimum number of matches" ;
int numBrightStars
"maximum number of bright reference stars to use" ;
std::string sourceFluxField
"name of flux field in source catalog" ;
double maxOffsetPix
"maximum allowed frame translation (pixels)" ;
double allowedNonperpDeg
"allowed non-perpendicularity of x and y axes (deg)" ;
A wrapper around a SimpleRecord or SourceRecord that allows us to record a pixel position in a way th...
std::shared_ptr< afw::table::SimpleRecord > record