7 #define M_PI 3.14159265358979323846 10 #include "lsst/log/Log.h" 20 LOG_LOGGER _log = LOG_GET(
"jointcal.ListMatch");
33 std::shared_ptr<const BaseStar>
s1,
s2;
37 Segment(std::shared_ptr<const BaseStar> star1, std::shared_ptr<const BaseStar> star2,
const int star1Rank,
40 s1 = std::move(star1);
41 s2 = std::move(star2);
46 r = sqrt(dx * dx + dy * dy);
51 return atan2(other->
dx * dy - dx * other->
dy, dx * other->
dx + dy * other->
dy);
55 stream <<
" dx " << segment.
dx <<
" dy " << segment.
dy <<
" r " << segment.
r << std::endl;
69 static bool DecreasingLength(
const Segment &first,
const Segment &second) {
return (first.
r > second.
r); }
75 siStop = list.begin();
76 int limit = std::min(nStars,
int(list.size())) - 1;
77 for (
int count = 0; count < limit; count++) ++siStop;
81 for (
auto si1 = list.begin(); si1 != siStop; ++si1, rank++)
82 for (
auto si2 = siStop; si2 != si1; --si2) {
83 push_back(
Segment(*si1, *si2, rank, gtransfo));
85 sort(DecreasingLength);
98 static std::unique_ptr<StarMatchList> MatchListExtract(
const SegmentPairList &pairList,
int rank1,
int rank2,
105 for (SegmentPairListCIterator spi = pairList.begin(); spi != pairList.end(); spi++) {
107 if (a_pair.first->s1rank != rank1 || a_pair.second->s1rank != rank2)
continue;
111 if (matchList->size() == 0)
112 matchList->push_back(
StarMatch(gtransfo.
apply(*(a_pair.first->s1)), *(a_pair.second->s1),
113 a_pair.first->s1, a_pair.second->s1));
115 matchList->push_back(
StarMatch(gtransfo.
apply(*(a_pair.first->s2)), *(a_pair.second->s2),
116 a_pair.first->s2, a_pair.second->s2));
121 static bool DecreasingQuality(
const std::unique_ptr<StarMatchList> &first,
122 const std::unique_ptr<StarMatchList> &second) {
123 int idiff = first->size() - second->size();
127 return (first->getDist2() < second->getDist2());
132 using SolList = std::list<std::unique_ptr<StarMatchList>>;
149 int nBinsAngle = 180;
150 double angleOffset =
M_PI / nBinsAngle;
153 Histo2d histo(nBinsR, minRatio, maxRatio, nBinsAngle, -
M_PI - angleOffset,
M_PI - angleOffset);
155 SegmentIterator segi1, segi2;
158 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
160 if (seg1->
r == 0)
continue;
161 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
168 ratio = seg2->
r / seg1->
r;
169 if (ratio > maxRatio)
continue;
170 if (ratio < minRatio)
break;
172 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
173 histo.
fill(ratio, angle);
187 double ratioMax, angleMax;
188 double maxContent = histo.
maxBin(ratioMax, angleMax);
189 histo.
fill(ratioMax, angleMax, -maxContent);
192 LOGLS_DEBUG(_log,
" valMax " << maxContent <<
" ratio " << ratioMax <<
" angle " << angleMax);
194 minRatio = ratioMax - binr / 2;
195 maxRatio = ratioMax + binr / 2;
196 double minAngle = angleMax - bina / 2;
197 double maxAngle = angleMax + bina / 2;
198 SegmentPairList pairList;
203 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
205 if (seg1->
r == 0)
continue;
206 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
208 ratio = seg2->
r / seg1->
r;
209 if (ratio > maxRatio)
continue;
210 if (ratio < minRatio)
213 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
214 if (angle < minAngle || angle > maxAngle)
continue;
219 for (
int iteration = 0; iteration < conditions.
maxTrialCount; iteration++) {
221 double maxval = historank.
maxBin(dr1, dr2);
223 historank.
fill(dr1, dr2, -maxval);
224 auto a_list = MatchListExtract(pairList,
int(dr1),
int(dr2),
GtransfoIdentity());
225 a_list->refineTransfo(conditions.
nSigmas);
226 Solutions.push_back(std::move(a_list));
229 Solutions.sort(DecreasingQuality);
230 std::unique_ptr<StarMatchList> best;
231 best.swap(*Solutions.begin());
233 Solutions.pop_front();
235 LOGLS_DEBUG(_log,
"Best solution " << best->computeResidual() <<
" npairs " << best->size());
236 LOGLS_DEBUG(_log, *(best->getTransfo()));
237 LOGLS_DEBUG(_log,
"Chi2 " << best->getChi2() <<
',' <<
" Number of solutions " << Solutions.size());
254 if (list1.size() <= 4 || list2.size() <= 4) {
255 LOGL_FATAL(_log,
"ListMatchupRotShift_New : (at least) one of the lists is too short.");
268 int nBinsAngle = 180;
269 double angleOffset =
M_PI / nBinsAngle;
274 conditions.
nStarsList2, sList1.size() * sList2.size());
276 SegmentIterator segi1, segi2;
280 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
282 if (seg1->
r == 0)
continue;
283 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
290 ratio = seg2->
r / seg1->
r;
291 if (ratio > maxRatio)
continue;
292 if (ratio < minRatio)
break;
294 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
295 histo.fill(ratio, angle, seg1->
s1rank + 0.5, seg2->
s1rank + 0.5);
306 int oldMaxContent = 0;
312 int maxContent = histo.maxBin(pars);
313 if (maxContent == 0)
break;
315 LOGLS_DEBUG(_log,
"ValMax " << maxContent <<
" ratio " << pars[0] <<
" angle " << pars[1]);
322 if (maxContent < oldMaxContent && i >= conditions.
maxTrialCount)
break;
324 oldMaxContent = maxContent;
326 int rank1L1 = int(pars[2]);
327 int rank1L2 = int(pars[3]);
328 double minAngle, maxAngle;
329 histo.binLimits(pars, 0, minRatio, maxRatio);
330 histo.binLimits(pars, 1, minAngle, maxAngle);
334 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
336 if (seg1->
s1rank != rank1L1)
continue;
337 if (seg1->
r == 0)
continue;
338 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
340 if (seg2->
s1rank != rank1L2)
continue;
342 if (a_list->size() == 0)
344 ratio = seg2->
r / seg1->
r;
345 if (ratio > maxRatio)
continue;
346 if (ratio < minRatio)
349 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
350 if (angle < minAngle || angle > maxAngle)
continue;
362 if (
int(a_list->size()) != maxContent + 1) {
363 LOGLS_ERROR(_log,
"There is an internal inconsistency in ListMatchupRotShift.");
364 LOGLS_ERROR(_log,
"maxContent = " << maxContent);
365 LOGLS_ERROR(_log,
"matches->size() = " << a_list->size());
367 a_list->refineTransfo(conditions.
nSigmas);
368 Solutions.push_back(std::move(a_list));
371 if (Solutions.size() == 0) {
372 LOGLS_ERROR(_log,
"Error In ListMatchup : not a single pair match.");
373 LOGLS_ERROR(_log,
"Probably, the relative scale of lists is not within bounds.");
374 LOGLS_ERROR(_log,
"min/max ratios: " << minRatio <<
' ' << maxRatio);
378 Solutions.sort(DecreasingQuality);
379 std::unique_ptr<StarMatchList> best;
380 best.swap(*Solutions.begin());
382 Solutions.pop_front();
384 LOGLS_INFO(_log,
"Best solution " << best->computeResidual() <<
" npairs " << best->size());
385 LOGLS_INFO(_log, *(best->getTransfo()));
386 LOGLS_INFO(_log,
"Chi2 " << best->getChi2() <<
", Number of solutions " << Solutions.size());
395 return ListMatchupRotShift_Old(list1, list2, gtransfo, conditions);
397 return ListMatchupRotShift_New(list1, list2, gtransfo, conditions);
414 std::unique_ptr<StarMatchList> flipped(ListMatchupRotShift(list1, list2, flip, conditions));
415 std::unique_ptr<StarMatchList> unflipped(
417 if (!flipped || !unflipped)
return std::unique_ptr<StarMatchList>(
nullptr);
420 "unflipped Residual " << unflipped->computeResidual() <<
" nused " << unflipped->size());
421 LOGLS_DEBUG(_log,
"flipped Residual " << flipped->computeResidual() <<
" nused " << flipped->size());
423 if (DecreasingQuality(flipped, unflipped)) {
424 if (conditions.
printLevel >= 1) LOGL_DEBUG(_log,
"Keeping flipped solution.");
430 if (conditions.
printLevel >= 1) LOGL_DEBUG(_log,
"Keeping unflipped solution.");
438 const Gtransfo >ransfo,
double maxShift) {
439 int ncomb = list1.size() * list2.size();
440 if (!ncomb)
return nullptr;
445 nx = (int)sqrt(ncomb);
447 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
451 for (s1 = list1.begin(); s1 != list1.end(); ++
s1) {
452 gtransfo.
apply((*s1)->x, (*s1)->y, x1, y1);
453 for (s2 = list2.begin(); s2 != list2.end(); ++
s2) {
454 histo.
fill((*s2)->x - x1, (*s2)->y - y1);
457 double dx = 0,
dy = 0;
465 const Gtransfo >ransfo,
double maxShift,
double binSize) {
468 int ncomb = list1.size() * list2.size();
472 nx = (int)sqrt(
double(ncomb));
473 if (!ncomb)
return std::unique_ptr<GtransfoLin>(
nullptr);
475 nx = int(2 * maxShift / binSize + 0.5);
477 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
478 double binSizeNew = 2 * maxShift / nx;
483 for (s1 = list1.begin(); s1 != list1.end(); ++
s1) {
484 gtransfo.
apply((*s1)->x, (*s1)->y, x1, y1);
493 for (
int i = 0; i < 4; ++i) {
494 double dx = 0,
dy = 0;
495 double count = histo.
maxBin(dx,
dy);
496 histo.
fill(dx,
dy, -count);
499 auto raw_matches =
listMatchCollect(list1, list2, newGuess.get(), binSizeNew);
501 raw_matches->applyTransfo(*matches, >ransfo);
502 matches->setTransfoOrder(1);
503 matches->refineTransfo(3.);
504 Solutions.push_back(std::move(matches));
506 Solutions.sort(DecreasingQuality);
507 std::unique_ptr<GtransfoLin> best(
new GtransfoLin(*std::const_pointer_cast<GtransfoLin>(
508 std::dynamic_pointer_cast<const GtransfoLin>(Solutions.front()->getTransfo()))));
517 const Gtransfo *guess,
const double maxDist) {
521 const Point *p1 = (*si);
523 const BaseStar *neighbour = list2.findClosest(p2);
524 if (!neighbour)
continue;
525 double distance = p2.
Distance(*neighbour);
526 if (distance < maxDist) {
527 matches->push_back(
StarMatch(*p1, *neighbour, *si, neighbour));
529 matches->back().distance = distance;
539 const Gtransfo *guess,
const double maxDist) {
547 if (!neighbour)
continue;
548 double distance = p2.
Distance(*neighbour);
549 if (distance < maxDist) {
550 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
552 matches->back().distance = distance;
555 matches->setTransfo(guess);
564 const Gtransfo *guess,
const double maxDist) {
565 const Gtransfo *bestTransfo = guess;
566 std::unique_ptr<StarMatchList> prevMatch;
569 m->setTransfo(bestTransfo);
570 m->refineTransfo(3.);
571 LOGLS_INFO(_log,
"Iterating: resid " << m->computeResidual() <<
" size " << m->size());
573 (prevMatch && m->computeResidual() < prevMatch->computeResidual() * 0.999 && m->Chi2() > 0)) {
575 bestTransfo = prevMatch->Transfo();
585 const double maxDist) {
591 if (!neighbour)
continue;
592 double distance = p1->Distance(*neighbour);
593 if (distance < maxDist) {
594 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
596 matches->back().distance = distance;
600 matches->setTransfo(std::make_shared<GtransfoIdentity>());
605 static bool is_transfo_ok(
const StarMatchList *match,
double pixSizeRatio2,
const size_t nmin) {
606 if ((fabs(fabs(std::dynamic_pointer_cast<const GtransfoLin>(match->
getTransfo())->determinant()) -
610 (match->size() > nmin))
612 LOGL_ERROR(_log,
"transfo is not ok!");
627 double dx = tf1.
x - tf2.
x;
628 double dy = tf1.
y - tf2.
y;
629 diff2 += (tf1.
vy * dx * dx + tf1.
vx * dy * dy - 2 * tf1.
vxy * dx *
dy) /
633 if (count)
return diff2 / double(count);
638 size_t nstars = match->size();
639 std::vector<double> resid(nstars);
640 std::vector<double>::iterator ir = resid.begin();
641 for (
auto it = match->begin(); it != match->end(); ++it, ++ir)
642 *ir = sqrt(transfo->
apply(it->point1).computeDist2(it->point2));
643 sort(resid.begin(), resid.end());
644 return (nstars & 1) ? resid[nstars / 2] : (resid[nstars / 2 - 1] + resid[nstars / 2]) * 0.5;
655 LOGLS_INFO(_log,
"listMatchCombinatorial: find match between " << list1.size() <<
" and " << list2.size()
658 double pixSizeRatio2 = std::pow(conditions.
sizeRatio, 2);
660 std::min(
size_t(10),
size_t(std::min(List1.size(), List2.size()) * conditions.
minMatchRatio));
662 std::unique_ptr<Gtransfo> transfo;
663 if (is_transfo_ok(match.get(), pixSizeRatio2, nmin))
664 transfo = match->getTransfo()->
clone();
666 LOGL_ERROR(_log,
"listMatchCombinatorial: direct transfo failed, trying reverse");
668 if (is_transfo_ok(match.get(), pixSizeRatio2, nmin))
669 transfo = match->inverseTransfo();
671 LOGL_FATAL(_log,
"FAILED");
676 LOGL_INFO(_log,
"FOUND");
678 LOGL_DEBUG(_log,
" listMatchCombinatorial: found the following transfo.");
679 LOGLS_DEBUG(_log, *transfo);
682 LOGL_ERROR(_log,
"listMatchCombinatorial: failed to find a transfo");
687 std::unique_ptr<Gtransfo> transfo,
const int maxOrder) {
689 return std::unique_ptr<Gtransfo>(
nullptr);
693 const double brightDist = 2.;
694 const double fullDist = 4.;
695 const double nSigmas = 3.;
696 const size_t nStars = 500;
710 auto brightMatch =
listMatchCollect(list1, list2, transfo.get(), brightDist);
711 double curChi2 =
computeChi2(*brightMatch, *transfo) / brightMatch->size();
713 LOGLS_INFO(_log,
"listMatchRefine: start: med.resid " << median_distance(fullMatch.get(), transfo.get())
714 <<
" #match " << fullMatch->size());
717 auto curTransfo = brightMatch->getTransfo()->clone();
721 brightMatch->setTransfoOrder(order);
722 brightMatch->refineTransfo(nSigmas);
723 transDiff = transfo_diff(list1, brightMatch->getTransfo().get(), curTransfo.get());
724 curTransfo = brightMatch->getTransfo()->clone();
726 }
while (brightMatch->size() > nstarmin && transDiff > 0.05 && ++iter < 5);
728 double prevChi2 = curChi2;
729 curChi2 =
computeChi2(*brightMatch, *curTransfo) / brightMatch->size();
732 LOGLS_INFO(_log,
"listMatchRefine: order " << order <<
" med.resid " 733 << median_distance(fullMatch.get(), curTransfo.get())
734 <<
" #match " << fullMatch->size());
735 if (((prevChi2 - curChi2) > 0.01 * curChi2) && curChi2 > 0) {
736 LOGLS_INFO(_log,
" listMatchRefine: order " << order <<
" was a better guess.");
737 transfo = brightMatch->getTransfo()->clone();
739 nstarmin = brightMatch->getTransfo()->getNpar();
740 }
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.