lsst.jointcal  14.0-26-gc4bc114+2
ListMatch.cc
Go to the documentation of this file.
1 #include <iostream>
2 #include <cmath>
3 #include <list>
4 #include <memory>
5 #include <algorithm>
6 #ifndef M_PI
7 #define M_PI 3.14159265358979323846 /* pi */
8 #endif
9 
10 #include "lsst/log/Log.h"
11 #include "lsst/jointcal/BaseStar.h"
13 #include "lsst/jointcal/Gtransfo.h"
14 #include "lsst/jointcal/Histo2d.h"
15 #include "lsst/jointcal/Histo4d.h"
18 
19 namespace {
20 LOG_LOGGER _log = LOG_GET("jointcal.ListMatch");
21 }
22 
23 namespace lsst {
24 namespace jointcal {
25 
26 // cuts.. limits, etc for combinatorial match
27 
28 /* a Segment is a pair of stars form the same image. it is used for matching starlists */
29 
30 struct Segment {
31  /* data */
32  double r, dx, dy;
34  int s1rank;
35 
36  /* constructor (could set last argument to identity by default) */
38  const Gtransfo &gtransfo) {
39  s1rank = star1Rank;
40  s1 = std::move(star1);
41  s2 = std::move(star2);
42  Point P1 = gtransfo.apply(*star1);
43  Point P2 = gtransfo.apply(*star2);
44  dx = P2.x - P1.x;
45  dy = P2.y - P1.y;
46  r = sqrt(dx * dx + dy * dy);
47  }
48 
49  /* arg(other/(*this)) if considered as complex(dx,dy) */
50  double relativeAngle(Segment *other) {
51  return atan2(other->dx * dy - dx * other->dy, dx * other->dx + dy * other->dy);
52  }
53 
54  friend std::ostream &operator<<(std::ostream &stream, const Segment &segment) {
55  stream << " dx " << segment.dx << " dy " << segment.dy << " r " << segment.r << std::endl;
56  return stream;
57  }
58 };
59 
60 class SegmentList : public std::list<Segment> {
61 public:
62  // SegmentList(const BaseStarList &list, const int nStar);
63  SegmentList(const BaseStarList &list, const int nStar, const Gtransfo &gtransfo = GtransfoIdentity());
64 };
65 
68 
69 static bool DecreasingLength(const Segment &first, const Segment &second) { return (first.r > second.r); }
70 
71 SegmentList::SegmentList(const BaseStarList &list, const int nStars, const Gtransfo &gtransfo) {
72  BaseStarCIterator siStop;
73 
74  /* find the fence */
75  siStop = list.begin();
76  int limit = std::min(nStars, int(list.size())) - 1; // -1 because test happens after incrementation
77  for (int count = 0; count < limit; count++) ++siStop;
78 
79  // iterate on star pairs
80  int rank = 0;
81  for (auto si1 = list.begin(); si1 != siStop; ++si1, rank++)
82  for (auto si2 = siStop; si2 != si1; --si2) {
83  push_back(Segment(*si1, *si2, rank, gtransfo));
84  }
85  sort(DecreasingLength); /* allows a break in loops */
86 }
87 
88 //#include <pair>
89 
90 struct SegmentPair : public std::pair<Segment *, Segment *> {
91  SegmentPair(Segment *f, Segment *s) : std::pair<Segment *, Segment *>(f, s){};
92 };
93 
95 typedef SegmentPairList::iterator SegmentPairListIterator;
96 typedef SegmentPairList::const_iterator SegmentPairListCIterator;
97 
98 static std::unique_ptr<StarMatchList> MatchListExtract(const SegmentPairList &pairList, int rank1, int rank2,
99  const Gtransfo &gtransfo) {
100  /* first Select in the segment pairs list the ones which make use of star rank1 in segment1
101  and star s2 in segment2 */
102 
104 
105  for (SegmentPairListCIterator spi = pairList.begin(); spi != pairList.end(); spi++) {
106  const SegmentPair &a_pair = *spi;
107  if (a_pair.first->s1rank != rank1 || a_pair.second->s1rank != rank2) continue;
108  /* now we store as star matches both ends of segment pairs ,
109  but only once the beginning of segments because they all have the same,
110  given the selection 3 lines above */
111  if (matchList->size() == 0)
112  matchList->push_back(StarMatch(gtransfo.apply(*(a_pair.first->s1)), *(a_pair.second->s1),
113  a_pair.first->s1, a_pair.second->s1));
114  /* always store the match at end */
115  matchList->push_back(StarMatch(gtransfo.apply(*(a_pair.first->s2)), *(a_pair.second->s2),
116  a_pair.first->s2, a_pair.second->s2));
117  }
118  return matchList;
119 }
120 
121 static bool DecreasingQuality(const std::unique_ptr<StarMatchList> &first,
123  int idiff = first->size() - second->size();
124  if (idiff != 0)
125  return (idiff > 0);
126  else
127  return (first->getDist2() < second->getDist2());
128 }
129 
130 /* many matching solutions (StarMatchList) will be compared. Store them in a SolList : */
131 
133 
134 /* This one searches a general transformation by histogramming the relative size and orientation
135 of star pairs ( Segment's) built from the 2 lists */
136 
137 static std::unique_ptr<StarMatchList> ListMatchupRotShift_Old(BaseStarList &list1, BaseStarList &list2,
138  const Gtransfo &gtransfo,
139  const MatchConditions &conditions) {
140  SegmentList sList1(list1, conditions.nStarsList1, gtransfo);
141  SegmentList sList2(list2, conditions.nStarsList2, GtransfoIdentity());
142 
143  /* choose the binning of the histogram so that
144  1: ratio = 1 and rotation angle = n * (pi/2) are bin centers. since
145  the angle is computed using atan2, its range is [-pi,pi],
146  and the histogram range is [-pi-eps, pi-eps], so
147  if (angle>pi- angleOffset) angle -= 2*pi before filling. */
148  int nBinsR = 21;
149  int nBinsAngle = 180; /* can be divided by 4 */
150  double angleOffset = M_PI / nBinsAngle;
151  double minRatio = conditions.minSizeRatio();
152  double maxRatio = conditions.maxSizeRatio();
153  Histo2d histo(nBinsR, minRatio, maxRatio, nBinsAngle, -M_PI - angleOffset, M_PI - angleOffset);
154 
155  SegmentIterator segi1, segi2;
156  Segment *seg1, *seg2;
157  double ratio, angle;
158  for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
159  seg1 = &(*segi1);
160  if (seg1->r == 0) continue;
161  for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
162  seg2 = &(*segi2);
163  /* if one considers the 2 segments as complex numbers z1 and z2, ratio=mod(z1/z2) and angle =
164  * arg(z1/z2) */
165  /* I did not put a member function in Segment to compute both because we apply a cut on ratio
166  before actually
167  computing the angle (which involves a call to atan2 (expensive)) */
168  ratio = seg2->r / seg1->r;
169  if (ratio > maxRatio) continue;
170  if (ratio < minRatio) break; /* use th fact that segment lists are sorted by decresing length */
171  angle = seg1->relativeAngle(seg2);
172  if (angle > M_PI - angleOffset) angle -= 2. * M_PI;
173  histo.fill(ratio, angle);
174  }
175  }
176  double binr, bina;
177  histo.binWidth(binr, bina);
178 
179  SolList Solutions;
180  /* now we want to find in the (r,theta) bins that have the highest counts, the star pair
181  (one in l1, one in list2) that contribute to the largest number of segment pairs in this bin :
182  so, we histogram a couple of integer that uniquely defines the stars, for the segment pairs
183  that contribute to the maximum bin. We choose to histogram the rank of s1 of segment 1
184  versus the rank of s1 for segment 2 */
185 
186  for (int i = 0; i < conditions.maxTrialCount; ++i) {
187  double ratioMax, angleMax;
188  double maxContent = histo.maxBin(ratioMax, angleMax);
189  histo.fill(ratioMax, angleMax, -maxContent);
190 
191  if (conditions.printLevel >= 1)
192  LOGLS_DEBUG(_log, " valMax " << maxContent << " ratio " << ratioMax << " angle " << angleMax);
193 
194  minRatio = ratioMax - binr / 2;
195  maxRatio = ratioMax + binr / 2;
196  double minAngle = angleMax - bina / 2;
197  double maxAngle = angleMax + bina / 2;
198  SegmentPairList pairList;
199  Histo2d historank(conditions.nStarsList1, 0., conditions.nStarsList1, conditions.nStarsList2, 0.,
200  conditions.nStarsList2);
201  /* reloop on segment pairs to select the ones in this specific bin */
202 
203  for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
204  seg1 = &(*segi1);
205  if (seg1->r == 0) continue;
206  for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
207  seg2 = &(*segi2);
208  ratio = seg2->r / seg1->r;
209  if (ratio > maxRatio) continue;
210  if (ratio < minRatio)
211  break; /* use the fact that segment lists are sorted by decresing length */
212  angle = seg1->relativeAngle(seg2);
213  if (angle > M_PI - angleOffset) angle -= 2. * M_PI;
214  if (angle < minAngle || angle > maxAngle) continue;
215  pairList.push_back(SegmentPair(seg1, seg2)); /* store the match */
216  historank.fill(seg1->s1rank + 0.5, seg2->s1rank + 0.5);
217  }
218  }
219  for (int iteration = 0; iteration < conditions.maxTrialCount; iteration++) {
220  double dr1, dr2;
221  double maxval = historank.maxBin(dr1, dr2);
222  /* set this bin to zero so that next iteration will find next maximum */
223  historank.fill(dr1, dr2, -maxval);
224  auto a_list = MatchListExtract(pairList, int(dr1), int(dr2), GtransfoIdentity());
225  a_list->refineTransfo(conditions.nSigmas); // mandatory for the sorting fields to be filled
226  Solutions.push_back(std::move(a_list));
227  }
228  } /* end of loop on (r,theta) bins */
229  Solutions.sort(DecreasingQuality);
231  best.swap(*Solutions.begin());
232  /* remove the first one from the list */
233  Solutions.pop_front();
234  if (conditions.printLevel >= 1) {
235  LOGLS_DEBUG(_log, "Best solution " << best->computeResidual() << " npairs " << best->size());
236  LOGLS_DEBUG(_log, *(best->getTransfo()));
237  LOGLS_DEBUG(_log, "Chi2 " << best->getChi2() << ',' << " Number of solutions " << Solutions.size());
238  }
239  return best;
240 }
241 
242 /* this matching routine searches brutally a match between lists in
243  the 4 parameter space: size ratio, rotation angle, x and y
244  shifts. This is done by histogramming where combinations of four
245  objets (2 on each list) fall in this 4 parameter space.
246 
247  One trick is that rather than using actual offsets, we histogram
248  object indices of the combination:
249 */
250 
251 static std::unique_ptr<StarMatchList> ListMatchupRotShift_New(BaseStarList &list1, BaseStarList &list2,
252  const Gtransfo &gtransfo,
253  const MatchConditions &conditions) {
254  if (list1.size() <= 4 || list2.size() <= 4) {
255  LOGL_FATAL(_log, "ListMatchupRotShift_New : (at least) one of the lists is too short.");
256  return nullptr;
257  }
258 
259  SegmentList sList1(list1, conditions.nStarsList1, gtransfo);
260  SegmentList sList2(list2, conditions.nStarsList2, GtransfoIdentity());
261 
262  /* choose the binning of the histogram so that
263  1: ratio = 1 and rotation angle = n * (pi/2) are bin centers. since
264  the angle is computed using atan2, its range is [-pi,pi],
265  and the histogram range is [-pi-eps, pi-eps], so
266  if (angle>pi- angleOffset) angle -= 2*pi before filling. */
267  int nBinsR = 21;
268  int nBinsAngle = 180; /* can be divided by 4 */
269  double angleOffset = M_PI / nBinsAngle;
270  double minRatio = conditions.minSizeRatio();
271  double maxRatio = conditions.maxSizeRatio();
272  SparseHisto4d histo(nBinsR, minRatio, maxRatio, nBinsAngle, -M_PI - angleOffset, M_PI - angleOffset,
273  conditions.nStarsList1, 0., conditions.nStarsList1, conditions.nStarsList2, 0.,
274  conditions.nStarsList2, sList1.size() * sList2.size());
275 
276  SegmentIterator segi1, segi2;
277  Segment *seg1, *seg2;
278  double ratio, angle;
279 
280  for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
281  seg1 = &(*segi1);
282  if (seg1->r == 0) continue;
283  for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
284  seg2 = &(*segi2);
285  /* if one considers the 2 segments as complex numbers z1 and z2, ratio=mod(z1/z2) and angle =
286  * arg(z1/z2) */
287  /* I did not put a member function in Segment to compute both because we apply a cut on ratio
288  before actually
289  computing the angle (which involves a call to atan2 (expensive)) */
290  ratio = seg2->r / seg1->r;
291  if (ratio > maxRatio) continue;
292  if (ratio < minRatio) break; /* use th fact that segment lists are sorted by decresing length */
293  angle = seg1->relativeAngle(seg2);
294  if (angle > M_PI - angleOffset) angle -= 2. * M_PI;
295  histo.fill(ratio, angle, seg1->s1rank + 0.5, seg2->s1rank + 0.5);
296  }
297  }
298 
299  SolList Solutions;
300  /* now we find the highest bins of the histogram, and recover the original objects.
301  This involves actually re-looping on the combinations, but it is much
302  faster that the original histogram filling loop, since we only compute
303  angle and ratio for Segments that have the right first object
304  */
305 
306  int oldMaxContent = 0;
307 
308  for (int i = 0; i < 4 * conditions.maxTrialCount;
309  ++i) // leave a limit to make avoid (almost) infinite loops
310  {
311  double pars[4];
312  int maxContent = histo.maxBin(pars);
313  if (maxContent == 0) break;
314  if (conditions.printLevel >= 1) {
315  LOGLS_DEBUG(_log, "ValMax " << maxContent << " ratio " << pars[0] << " angle " << pars[1]);
316  }
317  histo.zeroBin(pars);
318  if (i > 0) { /* the match possibilities come out in a random order when they have the same content.
319  so, we stop investigating guesses when the content goes down AND the requested search
320  depth
321  (maxTrialCount) is reached */
322  if (maxContent < oldMaxContent && i >= conditions.maxTrialCount) break;
323  }
324  oldMaxContent = maxContent;
325  /* reloop on segment pairs to select the ones in this specific bin */
326  int rank1L1 = int(pars[2]);
327  int rank1L2 = int(pars[3]);
328  double minAngle, maxAngle;
329  histo.binLimits(pars, 0, minRatio, maxRatio);
330  histo.binLimits(pars, 1, minAngle, maxAngle);
331 
333 
334  for (segi1 = sList1.begin(); segi1 != sList1.end(); ++segi1) {
335  seg1 = &(*segi1);
336  if (seg1->s1rank != rank1L1) continue;
337  if (seg1->r == 0) continue;
338  for (segi2 = sList2.begin(); segi2 != sList2.end(); ++segi2) {
339  seg2 = &(*segi2);
340  if (seg2->s1rank != rank1L2) continue;
341  // push in the list the match corresponding to end number 1 of segments
342  if (a_list->size() == 0)
343  a_list->push_back(StarMatch(*(seg1->s1), *(seg2->s1), seg1->s1, seg2->s1));
344  ratio = seg2->r / seg1->r;
345  if (ratio > maxRatio) continue;
346  if (ratio < minRatio)
347  break; /* use the fact that segment lists are sorted by decresing length */
348  angle = seg1->relativeAngle(seg2);
349  if (angle > M_PI - angleOffset) angle -= 2. * M_PI;
350  if (angle < minAngle || angle > maxAngle) continue;
351  /* here we have 2 segments which have the right
352  - length ratio
353  - relative angle
354  - first objects (objects on the end number 1).
355  The objects on the end number 2 are the actual matches : */
356  a_list->push_back(StarMatch(*(seg1->s2), *(seg2->s2), seg1->s2, seg2->s2));
357  }
358  }
359 
360  // a basic check for sanity of the algorithm :
361 
362  if (int(a_list->size()) != maxContent + 1) {
363  LOGLS_ERROR(_log, "There is an internal inconsistency in ListMatchupRotShift.");
364  LOGLS_ERROR(_log, "maxContent = " << maxContent);
365  LOGLS_ERROR(_log, "matches->size() = " << a_list->size());
366  }
367  a_list->refineTransfo(conditions.nSigmas);
368  Solutions.push_back(std::move(a_list));
369  }
370 
371  if (Solutions.size() == 0) {
372  LOGLS_ERROR(_log, "Error In ListMatchup : not a single pair match.");
373  LOGLS_ERROR(_log, "Probably, the relative scale of lists is not within bounds.");
374  LOGLS_ERROR(_log, "min/max ratios: " << minRatio << ' ' << maxRatio);
375  return nullptr;
376  }
377 
378  Solutions.sort(DecreasingQuality);
380  best.swap(*Solutions.begin());
381  /* remove the first one from the list */
382  Solutions.pop_front();
383  if (conditions.printLevel >= 1) {
384  LOGLS_INFO(_log, "Best solution " << best->computeResidual() << " npairs " << best->size());
385  LOGLS_INFO(_log, *(best->getTransfo()));
386  LOGLS_INFO(_log, "Chi2 " << best->getChi2() << ", Number of solutions " << Solutions.size());
387  }
388  return best;
389 }
390 
391 static std::unique_ptr<StarMatchList> ListMatchupRotShift(BaseStarList &list1, BaseStarList &list2,
392  const Gtransfo &gtransfo,
393  const MatchConditions &conditions) {
394  if (conditions.algorithm == 1)
395  return ListMatchupRotShift_Old(list1, list2, gtransfo, conditions);
396  else
397  return ListMatchupRotShift_New(list1, list2, gtransfo, conditions);
398 }
399 
401  const MatchConditions &conditions) {
402  list1.fluxSort();
403  list2.fluxSort();
404 
405  return ListMatchupRotShift(list1, list2, GtransfoIdentity(), conditions);
406 }
407 
409  const MatchConditions &conditions) {
410  list1.fluxSort();
411  list2.fluxSort();
412 
413  GtransfoLin flip(0, 0, 1, 0, 0, -1);
414  std::unique_ptr<StarMatchList> flipped(ListMatchupRotShift(list1, list2, flip, conditions));
416  ListMatchupRotShift(list1, list2, GtransfoIdentity(), conditions));
417  if (!flipped || !unflipped) return std::unique_ptr<StarMatchList>(nullptr);
418  if (conditions.printLevel >= 1) {
419  LOGLS_DEBUG(_log,
420  "unflipped Residual " << unflipped->computeResidual() << " nused " << unflipped->size());
421  LOGLS_DEBUG(_log, "flipped Residual " << flipped->computeResidual() << " nused " << flipped->size());
422  }
423  if (DecreasingQuality(flipped, unflipped)) {
424  if (conditions.printLevel >= 1) LOGL_DEBUG(_log, "Keeping flipped solution.");
425  // One should NOT apply the flip to the result because the matchlist
426  // (even the flipped one) contains the actual coordinates of stars.
427  // MatchListExtract is always called with GtransfoIdentity() as last parameter
428  return flipped;
429  } else {
430  if (conditions.printLevel >= 1) LOGL_DEBUG(_log, "Keeping unflipped solution.");
431  return unflipped;
432  }
433 }
434 
435 #ifdef STORAGE
436 // timing : 2.5 s for l1 of 1862 objects and l2 of 2617 objects
438  const Gtransfo &gtransfo, double maxShift) {
439  int ncomb = list1.size() * list2.size();
440  if (!ncomb) return nullptr;
441  int nx;
442  if (ncomb > 10000)
443  nx = 100;
444  else
445  nx = (int)sqrt(ncomb);
446 
447  Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
448 
450  double x1, y1;
451  for (s1 = list1.begin(); s1 != list1.end(); ++s1) {
452  gtransfo.apply((*s1)->x, (*s1)->y, x1, y1);
453  for (s2 = list2.begin(); s2 != list2.end(); ++s2) {
454  histo.fill((*s2)->x - x1, (*s2)->y - y1);
455  }
456  }
457  double dx = 0, dy = 0;
458  histo.maxBin(dx, dy);
460 }
461 #endif /*STORAGE*/
462 
463 // timing : 140 ms for l1 of 1862 objects and l2 of 2617 objects (450 MHz, "-O4") maxShift = 200.
465  const Gtransfo &gtransfo, double maxShift, double binSize) {
466  int nx;
467  if (binSize == 0) {
468  int ncomb = list1.size() * list2.size();
469  if (ncomb > 10000)
470  nx = 100;
471  else
472  nx = (int)sqrt(double(ncomb));
473  if (!ncomb) return std::unique_ptr<GtransfoLin>(nullptr);
474  } else
475  nx = int(2 * maxShift / binSize + 0.5);
476 
477  Histo2d histo(nx, -maxShift, maxShift, nx, -maxShift, maxShift);
478  double binSizeNew = 2 * maxShift / nx;
479 
481  FastFinder finder(list2);
482  double x1, y1;
483  for (s1 = list1.begin(); s1 != list1.end(); ++s1) {
484  gtransfo.apply((*s1)->x, (*s1)->y, x1, y1);
485  FastFinder::Iterator it = finder.beginScan(Point(x1, y1), maxShift);
486  while (*it) {
487  auto s2 = *it;
488  histo.fill(s2->x - x1, s2->y - y1);
489  ++it;
490  }
491  }
492  SolList Solutions;
493  for (int i = 0; i < 4; ++i) {
494  double dx = 0, dy = 0;
495  double count = histo.maxBin(dx, dy);
496  histo.fill(dx, dy, -count); // zero the maxbin
497  GtransfoLinShift shift(dx, dy);
498  auto newGuess = gtransfoCompose(&shift, &gtransfo);
499  auto raw_matches = listMatchCollect(list1, list2, newGuess.get(), binSizeNew);
501  raw_matches->applyTransfo(*matches, &gtransfo);
502  matches->setTransfoOrder(1);
503  matches->refineTransfo(3.);
504  Solutions.push_back(std::move(matches));
505  }
506  Solutions.sort(DecreasingQuality);
507  std::unique_ptr<GtransfoLin> best(new GtransfoLin(*std::const_pointer_cast<GtransfoLin>(
508  std::dynamic_pointer_cast<const GtransfoLin>(Solutions.front()->getTransfo()))));
509  return best;
510 }
511 
512 #ifdef STORAGE
513 
514 // this is the old fashioned way...
515 
516 std::unique_ptr<StarMatchList> listMatchCollect_Slow(const BaseStarList &list1, const BaseStarList &list2,
517  const Gtransfo *guess, const double maxDist) {
519  /****** Collect ***********/
520  for (BaseStarCIterator si = list1.begin(); si != list1.end(); ++si) {
521  const Point *p1 = (*si);
522  const Point p2 = guess->apply(*p1);
523  const BaseStar *neighbour = list2.findClosest(p2);
524  if (!neighbour) continue;
525  double distance = p2.Distance(*neighbour);
526  if (distance < maxDist) {
527  matches->push_back(StarMatch(*p1, *neighbour, *si, neighbour));
528  // assign the distance, since we have it in hand:
529  matches->back().distance = distance;
530  }
531  }
532  return matches;
533 }
534 #endif
535 
536 // here is the real active routine:
537 
539  const Gtransfo *guess, const double maxDist) {
541  /****** Collect ***********/
542  FastFinder finder(list2);
543  for (BaseStarCIterator si = list1.begin(); si != list1.end(); ++si) {
544  auto p1 = (*si);
545  Point p2 = guess->apply(*p1);
546  auto neighbour = finder.findClosest(p2, maxDist);
547  if (!neighbour) continue;
548  double distance = p2.Distance(*neighbour);
549  if (distance < maxDist) {
550  matches->push_back(StarMatch(*p1, *neighbour, p1, neighbour));
551  // assign the distance, since we have it in hand:
552  matches->back().distance = distance;
553  }
554  }
555  matches->setTransfo(guess);
556 
557  return matches;
558 }
559 
560 #ifdef STORAGE
561 // unused
563 std::unique_ptr<StarMatchList> CollectAndFit(const BaseStarList &list1, const BaseStarList &list2,
564  const Gtransfo *guess, const double maxDist) {
565  const Gtransfo *bestTransfo = guess;
567  while (true) {
568  auto m = listMatchCollect(list1, list2, bestTransfo, maxDist);
569  m->setTransfo(bestTransfo);
570  m->refineTransfo(3.);
571  LOGLS_INFO(_log, "Iterating: resid " << m->computeResidual() << " size " << m->size());
572  if (!prevMatch ||
573  (prevMatch && m->computeResidual() < prevMatch->computeResidual() * 0.999 && m->Chi2() > 0)) {
574  prevMatch.swap(m);
575  bestTransfo = prevMatch->Transfo();
576  } else {
577  break;
578  }
579  }
580  return prevMatch;
581 }
582 #endif
583 
585  const double maxDist) {
587  FastFinder finder(list2);
588  for (BaseStarCIterator si = list1.begin(); si != list1.end(); ++si) {
589  auto p1 = (*si);
590  auto neighbour = finder.findClosest(*p1, maxDist);
591  if (!neighbour) continue;
592  double distance = p1->Distance(*neighbour);
593  if (distance < maxDist) {
594  matches->push_back(StarMatch(*p1, *neighbour, p1, neighbour));
595  // assign the distance, since we have it in hand:
596  matches->back().distance = distance;
597  }
598  }
599 
600  matches->setTransfo(std::make_shared<GtransfoIdentity>());
601 
602  return matches;
603 }
604 
605 static bool is_transfo_ok(const StarMatchList *match, double pixSizeRatio2, const size_t nmin) {
606  if ((fabs(fabs(std::dynamic_pointer_cast<const GtransfoLin>(match->getTransfo())->determinant()) -
607  pixSizeRatio2) /
608  pixSizeRatio2 <
609  0.2) &&
610  (match->size() > nmin))
611  return true;
612  LOGL_ERROR(_log, "transfo is not ok!");
613  match->dumpTransfo();
614  return false;
615 }
616 
617 // utility to check current transfo difference
618 static double transfo_diff(const BaseStarList &List, const Gtransfo *T1, const Gtransfo *T2) {
619  double diff2 = 0;
620  FatPoint tf1;
621  Point tf2;
622  int count = 0;
623  for (BaseStarCIterator it = List.begin(); it != List.end(); ++it) {
624  const BaseStar &s = **it;
625  T1->transformPosAndErrors(s, tf1);
626  T2->apply(s, tf2);
627  double dx = tf1.x - tf2.x;
628  double dy = tf1.y - tf2.y;
629  diff2 += (tf1.vy * dx * dx + tf1.vx * dy * dy - 2 * tf1.vxy * dx * dy) /
630  (tf1.vx * tf1.vy - tf1.vxy * tf1.vxy);
631  count++;
632  }
633  if (count) return diff2 / double(count);
634  return 0;
635 }
636 
637 static double median_distance(const StarMatchList *match, const Gtransfo *transfo) {
638  size_t nstars = match->size();
639  std::vector<double> resid(nstars);
641  for (auto it = match->begin(); it != match->end(); ++it, ++ir)
642  *ir = sqrt(transfo->apply(it->point1).computeDist2(it->point2));
643  sort(resid.begin(), resid.end());
644  return (nstars & 1) ? resid[nstars / 2] : (resid[nstars / 2 - 1] + resid[nstars / 2]) * 0.5;
645 }
646 
648  const MatchConditions &conditions) {
649  BaseStarList list1, list2;
650  List1.copyTo(list1);
651  list1.fluxSort();
652  List2.copyTo(list2);
653  list2.fluxSort();
654 
655  LOGLS_INFO(_log, "listMatchCombinatorial: find match between " << list1.size() << " and " << list2.size()
656  << " stars...");
657  auto match = matchSearchRotShiftFlip(list1, list2, conditions);
658  double pixSizeRatio2 = std::pow(conditions.sizeRatio, 2);
659  size_t nmin =
660  std::min(size_t(10), size_t(std::min(List1.size(), List2.size()) * conditions.minMatchRatio));
661 
663  if (is_transfo_ok(match.get(), pixSizeRatio2, nmin))
664  transfo = match->getTransfo()->clone();
665  else {
666  LOGL_ERROR(_log, "listMatchCombinatorial: direct transfo failed, trying reverse");
667  match = matchSearchRotShiftFlip(list2, list1, conditions);
668  if (is_transfo_ok(match.get(), pixSizeRatio2, nmin))
669  transfo = match->inverseTransfo();
670  else {
671  LOGL_FATAL(_log, "FAILED");
672  }
673  }
674 
675  if (transfo) {
676  LOGL_INFO(_log, "FOUND");
677  if (conditions.printLevel >= 1) {
678  LOGL_DEBUG(_log, " listMatchCombinatorial: found the following transfo.");
679  LOGLS_DEBUG(_log, *transfo);
680  }
681  } else
682  LOGL_ERROR(_log, "listMatchCombinatorial: failed to find a transfo");
683  return transfo;
684 }
685 
687  std::unique_ptr<Gtransfo> transfo, const int maxOrder) {
688  if (!transfo) {
689  return std::unique_ptr<Gtransfo>(nullptr);
690  }
691 
692  // some hard-coded constants that could go in a param file
693  const double brightDist = 2.; // distance in pixels in a match
694  const double fullDist = 4.; // distance in pixels in a match between entire lists
695  const double nSigmas = 3.; // k-sigma clipping on residuals
696  const size_t nStars = 500; // max number of bright stars to fit
697 
698  int order = 1;
699  size_t nstarmin = 3;
700 
701  BaseStarList list1, list2;
702  List1.copyTo(list1);
703  list1.fluxSort();
704  list1.cutTail(nStars);
705  List2.copyTo(list2);
706  list2.fluxSort();
707  list2.cutTail(nStars);
708 
709  auto fullMatch = listMatchCollect(List1, List2, transfo.get(), fullDist);
710  auto brightMatch = listMatchCollect(list1, list2, transfo.get(), brightDist);
711  double curChi2 = computeChi2(*brightMatch, *transfo) / brightMatch->size();
712 
713  LOGLS_INFO(_log, "listMatchRefine: start: med.resid " << median_distance(fullMatch.get(), transfo.get())
714  << " #match " << fullMatch->size());
715 
716  do { // loop on transfo order on full list of stars
717  auto curTransfo = brightMatch->getTransfo()->clone();
718  unsigned iter = 0;
719  double transDiff;
720  do { // loop on transfo diff only on bright stars
721  brightMatch->setTransfoOrder(order);
722  brightMatch->refineTransfo(nSigmas);
723  transDiff = transfo_diff(list1, brightMatch->getTransfo().get(), curTransfo.get());
724  curTransfo = brightMatch->getTransfo()->clone();
725  brightMatch = listMatchCollect(list1, list2, curTransfo.get(), brightDist);
726  } while (brightMatch->size() > nstarmin && transDiff > 0.05 && ++iter < 5);
727 
728  double prevChi2 = curChi2;
729  curChi2 = computeChi2(*brightMatch, *curTransfo) / brightMatch->size();
730 
731  fullMatch = listMatchCollect(List1, List2, curTransfo.get(), fullDist);
732  LOGLS_INFO(_log, "listMatchRefine: order " << order << " med.resid "
733  << median_distance(fullMatch.get(), curTransfo.get())
734  << " #match " << fullMatch->size());
735  if (((prevChi2 - curChi2) > 0.01 * curChi2) && curChi2 > 0) {
736  LOGLS_INFO(_log, " listMatchRefine: order " << order << " was a better guess.");
737  transfo = brightMatch->getTransfo()->clone();
738  }
739  nstarmin = brightMatch->getTransfo()->getNpar();
740  } while (++order <= maxOrder);
741 
742  return transfo;
743 }
744 } // namespace jointcal
745 } // namespace lsst
#define LOGL_ERROR(logger, message...)
implements the linear transformations (6 real coefficients).
Definition: Gtransfo.h:294
BaseStarList::const_iterator BaseStarCIterator
Definition: BaseStar.h:80
std::shared_ptr< const BaseStar > s1
Definition: ListMatch.cc:33
void binWidth(double &Hdx, double &Hdy) const
Definition: Histo2d.h:19
SegmentPair(Segment *f, Segment *s)
Definition: ListMatch.cc:91
std::unique_ptr< Gtransfo > gtransfoCompose(const Gtransfo *left, const Gtransfo *right)
Returns a pointer to a composition.
Definition: Gtransfo.cc:368
A hanger for star associations.
Definition: StarMatch.h:31
A point in a plane.
Definition: Point.h:13
std::shared_ptr< const BaseStar > s2
Definition: ListMatch.cc:33
std::list< Segment >::const_iterator SegmentCIterator
Definition: ListMatch.cc:67
T swap(T... args)
T front(T... args)
void fill(float x, float y, float weight=1.)
Definition: Histo2d.cc:49
T endl(T... args)
A class to histogram in 4 dimensions.
Definition: Histo4d.h:10
STL namespace.
std::unique_ptr< Gtransfo > listMatchCombinatorial(const BaseStarList &list1, const BaseStarList &list2, const MatchConditions &conditions=MatchConditions())
Definition: ListMatch.cc:647
T end(T... args)
SegmentList(const BaseStarList &list, const int nStar, const Gtransfo &gtransfo=GtransfoIdentity())
Definition: ListMatch.cc:71
double Distance(const Point &other) const
Definition: Point.h:27
SegmentPairList::const_iterator SegmentPairListCIterator
Definition: ListMatch.cc:96
A Point with uncertainties.
Definition: FatPoint.h:11
void cutTail(const int nKeep)
cuts the end of the std::list
Definition: StarList.cc:24
#define M_PI
Definition: ListMatch.cc:7
std::unique_ptr< StarMatchList > matchSearchRotShift(BaseStarList &list1, BaseStarList &list2, const MatchConditions &conditions)
searches a geometrical transformation that goes from list1 to list2.
Definition: ListMatch.cc:400
T atan2(T... args)
#define LOGLS_INFO(logger, message)
double maxBin(double &x, double &y) const
Definition: Histo2d.cc:54
double x
coordinate
Definition: Point.h:18
T min(T... args)
pairs of points
The base class for handling stars. Used by all matching routines.
Definition: BaseStar.h:22
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.
Definition: FastFinder.cc:60
std::unique_ptr< StarMatchList > matchSearchRotShiftFlip(BaseStarList &list1, BaseStarList &list2, const MatchConditions &conditions)
same as above but searches also a flipped solution.
Definition: ListMatch.cc:408
T push_back(T... args)
first
std::lists of Stars.
Definition: StarList.h:35
std::shared_ptr< const Gtransfo > getTransfo() const
carries out a fit with outlier rejection
Definition: StarMatch.h:141
std::list< Segment >::iterator SegmentIterator
Definition: ListMatch.cc:66
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)
Definition: StarMatch.cc:203
#define LOGL_FATAL(logger, message...)
std::unique_ptr< Gtransfo > listMatchRefine(const BaseStarList &list1, const BaseStarList &list2, std::unique_ptr< Gtransfo > transfo, const int maxOrder=3)
Definition: ListMatch.cc:686
double relativeAngle(Segment *other)
Definition: ListMatch.cc:50
Iterator beginScan(const Point &where, double maxDist) const
Definition: FastFinder.cc:151
double computeChi2(const StarMatchList &L, const Gtransfo &gtransfo)
the actual chi2
Definition: StarMatch.cc:219
std::unique_ptr< StarMatchList > listMatchCollect(const BaseStarList &list1, const BaseStarList &list2, const Gtransfo *guess, const double maxDist)
assembles star matches.
Definition: ListMatch.cc:538
std::list< SegmentPair > SegmentPairList
Definition: ListMatch.cc:94
virtual void transformPosAndErrors(const FatPoint &in, FatPoint &out) const
Definition: Gtransfo.cc:100
Iterator meant to traverse objects within some limiting distance.
Definition: FastFinder.h:67
STL class.
T fabs(T... args)
T move(T... args)
T count(T... args)
T get(T... args)
#define LOGL_INFO(logger, message...)
void copyTo(StarList< Star > &copy) const
clears copy and makes a copy of the std::list to copy
Definition: StarList.cc:44
A do-nothing transformation. It anyway has dummy routines to mimick a Gtransfo.
Definition: Gtransfo.h:152
second
T size(T... args)
just here to provide a specialized constructor, and fit.
Definition: Gtransfo.h:358
STL class.
distance
void fluxSort()
a model routine to sort the std::list
Definition: StarList.cc:18
T begin(T... args)
T pow(T... args)
This is an auxillary class for matching objects from starlists.
Definition: FastFinder.h:31
Combinatorial searches for linear transformations to go from list1 to list2.
a virtual (interface) class for geometric transformations.
Definition: Gtransfo.h:41
#define LOGLS_DEBUG(logger, message)
#define LOGL_DEBUG(logger, message...)
friend std::ostream & operator<<(std::ostream &stream, const Segment &segment)
Definition: ListMatch.cc:54
T sort(T... args)
T sqrt(T... args)
m
Segment(std::shared_ptr< const BaseStar > star1, std::shared_ptr< const BaseStar > star2, const int star1Rank, const Gtransfo &gtransfo)
Definition: ListMatch.cc:37
T pop_front(T... args)
Parameters to be provided to combinatorial searches.
Definition: ListMatch.h:17
virtual std::unique_ptr< Gtransfo > clone() const =0
returns a copy (allocated by new) of the transformation.
Fast locator in starlists.
STL class.
#define LOG_GET(logger)
virtual void apply(const double xIn, const double yIn, double &xOut, double &yOut) const =0
SegmentPairList::iterator SegmentPairListIterator
Definition: ListMatch.cc:95
#define LOGLS_ERROR(logger, message)
std::unique_ptr< GtransfoLin > listMatchupShift(const BaseStarList &list1, const BaseStarList &list2, const Gtransfo &gtransfo, double maxShift, double binSize=0)
searches for a 2 dimensional shift using a very crude histogram method.
Definition: ListMatch.cc:464