31#define M_PI 3.14159265358979323846
44LOG_LOGGER _log =
LOG_GET(
"lsst.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);
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));
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());
276 const AstrometryTransform &transform,
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());
416 const AstrometryTransform &transform,
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)) {
463 const AstrometryTransform &transform,
465 int ncomb = list1.size() * list2.size();
466 if (!ncomb)
return nullptr;
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();
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);
556 if (distance < maxDist) {
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);
#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, 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::list< std::unique_ptr< StarMatchList > > SolList
std::unique_ptr< AstrometryTransform > listMatchRefine(const BaseStarList &list1, const BaseStarList &list2, std::unique_ptr< AstrometryTransform > transform, int maxOrder=3)
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 >::const_iterator SegmentCIterator
std::list< Segment >::iterator SegmentIterator
SegmentPairList::const_iterator SegmentPairListCIterator
std::unique_ptr< StarMatchList > listMatchCollect(const BaseStarList &list1, const BaseStarList &list2, const AstrometryTransform *guess, double maxDist)
assembles star matches.
BaseStarList::const_iterator BaseStarCIterator
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.
StarList< BaseStar > BaseStarList
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.
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)