31 #define M_PI 3.14159265358979323846 44 LOG_LOGGER _log =
LOG_GET(
"jointcal.ListMatch");
70 r =
sqrt(dx * dx + dy * dy);
75 return atan2(other->
dx * dy - dx * other->
dy, dx * other->
dx + dy * other->
dy);
79 stream <<
" dx " << segment.
dx <<
" dy " << segment.
dy <<
" r " << segment.
r <<
std::endl;
93 static bool DecreasingLength(
const Segment &first,
const Segment &second) {
return (first.
r > second.
r); }
99 siStop = list.
begin();
105 for (
auto si1 = list.
begin(); si1 != siStop; ++si1, rank++)
106 for (
auto si2 = siStop; si2 != si1; --si2) {
107 push_back(
Segment(*si1, *si2, rank, gtransfo));
109 sort(DecreasingLength);
129 for (SegmentPairListCIterator spi = pairList.
begin(); spi != pairList.
end(); spi++) {
131 if (a_pair.first->s1rank != rank1 || a_pair.second->s1rank != rank2)
continue;
135 if (matchList->size() == 0)
136 matchList->push_back(
StarMatch(gtransfo.
apply(*(a_pair.first->s1)), *(a_pair.second->s1),
137 a_pair.first->s1, a_pair.second->s1));
139 matchList->push_back(
StarMatch(gtransfo.
apply(*(a_pair.first->s2)), *(a_pair.second->s2),
140 a_pair.first->s2, a_pair.second->s2));
147 int idiff = first->size() - second->size();
151 return (first->getDist2() < second->getDist2());
173 int nBinsAngle = 180;
174 double angleOffset =
M_PI / nBinsAngle;
177 Histo2d histo(nBinsR, minRatio, maxRatio, nBinsAngle, -
M_PI - angleOffset,
M_PI - angleOffset);
179 SegmentIterator segi1, segi2;
182 for (segi1 = sList1.
begin(); segi1 != sList1.
end(); ++segi1) {
184 if (seg1->
r == 0)
continue;
185 for (segi2 = sList2.
begin(); segi2 != sList2.
end(); ++segi2) {
192 ratio = seg2->
r / seg1->
r;
193 if (ratio > maxRatio)
continue;
194 if (ratio < minRatio)
break;
196 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
197 histo.
fill(ratio, angle);
211 double ratioMax, angleMax;
212 double maxContent = histo.
maxBin(ratioMax, angleMax);
213 histo.
fill(ratioMax, angleMax, -maxContent);
216 LOGLS_DEBUG(_log,
" valMax " << maxContent <<
" ratio " << ratioMax <<
" angle " << angleMax);
218 minRatio = ratioMax - binr / 2;
219 maxRatio = ratioMax + binr / 2;
220 double minAngle = angleMax - bina / 2;
221 double maxAngle = angleMax + bina / 2;
222 SegmentPairList pairList;
227 for (segi1 = sList1.
begin(); segi1 != sList1.
end(); ++segi1) {
229 if (seg1->
r == 0)
continue;
230 for (segi2 = sList2.
begin(); segi2 != sList2.
end(); ++segi2) {
232 ratio = seg2->
r / seg1->
r;
233 if (ratio > maxRatio)
continue;
234 if (ratio < minRatio)
237 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
238 if (angle < minAngle || angle > maxAngle)
continue;
243 for (
int iteration = 0; iteration < conditions.
maxTrialCount; iteration++) {
245 double maxval = historank.
maxBin(dr1, dr2);
247 historank.
fill(dr1, dr2, -maxval);
248 auto a_list = MatchListExtract(pairList,
int(dr1),
int(dr2),
GtransfoIdentity());
249 a_list->refineTransfo(conditions.
nSigmas);
253 Solutions.
sort(DecreasingQuality);
259 LOGLS_DEBUG(_log,
"Best solution " << best->computeResidual() <<
" npairs " << best->size());
261 LOGLS_DEBUG(_log,
"Chi2 " << best->getChi2() <<
',' <<
" Number of solutions " << Solutions.
size());
278 if (list1.
size() <= 4 || list2.
size() <= 4) {
279 LOGL_FATAL(_log,
"ListMatchupRotShift_New : (at least) one of the lists is too short.");
292 int nBinsAngle = 180;
293 double angleOffset =
M_PI / nBinsAngle;
300 SegmentIterator segi1, segi2;
304 for (segi1 = sList1.
begin(); segi1 != sList1.
end(); ++segi1) {
306 if (seg1->
r == 0)
continue;
307 for (segi2 = sList2.
begin(); segi2 != sList2.
end(); ++segi2) {
314 ratio = seg2->
r / seg1->
r;
315 if (ratio > maxRatio)
continue;
316 if (ratio < minRatio)
break;
318 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
319 histo.fill(ratio, angle, seg1->
s1rank + 0.5, seg2->
s1rank + 0.5);
330 int oldMaxContent = 0;
336 int maxContent = histo.maxBin(pars);
337 if (maxContent == 0)
break;
339 LOGLS_DEBUG(_log,
"ValMax " << maxContent <<
" ratio " << pars[0] <<
" angle " << pars[1]);
346 if (maxContent < oldMaxContent && i >= conditions.
maxTrialCount)
break;
348 oldMaxContent = maxContent;
350 int rank1L1 = int(pars[2]);
351 int rank1L2 = int(pars[3]);
352 double minAngle, maxAngle;
353 histo.binLimits(pars, 0, minRatio, maxRatio);
354 histo.binLimits(pars, 1, minAngle, maxAngle);
358 for (segi1 = sList1.
begin(); segi1 != sList1.
end(); ++segi1) {
360 if (seg1->
s1rank != rank1L1)
continue;
361 if (seg1->
r == 0)
continue;
362 for (segi2 = sList2.
begin(); segi2 != sList2.
end(); ++segi2) {
364 if (seg2->
s1rank != rank1L2)
continue;
366 if (a_list->size() == 0)
368 ratio = seg2->
r / seg1->
r;
369 if (ratio > maxRatio)
continue;
370 if (ratio < minRatio)
373 if (angle >
M_PI - angleOffset) angle -= 2. *
M_PI;
374 if (angle < minAngle || angle > maxAngle)
continue;
386 if (
int(a_list->size()) != maxContent + 1) {
387 LOGLS_ERROR(_log,
"There is an internal inconsistency in ListMatchupRotShift.");
389 LOGLS_ERROR(_log,
"matches->size() = " << a_list->size());
391 a_list->refineTransfo(conditions.
nSigmas);
395 if (Solutions.
size() == 0) {
396 LOGLS_ERROR(_log,
"Error In ListMatchup : not a single pair match.");
397 LOGLS_ERROR(_log,
"Probably, the relative scale of lists is not within bounds.");
398 LOGLS_ERROR(_log,
"min/max ratios: " << minRatio <<
' ' << maxRatio);
402 Solutions.
sort(DecreasingQuality);
408 LOGLS_INFO(_log,
"Best solution " << best->computeResidual() <<
" npairs " << best->size());
410 LOGLS_INFO(_log,
"Chi2 " << best->getChi2() <<
", Number of solutions " << Solutions.
size());
419 return ListMatchupRotShift_Old(list1, list2, gtransfo, conditions);
421 return ListMatchupRotShift_New(list1, list2, gtransfo, conditions);
444 "unflipped Residual " << unflipped->computeResidual() <<
" nused " << unflipped->size());
445 LOGLS_DEBUG(_log,
"flipped Residual " << flipped->computeResidual() <<
" nused " << flipped->size());
447 if (DecreasingQuality(flipped, unflipped)) {
462 const Gtransfo >ransfo,
double maxShift) {
463 int ncomb = list1.
size() * list2.
size();
464 if (!ncomb)
return nullptr;
469 nx = (int)
sqrt(ncomb);
471 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
475 for (s1 = list1.
begin(); s1 != list1.
end(); ++
s1) {
476 gtransfo.
apply((*s1)->x, (*s1)->y, x1, y1);
477 for (s2 = list2.
begin(); s2 != list2.
end(); ++
s2) {
478 histo.
fill((*s2)->x - x1, (*s2)->y - y1);
481 double dx = 0,
dy = 0;
489 const Gtransfo >ransfo,
double maxShift,
double binSize) {
492 int ncomb = list1.
size() * list2.
size();
496 nx = (int)
sqrt(
double(ncomb));
499 nx = int(2 * maxShift / binSize + 0.5);
501 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
502 double binSizeNew = 2 * maxShift / nx;
507 for (s1 = list1.
begin(); s1 != list1.
end(); ++
s1) {
508 gtransfo.
apply((*s1)->x, (*s1)->y, x1, y1);
517 for (
int i = 0; i < 4; ++i) {
518 double dx = 0,
dy = 0;
520 histo.
fill(dx,
dy, -count);
523 auto raw_matches =
listMatchCollect(list1, list2, newGuess.get(), binSizeNew);
525 raw_matches->applyTransfo(*matches, >ransfo);
526 matches->setTransfoOrder(1);
527 matches->refineTransfo(3.);
530 Solutions.
sort(DecreasingQuality);
532 std::dynamic_pointer_cast<const GtransfoLin>(Solutions.
front()->getTransfo()))));
541 const Gtransfo *guess,
const double maxDist) {
545 const Point *p1 = (*si);
547 const BaseStar *neighbour = list2.findClosest(p2);
548 if (!neighbour)
continue;
550 if (distance < maxDist) {
551 matches->push_back(
StarMatch(*p1, *neighbour, *si, neighbour));
553 matches->back().distance = distance;
563 const Gtransfo *guess,
const double maxDist) {
571 if (!neighbour)
continue;
573 if (distance < maxDist) {
574 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
576 matches->back().distance = distance;
579 matches->setTransfo(guess);
588 const Gtransfo *guess,
const double maxDist) {
589 const Gtransfo *bestTransfo = guess;
593 m->setTransfo(bestTransfo);
594 m->refineTransfo(3.);
595 LOGLS_INFO(_log,
"Iterating: resid " <<
m->computeResidual() <<
" size " <<
m->size());
597 (prevMatch &&
m->computeResidual() < prevMatch->computeResidual() * 0.999 &&
m->Chi2() > 0)) {
599 bestTransfo = prevMatch->Transfo();
609 const double maxDist) {
615 if (!neighbour)
continue;
616 double distance = p1->Distance(*neighbour);
617 if (distance < maxDist) {
618 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
620 matches->back().distance = distance;
624 matches->setTransfo(std::make_shared<GtransfoIdentity>());
629 static bool is_transfo_ok(
const StarMatchList *match,
double pixSizeRatio2,
const size_t nmin) {
630 if ((
fabs(
fabs(std::dynamic_pointer_cast<const GtransfoLin>(match->
getTransfo())->determinant()) -
634 (match->
size() > nmin))
651 double dx = tf1.
x - tf2.
x;
652 double dy = tf1.
y - tf2.
y;
653 diff2 += (tf1.
vy * dx * dx + tf1.
vx * dy * dy - 2 * tf1.
vxy * dx *
dy) /
657 if (count)
return diff2 / double(count);
662 size_t nstars = match->
size();
665 for (
auto it = match->
begin(); it != match->
end(); ++it, ++ir)
666 *ir =
sqrt(transfo->
apply(it->point1).computeDist2(it->point2));
668 return (nstars & 1) ? resid[nstars / 2] : (resid[nstars / 2 - 1] + resid[nstars / 2]) * 0.5;
679 LOGLS_INFO(_log,
"listMatchCombinatorial: find match between " << list1.
size() <<
" and " << list2.
size()
687 if (is_transfo_ok(match.get(), pixSizeRatio2, nmin))
688 transfo = match->getTransfo()->
clone();
690 LOGL_ERROR(_log,
"listMatchCombinatorial: direct transfo failed, trying reverse");
692 if (is_transfo_ok(match.get(), pixSizeRatio2, nmin))
693 transfo = match->inverseTransfo();
702 LOGL_DEBUG(_log,
" listMatchCombinatorial: found the following transfo.");
706 LOGL_ERROR(_log,
"listMatchCombinatorial: failed to find a transfo");
717 const double brightDist = 2.;
718 const double fullDist = 4.;
719 const double nSigmas = 3.;
720 const size_t nStars = 500;
735 double curChi2 =
computeChi2(*brightMatch, *transfo) / brightMatch->size();
737 LOGLS_INFO(_log,
"listMatchRefine: start: med.resid " << median_distance(fullMatch.get(), transfo.
get())
738 <<
" #match " << fullMatch->size());
741 auto curTransfo = brightMatch->getTransfo()->clone();
745 brightMatch->setTransfoOrder(order);
746 brightMatch->refineTransfo(nSigmas);
747 transDiff = transfo_diff(list1, brightMatch->getTransfo().get(), curTransfo.get());
748 curTransfo = brightMatch->getTransfo()->clone();
750 }
while (brightMatch->size() > nstarmin && transDiff > 0.05 && ++iter < 5);
752 double prevChi2 = curChi2;
753 curChi2 =
computeChi2(*brightMatch, *curTransfo) / brightMatch->size();
756 LOGLS_INFO(_log,
"listMatchRefine: order " << order <<
" med.resid " 757 << median_distance(fullMatch.get(), curTransfo.get())
758 <<
" #match " << fullMatch->size());
759 if (((prevChi2 - curChi2) > 0.01 * curChi2) && curChi2 > 0) {
760 LOGLS_INFO(_log,
" listMatchRefine: order " << order <<
" was a better guess.");
761 transfo = brightMatch->getTransfo()->clone();
763 nstarmin = brightMatch->getTransfo()->getNpar();
764 }
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)
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())
#define LOGL_ERROR(logger, message...)
SegmentList(const BaseStarList &list, const int nStar, const Gtransfo >ransfo=GtransfoIdentity())
double Distance(const Point &other) const
SegmentPairList::const_iterator SegmentPairListCIterator
#define LOGL_DEBUG(logger, message...)
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.
#define LOGL_INFO(logger, message...)
#define LOGL_FATAL(logger, message...)
std::shared_ptr< const Gtransfo > getTransfo() const
carries out a fit with outlier rejection
std::list< Segment >::iterator SegmentIterator
#define LOGLS_DEBUG(logger, message)
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.
#define LOGLS_INFO(logger, message)
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.
table::Key< double > angle
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.
#define LOGLS_ERROR(logger, message)
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.
std::unique_ptr< Gtransfo > gtransfoCompose(Gtransfo const &left, Gtransfo const &right)
Returns a pointer to a composition of gtransfos, representing left(right()).