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.
stos_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 : stos_common.hxx
30 // Author : Pavel A. Koshevoy
31 // Created : 2006/05/31 14:29
32 // Copyright : (C) 2004-2008 University of Utah
33 // Description : Helper functions for slice to slice registration.
34 
35 #ifndef STOS_COMMON_HXX_
36 #define STOS_COMMON_HXX_
37 
38 // local includes:
39 #include <Core/ITKCommon/STOS/stos.hxx>
40 #include <Core/ITKCommon/common.hxx>
41 #include <Core/ITKCommon/the_text.hxx>
42 
43 // Boost includes:
44 #include <boost/shared_ptr.hpp>
45 #include <boost/filesystem.hpp>
46 #include <boost/algorithm/string/predicate.hpp>
47 namespace bfs=boost::filesystem;
48 
49 // system includes:
50 #include <fstream>
51 #include <list>
52 
53 
54 //----------------------------------------------------------------
55 // sort
56 //
57 // Sort the list so that the slices are sequential.
58 //
59 // Return true if the list was sorted successfully.
60 //
61 // Return false otherwise (may happen if there are
62 // disconnects or duplicates present).
63 //
64 template <typename TImage>
65 bool
66 sort(std::list<stos_t<TImage> > & stos)
67 {
68  if (stos.empty()) return true;
69 
70  // FIXME: this function should be retired, use stos_tree_t instead
71  //
72  // this function only works if there is at most 1 outbound mapping
73  // from any node to any other node
74  //
75  // so 0:1, 1:2, 2:3 will work, only 1 outbound mapping from each source node
76  // but 0:1, 0:2, 2:3 will not work, 0 node has 2 outbound mappings to 1 and 2
77  //
78  std::list<stos_t<TImage> > sorted;
79  sorted.push_back(remove_head(stos));
80 
81  while (!stos.empty())
82  {
83  // find a match for the head:
84  bool found_head_match = false;
85  for (typename std::list<stos_t<TImage> >::iterator i = stos.begin();
86  i != stos.end();)
87  {
88  const bfs::path & head = sorted.front().fn_[0];
89  const bfs::path & tail = (*i).fn_[1];
90  if (head == tail)
91  {
92  found_head_match = true;
93  sorted.push_front(*i);
94  i = stos.erase(i);
95  }
96  else
97  {
98  ++i;
99  }
100  }
101 
102  // find a match for the tail:
103  bool found_tail_match = false;
104  for (typename std::list<stos_t<TImage> >::iterator i = stos.begin();
105  i != stos.end();)
106  {
107  const bfs::path & tail = sorted.back().fn_[1];
108  const bfs::path & head = (*i).fn_[0];
109  if (head == tail)
110  {
111  found_tail_match = true;
112  sorted.push_back(*i);
113  i = stos.erase(i);
114  }
115  else
116  {
117  ++i;
118  }
119  }
120 
121  if (!found_head_match && !found_tail_match)
122  {
123  // could not find a match for either head or tail:
124  break;
125  }
126  }
127 
128  if (!stos.empty()) return false;
129 
130  stos.splice(stos.end(), sorted);
131  return true;
132 }
133 
134 
135 //----------------------------------------------------------------
136 // load_mosaic
137 //
138 template <typename T>
139 bool
140 load_mosaic(const bfs::path & fn_mosaic,
141  const bool & flip_mosaic,
142  const unsigned int & shrink_factor,
143  const bool & assemble_mosaic,
144  const bfs::path & image_path,
145  typename T::Pointer & mosaic,
146  const bool & assemble_mosaic_mask,
147  mask_t::Pointer & mosaic_mask,
148  const double & clahe_slope = 1.0,
149  const unsigned int & clahe_window = 64,
150  typename T::PixelType clahe_new_min = 0,
151  typename T::PixelType clahe_new_max = 255,
152  const bool & dont_allocate = false)
153 {
154 // std::cout << " reading " << fn_mosaic;
155 
156  std::fstream fin;
157  fin.open(fn_mosaic.c_str(), std::ios::in);
158  if (! fin.is_open())
159  {
160 // std::cout << ", failed...." << std::endl;
161  return false;
162  }
163 // std::cout << std::endl;
164 
165  std::list<bfs::path> fn_tiles;
166  std::vector<base_transform_t::Pointer> transform;
167  std::vector<typename T::ConstPointer> image;
168  std::vector<mask_t::ConstPointer> mask;
169 
170  double pixel_spacing = 1.0;
171  bool use_std_mask = false;
172  load_mosaic<base_transform_t>(fin,
173  pixel_spacing,
174  use_std_mask,
175  fn_tiles,
176  transform,
177  image_path);
178  fin.close();
179 
180  unsigned int num_images = transform.size();
181  assert(pixel_spacing != 0);
182 
183  image.resize(num_images);
184  mask.resize(num_images);
185 
186 // bfs::path mosaic_dir = IRPath::DirectoryFromPath( fn_mosaic );
187  bfs::path mosaic_dir = fn_mosaic.parent_path();
188 
189  unsigned int i = 0;
190  for (std::list<bfs::path>::const_iterator iter = fn_tiles.begin();
191  iter != fn_tiles.end(); ++iter, i++)
192  {
193  const bfs::path & fn_image = *iter;
194 
195  // read the image:
196 // std::cout << std::setw(3) << i << " ";
197  image[i] = std_tile<T>(fn_image, shrink_factor, pixel_spacing);
198  mask[i] = std_mask<T>(image[i], use_std_mask);
199  }
200 
201  // assempble the mosaic:
202  if (assemble_mosaic)
203  {
204  std::cout << " assembling a mosaic " << std::endl;
205  mosaic = make_mosaic<typename T::ConstPointer, base_transform_t::Pointer>
206  (FEATHER_BLEND_E, transform, image, mask, dont_allocate);
207 
208  // reset the mosaic image origin:
209  typename T::PointType origin = mosaic->GetOrigin();
210  origin[0] = 0;
211  origin[1] = 0;
212  mosaic->SetOrigin(origin);
213 
214  if (flip_mosaic && !dont_allocate)
215  {
216  mosaic = flip<T>(mosaic);
217  }
218  }
219 
220  if (assemble_mosaic_mask)
221  {
222  std::cout << " assembling a mosaic mask " << std::endl;
223  mosaic_mask = make_mask<base_transform_t::Pointer>(transform, mask);
224 
225  // reset the mosaic image origin:
226  mask_t::PointType origin = mosaic_mask->GetOrigin();
227  origin[0] = 0;
228  origin[1] = 0;
229  mosaic_mask->SetOrigin(origin);
230 
231  if (flip_mosaic)
232  {
233  mosaic_mask = flip<mask_t>(mosaic_mask);
234  }
235  }
236 
237  // preprocess the image with Contrast Limited Adaptive Histogram
238  // Equalization algorithm, remap the intensities into the [0, 1] range:
239  if (assemble_mosaic && !dont_allocate && clahe_slope > 1.0)
240  {
241  std::cout << " enhancing contrast with CLAHE " << std::endl;
242  mosaic = CLAHE<T>(mosaic,
243  clahe_window,
244  clahe_window,
245  clahe_slope,
246  256, // number of bins
247  clahe_new_min,
248  clahe_new_max,
249  mosaic_mask);
250  }
251 
252  return true;
253 }
254 
255 //----------------------------------------------------------------
256 // load_mosaic
257 //
258 // Backwards compatibility API
259 //
260 template <typename T>
261 bool
262 load_mosaic(const bfs::path & fn_mosaic,
263  const bool & flip_mosaic,
264  const unsigned int & shrink_factor,
265  const double & clahe_slope,
266  const bfs::path & image_path,
267  typename T::Pointer & mosaic,
268  mask_t::Pointer & mosaic_mask,
269  bool mosaic_mask_enabled = true,
270  bool dont_allocate = false,
271  typename T::PixelType clahe_new_min = 0,
272  typename T::PixelType clahe_new_max = 255)
273 {
274  return load_mosaic<T>(fn_mosaic,
275  flip_mosaic,
276  shrink_factor,
277  true, // assemble mosaic
278  image_path,
279  mosaic,
280  mosaic_mask_enabled, // assemble mosaic mask
281  mosaic_mask,
282  clahe_slope,
283  64, // clahe window
284  clahe_new_min,
285  clahe_new_max,
286  dont_allocate);
287 }
288 
289 //----------------------------------------------------------------
290 // load_slice
291 //
292 inline bool
293 load_slice(const bfs::path & fn_mosaic,
294  const bool & flip,
295  const unsigned int & shrink_factor,
296  const double & clahe_slope,
297  const bfs::path & image_path,
298  image_t::Pointer & mosaic,
299  mask_t::Pointer & mosaic_mask,
300  bool mosaic_mask_enabled = true,
301  bool dont_allocate = false)
302 {
303  return load_mosaic<image_t>(fn_mosaic,
304  flip,
305  shrink_factor,
306  clahe_slope,
307  image_path,
308  mosaic,
309  mosaic_mask,
310  mosaic_mask_enabled,
311  dont_allocate,
312  0.0,
313  1.0);
314 }
315 
316 //----------------------------------------------------------------
317 // load_slice
318 //
319 inline static bool
320 load_slice(const bfs::path & fn_mosaic,
321  const bool & flip,
322  const unsigned int & shrink_factor,
323  const double & clahe_slope,
324  const bfs::path & image_path,
325  image_t::Pointer & mosaic,
326  bool dont_allocate = false)
327 {
328  mask_t::Pointer dummy;
329  return load_slice(fn_mosaic,
330  flip,
331  shrink_factor,
332  clahe_slope,
333  image_path,
334  mosaic,
335  dummy,
336  false,
337  dont_allocate);
338 }
339 
340 
341 //----------------------------------------------------------------
342 // stos_tree_t
343 //
344 template <typename TImage>
346 {
347 public:
348 
349  struct node_t;
350 
351  //----------------------------------------------------------------
352  // link_t
353  //
354  struct link_t
355  {
356  link_t()
357  {}
358 
359  link_t(const boost::shared_ptr<node_t> & a,
360  const boost::shared_ptr<node_t> & b,
361  const stos_t<TImage> & stos):
362  a_(a),
363  b_(b),
364  stos_(stos)
365  {}
366 
367  inline bool operator < (const link_t & l) const
368  {
369  return (stos_.fn_[1] < l.stos_.fn_[1]);
370  }
371 
372  boost::shared_ptr<node_t> a_;
373  boost::shared_ptr<node_t> b_;
374  stos_t<TImage> stos_;
375  };
376 
377  //----------------------------------------------------------------
378  // node_t
379  //
380  struct node_t
381  {
382  node_t(const bfs::path & fn_data,
383  const bfs::path & image_dir,
384  const bfs::path & fn_mask,
385  bool flipped,
386  const vec2d_t & spacing):
387  fn_data_(fn_data),
388  image_dir_(image_dir),
389  fn_mask_(fn_mask),
390  flipped_(flipped),
391  spacing_(spacing)
392  {}
393 
394  // load the image data and image mask for this section:
395  void
396  load_data(typename TImage::Pointer & data,
397  mask_t::Pointer & mask,
398  unsigned int shrink_factor = 1,
399  double clahe_slope = 1,
400  unsigned int clahe_window = 64) const
401  {
402  if (fn_data_.extension() == ".mosaic")
403  {
404  load_mosaic<TImage>(fn_data_,
405  flipped_,
406  shrink_factor,
407  false,// assemble mosaic
408  image_dir_,
409  data,
410  true, // assemble mosaic mask
411  mask,
412  clahe_slope,
413  clahe_window);
414  }
415  else
416  {
417  data = std_tile<TImage>(fn_data_,
418  shrink_factor,
419  std::max(spacing_[0], spacing_[1]));
420 
421  if (fn_mask_.empty())
422  {
423  mask = cast<TImage, mask_t>(data);
424  mask->FillBuffer(1);
425  }
426  else
427  {
428  mask = std_tile<mask_t>(fn_mask_,
429  shrink_factor,
430  std::max(spacing_[0], spacing_[1]));
431  }
432  }
433  }
434 
435  // calc complete overlap region between this node
436  // and all its children, recursively:
437  mask_t::Pointer
438  calc_overlap_mask(unsigned int shrink_factor = 1,
439  double clahe_slope = 1,
440  unsigned int clahe_window = 64) const
441  {
442  // load this nodes mask:
443  typename TImage::Pointer data;
444  mask_t::Pointer mask;
445  load_data(data, mask, shrink_factor, clahe_slope, clahe_window);
446 
447  // we don't care about the image data here, get rid of it:
448  data = NULL;
449 
450  // gather childrens masks and clip this nodes mask against it children:
451  for (typename std::list<link_t>::const_iterator
452  i = children_.begin(); i != children_.end(); ++i)
453  {
454  const link_t & link = *i;
455 
456  // recurse on the child:
457  mask_t::Pointer imask = link.b_->calc_overlap_mask(shrink_factor,
458  clahe_slope,
459  clahe_window);
460 
461  // NOTE: this was added per James request -- don't clip
462  // againt the child node if it is a leaf node.
463  if (!link.b_->children_.empty())
464  {
465  // clip this nodes mask against the childs mask:
466  base_transform_t::ConstPointer t = link.stos_.t01_.GetPointer();
467 
468  if ( mask != mask_t::Pointer(NULL) && imask != mask_t::Pointer(NULL) )
469  clip_mask_a_by_b(mask, imask, t);
470  }
471  }
472 
473  // done:
474  return mask;
475  }
476 
477  // helper used by calc_overlap_mask:
478  static void clip_mask_a_by_b(mask_t::Pointer & mask_a,
479  mask_t::Pointer & mask_b,
480  const base_transform_t::ConstPointer & t_ab)
481  {
482  // FIXME: this could be parallelized:
483 
484  // iterate over the previous mask, generate the next mask:
485  typedef itk::LinearInterpolateImageFunction<mask_t, double> imask_t;
486  imask_t::Pointer imask_b = imask_t::New();
487  imask_b->SetInputImage(mask_b);
488 
489  // temporaries:
490  pnt2d_t pt_a;
491  pnt2d_t pt_b;
492 
493  const base_transform_t * t = t_ab.GetPointer();
494 
495  typedef typename TImage::RegionType rn_t;
496  typedef typename TImage::RegionType::SizeType sz_t;
497  typedef typename TImage::RegionType::IndexType ix_t;
498 
499  rn_t region = mask_a->GetLargestPossibleRegion();
500  ix_t origin = region.GetIndex();
501  sz_t extent = region.GetSize();
502 
503  typename ix_t::IndexValueType x_end = origin[0] + extent[0];
504  typename ix_t::IndexValueType y_end = origin[1] + extent[1];
505 
506  ix_t ix = origin;
507  for (ix[1] = origin[1]; ix[1] < y_end; ++ix[1])
508  {
509  for (ix[0] = origin[0]; ix[0] < x_end; ++ix[0])
510  {
511  if (mask_a->GetPixel(ix) == 0)
512  {
513  continue;
514  }
515 
516  mask_a->TransformIndexToPhysicalPoint(ix, pt_a);
517  pt_b = t->TransformPoint(pt_a);
518 
519  if (!imask_b->IsInsideBuffer(pt_b))
520  {
521  mask_a->SetPixel(ix, 0);
522  }
523  else
524  {
525  double val_mask_b = imask_b->Evaluate(pt_b);
526  if (val_mask_b < 0.5)
527  {
528  mask_a->SetPixel(ix, 0);
529  }
530  }
531  }
532  }
533  }
534 
535  void collect_stos(std::list<stos_t<TImage> > & stos) const
536  {
537  // gather mappings to children:
538  for (typename std::list<link_t>::const_iterator
539  i = children_.begin(); i != children_.end(); ++i)
540  {
541  const link_t & link = *i;
542  stos.push_back(link.stos_);
543  }
544 
545  // recurse on children:
546  for (typename std::list<link_t>::const_iterator
547  i = children_.begin(); i != children_.end(); ++i)
548  {
549  const link_t & link = *i;
550  link.b_->collect_stos(stos);
551  }
552  }
553 
554  void
555  dump(std::ostream & os)
556  {
557  for (typename std::list<link_t>::const_iterator
558  i = children_.begin(); i != children_.end(); ++i)
559  {
560  const link_t & link = *i;
561  os << fn_data_ << ':' << link.b_->fn_data_
562  << ", " << link.stos_.fn_load_
563  << '\n';
564  }
565 
566  // recurse on children:
567  for (typename std::list<link_t>::const_iterator
568  i = children_.begin(); i != children_.end(); ++i)
569  {
570  const link_t & link = *i;
571  link.b_->dump(os);
572  }
573  }
574 
575  // section info:
576  bfs::path fn_data_;
577  bfs::path image_dir_;
578  bfs::path fn_mask_;
579  bool flipped_;
580  vec2d_t spacing_;
581 
582  // tree info:
583  link_t parent_;
584  std::list<link_t> children_;
585  };
586 
587  //----------------------------------------------------------------
588  // lookup
589  //
590  // lookup a node by its name:
591  boost::shared_ptr<node_t>
592  lookup(const bfs::path & fn_data) const
593  {
594  for (typename std::list<boost::shared_ptr<node_t> >::const_iterator
595  i = nodes_.begin(); i != nodes_.end(); ++i)
596  {
597  const boost::shared_ptr<node_t> & node = *i;
598 
599  if ( boost::iequals(node->fn_data_.string(), fn_data.string()) )
600  {
601  return node;
602  }
603  }
604 
605  return boost::shared_ptr<node_t>();
606  }
607 
608  //----------------------------------------------------------------
609  // get_root
610  //
611  // lookup a root node:
612  boost::shared_ptr<node_t>
613  get_root(unsigned int root_index = 0)
614  {
615  unsigned int num_roots = 0;
616  for (typename std::list<boost::shared_ptr<node_t> >::iterator
617  i = nodes_.begin(); i != nodes_.end(); ++i)
618  {
619  const typename boost::shared_ptr<node_t> & node = *i;
620  if (!node->parent_.a_)
621  {
622  if (num_roots == root_index)
623  {
624  return node;
625  }
626 
627  ++num_roots;
628  }
629  }
630 
631  return boost::shared_ptr<node_t>();
632  }
633 
634  //----------------------------------------------------------------
635  // build
636  //
637  // build a tree of slices from a given list of slice-to-slice mappings,
638  // return the number of root nodes in the tree:
639  //
640  std::size_t
641  build(const std::list<stos_t<TImage> > & stos_list)
642  {
643  nodes_.clear();
644 
645  // add nodes to the tree:
646  for (typename std::list<stos_t<TImage> >::const_iterator
647  i = stos_list.begin(); i != stos_list.end(); ++i)
648  {
649  const stos_t<TImage> & stos = *i;
650 
651  boost::shared_ptr<node_t> a = lookup(stos.fn_[0]);
652  boost::shared_ptr<node_t> b = lookup(stos.fn_[1]);
653 
654  if (!a)
655  {
656  a = boost::shared_ptr<node_t>(new node_t(stos.fn_[0],
657  stos.image_dirs_[0],
658  stos.fn_mask_[0],
659  stos.flipped_[0],
660  stos.sp_[0]));
661  nodes_.push_back(a);
662  }
663 
664  if (!b)
665  {
666  b = boost::shared_ptr<node_t>(new node_t(stos.fn_[1],
667  stos.image_dirs_[1],
668  stos.fn_mask_[1],
669  stos.flipped_[1],
670  stos.sp_[1]));
671  nodes_.push_back(b);
672  }
673 
674  boost::shared_ptr<node_t> prev_parent = b->parent_.a_;
675  b->parent_ = link_t(a, b, stos);
676  a->children_.push_back(link_t(a, b, stos));
677  a->children_.sort();
678 
679  if (prev_parent)
680  {
681  // this shouldn't happen, a node can't have 2 parents in a tree:
682  return 0;
683  }
684  }
685 
686  // count root nodes:
687  std::size_t num_roots = 0;
688  for (typename std::list<boost::shared_ptr<node_t> >::iterator
689  i = nodes_.begin(); i != nodes_.end(); ++i)
690  {
691  const boost::shared_ptr<node_t> & node = *i;
692  if (!node->parent_.a_)
693  {
694  num_roots++;
695  }
696  }
697 
698  return num_roots;
699  }
700 
701  //----------------------------------------------------------------
702  // get_cascade
703  //
704  bool
705  get_cascade(const bfs::path & src,
706  const bfs::path & dst,
707  std::vector<base_transform_t::Pointer> & cascade)
708  {
709  cascade.clear();
710 
711  if (src == dst)
712  {
713  return true;
714  }
715 
716  boost::shared_ptr<node_t> node = lookup(dst);
717  if (!node)
718  {
719  return false;
720  }
721 
722  std::list<base_transform_t::Pointer> transforms;
723  while (node->parent_.a_)
724  {
725  base_transform_t::Pointer t =
726  const_cast<base_transform_t *>(node->parent_.stos_.t01_.GetPointer());
727  transforms.push_front(t);
728 
729  if ( boost::iequals(node->parent_.a_->fn_data_.string(), src.string()) )
730  {
731  cascade.assign(transforms.begin(), transforms.end());
732  return true;
733  }
734 
735  node = node->parent_.a_;
736  }
737 
738  // the dst node does not have src as a parent:
739  return false;
740  }
741 
742  // traverse the tree and collect the stos nodes:
743  void
744  collect_stos(std::list<stos_t<TImage> > & stos)
745  {
746  for (typename std::list<boost::shared_ptr<node_t> >::iterator
747  i = nodes_.begin(); i != nodes_.end(); ++i)
748  {
749  const typename boost::shared_ptr<node_t> & node = *i;
750  if (!node->parent_.a_)
751  {
752  node->collect_stos(stos);
753  }
754  }
755  }
756 
757  void
758  dump(std::ostream & os)
759  {
760  std::size_t root_count = 0;
761  for (typename std::list<boost::shared_ptr<node_t> >::iterator
762  i = nodes_.begin(); i != nodes_.end(); ++i)
763  {
764  const typename boost::shared_ptr<node_t> & node = *i;
765  if (!node->parent_.a_)
766  {
767  root_count++;
768  os << root_count << ". --------------------------------------------\n";
769  node->dump(os);
770  os << std::endl;
771  }
772  }
773  }
774 
775  // the tree nodes (there may be more than one tree among them):
776  std::list<boost::shared_ptr<node_t> > nodes_;
777 };
778 
779 
780 #endif // STOS_COMMON_HXX_
Definition: stos_common.hxx:345
Definition: stos_common.hxx:380
Definition: stos.hxx:60
Definition: tree.hxx:50