31 #define M_PI 3.14159265358979323846
44 LOG_LOGGER _log =
LOG_GET(
"jointcal.ListMatch");
79 stream <<
" dx " << segment.
dx <<
" dy " << segment.
dy <<
" r " << segment.
r <<
std::endl;
107 for (
auto si2 = siStop; si2 != si1; --si2) {
110 sort(DecreasingLength);
132 if (a_pair.first->s1rank != rank1 || a_pair.second->s1rank != rank2)
continue;
136 if (matchList->size() == 0)
137 matchList->push_back(
StarMatch(
transform.apply(*(a_pair.first->s1)), *(a_pair.second->s1),
138 a_pair.first->s1, a_pair.second->s1));
140 matchList->push_back(
StarMatch(
transform.apply(*(a_pair.first->s2)), *(a_pair.second->s2),
141 a_pair.first->s2, a_pair.second->s2));
174 int nBinsAngle = 180;
175 double angleOffset =
M_PI / nBinsAngle;
178 Histo2d histo(nBinsR, minRatio, maxRatio, nBinsAngle, -
M_PI - angleOffset,
M_PI - angleOffset);
183 for (segi1 = sList1.
begin(); segi1 != sList1.
end(); ++segi1) {
185 if (seg1->
r == 0)
continue;
186 for (segi2 = sList2.
begin(); segi2 != sList2.
end(); ++segi2) {
194 if (
ratio > maxRatio)
continue;
195 if (
ratio < minRatio)
break;
212 double ratioMax, angleMax;
213 double maxContent = histo.
maxBin(ratioMax, angleMax);
214 histo.
fill(ratioMax, angleMax, -maxContent);
217 LOGLS_DEBUG(_log,
" valMax " << maxContent <<
" ratio " << ratioMax <<
" angle " << angleMax);
219 minRatio = ratioMax - binr / 2;
220 maxRatio = ratioMax + binr / 2;
221 double minAngle = angleMax - bina / 2;
222 double maxAngle = angleMax + bina / 2;
228 for (segi1 = sList1.
begin(); segi1 != sList1.
end(); ++segi1) {
230 if (seg1->
r == 0)
continue;
231 for (segi2 = sList2.
begin(); segi2 != sList2.
end(); ++segi2) {
234 if (
ratio > maxRatio)
continue;
235 if (
ratio < minRatio)
239 if (angle < minAngle || angle > maxAngle)
continue;
244 for (
int iteration = 0; iteration < conditions.
maxTrialCount; iteration++) {
246 double maxval = historank.
maxBin(dr1, dr2);
248 historank.
fill(dr1, dr2, -maxval);
250 a_list->refineTransform(conditions.
nSigmas);
254 Solutions.
sort(DecreasingQuality);
260 LOGLS_DEBUG(_log,
"Best solution " << best->computeResidual() <<
" npairs " << best->size());
262 LOGLS_DEBUG(_log,
"Chi2 " << best->getChi2() <<
',' <<
" Number of solutions " << Solutions.
size());
277 const AstrometryTransform &transform,
278 const MatchConditions &conditions) {
279 if (list1.size() <= 4 || list2.size() <= 4) {
280 LOGL_FATAL(_log,
"ListMatchupRotShift_New : (at least) one of the lists is too short.");
284 SegmentList sList1(list1, conditions.nStarsList1, transform);
285 SegmentList sList2(list2, conditions.nStarsList2, AstrometryTransformIdentity());
293 int nBinsAngle = 180;
294 double angleOffset =
M_PI / nBinsAngle;
295 double minRatio = conditions.minSizeRatio();
296 double maxRatio = conditions.maxSizeRatio();
297 SparseHisto4d histo(nBinsR, minRatio, maxRatio, nBinsAngle, -
M_PI - angleOffset,
M_PI - angleOffset,
298 conditions.nStarsList1, 0., conditions.nStarsList1, conditions.nStarsList2, 0.,
299 conditions.nStarsList2, sList1.size() * sList2.size());
302 Segment *seg1, *seg2;
305 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
307 if (seg1->r == 0)
continue;
308 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
315 ratio = seg2->r / seg1->r;
316 if (ratio > maxRatio)
continue;
317 if (ratio < minRatio)
break;
318 angle = seg1->relativeAngle(seg2);
320 histo.fill(ratio, angle, seg1->s1rank + 0.5, seg2->s1rank + 0.5);
331 int oldMaxContent = 0;
333 for (
int i = 0; i < 4 * conditions.maxTrialCount;
337 int maxContent = histo.maxBin(pars);
338 if (maxContent == 0)
break;
339 if (conditions.printLevel >= 1) {
340 LOGLS_DEBUG(_log,
"ValMax " << maxContent <<
" ratio " << pars[0] <<
" angle " << pars[1]);
347 if (maxContent < oldMaxContent && i >= conditions.maxTrialCount)
break;
349 oldMaxContent = maxContent;
351 int rank1L1 = int(pars[2]);
352 int rank1L2 = int(pars[3]);
353 double minAngle, maxAngle;
354 histo.binLimits(pars, 0, minRatio, maxRatio);
355 histo.binLimits(pars, 1, minAngle, maxAngle);
359 for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
361 if (seg1->s1rank != rank1L1)
continue;
362 if (seg1->r == 0)
continue;
363 for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
365 if (seg2->s1rank != rank1L2)
continue;
367 if (a_list->size() == 0)
368 a_list->push_back(StarMatch(*(seg1->s1), *(seg2->s1), seg1->s1, seg2->s1));
369 ratio = seg2->r / seg1->r;
370 if (ratio > maxRatio)
continue;
371 if (ratio < minRatio)
373 angle = seg1->relativeAngle(seg2);
375 if (angle < minAngle || angle > maxAngle)
continue;
381 a_list->push_back(StarMatch(*(seg1->s2), *(seg2->s2), seg1->s2, seg2->s2));
387 if (
int(a_list->size()) != maxContent + 1) {
388 LOGLS_ERROR(_log,
"There is an internal inconsistency in ListMatchupRotShift.");
390 LOGLS_ERROR(_log,
"matches->size() = " << a_list->size());
392 a_list->refineTransform(conditions.nSigmas);
396 if (Solutions.size() == 0) {
397 LOGLS_ERROR(_log,
"Error In ListMatchup : not a single pair match.");
398 LOGLS_ERROR(_log,
"Probably, the relative scale of lists is not within bounds.");
399 LOGLS_ERROR(_log,
"min/max ratios: " << minRatio <<
' ' << maxRatio);
403 Solutions.sort(DecreasingQuality);
405 best.
swap(*Solutions.begin());
407 Solutions.pop_front();
408 if (conditions.printLevel >= 1) {
409 LOGLS_INFO(_log,
"Best solution " << best->computeResidual() <<
" npairs " << best->size());
411 LOGLS_INFO(_log,
"Chi2 " << best->getChi2() <<
", Number of solutions " << Solutions.size());
417 const AstrometryTransform &transform,
418 const MatchConditions &conditions) {
419 if (conditions.algorithm == 1)
420 return ListMatchupRotShift_Old(list1, list2, transform, conditions);
422 return ListMatchupRotShift_New(list1, list2, transform, conditions);
445 "unflipped Residual " << unflipped->computeResidual() <<
" nused " << unflipped->size());
446 LOGLS_DEBUG(_log,
"flipped Residual " << flipped->computeResidual() <<
" nused " << flipped->size());
448 if (DecreasingQuality(flipped, unflipped)) {
464 const AstrometryTransform &transform,
466 int ncomb = list1.size() * list2.size();
467 if (!ncomb)
return nullptr;
472 nx = (int)sqrt(ncomb);
474 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
478 for (s1 = list1.begin(); s1 != list1.end(); ++s1) {
479 transform.apply((*s1)->x, (*s1)->y, x1, y1);
480 for (s2 = list2.begin(); s2 != list2.end(); ++s2) {
481 histo.fill((*s2)->x - x1, (*s2)->y - y1);
484 double dx = 0, dy = 0;
485 histo.maxBin(dx, dy);
494 double maxShift,
double binSize) {
497 int ncomb = list1.
size() * list2.
size();
501 nx = (int)
sqrt(
double(ncomb));
504 nx = int(2 * maxShift / binSize + 0.5);
506 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
507 double binSizeNew = 2 * maxShift / nx;
512 for (s1 = list1.
begin(); s1 != list1.
end(); ++s1) {
513 transform.apply((*s1)->x, (*s1)->y, x1, y1);
517 histo.
fill(s2->x - x1, s2->y - y1);
522 for (
int i = 0; i < 4; ++i) {
523 double dx = 0, dy = 0;
528 auto raw_matches =
listMatchCollect(list1, list2, newGuess.get(), binSizeNew);
530 raw_matches->applyTransform(*matches, &
transform);
531 matches->setTransformOrder(1);
532 matches->refineTransform(3.);
535 Solutions.
sort(DecreasingQuality);
538 std::dynamic_pointer_cast<const AstrometryTransformLinear>(
539 Solutions.
front()->getTransform()))));
548 const AstrometryTransform *guess,
const double maxDist) {
552 const Point *p1 = (*si);
553 const Point p2 = guess->apply(*p1);
554 const BaseStar *neighbour = list2.findClosest(p2);
555 if (!neighbour)
continue;
556 double distance = p2.Distance(*neighbour);
557 if (distance < maxDist) {
558 matches->push_back(StarMatch(*p1, *neighbour, *si, neighbour));
560 matches->back().distance =
distance;
578 if (!neighbour)
continue;
581 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
583 matches->back().distance =
distance;
586 matches->setTransform(guess);
595 const AstrometryTransform *guess,
const double maxDist) {
596 const AstrometryTransform *bestTransform = guess;
600 m->setTransform(bestTransform);
601 m->refineTransform(3.);
602 LOGLS_INFO(_log,
"Iterating: resid " <<
m->computeResidual() <<
" size " <<
m->size());
604 (prevMatch &&
m->computeResidual() < prevMatch->computeResidual() * 0.999 &&
m->Chi2() > 0)) {
606 bestTransform = prevMatch->Transform();
616 const double maxDist) {
622 if (!neighbour)
continue;
623 double distance = p1->Distance(*neighbour);
625 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
627 matches->back().distance =
distance;
631 matches->setTransform(std::make_shared<AstrometryTransformIdentity>());
636 static bool is_transform_ok(
const StarMatchList *match,
double pixSizeRatio2,
const size_t nmin) {
637 if ((fabs(fabs(std::dynamic_pointer_cast<const AstrometryTransformLinear>(match->getTransform())
642 (match->size() > nmin))
645 match->printTransform();
650 static double transform_diff(
const BaseStarList &List,
const AstrometryTransform *T1,
651 const AstrometryTransform *T2) {
657 const BaseStar &s = **it;
658 T1->transformPosAndErrors(s, tf1);
660 double dx = tf1.x - tf2.x;
661 double dy = tf1.y - tf2.y;
662 diff2 += (tf1.vy * dx * dx + tf1.vx * dy * dy - 2 * tf1.vxy * dx * dy) /
663 (tf1.vx * tf1.vy - tf1.vxy * tf1.vxy);
666 if (count)
return diff2 / double(count);
670 static double median_distance(
const StarMatchList *match,
const AstrometryTransform *transform) {
671 size_t nstars = match->size();
674 for (
auto it = match->begin(); it != match->end(); ++it, ++ir)
675 *ir =
sqrt(
transform->apply(it->point1).computeDist2(it->point2));
676 sort(resid.begin(), resid.end());
677 return (nstars & 1) ? resid[nstars / 2] : (resid[nstars / 2 - 1] + resid[nstars / 2]) * 0.5;
689 LOGLS_INFO(_log,
"listMatchCombinatorial: find match between " << list1.
size() <<
" and " << list2.
size()
697 if (is_transform_ok(match.get(), pixSizeRatio2, nmin))
698 transform = match->getTransform()->clone();
700 LOGL_ERROR(_log,
"listMatchCombinatorial: direct transform failed, trying reverse");
702 if (is_transform_ok(match.get(), pixSizeRatio2, nmin))
712 LOGL_DEBUG(_log,
" listMatchCombinatorial: found the following transform.");
716 LOGL_ERROR(_log,
"listMatchCombinatorial: failed to find a transform");
722 const int maxOrder) {
728 const double brightDist = 2.;
729 const double fullDist = 4.;
730 const double nSigmas = 3.;
731 const size_t nStars = 500;
748 LOGLS_INFO(_log,
"listMatchRefine: start: med.resid " << median_distance(fullMatch.get(),
transform.get())
749 <<
" #match " << fullMatch->size());
752 auto curTransform = brightMatch->getTransform()->clone();
756 brightMatch->setTransformOrder(order);
757 brightMatch->refineTransform(nSigmas);
758 transDiff = transform_diff(list1, brightMatch->getTransform().get(), curTransform.get());
759 curTransform = brightMatch->getTransform()->clone();
760 brightMatch =
listMatchCollect(list1, list2, curTransform.get(), brightDist);
761 }
while (brightMatch->size() > nstarmin && transDiff > 0.05 && ++
iter < 5);
763 double prevChi2 = curChi2;
764 curChi2 =
computeChi2(*brightMatch, *curTransform) / brightMatch->size();
767 LOGLS_INFO(_log,
"listMatchRefine: order " << order <<
" med.resid "
768 << median_distance(fullMatch.get(), curTransform.get())
769 <<
" #match " << fullMatch->size());
770 if (((prevChi2 - curChi2) > 0.01 * curChi2) && curChi2 > 0) {
771 LOGLS_INFO(_log,
" listMatchRefine: order " << order <<
" was a better guess.");
772 transform = brightMatch->getTransform()->clone();
774 nstarmin = brightMatch->getTransform()->getNpar();
775 }
while (++order <= maxOrder);
#define LOGL_INFO(logger, message...)
#define LOGLS_INFO(logger, message)
#define LOGLS_ERROR(logger, message)
#define LOGL_ERROR(logger, message...)
#define LOGLS_DEBUG(logger, message)
#define LOGL_DEBUG(logger, message...)
#define LOGL_FATAL(logger, message...)
Fast locator in starlists.
table::Key< double > angle
Combinatorial searches for linear transformations to go from list1 to list2.
Iterator meant to traverse objects within some limiting distance.
This is an auxillary class for matching objects from starlists.
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.
Iterator beginScan(const Point &where, double maxDist) const
double maxBin(double &x, double &y) const
void binWidth(double &Hdx, double &Hdy) const
void fill(float x, float y, float weight=1.)
double Distance(const Point &other) const
SegmentList(const BaseStarList &list, const int nStar, const AstrometryTransform &transform=AstrometryTransformIdentity())
void cutTail(const int nKeep)
cuts the end of the std::list
void copyTo(StarList< Star > ©) const
clears copy and makes a copy of the std::list to copy
void fluxSort()
a model routine to sort the std::list
A hanger for star associations.
StarList< BaseStar > BaseStarList
std::list< std::unique_ptr< StarMatchList > > SolList
std::unique_ptr< StarMatchList > listMatchCollect(const BaseStarList &list1, const BaseStarList &list2, const AstrometryTransform *guess, const double maxDist)
assembles star matches.
std::unique_ptr< AstrometryTransform > listMatchCombinatorial(const BaseStarList &list1, const BaseStarList &list2, const MatchConditions &conditions=MatchConditions())
std::unique_ptr< AstrometryTransform > compose(AstrometryTransform const &left, AstrometryTransform const &right)
Returns a pointer to a composition of transforms, representing left(right()).
std::list< Segment >::iterator SegmentIterator
std::list< Segment >::const_iterator SegmentCIterator
SegmentPairList::const_iterator SegmentPairListCIterator
SegmentPairList::iterator SegmentPairListIterator
BaseStarList::const_iterator BaseStarCIterator
std::unique_ptr< AstrometryTransform > listMatchRefine(const BaseStarList &list1, const BaseStarList &list2, std::unique_ptr< AstrometryTransform > transform, const int maxOrder=3)
std::list< SegmentPair > SegmentPairList
double computeChi2(const StarMatchList &L, const AstrometryTransform &transform)
the actual chi2
std::unique_ptr< StarMatchList > matchSearchRotShift(BaseStarList &list1, BaseStarList &list2, const MatchConditions &conditions)
searches a geometrical transformation that goes from list1 to list2.
std::unique_ptr< StarMatchList > matchSearchRotShiftFlip(BaseStarList &list1, BaseStarList &list2, const MatchConditions &conditions)
same as above but searches also a flipped solution.
std::unique_ptr< AstrometryTransformLinear > listMatchupShift(const BaseStarList &list1, const BaseStarList &list2, const AstrometryTransform &transform, double maxShift, double binSize=0)
searches for a 2 dimensional shift using a very crude histogram method.
Class for a simple mapping implementing a generic AstrometryTransform.
Segment push_back(Segment ... args)
Segment sort(Segment ... args)
Parameters to be provided to combinatorial searches.
double maxSizeRatio() const
double minSizeRatio() const
std::shared_ptr< const BaseStar > s1
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 AstrometryTransform &transform)
std::shared_ptr< const BaseStar > s2
double relativeAngle(Segment *other)
SegmentPair(Segment *f, Segment *s)