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.
mosaic_refinement_common.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 : mosaic_refinement_common.hxx
30 // Author : Pavel A. Koshevoy, Joel Spaltenstein
31 // Created : Mon Mar 26 10:34:39 MDT 2007
32 // Copyright : (C) 2004-2008 University of Utah
33 // Description : Helper functions for automatic mosaic refinement.
34 
35 #ifndef MOSAIC_REFINEMENT_COMMON_HXX_
36 #define MOSAIC_REFINEMENT_COMMON_HXX_
37 
38 // the includes:
39 #include <Core/ITKCommon/common.hxx>
40 #include <Core/ITKCommon/grid_common.hxx>
41 #include <Core/ITKCommon/Optimizers/itkImageMosaicVarianceMetric.h>
42 #include <Core/ITKCommon/Optimizers/itkRegularStepGradientDescentOptimizer2.h>
43 #include <Core/ITKCommon/the_aa_bbox.hxx>
44 #include <Core/ITKCommon/ThreadUtils/the_thread_pool.hxx>
45 
46 // system includes:
47 #include <math.h>
48 #include <vector>
49 #include <sstream>
50 
51 #ifndef WIN32
52 #include <unistd.h>
53 #endif
54 
55 
56 //----------------------------------------------------------------
57 // regularize_displacements
58 //
59 extern void
60 regularize_displacements(// computed displacement vectors of the moving image
61  // grid transform control points, in mosaic space:
62  std::vector<vec2d_t> & xy_shift,
63  std::vector<double> & mass,
64 
65  image_t::Pointer & dx,
66  image_t::Pointer & dy,
67  image_t::Pointer & db,
68 
69  // median filter radius:
70  const unsigned int & median_radius);
71 
72 
73 //----------------------------------------------------------------
74 // refine_mosaic
75 //
76 template <class TImage, class TMask>
77 void
78 refine_mosaic(// all the tiles in the mosaic, and their masks:
79  array2d(typename TImage::Pointer) & pyramid,
80  std::vector<typename TMask::ConstPointer> & mask,
81  std::vector<base_transform_t::Pointer> & transform,
82 
83  unsigned int iterations_per_level)
84 {
85  typedef itk::LinearInterpolateImageFunction<TImage, double> interpolator_t;
86 
87  unsigned int pyramid_levels = pyramid.size();
88  if (pyramid_levels == 0) return;
89 
90  std::vector<typename TImage::Pointer> & image = pyramid[pyramid_levels - 1];
91  unsigned int num_images = image.size();
92  if (num_images == 0) return;
93 
94  std::ostringstream oss;
95  oss << "iterations per level: " << iterations_per_level << std::endl
96  << "transform type: " << transform[0]->GetTransformTypeAsString()
97  << std::endl;
98 
99  // try global refinement of the mosaic:
101  mosaic_metric_t;
102 
103  typename mosaic_metric_t::Pointer mosaic_metric = mosaic_metric_t::New();
104  mosaic_metric->image_.resize(num_images);
105  mosaic_metric->mask_.resize(num_images);
106  mosaic_metric->transform_.resize(num_images);
107  for (unsigned int i = 0; i < num_images; i++)
108  {
109  mosaic_metric->image_[i] = image[i];
110  mosaic_metric->mask_[i] = mask[i];
111  mosaic_metric->transform_[i] = transform[i];
112  }
113 
114  // FIXME: ITK doesn't have an API for this:
115  std::vector<bool> param_shared(transform[0]->GetNumberOfParameters(), false);
116  std::vector<bool> param_active(transform[0]->GetNumberOfParameters(), true);
117 
118  // setup the shared parameters mask:
119  mosaic_metric->setup_param_map(param_shared, param_active);
120  mosaic_metric->Initialize();
121 
122  // setup the optimizer scales:
123  typename mosaic_metric_t::params_t parameter_scales =
124  mosaic_metric->GetTransformParameters();
125  parameter_scales.Fill(1.0);
126 
127  for (unsigned int level = 0;
128  level < pyramid_levels && iterations_per_level > 0;
129  level++)
130  {
131  for (unsigned int i = 0; i < num_images; i++)
132  {
133  mosaic_metric->image_[i] = pyramid[level][i];
134  }
135 
136  typename mosaic_metric_t::measure_t metric_before =
137  mosaic_metric->GetValue(mosaic_metric->GetTransformParameters());
138 
139  // run several iterations of the optimizer:
140  for (unsigned int k = 0; k < 3; k++)
141  {
142  typename mosaic_metric_t::params_t params_before =
143  mosaic_metric->GetTransformParameters();
144 
145  typename mosaic_metric_t::measure_t metric_after =
146  std::numeric_limits<typename mosaic_metric_t::measure_t>::max();
147 
148  // use global refinement:
149  optimizer_t::Pointer optimizer = optimizer_t::New();
150  typename optimizer_observer_t<optimizer_t>::Pointer observer =
152  optimizer->AddObserver(itk::IterationEvent(), observer);
153  optimizer->SetMinimize(true);
154  optimizer->SetNumberOfIterations(iterations_per_level);
155  optimizer->SetMinimumStepLength(1e-12); // min_step
156  optimizer->SetMaximumStepLength(1e-5); // max_step
157  optimizer->SetGradientMagnitudeTolerance(1e-6);
158  optimizer->SetRelaxationFactor(5e-1);
159  optimizer->SetCostFunction(mosaic_metric);
160  optimizer->SetInitialPosition(params_before);
161  optimizer->SetScales(parameter_scales);
162  optimizer->SetPickUpPaceSteps(5);
163  optimizer->SetBackTracking(true);
164 
165  // refine the mosaic:
166  try
167  {
168  oss << "\n" << level << '.' << k << ": refining distortion transforms"
169  << std::endl;
170 
171  optimizer->StartOptimization();
172  }
173  catch (itk::ExceptionObject & exception)
174  {
175  // oops:
176  oss << "optimizer threw an exception:" << std::endl << exception.what() << std::endl;
177  }
178 
179  mosaic_metric->SetTransformParameters(optimizer->GetBestParams());
180  metric_after = optimizer->GetBestValue();
181 
182  typename mosaic_metric_t::params_t params_after =
183  mosaic_metric->GetTransformParameters();
184 
185  oss << "before: METRIC = " << metric_before
186  << ", PARAMS = " << params_before << std::endl
187  << "after: METRIC = " << metric_after
188  << ", PARAMS = " << params_after << std::endl;
189 
190  // quantify the improvement:
191  double improvement = 1.0 - metric_after / metric_before;
192  bool failed_to_improve = (metric_after - metric_before) >= 0.0;
193  bool negligible_improvement = !failed_to_improve && (improvement < 1e-3);
194 
195  if (!failed_to_improve)
196  {
197  oss << "IMPROVEMENT: " << std::setw(3) << static_cast<double>(100.0 * improvement)
198  << "%" << std::endl;
199  }
200 
201  if (failed_to_improve)
202  {
203  oss << "NOTE: minimization failed, ignoring registration results..."
204  << std::endl;
205 
206  // previous transform was better:
207  mosaic_metric->SetTransformParameters(params_before);
208  break;
209  }
210  else if (negligible_improvement)
211  {
212  oss << "NOTE: improvement is negligible..." << std::endl;
213  break;
214  }
215 
216  // avoid recalculating the same metric:
217  metric_before = metric_after;
218  }
219  }
220  CORE_LOG_MESSAGE(oss.str());
221 }
222 
223 
224 //----------------------------------------------------------------
225 // calc_displacements
226 //
227 // This is the original version used for single-threaded execution
228 //
229 template <typename TImage, typename TMask>
230 void
231 calc_displacements(// computed displacement vectors of the moving image
232  // grid transform control points, in mosaic space:
233  std::vector<vec2d_t> & xy_shift,
234  std::vector<double> & mass,
235 
236  // a flag indicating whether the tiles
237  // have already been transformed into mosaic space:
238  bool tiles_already_warped,
239 
240  // fixed:
241  const TImage * tile_0,
242  const TMask * mask_0,
243  const base_transform_t * forward_0,
244 
245  // moving:
246  const TImage * tile_1,
247  const TMask * mask_1,
248  const itk::GridTransform * forward_1,
249 
250  // neighborhood size:
251  const unsigned int & neighborhood,
252 
253  // minimum acceptable neighborhood overlap ratio:
254  const double & min_overlap,
255 
256  // median filter radius:
257  const unsigned int & median_radius)
258 {
259  // shortcuts:
260  const the_grid_transform_t & gt = forward_1->transform_;
261  unsigned int mesh_cols = gt.cols_ + 1;
262  unsigned int mesh_rows = gt.rows_ + 1;
263  unsigned mesh_size = gt.grid_.mesh_.size();
264  xy_shift.assign(mesh_size, vec2d(0, 0));
265 
266  // make sure both tiles have the same pixel spacing:
267  typename TImage::SpacingType sp = tile_1->GetSpacing();
268  if (sp != tile_0->GetSpacing()) return;
269 
270  // setup the local neighborhood:
271  typename TImage::SizeType sz;
272  sz[0] = neighborhood;
273  sz[1] = neighborhood;
274 
275  typename TImage::Pointer img[] = {
276  make_image<TImage>(sp, sz),
277  make_image<TImage>(sp, sz)
278  };
279 
280  typename TMask::Pointer msk[] = {
281  make_image<TMask>(sp, sz),
282  make_image<TMask>(sp, sz)
283  };
284 
285  // for each interpolation point, do a local neighborhood fft matching,
286  // and use the resulting displacement vector to adjust the mesh:
287  image_t::Pointer dx = make_image<image_t>(mesh_cols,
288  mesh_rows,
289  1.0,
290  0.0);
291 
292  image_t::Pointer dy = make_image<image_t>(mesh_cols,
293  mesh_rows,
294  1.0,
295  0.0);
296 
297  image_t::Pointer db = make_image<image_t>(mesh_cols,
298  mesh_rows,
299  1.0,
300  0.0);
301 
302  typename TImage::Pointer img_large;
303  typename TMask::Pointer msk_large;
304 
305  if (!tiles_already_warped)
306  {
307  typename TImage::SizeType sz_large(sz);
308  sz_large[0] *= 2;
309  sz_large[1] *= 2;
310  img_large = make_image<TImage>(sp, sz_large);
311  msk_large = make_image<TMask>(sp, sz_large);
312  }
313 
314  std::ostringstream oss;
315  oss << "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" << std::endl;
316  for (unsigned int i = 0; i < mesh_size; i++)
317  {
318  // shortcut:
319  const vertex_t & vertex = gt.grid_.mesh_[i];
320 
321  // find the mosaic space coordinates of this vertex:
322  pnt2d_t center;
323  gt.transform_inv(vertex.uv_, center);
324 
325  // extract a neighborhood of the vertex from both tiles:
326  image_t::IndexType index;
327  index[0] = i % mesh_cols;
328  index[1] = i / mesh_cols;
329  dx->SetPixel(index, 0);
330  dy->SetPixel(index, 0);
331  db->SetPixel(index, 0);
332 
333  // feed the two neighborhoods into the FFT translation estimator:
334  vec2d_t shift(vec2d(0, 0));
335  bool ok = tiles_already_warped ?
336 
337  refine_one_point_fft(shift,
338 
339  tile_0,
340  mask_0,
341 
342  tile_1,
343  mask_1,
344 
345  center,
346  min_overlap,
347 
348  img[0].GetPointer(),
349  msk[0].GetPointer(),
350 
351  img[1].GetPointer(),
352  msk[1].GetPointer()) :
353 
354  refine_one_point_fft(shift,
355 
356  tile_0,
357  mask_0,
358 
359  tile_1,
360  mask_1,
361 
362  forward_0,
363  forward_1,
364 
365  center,
366  min_overlap,
367 
368  sz,
369  sp,
370 
371  img_large.GetPointer(),
372  msk_large.GetPointer(),
373 
374  img[0].GetPointer(),
375  msk[0].GetPointer(),
376 
377  img[1].GetPointer(),
378  msk[1].GetPointer());
379  if (!ok)
380  {
381  continue;
382  }
383 
384  oss << i << ". shift: " << shift << std::endl;
385  dx->SetPixel(index, shift[0]);
386  dy->SetPixel(index, shift[1]);
387  db->SetPixel(index, 1);
388  }
389 
390  // regularize the displacement vectors here:
391  regularize_displacements(xy_shift, mass, dx, dy, db, median_radius);
392  CORE_LOG_MESSAGE(oss.str());
393 }
394 
395 
396 //----------------------------------------------------------------
397 // calc_displacements_t
398 //
399 //
400 template <typename TImage, typename TMask>
402 {
403 public:
404  calc_displacements_t(// a flag indicating whether the tiles
405  // have already been transformed into mosaic space:
406  bool tiles_already_warped,
407 
408  // fixed:
409  const TImage * tile_0,
410  const TMask * mask_0,
411  const base_transform_t * forward_0,
412 
413  // moving:
414  const TImage * tile_1,
415  const TMask * mask_1,
416  const itk::GridTransform * forward_1,
417 
418  // neighborhood size:
419  const unsigned int & neighborhood_size,
420 
421  // minimum acceptable neighborhood overlap ratio:
422  const double & min_overlap,
423 
424  // mesh node displacements:
425  image_t::Pointer dx,
426  image_t::Pointer dy,
427 
428  // mesh node displacement weights:
429  image_t::Pointer db,
430 
431  // mesh node index:
432  const std::list<image_t::IndexType> & index,
433 
434  // mesh node mosaic space coordinates:
435  const std::list<pnt2d_t> & center):
436  tiles_already_warped_(tiles_already_warped),
437 
438  tile_0_(tile_0),
439  mask_0_(mask_0),
440  forward_0_(forward_0),
441 
442  tile_1_(tile_1),
443  mask_1_(mask_1),
444  forward_1_(forward_1),
445 
446  min_overlap_(min_overlap),
447 
448  dx_(dx),
449  dy_(dy),
450  db_(db),
451 
452  index_(index.begin(), index.end()),
453  center_(center.begin(), center.end())
454  {
455  // make sure both tiles have the same pixel spacing:
456  sp_ = tile_1->GetSpacing();
457  bool ok = (sp_ == tile_0->GetSpacing());
458  if (!ok)
459  {
460  assert(false);
461  return;
462  }
463 
464  // setup the local neighborhood:
465  sz_[0] = neighborhood_size;
466  sz_[1] = neighborhood_size;
467  }
468 
469  // virtual:
470  void execute(the_thread_interface_t * thread)
471  {
472  WRAP(the_terminator_t terminator("calc_displacements_t"));
473 
474  typename TImage::Pointer img[] = {
475  make_image<TImage>(sp_, sz_),
476  make_image<TImage>(sp_, sz_)
477  };
478 
479  typename TMask::Pointer msk[] = {
480  make_image<TMask>(sp_, sz_),
481  make_image<TMask>(sp_, sz_)
482  };
483 
484  typename TImage::Pointer img_large;
485  typename TMask::Pointer msk_large;
486 
487  if (!tiles_already_warped_)
488  {
489  typename TImage::SizeType sz_large(sz_);
490  sz_large[0] *= 2;
491  sz_large[1] *= 2;
492  img_large = make_image<TImage>(sp_, sz_large);
493  msk_large = make_image<TMask>(sp_, sz_large);
494  }
495 
496  std::size_t num_nodes = center_.size();
497  for (std::size_t i = 0; i < num_nodes; i++)
498  {
499  // shortcuts:
500  const image_t::IndexType & index = index_[i];
501  const pnt2d_t & center = center_[i];
502 
503  // feed the two neighborhoods into the FFT translation estimator:
504  vec2d_t shift(vec2d(0, 0));
505 
506  bool ok = tiles_already_warped_ ?
507  // if the tiles are already warped we don't
508  // need the transforms
509  refine_one_point_fft(shift,
510 
511  tile_0_,
512  mask_0_,
513 
514  tile_1_,
515  mask_1_,
516 
517  center,
518  min_overlap_,
519 
520  img[0].GetPointer(),
521  msk[0].GetPointer(),
522 
523  img[1].GetPointer(),
524  msk[1].GetPointer()) :
525 
526  refine_one_point_fft(shift,
527 
528  tile_0_,
529  mask_0_,
530 
531  tile_1_,
532  mask_1_,
533 
534  forward_0_,
535  forward_1_,
536 
537  center,
538  min_overlap_,
539 
540  sz_,
541  sp_,
542 
543  img_large.GetPointer(),
544  msk_large.GetPointer(),
545 
546  img[0].GetPointer(),
547  msk[0].GetPointer(),
548 
549  img[1].GetPointer(),
550  msk[1].GetPointer());
551  if (ok)
552  {
553  dx_->SetPixel(index, shift[0]);
554  dy_->SetPixel(index, shift[1]);
555  db_->SetPixel(index, 1);
556  }
557  }
558  }
559 
560  // a flag indicating whether the tiles
561  // have already been transformed into mosaic space:
562  bool tiles_already_warped_;
563 
564  // fixed:
565  const TImage * tile_0_;
566  const TMask * mask_0_;
567  const base_transform_t * forward_0_;
568 
569  // moving:
570  const TImage * tile_1_;
571  const TMask * mask_1_;
572  const itk::GridTransform * forward_1_;
573 
574  // moving tile spacing (must be the same as fixed tile spacing):
575  typename TImage::SpacingType sp_;
576  typename TImage::SizeType sz_;
577 
578  // minimum acceptable neighborhood overlap ratio:
579  const double min_overlap_;
580 
581  // mesh node displacements:
582  image_t::Pointer dx_;
583  image_t::Pointer dy_;
584 
585  // mesh node displacement weights:
586  image_t::Pointer db_;
587 
588  // mesh node index:
589  std::vector<image_t::IndexType> index_;
590 
591  // mesh node mosaic space coordinates:
592  std::vector<pnt2d_t> center_;
593 };
594 
595 
596 //----------------------------------------------------------------
597 // calc_displacements_mt
598 //
599 // calculate transform mesh displacement vectors multi-threaded
600 //
601 template <typename TImage, typename TMask>
602 void
603 calc_displacements_mt(unsigned int num_threads,
604 
605  // computed displacement vectors of the moving image
606  // grid transform control points, in mosaic space:
607  std::vector<vec2d_t> & xy_shift,
608  std::vector<double> & mass,
609 
610  // a flag indicating whether the tiles
611  // have already been transformed into mosaic space:
612  bool tiles_already_warped,
613 
614  // fixed:
615  const TImage * tile_0,
616  const TMask * mask_0,
617  const base_transform_t * forward_0,
618 
619  // moving:
620  const TImage * tile_1,
621  const TMask * mask_1,
622  const itk::GridTransform * forward_1,
623 
624  // neighborhood size:
625  const unsigned int & neighborhood_size,
626 
627  // minimum acceptable neighborhood overlap ratio:
628  const double & min_overlap,
629 
630  // median filter radius:
631  const unsigned int & median_radius)
632 {
633  // make sure both tiles have the same pixel spacing:
634  if (tile_1->GetSpacing() != tile_0->GetSpacing()) return;
635 
636  // shortcuts:
637  const the_grid_transform_t & gt = forward_1->transform_;
638  unsigned int mesh_cols = gt.cols_ + 1;
639  unsigned int mesh_rows = gt.rows_ + 1;
640  unsigned mesh_size = gt.grid_.mesh_.size();
641  xy_shift.assign(mesh_size, vec2d(0, 0));
642 
643  // for each interpolation point, do a local neighborhood fft matching,
644  // and use the resulting displacement vector to adjust the mesh:
645  image_t::Pointer dx = make_image<image_t>(mesh_cols,
646  mesh_rows,
647  1.0,
648  0.0);
649 
650  image_t::Pointer dy = make_image<image_t>(mesh_cols,
651  mesh_rows,
652  1.0,
653  0.0);
654 
655  image_t::Pointer db = make_image<image_t>(mesh_cols,
656  mesh_rows,
657  1.0,
658  0.0);
659 
660  the_thread_pool_t thread_pool(num_threads);
661  thread_pool.set_idle_sleep_duration(50); // 50 usec
662 
663  // split nodes between threads:
664  std::vector<std::list<image_t::IndexType> > node_index_list(num_threads);
665  std::vector<std::list<pnt2d_t> > node_center_list(num_threads);
666 
667  for (unsigned int i = 0; i < mesh_size; i++)
668  {
669  // shortcuts:
670  const vertex_t & vertex = gt.grid_.mesh_[i];
671  const unsigned int which_thread = i % num_threads;
672 
673  // find the mosaic space coordinates of this vertex:
674  pnt2d_t center;
675  gt.transform_inv(vertex.uv_, center);
676  node_center_list[which_thread].push_back(center);
677 
678  // extract a neighborhood of the vertex from both tiles:
679  image_t::IndexType index;
680  index[0] = i % mesh_cols;
681  index[1] = i / mesh_cols;
682  dx->SetPixel(index, 0);
683  dy->SetPixel(index, 0);
684  db->SetPixel(index, 0);
685 
686  node_index_list[which_thread].push_back(index);
687  }
688 
689  // setup a transaction for each thread:
690  for (unsigned int i = 0; i < num_threads; i++)
691  {
693  new calc_displacements_t<TImage, TMask>(tiles_already_warped,
694 
695  tile_0,
696  mask_0,
697  forward_0,
698 
699  tile_1,
700  mask_1,
701  forward_1,
702 
703  neighborhood_size,
704  min_overlap,
705 
706  dx,
707  dy,
708  db,
709 
710  node_index_list[i],
711  node_center_list[i]);
712 
713  thread_pool.push_back(t);
714  }
715 
716  // run the transactions:
717  thread_pool.pre_distribute_work();
718  suspend_itk_multithreading_t suspend_itk_mt;
719  thread_pool.start();
720  thread_pool.wait();
721 
722  // regularize the displacement vectors here:
723  regularize_displacements(xy_shift, mass, dx, dy, db, median_radius);
724 }
725 
726 
727 //----------------------------------------------------------------
728 // intermediate_result_t
729 //
731 {
732 public:
734  {}
735 
737  {
738  *this = r;
739  }
740 
741  intermediate_result_t(unsigned int num_neighbors,
742  unsigned int mesh_rows,
743  unsigned int mesh_cols):
744  dx_(num_neighbors),
745  dy_(num_neighbors),
746  db_(num_neighbors)
747  {
748  for (unsigned int i = 0; i < num_neighbors; i++)
749  {
750  dx_[i] = make_image<image_t>(mesh_cols,
751  mesh_rows,
752  1.0,
753  0.0);
754 
755  dy_[i] = make_image<image_t>(mesh_cols,
756  mesh_rows,
757  1.0,
758  0.0);
759 
760  db_[i] = make_image<image_t>(mesh_cols,
761  mesh_rows,
762  1.0,
763  0.0);
764  }
765  }
766 
767  intermediate_result_t & operator = (const intermediate_result_t & r)
768  {
769  if (this != &r)
770  {
771  dx_.resize(r.dx_.size());
772  for (unsigned int i = 0; i < dx_.size(); i++)
773  {
774  dx_[i] = cast<image_t, image_t>(r.dx_[i]);
775  }
776 
777  dy_.resize(r.dy_.size());
778  for (unsigned int i = 0; i < dy_.size(); i++)
779  {
780  dy_[i] = cast<image_t, image_t>(r.dy_[i]);
781  }
782 
783  db_.resize(r.db_.size());
784  for (unsigned int i = 0; i < db_.size(); i++)
785  {
786  db_[i] = cast<image_t, image_t>(r.db_[i]);
787  }
788  }
789 
790  return *this;
791  }
792 
793  std::vector<image_t::Pointer> dx_;
794  std::vector<image_t::Pointer> dy_;
795  std::vector<image_t::Pointer> db_;
796 };
797 
798 //----------------------------------------------------------------
799 // calc_intermediate_results_t
800 //
801 template <typename img_ptr_t, typename msk_ptr_t>
803 {
804 public:
805  typedef typename img_ptr_t::ObjectType::Pointer::ObjectType TImage;
806  typedef typename msk_ptr_t::ObjectType::Pointer::ObjectType TMask;
807 
808  calc_intermediate_results_t(unsigned int thread_offset,
809  unsigned int thread_stride,
810  std::vector<itk::GridTransform::Pointer> & transform,
811  const std::vector<img_ptr_t> & warped_tile,
812  const std::vector<msk_ptr_t> & warped_mask,
813  const std::vector<the_dynamic_array_t<unsigned int> > & neighbors,
814  const bool & tiles_already_warped,
815  const unsigned int & neighborhood_size,
816  const double & minimum_overlap,
817  const bool & keep_first_tile_fixed,
818  std::vector<intermediate_result_t> & results):
819 
820  thread_offset_(thread_offset),
821  thread_stride_(thread_stride),
822  transform_(transform),
823  warped_tile_(warped_tile),
824  warped_mask_(warped_mask),
825  neighbors_(neighbors),
826  tiles_already_warped_(tiles_already_warped),
827  neighborhood_(neighborhood_size),
828  minimum_overlap_(minimum_overlap),
829  keep_first_tile_fixed_(keep_first_tile_fixed),
830  results_(results)
831  {}
832 
833  // virtual:
834  void execute(the_thread_interface_t * thread)
835  {
836  WRAP(the_terminator_t terminator("calc_intermediate_results_t::execute"));
837 
838  unsigned int num_tiles = warped_tile_.size();
839  unsigned int start = keep_first_tile_fixed_ ? 1 : 0;
840 
841  // setup the local neighborhood image buffers:
842  typename TImage::SpacingType sp = warped_tile_[start]->GetSpacing();
843  typename TImage::SizeType sz;
844  sz[0] = neighborhood_;
845  sz[1] = neighborhood_;
846 
847  typename TImage::Pointer img[] = {
848  make_image<TImage>(sp, sz),
849  make_image<TImage>(sp, sz)
850  };
851 
852  typename TMask::Pointer msk[] = {
853  make_image<TMask>(sp, sz),
854  make_image<TMask>(sp, sz)
855  };
856 
857  typename TImage::Pointer img_large;
858  typename TMask::Pointer msk_large;
859 
860  if (!tiles_already_warped_)
861  {
862  typename TImage::SizeType sz_large(sz);
863  sz_large[0] *= 2;
864  sz_large[1] *= 2;
865  img_large = make_image<TImage>(sp, sz_large);
866  msk_large = make_image<TMask>(sp, sz_large);
867  }
868 
869  std::ostringstream oss;
870  for (unsigned int tile_index = start; tile_index < num_tiles; tile_index++)
871  {
872  // shortcuts:
873  const unsigned int num_neighbors = neighbors_[tile_index].size();
874  const TImage * tile = warped_tile_[tile_index];
875  const TMask * mask = warped_mask_[tile_index];
876  const itk::GridTransform * transform = transform_[tile_index];
877 
878  const the_grid_transform_t & gt = transform->transform_;
879  const unsigned int mesh_cols = gt.cols_ + 1;
880  const unsigned int mesh_size = gt.grid_.mesh_.size();
881 
882  // shortcuts to intermediate mesh refinement results for this tile:
883  intermediate_result_t & results = results_[tile_index];
884 
885  for (unsigned int neighbor = 0; neighbor < num_neighbors; neighbor++)
886  {
887  WRAP(terminator.terminate_on_request());
888 
889  // shortcuts:
890  image_t::Pointer & dx = results.dx_[neighbor];
891  image_t::Pointer & dy = results.dy_[neighbor];
892  image_t::Pointer & db = results.db_[neighbor];
893 
894  const unsigned int neighbor_index = neighbors_[tile_index][neighbor];
895  oss << thread_offset_ << " thread, matching "
896  << tile_index << ":" << neighbor_index << std::endl;
897 
898  // shortcuts:
899  const TImage * neighbor_tile = warped_tile_[neighbor_index];
900  const TMask * neighbor_mask = warped_mask_[neighbor_index];
901  const base_transform_t * neighbor_xform = transform_[neighbor_index];
902 
903  for (unsigned int mesh_index = thread_offset_;
904  mesh_index < mesh_size;
905  mesh_index += thread_stride_)
906  {
907  // shortcut:
908  const vertex_t & vertex = gt.grid_.mesh_[mesh_index];
909 
910  // find the mosaic space coordinates of this vertex:
911  pnt2d_t center;
912  gt.transform_inv(vertex.uv_, center);
913 
914  // extract a neighborhood of the vertex from both tiles:
915  image_t::IndexType index;
916  index[0] = mesh_index % mesh_cols;
917  index[1] = mesh_index / mesh_cols;
918  dx->SetPixel(index, 0);
919  dy->SetPixel(index, 0);
920  db->SetPixel(index, 0);
921 
922  // feed the two neighborhoods into the FFT translation estimator:
923  vec2d_t shift(vec2d(0, 0));
924  bool ok = tiles_already_warped_ ?
925 
926  refine_one_point_fft(shift,
927 
928  // fixed:
929  neighbor_tile,
930  neighbor_mask,
931 
932  // moving:
933  tile,
934  mask,
935 
936  center,
937  minimum_overlap_,
938 
939  img[0].GetPointer(),
940  msk[0].GetPointer(),
941 
942  img[1].GetPointer(),
943  msk[1].GetPointer()) :
944 
945  refine_one_point_fft(shift,
946 
947  // fixed:
948  neighbor_tile,
949  neighbor_mask,
950 
951  // moving:
952  tile,
953  mask,
954 
955  neighbor_xform,
956  transform,
957 
958  center,
959  minimum_overlap_,
960 
961  sz,
962  sp,
963 
964  img_large.GetPointer(),
965  msk_large.GetPointer(),
966 
967  img[0].GetPointer(),
968  msk[0].GetPointer(),
969 
970  img[1].GetPointer(),
971  msk[1].GetPointer());
972  if (!ok)
973  {
974  continue;
975  }
976 
977  dx->SetPixel(index, shift[0]);
978  dy->SetPixel(index, shift[1]);
979  db->SetPixel(index, 1);
980  }
981  }
982  }
983  CORE_LOG_MESSAGE(oss.str());
984  }
985 
986  unsigned int thread_offset_;
987  unsigned int thread_stride_;
988  std::vector<itk::GridTransform::Pointer> & transform_;
989  const std::vector<img_ptr_t> & warped_tile_;
990  const std::vector<msk_ptr_t> & warped_mask_;
991  const std::vector<the_dynamic_array_t<unsigned int> > & neighbors_;
992  const bool tiles_already_warped_;
993  const unsigned int neighborhood_;
994  const double minimum_overlap_; // neighborhood overlap
995  const bool keep_first_tile_fixed_;
996  std::vector<intermediate_result_t> & results_;
997 };
998 
999 
1000 //----------------------------------------------------------------
1001 // update_tile_mesh_t
1002 //
1004 {
1005 public:
1006  update_tile_mesh_t(unsigned int tile_index,
1007  bool keep_first_tile_fixed,
1008  unsigned int median_filter_radius,
1009  std::vector<itk::GridTransform::Pointer> & transform,
1010  std::vector<intermediate_result_t> & results):
1011  tile_index_(tile_index),
1012  keep_first_tile_fixed_(keep_first_tile_fixed),
1013  median_filter_radius_(median_filter_radius),
1014  transform_(transform),
1015  results_(results)
1016  {}
1017 
1018  // virtual:
1019  void execute(the_thread_interface_t * thread)
1020  {
1021  WRAP(the_terminator_t terminator("update_tile_mesh_t::execute"));
1022 
1023  std::ostringstream oss;
1024  oss << tile_index_ << " mesh update" << std::endl;
1025  CORE_LOG_MESSAGE(oss.str());
1026 
1027  // shortcuts:
1028  itk::GridTransform * transform = transform_[tile_index_];
1029  the_grid_transform_t & gt = transform->transform_;
1030  const unsigned int mesh_size = gt.grid_.mesh_.size();
1031 
1032  // shortcuts to intermediate mesh refinement results for this tile:
1033  intermediate_result_t & results = results_[tile_index_];
1034  const unsigned int num_neighbors = results.dx_.size();
1035 
1036  std::vector<vec2d_t> shift(mesh_size, vec2d(0, 0));
1037  std::vector<double> mass(mesh_size, 0);
1038 
1039  for (unsigned int neighbor = 0; neighbor < num_neighbors; neighbor++)
1040  {
1041  WRAP(terminator.terminate_on_request());
1042 
1043  // shortcuts:
1044  image_t::Pointer & dx = results.dx_[neighbor];
1045  image_t::Pointer & dy = results.dy_[neighbor];
1046  image_t::Pointer & db = results.db_[neighbor];
1047 
1048  std::vector<vec2d_t> neighbor_pull(mesh_size, vec2d(0, 0));
1049  regularize_displacements(neighbor_pull,
1050  mass,
1051  dx,
1052  dy,
1053  db,
1054  median_filter_radius_);
1055 
1056  // blend the displacement vectors:
1057  for (unsigned int i = 0; i < mesh_size; i++)
1058  {
1059  shift[i] += neighbor_pull[i];
1060  }
1061  }
1062 
1063  // FIXME: if (num_neighbors > 1)
1064  if (!keep_first_tile_fixed_)
1065  {
1066  // normalize:
1067  for (unsigned int i = 0; i < mesh_size; i++)
1068  {
1069  double scale = 1 / (1 + mass[i]);
1070  shift[i] *= scale;
1071  }
1072  }
1073 
1074  gt.grid_.update(&(shift[0]));
1075  transform->setup(gt);
1076  }
1077 
1078  const unsigned int tile_index_;
1079  const bool keep_first_tile_fixed_;
1080  const unsigned int median_filter_radius_;
1081  std::vector<itk::GridTransform::Pointer> & transform_;
1082  std::vector<intermediate_result_t> & results_;
1083 };
1084 
1085 
1086 //----------------------------------------------------------------
1087 // refine_mosaic
1088 //
1089 // use FFT to refine the grid transforms directly:
1090 //
1091 template <typename img_ptr_t, typename msk_ptr_t>
1092 void
1093 refine_mosaic(std::vector<itk::GridTransform::Pointer> & transform,
1094  const std::vector<img_ptr_t> & tile,
1095  const std::vector<msk_ptr_t> & mask,
1096  const unsigned int & neighborhood,
1097  const bool & prewarp_tiles = true,
1098  const double & minimum_overlap = 0.25,
1099  const unsigned int & median_radius = 1,
1100  const unsigned int & num_passes = 1,
1101  const bool & keep_first_tile_fixed = false)
1102 {
1103  typedef itk::GridTransform itk_grid_transform_t;
1104  typedef typename img_ptr_t::ObjectType::Pointer::ObjectType TImage;
1105  typedef typename msk_ptr_t::ObjectType::Pointer::ObjectType TMask;
1106 
1107  // shortcut:
1108  unsigned int num_tiles = tile.size();
1109  if (num_tiles < 2) return;
1110 
1111  // image space bounding boxes:
1112  std::vector<pnt2d_t> image_min;
1113  std::vector<pnt2d_t> image_max;
1114  calc_image_bboxes<img_ptr_t>(tile, image_min, image_max);
1115 
1116  // mosaic space bounding boxes:
1117  std::vector<pnt2d_t> mosaic_min;
1118  std::vector<pnt2d_t> mosaic_max;
1119  calc_mosaic_bboxes<pnt2d_t, itk_grid_transform_t::Pointer>(transform,
1120  image_min,
1121  image_max,
1122  mosaic_min,
1123  mosaic_max,
1124  16);
1125 
1126  unsigned int start = keep_first_tile_fixed ? 1 : 0;
1127 
1128  // find overlapping neighbors for each tile:
1129  std::vector<the_dynamic_array_t<unsigned int> > neighbors(num_tiles);
1130  for (unsigned int i = start; i < num_tiles; i++)
1131  {
1132  the_aa_bbox_t ibox;
1133  ibox << p3x1_t(mosaic_min[i][0], mosaic_min[i][1], 0)
1134  << p3x1_t(mosaic_max[i][0], mosaic_max[i][1], 0);
1135 
1136  for (unsigned int j = 0; j < num_tiles; j++)
1137  {
1138  if (i == j) continue;
1139 
1140  the_aa_bbox_t jbox;
1141  jbox << p3x1_t(mosaic_min[j][0], mosaic_min[j][1], 0)
1142  << p3x1_t(mosaic_max[j][0], mosaic_max[j][1], 0);
1143 
1144  if (!ibox.intersects(jbox)) continue;
1145 
1146  neighbors[i].push_back(j);
1147  }
1148  }
1149 
1150  std::vector<typename TImage::Pointer> warped_tile(num_tiles);
1151  std::vector<typename TMask::Pointer> warped_mask(num_tiles);
1152 
1153  if (keep_first_tile_fixed)
1154  {
1155  warped_tile[0] = tile[0];
1156  warped_mask[0] = const_cast<TMask *>(mask[0].GetPointer());
1157  }
1158 
1159  std::ostringstream oss;
1160  for (unsigned int pass = 0; pass < num_passes; pass++)
1161  {
1162  oss << "--------------------------- pass " << pass
1163  << " ---------------------------" << std::endl;
1164 
1165  if (prewarp_tiles)
1166  {
1167  // warp the tiles:
1168  for (unsigned int i = start; i < num_tiles; i++)
1169  {
1170  oss << std::setw(4) << i << ". warping image tile" << std::endl;
1171  warped_tile[i] = warp<TImage>(tile[i], transform[i].GetPointer());
1172 
1173  if (mask[i].GetPointer() != NULL)
1174  {
1175  oss << " warping image tile mask" << std::endl;
1176  warped_mask[i] = warp<TMask>(mask[i], transform[i].GetPointer());
1177  }
1178  }
1179  }
1180 
1181  std::vector<std::vector<vec2d_t> > shift(num_tiles);
1182  for (unsigned int i = start; i < num_tiles; i++)
1183  {
1184  the_grid_transform_t & gt = transform[i]->transform_;
1185  unsigned int mesh_size = gt.grid_.mesh_.size();
1186 
1187  std::vector<std::vector<vec2d_t> > shift_i(neighbors[i].size());
1188  std::vector<double> mass(mesh_size, 0);
1189 
1190  for (unsigned int k = 0; k < neighbors[i].size(); k++)
1191  {
1192  unsigned int j = neighbors[i][k];
1193  oss << "matching " << i << ":" << j << std::endl;
1194 
1195  calc_displacements<TImage, TMask>(shift_i[k],
1196  mass,
1197 
1198  // tiles-already-warped flag:
1199  prewarp_tiles,
1200 
1201  // fixed:
1202  warped_tile[j],
1203  warped_mask[j],
1204  transform[j].GetPointer(),
1205 
1206  // moving:
1207  warped_tile[i],
1208  warped_mask[i],
1209  transform[i].GetPointer(),
1210 
1211  neighborhood,
1212  minimum_overlap,
1213  median_radius);
1214  }
1215 
1216  // blend the displacement vectors:
1217  shift[i].assign(mesh_size, vec2d(0, 0));
1218 
1219  for (unsigned int j = 0; j < shift_i.size(); j++)
1220  {
1221  for (unsigned int k = 0; k < mesh_size; k++)
1222  {
1223  shift[i][k] += shift_i[j][k];
1224  }
1225  }
1226 
1227  if (!keep_first_tile_fixed)
1228  {
1229  // normalize:
1230  for (unsigned int k = 0; k < mesh_size; k++)
1231  {
1232  double scale = 1 / (1 + mass[k]);
1233  shift[i][k] *= scale;
1234  }
1235  }
1236  }
1237 
1238  for (unsigned int i = start; i < num_tiles; i++)
1239  {
1240  the_grid_transform_t & gt = transform[i]->transform_;
1241  gt.grid_.update(&(shift[i][0]));
1242  transform[i]->setup(gt);
1243  }
1244  }
1245  CORE_LOG_MESSAGE(oss.str());
1246 }
1247 
1248 
1249 //----------------------------------------------------------------
1250 // warp_tile_transaction_t
1251 //
1252 template <typename img_ptr_t, typename msk_ptr_t>
1254 {
1255 public:
1256  typedef typename img_ptr_t::ObjectType::Pointer::ObjectType TImage;
1257  typedef typename msk_ptr_t::ObjectType::Pointer::ObjectType TMask;
1258 
1259  warp_tile_transaction_t(unsigned int tile_index,
1260  const std::vector<itk::GridTransform::Pointer> &
1261  transform,
1262  const std::vector<img_ptr_t> & tile,
1263  const std::vector<msk_ptr_t> & mask,
1264  std::vector<typename TImage::Pointer> & warped_tile,
1265  std::vector<typename TMask::Pointer> & warped_mask):
1266  tile_index_(tile_index),
1267  transform_(transform),
1268  tile_(tile),
1269  mask_(mask),
1270  warped_tile_(warped_tile),
1271  warped_mask_(warped_mask)
1272  {}
1273 
1274  // virtual:
1275  void execute(the_thread_interface_t * thread)
1276  {
1277  WRAP(the_terminator_t terminator("warp_tile_transaction_t"));
1278 
1279  std::ostringstream oss;
1280  oss << setw(4) << tile_index_ << ". warping image tile" << std::endl;
1281  warped_tile_[tile_index_] =
1282  warp<TImage>(tile_[tile_index_],
1283  transform_[tile_index_].GetPointer());
1284 
1285  if (mask_[tile_index_].GetPointer() != NULL)
1286  {
1287  oss << setw(4) << tile_index_ << ". warping image tile mask" << std::endl;
1288  warped_mask_[tile_index_] =
1289  warp<TMask>(mask_[tile_index_],
1290  transform_[tile_index_].GetPointer());
1291  }
1292  CORE_LOG_MESSAGE(oss.str());
1293  }
1294 
1295  unsigned int tile_index_;
1296  const std::vector<itk::GridTransform::Pointer> & transform_;
1297  const std::vector<img_ptr_t> & tile_;
1298  const std::vector<msk_ptr_t> & mask_;
1299  std::vector<typename TImage::Pointer> & warped_tile_;
1300  std::vector<typename TMask::Pointer> & warped_mask_;
1301 };
1302 
1303 
1304 //----------------------------------------------------------------
1305 // refine_one_tile_t
1306 //
1307 template <typename img_ptr_t, typename msk_ptr_t>
1309 {
1310 public:
1311  typedef typename img_ptr_t::ObjectType::Pointer::ObjectType TImage;
1312  typedef typename msk_ptr_t::ObjectType::Pointer::ObjectType TMask;
1313 
1314  refine_one_tile_t(unsigned int tile_index,
1315  std::vector<itk::GridTransform::Pointer> & transform,
1316  const std::vector<img_ptr_t> & warped_tile,
1317  const std::vector<msk_ptr_t> & warped_mask,
1318  const std::vector<the_dynamic_array_t<unsigned int> > & neighbors,
1319  const bool & tiles_already_warped,
1320  const unsigned int & neighborhood_size,
1321  const double & minimum_overlap, // neighbrhood overlap
1322  const unsigned int & median_filter_radius, // for outliers
1323  const bool & keep_first_tile_fixed,
1324  std::vector<std::vector<vec2d_t> > & shift):
1325 
1326  tile_index_(tile_index),
1327  transform_(transform),
1328  warped_tile_(warped_tile),
1329  warped_mask_(warped_mask),
1330  neighbors_(neighbors),
1331  tiles_already_warped_(tiles_already_warped),
1332  neighborhood_(neighborhood_size),
1333  minimum_overlap_(minimum_overlap),
1334  median_radius_(median_filter_radius),
1335  keep_first_tile_fixed_(keep_first_tile_fixed),
1336  shift_(shift)
1337  {}
1338 
1339  // virtual:
1340  void execute(the_thread_interface_t * thread)
1341  {
1342  WRAP(the_terminator_t terminator("refine_one_tile_t"));
1343 
1344  the_grid_transform_t & gt = transform_[tile_index_]->transform_;
1345  unsigned int mesh_size = gt.grid_.mesh_.size();
1346  std::vector<double> mass(mesh_size, 0);
1347 
1348  unsigned int num_neighbors = neighbors_[tile_index_].size();
1349  std::vector<std::vector<vec2d_t> > shift_i(num_neighbors);
1350 
1351  std::ostringstream oss;
1352  for (unsigned int k = 0; k < num_neighbors; k++)
1353  {
1354  unsigned int j = neighbors_[tile_index_][k];
1355  oss << "matching " << tile_index_ << ":" << j << std::endl;
1356 
1357  calc_displacements<TImage, TMask>(shift_i[k],
1358  mass,
1359 
1360  // tiles-already-warped flag:
1361  tiles_already_warped_,
1362 
1363  // fixed:
1364  warped_tile_[j],
1365  warped_mask_[j],
1366  transform_[j].GetPointer(),
1367 
1368  // moving:
1369  warped_tile_[tile_index_],
1370  warped_mask_[tile_index_],
1371  transform_[tile_index_].GetPointer(),
1372 
1373  neighborhood_,
1374  minimum_overlap_,
1375  median_radius_);
1376  }
1377 
1378  // blend the displacement vectors:
1379  shift_[tile_index_].assign(mesh_size, vec2d(0, 0));
1380 
1381  for (unsigned int j = 0; j < num_neighbors; j++)
1382  {
1383  for (unsigned int k = 0; k < mesh_size; k++)
1384  {
1385  shift_[tile_index_][k] += shift_i[j][k];
1386  }
1387  }
1388 
1389  if (!keep_first_tile_fixed_)
1390  {
1391  // normalize:
1392  for (unsigned int k = 0; k < mesh_size; k++)
1393  {
1394  double scale = 1 / (1 + mass[k]);
1395  shift_[tile_index_][k] *= scale;
1396  }
1397  }
1398  CORE_LOG_MESSAGE(oss.str());
1399  }
1400 
1401  unsigned int tile_index_;
1402  std::vector<itk::GridTransform::Pointer> & transform_;
1403  const std::vector<img_ptr_t> & warped_tile_;
1404  const std::vector<msk_ptr_t> & warped_mask_;
1405  const std::vector<the_dynamic_array_t<unsigned int> > & neighbors_;
1406  const bool tiles_already_warped_;
1407  const unsigned int neighborhood_;
1408  const double minimum_overlap_; // neighbrhood overlap
1409  const unsigned int median_radius_; // for outliers
1410  const bool keep_first_tile_fixed_;
1411  std::vector<std::vector<vec2d_t> > & shift_;
1412 };
1413 
1414 
1415 //----------------------------------------------------------------
1416 // refine_mosaic_mt
1417 //
1418 template<typename img_ptr_t, typename msk_ptr_t>
1419 void
1420 refine_mosaic_mt(std::vector<itk::GridTransform::Pointer> & transform,
1421  const std::vector<img_ptr_t> & tile,
1422  const std::vector<msk_ptr_t> & mask,
1423  const unsigned int & neighborhood_size,
1424  const bool & prewarp_tiles, // FIXME: always true?
1425  const double & minimum_overlap, // neighbrhood overlap
1426  const unsigned int & median_radius, // for outliers
1427  const unsigned int & num_passes,
1428  const bool & keep_first_tile_fixed, // FIXME: stos only?
1429  const double & displacement_threshold,
1430  unsigned int num_threads) // max concurrent threads
1431 {
1432  if (num_threads == 1)
1433  {
1434  refine_mosaic<img_ptr_t, msk_ptr_t>(transform,
1435  tile,
1436  mask,
1437  neighborhood_size,
1438  prewarp_tiles,
1439  minimum_overlap,
1440  median_radius,
1441  num_passes,
1442  keep_first_tile_fixed);
1443  return;
1444  }
1445 
1446  typedef itk::GridTransform itk_grid_transform_t;
1447  typedef typename img_ptr_t::ObjectType::Pointer::ObjectType TImage;
1448  typedef typename msk_ptr_t::ObjectType::Pointer::ObjectType TMask;
1449 
1450  // shortcut:
1451  unsigned int num_tiles = tile.size();
1452  if (num_tiles < 2) return;
1453 
1454  // image space bounding boxes:
1455  std::vector<pnt2d_t> image_min;
1456  std::vector<pnt2d_t> image_max;
1457  calc_image_bboxes<img_ptr_t>(tile, image_min, image_max);
1458 
1459  // mosaic space bounding boxes:
1460  std::vector<pnt2d_t> mosaic_min;
1461  std::vector<pnt2d_t> mosaic_max;
1462  const unsigned int SAMPLE_POINTS_ALONG_IMAGE_EDGES = 16;
1463  calc_mosaic_bboxes<pnt2d_t, itk_grid_transform_t::Pointer>(transform,
1464  image_min,
1465  image_max,
1466  mosaic_min,
1467  mosaic_max,
1468  SAMPLE_POINTS_ALONG_IMAGE_EDGES);
1469 
1470  // Relative to the image size.
1471  //double threshold =
1472  // displacement_threshold * std::abs(mosaic_min[0][0] - mosaic_max[0][0]);
1473 
1474  // Relative to a single pixel.
1475  double threshold = displacement_threshold;
1476 
1477  unsigned int start = keep_first_tile_fixed ? 1 : 0;
1478 
1479  // find overlapping neighbors for each tile:
1480  std::vector<the_dynamic_array_t<unsigned int> > neighbors(num_tiles);
1481  for (unsigned int i = start; i < num_tiles; i++)
1482  {
1483  the_aa_bbox_t ibox;
1484  ibox << p3x1_t(mosaic_min[i][0], mosaic_min[i][1], 0)
1485  << p3x1_t(mosaic_max[i][0], mosaic_max[i][1], 0);
1486 
1487  for (unsigned int j = 0; j < num_tiles; j++)
1488  {
1489  if (i == j) continue;
1490 
1491  the_aa_bbox_t jbox;
1492  jbox << p3x1_t(mosaic_min[j][0], mosaic_min[j][1], 0)
1493  << p3x1_t(mosaic_max[j][0], mosaic_max[j][1], 0);
1494 
1495  if (!ibox.intersects(jbox)) continue;
1496 
1497  neighbors[i].push_back(j);
1498  }
1499  }
1500 
1501  std::vector<typename TImage::Pointer> warped_tile(num_tiles);
1502  std::vector<typename TMask::Pointer> warped_mask(num_tiles);
1503 
1504  double last_average = std::numeric_limits<double>::max();
1505 
1506  // Initialize "warped" tiles.
1507  // If warping is actually requested do it on each pass
1508  for (unsigned int i = 0; i < num_tiles; i++)
1509  {
1510  warped_tile[i] = tile[i];
1511  warped_mask[i] = const_cast<TMask *>(mask[i].GetPointer());
1512  }
1513 
1514  the_thread_pool_t thread_pool(num_threads);
1515  thread_pool.set_idle_sleep_duration(50); // 50 usec
1516 
1517  std::ostringstream oss;
1518  for (unsigned int pass = 0; pass < num_passes; pass++)
1519  {
1520  double major_percent = 0.15 + 0.8 * ((double)pass/(double)num_passes);
1521  double next_major = 0.15 + 0.8 * ((double)(pass + 1)/(double)num_passes);
1522  set_major_progress(major_percent);
1523 
1524  oss << "--------------------------- pass " << pass
1525  << " ---------------------------" << std::endl;
1526 
1527  if (prewarp_tiles)
1528  {
1529  // warp the tiles -- split into a set of transactions and executed:
1530 
1531  std::list<the_transaction_t *> schedule;
1532  for (unsigned int i = start; i < num_tiles; i++)
1533  {
1536  transform,
1537  tile,
1538  mask,
1539  warped_tile,
1540  warped_mask);
1541  schedule.push_back(t);
1542  }
1543 
1544  thread_pool.push_back(schedule);
1545  thread_pool.pre_distribute_work();
1546  suspend_itk_multithreading_t suspend_itk_mt;
1547  thread_pool.start();
1548  thread_pool.wait();
1549  }
1550 
1551  set_minor_progress(0.2, next_major);
1552 
1553 //#if 1
1554  // calculating displacements:
1555  std::vector<std::vector<vec2d_t> > shift(num_tiles);
1556 
1557  // this is the original coarse-scale parallelization:
1558  unsigned int num_tiles_distributed =
1559  (num_tiles - start) - (num_tiles - start) % num_threads;
1560 
1561  unsigned int num_tiles_remaining =
1562  (num_tiles - start) - num_tiles_distributed;
1563 
1564  std::list<the_transaction_t *> schedule;
1565  for (unsigned int i = 0; i < num_tiles_distributed; i++)
1566  {
1567  unsigned int index = start + i;
1568 
1569  typedef
1571  tile_transaction_t;
1572 
1573  tile_transaction_t * t = new tile_transaction_t(index,
1574  transform,
1575  warped_tile,
1576  warped_mask,
1577  neighbors,
1578  prewarp_tiles,
1579  neighborhood_size,
1580  minimum_overlap,
1581  median_radius,
1582  keep_first_tile_fixed,
1583  shift);
1584  schedule.push_back(t);
1585  }
1586 
1587  thread_pool.push_back(schedule);
1588  // thread_pool.pre_distribute_work();
1589  suspend_itk_multithreading_t suspend_itk_mt;
1590  thread_pool.start();
1591  thread_pool.wait();
1592 
1593  set_minor_progress(0.9, next_major);
1594 
1595  // this is the broken fine-scale parallelization:
1596  for (unsigned int i = 0; i < num_tiles_remaining; i++)
1597  {
1598  unsigned int index = start + num_tiles_distributed + i;
1599 
1600  the_grid_transform_t & gt = transform[index]->transform_;
1601  unsigned int mesh_size = gt.grid_.mesh_.size();
1602 
1603  std::vector<std::vector<vec2d_t> > shift_i(neighbors[index].size());
1604  std::vector<double> mass(mesh_size, 0);
1605 
1606  for (unsigned int k = 0; k < neighbors[index].size(); k++)
1607  {
1608  unsigned int j = neighbors[index][k];
1609  oss << "matching " << index << ":" << j << std::endl;
1610 
1611  calc_displacements_mt<TImage, TMask>(num_threads,
1612  shift_i[k],
1613  mass,
1614 
1615  // tiles-already-warped flag:
1616  prewarp_tiles,
1617 
1618  // fixed:
1619  warped_tile[j],
1620  warped_mask[j],
1621  transform[j].GetPointer(),
1622 
1623  // moving:
1624  warped_tile[index],
1625  warped_mask[index],
1626  transform[index].GetPointer(),
1627 
1628  neighborhood_size,
1629  minimum_overlap,
1630  median_radius);
1631  }
1632 
1633  // blend the displacement vectors:
1634  shift[index].assign(mesh_size, vec2d(0, 0));
1635 
1636  for (unsigned int j = 0; j < shift_i.size(); j++)
1637  {
1638  for (unsigned int k = 0; k < mesh_size; k++)
1639  {
1640  shift[index][k] += shift_i[j][k];
1641  }
1642  }
1643 
1644  if (!keep_first_tile_fixed)
1645  {
1646  // normalize:
1647  for (unsigned int k = 0; k < mesh_size; k++)
1648  {
1649  double scale = 1 / (1 + mass[k]);
1650  shift[index][k] *= scale;
1651  }
1652  }
1653  }
1654 
1655  // once all displacement calculations are
1656  // finished the transform grids can be updated and we
1657  // can move on to the next pass:
1658 
1659  for (unsigned int i = start; i < num_tiles; i++)
1660  {
1661  the_grid_transform_t & gt = transform[i]->transform_;
1662  gt.grid_.update(&(shift[i][0]));
1663  transform[i]->setup(gt);
1664  }
1665 
1666 //#else
1667 // // this is the "improved" fine scale parallelization:
1668 //
1669 // // setup intermediate mesh refinement result structures:
1670 // std::vector<intermediate_result_t> results(num_tiles);
1671 // for (unsigned int i = start; i < num_tiles; i++)
1672 // {
1673 // const unsigned int num_neighbors = neighbors[i].size();
1674 // const the_grid_transform_t & gt = transform[i]->transform_;
1675 // const unsigned int mesh_rows = gt.rows_ + 1;
1676 // const unsigned int mesh_cols = gt.cols_ + 1;
1677 //
1678 // intermediate_result_t & result = results[i];
1679 // result = intermediate_result_t(num_neighbors,
1680 // mesh_rows,
1681 // mesh_cols);
1682 // }
1683 //
1684 // std::list<the_transaction_t *> schedule;
1685 // for (unsigned int i = 0; i < num_threads; i++)
1686 // {
1687 // typedef
1688 // calc_intermediate_results_t
1689 // <typename TImage::Pointer, typename TMask::Pointer>
1690 // tile_transaction_t;
1691 //
1692 // tile_transaction_t * t = new tile_transaction_t(i,
1693 // num_threads,
1694 // transform,
1695 // warped_tile,
1696 // warped_mask,
1697 // neighbors,
1698 // prewarp_tiles,
1699 // neighborhood_size,
1700 // minimum_overlap,
1701 // keep_first_tile_fixed,
1702 // results);
1703 // schedule.push_back(t);
1704 // }
1705 //
1706 // thread_pool.push_back(schedule);
1707 // thread_pool.pre_distribute_work();
1708 // suspend_itk_multithreading_t suspend_itk_mt;
1709 // thread_pool.start();
1710 // thread_pool.wait();
1711 //
1712 // for (unsigned int i = start; i < num_tiles; i++)
1713 // {
1714 // update_tile_mesh_t * t = new update_tile_mesh_t(i,
1715 // keep_first_tile_fixed,
1716 // median_radius,
1717 // transform,
1718 // results);
1719 // schedule.push_back(t);
1720 // }
1721 //
1722 // thread_pool.push_back(schedule);
1723 // thread_pool.pre_distribute_work();
1724 // thread_pool.start();
1725 // thread_pool.wait();
1726 //#endif
1727  double worst = 0.0, avg = 0.0, count = 0.0;
1728  for (int i = 0; i < shift.size(); i++)
1729  {
1730  for (int k = 0; k < shift[i].size(); k++)
1731  {
1732  if ( std::abs(shift[i][k][0]) > worst )
1733  worst = std::abs(shift[i][k][0]);
1734  if ( std::abs(shift[i][k][1]) > worst )
1735  worst = std::abs(shift[i][k][1]);
1736 
1737  avg += std::abs(shift[i][k][0]) + std::abs(shift[i][k][1]);
1738  count += 2;
1739  }
1740  }
1741  avg /= count;
1742  std::cout << pass << " Average Displacement: " << avg
1743  << " Max Displacement: " << worst << std::endl;
1744 
1745  // If there's an exact cutoff...
1746  if ( count > 0 )
1747  {
1748  if ( avg <= threshold )
1749  break;
1750  else if ( avg >= last_average )
1751  break;
1752  last_average = avg;
1753  }
1754  }
1755  CORE_LOG_MESSAGE(oss.str());
1756 }
1757 
1758 
1759 #endif // MOSAIC_REFINEMENT_COMMON_HXX_
Definition: v3x1p3x1.hxx:826
Computes mean pixel variance within the overlapping regions of a mosaic.
Definition: itkImageMosaicVarianceMetric.h:69
Definition: mosaic_refinement_common.hxx:1253
Definition: mosaic_refinement_common.hxx:1308
Definition: optimizer_observer.hxx:44
Definition: mosaic_refinement_common.hxx:802
Definition: the_transaction.hxx:55
Definition: itkGridTransform.h:53
Definition: mosaic_refinement_common.hxx:401
Definition: the_dynamic_array.hxx:45
Definition: the_terminator.hxx:58
Definition: mosaic_refinement_common.hxx:730
Definition: the_thread_pool.hxx:105
Definition: the_aa_bbox.hxx:81
Definition: itkDiscontinuousTransform.h:186
Definition: common.hxx:619
Definition: the_thread_interface.hxx:57
Definition: mosaic_refinement_common.hxx:1003
Definition: itkDiscontinuousTransform.h:56