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