7 #define M_PI 3.14159265358979323846 10 #include "lsst/log/Log.h" 20 LOG_LOGGER _log = LOG_GET(
"jointcal.ListMatch");
28 static double sqr(
double x) {
return x * x; }
35 std::shared_ptr<const BaseStar>
s1,
s2;
39 Segment(std::shared_ptr<const BaseStar> star1, std::shared_ptr<const BaseStar> star2,
const int star1Rank,
42 s1 = std::move(star1);
43 s2 = std::move(star2);
48 r = sqrt(dx * dx + dy * dy);
53 return atan2(other->
dx * dy - dx * other->
dy, dx * other->
dx + dy * other->
dy);
57 stream <<
" dx " << segment.
dx <<
" dy " << segment.
dy <<
" r " << segment.
r << std::endl;
71 static bool DecreasingLength(
const Segment &first,
const Segment &second) {
return (first.
r > second.
r); }
77 siStop = list.begin();
78 int limit = std::min(nStars,
int(list.size())) - 1;
79 for (
int count = 0; count < limit; count++) ++siStop;
83 for (
auto si1 = list.begin(); si1 != siStop; ++si1, rank++)
84 for (
auto si2 = siStop; si2 != si1; --si2) {
85 push_back(
Segment(*si1, *si2, rank, gtransfo));
87 this->sort(DecreasingLength);
100 static std::unique_ptr<StarMatchList> MatchListExtract(
const SegmentPairList &pairList,
int rank1,
int rank2,
107 for (SegmentPairListCIterator spi = pairList.begin(); spi != pairList.end(); spi++) {
109 if (a_pair.first->s1rank != rank1 || a_pair.second->s1rank != rank2)
continue;
113 if (matchList->size() == 0)
114 matchList->push_back(
StarMatch(gtransfo.
apply(*(a_pair.first->s1)), *(a_pair.second->s1),
115 a_pair.first->s1, a_pair.second->s1));
117 matchList->push_back(
StarMatch(gtransfo.
apply(*(a_pair.first->s2)), *(a_pair.second->s2),
118 a_pair.first->s2, a_pair.second->s2));
123 static bool DecreasingQuality(
const std::unique_ptr<StarMatchList> &first,
124 const std::unique_ptr<StarMatchList> &second) {
125 int idiff = first->size() - second->size();
129 return (first->getDist2() < second->getDist2());
134 using SolList = std::list<std::unique_ptr<StarMatchList>>;
151 int nBinsAngle = 180;
152 double angleOffset =
M_PI / nBinsAngle;
155 Histo2d histo(nBinsR, minRatio, maxRatio, nBinsAngle, -
M_PI - angleOffset,
M_PI - angleOffset);
157 SegmentIterator segi1, segi2;
160 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
162 if (seg1->
r == 0)
continue;
163 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
170 ratio = seg2->
r / seg1->
r;
171 if (ratio > maxRatio)
continue;
172 if (ratio < minRatio)
break;
174 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
175 histo.
fill(ratio, angle);
189 double ratioMax, angleMax;
190 double maxContent = histo.
maxBin(ratioMax, angleMax);
191 histo.
fill(ratioMax, angleMax, -maxContent);
194 LOGLS_DEBUG(_log,
" valMax " << maxContent <<
" ratio " << ratioMax <<
" angle " << angleMax);
196 minRatio = ratioMax - binr / 2;
197 maxRatio = ratioMax + binr / 2;
198 double minAngle = angleMax - bina / 2;
199 double maxAngle = angleMax + bina / 2;
200 SegmentPairList pairList;
205 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
207 if (seg1->
r == 0)
continue;
208 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
210 ratio = seg2->
r / seg1->
r;
211 if (ratio > maxRatio)
continue;
212 if (ratio < minRatio)
215 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
216 if (angle < minAngle || angle > maxAngle)
continue;
221 for (
int iteration = 0; iteration < conditions.
maxTrialCount; iteration++) {
223 double maxval = historank.
maxBin(dr1, dr2);
225 historank.
fill(dr1, dr2, -maxval);
226 auto a_list = MatchListExtract(pairList,
int(dr1),
int(dr2),
GtransfoIdentity());
227 a_list->refineTransfo(conditions.
nSigmas);
228 Solutions.push_back(std::move(a_list));
231 Solutions.sort(DecreasingQuality);
232 std::unique_ptr<StarMatchList> best;
233 best.swap(*Solutions.begin());
235 Solutions.pop_front();
237 LOGLS_DEBUG(_log,
"Best solution " << best->computeResidual() <<
" npairs " << best->size());
238 LOGLS_DEBUG(_log, *(best->getTransfo()));
239 LOGLS_DEBUG(_log,
"Chi2 " << best->getChi2() <<
',' <<
" Number of solutions " << Solutions.size());
256 if (list1.size() <= 4 || list2.size() <= 4) {
257 LOGL_FATAL(_log,
"ListMatchupRotShift_New : (at least) one of the lists is too short.");
270 int nBinsAngle = 180;
271 double angleOffset =
M_PI / nBinsAngle;
276 conditions.
nStarsList2, sList1.size() * sList2.size());
278 SegmentIterator segi1, segi2;
282 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
284 if (seg1->
r == 0)
continue;
285 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
292 ratio = seg2->
r / seg1->
r;
293 if (ratio > maxRatio)
continue;
294 if (ratio < minRatio)
break;
296 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
297 histo.fill(ratio, angle, seg1->
s1rank + 0.5, seg2->
s1rank + 0.5);
308 int oldMaxContent = 0;
314 int maxContent = histo.maxBin(pars);
315 if (maxContent == 0)
break;
317 LOGLS_DEBUG(_log,
"ValMax " << maxContent <<
" ratio " << pars[0] <<
" angle " << pars[1]);
324 if (maxContent < oldMaxContent && i >= conditions.
maxTrialCount)
break;
326 oldMaxContent = maxContent;
328 int rank1L1 = int(pars[2]);
329 int rank1L2 = int(pars[3]);
330 double minAngle, maxAngle;
331 histo.binLimits(pars, 0, minRatio, maxRatio);
332 histo.binLimits(pars, 1, minAngle, maxAngle);
336 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
338 if (seg1->
s1rank != rank1L1)
continue;
339 if (seg1->
r == 0)
continue;
340 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
342 if (seg2->
s1rank != rank1L2)
continue;
344 if (a_list->size() == 0)
346 ratio = seg2->
r / seg1->
r;
347 if (ratio > maxRatio)
continue;
348 if (ratio < minRatio)
351 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
352 if (angle < minAngle || angle > maxAngle)
continue;
364 if (
int(a_list->size()) != maxContent + 1) {
365 LOGLS_ERROR(_log,
"There is an internal inconsistency in ListMatchupRotShift.");
366 LOGLS_ERROR(_log,
"maxContent = " << maxContent);
367 LOGLS_ERROR(_log,
"matches->size() = " << a_list->size());
369 a_list->refineTransfo(conditions.
nSigmas);
370 Solutions.push_back(std::move(a_list));
373 if (Solutions.size() == 0) {
374 LOGLS_ERROR(_log,
"Error In ListMatchup : not a single pair match.");
375 LOGLS_ERROR(_log,
"Probably, the relative scale of lists is not within bounds.");
376 LOGLS_ERROR(_log,
"min/max ratios: " << minRatio <<
' ' << maxRatio);
380 Solutions.sort(DecreasingQuality);
381 std::unique_ptr<StarMatchList> best;
382 best.swap(*Solutions.begin());
384 Solutions.pop_front();
386 LOGLS_INFO(_log,
"Best solution " << best->computeResidual() <<
" npairs " << best->size());
387 LOGLS_INFO(_log, *(best->getTransfo()));
388 LOGLS_INFO(_log,
"Chi2 " << best->getChi2() <<
", Number of solutions " << Solutions.size());
397 return ListMatchupRotShift_Old(list1, list2, gtransfo, conditions);
399 return ListMatchupRotShift_New(list1, list2, gtransfo, conditions);
416 std::unique_ptr<StarMatchList> flipped(ListMatchupRotShift(list1, list2, flip, conditions));
417 std::unique_ptr<StarMatchList> unflipped(
419 if (!flipped || !unflipped)
return std::unique_ptr<StarMatchList>(
nullptr);
422 "unflipped Residual " << unflipped->computeResidual() <<
" nused " << unflipped->size());
423 LOGLS_DEBUG(_log,
"flipped Residual " << flipped->computeResidual() <<
" nused " << flipped->size());
425 if (DecreasingQuality(flipped, unflipped)) {
426 if (conditions.
printLevel >= 1) LOGL_DEBUG(_log,
"Keeping flipped solution.");
432 if (conditions.
printLevel >= 1) LOGL_DEBUG(_log,
"Keeping unflipped solution.");
440 const Gtransfo >ransfo,
double maxShift) {
441 int ncomb = list1.size() * list2.size();
442 if (!ncomb)
return nullptr;
447 nx = (int)sqrt(ncomb);
449 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
453 for (s1 = list1.begin(); s1 != list1.end(); ++
s1) {
454 gtransfo.
apply((*s1)->x, (*s1)->y, x1, y1);
455 for (s2 = list2.begin(); s2 != list2.end(); ++
s2) {
456 histo.
fill((*s2)->x - x1, (*s2)->y - y1);
459 double dx = 0,
dy = 0;
467 const Gtransfo >ransfo,
double maxShift,
double binSize) {
470 int ncomb = list1.size() * list2.size();
474 nx = (int)sqrt(
double(ncomb));
475 if (!ncomb)
return std::unique_ptr<GtransfoLin>(
nullptr);
477 nx = int(2 * maxShift / binSize + 0.5);
479 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
480 double binSizeNew = 2 * maxShift / nx;
485 for (s1 = list1.begin(); s1 != list1.end(); ++
s1) {
486 gtransfo.
apply((*s1)->x, (*s1)->y, x1, y1);
495 for (
int i = 0; i < 4; ++i) {
496 double dx = 0,
dy = 0;
497 double count = histo.
maxBin(dx,
dy);
498 histo.
fill(dx,
dy, -count);
501 auto raw_matches =
listMatchCollect(list1, list2, newGuess.get(), binSizeNew);
503 raw_matches->applyTransfo(*matches, >ransfo);
504 matches->setTransfoOrder(1);
505 matches->refineTransfo(3.);
506 Solutions.push_back(std::move(matches));
508 Solutions.sort(DecreasingQuality);
509 std::unique_ptr<GtransfoLin> best(
new GtransfoLin(*std::const_pointer_cast<GtransfoLin>(
510 std::dynamic_pointer_cast<const GtransfoLin>(Solutions.front()->getTransfo()))));
519 const Gtransfo *guess,
const double maxDist) {
523 const Point *p1 = (*si);
525 const BaseStar *neighbour = list2.findClosest(p2);
526 if (!neighbour)
continue;
527 double distance = p2.
Distance(*neighbour);
528 if (distance < maxDist) {
529 matches->push_back(
StarMatch(*p1, *neighbour, *si, neighbour));
531 matches->back().distance = distance;
541 const Gtransfo *guess,
const double maxDist) {
549 if (!neighbour)
continue;
550 double distance = p2.
Distance(*neighbour);
551 if (distance < maxDist) {
552 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
554 matches->back().distance = distance;
557 matches->setTransfo(guess);
566 const Gtransfo *guess,
const double maxDist) {
567 const Gtransfo *bestTransfo = guess;
568 std::unique_ptr<StarMatchList> prevMatch;
571 m->setTransfo(bestTransfo);
572 m->refineTransfo(3.);
573 LOGLS_INFO(_log,
"Iterating: resid " << m->computeResidual() <<
" size " << m->size());
575 (prevMatch && m->computeResidual() < prevMatch->computeResidual() * 0.999 && m->Chi2() > 0)) {
577 bestTransfo = prevMatch->Transfo();
587 const double maxDist) {
593 if (!neighbour)
continue;
594 double distance = p1->Distance(*neighbour);
595 if (distance < maxDist) {
596 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
598 matches->back().distance = distance;
602 matches->setTransfo(std::make_shared<GtransfoIdentity>());
607 static bool is_transfo_ok(
const StarMatchList *match,
double pixSizeRatio2,
const size_t nmin) {
608 if ((fabs(fabs(std::dynamic_pointer_cast<const GtransfoLin>(match->
getTransfo())->determinant()) -
612 (match->size() > nmin))
614 LOGL_ERROR(_log,
"transfo is not ok!");
629 double dx = tf1.
x - tf2.
x;
630 double dy = tf1.
y - tf2.
y;
631 diff2 += (tf1.
vy * dx * dx + tf1.
vx * dy * dy - 2 * tf1.
vxy * dx *
dy) /
635 if (count)
return diff2 / double(count);
640 size_t nstars = match->size();
641 std::vector<double> resid(nstars);
642 std::vector<double>::iterator ir = resid.begin();
643 for (
auto it = match->begin(); it != match->end(); ++it, ++ir)
644 *ir = sqrt(transfo->
apply(it->point1).computeDist2(it->point2));
645 sort(resid.begin(), resid.end());
646 return (nstars & 1) ? resid[nstars / 2] : (resid[nstars / 2 - 1] + resid[nstars / 2]) * 0.5;
657 LOGLS_INFO(_log,
"listMatchCombinatorial: find match between " << list1.size() <<
" and " << list2.size()
660 double pixSizeRatio2 = sqr(conditions.
sizeRatio);
662 std::min(
size_t(10),
size_t(std::min(List1.size(), List2.size()) * conditions.
minMatchRatio));
664 std::unique_ptr<Gtransfo> transfo;
665 if (is_transfo_ok(match.get(), pixSizeRatio2, nmin))
666 transfo = match->getTransfo()->
clone();
668 LOGL_ERROR(_log,
"listMatchCombinatorial: direct transfo failed, trying reverse");
670 if (is_transfo_ok(match.get(), pixSizeRatio2, nmin))
671 transfo = match->inverseTransfo();
673 LOGL_FATAL(_log,
"FAILED");
678 LOGL_INFO(_log,
"FOUND");
680 LOGL_DEBUG(_log,
" listMatchCombinatorial: found the following transfo.");
681 LOGLS_DEBUG(_log, *transfo);
684 LOGL_ERROR(_log,
"listMatchCombinatorial: failed to find a transfo");
689 std::unique_ptr<Gtransfo> transfo,
const int maxOrder) {
691 return std::unique_ptr<Gtransfo>(
nullptr);
695 const double brightDist = 2.;
696 const double fullDist = 4.;
697 const double nSigmas = 3.;
698 const size_t nStars = 500;
712 auto brightMatch =
listMatchCollect(list1, list2, transfo.get(), brightDist);
713 double curChi2 =
computeChi2(*brightMatch, *transfo) / brightMatch->size();
715 LOGLS_INFO(_log,
"listMatchRefine: start: med.resid " << median_distance(fullMatch.get(), transfo.get())
716 <<
" #match " << fullMatch->size());
719 auto curTransfo = brightMatch->getTransfo()->clone();
723 brightMatch->setTransfoOrder(order);
724 brightMatch->refineTransfo(nSigmas);
725 transDiff = transfo_diff(list1, brightMatch->getTransfo().get(), curTransfo.get());
726 curTransfo = brightMatch->getTransfo()->clone();
728 }
while (brightMatch->size() > nstarmin && transDiff > 0.05 && ++iter < 5);
730 double prevChi2 = curChi2;
731 curChi2 =
computeChi2(*brightMatch, *curTransfo) / brightMatch->size();
734 LOGLS_INFO(_log,
"listMatchRefine: order " << order <<
" med.resid " 735 << median_distance(fullMatch.get(), curTransfo.get())
736 <<
" #match " << fullMatch->size());
737 if (((prevChi2 - curChi2) > 0.01 * curChi2) && curChi2 > 0) {
738 LOGLS_INFO(_log,
" listMatchRefine: order " << order <<
" was a better guess.");
739 transfo = brightMatch->getTransfo()->clone();
741 nstarmin = brightMatch->getTransfo()->getNpar();
742 }
while (++order <= maxOrder);
implements the linear transformations (6 real coefficients).
BaseStarList::const_iterator BaseStarCIterator
std::shared_ptr< const BaseStar > s1
void binWidth(double &Hdx, double &Hdy) const
SegmentPair(Segment *f, Segment *s)
std::unique_ptr< Gtransfo > gtransfoCompose(const Gtransfo *left, const Gtransfo *right)
Returns a pointer to a composition.
A hanger for star associations.
std::shared_ptr< const BaseStar > s2
std::list< Segment >::const_iterator SegmentCIterator
void fill(float x, float y, float weight=1.)
A class to histogram in 4 dimensions.
double maxSizeRatio() const
std::unique_ptr< Gtransfo > listMatchCombinatorial(const BaseStarList &list1, const BaseStarList &list2, const MatchConditions &conditions=MatchConditions())
SegmentList(const BaseStarList &list, const int nStar, const Gtransfo >ransfo=GtransfoIdentity())
double Distance(const Point &other) const
SegmentPairList::const_iterator SegmentPairListCIterator
A Point with uncertainties.
void cutTail(const int nKeep)
cuts the end of the std::list
std::unique_ptr< StarMatchList > matchSearchRotShift(BaseStarList &list1, BaseStarList &list2, const MatchConditions &conditions)
searches a geometrical transformation that goes from list1 to list2.
double maxBin(double &x, double &y) const
The base class for handling stars. Used by all matching routines.
std::shared_ptr< const BaseStar > findClosest(const Point &where, const double maxDist, bool(*SkipIt)(const BaseStar &)=nullptr) const
Find the closest with some rejection capability.
std::unique_ptr< StarMatchList > matchSearchRotShiftFlip(BaseStarList &list1, BaseStarList &list2, const MatchConditions &conditions)
same as above but searches also a flipped solution.
std::shared_ptr< const Gtransfo > getTransfo() const
carries out a fit with outlier rejection
std::list< Segment >::iterator SegmentIterator
Class for a simple mapping implementing a generic Gtransfo.
void dumpTransfo(std::ostream &stream=std::cout) const
print the matching transformation quality (transfo, chi2, residual)
std::unique_ptr< Gtransfo > listMatchRefine(const BaseStarList &list1, const BaseStarList &list2, std::unique_ptr< Gtransfo > transfo, const int maxOrder=3)
double relativeAngle(Segment *other)
Iterator beginScan(const Point &where, double maxDist) const
double minSizeRatio() const
double computeChi2(const StarMatchList &L, const Gtransfo >ransfo)
the actual chi2
std::unique_ptr< StarMatchList > listMatchCollect(const BaseStarList &list1, const BaseStarList &list2, const Gtransfo *guess, const double maxDist)
assembles star matches.
std::list< SegmentPair > SegmentPairList
virtual void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
Iterator meant to traverse objects within some limiting distance.
void copyTo(StarList< Star > ©) const
clears copy and makes a copy of the std::list to copy
A do-nothing transformation. It anyway has dummy routines to mimick a Gtransfo.
just here to provide a specialized constructor, and fit.
void fluxSort()
a model routine to sort the std::list
This is an auxillary class for matching objects from starlists.
Combinatorial searches for linear transformations to go from list1 to list2.
a virtual (interface) class for geometric transformations.
friend std::ostream & operator<<(std::ostream &stream, const Segment &segment)
Segment(std::shared_ptr< const BaseStar > star1, std::shared_ptr< const BaseStar > star2, const int star1Rank, const Gtransfo >ransfo)
Parameters to be provided to combinatorial searches.
virtual std::unique_ptr< Gtransfo > clone() const =0
returns a copy (allocated by new) of the transformation.
Fast locator in starlists.
std::list< std::unique_ptr< StarMatchList > > SolList
virtual void apply(const double xIn, const double yIn, double &xOut, double &yOut) const =0
SegmentPairList::iterator SegmentPairListIterator
std::unique_ptr< GtransfoLin > listMatchupShift(const BaseStarList &list1, const BaseStarList &list2, const Gtransfo >ransfo, double maxShift, double binSize=0)
searches for a 2 dimensional shift using a very crude histogram method.