31#define M_PI 3.14159265358979323846
79 stream <<
" dx " << segment.
dx <<
" dy " << segment.
dy <<
" r " << segment.
r <<
std::endl;
94static bool DecreasingLength(
const Segment &first,
const Segment &second) {
return (first.r > second.r); }
107 for (
auto si2 = siStop; si2 != si1; --si2) {
110 sort(DecreasingLength);
130 for (
const auto & a_pair : pairList) {
131 if (a_pair.first->s1rank != rank1 || a_pair.second->s1rank != rank2)
continue;
135 if (matchList->size() == 0)
136 matchList->push_back(
StarMatch(
transform.apply(*(a_pair.first->s1)), *(a_pair.second->s1),
137 a_pair.first->s1, a_pair.second->s1));
139 matchList->push_back(
StarMatch(
transform.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);
182 for (segi1 = sList1.
begin(); segi1 != sList1.
end(); ++segi1) {
184 if (seg1->
r == 0)
continue;
185 for (segi2 = sList2.
begin(); segi2 != sList2.
end(); ++segi2) {
193 if (
ratio > maxRatio)
continue;
194 if (
ratio < minRatio)
break;
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;
227 for (segi1 = sList1.
begin(); segi1 != sList1.
end(); ++segi1) {
229 if (seg1->
r == 0)
continue;
230 for (segi2 = sList2.
begin(); segi2 != sList2.
end(); ++segi2) {
233 if (
ratio > maxRatio)
continue;
234 if (
ratio < minRatio)
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);
249 a_list->refineTransform(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());
277 const MatchConditions &conditions) {
278 if (list1.size() <= 4 || list2.size() <= 4) {
279 LOGL_FATAL(_log,
"ListMatchupRotShift_New : (at least) one of the lists is too short.");
283 SegmentList sList1(list1, conditions.nStarsList1,
transform);
284 SegmentList sList2(list2, conditions.nStarsList2, AstrometryTransformIdentity());
292 int nBinsAngle = 180;
293 double angleOffset =
M_PI / nBinsAngle;
294 double minRatio = conditions.minSizeRatio();
295 double maxRatio = conditions.maxSizeRatio();
296 SparseHisto4d histo(nBinsR, minRatio, maxRatio, nBinsAngle, -
M_PI - angleOffset,
M_PI - angleOffset,
297 conditions.nStarsList1, 0., conditions.nStarsList1, conditions.nStarsList2, 0.,
298 conditions.nStarsList2, sList1.size() * sList2.size());
301 Segment *seg1, *seg2;
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;
317 angle = seg1->relativeAngle(seg2);
319 histo.fill(ratio,
angle, seg1->s1rank + 0.5, seg2->s1rank + 0.5);
330 int oldMaxContent = 0;
332 for (
int i = 0; i < 4 * conditions.maxTrialCount;
336 int maxContent = histo.maxBin(pars);
337 if (maxContent == 0)
break;
338 if (conditions.printLevel >= 1) {
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;
367 a_list->push_back(StarMatch(*(seg1->s1), *(seg2->s1), seg1->s1, seg2->s1));
368 ratio = seg2->r / seg1->r;
369 if (ratio > maxRatio)
continue;
370 if (ratio < minRatio)
372 angle = seg1->relativeAngle(seg2);
374 if (angle < minAngle || angle > maxAngle)
continue;
380 a_list->push_back(StarMatch(*(seg1->s2), *(seg2->s2), seg1->s2, seg2->s2));
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->refineTransform(conditions.nSigmas);
395 if (Solutions.empty()) {
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);
404 best.
swap(*Solutions.begin());
406 Solutions.pop_front();
407 if (conditions.printLevel >= 1) {
408 LOGLS_INFO(_log,
"Best solution " << best->computeResidual() <<
" npairs " << best->size());
410 LOGLS_INFO(_log,
"Chi2 " << best->getChi2() <<
", Number of solutions " << Solutions.size());
417 const MatchConditions &conditions) {
418 if (conditions.algorithm == 1)
419 return ListMatchupRotShift_Old(list1, list2,
transform, conditions);
421 return ListMatchupRotShift_New(list1, list2,
transform, 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)) {
465 int ncomb = list1.size() * list2.size();
466 if (!ncomb)
return nullptr;
471 nx = (int)
sqrt(ncomb);
473 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
477 for (s1 = list1.begin(); s1 != list1.end(); ++s1) {
478 transform.apply((*s1)->x, (*s1)->y, x1, y1);
479 for (s2 = list2.begin(); s2 != list2.end(); ++s2) {
480 histo.fill((*s2)->x - x1, (*s2)->y - y1);
483 double dx = 0, dy = 0;
484 histo.maxBin(dx, dy);
493 double maxShift,
double binSize) {
496 int ncomb = list1.
size() * list2.
size();
500 nx = (int)
sqrt(
double(ncomb));
503 nx = int(2 * maxShift / binSize + 0.5);
505 Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
506 double binSizeNew = 2 * maxShift / nx;
511 for (s1 = list1.
begin(); s1 != list1.
end(); ++s1) {
512 transform.apply((*s1)->x, (*s1)->y, x1, y1);
516 histo.
fill(s2->x - x1, s2->y - y1);
521 for (
int i = 0; i < 4; ++i) {
522 double dx = 0, dy = 0;
527 auto raw_matches =
listMatchCollect(list1, list2, newGuess.get(), binSizeNew);
529 raw_matches->applyTransform(*matches, &
transform);
530 matches->setTransformOrder(1);
531 matches->refineTransform(3.);
534 Solutions.
sort(DecreasingQuality);
537 std::dynamic_pointer_cast<const AstrometryTransformLinear>(
538 Solutions.
front()->getTransform()))));
547 const AstrometryTransform *guess,
const double maxDist) {
551 const Point *p1 = (*si);
552 const Point p2 = guess->apply(*p1);
553 const BaseStar *neighbour = list2.findClosest(p2);
554 if (!neighbour)
continue;
555 double distance = p2.Distance(*neighbour);
557 matches->push_back(StarMatch(*p1, *neighbour, *si, neighbour));
559 matches->back().distance =
distance;
573 for (
const auto & si : list1) {
577 if (!neighbour)
continue;
580 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
582 matches->back().distance =
distance;
585 matches->setTransform(guess);
594 const AstrometryTransform *guess,
const double maxDist) {
595 const AstrometryTransform *bestTransform = guess;
599 m->setTransform(bestTransform);
600 m->refineTransform(3.);
601 LOGLS_INFO(_log,
"Iterating: resid " <<
m->computeResidual() <<
" size " <<
m->size());
603 (prevMatch &&
m->computeResidual() < prevMatch->computeResidual() * 0.999 &&
m->Chi2() > 0)) {
605 bestTransform = prevMatch->Transform();
615 const double maxDist) {
618 for (
const auto & si : list1) {
621 if (!neighbour)
continue;
622 double distance = p1->Distance(*neighbour);
624 matches->push_back(
StarMatch(*p1, *neighbour, p1, neighbour));
626 matches->back().distance =
distance;
630 matches->setTransform(std::make_shared<AstrometryTransformIdentity>());
635static bool is_transform_ok(
const StarMatchList *match,
double pixSizeRatio2,
const size_t nmin) {
636 if ((
fabs(
fabs(std::dynamic_pointer_cast<const AstrometryTransformLinear>(match->getTransform())
641 (match->size() > nmin))
644 match->printTransform();
649static double transform_diff(
const BaseStarList &List,
const AstrometryTransform *T1,
650 const AstrometryTransform *T2) {
655 for (
const auto & it : List) {
656 const BaseStar &s = *it;
657 T1->transformPosAndErrors(s, tf1);
659 double dx = tf1.x - tf2.x;
660 double dy = tf1.y - tf2.y;
661 diff2 += (tf1.vy * dx * dx + tf1.vx * dy * dy - 2 * tf1.vxy * dx * dy) /
662 (tf1.vx * tf1.vy - tf1.vxy * tf1.vxy);
669static double median_distance(
const StarMatchList *match,
const AstrometryTransform *
transform) {
670 size_t nstars = match->size();
672 auto ir = resid.begin();
673 for (
auto it = match->begin(); it != match->end(); ++it, ++ir)
674 *ir =
sqrt(
transform->apply(it->point1).computeDist2(it->point2));
675 sort(resid.begin(), resid.end());
676 return (nstars & 1) ? resid[nstars / 2] : (resid[nstars / 2 - 1] + resid[nstars / 2]) * 0.5;
688 LOGLS_INFO(_log,
"listMatchCombinatorial: find match between " << list1.
size() <<
" and " << list2.
size()
696 if (is_transform_ok(match.get(), pixSizeRatio2, nmin))
697 transform = match->getTransform()->clone();
699 LOGL_ERROR(_log,
"listMatchCombinatorial: direct transform failed, trying reverse");
701 if (is_transform_ok(match.get(), pixSizeRatio2, nmin))
711 LOGL_DEBUG(_log,
" listMatchCombinatorial: found the following transform.");
715 LOGL_ERROR(_log,
"listMatchCombinatorial: failed to find a transform");
721 const int maxOrder) {
727 const double brightDist = 2.;
728 const double fullDist = 4.;
729 const double nSigmas = 3.;
730 const size_t nStars = 500;
747 LOGLS_INFO(_log,
"listMatchRefine: start: med.resid " << median_distance(fullMatch.get(),
transform.get())
748 <<
" #match " << fullMatch->size());
751 auto curTransform = brightMatch->getTransform()->clone();
755 brightMatch->setTransformOrder(
order);
756 brightMatch->refineTransform(nSigmas);
757 transDiff = transform_diff(list1, brightMatch->getTransform().get(), curTransform.get());
758 curTransform = brightMatch->getTransform()->clone();
759 brightMatch =
listMatchCollect(list1, list2, curTransform.get(), brightDist);
760 }
while (brightMatch->size() > nstarmin && transDiff > 0.05 && ++iter < 5);
762 double prevChi2 = curChi2;
763 curChi2 =
computeChi2(*brightMatch, *curTransform) / brightMatch->size();
767 << median_distance(fullMatch.get(), curTransform.get())
768 <<
" #match " << fullMatch->size());
769 if (((prevChi2 - curChi2) > 0.01 * curChi2) && curChi2 > 0) {
770 LOGLS_INFO(_log,
" listMatchRefine: order " <<
order <<
" was a better guess.");
771 transform = brightMatch->getTransform()->clone();
773 nstarmin = brightMatch->getTransform()->getNpar();
774 }
while (++
order <= maxOrder);
Fast locator in starlists.
table::Key< double > angle
Combinatorial searches for linear transformations to go from list1 to list2.
LSST DM logging module built on log4cxx.
#define LOG_GET(logger)
Returns a Log object associated with logger.
#define LOGL_INFO(logger, message...)
Log a info-level message using a varargs/printf style interface.
#define LOGLS_INFO(logger, message)
Log a info-level message using an iostream-based interface.
#define LOGLS_ERROR(logger, message)
Log a error-level message using an iostream-based interface.
#define LOGL_ERROR(logger, message...)
Log a error-level message using a varargs/printf style interface.
#define LOGLS_DEBUG(logger, message)
Log a debug-level message using an iostream-based interface.
#define LOGL_DEBUG(logger, message...)
Log a debug-level message using a varargs/printf style interface.
#define LOGL_FATAL(logger, message...)
Log a fatal-level message using a varargs/printf style interface.
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, 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, int nStar, const AstrometryTransform &transform=AstrometryTransformIdentity())
void cutTail(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.
std::unique_ptr< AstrometryTransform > listMatchRefine(const BaseStarList &list1, const BaseStarList &list2, std::unique_ptr< AstrometryTransform > transform, int maxOrder=3)
std::list< Segment >::const_iterator SegmentCIterator
BaseStarList::const_iterator BaseStarCIterator
std::list< std::unique_ptr< StarMatchList > > SolList
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::unique_ptr< StarMatchList > listMatchCollect(const BaseStarList &list1, const BaseStarList &list2, const AstrometryTransform *guess, double maxDist)
assembles star matches.
StarList< BaseStar > BaseStarList
double computeChi2(const StarMatchList &L, const AstrometryTransform &transform)
the actual chi2
SegmentPairList::const_iterator SegmentPairListCIterator
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::list< Segment >::iterator SegmentIterator
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.
SegmentPairList::iterator SegmentPairListIterator
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
double relativeAngle(Segment *other) const
Segment(std::shared_ptr< const BaseStar > star1, std::shared_ptr< const BaseStar > star2, const int star1Rank, const AstrometryTransform &transform)
friend std::ostream & operator<<(std::ostream &stream, const Segment &segment)
std::shared_ptr< const BaseStar > s2
SegmentPair(Segment *f, Segment *s)