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" 36 struct insertionOrderTag {};
37 typedef 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;
55 inline double absDeltaAngle(
double ang1,
double ang2) {
return std::fmod(
std::fabs(ang1 - ang2),
M_PI * 2); }
57 bool cmpPair(ProxyPair
const &
a, ProxyPair
const &
b) {
return a.
distance > b.
distance; }
64 struct CompareProxyFlux {
92 if (startInd >= a.
size()) {
95 CompareProxyFlux cmp = {key};
99 return ProxyVector(b.begin() + startInd, b.begin() + endInd);
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) {
118 ProxyPair
const &q,
double e,
double dpa,
double e_dpa = 0.02) {
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) {
141 void 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);
158 int ncoeff = (order + 1) * (order + 2) / 2;
163 for (
int i = 0; i <= order; i++) {
164 for (
int k = 0; k <= i; k++) {
173 for (
size_t k = 0; k < img.
size(); k++) {
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);
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) {
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;
306 void addNearestMatch(MultiIndexedProxyPairList &proxyPairList, boost::shared_array<double> coeff,
309 auto x0 = source.
getX();
310 auto y0 = source.
getY();
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;
356 for (
auto sourcePtr = sourceCat.
begin(); sourcePtr != sourceCat.
end(); ++sourcePtr) {
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,
390 std::static_pointer_cast<afwTable::SourceRecord>(proxyPairIter->second.record),
391 proxyPairIter->distance));
404 if (refFluxField.empty()) {
407 if (sourceFluxField.empty()) {
410 if (numBrightStars <= 0) {
413 if (minMatchedPairs < 0) {
416 if (matchingAllowancePix <= 0) {
419 if (maxOffsetPix <= 0) {
422 if (maxRotationDeg <= 0) {
425 if (allowedNonperpDeg <= 0) {
428 if (numPointsForShape <= 0) {
431 if (maxDeterminant <= 0) {
440 for (afwTable::SourceCatalog::const_iterator sourcePtr = sourceCat.begin(); sourcePtr != sourceCat.end();
465 if (posRefCat.
empty()) {
468 if (sourceCat.empty()) {
471 if (posRefBegInd < 0) {
474 if (static_cast<size_t>(posRefBegInd) >= posRefCat.
size()) {
482 for (
auto iter = sourceCat.begin();
iter != sourceCat.end(); ++
iter) {
485 srcCenter /= sourceCat.size();
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++) {
518 posRefPairList.
push_back(ProxyPair(posRefSubCat[i], posRefSubCat[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++) {
528 sourcePairList.push_back(ProxyPair(sourceSubCat[i], sourceSubCat[j]));
531 std::sort(sourcePairList.begin(), sourcePairList.end(), cmpPair);
538 for (
size_t ii = 0; ii < sourcePairList.size(); ii++) {
539 ProxyPair p = sourcePairList[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;
569 ProxyPair pp(p.
first, sourceSubCat[k]);
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();
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];
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
constexpr double asDegrees() const noexcept
Return an Angle's value in degrees.
lsst::geom::Point2D skyToPixel(lsst::geom::SpherePoint const &sky) const
Compute pixel position(s) from sky position(s)
int numPointsForShape
"number of points in a matching shape" ;
lsst::geom::SpherePoint pixelToSky(lsst::geom::Point2D const &pixel) const
Compute sky position(s) from pixel position(s)
afw::table::ReferenceMatchVector matchOptimisticB(afw::table::SimpleCatalog const &posRefCat, afw::table::SourceCatalog const &sourceCat, MatchOptimisticBControl const &control, afw::geom::SkyWcs const &wcs, int posRefBegInd=0, bool verbose=false)
Match sources to stars in a position reference catalog using optimistic pattern matching B...
double matchingAllowancePix
"maximum allowed distance between reference objects and sources (pixels)" ;
std::vector< ReferenceMatch > ReferenceMatchVector
Schema getSchema() const
Return the schema associated with the catalog's table.
Vector3d is a vector in ℝ³ with components stored in double precision.
AngleUnit constexpr radians
constant with units of radians
A class representing an angle.
table::Key< table::Array< std::uint8_t > > wcs
std::vector< RecordProxy > ProxyVector
This file declares a class for representing vectors in ℝ³.
Eigen::Matrix2d getCdMatrix(lsst::geom::Point2D const &pixel) const
Get the 2x2 CD matrix at the specified pixel position.
SchemaItem< T > find(std::string const &name) const
Find a SchemaItem in the Schema by name.
A base class for image defects.
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
iterator end()
Iterator access.
double maxDeterminant
"?" ;
Lightweight representation of a geometric match between two records.
const char * source()
Source function that allows astChannel to source from a Stream.
ProxyVector makeProxies(afw::table::SourceCatalog const &sourceCat, afw::geom::SkyWcs const &distortedWcs, afw::geom::SkyWcs const &tanWcs)
bool empty() const
Return true if the catalog has no records.
double maxOffsetPix
"maximum allowed frame translation (pixels)" ;
table::Key< table::Array< double > > coeff
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
int numBrightStars
"maximum number of bright reference stars to use" ;
Point in an unspecified spherical coordinate system.
std::string refFluxField
"name of flux field in reference catalog" ;
Base::const_iterator const_iterator
A wrapper around a SimpleRecord or SourceRecord that allows us to record a pixel position in a way th...
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Reports invalid arguments.
double maxRotationDeg
"maximum allowed frame rotation (deg)" ;
size_type size() const
Return the number of elements in the catalog.
std::string sourceFluxField
"name of flux field in source catalog" ;
Extent< double, 2 > Extent2D
iterator begin()
Iterator access.
double allowedNonperpDeg
"allowed non-perpendicularity of x and y axes (deg)" ;
int minMatchedPairs
"minimum number of matches" ;
boost::shared_ptr< afw::table::SimpleRecord > record
A FunctorKey used to get or set celestial coordinates from a pair of lsst::geom::Angle keys...