31 #define M_PI 3.14159265358979323846
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());
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());
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)) {
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);
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();
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);
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, 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.
A base class for image defects.
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)