Seg3D  2.4
Seg3D is a free volume segmentation and processing tool developed by the NIH Center for Integrative Biomedical Computing at the University of Utah Scientific Computing and Imaging (SCI) Institute.
match.hxx
1 /*
2  For more information, please see: http://software.sci.utah.edu
3 
4  The MIT License
5 
6  Copyright (c) 2016 Scientific Computing and Imaging Institute,
7  University of Utah.
8 
9 
10  Permission is hereby granted, free of charge, to any person obtaining a
11  copy of this software and associated documentation files (the "Software"),
12  to deal in the Software without restriction, including without limitation
13  the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  and/or sell copies of the Software, and to permit persons to whom the
15  Software is furnished to do so, subject to the following conditions:
16 
17  The above copyright notice and this permission notice shall be included
18  in all copies or substantial portions of the Software.
19 
20  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  DEALINGS IN THE SOFTWARE.
27  */
28 
29 // File : match.hxx
30 // Author : Pavel A. Koshevoy
31 // Created : 2006/04/10 15:41
32 // Copyright : (C) 2004-2008 University of Utah
33 // Description : Feature matching helper functions --
34 // SIFT descriptor key matching and match filtering.
35 
36 #ifndef MATCH_HXX_
37 #define MATCH_HXX_
38 
39 // local includes:
40 #include <Core/ITKCommon/extrema.hxx>
41 #include <Core/ITKCommon/pyramid.hxx>
42 #include <Core/ITKCommon/Transform/itkLegendrePolynomialTransform.h>
43 #include <Core/ITKCommon/common.hxx>
44 
45 
46 //----------------------------------------------------------------
47 // match_t
48 //
49 class match_t
50 {
51 public:
52  match_t():
53  a_(NULL),
54  b_(NULL),
55  error_(std::numeric_limits<double>::max()),
56  r_(1.0)
57  {}
58 
59  match_t(const descriptor_t * a,
60  const descriptor_t * b,
61  const double & error,
62  const double & r):
63  a_(a),
64  b_(b),
65  error_(error),
66  r_(r)
67  {
68  assert(a != NULL && b != NULL);
69  }
70 
71  // this will be used to sort the matches from best to worst:
72  inline bool operator < (const match_t & match) const
73  {
74  // FIXME:
75  assert(a_ != NULL && b_ != NULL);
76 
77  return error_ < match.error_;
78  }
79 
80  // matched keys:
81  const descriptor_t * a_;
82  const descriptor_t * b_;
83 
84  // mismatch error in descriptor space:
85  double error_;
86 
87  // ratio of error_ to the error_ of the next best match:
88  double r_;
89 };
90 
91 
92 //----------------------------------------------------------------
93 // load_image
94 //
95 extern image_t::Pointer
96 load_image(const bfs::path & fn_load,
97  const unsigned int & shrink_factor,
98  const double & pixel_spacing);
99 
100 
101 //----------------------------------------------------------------
102 // setup_pyramid
103 //
104 extern void
105 setup_pyramid(pyramid_t & pyramid,
106  const unsigned int index,
107  const bfs::path & fn_load,
108  const image_t * image,
109  const mask_t * image_mask,
110  const unsigned int & descriptor_version,
111  unsigned int num_scales = 1,
112  const bool & generate_keys = true,
113  const bfs::path & fn_debug = "");
114 
115 //----------------------------------------------------------------
116 // match_keys
117 //
118 extern void
119 match_keys(const pyramid_t & a,
120  const pyramid_t & b,
121  std::list<match_t> & ab_list,
122  std::list<const match_t *> & ab,
123  const double & percentage_to_keep);
124 
125 //----------------------------------------------------------------
126 // prefilter_matches_v1
127 //
128 extern void
129 prefilter_matches_v1(const bfs::path & fn_prefix,
130  const double & peak_ratio_threshold,
131  const std::list<const match_t *> & complete,
132  std::list<const match_t *> & filtered);
133 
134 //----------------------------------------------------------------
135 // prefilter_matches_v2
136 //
137 extern void
138 prefilter_matches_v2(const bfs::path & fn_prefix,
139  const double & distortion_threshold,
140  const std::list<const match_t *> & complete,
141  std::list<const match_t *> & filtered);
142 
143 //----------------------------------------------------------------
144 // rematch_keys
145 //
146 extern void
147 rematch_keys(const bfs::path & fn_prefix,
148  const pyramid_t & pa, // FIXME: remove this
149  const pyramid_t & pb, // FIXME: remove this
150  const std::list<descriptor_t> & a,
151  const std::list<descriptor_t> & b,
152  const base_transform_t * t_ab,
153  const double & window_radius,
154  std::list<match_t> & ab);
155 
156 //----------------------------------------------------------------
157 // rematch_keys
158 //
159 extern void
160 rematch_keys(const bfs::path & fn_prefix,
161  const pyramid_t & a,
162  const pyramid_t & b,
163  const base_transform_t * t_ab,
164  const double & window_radius,
165  std::list<match_t> & ab_list,
166  std::list<const match_t *> & ab,
167  const double & percentage_to_keep);
168 
169 extern void
170 bestfit_stats(const std::vector<const match_t *> & ab,
171  const std::list<unsigned int> & inliers);
172 
173 
174 //----------------------------------------------------------------
175 // solve_for_parameters
176 //
177 template <class transform_t>
178 static void
179 solve_for_parameters(// the transform being solved for:
180  transform_t * t_ab,
181  const unsigned int & start_with_degree,
182  const unsigned int & degrees_included,
183 
184  // match pairs:
185  const std::vector<const match_t *> & ab,
186 
187  // the list of matches that produced the best fit:
188  const std::list<unsigned int> & bestfit)
189 {
190  std::vector<image_t::PointType> uv(bestfit.size());
191  std::vector<image_t::PointType> xy(bestfit.size());
192 
193  unsigned int position = 0;
194  for (std::list<unsigned int>::const_iterator i = bestfit.begin();
195  i != bestfit.end();
196  ++i, ++position)
197  {
198  const unsigned int & index = *i;
199  const match_t * match = ab[index];
200  const image_t::PointType & a_uv = match->a_->extrema_->local_coords_;
201  const image_t::PointType & b_xy = match->b_->extrema_->local_coords_;
202 
203  uv[position] = a_uv;
204  xy[position] = b_xy;
205  }
206 
207  // fit to all detected inliers:
208  t_ab->solve_for_parameters(start_with_degree, degrees_included, uv, xy);
209 }
210 
211 //----------------------------------------------------------------
212 // refine_inliers
213 //
214 template <class transform_t>
215 bool
216 refine_inliers(const transform_t * t_ab,
217  const double & t,
218  const std::vector<const match_t *> & ab,
219  std::vector<bool> & is_inlier,
220  std::list<unsigned int> & inliers,
221  double & model_quality)
222 {
223  // shortcut:
224  const unsigned int & num_matches = ab.size();
225 
226  const double inlier_threshold = t * t;
227  double inlier_error = 0.0;
228  double total_error = 0.0;
229  model_quality = 0.0;
230 
231  bool removed_inliers = false;
232  bool added_inliers = false;
233  for (unsigned int i = 0; i < num_matches; i++)
234  {
235  // transform one of the points in the match pair and see how
236  // closely it lands to its counterpart:
237  const match_t * match = ab[i];
238  const image_t::PointType & a_uv = match->a_->extrema_->local_coords_;
239  const image_t::PointType & b_xy = match->b_->extrema_->local_coords_;
240 
241  image_t::PointType a_xy = t_ab->TransformPoint(a_uv);
242  double dx = (a_xy[0] - b_xy[0]);// / t_ab->GetXmax();
243  double dy = (a_xy[1] - b_xy[1]);// / t_ab->GetYmax();
244  double d2 = dx * dx + dy * dy;
245  total_error += d2;
246 
247  // FIXME: allow for looser tolerances at the coarser resolution levels:
248  // double s = integer_power<double>(2.0, match->a_->extrema_->octave_);
249  // double inlier_threshold = (t * s) * (t * s);
250 
251  if (d2 <= inlier_threshold)
252  {
253  // FIXME:
254  inlier_error += d2;
255  // inlier_error += sqrt(d2);
256 
257  if (!is_inlier[i])
258  {
259  inliers.push_back(i);
260  is_inlier[i] = true;
261  added_inliers = true;
262  }
263  }
264  else if (is_inlier[i])
265  {
266  inliers.remove(i);
267  is_inlier[i] = false;
268  removed_inliers = true;
269  }
270  }
271 
272  const unsigned int & inliers_count = inliers.size();
273 
274  if (inliers_count > 0)
275  {
276  //#if 1
277  model_quality = double(inliers_count);
278  //#else
279  // // model_quality = 1.0 / total_error;
280  // // model_quality = num_matches / total_error;
281  // // model_quality = inliers_count / total_error;
282  // // model_quality = inliers_count / inlier_error;
283  // /* model_quality =
284  // (double(inliers_count) * double(inliers_count)) /
285  // (1.0 + sqrt(inlier_error));
286  // */
287  //#endif
288  }
289 
290  return removed_inliers || added_inliers;
291 }
292 
293 //----------------------------------------------------------------
294 // RANSAC
295 //
296 template <class transform_t>
297 static bool
298 RANSAC(// RANSAC controls:
299  const unsigned int & k,
300  const unsigned int & n,
301  const unsigned int & d,
302  const double & t,
303 
304  // transform controls:
305  const transform_t * initial_t_ab,
306  const unsigned int & start_with_degree,
307  const unsigned int & degrees_included,
308 
309  // match pairs:
310  const std::vector<const match_t *> & ab,
311 
312  // pass back the results via these variables:
313  typename transform_t::ParametersType & best_params,
314  std::list<unsigned int> & bestfit,
315  double & bestquality)
316 {
317  const double initial_bestquality = bestquality;
318  best_params = initial_t_ab->GetParameters();
319 
320  typename transform_t::Pointer t_ab = transform_t::New();
321  t_ab->SetFixedParameters(initial_t_ab->GetFixedParameters());
322  t_ab->SetParameters(initial_t_ab->GetParameters());
323 
324  // shortcut:
325  const unsigned int & num_matches = ab.size();
326 
327  // at least n sample points are required to estimate the model parameters:
328  if (num_matches < n) return false;
329  if (degrees_included == 0) return false;
330 
331  // bookkeeping arrays:
332  std::vector<image_t::PointType> uv(n);
333  std::vector<image_t::PointType> xy(n);
334 
335  bool start_with_bestfit = bestfit.size() > 0;
336 
337  for (unsigned int i = 0; i < k; i++)
338  {
339  std::list<unsigned int> inliers;
340  std::vector<bool> is_inlier;
341  is_inlier.assign(num_matches, false);
342 
343  if (!start_with_bestfit)
344  {
345  // pick n randomly selected matches:
346  for (unsigned int j = 0; j < n; j++)
347  {
348  // assume the matches are sorted in the order of increasing mismatch,
349  // implement importance sampling to take advantage of this:
350  unsigned int index;
351  while (true)
352  {
353  // importance sampling of the matches,
354  // better matches are sampled more frequently:
355  double nx = double(num_matches);
356  double q = drand();
357  /*
358  // PDF(x) = ((1 - x/nx)^2) / nx
359  // CDF(x) = (x/nx - 1)^3 + 1
360  double x = nx * (1.0 - pow(q, 1.0 / 3.0));
361  */
362 
363  // uniform sampling:
364  double x = nx * q;
365 
366  index = std::min(num_matches - 1, (unsigned int)(x));
367  if (!is_inlier[index]) break;
368  }
369 
370  inliers.push_back(index);
371  is_inlier[index] = true;
372 
373  const match_t * match = ab[index];
374  uv[j] = match->a_->extrema_->local_coords_;
375  xy[j] = match->b_->extrema_->local_coords_;
376  }
377 
378  // setup transform based on the selected matches:
379  t_ab->solve_for_parameters(start_with_degree, degrees_included, uv, xy);
380  }
381  else
382  {
383  // re-initialize with previous best fit data:
384  std::list<unsigned int>::const_iterator iter = bestfit.begin();
385  for (unsigned int j = 0; j < bestfit.size(); j++, ++iter)
386  {
387  const unsigned int & id = *iter;
388  inliers.push_back(id);
389  is_inlier[id] = true;
390  }
391 
392  solve_for_parameters<transform_t>(t_ab,
393  start_with_degree,
394  degrees_included,
395  ab,
396  inliers);
397  start_with_bestfit = false;
398  }
399 
400  // find other potential inliers and calculate the error measure:
401  double model_quality = std::numeric_limits<double>::max();
402  bool inliers_were_altered = refine_inliers<transform_t>(t_ab,
403  t,
404  ab,
405  is_inlier,
406  inliers,
407  model_quality);
408 
409  if (model_quality > bestquality)
410  {
411  // allow for the removal of outliers (in case the original
412  // inliers were poor):
413  double quality = model_quality;
414  for (unsigned int j = 0; j < 100 && inliers_were_altered; j++)
415  {
416  if (inliers.size() < n) break;
417 
418  // update the transform to reflect the changes to the inliers:
419  solve_for_parameters<transform_t>(t_ab,
420  start_with_degree,
421  degrees_included,
422  ab,
423  inliers);
424 
425  // re-calculate the error measure, update the inliers:
426  inliers_were_altered = refine_inliers<transform_t>(t_ab,
427  t,
428  ab,
429  is_inlier,
430  inliers,
431  quality);
432  }
433 
434  if (quality > bestquality && inliers.size() >= n)
435  {
436  solve_for_parameters<transform_t>(t_ab,
437  start_with_degree,
438  degrees_included,
439  ab,
440  inliers);
441 
442  // save the better result:
443  bestquality = quality;
444  best_params = t_ab->GetParameters();
445  bestfit = inliers;
446  std::cout << "FIXME: quality: " << bestquality
447  << ", iteration: " << i << std::endl;
448  }
449  }
450  }
451 
452  return initial_bestquality < bestquality;
453 }
454 
455 //----------------------------------------------------------------
456 // match
457 //
458 // Match the pyramids with a Legendre polynomial transform of a
459 // given order, where order == degree of the polynomial + 1.
460 //
461 // 2nd order == affine transform
462 // 3rd order == quadratic transform
463 // 4th order == cubic polynomial
464 //
465 template <typename transform_t>
466 typename transform_t::Pointer
467 match(const unsigned int order,
468  const pyramid_t & a,
469  const pyramid_t & b,
470  const bfs::path & fn_debug)
471 {
472  // a threshold value for determining when a data point fits a model:
473  const double t = 2.0 * a.octave_[0].L_[0]->GetSpacing()[0]
474  * (1.0 + integer_power<double>(2.0, a.octaves()));
475 
476  // precompute the key matches:
477  std::cout << "matching " << a.fn_data_ << " to " << b.fn_data_ << std::endl;
478  std::list<match_t> ab_list;
479  std::list<const match_t *> ab_sorted;
480  match_keys(a, b, ab_list, ab_sorted, 1.0);
481 
482  std::cout << "pre-filtering the key matches, t: " << t << std::endl;
483  std::list<const match_t *> ab_sorted_filtered;
484  // prefilter_matches_v1(fn_debug, 0.61, ab_sorted, ab_sorted_filtered);
485  prefilter_matches_v2(fn_debug, 0.1, ab_sorted, ab_sorted_filtered);
486 
487  // maximum number of RANSAC iterations:
488  // const unsigned int k = 20 * ab_sorted.size();
489  const unsigned int k = 33333;
490  std::cout << "RANSAC will try " << k << " iterations (instead of "
491  << 20 * ab_sorted.size() << ")" << std::endl;
492 
493 #ifdef VIS_DEBUG
494  visualize_matches_v2(a, b, ab_sorted_filtered, fn_debug);
495 #endif
496 
497  // initialise the transform from pyramid a to pyramid b:
498  typename transform_t::Pointer t_ab =
499  setup_transform<transform_t, image_t>(b.octave_[0].L_[0]);
500 
501  // image center distortion will be used later to estimate the translation
502  // vector (needed for stable higher order parameter estimation):
503  image_t::PointType center;
504  center[0] = t_ab->GetUc();
505  center[1] = t_ab->GetVc();
506 
507  // estimate affine transform parameters:
508  std::vector<const match_t *> ab;
509  ab.assign(ab_sorted_filtered.begin(), ab_sorted_filtered.end());
510 
511  std::cout << "estimating the affine transform" << std::endl;
512  typename transform_t::ParametersType params_01;
513  std::list<unsigned int> bestfit;
514  double bestquality = 0.0;
515 
516  const unsigned int initial_order = std::min(2u, order);
517  const unsigned int initial_coeff =
518  transform_t::count_coefficients(0, initial_order);
519 
520  if (RANSAC<transform_t>(k,
521  initial_coeff,
522  initial_coeff * 2,
523  t,
524  t_ab, 0, initial_order,
525  ab,
526  params_01, bestfit, bestquality))
527  {
528  t_ab->SetParameters(params_01);
529 
530  // FIXME:
531  bestfit_stats(ab, bestfit);
532 
533  // FIXME:
534 #ifdef VIS_DEBUG
535  visualize_best_fit(fn_debug + "d01a-",
536  a.octave_[0].L_[0],
537  b.octave_[0].L_[0],
538  t_ab,
539  ab,
540  bestfit,
541  a.octave_[0].mask_,
542  b.octave_[0].mask_);
543 #endif // VIS_DEBUG
544 
545 #ifdef TRY_REMATCHING
546  // re-match the keys with correct position information:
547  std::cout << "re-matching the keys based on the affine transform estimate"
548  << std::endl;
549  rematch_keys(fn_debug, a, b, t_ab,
550  4.0 * t,
551  ab_list, ab_sorted, 1.0);
552 
553  // FIXME: do I really want to do this again?
554  ab_sorted_filtered.clear();
555  prefilter_matches_v2(fn_debug, 0.1, ab_sorted, ab_sorted_filtered);
556 
557 #ifdef VIS_DEBUG
558  if (fn_debug.size() != 0)
559  {
560  visualize_matches_v2(a, b, ab_sorted_filtered, fn_debug + "rematched");
561  }
562 #endif // VIS_DEBUG
563 
564  // ab.assign(ab_sorted.begin(), ab_sorted.end());
565  ab.assign(ab_sorted_filtered.begin(), ab_sorted_filtered.end());
566 
567  // re-estimate the affine transform using the rematched keys:
568  std::cout << "re-estimating the affine transform" << std::endl;
569  bestquality = 0.0;
570  bestfit.clear();
571  if (RANSAC<transform_t>(k,
572  initial_coeff,
573  initial_coeff * 2,
574  t,
575  t_ab, 0, initial_order,
576  ab,
577  params_01, bestfit, bestquality))
578  {
579  t_ab->SetParameters(params_01);
580 
581  // FIXME:
582  bestfit_stats(ab, bestfit);
583 
584  // move the translation vector into the fixed parameters of
585  // the transform and re-estimate the affine parameters:
586  image_t::PointType shifted_center = t_ab->TransformPoint(center);
587  double shift_x = shifted_center[0] - center[0];
588  double shift_y = shifted_center[1] - center[1];
589  t_ab->setup_translation(shift_x, shift_y);
590  solve_for_parameters<transform_t>
591  (t_ab, 0, initial_order, ab, bestfit);
592 
593  // FIXME:
594 #ifdef VIS_DEBUG
595  visualize_best_fit(fn_debug + "d01b-",
596  a.octave_[0].L_[0],
597  b.octave_[0].L_[0],
598  t_ab,
599  ab,
600  bestfit,
601  a.octave_[0].mask_,
602  b.octave_[0].mask_);
603 #endif // VIS_DEBUG
604 
605  if (order > 2)
606  {
607  unsigned int remaining_degrees = std::min(2u, order - 2);
608  unsigned int remaining_coeff =
609  transform_t::count_coefficients(2, remaining_degrees);
610 
611  // perform high order parameter estimation:
612  std::cout << "estimating the higher order transform "
613  << "(affine parameters remain fixed)" << std::endl;
614  typename transform_t::ParametersType params_0123;
615 
616  // FIXME: perhaps this should be on a switch?
617  // bestquality = 0.0;
618  bestquality *= 0.75;
619 
620  if (RANSAC<transform_t>(k,
621  remaining_coeff,
622  remaining_coeff * 2,
623  t,
624  t_ab, 2, remaining_degrees,
625  ab,
626  params_0123, bestfit, bestquality))
627  {
628  t_ab->SetParameters(params_0123);
629 
630  // FIXME:
631  bestfit_stats(ab, bestfit);
632 
633  // re-solve for affine transform in order to accomodate the new
634  // inliers detected due to the higher order polynomial coefficients
635  // of the transform, then re-solve for the higher order
636  // polynomial coefficients:
637  solve_for_parameters<transform_t>
638  (t_ab, 0, 2, ab, bestfit);
639 
640  solve_for_parameters<transform_t>
641  (t_ab, 2, remaining_degrees, ab, bestfit);
642 
643  // FIXME:
644 #ifdef VIS_DEBUG
645  visualize_best_fit(fn_debug + "d0123-",
646  a.octave_[0].L_[0],
647  b.octave_[0].L_[0],
648  t_ab,
649  ab,
650  bestfit,
651  a.octave_[0].mask_,
652  b.octave_[0].mask_);
653 #endif // VIS_DEBUG
654  }
655  }
656  }
657 #endif // TRY_REMATCHING
658  }
659 
660  return t_ab;
661 }
662 
663 
664 #endif // MATCH_HXX_
Definition: match.hxx:49
Definition: extrema.hxx:82
Definition: pyramid.hxx:173