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.
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 : common.hxx
30 // Author : Pavel A. Koshevoy
31 // Created : 2005/03/24 16:53
32 // Copyright : (C) 2004-2008 University of Utah
33 // Description : Helper functions for mosaicing, image warping,
34 // image preprocessing, convenience wrappers for
35 // ITK file and ITK filters.
36 
37 #ifndef COMMON_HXX_
38 #define COMMON_HXX_
39 
40 // local includes:
41 #include <Core/ITKCommon/the_text.hxx>
42 #include <Core/ITKCommon/the_utils.hxx>
43 #include <Core/ITKCommon/ThreadUtils/itk_terminator.hxx>
44 #include <Core/ITKCommon/ThreadUtils/the_boost_thread.hxx>
45 #include <Core/ITKCommon/ThreadUtils/the_transaction.hxx>
46 #include <Core/ITKCommon/ThreadUtils/the_thread_pool.hxx>
47 #include <Core/ITKCommon/Filtering/itkNormalizeImageFilterWithMask.h>
48 #include <Core/ITKCommon/Transform/itkLegendrePolynomialTransform.h>
49 
50 // boost:
51 #include <boost/filesystem.hpp>
52 
53 // system includes:
54 #include <iostream>
55 #include <iomanip>
56 #include <cstdlib>
57 #include <vector>
58 #include <list>
59 #include <limits>
60 #include <string>
61 
62 
63 // common:
64 #include <itkSimpleFilterWatcher.h>
65 #include <itkImageFileReader.h>
66 #include <itkImageFileWriter.h>
67 #include <itkCastImageFilter.h>
68 #include <itkExtractImageFilter.h>
69 #include <itkResampleImageFilter.h>
70 #include <itkRescaleIntensityImageFilter.h>
71 #include <itkThresholdImageFilter.h>
72 #include <itkConstantPadImageFilter.h>
73 #include <itkDiscreteGaussianImageFilter.h>
74 #include <itkImageRegionConstIteratorWithIndex.h>
75 #include <itkShrinkImageFilter.h>
76 #include <itkMedianImageFilter.h>
77 #include <itkPasteImageFilter.h>
78 
79 // image metrics:
80 #include <itkNormalizedCorrelationImageToImageMetric.h>
81 #include <itkMeanSquaresImageToImageMetric.h>
82 
83 // image interpolators:
84 #include <itkNearestNeighborInterpolateImageFunction.h>
85 #include <itkLinearInterpolateImageFunction.h>
86 #include <itkBSplineInterpolateImageFunction.h>
87 
88 // registration:
89 #include <itkCommand.h>
90 #include <itkImageMaskSpatialObject.h>
91 
92 // transforms:
93 #include <itkTransformBase.h>
94 #include <itkTranslationTransform.h>
95 #include <itkScaleTransform.h>
96 
97 // mosaic:
98 #include <itkNumericTraits.h>
99 #include <itkAddImageFilter.h>
100 #include <itkSubtractImageFilter.h>
101 #include <itkDivideImageFilter.h>
102 #include <itkMultiplyImageFilter.h>
103 #include <itkRGBPixel.h>
104 #include <itkComposeRGBImageFilter.h>
105 
106 #include <Core/Utils/Exception.h>
107 #include <Core/Utils/Log.h>
108 
109 namespace bfs=boost::filesystem;
110 
111 //----------------------------------------------------------------
112 // image_size_value_t
113 //
114 typedef itk::Size<2>::SizeValueType image_size_value_t;
115 
116 //----------------------------------------------------------------
117 // array2d
118 //
119 #define array2d( T ) std::vector<std::vector<T> >
120 
121 //----------------------------------------------------------------
122 // mask_t
123 //
124 // 2D image with unsigned char pixel type.
125 //
126 typedef itk::Image<unsigned char, 2> mask_t;
127 
128 //----------------------------------------------------------------
129 // mask_so_t
130 //
131 // Spatial object for mask_t, used to specify a mask to ITK image
132 // filters.
133 //
134 typedef itk::ImageMaskSpatialObject<2> mask_so_t;
135 
136 //----------------------------------------------------------------
137 // mask_so
138 //
139 // Convenience function for setting up a mask spatial object for
140 // a give mask image.
141 //
142 inline mask_so_t::Pointer
143 mask_so(const mask_t * mask)
144 {
145  mask_so_t::Pointer so = mask_so_t::New();
146  so->SetImage(mask);
147  return so;
148 }
149 
150 //----------------------------------------------------------------
151 // BREAK
152 //
153 // This is for debugging. Insert a call to BREAK somewhere in
154 // your code, recompile. Then load into a debugger and put a break
155 // point in BREAK. This is to be used in those cases when you know
156 // exactly when you want to hit the breakpoint.
157 //
158 extern int BREAK(unsigned int i);
159 
160 //----------------------------------------------------------------
161 // native_pixel_t
162 //
163 // Native refers to the usual 8 bit pixels here. These are native
164 // (standard) in the real world, but may look odd in the medical
165 // imaging world where float or short int are more common.
166 //
167 typedef unsigned char native_pixel_t;
168 
169 //----------------------------------------------------------------
170 // native_image_t
171 //
172 // 8-bit grayscale image.
173 //
174 typedef itk::Image<native_pixel_t, 2> native_image_t;
175 
176 //----------------------------------------------------------------
177 // pixel_t
178 //
179 // All-encompasing pixel type -- float.
180 // Works everywhere, takes up 4 times as much memory as 8-bit pixels.
181 //
182 typedef float pixel_t;
183 
184 //----------------------------------------------------------------
185 // std_tile
186 //
187 // Load a mosaic tile image, reset the origin to zero, set pixel
188 // spacing as specified. Downscale the image according to the
189 // shrink factor.
190 //
191 template <typename T>
192 typename T::Pointer
193 std_tile(const bfs::path& fn_image,
194  const unsigned int & shrink_factor,
195  const double & pixel_spacing,
196  bool blab = false);
197 
198 //----------------------------------------------------------------
199 // image_t
200 //
201 // float grayscale image.
202 //
203 typedef itk::Image<pixel_t, 2> image_t;
204 
205 //----------------------------------------------------------------
206 // base_transform_t
207 //
208 // Shorthand for abstract 2D tranforms.
209 //
210 typedef itk::Transform<double, 2, 2> base_transform_t;
211 
212 //----------------------------------------------------------------
213 // identity_transform_t
214 //
215 // Shorthand for 2D identity ITK transform.
216 //
217 typedef itk::IdentityTransform<double, 2> identity_transform_t;
218 
219 //----------------------------------------------------------------
220 // translate_transform_t
221 //
222 // Shorthand for 2D rigid translation ITK transform.
223 //
224 typedef itk::TranslationTransform<double, 2> translate_transform_t;
225 
226 //----------------------------------------------------------------
227 // pnt2d_t
228 //
229 // Shorthand for 2D points.
230 //
231 typedef itk::Point<double, 2> pnt2d_t;
232 
233 //----------------------------------------------------------------
234 // vec2d_t
235 //
236 // Shorthand for 2D vectors.
237 //
238 typedef itk::Vector<double, 2> vec2d_t;
239 
240 //----------------------------------------------------------------
241 // xyz_t
242 //
243 // Shorthand for 3D points. This is typically used to represent RGB
244 // or HSV colors.
245 //
246 typedef itk::Vector<double, 3> xyz_t;
247 
248 //----------------------------------------------------------------
249 // xyz
250 //
251 // Constructor function for xyz_t.
252 //
253 inline static xyz_t
254 xyz(const double & r, const double & g, const double & b)
255 {
256  xyz_t rgb;
257  rgb[0] = r;
258  rgb[1] = g;
259  rgb[2] = b;
260  return rgb;
261 }
262 
263 //----------------------------------------------------------------
264 // hsv_to_rgb
265 //
266 // Convenience function for converting between HSV/RGB color
267 // spaces. This is used for colormapping.
268 //
269 inline static xyz_t
270 hsv_to_rgb(const xyz_t & HSV)
271 {
272  double H = HSV[0];
273  double S = HSV[1];
274  double V = HSV[2];
275 
276  xyz_t RGB;
277  double & R = RGB[0];
278  double & G = RGB[1];
279  double & B = RGB[2];
280 
281  if (S == 0.0)
282  {
283  // monochromatic:
284  R = V;
285  G = V;
286  B = V;
287  return RGB;
288  }
289 
290  H *= 6.0;
291  double i = floor(H);
292  double f = H - i;
293 
294  double p = V * (1.0 - S);
295  double q = V * (1.0 - S * f);
296  double t = V * (1.0 - S * (1 - f));
297 
298  if (i == 0.0)
299  {
300  R = V;
301  G = t;
302  B = p;
303  }
304  else if (i == 1.0)
305  {
306  R = q;
307  G = V;
308  B = p;
309  }
310  else if (i == 2.0)
311  {
312  R = p;
313  G = V;
314  B = t;
315  }
316  else if (i == 3.0)
317  {
318  R = p;
319  G = q;
320  B = V;
321  }
322  else if (i == 4.0)
323  {
324  R = t;
325  G = p;
326  B = V;
327  }
328  else
329  {
330  // i == 5.0
331  R = V;
332  G = p;
333  B = q;
334  }
335 
336  return RGB;
337 }
338 
339 //----------------------------------------------------------------
340 // rgb_to_hsv
341 //
342 // Convenience function for converting between RGB/HSV color
343 // spaces. This is used for colormapping.
344 //
345 inline static xyz_t
346 rgb_to_hsv(const xyz_t & RGB)
347 {
348  double R = RGB[0];
349  double G = RGB[1];
350  double B = RGB[2];
351 
352  xyz_t HSV;
353  double & H = HSV[0];
354  double & S = HSV[1];
355  double & V = HSV[2];
356 
357  double min = std::min(R, std::min(G, B));
358  double max = std::max(R, std::max(G, B));
359  V = max;
360 
361  double delta = max - min;
362  if (max == 0)
363  {
364  S = 0;
365  H = -1;
366  }
367  else
368  {
369  S = delta / max;
370 
371  if (delta == 0)
372  {
373  delta = 1;
374  }
375 
376  if (R == max)
377  {
378  // between yellow & magenta
379  H = (G - B) / delta;
380  }
381  else if (G == max)
382  {
383  // between cyan & yellow
384  H = (B - R) / delta + 2;
385  }
386  else
387  {
388  // between magenta & cyan
389  H = (R - G) / delta + 4;
390  }
391 
392  H /= 6.0;
393 
394  if (H < 0.0)
395  {
396  H = H + 1.0;
397  }
398  }
399 
400  return HSV;
401 }
402 
403 //----------------------------------------------------------------
404 // pnt2d
405 //
406 // Constructor function for pnt2d_t.
407 //
408 inline static const pnt2d_t
409 pnt2d(const double & x, const double & y)
410 {
411  pnt2d_t pt;
412  pt[0] = x;
413  pt[1] = y;
414  return pt;
415 }
416 
417 //----------------------------------------------------------------
418 // vec2d
419 //
420 // Constructor function for vec2d_t.
421 //
422 inline static const vec2d_t
423 vec2d(const double & x, const double & y)
424 {
425  vec2d_t vc;
426  vc[0] = x;
427  vc[1] = y;
428  return vc;
429 }
430 
431 //----------------------------------------------------------------
432 // add
433 //
434 // Arithmetics helper function.
435 //
436 inline static const pnt2d_t
437 add(const pnt2d_t & pt, const vec2d_t & vc)
438 {
439  pnt2d_t out;
440  out[0] = pt[0] + vc[0];
441  out[1] = pt[1] + vc[1];
442  return out;
443 }
444 
445 //----------------------------------------------------------------
446 // add
447 //
448 // Arithmetics helper function.
449 //
450 inline static const vec2d_t
451 add(const vec2d_t & a, const vec2d_t & b)
452 {
453  vec2d_t out;
454  out[0] = a[0] + b[0];
455  out[1] = a[1] + b[1];
456  return out;
457 }
458 
459 //----------------------------------------------------------------
460 // sub
461 //
462 // return (a - b)
463 //
464 inline static const vec2d_t
465 sub(const pnt2d_t & a, const pnt2d_t & b)
466 {
467  vec2d_t out;
468  out[0] = a[0] - b[0];
469  out[1] = a[1] - b[1];
470  return out;
471 }
472 
473 //----------------------------------------------------------------
474 // mul
475 //
476 // Arithmetics helper function.
477 //
478 inline static const vec2d_t
479 mul(const double & s, const vec2d_t & vc)
480 {
481  vec2d_t out;
482  out[0] = s * vc[0];
483  out[1] = s * vc[1];
484  return out;
485 }
486 
487 //----------------------------------------------------------------
488 // neg
489 //
490 // Arithmetics helper function.
491 //
492 inline static const vec2d_t
493 neg(const vec2d_t & v)
494 {
495  return vec2d(-v[0], -v[1]);
496 }
497 
498 //----------------------------------------------------------------
499 // is_empty_bbox
500 //
501 // Test whether a bounding box is empty (min > max)
502 //
503 extern bool
504 is_empty_bbox(const pnt2d_t & min,
505  const pnt2d_t & max);
506 
507 //----------------------------------------------------------------
508 // is_singular_bbox
509 //
510 // Test whether a bounding box is singular (min == max)
511 //
512 extern bool
513 is_singular_bbox(const pnt2d_t & min,
514  const pnt2d_t & max);
515 
516 
517 //----------------------------------------------------------------
518 // calc_tile_mosaic_bbox
519 //
520 // Calculate the warped image bounding box in the mosaic space.
521 //
522 extern bool
523 calc_tile_mosaic_bbox(const base_transform_t * mosaic_to_tile,
524 
525  // image space bounding boxes of the tile:
526  const pnt2d_t & tile_min,
527  const pnt2d_t & tile_max,
528 
529  // mosaic space bounding boxes of the tile:
530  pnt2d_t & mosaic_min,
531  pnt2d_t & mosaic_max,
532 
533  // sample points along the image edges:
534  const unsigned int np = 15);
535 
536 
537 
538 //----------------------------------------------------------------
539 // calc_tile_mosaic_bbox
540 //
541 // Calculate the warped image bounding box in the mosaic space.
542 //
543 template <typename T>
544 bool
545 calc_tile_mosaic_bbox(const base_transform_t * mosaic_to_tile,
546  const T * tile,
547 
548  // mosaic space bounding boxes of the tile:
549  pnt2d_t & mosaic_min,
550  pnt2d_t & mosaic_max,
551 
552  // sample points along the image edges:
553  const unsigned int np = 15)
554 {
555  typename T::SizeType sz = tile->GetLargestPossibleRegion().GetSize();
556  typename T::SpacingType sp = tile->GetSpacing();
557 
558  pnt2d_t tile_min = tile->GetOrigin();
559  pnt2d_t tile_max;
560  tile_max[0] = tile_min[0] + sp[0] * static_cast<double>(sz[0]);
561  tile_max[1] = tile_min[1] + sp[1] * static_cast<double>(sz[1]);
562 
563  return calc_tile_mosaic_bbox(mosaic_to_tile,
564  tile_min,
565  tile_max,
566  mosaic_min,
567  mosaic_max,
568  np);
569 }
570 
571 
572 //----------------------------------------------------------------
573 // calc_image_bbox
574 //
575 // Calculate the bounding box for a given image,
576 // expressed in image space.
577 //
578 template <typename T>
579 void
580 calc_image_bbox(const T * image, pnt2d_t & bbox_min, pnt2d_t & bbox_max)
581 {
582  typename T::SizeType sz = image->GetLargestPossibleRegion().GetSize();
583  typename T::SpacingType sp = image->GetSpacing();
584  bbox_min = image->GetOrigin();
585  bbox_max[0] = bbox_min[0] + sp[0] * static_cast<double>(sz[0]);
586  bbox_max[1] = bbox_min[1] + sp[1] * static_cast<double>(sz[1]);
587 }
588 
589 
590 //----------------------------------------------------------------
591 // clamp_bbox
592 //
593 // Restrict a bounding box to be within given limits.
594 //
595 extern void
596 clamp_bbox(const pnt2d_t & confines_min,
597  const pnt2d_t & confines_max,
598  pnt2d_t & min,
599  pnt2d_t & max);
600 
601 //----------------------------------------------------------------
602 // update_bbox
603 //
604 // Expand the bounding box to include a given point.
605 //
606 inline static void
607 update_bbox(pnt2d_t & min, pnt2d_t & max, const pnt2d_t & pt)
608 {
609  if (min[0] > pt[0]) min[0] = pt[0];
610  if (min[1] > pt[1]) min[1] = pt[1];
611  if (max[0] < pt[0]) max[0] = pt[0];
612  if (max[1] < pt[1]) max[1] = pt[1];
613 }
614 
615 
616 //----------------------------------------------------------------
617 // suspend_itk_multithreading_t
618 //
620 {
621 public:
623  itk_threads_(itk::MultiThreader::GetGlobalDefaultNumberOfThreads()),
624  max_threads_(itk::MultiThreader::GetGlobalMaximumNumberOfThreads())
625  {
626  // turn off ITK multithreading:
627  itk::MultiThreader::SetGlobalDefaultNumberOfThreads(1);
628  itk::MultiThreader::SetGlobalMaximumNumberOfThreads(1);
629  }
630 
632  {
633  // restore ITK multithreading:
634  itk::MultiThreader::SetGlobalMaximumNumberOfThreads(max_threads_);
635  itk::MultiThreader::SetGlobalDefaultNumberOfThreads(itk_threads_);
636  }
637 
638 private:
639  int itk_threads_;
640  int max_threads_;
641 };
642 
643 
644 //----------------------------------------------------------------
645 // cast
646 //
647 // Return a copy of image T0 cast to T1.
648 //
649 template <class T0, class T1>
650 typename T1::Pointer
651 cast(const T0 * a)
652 {
653  typedef typename itk::CastImageFilter<T0, T1> cast_t;
654  typename cast_t::Pointer filter = cast_t::New();
655  filter->SetInput(a);
656 
657  // put a terminator on the filter:
658  WRAP(terminator_t<cast_t> terminator(filter));
659  filter->Update();
660  return filter->GetOutput();
661 }
662 
663 //----------------------------------------------------------------
664 // make_image
665 //
666 // make an image of the requested size, filled with some
667 // value (by default zero):
668 //
669 template <class image_t>
670 typename image_t::Pointer
671 make_image(const typename image_t::RegionType::SizeType & sz,
672  const typename image_t::PixelType & fill_value =
673  itk::NumericTraits<typename image_t::PixelType>::Zero)
674 {
675  typename image_t::Pointer image = image_t::New();
676  image->SetRegions(sz);
677  image->Allocate();
678  image->FillBuffer(fill_value);
679  return image;
680 }
681 
682 //----------------------------------------------------------------
683 // make_image
684 //
685 // make an image of the requested size, filled with some
686 // value (by default zero):
687 //
688 template <class image_t>
689 typename image_t::Pointer
690 make_image(const unsigned int width,
691  const unsigned int height,
692  const double spacing,
693  typename image_t::PixelType fill_value =
694  itk::NumericTraits<typename image_t::PixelType>::Zero)
695 {
696  typename image_t::SizeType sz;
697  sz[0] = width;
698  sz[1] = height;
699 
700  typename image_t::Pointer image = make_image<image_t>(sz, fill_value);
701  typename image_t::SpacingType sp;
702  sp[0] = spacing;
703  sp[1] = spacing;
704  image->SetSpacing(sp);
705 
706  return image;
707 }
708 
709 //----------------------------------------------------------------
710 // make_image
711 //
712 // make an image of the requested size, filled with some
713 // value (by default zero):
714 //
715 template <class image_t>
716 typename image_t::Pointer
717 make_image(const unsigned int width,
718  const unsigned int height,
719  const double spacing,
720  typename image_t::PointType origin,
721  typename image_t::PixelType fill_value =
722  itk::NumericTraits<typename image_t::PixelType>::Zero)
723 {
724  typename image_t::Pointer image =
725  make_image<image_t>(width, height, spacing, fill_value);
726  image->SetOrigin(origin);
727  return image;
728 }
729 
730 //----------------------------------------------------------------
731 // make_image
732 //
733 // make an image of the requested size, filled with some
734 // value (by default zero):
735 //
736 template <class image_t>
737 typename image_t::Pointer
738 make_image(const typename image_t::SpacingType & sp,
739  const typename image_t::RegionType::SizeType & sz,
740  const typename image_t::PixelType & fill_value =
741  itk::NumericTraits<typename image_t::PixelType>::Zero)
742 {
743  typename image_t::Pointer image = image_t::New();
744  image->SetRegions(sz);
745  image->SetSpacing(sp);
746  image->Allocate();
747  image->FillBuffer(fill_value);
748  return image;
749 }
750 
751 //----------------------------------------------------------------
752 // make_image
753 //
754 // make an image of the requested size, filled with some
755 // value (by default zero):
756 //
757 template <class image_t>
758 typename image_t::Pointer
759 make_image(const typename image_t::PointType & origin,
760  const typename image_t::SpacingType & sp,
761  const typename image_t::RegionType::SizeType & sz,
762  const typename image_t::PixelType & fill_value =
763  itk::NumericTraits<typename image_t::PixelType>::Zero)
764 {
765  typename image_t::Pointer image = image_t::New();
766  image->SetOrigin(origin);
767  image->SetRegions(sz);
768  image->SetSpacing(sp);
769  image->Allocate();
770  image->FillBuffer(fill_value);
771  return image;
772 }
773 
774 //----------------------------------------------------------------
775 // add
776 //
777 // image arithmetic -- add two images together:
778 //
779 template <class image_t>
780 typename image_t::Pointer
781 add(const image_t * a, const image_t * b)
782 {
783  typedef typename itk::AddImageFilter<image_t, image_t, image_t> sum_t;
784 
785  typename sum_t::Pointer sum = sum_t::New();
786  sum->SetInput1(a);
787  sum->SetInput2(b);
788 
789  WRAP(terminator_t<sum_t> terminator(sum));
790  sum->Update();
791  return sum->GetOutput();
792 }
793 
794 //----------------------------------------------------------------
795 // subtract
796 //
797 // image arithmetic -- subtract two images: c = a - b
798 //
799 template <class image_t>
800 typename image_t::Pointer
801 subtract(const image_t * a, const image_t * b)
802 {
803  typedef typename itk::SubtractImageFilter<image_t, image_t, image_t> dif_t;
804 
805  typename dif_t::Pointer dif = dif_t::New();
806  dif->SetInput1(a);
807  dif->SetInput2(b);
808 
809  WRAP(terminator_t<dif_t> terminator(dif));
810  dif->Update();
811  return dif->GetOutput();
812 }
813 
814 //----------------------------------------------------------------
815 // multiply
816 //
817 // image arithmetic -- return an image resulting from
818 // pixel-wise division a/b:
819 //
820 template <class image_t>
821 typename image_t::Pointer
822 multiply(const image_t * a, const image_t * b)
823 {
824  typedef typename itk::MultiplyImageFilter<image_t, image_t, image_t> mul_t;
825 
826  typename mul_t::Pointer mul = mul_t::New();
827  mul->SetInput1(a);
828  mul->SetInput2(b);
829 
830  WRAP(terminator_t<mul_t> terminator(mul));
831  mul->Update();
832  return mul->GetOutput();
833 }
834 
835 //----------------------------------------------------------------
836 // divide
837 //
838 // image arithmetic -- return an image resulting from
839 // pixel-wise division a/b:
840 //
841 template <class image_t, class mask_t>
842 typename image_t::Pointer
843 divide(const image_t * a, const mask_t * b)
844 {
845  typedef typename itk::DivideImageFilter<image_t, mask_t, image_t> div_t;
846 
847  typename div_t::Pointer div = div_t::New();
848  div->SetInput1(a);
849  div->SetInput2(b);
850 
851  WRAP(terminator_t<div_t> terminator(div));
852  div->Update();
853  return div->GetOutput();
854 }
855 
856 //----------------------------------------------------------------
857 // pixel_in_mask
858 //
859 // Check whether a given pixel falls inside a mask.
860 //
861 // NOTE: the mask is assumed to be at higher resolution
862 // than the image.
863 //
864 template <class image_t>
865 inline static bool
866 pixel_in_mask(const mask_t * mask,
867  const mask_t::SizeType & mask_size,
868  const typename image_t::IndexType & index,
869  const unsigned int spacing_scale)
870 {
871  mask_t::IndexType mask_index(index);
872  mask_index[0] *= spacing_scale;
873  mask_index[1] *= spacing_scale;
874 
875  if (mask_index[0] >= 0 &&
876  mask_index[1] >= 0 &&
877  image_size_value_t(mask_index[0]) < mask_size[0] &&
878  image_size_value_t(mask_index[1]) < mask_size[1])
879  {
880  // check the mask:
881  return (mask == NULL) ? true : (mask->GetPixel(mask_index) != 0);
882  }
883 
884  // pixel falls outside the mask image:
885  return false;
886 }
887 
888 //----------------------------------------------------------------
889 // pixel_in_mask
890 //
891 // Check whether a given physical point falls inside a mask.
892 //
893 template <typename T>
894 inline static bool
895 pixel_in_mask(const T * mask,
896  const typename T::PointType & physical_point)
897 {
898  if (mask == NULL) return true;
899 
900  typename T::IndexType mask_pixel_index;
901  if (mask->TransformPhysicalPointToIndex(physical_point, mask_pixel_index))
902  {
903  // let the mask decide:
904  return mask->GetPixel(mask_pixel_index) != 0;
905  }
906 
907  // point falls outside the mask image:
908  return false;
909 }
910 
911 //----------------------------------------------------------------
912 // pixel_in_mask
913 //
914 // Check whether a given pixel falls inside a mask.
915 //
916 template <typename T>
917 inline static bool
918 pixel_in_mask(const T * image,
919  const itk::Image<unsigned char, T::ImageDimension> * mask,
920  const typename T::IndexType & image_pixel_index)
921 {
922  if (mask == NULL)
923  {
924  return true;
925  }
926 
927  typename T::PointType physical_point;
928  image->TransformIndexToPhysicalPoint(image_pixel_index, physical_point);
929 
930  typedef itk::Image<unsigned char, T::ImageDimension> mask_t;
931  return pixel_in_mask<mask_t>(mask, physical_point);
932 }
933 
934 //----------------------------------------------------------------
935 // image_min_max
936 //
937 // Find min/max pixel values, return mean pixel value (average):
938 //
939 template <typename T>
940 double
941 image_min_max(const T * a,
942  typename T::PixelType & min,
943  typename T::PixelType & max,
944  const itk::Image<unsigned char, T::ImageDimension> * mask = NULL)
945 {
946  WRAP(itk_terminator_t terminator("image_min_max"));
947 
948  typedef typename T::PixelType pixel_t;
949  typedef typename itk::ImageRegionConstIteratorWithIndex<T> iter_t;
950 
951  min = std::numeric_limits<pixel_t>::max();
952  max = -min;
953 
954  double mean = 0.0;
955  double counter = 0.0;
956  iter_t iter(a, a->GetLargestPossibleRegion());
957  for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter)
958  {
959  // make sure there hasn't been an interrupt:
960  WRAP(terminator.terminate_on_request());
961 
962  if (!pixel_in_mask<T>(a, mask, iter.GetIndex()))
963  {
964  continue;
965  }
966 
967  pixel_t v = iter.Get();
968  min = std::min(min, v);
969  max = std::max(max, v);
970 
971  mean += static_cast<double>(v);
972  counter += 1.0;
973  }
974 
975  mean /= counter;
976  return mean;
977 }
978 
979 //----------------------------------------------------------------
980 // remap_min_max_inplace
981 //
982 // Rescale image intensities in-place
983 //
984 template <class image_t>
985 bool
986 remap_min_max_inplace(image_t * a,
987  double min,
988  double max,
989  double new_min,
990  double new_max)
991 {
992  WRAP(itk_terminator_t terminator("remap_min_max_inplace"));
993 
994  typedef typename image_t::PixelType pixel_t;
995  typedef typename itk::ImageRegionIterator<image_t> iter_t;
996 
997  // rescale the intensities:
998  double rng = max - min;
999  if (rng == 0.0) return false;
1000 
1001  double new_rng = new_max - new_min;
1002 
1003  iter_t iter(a, a->GetLargestPossibleRegion());
1004  for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter)
1005  {
1006  // make sure there hasn't been an interrupt:
1007  WRAP(terminator.terminate_on_request());
1008 
1009  double t = (iter.Get() - min) / rng;
1010  iter.Set(pixel_t(new_min + t * new_rng));
1011  }
1012 
1013  return true;
1014 }
1015 
1016 //----------------------------------------------------------------
1017 // remap_min_max_inplace
1018 //
1019 // Rescale image intensities in-place
1020 //
1021 template <class image_t>
1022 bool
1023 remap_min_max_inplace(image_t * a,
1024  typename image_t::PixelType new_min = 0,
1025  typename image_t::PixelType new_max = 255)
1026 {
1027  WRAP(itk_terminator_t terminator("remap_min_max_inplace"));
1028 
1029  typedef typename image_t::PixelType pixel_t;
1030  typedef typename itk::ImageRegionIterator<image_t> iter_t;
1031 
1032  double min = std::numeric_limits<pixel_t>::max();
1033  double max = -min;
1034 
1035  iter_t iter(a, a->GetLargestPossibleRegion());
1036  for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter)
1037  {
1038  // make sure there hasn't been an interrupt:
1039  WRAP(terminator.terminate_on_request());
1040 
1041  double v = iter.Get();
1042  min = std::min(min, v);
1043  max = std::max(max, v);
1044  }
1045 
1046  return remap_min_max_inplace<image_t>(a, min, max, new_min, new_max);
1047 }
1048 
1049 //----------------------------------------------------------------
1050 // remap_min_max
1051 //
1052 // Return a copy of the image with rescaled pixel intensities
1053 //
1054 template <typename T>
1055 typename T::Pointer
1056 remap_min_max(const T * a,
1057  typename T::PixelType new_min = 0,
1058  typename T::PixelType new_max = 255)
1059 {
1060  WRAP(itk_terminator_t terminator("remap_min_max"));
1061 
1062  typedef typename T::RegionType rn_t;
1063  typedef typename T::SizeType sz_t;
1064  rn_t rn = a->GetLargestPossibleRegion();
1065  sz_t sz = rn.GetSize();
1066 
1067  typename T::Pointer b = T::New();
1068  b->SetRegions(sz);
1069  b->SetSpacing(a->GetSpacing());
1070  b->Allocate();
1071 
1072  double min = std::numeric_limits<pixel_t>::max();
1073  double max = -min;
1074 
1075  typename T::IndexType ix_end;
1076  ix_end[0] = sz[0];
1077  ix_end[1] = sz[1];
1078 
1079  typename T::IndexType ix;
1080  for (ix[1] = 0; ix[1] < ix_end[1]; ++ix[1])
1081  {
1082  // make sure there hasn't been an interrupt:
1083  WRAP(terminator.terminate_on_request());
1084 
1085  for (ix[0] = 0; ix[0] < ix_end[0]; ++ix[0])
1086  {
1087  double v = a->GetPixel(ix);
1088  min = std::min(min, v);
1089  max = std::max(max, v);
1090  }
1091  }
1092 
1093  // rescale the intensities:
1094  double rng = max - min;
1095  if (rng == 0.0)
1096  {
1097  b->FillBuffer(itk::NumericTraits<typename T::PixelType>::Zero);
1098  return b;
1099  }
1100 
1101  double new_rng = new_max - new_min;
1102 
1103  for (ix[1] = 0; ix[1] < ix_end[1]; ++ix[1])
1104  {
1105  // make sure there hasn't been an interrupt:
1106  WRAP(terminator.terminate_on_request());
1107 
1108  for (ix[0] = 0; ix[0] < ix_end[0]; ++ix[0])
1109  {
1110  double v = a->GetPixel(ix);
1111  double t = (v - min) / rng;
1112  b->SetPixel(ix, typename T::PixelType(new_min + t * new_rng));
1113  }
1114  }
1115 
1116  return b;
1117 }
1118 
1119 //----------------------------------------------------------------
1120 // invert
1121 //
1122 // Invert pixel intensities in-place.
1123 //
1124 template <class T>
1125 void
1126 invert(typename T::Pointer & a)
1127 {
1128  WRAP(itk_terminator_t terminator("invert"));
1129 
1130  typedef typename T::PixelType pixel_t;
1131  typedef typename itk::ImageRegionIterator<T> iter_t;
1132 
1133  pixel_t min = std::numeric_limits<pixel_t>::max();
1134  pixel_t max = -min;
1135 
1136  iter_t iter(a, a->GetLargestPossibleRegion());
1137  for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter)
1138  {
1139  // make sure there hasn't been an interrupt:
1140  WRAP(terminator.terminate_on_request());
1141 
1142  pixel_t v = iter.Get();
1143  min = std::min(min, v);
1144  max = std::max(max, v);
1145  }
1146 
1147  pixel_t rng = max - min;
1148  if (rng == pixel_t(0)) return;
1149 
1150  // rescale the value:
1151  pixel_t new_min = max;
1152  pixel_t new_max = min;
1153  pixel_t new_rng = new_max - new_min;
1154 
1155  for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter)
1156  {
1157  // make sure there hasn't been an interrupt:
1158  WRAP(terminator.terminate_on_request());
1159 
1160  pixel_t t = (iter.Get() - min) / rng;
1161  iter.Set(new_min + t * new_rng);
1162  }
1163 }
1164 
1165 //----------------------------------------------------------------
1166 // threshold
1167 //
1168 // Return a copy of the image with pixels above and below
1169 // a given threshold remapped to new values.
1170 //
1171 template <typename T>
1172 typename T::Pointer
1173 threshold(const T * a,
1174  typename T::PixelType min,
1175  typename T::PixelType max,
1176  typename T::PixelType new_min,
1177  typename T::PixelType new_max)
1178 {
1179  WRAP(itk_terminator_t terminator("threshold"));
1180 
1181  typename T::RegionType::SizeType sz = a->GetLargestPossibleRegion().GetSize();
1182  typename T::Pointer b = T::New();
1183  b->SetRegions(sz);
1184  b->SetSpacing(a->GetSpacing());
1185  b->Allocate();
1186 
1187  typedef typename itk::ImageRegionConstIteratorWithIndex<T> itex_t;
1188  itex_t itex(a, sz);
1189  for (itex.GoToBegin(); !itex.IsAtEnd(); ++itex)
1190  {
1191  // make sure there hasn't been an interrupt:
1192  WRAP(terminator.terminate_on_request());
1193 
1194  typename T::PixelType v = itex.Get();
1195  if (v < min) v = new_min;
1196  else if (v > max) v = new_max;
1197 
1198  b->SetPixel(itex.GetIndex(), v);
1199  }
1200 
1201  return b;
1202 }
1203 
1204 //----------------------------------------------------------------
1205 // clip_min_max
1206 //
1207 // Clip the pixel intensities to the specified min/max limits.
1208 //
1209 template <typename T>
1210 typename T::Pointer
1211 clip_min_max(const T * a,
1212  typename T::PixelType min = 0,
1213  typename T::PixelType max = 255)
1214 { return threshold<T>(a, min, max, min, max); }
1215 
1216 //----------------------------------------------------------------
1217 // calc_padding
1218 //
1219 // Given two 2D images, return the pixel bounding box encompasing both.
1220 //
1221 template <typename T>
1222 typename T::SizeType
1223 calc_padding(const T * a, const T * b)
1224 {
1225  typedef typename T::SizeType sz_t;
1226  sz_t sa = a->GetLargestPossibleRegion().GetSize();
1227  const sz_t sb = b->GetLargestPossibleRegion().GetSize();
1228 
1229  const unsigned int d = T::GetImageDimension();
1230  for (unsigned int i = 0; i < d; i++)
1231  {
1232  sa[i] = std::max(sa[i], sb[i]);
1233  }
1234 
1235  return sa;
1236 }
1237 
1238 //----------------------------------------------------------------
1239 // pad
1240 //
1241 // Pad an image to a given size (in pixels), return the padded image.
1242 // The image is padded with zeros.
1243 //
1244 template <typename T>
1245 typename T::Pointer
1246 pad(const T * a,
1247  const typename T::SizeType & size)
1248 {
1249  typedef typename T::RegionType rn_t;
1250  typedef typename T::SizeType sz_t;
1251  rn_t r = a->GetLargestPossibleRegion();
1252  sz_t z = r.GetSize();
1253 
1254  WRAP(itk_terminator_t terminator("pad"));
1255 
1256  typename T::Pointer b = T::New();
1257  b->SetRegions(size);
1258  b->SetSpacing(a->GetSpacing());
1259  b->Allocate();
1260 
1261  typename T::PixelType zero = itk::NumericTraits<typename T::PixelType>::Zero;
1262  typename T::IndexType ix;
1263  for (ix[1] = 0; (image_size_value_t)(ix[1]) < z[1]; ++ix[1])
1264  {
1265  for (ix[0] = 0; (image_size_value_t)(ix[0]) < z[0]; ++ix[0])
1266  {
1267  b->SetPixel(ix, a->GetPixel(ix));
1268  }
1269 
1270  for (ix[0] = z[0]; (image_size_value_t)(ix[0]) < size[0]; ++ix[0])
1271  {
1272  b->SetPixel(ix, zero);
1273  }
1274  }
1275 
1276  for (ix[1] = z[1]; (image_size_value_t)(ix[1]) < size[1]; ++ix[1])
1277  {
1278  for (ix[0] = 0; (image_size_value_t)(ix[0]) < size[0]; ++ix[0])
1279  {
1280  b->SetPixel(ix, zero);
1281  }
1282  }
1283 
1284  return b;
1285 }
1286 
1287 //----------------------------------------------------------------
1288 // crop
1289 //
1290 // Return a copy of the cropped image region
1291 //
1292 template <typename T>
1293 typename T::Pointer
1294 crop(const T * image,
1295  const typename T::IndexType & min,
1296  const typename T::IndexType & max)
1297 {
1298  WRAP(itk_terminator_t terminator("crop"));
1299  typedef typename T::RegionType::SizeType sz_t;
1300 
1301  sz_t img_sz = image->GetLargestPossibleRegion().GetSize();
1302  sz_t reg_sz;
1303  const unsigned int d = T::GetImageDimension();
1304  for (unsigned int i = 0; i < d; i++)
1305  {
1306  reg_sz[i] = std::min(static_cast<unsigned int>(max[i] - min[i] + 1),
1307  static_cast<unsigned int>(img_sz[i] - min[i]));
1308  }
1309 
1310  typename T::RegionType region;
1311  region.SetIndex(min);
1312  region.SetSize(reg_sz);
1313 
1314  typename T::Pointer out = make_image<T>(reg_sz);
1315 
1316  typedef itk::ImageRegionIteratorWithIndex<T> itex_t;
1317  itex_t itex(out, out->GetLargestPossibleRegion());
1318  for (itex.GoToBegin(); !itex.IsAtEnd(); ++itex)
1319  {
1320  // make sure there hasn't been an interrupt:
1321  WRAP(terminator.terminate_on_request());
1322 
1323  typename T::IndexType ix1 = itex.GetIndex();
1324  typename T::IndexType ix0;
1325  for (unsigned int i = 0; i < d; i++)
1326  {
1327  ix0[i] = ix1[i] + min[i];
1328  }
1329 
1330  out->SetPixel(ix1, image->GetPixel(ix0));
1331  }
1332 
1333  // FIXME: do I really want this?
1334  {
1335  typename T::PointType origin;
1336  image->TransformIndexToPhysicalPoint(region.GetIndex(), origin);
1337  out->SetOrigin(origin);
1338  }
1339  out->SetSpacing(image->GetSpacing());
1340 
1341  return out;
1342 }
1343 
1344 //----------------------------------------------------------------
1345 // smooth
1346 //
1347 // Return a copy of the image smothed with a Gaussian kernel.
1348 //
1349 template <typename T>
1350 typename T::Pointer
1351 smooth(const T * in,
1352  const double sigma,
1353  const double maximum_error = 0.1)
1354 {
1355  typedef typename itk::DiscreteGaussianImageFilter<T, T> smoother_t;
1356  typename smoother_t::Pointer smoother = smoother_t::New();
1357 
1358  // FIXME: itk::SimpleFilterWatcher w(smoother.GetPointer(), "smoother");
1359 
1360  smoother->SetInput(in);
1361  smoother->SetUseImageSpacing(false);
1362  smoother->SetVariance(sigma * sigma);
1363  smoother->SetMaximumError(maximum_error);
1364 
1365  WRAP(terminator_t<smoother_t> terminator(smoother));
1366  smoother->Update();
1367  return smoother->GetOutput();
1368 }
1369 
1370 //----------------------------------------------------------------
1371 // shrink
1372 //
1373 // Return a scaled down copy of the image.
1374 //
1375 template <typename T>
1376 typename T::Pointer
1377 shrink(const T * in,
1378  const unsigned int & shrink_factor,
1379  const double maximum_error = 0.1,
1380  const bool & antialias = true)
1381 {
1382  // Create caster, smoother and shrinker filters
1383  typedef itk::Image<float, T::ImageDimension> flt_img_t;
1384  typename flt_img_t::Pointer image = cast<T, flt_img_t>(in);
1385 
1386  if (antialias)
1387  {
1388  double variance = static_cast<double>(shrink_factor) / 2.0;
1389  double sigma = sqrt(variance);
1390  image = ::smooth<flt_img_t>(image, sigma, maximum_error);
1391  }
1392 
1393  typedef itk::ShrinkImageFilter<flt_img_t, flt_img_t> shrinker_t;
1394  typename shrinker_t::Pointer shrinker = shrinker_t::New();
1395 
1396  // FIXME: itk::SimpleFilterWatcher w(shrinker.GetPointer(), "shrinker");
1397 
1398  shrinker->SetInput(image);
1399  shrinker->SetShrinkFactors(shrink_factor);
1400 
1401  WRAP(terminator_t<shrinker_t> terminator(shrinker));
1402  shrinker->Update();
1403  image = shrinker->GetOutput();
1404  return cast<flt_img_t, T>(image);
1405 }
1406 
1407 //----------------------------------------------------------------
1408 // load
1409 //
1410 // Convenience functions for loading an ITK image.
1411 //
1412 template <typename T>
1413 typename T::Pointer
1414 load(const bfs::path& filename, bool blab = false)
1415 {
1416  typedef typename itk::ImageFileReader<T> reader_t;
1417  typename reader_t::Pointer reader = reader_t::New();
1418 
1419  // FIXME: itk::SimpleFilterWatcher w(reader.GetPointer(), "reader");
1420 
1421  reader->SetFileName(filename.string().c_str());
1422  //if (blab) std::cout << "loading " << filename.string() << std::endl;
1423 
1424 // WRAP(terminator_t<reader_t> terminator(reader));
1425 
1426  reader->Update();
1427  return reader->GetOutput();
1428 }
1429 
1430 //----------------------------------------------------------------
1431 // save
1432 //
1433 // Convenience functions for saving an ITK image.
1434 //
1435 template <typename T>
1436 void
1437 save(const T * image, const bfs::path & filename, bool blab = false)
1438 {
1439  typedef typename itk::ImageFileWriter<T> writer_t;
1440  typename writer_t::Pointer writer = writer_t::New();
1441  writer->SetInput(image);
1442 
1443  //if (blab) std::cout << "saving " << filename << std::endl;
1444  writer->SetFileName(filename.string().c_str());
1445  writer->Update();
1446 }
1447 
1448 //----------------------------------------------------------------
1449 // save_image_tile
1450 //
1451 // Convenience functions for saving out a section of an ITK image.
1452 //
1453 template <typename T>
1454 void
1455 save_image_tile(T * image,
1456  const bfs::path & filename,
1457  const unsigned int x,
1458  const unsigned int y,
1459  const unsigned int source_width,
1460  const unsigned int source_height,
1461  const unsigned int tile_width,
1462  const unsigned int tile_height,
1463  bool blab = false)
1464 {
1465  typename itk::PasteImageFilter<T>::Pointer pasteImageFilter =
1466  itk::PasteImageFilter<T>::New();
1467 
1468  pasteImageFilter->SetSourceImage( image );
1469 
1470  typename T::RegionType mosaicRegion = image->GetBufferedRegion();
1471  typename T::SizeType mosaicSize = mosaicRegion.GetSize();
1472 
1473  // Setup the region we're interested in pasting to a new image.
1474  typename T::IndexType sourceIndex;
1475  sourceIndex[0] = x;
1476  sourceIndex[1] = y;
1477 
1478  typename T::SizeType sourceSize;
1479  sourceSize[0] = source_width;
1480  sourceSize[1] = source_height;
1481 
1482  typename T::RegionType sourceRegion;
1483  sourceRegion.SetIndex( sourceIndex );
1484  sourceRegion.SetSize( sourceSize );
1485  pasteImageFilter->SetSourceRegion( sourceRegion );
1486 
1487  // Create a new image to paste the section onto.
1488  typename T::Pointer destImage = T::New();
1489  pasteImageFilter->SetDestinationImage( destImage );
1490 
1491  typename T::IndexType destIndex;
1492  destIndex[0] = 0;
1493  destIndex[1] = 0;
1494 
1495  typename T::SizeType destSize;
1496  destSize[0] = tile_width;
1497  destSize[1] = tile_height;
1498 
1499  typename T::RegionType sectionRegion;
1500  sectionRegion.SetIndex( destIndex );
1501  sectionRegion.SetSize( destSize );
1502  destImage->SetRegions( sectionRegion );
1503  destImage->Allocate();
1504  destImage->FillBuffer(0);
1505 
1506  pasteImageFilter->SetDestinationIndex( destIndex );
1507 
1508  typename T::Pointer partialImage = pasteImageFilter->GetOutput();
1509 
1510  typedef typename itk::ImageFileWriter<T> writer_t;
1511  typename writer_t::Pointer writer = writer_t::New();
1512  writer->SetInput(partialImage);
1513 
1514  //if (blab)
1515  //{
1516  // std::cout << "saving " << filename << std::endl;
1517  //}
1518 
1519  writer->SetFileName(filename.string().c_str());
1520  writer->Update();
1521 }
1522 
1523 //----------------------------------------------------------------
1524 // save_as_tiles
1525 //
1526 // Convenience functions for saving out series of tiles as ITK images. Also
1527 // writes out an xml file explaining the position of these files.
1528 //
1529 template <typename T>
1530 bool
1531 save_as_tiles(T * image,
1532  const bfs::path & prefix,
1533  const bfs::path & extension,
1534  unsigned int w,
1535  unsigned int h,
1536  const double downsample,
1537  bool save_image = true,
1538  bool blab = false)
1539 {
1540  // the_text_t fileName = prefix;
1541 // the_text_t xmlFileName = fileName;
1542 // xmlFileName += ".xml";
1543 // bfs::path fileName(prefix);
1544  bfs::path xmlFileName(prefix);
1545  xmlFileName.replace_extension("xml");
1546  std::ofstream xmlOut(xmlFileName.string().c_str());
1547 
1548  if (! xmlOut.is_open())
1549  {
1550  std::cout << "Error opening xml file for writing: " << xmlFileName << std::endl;
1551  return false;
1552  }
1553 
1554  typename T::RegionType mosaicRegion = image->GetBufferedRegion();
1555  typename T::SizeType mosaicSize = mosaicRegion.GetSize();
1556  if (w == std::numeric_limits<unsigned int>::max())
1557  {
1558  w = mosaicSize[0];
1559  }
1560 
1561  if (h == std::numeric_limits<unsigned int>::max())
1562  {
1563  h = mosaicSize[1];
1564  }
1565 
1566  unsigned int numTilesWide = (mosaicSize[0] + (w - 1)) / w;
1567  unsigned int numTilesTall = (mosaicSize[1] + (h - 1)) / h;
1568 
1569  // extract just the name portion
1570 // size_t num_forward = fileName.contains('/');
1571 // size_t num_backward = fileName.contains('\\');
1572 // char slash = ( num_forward > num_backward ) ? '/' : '\\';
1573 // the_text_t name_part = fileName.reverse().cut(slash, 0, 0).reverse() + "_";
1574 // bfs::path name_part = fileName.stem() + "_";
1575  std::string name_part(prefix.stem().string() + "_");
1576 
1577  xmlOut << "<?xml version=\"1.0\"?>" << std::endl;
1578  xmlOut << "<Level GridDimX=\"" << numTilesWide
1579  << "\" GridDimY=\"" << numTilesTall
1580  << "\" " << "TileXDim=\"" << w
1581  << "\" " << "TileYDim=\"" << h
1582  << "\" " << "Downsample=\"" << downsample
1583  << "\" " << "FilePrefix=\"" << name_part
1584  << "\" " << "FilePostfix=\"" << extension
1585  << "\"/>" << std::endl;
1586 
1587  for (unsigned int x = 0, xid = 0; x < mosaicSize[0]; x += w, xid++)
1588  {
1589  for (unsigned int y = 0, yid = 0; y < mosaicSize[1]; y += h, yid++)
1590  {
1591 // the_text_t fn_partialSave = prefix;
1592  bfs::path fn_partialSave(prefix);
1593  fn_partialSave += "_X";
1594  fn_partialSave += the_text_t::number(xid, 3, '0');
1595  fn_partialSave += "_Y";
1596  fn_partialSave += the_text_t::number(yid, 3, '0');
1597  fn_partialSave += extension;
1598 
1599  unsigned int sectionWidth = std::min<unsigned int>(w, mosaicSize[0] - x);
1600  unsigned int sectionHeight = std::min<unsigned int>(h, mosaicSize[1] - y);
1601  if ( save_image )
1602  {
1603  save_image_tile<T>(image,
1604  fn_partialSave,
1605  x,
1606  y,
1607  sectionWidth,
1608  sectionHeight,
1609  w,
1610  h);
1611  }
1612  }
1613  }
1614 
1615  xmlOut.close();
1616  return true;
1617 }
1618 
1619 //----------------------------------------------------------------
1620 // save_tile_xml
1621 //
1622 // Exports the same xml file as save_as_tiles. But without actually
1623 // saving the tiles out.
1624 //
1625 template <typename T>
1626 bool
1627 save_tile_xml(const bfs::path & prefix,
1628  const bfs::path & extension,
1629  unsigned int w,
1630  unsigned int h,
1631  unsigned int full_width,
1632  unsigned int full_height,
1633  const double downsample,
1634  bool save_image = true,
1635  bool blab = false)
1636 {
1637  // the_text_t fileName = prefix;
1638 // the_text_t xmlFileName = fileName;
1639 // xmlFileName += ".xml";
1640 // bfs::path fileName(prefix);
1641  bfs::path xmlFileName(prefix);
1642  xmlFileName.replace_extension("xml");
1643  std::ofstream xmlOut(xmlFileName.c_str());
1644 
1645  if (!xmlOut.is_open())
1646  {
1647  std::cout << "Error opening xml file for writing: " << xmlFileName << std::endl;
1648  return false;
1649  }
1650 
1651  if (w == std::numeric_limits<unsigned int>::max())
1652  {
1653  w = full_width;
1654  }
1655 
1656  if (h == std::numeric_limits<unsigned int>::max())
1657  {
1658  h = full_height;
1659  }
1660 
1661  unsigned int numTilesWide = (full_width + (w - 1)) / w;
1662  unsigned int numTilesTall = (full_height + (h - 1)) / h;
1663 
1664  // extract just the name portion
1665 // size_t num_forward = fileName.contains('/');
1666 // size_t num_backward = fileName.contains('\\');
1667 // char slash = ( num_forward > num_backward ) ? '/' : '\\';
1668 // the_text_t name_part = fileName.reverse().cut(slash, 0, 0).reverse() + "_";
1669  std::string name_part(prefix.stem().string() + "_");
1670 
1671  xmlOut << "<?xml version=\"1.0\"?>" << std::endl;
1672  xmlOut << "<Level GridDimX=\"" << numTilesWide
1673  << "\" GridDimY=\"" << numTilesTall
1674  << "\" " << "TileXDim=\"" << w
1675  << "\" " << "TileYDim=\"" << h
1676  << "\" " << "Downsample=\"" << downsample
1677  << "\" " << "FilePrefix=\"" << name_part
1678  << "\" " << "FilePostfix=\"" << extension
1679  << "\"/>" << std::endl;
1680 
1681  for (unsigned int x = 0, xid = 0; x < full_width; x += w, xid++)
1682  {
1683  for (unsigned int y = 0, yid = 0; y < full_height; y += h, yid++)
1684  {
1685 // the_text_t fn_partialSave = prefix;
1686  bfs::path fn_partialSave = prefix;
1687  fn_partialSave += "_X";
1688  fn_partialSave += the_text_t::number(xid, 3, '0');
1689  fn_partialSave += "_Y";
1690  fn_partialSave += the_text_t::number(yid, 3, '0');
1691  fn_partialSave += extension;
1692  }
1693  }
1694 
1695  xmlOut.close();
1696  return true;
1697 }
1698 
1699 //----------------------------------------------------------------
1700 // calc_area
1701 //
1702 // Calculate the area covered by a given number of pixels
1703 // at the specified pixels spacing.
1704 //
1705 inline double
1706 calc_area(const itk::Vector<double, 2> & spacing,
1707  const unsigned long int pixels)
1708 {
1709  double pixel_area = spacing[0] * spacing[1];
1710  double area = pixel_area * static_cast<double>(pixels);
1711  return area;
1712 }
1713 
1714 //----------------------------------------------------------------
1715 // calc_area
1716 //
1717 // Calculate image area under the mask.
1718 //
1719 template <typename T>
1720 double
1721 calc_area(const T * image, const mask_t * mask)
1722 {
1723  WRAP(itk_terminator_t terminator("calc_area"));
1724  unsigned long int pixels = 0;
1725 
1726  typedef itk::ImageRegionConstIteratorWithIndex<T> itex_t;
1727  itex_t itex(image, image->GetLargestPossibleRegion());
1728 
1729  for (itex.GoToBegin(); !itex.IsAtEnd(); ++itex)
1730  {
1731  // make sure there hasn't been an interrupt:
1732  WRAP(terminator.terminate_on_request());
1733 
1734  if (pixel_in_mask<T>(image, mask, itex.GetIndex()))
1735  {
1736  pixels++;
1737  }
1738  }
1739 
1740  return calc_area(image->GetSpacing(), pixels);
1741 }
1742 
1743 //----------------------------------------------------------------
1744 // get_area
1745 //
1746 template <typename T>
1747 double
1748 get_area(const T * image)
1749 {
1750  const typename T::RegionType::SizeType & sz =
1751  image->GetLargestPossibleRegion().GetSize();
1752  const unsigned int num_pixels = sz[0] * sz[1];
1753  return calc_area(image->GetSpacing(), num_pixels);
1754 }
1755 
1756 //----------------------------------------------------------------
1757 // eval_metric
1758 //
1759 // A wrapper function for evaluating an image registration metric for
1760 // two images (one fixed, one moving) in the overlap region defined
1761 // by the fixed image to moving image transform and image masks.
1762 //
1763 template <class metric_t, class interpolator_t>
1764 double
1765 eval_metric(const base_transform_t * fi_to_mi,
1766  const typename metric_t::FixedImageType * fi,
1767  const typename metric_t::MovingImageType * mi,
1768  const mask_t * fi_mask = NULL,
1769  const mask_t * mi_mask = NULL)
1770 {
1771  typename metric_t::Pointer metric = metric_t::New();
1772  metric->SetFixedImage(fi);
1773  metric->SetMovingImage(mi);
1774  metric->SetTransform(const_cast<base_transform_t *>(fi_to_mi));
1775  metric->SetInterpolator(interpolator_t::New());
1776 
1777  // Instead of iterating over the whole fixed image, find the
1778  // bounding box of the overlapping region of the fixed and
1779  // moving images, clip it to the fixed image bounding box, and iterate
1780  // over that -- this will produce a dramatic speedup in situations
1781  // where the fixed image is large and the moving image is small:
1782  typedef typename metric_t::FixedImageType fi_image_t;
1783  typename fi_image_t::RegionType fi_roi = fi->GetLargestPossibleRegion();
1784 
1785  // find the bounding box of the moving image in the space of the fixed image:
1786  pnt2d_t mi_min;
1787  pnt2d_t mi_max;
1788  if (calc_tile_mosaic_bbox(fi_to_mi,
1789  mi,
1790  mi_min,
1791  mi_max,
1792  16))
1793  {
1794  // find the bounding box of the fixed image:
1795  pnt2d_t fi_min;
1796  pnt2d_t fi_max;
1797  calc_image_bbox(fi, fi_min, fi_max);
1798 
1799  // clip the bounding box to the bounding box of the fixed image:
1800  clamp_bbox(fi_min, fi_max, mi_min, mi_max);
1801 
1802  // reset the region of interest:
1803  typename fi_image_t::IndexType roi_index[2];
1804  if (fi->TransformPhysicalPointToIndex(mi_min, roi_index[0]) &&
1805  fi->TransformPhysicalPointToIndex(mi_max, roi_index[1]))
1806  {
1807  typename fi_image_t::SizeType roi_size;
1808  roi_size[0] = roi_index[1][0] - roi_index[0][0] + 1;
1809  roi_size[1] = roi_index[1][1] - roi_index[0][1] + 1;
1810  fi_roi.SetIndex(roi_index[0]);
1811  fi_roi.SetSize(roi_size);
1812  }
1813  }
1814 
1815  metric->SetFixedImageRegion(fi_roi);
1816 
1817  if (fi_mask != NULL)
1818  {
1819  metric->SetFixedImageMask(mask_so(fi_mask));
1820  }
1821 
1822  if (mi_mask != NULL)
1823  {
1824  metric->SetMovingImageMask(mask_so(mi_mask));
1825  }
1826 
1827  metric->Initialize();
1828 
1829  double measure;
1830  try
1831  {
1832  measure = metric->GetValue(fi_to_mi->GetParameters());
1833  }
1834  catch (itk::ExceptionObject & exception)
1835  {
1836  std::ostringstream oss;
1837  oss << "image metric threw an exception:" << exception;
1838  CORE_LOG_WARNING(oss.str());
1839  measure = std::numeric_limits<double>::max();
1840  }
1841 
1842  return measure;
1843 }
1844 
1845 //----------------------------------------------------------------
1846 // my_metric
1847 //
1848 // Calculate a normalized cross correlation metric between two
1849 // masked images, calculate the area of overlap.
1850 //
1851 template <typename TImage, typename TInterpolator>
1852 double
1853 my_metric(double & area,
1854 
1855  const TImage * fi,
1856  const TImage * mi,
1857 
1858  const base_transform_t * fi_to_mi,
1859  const mask_t * fi_mask,
1860  const mask_t * mi_mask,
1861 
1862  const TInterpolator * mi_interpolator)
1863 {
1864  WRAP(itk_terminator_t terminator("my_metric"));
1865  typedef typename itk::ImageRegionConstIteratorWithIndex<TImage> itex_t;
1866  typedef typename TImage::IndexType index_t;
1867  typedef typename TImage::PointType point_t;
1868  typedef typename TImage::PixelType pixel_t;
1869 
1870  // Instead of iterating over the whole fixed image, find the
1871  // bounding box of the overlapping region of the fixed and
1872  // moving images, clip it to the fixed image bounding box, and iterate
1873  // over that -- this will produce a dramatic speedup in situations
1874  // where the fixed image is large and the moving image is small:
1875  typename TImage::RegionType fi_roi = fi->GetLargestPossibleRegion();
1876 
1877  // find the bounding box of the moving image in the space of the fixed image:
1878  pnt2d_t mi_min;
1879  pnt2d_t mi_max;
1880  if (calc_tile_mosaic_bbox(fi_to_mi,
1881  mi,
1882  mi_min,
1883  mi_max,
1884  16))
1885  {
1886  // find the bounding box of the fixed image:
1887  pnt2d_t fi_min;
1888  pnt2d_t fi_max;
1889  calc_image_bbox(fi, fi_min, fi_max);
1890 
1891  // clip the bounding box to the bounding box of the fixed image:
1892  clamp_bbox(fi_min, fi_max, mi_min, mi_max);
1893  if (is_singular_bbox(mi_min, mi_max))
1894  {
1895  return std::numeric_limits<double>::max();
1896  }
1897 
1898  // reset the region of interest:
1899  index_t roi_index[2];
1900  if (fi->TransformPhysicalPointToIndex(mi_min, roi_index[0]) &&
1901  fi->TransformPhysicalPointToIndex(mi_max, roi_index[1]))
1902  {
1903  typename TImage::SizeType roi_size;
1904  roi_size[0] = roi_index[1][0] - roi_index[0][0] + 1;
1905  roi_size[1] = roi_index[1][1] - roi_index[0][1] + 1;
1906  fi_roi.SetIndex(roi_index[0]);
1907  fi_roi.SetSize(roi_size);
1908  }
1909  }
1910 
1911  // performance shortcuts:
1912  mask_t::SizeType fi_mask_size = fi->GetLargestPossibleRegion().GetSize();
1913  unsigned int fi_spacing_scale = 1;
1914  if (fi_mask)
1915  {
1916  mask_t::SizeType sz = fi_mask->GetLargestPossibleRegion().GetSize();
1917  fi_spacing_scale = sz[0] / fi_mask_size[0];
1918  fi_mask_size = sz;
1919  }
1920 
1921  // counters:
1922  double ab = 0.0;
1923  double aa = 0.0;
1924  double bb = 0.0;
1925  double sa = 0.0;
1926  double sb = 0.0;
1927  unsigned long int pixels = 0;
1928 
1929  // iterate over the image:
1930  itex_t itex(fi, fi_roi);
1931  index_t fi_ix;
1932  for (itex.GoToBegin(); !itex.IsAtEnd(); ++itex)
1933  {
1934  // make sure there hasn't been an interrupt:
1935  WRAP(terminator.terminate_on_request());
1936 
1937  fi_ix = itex.GetIndex();
1938  if (fi_mask && !pixel_in_mask<TImage>(fi_mask,
1939  fi_mask_size,
1940  fi_ix,
1941  fi_spacing_scale))
1942  {
1943  continue;
1944  }
1945 
1946  // point coordinates in the fixed image:
1947  point_t xy;
1948  fi->TransformIndexToPhysicalPoint(fi_ix, xy);
1949 
1950  // corresponding coordinates in the moving image:
1951  const point_t uv = fi_to_mi->TransformPoint(xy);
1952 
1953  // check whether the point is within the moving image:
1954  if (!pixel_in_mask<mask_t>(mi_mask, uv) ||
1955  !mi_interpolator->IsInsideBuffer(uv))
1956  {
1957  continue;
1958  }
1959 
1960  pixel_t m = pixel_t(mi_interpolator->Evaluate(uv));
1961  pixel_t f = itex.Get();
1962 
1963  double A = f;
1964  double B = m;
1965 
1966  ab += A * B;
1967  aa += A * A;
1968  bb += B * B;
1969  sa += A;
1970  sb += B;
1971 
1972  pixels++;
1973  }
1974 
1975  area = calc_area(fi->GetSpacing(), pixels);
1976  if (area == 0)
1977  {
1978  return std::numeric_limits<double>::max();
1979  }
1980 
1981  aa = aa - ((sa * sa) / static_cast<double>(pixels));
1982  bb = bb - ((sb * sb) / static_cast<double>(pixels));
1983  ab = ab - ((sa * sb) / static_cast<double>(pixels));
1984 
1985  double result = -ab / sqrt(aa * bb);
1986  return result;
1987 }
1988 
1989 //----------------------------------------------------------------
1990 // my_metric
1991 //
1992 // Calculate the normalized cross correlation metric between two
1993 // masked images and the ratio of area of overlap to the area of the
1994 // smaller image. The metric is restricted by the min/max overlap
1995 // ratio thresholds.
1996 //
1997 template <typename TImage>
1998 double
1999 my_metric(double & overlap,
2000  const TImage * fi,
2001  const TImage * mi,
2002  const base_transform_t * fi_to_mi,
2003  const mask_t * fi_mask = NULL,
2004  const mask_t * mi_mask = NULL,
2005  const double min_overlap = 0.05,
2006  const double max_overlap = 1.0)
2007 {
2008  overlap = 0;
2009 
2010  typedef itk::NearestNeighborInterpolateImageFunction
2011  <TImage, double> interpolator_t;
2012  typename interpolator_t::Pointer mi_interpolator = interpolator_t::New();
2013  mi_interpolator->SetInputImage(mi);
2014 
2015  double overlap_area = 0;
2016  double m = my_metric<TImage, interpolator_t>(overlap_area,
2017  fi,
2018  mi,
2019  fi_to_mi,
2020  fi_mask,
2021  mi_mask,
2022  mi_interpolator);
2023  if (overlap_area != 0)
2024  {
2025 //#if 0
2026 // // this is very slow, but accurate:
2027 // double area_a = calc_area(fi, fi_mask);
2028 // double area_b = calc_area(mi, mi_mask);
2029 //#else
2030  // this is fast, but approximate -- the mask is ingored,
2031  // only the image dimensions and pixel spacing are used:
2032  double area_a = get_area<TImage>(fi);
2033  double area_b = get_area<TImage>(mi);
2034 //#endif
2035 
2036  double smaller_area = std::min(area_a, area_b);
2037  overlap = overlap_area / smaller_area;
2038 
2039  if (overlap < min_overlap || overlap > max_overlap)
2040  {
2041  return std::numeric_limits<double>::max();
2042  }
2043  }
2044 
2045  return m;
2046 }
2047 
2048 //----------------------------------------------------------------
2049 // my_metric
2050 //
2051 // Calculate the normalized cross correlation metric between two
2052 // masked images and the ratio of area of overlap to the area of the
2053 // smaller image. The metric is restricted by the min/max overlap
2054 // ratio thresholds.
2055 //
2056 template <typename TImage>
2057 double
2058 my_metric(const TImage * fi,
2059  const TImage * mi,
2060  const base_transform_t * fi_to_mi,
2061  const mask_t * fi_mask = NULL,
2062  const mask_t * mi_mask = NULL,
2063  const double min_overlap = 0.0,
2064  const double max_overlap = 1.0)
2065 {
2066  double overlap = 0.0;
2067  return my_metric<TImage>(overlap,
2068  fi,
2069  mi,
2070  fi_to_mi,
2071  fi_mask,
2072  mi_mask,
2073  min_overlap,
2074  max_overlap);
2075 }
2076 
2077 //----------------------------------------------------------------
2078 // calc_image_bbox
2079 //
2080 // Calculate the bounding box for a given image,
2081 // expressed in image space.
2082 //
2083 //template <typename T>
2084 //void
2085 //calc_image_bbox(const T * image, pnt2d_t & bbox_min, pnt2d_t & bbox_max)
2086 //{
2087 // typename T::SizeType sz = image->GetLargestPossibleRegion().GetSize();
2088 // typename T::SpacingType sp = image->GetSpacing();
2089 // bbox_min = image->GetOrigin();
2090 // bbox_max[0] = bbox_min[0] + sp[0] * static_cast<double>(sz[0]);
2091 // bbox_max[1] = bbox_min[1] + sp[1] * static_cast<double>(sz[1]);
2092 //}
2093 
2094 //----------------------------------------------------------------
2095 // calc_image_bboxes
2096 //
2097 // Calculate the bounding boxes for a set of given images,
2098 // expressed in image space.
2099 //
2100 template <class image_pointer_t>
2101 void
2102 calc_image_bboxes(const std::vector<image_pointer_t> & image,
2103 
2104  // image space bounding boxes (for feathering):
2105  std::vector<pnt2d_t> & image_min,
2106  std::vector<pnt2d_t> & image_max)
2107 {
2108  typedef typename image_pointer_t::ObjectType::Pointer::ObjectType T;
2109  typedef typename T::RegionType::SizeType imagesz_t;
2110  typedef typename T::SpacingType spacing_t;
2111 
2112  const unsigned int num_images = image.size();
2113  image_min.resize(num_images);
2114  image_max.resize(num_images);
2115 
2116  // calculate the bounding boxes:
2117  for (unsigned int i = 0; i < num_images; i++)
2118  {
2119  // initialize an empty bounding box:
2120  image_min[i][0] = std::numeric_limits<double>::max();
2121  image_min[i][1] = image_min[i][0];
2122  image_max[i][0] = -image_min[i][0];
2123  image_max[i][1] = -image_min[i][0];
2124 
2125  // it happens:
2126  if (image[i].GetPointer() == NULL) continue;
2127 
2128  // bounding box in the image space:
2129  calc_image_bbox<T>(image[i], image_min[i], image_max[i]);
2130  }
2131 }
2132 
2133 //----------------------------------------------------------------
2134 // calc_image_bboxes_load_images
2135 //
2136 // Calculate the bounding boxes for a set of images,
2137 // expressed in image space. These must be loaded, and are loaded
2138 // one by one to save memory on large images.
2139 //
2140 template <class image_pointer_t>
2141 void
2142 calc_image_bboxes_load_images(const std::list<bfs::path> & in,
2143 
2144  // image space bounding boxes (for feathering):
2145  std::vector<pnt2d_t> & image_min,
2146  std::vector<pnt2d_t> & image_max,
2147 
2148  const unsigned int shrink_factor,
2149  const double pixel_spacing)
2150 {
2151  typedef typename image_pointer_t::ObjectType::Pointer::ObjectType T;
2152  typedef typename T::RegionType::SizeType imagesz_t;
2153  typedef typename T::SpacingType spacing_t;
2154 
2155  const unsigned int num_images = in.size();
2156  image_min.resize(num_images);
2157  image_max.resize(num_images);
2158 
2159  // calculate the bounding boxes:
2160  unsigned int i = 0;
2161  for (std::list<bfs::path>::const_iterator iter = in.begin();
2162  iter != in.end(); ++iter, i++)
2163  {
2164  // initialize an empty bounding box:
2165  image_min[i][0] = std::numeric_limits<double>::max();
2166  image_min[i][1] = image_min[i][0];
2167  image_max[i][0] = -image_min[i][0];
2168  image_max[i][1] = -image_min[i][0];
2169 
2170  // Read in just the OutputInformation for speed.
2171  typedef typename itk::ImageFileReader<T> reader_t;
2172  typename reader_t::Pointer reader = reader_t::New();
2173 
2174  reader->SetFileName((*iter).string().c_str());
2175  //std::cout << "loading region from " << (*iter) << std::endl;
2176 
2177  WRAP(terminator_t<reader_t> terminator(reader));
2178 
2179  // Load up the OutputInformation, faster than the whole image
2180  reader->UpdateOutputInformation();
2181  typename T::Pointer image = reader->GetOutput();
2182 
2183  // bounding box in the image space:
2184  calc_image_bbox<T>(image, image_min[i], image_max[i]);
2185 
2186  // Unload the image.
2187  image = image_t::Pointer(NULL);
2188  }
2189 }
2190 
2191 //----------------------------------------------------------------
2192 // calc_tile_mosaic_bbox
2193 //
2194 // Calculate the warped image bounding box in the mosaic space.
2195 //
2196 //extern bool
2197 //calc_tile_mosaic_bbox(const base_transform_t * mosaic_to_tile,
2198 //
2199 // // image space bounding boxes of the tile:
2200 // const pnt2d_t & tile_min,
2201 // const pnt2d_t & tile_max,
2202 //
2203 // // mosaic space bounding boxes of the tile:
2204 // pnt2d_t & mosaic_min,
2205 // pnt2d_t & mosaic_max,
2206 //
2207 // // sample points along the image edges:
2208 // const unsigned int np = 15);
2209 
2210 //----------------------------------------------------------------
2211 // calc_tile_mosaic_bbox
2212 //
2213 // Calculate the warped image bounding box in the mosaic space.
2214 //
2215 //template <typename T>
2216 //bool
2217 //calc_tile_mosaic_bbox(const base_transform_t * mosaic_to_tile,
2218 // const T * tile,
2219 //
2220 // // mosaic space bounding boxes of the tile:
2221 // pnt2d_t & mosaic_min,
2222 // pnt2d_t & mosaic_max,
2223 //
2224 // // sample points along the image edges:
2225 // const unsigned int np = 15)
2226 //{
2227 // typename T::SizeType sz = tile->GetLargestPossibleRegion().GetSize();
2228 // typename T::SpacingType sp = tile->GetSpacing();
2229 //
2230 // pnt2d_t tile_min = tile->GetOrigin();
2231 // pnt2d_t tile_max;
2232 // tile_max[0] = tile_min[0] + sp[0] * static_cast<double>(sz[0]);
2233 // tile_max[1] = tile_min[1] + sp[1] * static_cast<double>(sz[1]);
2234 //
2235 // return calc_tile_mosaic_bbox(mosaic_to_tile,
2236 // tile_min,
2237 // tile_max,
2238 // mosaic_min,
2239 // mosaic_max,
2240 // np);
2241 //}
2242 
2243 //----------------------------------------------------------------
2244 // calc_mosaic_bboxes
2245 //
2246 // Calculate the warped image bounding boxes in the mosaic space.
2247 //
2248 template <class point_t, class transform_pointer_t>
2249 bool
2250 calc_mosaic_bboxes(const std::vector<transform_pointer_t> & xform,
2251 
2252  // image space bounding boxes of mosaic tiles::
2253  const std::vector<point_t> & image_min,
2254  const std::vector<point_t> & image_max,
2255 
2256  // mosaic space bounding boxes of individual tiles:
2257  std::vector<point_t> & mosaic_min,
2258  std::vector<point_t> & mosaic_max,
2259 
2260  // sample points along the image edges:
2261  const unsigned int np = 15)
2262 {
2263  const unsigned int num_images = xform.size();
2264  mosaic_min.resize(num_images);
2265  mosaic_max.resize(num_images);
2266 
2267  point_t image_point;
2268  point_t mosaic_point;
2269 
2270  // calculate the bounding boxes
2271  bool ok = true;
2272  for (unsigned int i = 0; i < num_images; i++)
2273  {
2274  ok = ok & calc_tile_mosaic_bbox(xform[i],
2275  image_min[i],
2276  image_max[i],
2277  mosaic_min[i],
2278  mosaic_max[i],
2279  np);
2280  }
2281 
2282  return ok;
2283 }
2284 
2285 
2286 //----------------------------------------------------------------
2287 // calc_mosaic_bbox
2288 //
2289 // Calculate the mosaic bounding box (a union of mosaic space
2290 // bounding boxes of the warped tiles).
2291 //
2292 template <class point_t>
2293 void
2294 calc_mosaic_bbox(// mosaic space bounding boxes of individual mosaic tiles:
2295  const std::vector<point_t> & mosaic_min,
2296  const std::vector<point_t> & mosaic_max,
2297 
2298  // mosiac bounding box:
2299  point_t & min,
2300  point_t & max)
2301 {
2302  // initialize an empty bounding box:
2303  min[0] = std::numeric_limits<double>::max();
2304  min[1] = min[0];
2305  max[0] = -min[0];
2306  max[1] = -min[0];
2307 
2308  // calculate the bounding box:
2309  const unsigned int num_images = mosaic_min.size();
2310  for (unsigned int i = 0; i < num_images; i++)
2311  {
2312  // it happens:
2313  if (mosaic_min[i][0] == std::numeric_limits<double>::max()) continue;
2314 
2315  // update the mosaic bounding box:
2316  update_bbox(min, max, mosaic_min[i]);
2317  update_bbox(min, max, mosaic_max[i]);
2318  }
2319 }
2320 
2321 //----------------------------------------------------------------
2322 // calc_mosaic_bbox
2323 //
2324 // Calculate the mosaic bounding box (a union of mosaic space
2325 // bounding boxes of the warped tiles).
2326 //
2327 template <class image_pointer_t, class transform_pointer_t>
2328 void
2329 calc_mosaic_bbox(const std::vector<transform_pointer_t> & transform,
2330  const std::vector<image_pointer_t> & image,
2331 
2332  // mosaic bounding box:
2333  typename image_pointer_t::ObjectType::PointType & min,
2334  typename image_pointer_t::ObjectType::PointType & max,
2335 
2336  // points along the image edges:
2337  const unsigned int np = 15)
2338 {
2339  typedef typename image_pointer_t::ObjectType::Pointer::ObjectType image_t;
2340  typedef typename image_t::PointType point_t;
2341 
2342  // image space bounding boxes:
2343  std::vector<point_t> image_min;
2344  std::vector<point_t> image_max;
2345  calc_image_bboxes<image_pointer_t>(image, image_min, image_max);
2346 
2347  // mosaic space bounding boxes:
2348  std::vector<point_t> mosaic_min;
2349  std::vector<point_t> mosaic_max;
2350 
2351  // FIXME:
2352  calc_mosaic_bboxes<point_t, transform_pointer_t>(transform,
2353  image_min,
2354  image_max,
2355  mosaic_min,
2356  mosaic_max,
2357  np);
2358 
2359  // mosiac bounding box:
2360  calc_mosaic_bbox<point_t>(mosaic_min, mosaic_max, min, max);
2361 }
2362 
2363 //----------------------------------------------------------------
2364 // calc_mosaic_bbox_load_images
2365 //
2366 // Calculate the mosaic bounding box (a union of mosaic space
2367 // bounding boxes of the warped tiles).
2368 //
2369 template <class image_pointer_t, class transform_pointer_t>
2370 void
2371 calc_mosaic_bbox_load_images(const std::vector<transform_pointer_t> & transform,
2372  const std::list<bfs::path> & fn_image,
2373 
2374  // mosaic bounding box:
2375  typename image_pointer_t::ObjectType::PointType & min,
2376  typename image_pointer_t::ObjectType::PointType & max,
2377  std::vector<typename image_pointer_t::ObjectType::PointType> & mosaic_min,
2378  std::vector<typename image_pointer_t::ObjectType::PointType> & mosaic_max,
2379  const unsigned int shrink_factor,
2380  const double pixel_spacing,
2381 
2382  // points along the image edges:
2383  const unsigned int np = 15)
2384 {
2385  typedef typename image_pointer_t::ObjectType::Pointer::ObjectType image_t;
2386  typedef typename image_t::PointType point_t;
2387 
2388  // image space bounding boxes:
2389  std::vector<point_t> image_min;
2390  std::vector<point_t> image_max;
2391 
2392  calc_image_bboxes_load_images<image_pointer_t>(fn_image,
2393  image_min,
2394  image_max,
2395  shrink_factor,
2396  pixel_spacing);
2397 
2398  // mosaic space bounding boxes:
2399  // FIXME:
2400  calc_mosaic_bboxes<point_t, transform_pointer_t>(transform,
2401  image_min,
2402  image_max,
2403  mosaic_min,
2404  mosaic_max,
2405  np);
2406 
2407  // mosiac bounding box:
2408  calc_mosaic_bbox<point_t>(mosaic_min, mosaic_max, min, max);
2409 }
2410 
2411 
2412 //----------------------------------------------------------------
2413 // bbox_overlap
2414 //
2415 // Test whether two bounding boxes overlap.
2416 //
2417 inline bool
2418 bbox_overlap(const pnt2d_t & min_box1,
2419  const pnt2d_t & max_box1,
2420  const pnt2d_t & min_box2,
2421  const pnt2d_t & max_box2)
2422 {
2423  return
2424  max_box1[0] > min_box2[0] &&
2425  min_box1[0] < max_box2[0] &&
2426  max_box1[1] > min_box2[1] &&
2427  min_box1[1] < max_box2[1];
2428 }
2429 
2430 //----------------------------------------------------------------
2431 // inside_bbox
2432 //
2433 // Test whether a given point is inside the bounding box.
2434 //
2435 inline bool
2436 inside_bbox(const pnt2d_t & min, const pnt2d_t & max, const pnt2d_t & pt)
2437 {
2438  return
2439  min[0] <= pt[0] &&
2440  pt[0] <= max[0] &&
2441  min[1] <= pt[1] &&
2442  pt[1] <= max[1];
2443 }
2444 
2445 //----------------------------------------------------------------
2446 // calc_pixel_weight
2447 //
2448 // Calculate a pixel feathering weight used to blend mosaic tiles.
2449 //
2450 inline static double
2451 calc_pixel_weight(const pnt2d_t & bbox_min,
2452  const pnt2d_t & bbox_max,
2453  const pnt2d_t & pt)
2454 {
2455  double w = bbox_max[0] - bbox_min[0];
2456  double h = bbox_max[1] - bbox_min[1];
2457  double r = 0.5 * ((w > h) ? h : w);
2458  double sx = std::min(pt[0] - bbox_min[0], bbox_max[0] - pt[0]);
2459  double sy = std::min(pt[1] - bbox_min[1], bbox_max[1] - pt[1]);
2460  double s = (1.0 + std::min(sx, sy)) / (r + 1.0);
2461  return s * s * s * s;
2462 }
2463 
2464 
2465 //----------------------------------------------------------------
2466 // feathering_t
2467 //
2468 // Supported pixel feathering modes
2469 //
2470 typedef enum {
2471  FEATHER_NONE_E,
2472  FEATHER_BLEND_E,
2473  FEATHER_BINARY_E
2474 } feathering_t;
2475 
2476 //----------------------------------------------------------------
2477 // make_mosaic_st
2478 //
2479 // Assemble a portion of the mosaic positioned at mosaic_min.
2480 // Each tile may be individually tinted with a grayscale color.
2481 // Individual tiles may be omitted.
2482 // Background color (outside the mask) may be specified.
2483 // Tile masks are optional and may be NULL.
2484 //
2485 template <class image_pointer_t, class transform_pointer_t>
2486 typename image_pointer_t::ObjectType::Pointer
2487 make_mosaic_st
2488 (bool assemble_mosaic_mask,
2489  mask_t::Pointer & mosaic_mask,
2490  const typename image_pointer_t::ObjectType::SpacingType & mosaic_sp,
2491  const typename image_pointer_t::ObjectType::PointType & mosaic_min,
2492  const typename image_pointer_t::ObjectType::SizeType & mosaic_sz,
2493  const unsigned int num_images,
2494  const std::vector<bool> & omit,
2495  const std::vector<double> & tint,
2496  const std::vector<transform_pointer_t> & transform,
2497  const std::vector<image_pointer_t> & image,
2498 
2499  // optional image masks:
2500  const std::vector<mask_t::ConstPointer> & image_mask =
2501  std::vector<mask_t::ConstPointer>(0),
2502 
2503  // feathering to reduce image blurring is optional:
2504  const feathering_t feathering = FEATHER_NONE_E,
2505  double background = 255.0,
2506 
2507  bool dont_allocate = false)
2508 {
2509  WRAP(itk_terminator_t terminator("make_mosaic_st"));
2510 
2511  typedef typename image_pointer_t::ObjectType::Pointer::ObjectType image_t;
2512  typedef typename image_t::IndexType index_t;
2513  typedef typename image_t::PointType point_t;
2514  typedef typename image_t::PixelType pixel_t;
2515  typedef typename image_t::SpacingType spacing_t;
2516  typedef typename image_t::RegionType::SizeType imagesz_t;
2517  typedef typename itk::ImageRegionIteratorWithIndex<image_t> itex_t;
2518 
2519  // setup the image interpolators:
2520  typedef typename itk::LinearInterpolateImageFunction
2521  <image_t, double> img_interpolator_t;
2522  std::vector<typename img_interpolator_t::Pointer> img(num_images);
2523 
2524  for (unsigned int i = 0; i < num_images; i++)
2525  {
2526  img[i] = img_interpolator_t::New();
2527  img[i]->SetInputImage(image[i]);
2528  }
2529 
2530  // setup the image mask interpolators:
2531  typedef itk::NearestNeighborInterpolateImageFunction
2532  <mask_t, double> msk_interpolator_t;
2533  std::vector<msk_interpolator_t::Pointer> msk(num_images);
2534  msk.assign(num_images, msk_interpolator_t::Pointer(NULL));
2535 
2536  for (unsigned int i = 0; i < image_mask.size() && i < num_images; i++)
2537  {
2538  if (image_mask[i].GetPointer() == NULL) continue;
2539 
2540  msk[i] = msk_interpolator_t::New();
2541  msk[i]->SetInputImage(image_mask[i]);
2542  }
2543 
2544  // image space bounding boxes (for feathering):
2545  std::vector<point_t> bbox_min(num_images);
2546  std::vector<point_t> bbox_max(num_images);
2547  calc_image_bboxes<image_pointer_t>(image, bbox_min, bbox_max);
2548 
2549  // mosaic space bounding boxes:
2550  std::vector<point_t> min(num_images);
2551  std::vector<point_t> max(num_images);
2552 
2553  calc_mosaic_bboxes<point_t, transform_pointer_t>(transform,
2554  bbox_min,
2555  bbox_max,
2556  min,
2557  max);
2558 
2559  // setup the mosaic image:
2560  typename image_t::Pointer mosaic = image_t::New();
2561  mosaic->SetOrigin(mosaic_min);
2562  mosaic->SetRegions(mosaic_sz);
2563  mosaic->SetSpacing(mosaic_sp);
2564 
2565  mosaic_mask = NULL;
2566  if (assemble_mosaic_mask)
2567  {
2568  mosaic_mask = mask_t::New();
2569  mosaic_mask->SetOrigin(mosaic_min);
2570  mosaic_mask->SetRegions(mosaic_sz);
2571  mosaic_mask->SetSpacing(mosaic_sp);
2572  }
2573 
2574  if (dont_allocate)
2575  {
2576  // this is useful for estimating the mosaic size:
2577  return mosaic;
2578  }
2579 
2580  // make sure there hasn't been an interrupt:
2581  WRAP(terminator.terminate_on_request());
2582 
2583  mosaic->Allocate();
2584 
2585  if (assemble_mosaic_mask)
2586  {
2587  mosaic_mask->Allocate();
2588  }
2589 
2590  // this is needed in order to prevent holes in the mask mosaic:
2591  bool integer_pixel = std::numeric_limits<pixel_t>::is_integer;
2592  double pixel_max = static_cast<double>(std::numeric_limits<pixel_t>::max());
2593  double pixel_min = integer_pixel ?
2594  static_cast<double>(std::numeric_limits<pixel_t>::min()) : -pixel_max;
2595 
2596  itex_t itex(mosaic, mosaic->GetLargestPossibleRegion());
2597  for (itex.GoToBegin(); !itex.IsAtEnd(); ++itex)
2598  {
2599  // make sure there hasn't been an interrupt:
2600  WRAP(terminator.terminate_on_request());
2601 
2602  point_t point;
2603  mosaic->TransformIndexToPhysicalPoint(itex.GetIndex(), point);
2604 
2605  double pixel = 0.0;
2606  double weight = 0.0;
2607  unsigned int num_pixels = 0;
2608  for (unsigned int k = 0; k < num_images; k++)
2609  {
2610  // don't try to add missing or omitted images to the mosaic:
2611  if (omit[k] || (image[k].GetPointer() == NULL)) continue;
2612 
2613  // avoid undesirable distortion artifacts:
2614  if (!inside_bbox(min[k], max[k], point)) continue;
2615 
2616  const transform_pointer_t & t = transform[k];
2617  point_t pt_k = t->TransformPoint(point);
2618 
2619  // make sure the pixel maps into the image:
2620  if (!img[k]->IsInsideBuffer(pt_k)) continue;
2621 
2622  // make sure the pixel maps into the image mask:
2623  double alpha = 1.0;
2624  if ((msk[k].GetPointer() != NULL) &&
2625  (alpha = msk[k]->Evaluate(pt_k)) < 1.0) continue;
2626 
2627  // feather out the edges by giving them a tiny weight:
2628  num_pixels++;
2629  double wp = tint[k];
2630  double p = img[k]->Evaluate(pt_k) * wp;
2631  double wa = ((alpha == 1.0) ? 1e-0 : 1e-6) * wp;
2632 
2633  if (feathering == FEATHER_NONE_E)
2634  {
2635  pixel += p * wa;
2636  weight += wa;
2637  }
2638  else
2639  {
2640  double pixel_weight =
2641  calc_pixel_weight(bbox_min[k], bbox_max[k], pt_k);
2642 
2643  if (feathering == FEATHER_BLEND_E)
2644  {
2645  pixel += pixel_weight * p * wa;
2646  weight += pixel_weight * wa;
2647  }
2648  else // FEATHER_BINARY_E
2649  {
2650  if (pixel_weight > weight)
2651  {
2652  pixel = pixel_weight * p * wa;
2653  weight = pixel_weight * wa;
2654  }
2655  }
2656  }
2657  }
2658 
2659  // calculate the final pixel value:
2660  if (weight > 0.0)
2661  {
2662  pixel /= weight;
2663  if (integer_pixel)
2664  {
2665  pixel = floor(pixel + 0.5);
2666 
2667  // make sure we don't exceed the intensity range:
2668  pixel = std::max(pixel_min, std::min(pixel_max, pixel));
2669  }
2670 
2671  pixel_t tmp = pixel_t(pixel);
2672  itex.Set(tmp);
2673  }
2674  else
2675  {
2676  itex.Set(num_pixels > 0 ? 0 : pixel_t(background));
2677  }
2678 
2679  if (mosaic_mask.GetPointer())
2680  {
2681  mask_t::PixelType mask_pixel = (weight > 0.0) ? 1 : 0;
2682  mosaic_mask->SetPixel(itex.GetIndex(), mask_pixel);
2683  }
2684  }
2685 
2686  return mosaic;
2687 }
2688 
2689 
2690 //----------------------------------------------------------------
2691 // assemble_mosaic_t
2692 //
2693 // Parallelized mosaic assembly mechanism
2694 //
2695 template <typename image_pointer_t, typename transform_pointer_t>
2697 {
2698 public:
2699  typedef typename image_pointer_t::ObjectType::Pointer::ObjectType tile_t;
2700  typedef typename image_t::PointType pnt_t;
2701  typedef typename image_t::PixelType pxl_t;
2702  typedef typename image_t::RegionType rn_t;
2703  typedef typename image_t::RegionType::SizeType sz_t;
2704  typedef typename image_t::RegionType::IndexType ix_t;
2705  typedef typename itk::LinearInterpolateImageFunction<tile_t, double> itile_t;
2706  typedef itk::NearestNeighborInterpolateImageFunction<mask_t, double> imask_t;
2707 
2708  assemble_mosaic_t(// thread index and number of threads, used to avoid
2709  // concurrent access to the same point in the mosaic:
2710  unsigned int thread_offset,
2711  unsigned int thread_stride,
2712 
2713  // mosaic being assembled:
2714  typename tile_t::Pointer & mosaic,
2715  mask_t * mosaic_mask,
2716 
2717  // number of tiles to use for this mosaic (may be fewer
2718  // then the number of tiles available):
2719  unsigned int num_tiles,
2720 
2721  // omitted tile flags:
2722  const std::vector<bool> & omit,
2723 
2724  // tile tint:
2725  const std::vector<double> & tint,
2726 
2727  // tile trasforms:
2728  const std::vector<transform_pointer_t> & transform,
2729 
2730  // tiles and tile masks
2731  const std::vector<image_pointer_t> & tile,
2732  const std::vector<mask_t::ConstPointer> & mask,
2733 
2734  // tile and tile mask interpolators:
2735  const std::vector<typename itile_t::Pointer> & itile,
2736  const std::vector<typename imask_t::Pointer> & imask,
2737 
2738  // image space tile bounding boxes (for feathering):
2739  const std::vector<pnt_t> & bbox_min,
2740  const std::vector<pnt_t> & bbox_max,
2741 
2742  // mosaic space tile bounding boxes:
2743  const std::vector<pnt_t> & min,
2744  const std::vector<pnt_t> & max,
2745 
2746  // overlap region feathering method:
2747  feathering_t feathering,
2748 
2749  // default mosaic pixel value:
2750  double background):
2751 
2752  thread_offset_(thread_offset),
2753  thread_stride_(thread_stride),
2754  mosaic_(mosaic),
2755  mosaic_mask_(mosaic_mask),
2756  num_tiles_(num_tiles),
2757  omit_(omit),
2758  tint_(tint),
2759  transform_(transform),
2760  tile_(tile),
2761  mask_(mask),
2762  itile_(itile),
2763  imask_(imask),
2764  bbox_min_(bbox_min),
2765  bbox_max_(bbox_max),
2766  min_(min),
2767  max_(max),
2768  feathering_(feathering),
2769  background_(background)
2770  {}
2771 
2772  void execute(the_thread_interface_t * thread)
2773  {
2774  WRAP(itk_terminator_t terminator("assemble_mosaic_t::execute"));
2775 
2776  // this is needed in order to prevent holes in the mask mosaic:
2777  const bool integer_pixel = std::numeric_limits<pxl_t>::is_integer;
2778  const double pixel_max = static_cast<double>(std::numeric_limits<pxl_t>::max());
2779  const double pixel_min = integer_pixel ?
2780  static_cast<double>(std::numeric_limits<pxl_t>::min()) : -pixel_max;
2781 
2782  rn_t region = mosaic_->GetLargestPossibleRegion();
2783  ix_t origin = region.GetIndex();
2784  sz_t extent = region.GetSize();
2785  ix_t::IndexValueType x_end = origin[0] + extent[0];
2786  ix_t::IndexValueType y_end = origin[1] + extent[1];
2787 
2788  ix_t ix = origin;
2789  for (ix[1] = origin[1]; ix[1] < y_end; ++ix[1])
2790  {
2791  for (ix[0] = origin[0] + thread_offset_;
2792  ix[0] < x_end;
2793  ix[0] += thread_stride_)
2794  {
2795  // check whether termination was requested:
2796  WRAP(terminator.terminate_on_request());
2797 
2798  pnt_t point;
2799  mosaic_->TransformIndexToPhysicalPoint(ix, point);
2800 
2801  double pixel = 0.0;
2802  double weight = 0.0;
2803  unsigned int num_pixels = 0;
2804 
2805  for (unsigned int k = 0; k < num_tiles_; k++)
2806  {
2807  // don't try to add missing or omitted images to the mosaic:
2808  if (omit_[k] || (tile_[k].GetPointer() == NULL))
2809  {
2810  continue;
2811  }
2812 
2813  // avoid undesirable distortion artifacts:
2814  if (!inside_bbox(min_[k], max_[k], point))
2815  {
2816  continue;
2817  }
2818 
2819  const transform_pointer_t & t = transform_[k];
2820  pnt_t pt_k = t->TransformPoint(point);
2821 
2822  // make sure the pixel maps into the image:
2823  if (!itile_[k]->IsInsideBuffer(pt_k))
2824  {
2825  continue;
2826  }
2827 
2828  // make sure the pixel maps into the image mask:
2829  double alpha = 1.0;
2830  if ((imask_[k].GetPointer() != NULL) &&
2831  (alpha = imask_[k]->Evaluate(pt_k)) < 1.0)
2832  {
2833  continue;
2834  }
2835 
2836  // feather out the edges by giving them a tiny weight:
2837  num_pixels++;
2838  double wp = tint_[k];
2839  double p = itile_[k]->Evaluate(pt_k) * wp;
2840  double wa = ((alpha == 1.0) ? 1e-0 : 1e-6) * wp;
2841 
2842  if (feathering_ == FEATHER_NONE_E)
2843  {
2844  pixel += p * wa;
2845  weight += wa;
2846  }
2847  else
2848  {
2849  double pixel_weight = calc_pixel_weight(bbox_min_[k],
2850  bbox_max_[k],
2851  pt_k);
2852 
2853  if (feathering_ == FEATHER_BLEND_E)
2854  {
2855  pixel += pixel_weight * p * wa;
2856  weight += pixel_weight * wa;
2857  }
2858  else // FEATHER_BINARY_E
2859  {
2860  if (pixel_weight > weight)
2861  {
2862  pixel = pixel_weight * p * wa;
2863  weight = pixel_weight * wa;
2864  }
2865  }
2866  }
2867  }
2868 
2869  // calculate the final pixel value:
2870  if (weight > 0.0)
2871  {
2872  pixel /= weight;
2873  if (integer_pixel)
2874  {
2875  pixel = floor(pixel + 0.5);
2876 
2877  // make sure we don't exceed the intensity range:
2878  pixel = std::max(pixel_min, std::min(pixel_max, pixel));
2879  }
2880 
2881  pxl_t output = pxl_t(pixel);
2882  mosaic_->SetPixel(ix, output);
2883  }
2884  else
2885  {
2886  pxl_t output = num_pixels > 0 ? 0 : pxl_t(background_);
2887  mosaic_->SetPixel(ix, output);
2888  }
2889 
2890  if (mosaic_mask_)
2891  {
2892  mask_t::PixelType mask_pixel = (weight > 0.0) ? 1 : 0;
2893  mosaic_mask_->SetPixel(ix, mask_pixel);
2894  }
2895  }
2896  }
2897  }
2898 
2899  // data members facilitating concurrent mosaic assembly:
2900  unsigned int thread_offset_;
2901  unsigned int thread_stride_;
2902 
2903  // output mosaic:
2904  typename tile_t::Pointer & mosaic_;
2905  mask_t * mosaic_mask_;
2906 
2907  // number of tiles used for this mosaic (may be fewer than
2908  // what's available from the tile vector):
2909  unsigned int num_tiles_;
2910 
2911  // omitted tile flags:
2912  const std::vector<bool> & omit_;
2913 
2914  // tile tint:
2915  const std::vector<double> & tint_;
2916 
2917  // tile trasforms:
2918  const std::vector<transform_pointer_t> & transform_;
2919 
2920  // tiles and tile masks
2921  const std::vector<image_pointer_t> & tile_;
2922  const std::vector<mask_t::ConstPointer> & mask_;
2923 
2924 
2925  // tile and tile mask interpolators:
2926  const std::vector<typename itile_t::Pointer> & itile_;
2927  const std::vector<typename imask_t::Pointer> & imask_;
2928 
2929  // image space tile bounding boxes (for feathering):
2930  const std::vector<pnt_t> & bbox_min_;
2931  const std::vector<pnt_t> & bbox_max_;
2932 
2933  // mosaic space tile bounding boxes:
2934  const std::vector<pnt_t> & min_;
2935  const std::vector<pnt_t> & max_;
2936 
2937  // overlap region feathering method:
2938  feathering_t feathering_;
2939 
2940  // default mosaic pixel value:
2941  double background_;
2942 };
2943 
2944 //----------------------------------------------------------------
2945 // make_mosaic_mt
2946 //
2947 // Assemble a portion of the mosaic positioned at mosaic_min.
2948 // Each tile may be individually tinted with a grayscale color.
2949 // Individual tiles may be omitted.
2950 // Background color (outside the mask) may be specified.
2951 // Tile masks are optional and may be NULL.
2952 //
2953 template <class image_pointer_t, class transform_pointer_t>
2954 typename image_pointer_t::ObjectType::Pointer
2955 make_mosaic_mt
2956 (unsigned int num_threads,
2957  bool assemble_mosaic_mask,
2958  mask_t::Pointer & mosaic_mask,
2959  const typename image_pointer_t::ObjectType::SpacingType & mosaic_sp,
2960  const typename image_pointer_t::ObjectType::PointType & mosaic_min,
2961  const typename image_pointer_t::ObjectType::SizeType & mosaic_sz,
2962  const unsigned int num_images,
2963  const std::vector<bool> & omit,
2964  const std::vector<double> & tint,
2965  const std::vector<transform_pointer_t> & transform,
2966  const std::vector<image_pointer_t> & image,
2967 
2968  // optional image masks:
2969  const std::vector<mask_t::ConstPointer> & image_mask =
2970  std::vector<mask_t::ConstPointer>(0),
2971 
2972  // feathering to reduce image blurring is optional:
2973  const feathering_t feathering = FEATHER_NONE_E,
2974  double background = 255.0,
2975 
2976  bool dont_allocate = false)
2977 {
2978  if (num_threads == 1)
2979  {
2980  // use the old single-threaded code:
2981  return
2982  make_mosaic_st<image_pointer_t, transform_pointer_t>
2983  (assemble_mosaic_mask,
2984  mosaic_mask,
2985  mosaic_sp,
2986  mosaic_min,
2987  mosaic_sz,
2988  num_images,
2989  omit,
2990  tint,
2991  transform,
2992  image,
2993  image_mask,
2994  feathering,
2995  background,
2996  dont_allocate);
2997  }
2998 
2999  WRAP(itk_terminator_t terminator("make_mosaic_mt"));
3000 
3001  typedef typename image_pointer_t::ObjectType::Pointer::ObjectType img_t;
3002  typedef typename img_t::PointType pnt_t;
3003 
3004  // setup the image interpolators:
3005  typedef typename itk::LinearInterpolateImageFunction
3006  <img_t, double> img_interpolator_t;
3007  std::vector<typename img_interpolator_t::Pointer> img(num_images);
3008 
3009  for (unsigned int i = 0; i < num_images; i++)
3010  {
3011  img[i] = img_interpolator_t::New();
3012  img[i]->SetInputImage(image[i]);
3013  }
3014 
3015  // setup the image mask interpolators:
3016  typedef itk::NearestNeighborInterpolateImageFunction
3017  <mask_t, double> msk_interpolator_t;
3018  std::vector<msk_interpolator_t::Pointer> msk(num_images);
3019  msk.assign(num_images, msk_interpolator_t::Pointer(NULL));
3020 
3021  for (unsigned int i = 0; i < image_mask.size() && i < num_images; i++)
3022  {
3023  if (image_mask[i].GetPointer() == NULL) continue;
3024 
3025  msk[i] = msk_interpolator_t::New();
3026  msk[i]->SetInputImage(image_mask[i]);
3027  }
3028 
3029  // image space bounding boxes (for feathering):
3030  std::vector<pnt_t> bbox_min(num_images);
3031  std::vector<pnt_t> bbox_max(num_images);
3032  calc_image_bboxes<image_pointer_t>(image, bbox_min, bbox_max);
3033 
3034  // mosaic space bounding boxes:
3035  std::vector<pnt_t> min(num_images);
3036  std::vector<pnt_t> max(num_images);
3037  calc_mosaic_bboxes<pnt_t, transform_pointer_t>(transform,
3038  bbox_min,
3039  bbox_max,
3040  min,
3041  max);
3042 
3043  // setup the mosaic image:
3044  typename img_t::Pointer mosaic = img_t::New();
3045  mosaic->SetOrigin(mosaic_min);
3046  mosaic->SetRegions(mosaic_sz);
3047  mosaic->SetSpacing(mosaic_sp);
3048 
3049  mosaic_mask = NULL;
3050  if (assemble_mosaic_mask)
3051  {
3052  mosaic_mask = mask_t::New();
3053  mosaic_mask->SetOrigin(mosaic_min);
3054  mosaic_mask->SetRegions(mosaic_sz);
3055  mosaic_mask->SetSpacing(mosaic_sp);
3056  }
3057 
3058  if (dont_allocate)
3059  {
3060  // this is useful for estimating the mosaic size:
3061  return mosaic;
3062  }
3063 
3064  // allocate the mosaic:
3065  mosaic->Allocate();
3066 
3067  if (assemble_mosaic_mask)
3068  {
3069  mosaic_mask->Allocate();
3070  }
3071 
3072  // setup transactions for multi-threaded mosaic assembly:
3073  std::list<the_transaction_t *> schedule;
3074  for (unsigned int i = 0; i < num_threads; i++)
3075  {
3078  (i,
3079  num_threads,
3080  mosaic,
3081  mosaic_mask.GetPointer(),
3082  num_images,
3083  omit,
3084  tint,
3085  transform,
3086  image,
3087  image_mask,
3088  img,
3089  msk,
3090  bbox_min,
3091  bbox_max,
3092  min,
3093  max,
3094  feathering,
3095  background);
3096 
3097  schedule.push_back(t);
3098  }
3099 
3100  // setup the thread pool:
3101  the_thread_pool_t thread_pool(num_threads);
3102  thread_pool.set_idle_sleep_duration(50); // 50 usec
3103  thread_pool.push_back(schedule);
3104  thread_pool.pre_distribute_work();
3105 
3106  // execute mosaic assembly transactions:
3107  suspend_itk_multithreading_t suspend_itk_mt;
3108  thread_pool.start();
3109  thread_pool.wait();
3110 
3111  // done:
3112  return mosaic;
3113 }
3114 
3115 //----------------------------------------------------------------
3116 // make_mosaic
3117 //
3118 // Assemble a portion of the mosaic positioned at mosaic_min.
3119 // Each tile may be individually tinted with a grayscale color.
3120 // Individual tiles may be omitted.
3121 // Background color (outside the mask) may be specified.
3122 // Tile masks are optional and may be NULL.
3123 //
3124 // Use all possible processors/cores available for multi-threading:
3125 //
3126 template <class image_pointer_t, class transform_pointer_t>
3127 typename image_pointer_t::ObjectType::Pointer
3128 make_mosaic
3129 (const typename image_pointer_t::ObjectType::SpacingType & mosaic_sp,
3130  const typename image_pointer_t::ObjectType::PointType & mosaic_min,
3131  const typename image_pointer_t::ObjectType::SizeType & mosaic_sz,
3132  const unsigned int num_images,
3133  const std::vector<bool> & omit,
3134  const std::vector<double> & tint,
3135  const std::vector<transform_pointer_t> & transform,
3136  const std::vector<image_pointer_t> & image,
3137 
3138  // optional image masks:
3139  const std::vector<mask_t::ConstPointer> & image_mask =
3140  std::vector<mask_t::ConstPointer>(0),
3141 
3142  // feathering to reduce image blurring is optional:
3143  const feathering_t feathering = FEATHER_NONE_E,
3144  double background = 255.0,
3145 
3146  bool dont_allocate = false)
3147 {
3148  // use as many threads as supported by hardware:
3149  unsigned int num_threads = boost::thread::hardware_concurrency();
3150 
3151  // NOTE: for backwards compatibility we do not assemble the mosaic mask:
3152  const bool assemble_mosaic_mask = false;
3153  mask_t::Pointer mosaic_mask;
3154 
3155  return
3156  make_mosaic_mt<image_pointer_t, transform_pointer_t>
3157  (num_threads,
3158  assemble_mosaic_mask,
3159  mosaic_mask,
3160  mosaic_sp,
3161  mosaic_min,
3162  mosaic_sz,
3163  num_images,
3164  omit,
3165  tint,
3166  transform,
3167  image,
3168  image_mask,
3169  feathering,
3170  background,
3171  dont_allocate);
3172 }
3173 
3174 
3175 //----------------------------------------------------------------
3176 // make_mosaic
3177 //
3178 // Assemble a portion of the mosaic positioned at mosaic_min.
3179 // Tiles are not tinted.
3180 //
3181 template <class image_pointer_t, class transform_pointer_t>
3182 typename image_pointer_t::ObjectType::Pointer
3183 make_mosaic
3184 (const typename image_pointer_t::ObjectType::SpacingType & mosaic_sp,
3185  const typename image_pointer_t::ObjectType::PointType & mosaic_min,
3186  const typename image_pointer_t::ObjectType::SizeType & mosaic_sz,
3187  const unsigned int num_images,
3188  const std::vector<bool> & omit,
3189  const std::vector<transform_pointer_t> & transform,
3190  const std::vector<image_pointer_t> & image,
3191 
3192  // optional image masks:
3193  const std::vector<mask_t::ConstPointer> & image_mask =
3194  std::vector<mask_t::ConstPointer>(0),
3195 
3196  // feathering to reduce image blurring is optional:
3197  const feathering_t feathering = FEATHER_NONE_E,
3198  double background = 255.0,
3199 
3200  bool dont_allocate = false)
3201 {
3202  const std::vector<double> tint(num_images, 1.0);
3203  return make_mosaic<image_pointer_t, transform_pointer_t>
3204  (mosaic_sp,
3205  mosaic_min,
3206  mosaic_sz,
3207  num_images,
3208  omit,
3209  tint,
3210  transform,
3211  image,
3212  image_mask,
3213  feathering,
3214  background,
3215  dont_allocate);
3216 }
3217 
3218 //----------------------------------------------------------------
3219 // make_mosaic
3220 //
3221 // Assemble a portion of the mosaic bounded by
3222 // mosaic_min and mosaic_max.
3223 //
3224 template <class image_pointer_t, class transform_pointer_t>
3225 typename image_pointer_t::ObjectType::Pointer
3226 make_mosaic
3227 (const typename image_pointer_t::ObjectType::SpacingType & mosaic_sp,
3228  const typename image_pointer_t::ObjectType::PointType & mosaic_min,
3229  const typename image_pointer_t::ObjectType::PointType & mosaic_max,
3230  const unsigned int num_images,
3231  const std::vector<bool> & omit,
3232  const std::vector<transform_pointer_t> & transform,
3233  const std::vector<image_pointer_t> & image,
3234 
3235  // optional image masks:
3236  const std::vector<mask_t::ConstPointer> & image_mask =
3237  std::vector<mask_t::ConstPointer>(0),
3238 
3239  // feathering to reduce image blurring is optional:
3240  const feathering_t feathering = FEATHER_NONE_E,
3241 
3242  double background = 255.0,
3243 
3244  bool dont_allocate = false)
3245 {
3246  typename image_pointer_t::ObjectType::SizeType mosaic_sz;
3247  mosaic_sz[0] = static_cast<unsigned int>((mosaic_max[0] - mosaic_min[0]) /
3248  mosaic_sp[0]);
3249  mosaic_sz[1] = static_cast<unsigned int>((mosaic_max[1] - mosaic_min[1]) /
3250  mosaic_sp[1]);
3251 
3252  return make_mosaic<image_pointer_t, transform_pointer_t>
3253  (mosaic_sp,
3254  mosaic_min,
3255  mosaic_sz,
3256  num_images,
3257  omit,
3258  transform,
3259  image,
3260  image_mask,
3261  feathering,
3262  background,
3263  dont_allocate);
3264 }
3265 
3266 //----------------------------------------------------------------
3267 // make_mosaic
3268 //
3269 // Assemble a portion of the mosaic bounded by
3270 // mosaic_min and mosaic_max.
3271 //
3272 template <class image_pointer_t, class transform_pointer_t>
3273 typename image_pointer_t::ObjectType::Pointer
3274 make_mosaic
3275 (const typename image_pointer_t::ObjectType::SpacingType & mosaic_sp,
3276  const unsigned int num_images,
3277  const std::vector<bool> & omit,
3278  const std::vector<transform_pointer_t> & transform,
3279  const std::vector<image_pointer_t> & image,
3280 
3281  // optional image masks:
3282  const std::vector<mask_t::ConstPointer> & image_mask =
3283  std::vector<mask_t::ConstPointer>(0),
3284 
3285  // feathering to reduce image blurring is optional:
3286  const feathering_t feathering = FEATHER_NONE_E,
3287 
3288  double background = 255.0,
3289 
3290  bool dont_allocate = false)
3291 {
3292  // mosaic bounding box:
3293  typename image_pointer_t::ObjectType::PointType mosaic_min;
3294  typename image_pointer_t::ObjectType::PointType mosaic_max;
3295  calc_mosaic_bbox<image_pointer_t, transform_pointer_t>
3296  (transform,
3297  image,
3298  mosaic_min,
3299  mosaic_max);
3300 
3301  return make_mosaic<image_pointer_t, transform_pointer_t>
3302  (mosaic_sp,
3303  mosaic_min,
3304  mosaic_max,
3305  num_images,
3306  omit,
3307  transform,
3308  image,
3309  image_mask,
3310  feathering,
3311  background,
3312  dont_allocate);
3313 }
3314 
3315 //----------------------------------------------------------------
3316 // make_mosaic
3317 //
3318 // Assemble the entire mosaic from a set of tiles and transforms.
3319 // Individual tiles may be omitted.
3320 //
3321 template <class image_pointer_t, class transform_pointer_t>
3322 typename image_pointer_t::ObjectType::Pointer
3323 make_mosaic(const std::vector<image_pointer_t> & image,
3324  const std::vector<transform_pointer_t> & transform,
3325  const feathering_t feathering = FEATHER_NONE_E,
3326  const unsigned int omit = std::numeric_limits<unsigned int>::max(),
3327  unsigned int num_images = std::numeric_limits<unsigned int>::max(),
3328  // the masks are optional:
3329  const std::vector<mask_t::ConstPointer> & image_mask =
3330  std::vector<mask_t::ConstPointer>(0))
3331 {
3332  if (num_images == std::numeric_limits<unsigned int>::max())
3333  {
3334  num_images = image.size();
3335  }
3336 
3337  std::vector<bool> omit_vec(num_images);
3338  omit_vec.assign(num_images, false);
3339  if (omit != std::numeric_limits<unsigned int>::max())
3340  {
3341  omit_vec[omit] = true;
3342  }
3343 
3344  return
3345  make_mosaic<image_pointer_t, transform_pointer_t>
3346  (image[0]->GetSpacing(),
3347  num_images,
3348  omit_vec,
3349  transform,
3350  image,
3351  image_mask,
3352  feathering,
3353  0.0); // background
3354 }
3355 
3356 //----------------------------------------------------------------
3357 // update_mosaic
3358 //
3359 // Expand the mosaic by adding another tile to it. This may be
3360 // used to assemble the mosaic incrementally.
3361 //
3362 template <typename T>
3363 typename T::Pointer
3364 update_mosaic(const T * mosaic,
3365  const T * tile,
3366  const base_transform_t * tile_transform,
3367  const mask_t * mask_mosaic = NULL,
3368  const mask_t * mask_tile = NULL)
3369 {
3370  std::vector<typename T::ConstPointer> image(2);
3371  image[0] = mosaic;
3372  image[1] = tile;
3373 
3374  std::vector<base_transform_t::ConstPointer> transform(2);
3375  transform[0] = identity_transform_t::New();
3376  transform[1] = tile_transform;
3377 
3378  std::vector<mask_t::ConstPointer> image_mask(2);
3379  image_mask[0] = mask_mosaic;
3380  image_mask[1] = mask_tile;
3381 
3382  std::vector<bool> omit(2);
3383  omit.assign(2, false);
3384 
3385  return
3386  make_mosaic<typename T::ConstPointer, base_transform_t::ConstPointer>
3387  (mosaic->GetSpacing(),
3388  2,
3389  omit,
3390  transform,
3391  image,
3392  image_mask,
3393  FEATHER_NONE_E);
3394 }
3395 
3396 //----------------------------------------------------------------
3397 // make_mask_st
3398 //
3399 // Assemble a mask for the entire mosaic.
3400 // Individual tiles may be omitted.
3401 //
3402 template <class transform_pointer_t>
3403 mask_t::Pointer
3404 make_mask_st(const mask_t::SpacingType & mosaic_sp,
3405  const unsigned int num_images,
3406  const std::vector<bool> & omit,
3407  const std::vector<transform_pointer_t> & transform,
3408  const std::vector<mask_t::ConstPointer> & image_mask)
3409 {
3410  WRAP(itk_terminator_t terminator("make_mask_st"));
3411 
3412  typedef mask_t::IndexType index_t;
3413  typedef mask_t::PointType point_t;
3414  typedef mask_t::PixelType pixel_t;
3415  typedef mask_t::SpacingType spacing_t;
3416  typedef mask_t::RegionType::SizeType imagesz_t;
3417  typedef itk::ImageRegionIteratorWithIndex<mask_t> itex_t;
3418 
3419  // setup the image mask interpolators:
3420  typedef itk::NearestNeighborInterpolateImageFunction
3421  <mask_t, double> msk_interpolator_t;
3422  std::vector<msk_interpolator_t::Pointer> msk(num_images);
3423  msk.assign(num_images, msk_interpolator_t::Pointer(NULL));
3424 
3425  for (unsigned int i = 0; i < image_mask.size() && i < num_images; i++)
3426  {
3427  if (image_mask[i].GetPointer() == NULL) continue;
3428 
3429  msk[i] = msk_interpolator_t::New();
3430  msk[i]->SetInputImage(image_mask[i]);
3431  }
3432 
3433  // image space bounding boxes (for feathering):
3434  std::vector<point_t> bbox_min(num_images);
3435  std::vector<point_t> bbox_max(num_images);
3436  calc_image_bboxes<mask_t::ConstPointer>(image_mask, bbox_min, bbox_max);
3437 
3438  // mosaic space bounding boxes:
3439  std::vector<point_t> min(num_images);
3440  std::vector<point_t> max(num_images);
3441  calc_mosaic_bboxes<point_t, transform_pointer_t>(transform,
3442  bbox_min,
3443  bbox_max,
3444  min,
3445  max);
3446 
3447  // mosiac bounding box:
3448  point_t mosaic_min;
3449  point_t mosaic_max;
3450  calc_mosaic_bbox<point_t>(min, max, mosaic_min, mosaic_max);
3451 
3452  // setup the mosaic image:
3453  mask_t::Pointer mosaic = mask_t::New();
3454  imagesz_t mosaic_sz;
3455 
3456  mosaic_sz[0] = static_cast<unsigned int>((mosaic_max[0] - mosaic_min[0]) /
3457  mosaic_sp[0]);
3458  mosaic_sz[1] = static_cast<unsigned int>((mosaic_max[1] - mosaic_min[1]) /
3459  mosaic_sp[1]);
3460  mosaic->SetOrigin(pnt2d(mosaic_min[0], mosaic_min[1]));
3461  mosaic->SetRegions(mosaic_sz);
3462  mosaic->SetSpacing(mosaic_sp);
3463 
3464  // make sure there hasn't been an interrupt:
3465  WRAP(terminator.terminate_on_request());
3466 
3467  mosaic->Allocate();
3468  mosaic->FillBuffer(itk::NumericTraits<pixel_t>::Zero);
3469 
3470  itex_t itex(mosaic, mosaic->GetLargestPossibleRegion());
3471  for (itex.GoToBegin(); !itex.IsAtEnd(); ++itex)
3472  {
3473  // make sure there hasn't been an interrupt:
3474  WRAP(terminator.terminate_on_request());
3475 
3476  point_t point;
3477  mosaic->TransformIndexToPhysicalPoint(itex.GetIndex(), point);
3478 
3479  for (unsigned int k = 0; k < num_images; k++)
3480  {
3481  // don't try to add missing or omitted images to the mosaic:
3482  if (omit[k] || (image_mask[k].GetPointer() == NULL)) continue;
3483 
3484  // avoid undesirable radial distortion artifacts for R >> Rmax:
3485  if (!inside_bbox(min[k], max[k], point)) continue;
3486 
3487  const transform_pointer_t & t = transform[k];
3488  point_t pt_k = t->TransformPoint(point);
3489 
3490  // make sure the pixel maps into the image:
3491  if (!msk[k]->IsInsideBuffer(pt_k)) continue;
3492 
3493  // make sure the pixel maps into the image mask:
3494  if (msk[k]->Evaluate(pt_k) == 0) continue;
3495 
3496  itex.Set(1);
3497  break;
3498  }
3499  }
3500 
3501  return mosaic;
3502 }
3503 
3504 
3505 
3506 //----------------------------------------------------------------
3507 // assemble_mask_t
3508 //
3509 // Parallelized mosaic mask assembly mechanism
3510 //
3511 template <typename transform_pointer_t>
3513 {
3514 public:
3515  typedef mask_t::PointType pnt_t;
3516  typedef mask_t::PixelType pxl_t;
3517  typedef mask_t::RegionType rn_t;
3518  typedef mask_t::RegionType::SizeType sz_t;
3519  typedef mask_t::RegionType::IndexType ix_t;
3520  typedef itk::NearestNeighborInterpolateImageFunction<mask_t, double> imask_t;
3521 
3522  assemble_mask_t(// thread index and number of threads, used to avoid
3523  // concurrent access to the same point in the mosaic:
3524  unsigned int thread_offset,
3525  unsigned int thread_stride,
3526 
3527  // mosaic being assembled:
3528  mask_t::Pointer & mosaic,
3529 
3530  // number of tiles to use for this mosaic (may be fewer
3531  // then the number of tiles available):
3532  unsigned int num_tiles,
3533 
3534  // omitted tile flags:
3535  const std::vector<bool> & omit,
3536 
3537  // tile trasforms:
3538  const std::vector<transform_pointer_t> & transform,
3539 
3540  // tiles and tile masks
3541  const std::vector<mask_t::ConstPointer> & mask,
3542 
3543  // tile and tile mask interpolators:
3544  const std::vector<typename imask_t::Pointer> & imask,
3545 
3546  // mosaic space tile bounding boxes:
3547  const std::vector<pnt_t> & min,
3548  const std::vector<pnt_t> & max):
3549 
3550  thread_offset_(thread_offset),
3551  thread_stride_(thread_stride),
3552  mosaic_(mosaic),
3553  num_tiles_(num_tiles),
3554  omit_(omit),
3555  transform_(transform),
3556  mask_(mask),
3557  imask_(imask),
3558  min_(min),
3559  max_(max)
3560  {}
3561 
3562  void execute(the_thread_interface_t * thread)
3563  {
3564  WRAP(itk_terminator_t terminator("assemble_mask_t::execute"));
3565 
3566  rn_t region = mosaic_->GetLargestPossibleRegion();
3567  ix_t origin = region.GetIndex();
3568  sz_t extent = region.GetSize();
3569  ix_t::IndexValueType x_end = origin[0] + extent[0];
3570  ix_t::IndexValueType y_end = origin[1] + extent[1];
3571 
3572  ix_t ix = origin;
3573  for (ix[1] = origin[1]; ix[1] < y_end; ++ix[1])
3574  {
3575  for (ix[0] = origin[0] + thread_offset_;
3576  ix[0] < x_end;
3577  ix[0] += thread_stride_)
3578  {
3579  // check whether termination was requested:
3580  WRAP(terminator.terminate_on_request());
3581 
3582  pnt_t point;
3583  mosaic_->TransformIndexToPhysicalPoint(ix, point);
3584 
3585  mask_t::PixelType mask_pixel = 0;
3586  for (unsigned int k = 0; k < num_tiles_; k++)
3587  {
3588  // don't try to add missing or omitted images to the mosaic:
3589  if (omit_[k] || (mask_[k].GetPointer() == NULL))
3590  {
3591  continue;
3592  }
3593 
3594  // avoid undesirable distortion artifacts:
3595  if (!inside_bbox(min_[k], max_[k], point))
3596  {
3597  continue;
3598  }
3599 
3600  const transform_pointer_t & t = transform_[k];
3601  pnt_t pt_k = t->TransformPoint(point);
3602 
3603  // make sure the pixel maps into the image:
3604  if (!imask_[k]->IsInsideBuffer(pt_k))
3605  {
3606  continue;
3607  }
3608 
3609  // make sure the pixel maps into the image mask:
3610  if (imask_[k]->Evaluate(pt_k) == 0)
3611  {
3612  continue;
3613  }
3614 
3615  mask_pixel = 1;
3616  break;
3617  }
3618 
3619  mosaic_->SetPixel(ix, mask_pixel);
3620  }
3621  }
3622  }
3623 
3624  // data members facilitating concurrent mosaic assembly:
3625  unsigned int thread_offset_;
3626  unsigned int thread_stride_;
3627 
3628  // output mosaic:
3629  mask_t::Pointer & mosaic_;
3630 
3631  // number of tiles used for this mosaic (may be fewer than
3632  // what's available from the tile vector):
3633  unsigned int num_tiles_;
3634 
3635  // omitted tile flags:
3636  const std::vector<bool> & omit_;
3637 
3638  // tile trasforms:
3639  const std::vector<transform_pointer_t> & transform_;
3640 
3641  // tile masks
3642  const std::vector<mask_t::ConstPointer> & mask_;
3643 
3644  // tile mask interpolators:
3645  const std::vector<typename imask_t::Pointer> & imask_;
3646 
3647  // mosaic space tile bounding boxes:
3648  const std::vector<pnt_t> & min_;
3649  const std::vector<pnt_t> & max_;
3650 };
3651 
3652 
3653 //----------------------------------------------------------------
3654 // make_mask_mt
3655 //
3656 // Assemble a mask for the entire mosaic.
3657 // Individual tiles may be omitted.
3658 //
3659 template <class transform_pointer_t>
3660 mask_t::Pointer
3661 make_mask_mt(unsigned int num_threads,
3662  const mask_t::SpacingType & mosaic_sp,
3663  const unsigned int num_images,
3664  const std::vector<bool> & omit,
3665  const std::vector<transform_pointer_t> & transform,
3666  const std::vector<mask_t::ConstPointer> & image_mask)
3667 {
3668  if (num_threads == 1)
3669  {
3670  // use the old single-threaded code:
3671  return make_mask_st<transform_pointer_t>(mosaic_sp,
3672  num_images,
3673  omit,
3674  transform,
3675  image_mask);
3676  }
3677 
3678  // FIXME: make it multi-threaded:
3679  WRAP(itk_terminator_t terminator("make_mask_mt"));
3680 
3681  // setup the image mask interpolators:
3682  typedef itk::NearestNeighborInterpolateImageFunction<mask_t, double> imask_t;
3683  std::vector<imask_t::Pointer> imask(num_images);
3684  imask.assign(num_images, imask_t::Pointer(NULL));
3685 
3686  for (unsigned int i = 0; i < image_mask.size() && i < num_images; i++)
3687  {
3688  if (image_mask[i].GetPointer() == NULL) continue;
3689 
3690  imask[i] = imask_t::New();
3691  imask[i]->SetInputImage(image_mask[i]);
3692  }
3693 
3694  // image space bounding boxes:
3695  typedef mask_t::PointType pnt_t;
3696  std::vector<pnt_t> bbox_min(num_images);
3697  std::vector<pnt_t> bbox_max(num_images);
3698  calc_image_bboxes<mask_t::ConstPointer>(image_mask, bbox_min, bbox_max);
3699 
3700  // mosaic space bounding boxes:
3701  std::vector<pnt_t> min(num_images);
3702  std::vector<pnt_t> max(num_images);
3703  calc_mosaic_bboxes<pnt_t, transform_pointer_t>(transform,
3704  bbox_min,
3705  bbox_max,
3706  min,
3707  max);
3708 
3709  // mosiac bounding box:
3710  pnt_t mosaic_min;
3711  pnt_t mosaic_max;
3712  calc_mosaic_bbox<pnt_t>(min, max, mosaic_min, mosaic_max);
3713 
3714  // setup the mosaic image:
3715  mask_t::Pointer mosaic = mask_t::New();
3716  mask_t::RegionType::SizeType mosaic_sz;
3717  mosaic_sz[0] = static_cast<unsigned int>((mosaic_max[0] - mosaic_min[0]) /
3718  mosaic_sp[0]);
3719  mosaic_sz[1] = static_cast<unsigned int>((mosaic_max[1] - mosaic_min[1]) /
3720  mosaic_sp[1]);
3721  mosaic->SetOrigin(pnt2d(mosaic_min[0], mosaic_min[1]));
3722  mosaic->SetRegions(mosaic_sz);
3723  mosaic->SetSpacing(mosaic_sp);
3724  mosaic->Allocate();
3725 
3726  // setup transactions for multi-threaded mosaic assembly:
3727  std::list<the_transaction_t *> schedule;
3728  for (unsigned int i = 0; i < num_threads; i++)
3729  {
3732  num_threads,
3733  mosaic,
3734  num_images,
3735  omit,
3736  transform,
3737  image_mask,
3738  imask,
3739  min,
3740  max);
3741 
3742  schedule.push_back(t);
3743  }
3744 
3745  // setup the thread pool:
3746  the_thread_pool_t thread_pool(num_threads);
3747  thread_pool.set_idle_sleep_duration(50); // 50 usec
3748  thread_pool.push_back(schedule);
3749  thread_pool.pre_distribute_work();
3750 
3751  // execute mosaic assembly transactions:
3752  suspend_itk_multithreading_t suspend_itk_mt;
3753  thread_pool.start();
3754  thread_pool.wait();
3755 
3756  // done:
3757  return mosaic;
3758 }
3759 
3760 //----------------------------------------------------------------
3761 // make_mask
3762 //
3763 template <class transform_pointer_t>
3764 mask_t::Pointer
3765 make_mask(const mask_t::SpacingType & mosaic_sp,
3766  const unsigned int num_images,
3767  const std::vector<bool> & omit,
3768  const std::vector<transform_pointer_t> & transform,
3769  const std::vector<mask_t::ConstPointer> & image_mask)
3770 {
3771  // use as many threads as supported by hardware:
3772  unsigned int num_threads = boost::thread::hardware_concurrency();
3773 
3774  return make_mask_mt<transform_pointer_t>(num_threads,
3775  mosaic_sp,
3776  num_images,
3777  omit,
3778  transform,
3779  image_mask);
3780 }
3781 
3782 //----------------------------------------------------------------
3783 // make_mask
3784 //
3785 // Assemble a mask for the entire mosaic.
3786 //
3787 template <class transform_pointer_t>
3788 mask_t::Pointer
3789 make_mask(const std::vector<transform_pointer_t> & transform,
3790  const std::vector<mask_t::ConstPointer> & image_mask)
3791 {
3792  const unsigned int num_images = transform.size();
3793  return make_mask<transform_pointer_t>(image_mask[0]->GetSpacing(),
3794  num_images,
3795  std::vector<bool>(num_images, false),
3796  transform,
3797  image_mask);
3798 }
3799 
3800 //----------------------------------------------------------------
3801 // make_mosaic
3802 //
3803 // Assemble the entire mosaic.
3804 //
3805 template <class image_pointer_t, class transform_pointer_t>
3806 typename image_pointer_t::ObjectType::Pointer
3807 make_mosaic(const feathering_t feathering,
3808  const std::vector<transform_pointer_t> & transform,
3809  const std::vector<image_pointer_t> & image,
3810  const std::vector<mask_t::ConstPointer> & image_mask =
3811  std::vector<mask_t::ConstPointer>(0),
3812  bool dont_allocate = false)
3813 {
3814  const unsigned int num_images = transform.size();
3815  return make_mosaic<image_pointer_t, transform_pointer_t>
3816  (image[0]->GetSpacing(),
3817  num_images,
3818  std::vector<bool>(num_images, false), // omit
3819  transform,
3820  image,
3821 
3822  // optional image masks:
3823  image_mask,
3824 
3825  // feathering to reduce image blurring is optional:
3826  feathering,
3827  0.0,
3828  dont_allocate);
3829 }
3830 
3831 //----------------------------------------------------------------
3832 // warp
3833 //
3834 // Return a copy of a given image warped according to
3835 // a given transform.
3836 //
3837 template <typename T>
3838 typename T::Pointer
3839 warp(const T * a,
3840  const base_transform_t * t)
3841 {
3842  std::vector<typename T::ConstPointer> image(1);
3843  image[0] = a;
3844 
3845  std::vector<base_transform_t::ConstPointer> transform(1);
3846  transform[0] = t;
3847 
3848  return
3849  make_mosaic<typename T::ConstPointer, base_transform_t::ConstPointer>
3850  (image,
3851  transform,
3852  FEATHER_NONE_E);
3853 }
3854 
3855 //----------------------------------------------------------------
3856 // resize
3857 //
3858 // scale = 0.5 ---> reduce image in half along each dimension
3859 // scale = 2.0 ---> double the image size along each dimension
3860 //
3861 template <typename T>
3862 typename T::Pointer
3863 resize(const T * in, double scale)
3864 {
3865  typedef itk::ScaleTransform<double, 2> scale_t;
3866  scale_t::Pointer transform = scale_t::New();
3867  scale_t::ScaleType s = transform->GetScale();
3868  s[0] = 1.0 / scale;
3869  s[1] = 1.0 / scale;
3870  transform->SetScale(s);
3871  return warp<T>(in, transform);
3872 }
3873 
3874 //----------------------------------------------------------------
3875 // resize
3876 //
3877 // Calculate a scale factor for a given image, such that
3878 // the largest dimension of the image is equal to max_edge_size.
3879 // Return a scaled copy of the image.
3880 //
3881 template <typename T>
3882 typename T::Pointer
3883 resize(const T * in, unsigned int max_edge_size, double & scale)
3884 {
3885  typename T::RegionType::SizeType sz =
3886  in->GetLargestPossibleRegion().GetSize();
3887 
3888  double xScale = static_cast<double>(max_edge_size) / static_cast<double>(sz[0]);
3889  double yScale = static_cast<double>(max_edge_size) / static_cast<double>(sz[1]);
3890  scale = xScale < yScale ? xScale : yScale;
3891  return resize<T>(in, scale);
3892 }
3893 
3894 //----------------------------------------------------------------
3895 // resize
3896 //
3897 // Scale a given image such that the largest dimension
3898 // of the image is equal to max_edge_size.
3899 // Return a scaled copy of the image.
3900 //
3901 template <typename T>
3902 typename T::Pointer
3903 resize(const T * in, unsigned int max_edge_size)
3904 {
3905  double scale;
3906  return resize<T>(in, max_edge_size, scale);
3907 }
3908 
3909 
3910 //----------------------------------------------------------------
3911 // save_mosaic
3912 //
3913 // Assemble the mosaic and save the mosaic image
3914 // Individual mosaic tiles may be omitted.
3915 //
3916 template <class image_pointer_t, class transform_pointer_t>
3917 void
3918 save_mosaic(const std::vector<image_pointer_t> & image,
3919  const std::vector<transform_pointer_t> & transform,
3920  const bfs::path & filename,
3921  const unsigned int omit = std::numeric_limits<unsigned int>::max(),
3922  unsigned int num_images = std::numeric_limits<unsigned int>::max())
3923 {
3924  typedef typename image_pointer_t::ObjectType::Pointer::ObjectType image_t;
3925 
3926  typename image_t::Pointer mosaic =
3927  make_mosaic<image_pointer_t, transform_pointer_t>(image,
3928  transform,
3929  false,
3930  omit,
3931  num_images);
3932  save<image_t>(mosaic, filename,false);
3933 }
3934 
3935 //----------------------------------------------------------------
3936 // align_fi_mi
3937 //
3938 // Warp images fi and mi. Both warped images will have the same
3939 // dimensions as a mosaic assembled from fi and mi.
3940 //
3941 template <typename T>
3942 void
3943 align_fi_mi(const T * fi,
3944  const T * mi,
3945  const base_transform_t * mi_transform,
3946  typename T::Pointer & fi_aligned,
3947  typename T::Pointer & mi_aligned)
3948 {
3949  std::vector<typename T::ConstPointer> image(2);
3950  image[0] = fi;
3951  image[1] = mi;
3952 
3953  std::vector<base_transform_t::ConstPointer> transform(2);
3954  transform[0] = identity_transform_t::New();
3955  transform[1] = mi_transform;
3956 
3957  fi_aligned =
3958  make_mosaic<typename T::ConstPointer, base_transform_t::ConstPointer>
3959  (image,
3960  transform,
3961  FEATHER_NONE_E,
3962  1);
3963 
3964  mi_aligned =
3965  make_mosaic<typename T::ConstPointer, base_transform_t::ConstPointer>
3966  (image,
3967  transform,
3968  FEATHER_NONE_E,
3969  0);
3970 }
3971 
3972 //----------------------------------------------------------------
3973 // save_composite
3974 //
3975 // Save an RGB image of images fi and mi registered via
3976 // a given transform. The mi image will be saved in the red channel,
3977 // and fi will be saved in the green and blue channels.
3978 //
3979 template <typename T>
3980 void
3981 save_composite(const bfs::path & filename,
3982  const T * fi,
3983  const T * mi,
3984  const base_transform_t * xform,
3985  bool blab = false)
3986 {
3987  typename T::Pointer fi_aligned;
3988  typename T::Pointer mi_aligned;
3989  align_fi_mi(fi, mi, xform, fi_aligned, mi_aligned);
3990 
3991  typedef itk::CastImageFilter<T, native_image_t> cast_to_native_t;
3992  typename cast_to_native_t::Pointer fi_native = cast_to_native_t::New();
3993  typename cast_to_native_t::Pointer mi_native = cast_to_native_t::New();
3994  fi_native->SetInput(fi_aligned);
3995  mi_native->SetInput(mi_aligned);
3996 
3997  typedef itk::RGBPixel<native_pixel_t> composite_pixel_t;
3998  typedef itk::Image<composite_pixel_t, 2> composite_image_t;
3999  typedef itk::ComposeRGBImageFilter<native_image_t, composite_image_t>
4000  compose_filter_t;
4001 
4002  compose_filter_t::Pointer composer = compose_filter_t::New();
4003  composer->SetInput1(mi_native->GetOutput());
4004  composer->SetInput2(fi_native->GetOutput());
4005  composer->SetInput3(fi_native->GetOutput());
4006 
4007  typedef itk::ImageFileWriter<composite_image_t> composite_writer_t;
4008  composite_writer_t::Pointer writer = composite_writer_t::New();
4009  writer->SetFileName(filename.string().c_str());
4010  writer->SetInput(composer->GetOutput());
4011 
4012  //if (blab) std::cout << "saving " << filename << std::endl;
4013  writer->Update();
4014 }
4015 
4016 //----------------------------------------------------------------
4017 // make_mosaic_rgb
4018 //
4019 // Assemble a portion of the mosaic positioned at mosaic_min.
4020 // Each tile may be individually tinted with an RGB color.
4021 // Individual tiles may be omitted.
4022 // Background color (outside the mask) may be specified.
4023 // Tile masks are optional and may be NULL.
4024 //
4025 template <class image_pointer_t, class transform_pointer_t>
4026 void
4027 make_mosaic_rgb
4028 (typename image_pointer_t::ObjectType::Pointer * mosaic,
4029  const typename image_pointer_t::ObjectType::SpacingType & mosaic_sp,
4030  const typename image_pointer_t::ObjectType::PointType & mosaic_min,
4031  const typename image_pointer_t::ObjectType::SizeType & mosaic_sz,
4032  const xyz_t & background_color,
4033  const std::vector<xyz_t> & color,
4034  const std::vector<bool> & omit,
4035  const std::vector<transform_pointer_t> & transform,
4036  const std::vector<image_pointer_t> & image,
4037 
4038  // optional image masks:
4039  const std::vector<mask_t::ConstPointer> & image_mask =
4040  std::vector<mask_t::ConstPointer>(0),
4041 
4042  // feathering to reduce image blurring is optional:
4043  const feathering_t feathering = FEATHER_NONE_E)
4044 {
4045  WRAP(itk_terminator_t terminator("make_mosaic_rgb"));
4046  unsigned int num_images = image.size();
4047 
4048  for (unsigned int i = 0; i < 3; i++)
4049  {
4050  std::vector<double> tint(num_images);
4051  for (unsigned int j = 0; j < num_images; j++)
4052  {
4053  tint[j] = color[j][i] / 255.0;
4054  }
4055 
4056  mosaic[i] =
4057  make_mosaic<image_pointer_t, transform_pointer_t>
4058  (mosaic_sp,
4059  mosaic_min,
4060  mosaic_sz,
4061  num_images,
4062  omit,
4063  tint,
4064  transform,
4065  image,
4066  image_mask,
4067  feathering,
4068  background_color[i]);
4069  }
4070 }
4071 
4072 //----------------------------------------------------------------
4073 // make_mosaic_rgb
4074 //
4075 // Assemble a portion of the mosaic bounded by
4076 // mosaic_min and mosaic_max.
4077 //
4078 // Each tile may be individually tinted with an RGB color.
4079 // Individual tiles may be omitted.
4080 // Background color (outside the mask) may be specified.
4081 // Tile masks are optional and may be NULL.
4082 //
4083 template <class image_pointer_t, class transform_pointer_t>
4084 void
4085 make_mosaic_rgb
4086 (typename image_pointer_t::ObjectType::Pointer * mosaic,
4087  const typename image_pointer_t::ObjectType::SpacingType & mosaic_sp,
4088  const typename image_pointer_t::ObjectType::PointType & mosaic_min,
4089  const typename image_pointer_t::ObjectType::PointType & mosaic_max,
4090  const xyz_t & background_color,
4091  const std::vector<xyz_t> & color,
4092  const std::vector<bool> & omit,
4093  const std::vector<transform_pointer_t> & transform,
4094  const std::vector<image_pointer_t> & image,
4095 
4096  // optional image masks:
4097  const std::vector<mask_t::ConstPointer> & image_mask =
4098  std::vector<mask_t::ConstPointer>(0),
4099 
4100  // feathering to reduce image blurring is optional:
4101  const feathering_t feathering = FEATHER_NONE_E)
4102 {
4103  typename image_pointer_t::ObjectType::SizeType mosaic_sz;
4104  mosaic_sz[0] = static_cast<unsigned int>((mosaic_max[0] - mosaic_min[0]) /
4105  mosaic_sp[0]);
4106  mosaic_sz[1] = static_cast<unsigned int>((mosaic_max[1] - mosaic_min[1]) /
4107  mosaic_sp[1]);
4108 
4109  make_mosaic_rgb<image_pointer_t, transform_pointer_t>
4110  (mosaic,
4111  mosaic_sp,
4112  mosaic_min,
4113  mosaic_sz,
4114  background_color,
4115  color,
4116  omit,
4117  transform,
4118  image,
4119  image_mask,
4120  feathering);
4121 }
4122 
4123 //----------------------------------------------------------------
4124 // make_mosaic_rgb
4125 //
4126 // Assemble the entire mosaic.
4127 // Each tile may be individually tinted with an RGB color.
4128 // Individual tiles may be omitted.
4129 // Background color (outside the mask) may be specified.
4130 // Tile masks are optional and may be NULL.
4131 //
4132 template <class image_pointer_t, class transform_pointer_t>
4133 void
4134 make_mosaic_rgb(typename image_pointer_t::ObjectType::Pointer * mosaic,
4135  const xyz_t & background_color,
4136  const std::vector<xyz_t> & color,
4137  const std::vector<bool> & omit,
4138  const std::vector<transform_pointer_t> & transform,
4139  const std::vector<image_pointer_t> & image,
4140 
4141  // optional image masks:
4142  const std::vector<mask_t::ConstPointer> & image_mask =
4143  std::vector<mask_t::ConstPointer>(0),
4144 
4145  // feathering to reduce image blurring is optional:
4146  const feathering_t feathering = FEATHER_NONE_E)
4147 {
4148  // mosaic pixel spacing will be the same as for the first mosaic tile:
4149  typename image_pointer_t::ObjectType::SpacingType mosaic_sp =
4150  image[0]->GetSpacing();
4151 
4152  // mosaic bounding box:
4153  typename image_pointer_t::ObjectType::PointType mosaic_min;
4154  typename image_pointer_t::ObjectType::PointType mosaic_max;
4155  calc_mosaic_bbox<image_pointer_t, transform_pointer_t>
4156  (transform,
4157  image,
4158  mosaic_min,
4159  mosaic_max);
4160 
4161  make_mosaic_rgb<image_pointer_t, transform_pointer_t>
4162  (mosaic,
4163  mosaic_sp,
4164  mosaic_min,
4165  mosaic_max,
4166  background_color,
4167  color,
4168  omit,
4169  transform,
4170  image,
4171  image_mask,
4172  feathering);
4173 }
4174 
4175 //----------------------------------------------------------------
4176 // make_colors
4177 //
4178 // Generate a scrambled rainbow colormap.
4179 //
4180 extern void
4181 make_colors(const unsigned int & num_color, std::vector<xyz_t> & color);
4182 
4183 //----------------------------------------------------------------
4184 // make_mosaic_rgb
4185 //
4186 // Assemble the entire mosaic.
4187 // Each tile will be tinted with an RGB color
4188 // from a scrambled rainbox colormap.
4189 // Individual tiles may be omitted.
4190 // Background color (outside the mask) may be specified.
4191 // Tile masks are optional and may be NULL.
4192 //
4193 template <class image_pointer_t, class transform_pointer_t>
4194 void
4195 make_mosaic_rgb(typename image_pointer_t::ObjectType::Pointer * mosaic,
4196  const std::vector<bool> & omit,
4197  const std::vector<transform_pointer_t> & transform,
4198  const std::vector<image_pointer_t> & image,
4199 
4200  // optional image masks:
4201  const std::vector<mask_t::ConstPointer> & image_mask =
4202  std::vector<mask_t::ConstPointer>(0),
4203 
4204  // feathering to reduce image blurring is optional:
4205  const feathering_t feathering = FEATHER_NONE_E,
4206 
4207  // background color:
4208  double background = 255.0,
4209 
4210  // should the first tile be white?
4211  bool first_tile_white = false)
4212 {
4213  static const xyz_t EAST = xyz(1, 0, 0);
4214  static const xyz_t NORTH = xyz(0, 1, 0);
4215  static const xyz_t WEST = xyz(0, 0, 1);
4216  static const xyz_t SOUTH = xyz(0, 0, 0);
4217 
4218  unsigned int num_images = image.size();
4219  xyz_t background_color = xyz(background, background, background);
4220  std::vector<xyz_t> color;
4221 
4222  if (first_tile_white)
4223  {
4224  std::vector<xyz_t> tmp;
4225  make_colors(num_images - 1, tmp);
4226  color.resize(num_images);
4227  color[0] = xyz(255, 255, 255);
4228  for (unsigned int i = 1; i < num_images; i++)
4229  {
4230  color[i] = tmp[i - 1];
4231  }
4232  }
4233  else
4234  {
4235  make_colors(num_images, color);
4236  }
4237 
4238  make_mosaic_rgb<image_pointer_t, transform_pointer_t>
4239  (mosaic,
4240  background_color,
4241  color,
4242  omit,
4243  transform,
4244  image,
4245  image_mask,
4246  feathering);
4247 }
4248 
4249 //----------------------------------------------------------------
4250 // make_mosaic_rgb
4251 //
4252 // Assemble the entire mosaic.
4253 // Each tile will be tinted with an RGB color
4254 // from a scrambled rainbox colormap.
4255 // Background color (outside the mask) may be specified.
4256 // Tile masks are optional and may be NULL.
4257 //
4258 template <class image_pointer_t, class transform_pointer_t>
4259 void
4260 make_mosaic_rgb(typename image_pointer_t::ObjectType::Pointer * mosaic,
4261  const feathering_t feathering,
4262  const std::vector<transform_pointer_t> & transform,
4263  const std::vector<image_pointer_t> & image,
4264  const std::vector<mask_t::ConstPointer> & image_mask =
4265  std::vector<mask_t::ConstPointer>(0),
4266  const double background = 255.0,
4267  bool first_tile_white = false)
4268 {
4269  make_mosaic_rgb<image_pointer_t, transform_pointer_t>
4270  (mosaic,
4271  std::vector<bool>(transform.size(), false),
4272  transform,
4273  image,
4274  image_mask,
4275  feathering,
4276  background,
4277  first_tile_white);
4278 }
4279 
4280 //----------------------------------------------------------------
4281 // to_rgb
4282 //
4283 // Make an RGB image from a given grayscale image
4284 //
4285 template <typename T>
4286 void
4287 to_rgb(const T * image, native_image_t::Pointer * rgb)
4288 {
4289  rgb[0] =
4290  cast<T, native_image_t>(remap_min_max<T>(image, 0.0, 255.0));
4291  rgb[1] = cast<native_image_t, native_image_t>(rgb[0]);
4292  rgb[2] = cast<native_image_t, native_image_t>(rgb[0]);
4293 }
4294 
4295 //----------------------------------------------------------------
4296 // save_rgb
4297 //
4298 // Save and RGB image specified by the individual color channel images.
4299 //
4300 template <class image_ptr_t>
4301 void
4302 save_rgb(const image_ptr_t * image, const bfs::path & filename, bool blab = false)
4303 {
4304  typedef typename image_ptr_t::ObjectType::Pointer::ObjectType image_t;
4305 
4306  typedef itk::RGBPixel<native_pixel_t> composite_pixel_t;
4307  typedef itk::Image<composite_pixel_t, 2> composite_image_t;
4308  typedef itk::ComposeRGBImageFilter<image_t, composite_image_t>
4309  compose_filter_t;
4310 
4311  typename compose_filter_t::Pointer composer = compose_filter_t::New();
4312  composer->SetInput1(image[0]);
4313  composer->SetInput2(image[1]);
4314  composer->SetInput3(image[2]);
4315 
4316  typedef itk::ImageFileWriter<composite_image_t> composite_writer_t;
4317  typename composite_writer_t::Pointer writer = composite_writer_t::New();
4318  writer->SetFileName(filename.string().c_str());
4319  writer->SetInput(composer->GetOutput());
4320 
4321  //if (blab) std::cout << "saving " << filename << std::endl;
4322  writer->Update();
4323 }
4324 
4325 //----------------------------------------------------------------
4326 // save_mosaic_rgb
4327 //
4328 // Assemble the entire mosaic.
4329 // Each tile will be tinted with an RGB color
4330 // from a scrambled rainbox colormap.
4331 // Background color (outside the mask) may be specified.
4332 // Tile masks are optional and may be NULL.
4333 //
4334 // Save the resulting mosaic
4335 //
4336 template <class image_pointer_t, class transform_pointer_t>
4337 void
4338 save_mosaic_rgb(const bfs::path & filename,
4339  const feathering_t feathering,
4340  const std::vector<transform_pointer_t> & transform,
4341  const std::vector<image_pointer_t> & image,
4342  const std::vector<mask_t::ConstPointer> & image_mask =
4343  std::vector<mask_t::ConstPointer>(0),
4344  const double background = 255.0,
4345  bool first_tile_white = false,
4346  bool blab = false)
4347 {
4348  typedef typename image_pointer_t::ObjectType::Pointer::ObjectType image_t;
4349  typename image_t::Pointer mosaic[3];
4350  make_mosaic_rgb<image_pointer_t, transform_pointer_t>
4351  (mosaic,
4352  feathering,
4353  transform,
4354  image,
4355  image_mask,
4356  background,
4357  first_tile_white);
4358  save_rgb<typename image_t::Pointer>(mosaic, filename, blab);
4359 }
4360 
4361 //----------------------------------------------------------------
4362 // save_rgb
4363 //
4364 // Save an RGB image of images fi and mi registered via
4365 // a given transform.
4366 //
4367 template <typename T>
4368 void
4369 save_rgb(const bfs::path & fn_save,
4370  const T * fi,
4371  const T * mi,
4372  const base_transform_t * xform,
4373  const mask_t * fi_mask = 0,
4374  const mask_t * mi_mask = 0,
4375  bool blab = false)
4376 {
4377  std::vector<typename T::ConstPointer> image(2);
4378  std::vector<mask_t::ConstPointer> mask(2);
4379  std::vector<base_transform_t::ConstPointer> transform(2);
4380 
4381  image[0] = fi;
4382  image[1] = mi;
4383 
4384  mask[0] = fi_mask;
4385  mask[1] = mi_mask;
4386 
4387  transform[0] = identity_transform_t::New();
4388  transform[1] = xform;
4389 
4390  typename T::Pointer mosaic[3];
4391  make_mosaic_rgb(mosaic,
4392  std::vector<bool>(2, false),
4393  transform,
4394  image,
4395  mask,
4396  FEATHER_NONE_E,
4397  255.0,
4398  false);
4399 
4400  save_rgb<typename T::Pointer>(mosaic, fn_save, blab);
4401 }
4402 
4403 
4404 //----------------------------------------------------------------
4405 // inverse
4406 //
4407 // Return an inverse of a given translation transform.
4408 //
4409 inline static translate_transform_t::Pointer
4410 inverse(const translate_transform_t * transform)
4411 {
4412  translate_transform_t::Pointer inv = NULL;
4413  if (transform != NULL)
4414  {
4415  inv = translate_transform_t::New();
4416  inv->SetOffset(neg(transform->GetOffset()));
4417  }
4418 
4419  return inv;
4420 }
4421 
4422 
4423 //----------------------------------------------------------------
4424 // remap
4425 //
4426 // Re-arrange a data vector according to a given mapping
4427 //
4428 template <class data_t>
4429 void
4430 remap(const std::list<unsigned int> & mapping,
4431  const std::vector<data_t> & in,
4432  std::vector<data_t> & out)
4433 {
4434  unsigned int size = mapping.size();
4435  out.resize(size);
4436 
4437  typename std::list<unsigned int>::const_iterator iter = mapping.begin();
4438  for (unsigned int i = 0; i < size; i++, ++iter)
4439  {
4440  out[i] = in[*iter];
4441  }
4442 }
4443 
4444 //----------------------------------------------------------------
4445 // remap
4446 //
4447 // Re-arrange a data list according to a given mapping
4448 //
4449 template <class data_t>
4450 void
4451 remap(const std::list<unsigned int> & mapping,
4452  const std::list<data_t> & in,
4453  std::list<data_t> & out)
4454 {
4455  unsigned int size = in.size();
4456 
4457  // setup rapid access to the list elements:
4458  std::vector<const data_t *> rapid(size);
4459  {
4460  typename std::list<data_t>::const_iterator iter = in.begin();
4461  for (unsigned int i = 0; i < size; i++, ++iter)
4462  {
4463  rapid[i] = &(*iter);
4464  }
4465  }
4466 
4467  // swap the list elements:
4468  out.clear();
4469  typename std::list<unsigned int>::const_iterator iter = mapping.begin();
4470  for (; iter != mapping.end(); ++iter)
4471  {
4472  out.push_back(*(rapid[*iter]));
4473  }
4474 }
4475 
4476 //----------------------------------------------------------------
4477 // remap
4478 //
4479 // Return a re-arranged data container according to a given mapping.
4480 //
4481 template <class container_t>
4482 container_t
4483 remap(const std::list<unsigned int> & mapping,
4484  const container_t & container)
4485 {
4486  container_t out;
4487  remap(mapping, container, out);
4488  return out;
4489 }
4490 
4491 
4492 //----------------------------------------------------------------
4493 // mark
4494 //
4495 // Draw a cross mark in the given image.
4496 //
4497 template <typename T>
4498 void
4499 mark(typename T::Pointer & image,
4500  const typename T::IndexType & index,
4501  const typename T::PixelType & mark_value,
4502  const int arm_length = 2,
4503  const char symbol = '+')
4504 {
4505  typedef typename T::SpacingType spacing_t;
4506  typedef typename T::RegionType::SizeType image_size_t;
4507  typedef typename T::IndexType index_t;
4508 
4509  image_size_t sz = image->GetLargestPossibleRegion().GetSize();
4510  index_t xy;
4511 
4512  for (int j = -arm_length; j <= arm_length; j++)
4513  {
4514  int x = index[0] + j;
4515  int y = index[1] + j;
4516 
4517  if (symbol == '+')
4518  {
4519  // draw a cross:
4520  xy[0] = x;
4521  xy[1] = index[1];
4522  if (xy[0] >= 0 && image_size_value_t(xy[0]) < sz[0] &&
4523  xy[1] >= 0 && image_size_value_t(xy[1]) < sz[1])
4524  {
4525  image->SetPixel(xy, mark_value);
4526  }
4527 
4528  xy[0] = index[0];
4529  xy[1] = y;
4530  if (xy[0] >= 0 && image_size_value_t(xy[0]) < sz[0] &&
4531  xy[1] >= 0 && image_size_value_t(xy[1]) < sz[1])
4532  {
4533  image->SetPixel(xy, mark_value);
4534  }
4535  }
4536  else
4537  {
4538  // draw a diagonal cross:
4539  xy[0] = x;
4540  xy[1] = y;
4541  if (xy[0] >= 0 && image_size_value_t(xy[0]) < sz[0] &&
4542  xy[1] >= 0 && image_size_value_t(xy[1]) < sz[1])
4543  {
4544  image->SetPixel(xy, mark_value);
4545  }
4546 
4547  xy[1] = index[1] - j;
4548  if (xy[0] >= 0 && image_size_value_t(xy[0]) < sz[0] &&
4549  xy[1] >= 0 && image_size_value_t(xy[1]) < sz[1])
4550  {
4551  image->SetPixel(xy, mark_value);
4552  }
4553  }
4554  }
4555 }
4556 
4557 //----------------------------------------------------------------
4558 // mark
4559 //
4560 // Draw a cross mark in the given image.
4561 //
4562 template <typename T>
4563 void
4564 mark(typename T::Pointer & image,
4565  const pnt2d_t & mark_coords,
4566  const typename T::PixelType & mark_value,
4567  const int arm_length = 2,
4568  const char symbol = '+')
4569 {
4570  typedef typename T::IndexType index_t;
4571 
4572  index_t index;
4573  if (!image->TransformPhysicalPointToIndex(mark_coords, index))
4574  {
4575  // the mark lays outside of the image:
4576  return;
4577  }
4578 
4579  mark<T>(image, index, mark_value, arm_length, symbol);
4580 }
4581 
4582 //----------------------------------------------------------------
4583 // fill
4584 //
4585 // Fill a portion of the image with a constant value.
4586 //
4587 template <typename T>
4588 void
4589 fill(typename T::Pointer & a,
4590  unsigned int x,
4591  unsigned int y,
4592  unsigned int w,
4593  unsigned int h,
4594  const typename T::PixelType & fill_value =
4595  itk::NumericTraits<typename T::PixelType>::Zero)
4596 {
4597  WRAP(itk_terminator_t terminator("fill"));
4598 
4599  // shortcuts:
4600  typedef typename T::IndexType ix_t;
4601  typedef typename T::RegionType rn_t;
4602  typedef typename T::SizeType sz_t;
4603 
4604  sz_t sz = a->GetLargestPossibleRegion().GetSize();
4605 
4606  ix_t origin;
4607  origin[0] = x;
4608  origin[1] = y;
4609 
4610  sz_t extent;
4611  extent[0] = w;
4612  extent[1] = h;
4613 
4614  rn_t region;
4615  region.SetIndex(origin);
4616  region.SetSize(extent);
4617 
4618  typedef typename itk::ImageRegionIterator<T> iter_t;
4619  iter_t iter(a, region);
4620  for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter)
4621  {
4622  // make sure there hasn't been an interrupt:
4623  WRAP(terminator.terminate_on_request());
4624 
4625  ix_t ix = iter.GetIndex();
4626  if (image_size_value_t(ix[0]) >= sz[0] ||
4627  image_size_value_t(ix[1]) >= sz[1])
4628  {
4629  continue;
4630  }
4631 
4632  iter.Set(fill_value);
4633  }
4634 }
4635 
4636 //----------------------------------------------------------------
4637 // save_transform
4638 //
4639 // Save an ITK transform to a stream.
4640 //
4641 extern void
4642 save_transform(std::ostream & so, const itk::TransformBase * t);
4643 
4644 //----------------------------------------------------------------
4645 // load_transform
4646 //
4647 // Load an ITK transform of specified type from a stream.
4648 //
4649 extern itk::TransformBase::Pointer
4650 load_transform(std::istream & si, const bfs::path & transform_type);
4651 
4652 //----------------------------------------------------------------
4653 // load_transform
4654 //
4655 // Load transform type from the stream, then load the transform
4656 // of that type.
4657 //
4658 extern itk::TransformBase::Pointer
4659 load_transform(std::istream & si);
4660 
4661 
4662 //----------------------------------------------------------------
4663 // save_mosaic
4664 //
4665 // Save image filenames and associated ITK transforms to a stream.
4666 //
4667 extern void
4668 save_mosaic(std::ostream & so,
4669  const unsigned int & num_images,
4670  const double & pixel_spacing,
4671  const bool & use_std_mask,
4672  const std::vector<bfs::path> & images,
4673  const std::vector<const itk::TransformBase *>& transform);
4674 
4675 //----------------------------------------------------------------
4676 // load_mosaic
4677 //
4678 // Load image filenames and associated ITK transforms from a stream.
4679 //
4680 extern void
4681 load_mosaic(std::istream & si,
4682  double & pixel_spacing,
4683  bool & use_std_mask,
4684  std::vector<bfs::path> & image,
4685  std::vector<itk::TransformBase::Pointer> & transform);
4686 
4687 
4688 //----------------------------------------------------------------
4689 // save_mosaic
4690 //
4691 // Save tile image names and associated ITK transforms to a stream.
4692 //
4693 template <class transform_t>
4694 void
4695 save_mosaic(std::ostream & so,
4696  const double & pixel_spacing,
4697  const bool & use_std_mask,
4698  const std::list<bfs::path> & images,
4699  const std::vector<typename transform_t::Pointer> & transforms)
4700 {
4701  unsigned int num_images = transforms.size();
4702  std::vector<bfs::path> image(num_images);
4703  std::vector<const itk::TransformBase *> tbase(num_images);
4704 
4705  std::vector<typename transform_t::Pointer> & ttmp =
4706  const_cast<std::vector<typename transform_t::Pointer> &>(transforms);
4707 
4708  std::list<bfs::path>::const_iterator iter = images.begin();
4709  for (unsigned int i = 0; i < images.size(); i++, ++iter)
4710  {
4711  // TODO: copy to new container needed?
4712  image[i] = *iter;
4713  tbase[i] = ttmp[i].GetPointer();
4714  }
4715 
4716  save_mosaic(so,
4717  image.size(),
4718  pixel_spacing,
4719  use_std_mask,
4720  image,
4721  tbase);
4722 }
4723 
4724 //----------------------------------------------------------------
4725 // load_mosaic
4726 //
4727 // Load tile image names and associated ITK transforms from a stream.
4728 //
4729 template <class transform_t>
4730 void
4731 load_mosaic(std::istream & si,
4732  double & pixel_spacing,
4733  bool & use_std_mask,
4734  std::list<bfs::path> & images,
4735  std::vector<typename transform_t::Pointer> & transforms,
4736  const bfs::path & image_path)
4737 {
4738  std::vector<bfs::path> image;
4739  std::vector<itk::TransformBase::Pointer> tbase;
4740 
4741  load_mosaic(si, pixel_spacing, use_std_mask, image, tbase);
4742  transforms.resize(tbase.size());
4743  for (unsigned int i = 0; i < tbase.size(); i++)
4744  {
4745  images.push_back(image[i]);
4746  transforms[i] = dynamic_cast<transform_t *>(tbase[i].GetPointer());
4747  }
4748 
4749  if ( image_path.empty() )
4750  return;
4751 
4752  // Replace global paths for backwards compatibility.
4753  for (std::list<bfs::path>::iterator iter = images.begin();
4754  iter != images.end(); ++iter)
4755  {
4756  (*iter) = image_path / iter->filename();
4757  }
4758 }
4759 
4760 //----------------------------------------------------------------
4761 // histogram_equalization
4762 //
4763 // Histogram Equalization.
4764 // Transformed image will be mapped into the new min/max pixel
4765 // range. If the new range is not specified, pixels will be
4766 // remapped into the original range.
4767 //
4768 template <typename T>
4769 typename T::Pointer
4770 histogram_equalization(const T * in,
4771  const unsigned int bins = 256,
4772  typename T::PixelType new_min =
4773  std::numeric_limits<typename T::PixelType>::max(),
4774  typename T::PixelType new_max =
4775  -std::numeric_limits<typename T::PixelType>::max(),
4776  const mask_t * mask = NULL)
4777 {
4778  // local typedefs:
4779  typedef typename T::RegionType rn_t;
4780  typedef typename T::IndexType ix_t;
4781  typedef typename T::SizeType sz_t;
4782  typedef typename T::PixelType pixel_t;
4783 
4784  // range of the image intensities:
4785  pixel_t p_min;
4786  pixel_t p_max;
4787  image_min_max<T>(in, p_min, p_max, mask);
4788  pixel_t p_rng = p_max - p_min;
4789 
4790  typename T::Pointer image = cast<T, T>(in);
4791 
4792  if (p_rng == pixel_t(0))
4793  {
4794  // nothing to do:
4795  return image;
4796  }
4797 
4798  if (new_min == std::numeric_limits<pixel_t>::max()) new_min = p_min;
4799  if (new_max == -std::numeric_limits<pixel_t>::max()) new_max = p_max;
4800  pixel_t new_rng = new_max - new_min;
4801 
4802  sz_t sz = in->GetLargestPossibleRegion().GetSize();
4803 
4804  // initialize the histogram:
4805  std::vector<unsigned int> pdf(bins);
4806  std::vector<double> clipped_pdf(bins);
4807  std::vector<double> cdf(bins);
4808 
4809  for (unsigned int i = 0; i < bins; i++)
4810  {
4811  pdf[i] = 0;
4812  clipped_pdf[i] = 0.0;
4813  cdf[i] = 0.0;
4814  }
4815 
4816  typedef typename itk::ImageRegionConstIteratorWithIndex<T> iter_t;
4817  iter_t iter(in, in->GetLargestPossibleRegion());
4818  for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter)
4819  {
4820  ix_t ix = iter.GetIndex();
4821 
4822  if (pixel_in_mask<T>(in, mask, ix))
4823  {
4824  pixel_t p = iter.Get();
4825  unsigned int bin =
4826  static_cast<unsigned int>(static_cast<double>(p - p_min) / static_cast<double>(p_rng) *
4827  static_cast<double>(bins - 1));
4828  pdf[bin]++;
4829  }
4830  }
4831 
4832  // build a cumulative distribution function (CDF):
4833  cdf[0] = static_cast<double>(pdf[0]);
4834  for (unsigned int i = 1; i < bins; i++)
4835  {
4836  cdf[i] = cdf[i - 1] + static_cast<double>(pdf[i]);
4837  }
4838 
4839  // update the image:
4840  iter = iter_t(in, in->GetLargestPossibleRegion());
4841  for (iter.GoToBegin(); !iter.IsAtEnd(); ++iter)
4842  {
4843  ix_t ix = iter.GetIndex();
4844 
4845  // generate the output pixel:
4846  pixel_t p_out = new_min;
4847  if (pixel_in_mask<T>(in, mask, ix))
4848  {
4849  pixel_t p_in = iter.Get();
4850  unsigned int bin = static_cast<unsigned int>((static_cast<double>(p_in - p_min) /
4851  static_cast<double>(p_rng)) *
4852  static_cast<double>(bins - 1));
4853  p_out = pixel_t(static_cast<double>(new_min) + static_cast<double>(new_rng) *
4854  static_cast<double>(cdf[bin]) / static_cast<double>(cdf[bins - 1]));
4855  }
4856 
4857  image->SetPixel(ix, p_out);
4858  }
4859 
4860  return image;
4861 }
4862 
4863 
4864 //----------------------------------------------------------------
4865 // clip_histogram
4866 //
4867 // This is used by CLAHE to limit the contrast ratio slope.
4868 //
4869 extern void
4870 clip_histogram(const double & clipping_height,
4871  const unsigned int & pdf_size,
4872  const unsigned int * pdf,
4873  double * clipped_pdf);
4874 
4875 //----------------------------------------------------------------
4876 // CLAHE
4877 //
4878 // Perform Contrast-Limited Adaptive Histogram Equalization
4879 // Transformed image will be mapped into the new min/max pixel
4880 // range. If the new range is not specified, pixels will be
4881 // remapped into the original range.
4882 //
4883 template <typename T>
4884 typename T::Pointer
4885 CLAHE(const T * in,
4886  int nx,
4887  int ny,
4888  double max_slope,
4889  const unsigned int bins = 256,
4890  typename T::PixelType new_min =
4891  std::numeric_limits<typename T::PixelType>::max(),
4892  typename T::PixelType new_max =
4893  -std::numeric_limits<typename T::PixelType>::max(),
4894  const mask_t * mask = NULL)
4895 {
4896  // local typedefs:
4897  typedef typename T::RegionType rn_t;
4898  typedef typename T::IndexType ix_t;
4899  typedef typename T::SizeType sz_t;
4900  typedef typename T::PixelType pixel_t;
4901 
4902  // sanity check:
4903 // assert(nx > 1 && ny > 1);
4904  if ( nx <= 1 && ny <= 1 )
4905  {
4906  CORE_THROW_EXCEPTION("Window dimensions (nx, ny) fail sanity check");
4907  }
4908 
4909  // assert(max_slope >= 1.0 || max_slope == 0.0);
4910  if ( max_slope < 1.0 && max_slope != 0 ) // TODO: comparison tolerance?
4911  {
4912  CORE_THROW_EXCEPTION("Max_slope fails sanity check");
4913  }
4914 
4915  if (in == 0)
4916  {
4917  CORE_THROW_EXCEPTION("Null input image");
4918  }
4919 
4920  // range of the image intensities:
4921  pixel_t p_min;
4922  pixel_t p_max;
4923  image_min_max<T>(in, p_min, p_max, mask);
4924  pixel_t p_rng = p_max - p_min;
4925 
4926  typename T::Pointer image = cast<T, T>(in);
4927 
4928  if (p_rng == pixel_t(0))
4929  {
4930  // nothing to do:
4931  return image;
4932  }
4933 
4934  if (new_min == std::numeric_limits<pixel_t>::max()) new_min = p_min;
4935  if (new_max == -std::numeric_limits<pixel_t>::max()) new_max = p_max;
4936  pixel_t new_rng = new_max - new_min;
4937 
4938  sz_t sz = in->GetLargestPossibleRegion().GetSize();
4939  const int image_w = sz[0];
4940  const int image_h = sz[1];
4941 
4942  // make sure the histogram window doesn't exceed the image:
4943  nx = std::min(nx, image_w);
4944  ny = std::min(ny, image_h);
4945 
4946  // calculate the histogram window center:
4947  const int cx = nx / 2;
4948  const int cy = ny / 2;
4949 
4950  // initialize the histogram:
4951  std::vector<unsigned int> pdf(bins);
4952  std::vector<double> clipped_pdf(bins);
4953  std::vector<double> cdf(bins);
4954 
4955  for (unsigned int i = 0; i < bins; i++)
4956  {
4957  pdf[i] = 0;
4958  clipped_pdf[i] = 0.0;
4959  cdf[i] = 0.0;
4960  }
4961 
4962  for (int x = 0; x < nx; x++)
4963  {
4964  for (int y = 0; y < ny; y++)
4965  {
4966  ix_t ix;
4967  ix[0] = x;
4968  ix[1] = y;
4969 
4970  if (pixel_in_mask<T>(in, mask, ix))
4971  {
4972  pixel_t p = in->GetPixel(ix);
4973  unsigned int bin =
4974  static_cast<unsigned int>(static_cast<double>(p - p_min) / static_cast<double>(p_rng) *
4975  static_cast<double>(bins - 1));
4976  pdf[bin]++;
4977  }
4978  }
4979  }
4980 
4981  // clip the histogram:
4982  if (max_slope != 0.0)
4983  {
4984  double clipping_height =
4985  (static_cast<double>(nx * ny) / static_cast<double>(bins)) * max_slope;
4986  clip_histogram(clipping_height, bins, &(pdf[0]), &(clipped_pdf[0]));
4987  }
4988  else
4989  {
4990  for (unsigned int i = 0; i < bins; i++)
4991  {
4992  clipped_pdf[i] = static_cast<double>(pdf[i]);
4993  }
4994  }
4995 
4996  // build a cumulative distribution function (CDF):
4997  cdf[0] = static_cast<double>(clipped_pdf[0]);
4998  for (unsigned int i = 1; i < bins; i++)
4999  {
5000  cdf[i] = cdf[i - 1] + static_cast<double>(clipped_pdf[i]);
5001  }
5002 
5003  // start at the origin:
5004  int hist_x = cx;
5005  int hist_y = cy;
5006 //#if 1
5007  for (int x = 0; x < image_w; x++)
5008  {
5009  // alternate the direction of the scanline traversal:
5010  int y0 = (x % 2 == 0) ? 0 : image_h - 1;
5011  int y1 = (x % 2 == 0) ? image_h - 1 : 0;
5012  int dy = (x % 2 == 0) ? 1 : -1;
5013 
5014  for (int y = y0; y != (y1 + dy); y += dy)
5015  {
5016 //#else
5017 // /*
5018 // for (int y = 0; y < image_h; y++)
5019 // {
5020 // // alternate the direction of the scanline traversal:
5021 // int x0 = (y % 2 == 0) ? 0 : image_w - 1;
5022 // int x1 = (y % 2 == 0) ? image_w - 1 : 0;
5023 // int dx = (y % 2 == 0) ? 1 : -1;
5024 //
5025 // for (int x = x0; x != (x1 + dx); x += dx)
5026 // {
5027 // */
5028 //#endif
5029  int spill_x0 = std::max(0, cx - x);
5030  int spill_y0 = std::max(0, cy - y);
5031  int spill_x1 = std::max(0, x - (image_w - nx + cx));
5032  int spill_y1 = std::max(0, y - (image_h - ny + cy));
5033 
5034  int new_hx = x + spill_x0 - spill_x1;
5035  int new_hy = y + spill_y0 - spill_y1;
5036 
5037  int shift_x = new_hx - hist_x;
5038  int shift_y = new_hy - hist_y;
5039 
5040  if (shift_x != 0)
5041  {
5042  // update the histogram (add-remove columns):
5043 // assert(shift_x == 1 || shift_x == -1);
5044  if ( shift_x != 1 && shift_x != -1 )
5045  {
5046  CORE_THROW_EXCEPTION("bad histogram shift");
5047  }
5048 
5049  for (int ty = 0; ty < ny; ty++)
5050  {
5051  ix_t ix;
5052  ix[1] = hist_y + ty - cy;
5053 
5054  // add:
5055  ix[0] = (shift_x > 0) ? new_hx - cx + nx - 1 : new_hx - cx;
5056 
5057  if (pixel_in_mask<T>(in, mask, ix))
5058  {
5059  pixel_t a = in->GetPixel(ix);
5060  unsigned int bin = static_cast<unsigned int>(
5061  (static_cast<double>(a - p_min) / static_cast<double>(p_rng)) * static_cast<double>(bins - 1));
5062  pdf[bin]++;
5063  }
5064 
5065  // remove:
5066  ix[0] = (shift_x > 0) ? hist_x - cx : hist_x - cx + nx - 1;
5067  if (pixel_in_mask<T>(in, mask, ix))
5068  {
5069  pixel_t d = in->GetPixel(ix);
5070  unsigned int bin = static_cast<unsigned int>(
5071  (static_cast<double>(d - p_min) / static_cast<double>(p_rng)) * static_cast<double>(bins - 1));
5072  pdf[bin]--;
5073  }
5074  }
5075 
5076  hist_x = new_hx;
5077  }
5078 
5079  if (shift_y != 0)
5080  {
5081  // update the histogram (add-remove rows):
5082 // assert(shift_y == 1 || shift_y == -1);
5083  if ( shift_y != 1 && shift_y != -1 )
5084  {
5085  CORE_THROW_EXCEPTION("bad histogram shift");
5086  }
5087 
5088  for (int tx = 0; tx < nx; tx++)
5089  {
5090  ix_t ix;
5091  ix[0] = hist_x + tx - cx;
5092 
5093  // add:
5094  ix[1] = (shift_y > 0) ? new_hy - cy + ny - 1 : new_hy - cy;
5095  if (pixel_in_mask<T>(in, mask, ix))
5096  {
5097  pixel_t a = in->GetPixel(ix);
5098  unsigned int bin = static_cast<unsigned int>(
5099  (static_cast<double>(a - p_min) / static_cast<double>(p_rng)) * static_cast<double>(bins - 1));
5100  pdf[bin]++;
5101  }
5102 
5103  // remove:
5104  ix[1] = (shift_y > 0) ? hist_y - cy : hist_y - cy + ny - 1;
5105  if (pixel_in_mask<T>(in, mask, ix))
5106  {
5107  pixel_t d = in->GetPixel(ix);
5108  unsigned int bin = static_cast<unsigned int>(
5109  (static_cast<double>(d - p_min) / static_cast<double>(p_rng)) * static_cast<double>(bins - 1));
5110  pdf[bin]--;
5111  }
5112  }
5113 
5114  hist_y = new_hy;
5115  }
5116 
5117  if (shift_x != 0 || shift_y != 0)
5118  {
5119  // clip the histogram:
5120  if (max_slope != 0.0)
5121  {
5122  double clipping_height =
5123  (static_cast<double>(nx * ny) / static_cast<double>(bins)) * max_slope;
5124  clip_histogram(clipping_height, bins, &(pdf[0]), &(clipped_pdf[0]));
5125  }
5126  else
5127  {
5128  for (unsigned int i = 0; i < bins; i++)
5129  {
5130  clipped_pdf[i] = static_cast<double>(pdf[i]);
5131  }
5132  }
5133 
5134  // build a cumulative distribution function (CDF):
5135  cdf[0] = static_cast<double>(clipped_pdf[0]);
5136  for (unsigned int i = 1; i < bins; i++)
5137  {
5138  cdf[i] = cdf[i - 1] + static_cast<double>(clipped_pdf[i]);
5139  }
5140  }
5141 
5142  // generate the output pixel:
5143  ix_t ix;
5144  ix[0] = x;
5145  ix[1] = y;
5146 
5147  pixel_t p_out = new_min;
5148  if (pixel_in_mask<T>(in, mask, ix))
5149  {
5150  pixel_t p_in = in->GetPixel(ix);
5151  unsigned int bin = static_cast<unsigned int>(
5152  (static_cast<double>(p_in - p_min) / static_cast<double>(p_rng)) * static_cast<double>(bins - 1));
5153  p_out = pixel_t(static_cast<double>(new_min) + static_cast<double>(new_rng) *
5154  static_cast<double>(cdf[bin]) / static_cast<double>(cdf[bins - 1]));
5155  }
5156 
5157  image->SetPixel(ix, p_out);
5158  }
5159  }
5160 
5161  return image;
5162 }
5163 
5164 
5165 //----------------------------------------------------------------
5166 // median
5167 //
5168 // Return a copy of a given image de-noised with a median filter.
5169 //
5170 template <typename T>
5171 typename T::Pointer
5172 median(const T * a, const unsigned int & median_radius)
5173 {
5174  typedef itk::MedianImageFilter<T, T> filter_t;
5175  typename filter_t::Pointer filter = filter_t::New();
5176  typename filter_t::InputSizeType radius;
5177  radius[0] = median_radius;
5178  radius[1] = median_radius;
5179  filter->SetInput(a);
5180  filter->SetRadius(radius);
5181 
5182  // FIXME:
5183  // itk::SimpleFilterWatcher w(filter.GetPointer(), "median");
5184 
5185  WRAP(terminator_t<filter_t> terminator(filter));
5186  filter->Update();
5187  return filter->GetOutput();
5188 }
5189 
5190 
5191 //----------------------------------------------------------------
5192 // normalize
5193 //
5194 // Return a copy of the image normalized (pixels shifted and
5195 // rescaled) using the
5196 //
5197 template <typename T>
5198 typename T::Pointer
5199 normalize(const T * a, const mask_t * ma)
5200 {
5201  typedef itk::NormalizeImageFilterWithMask<T, T> filter_t;
5202  typename filter_t::Pointer filter = filter_t::New();
5203  filter->SetInput(a);
5204  filter->SetImageMask(mask_so(ma));
5205 
5206  WRAP(terminator_t<filter_t> terminator(filter));
5207  filter->Update();
5208  return filter->GetOutput();
5209 }
5210 
5211 //----------------------------------------------------------------
5212 // calc_pixel_weight
5213 //
5214 // Calculate a pixel feathering weight used to blend mosaic tiles.
5215 //
5216 inline double
5217 pixel_weight(const image_t::IndexType & min,
5218  const image_t::IndexType & max,
5219  const image_t::IndexType & ix)
5220 {
5221  double w = static_cast<double>(max[0]) - static_cast<double>(min[0]);
5222  double h = static_cast<double>(max[1]) - static_cast<double>(min[1]);
5223  double r = 0.5 * ((w > h) ? h : w);
5224  double sx = std::min(static_cast<double>(ix[0]) - static_cast<double>(min[0]),
5225  static_cast<double>(max[0]) - static_cast<double>(ix[0]));
5226  double sy = std::min(static_cast<double>(ix[1]) - static_cast<double>(min[1]),
5227  static_cast<double>(max[1]) - static_cast<double>(ix[1]));
5228  double s = (1.0 + std::min(sx, sy)) / (r + 1.0);
5229  return s;
5230 }
5231 
5232 //----------------------------------------------------------------
5233 // normalize
5234 //
5235 // Tiled image normalization -- mean shift and rescale
5236 // pixel intensities to match a normal distribution.
5237 // Tiles overlap and the overlap regions are blended together.
5238 // Clip the pixels to the [-3, 3] range
5239 // and remap into the new [min, max] range.
5240 //
5241 template <typename T>
5242 typename T::Pointer
5243 normalize(const T * image,
5244  const unsigned int cols,
5245  const unsigned int rows,
5246  typename T::PixelType new_min =
5247  std::numeric_limits<typename T::PixelType>::max(),
5248  typename T::PixelType new_max =
5249  -std::numeric_limits<typename T::PixelType>::max(),
5250  const mask_t * mask = NULL)
5251 {
5252  WRAP(itk_terminator_t terminator("normalize"));
5253 
5254  if (new_min == std::numeric_limits<typename T::PixelType>::max() ||
5255  new_max == -std::numeric_limits<typename T::PixelType>::max())
5256  {
5257  // range of the image intensities:
5258  typename T::PixelType p_min;
5259  typename T::PixelType p_max;
5260  image_min_max<T>(image, p_min, p_max, mask);
5261 
5262  if (new_min == std::numeric_limits<typename T::PixelType>::max())
5263  {
5264  new_min = p_min;
5265  }
5266 
5267  if (new_max == -std::numeric_limits<typename T::PixelType>::max())
5268  {
5269  new_max = p_max;
5270  }
5271  }
5272 
5273  // split the image into a set of overlapping tiles:
5274  typename T::SizeType sz = image->GetLargestPossibleRegion().GetSize();
5275 
5276  double half_tw = static_cast<double>(sz[0]) / static_cast<double>(cols + 1);
5277  double half_th = static_cast<double>(sz[1]) / static_cast<double>(rows + 1);
5278 
5279  typename T::Pointer out = cast<T, T>(image);
5280 
5281  array2d(typename T::Pointer) tile;
5282  resize<typename T::Pointer>(tile, cols, rows);
5283 
5284  array2d(mask_t::Pointer) tile_mask;
5285  resize<mask_t::Pointer>(tile_mask, cols, rows);
5286 
5287  array2d(typename T::IndexType) tile_min;
5288  resize<typename T::IndexType>(tile_min, cols, rows);
5289 
5290  array2d(typename T::IndexType) tile_max;
5291  resize<typename T::IndexType>(tile_max, cols, rows);
5292 
5293  for (unsigned int i = 0; i < cols; i++)
5294  {
5295  double x0 = static_cast<double>(i) * half_tw;
5296  unsigned int ix0 = static_cast<unsigned int>(floor(x0));
5297  double x1 = static_cast<double>(ix0) + (x0 - static_cast<double>(ix0)) + 2.0 * half_tw;
5298  unsigned int ix1 = std::min(static_cast<unsigned int>(sz[0] - 1),
5299  static_cast<unsigned int>(ceil(x1)));
5300 
5301  for (unsigned int j = 0; j < rows; j++)
5302  {
5303  double y0 = static_cast<double>(j) * half_th;
5304  unsigned int iy0 = static_cast<unsigned int>(floor(y0));
5305  double y1 = static_cast<double>(iy0) + (y0 - static_cast<double>(iy0)) + 2.0 * half_th;
5306  unsigned int iy1 = std::min(static_cast<unsigned int>(sz[1] - 1),
5307  static_cast<unsigned int>(ceil(y1)));
5308 
5309  tile_min[i][j][0] = ix0;
5310  tile_min[i][j][1] = iy0;
5311  tile_max[i][j][0] = ix1;
5312  tile_max[i][j][1] = iy1;
5313 
5314  tile[i][j] = crop<T>(image, tile_min[i][j], tile_max[i][j]);
5315  tile_mask[i][j] = crop<mask_t>(mask, tile_min[i][j], tile_max[i][j]);
5316 
5317  typename T::Pointer v1 =
5318  normalize<T>(tile[i][j], tile_mask[i][j]);
5319 
5320  typename T::Pointer v2 =
5321  threshold<T>(v1, -3, 3, -3, 3);
5322 
5323  tile[i][j] = v2;
5324  }
5325  }
5326 
5327  // blend the tiles together:
5328  typedef itk::ImageRegionIteratorWithIndex<T> itex_t;
5329  itex_t itex(out, out->GetLargestPossibleRegion());
5330  for (itex.GoToBegin(); !itex.IsAtEnd(); ++itex)
5331  {
5332  // make sure there hasn't been an interrupt:
5333  WRAP(terminator.terminate_on_request());
5334 
5335  typename T::IndexType ix = itex.GetIndex();
5336 
5337  double wsum = 0.0;
5338  double mass = 0.0;
5339  for (unsigned int i = 0; i < cols; i++)
5340  {
5341  for (unsigned int j = 0; j < rows; j++)
5342  {
5343  if (ix[0] >= tile_min[i][j][0] &&
5344  ix[0] <= tile_max[i][j][0] &&
5345  ix[1] >= tile_min[i][j][1] &&
5346  ix[1] <= tile_max[i][j][1])
5347  {
5348  typename T::IndexType index;
5349  index[0] = ix[0] - tile_min[i][j][0];
5350  index[1] = ix[1] - tile_min[i][j][1];
5351 
5352  double weight = pixel_weight(tile_min[i][j], tile_max[i][j], ix);
5353  wsum += weight * tile[i][j]->GetPixel(index);
5354  mass += weight;
5355  }
5356  }
5357  }
5358 
5359  if (mass != 0.0) wsum /= mass;
5360  out->SetPixel(ix, wsum);
5361  }
5362 
5363  remap_min_max_inplace<T>(out, new_min, new_max);
5364  return out;
5365 }
5366 
5367 
5368 //----------------------------------------------------------------
5369 // forward_transform
5370 //
5371 // Cascaded forward transform. This function assumes
5372 // that it is given a vector of forward transforms.
5373 //
5374 template <typename T, class transform_vec_t>
5375 typename T::PointType
5376 forward_transform(const transform_vec_t & t,
5377  const unsigned int t_size,
5378  const typename T::PointType & in)
5379 {
5380  typename T::PointType out = in;
5381  for (unsigned int i = 0; i < t_size; i++)
5382  {
5383  out = t[i]->TransformPoint(out);
5384  }
5385  return out;
5386 }
5387 
5388 //----------------------------------------------------------------
5389 // inverse_transform
5390 //
5391 // Cascaded inverse transform. This function assumes
5392 // that it is given a vector of inverse transforms t_inverse,
5393 // it will not call GetInverseTransform on any of them.
5394 //
5395 template <typename T, class transform_vec_t>
5396 typename T::PointType
5397 inverse_transform(const transform_vec_t & t_inverse,
5398  const unsigned int t_size,
5399  const typename T::PointType & in)
5400 {
5401  typename T::PointType out = in;
5402  for (int i = t_size - 1; i >= 0; i--)
5403  {
5404  out = t_inverse[i]->TransformPoint(out);
5405  }
5406  return out;
5407 }
5408 
5409 
5410 //----------------------------------------------------------------
5411 // flip
5412 //
5413 // Flip an image around vertical and/or horizontal axis.
5414 //
5415 // flip_x -- flip around the vertical axis
5416 // flip_y -- flip around the horizontal axis.
5417 //
5418 template <typename T>
5419 typename T::Pointer
5420 flip(const T * in,
5421  const bool flip_x = true,
5422  const bool flip_y = false)
5423 {
5424  WRAP(itk_terminator_t terminator("flip"));
5425 
5426  // local typedefs:
5427  typedef typename T::RegionType rn_t;
5428  typedef typename T::IndexType ix_t;
5429  typedef typename T::SizeType sz_t;
5430 
5431  typename T::Pointer out = cast<T, T>(in);
5432 
5433  if (flip_x || flip_y)
5434  {
5435  rn_t rn = in->GetLargestPossibleRegion();
5436  sz_t sz = rn.GetSize();
5437 
5438  typedef itk::ImageRegionIteratorWithIndex<T> itex_t;
5439  itex_t itex(out, rn);
5440 
5441  for (itex.GoToBegin(); !itex.IsAtEnd(); ++itex)
5442  {
5443  // make sure there hasn't been an interrupt:
5444  WRAP(terminator.terminate_on_request());
5445 
5446  ix_t ix = itex.GetIndex();
5447  if (flip_x) ix[0] = sz[0] - ix[0] - 1;
5448  if (flip_y) ix[1] = sz[1] - ix[1] - 1;
5449 
5450  itex.Set(in->GetPixel(ix));
5451  }
5452  }
5453 
5454  return out;
5455 }
5456 
5457 
5458 //----------------------------------------------------------------
5459 // overlap_ratio
5460 //
5461 // Calculate the ratio of the overlap region area to the area
5462 // of the smaller image.
5463 //
5464 template <typename T>
5465 double
5466 overlap_ratio(const T * fi,
5467  const T * mi,
5468  const base_transform_t * fi_to_mi)
5469 {
5470  if (fi_to_mi == NULL) return 0.0;
5471 
5472  typename T::PointType fi_min = fi->GetOrigin();
5473  typename T::PointType mi_min = mi->GetOrigin();
5474 
5475  typename T::SpacingType fi_sp = fi->GetSpacing();
5476  typename T::SpacingType mi_sp = mi->GetSpacing();
5477 
5478  typename T::SizeType fi_sz = fi->GetLargestPossibleRegion().GetSize();
5479  typename T::SizeType mi_sz = mi->GetLargestPossibleRegion().GetSize();
5480 
5481  typename T::PointType fi_max =
5482  fi_min + vec2d((fi_sz[0]) * fi_sp[0],
5483  (fi_sz[1]) * fi_sp[1]);
5484 
5485  typename T::PointType mi_max =
5486  mi_min + vec2d((mi_sz[0]) * mi_sp[0],
5487  (mi_sz[1]) * mi_sp[1]);
5488 
5489  const double w0 = fi_max[0] - fi_min[0];
5490  const double h0 = fi_max[1] - fi_min[1];
5491 
5492  const double w1 = mi_max[0] - mi_min[0];
5493  const double h1 = mi_max[1] - mi_min[1];
5494 
5495  const double smaller_area = std::min(w0, w1) * std::min(h0, h1);
5496 
5497  typename T::PointType ul = fi_to_mi->TransformPoint(fi_min);
5498  ul[0] = std::max(0.0, std::min(w1, ul[0] - mi_min[0]));
5499  ul[1] = std::max(0.0, std::min(h1, ul[1] - mi_min[1]));
5500 
5501  typename T::PointType lr = fi_to_mi->TransformPoint(fi_max);
5502  lr[0] = std::max(0.0, std::min(w1, lr[0] - mi_min[0]));
5503  lr[1] = std::max(0.0, std::min(h1, lr[1] - mi_min[1]));
5504 
5505  const double dx = lr[0] - ul[0];
5506  const double dy = lr[1] - ul[1];
5507  const double area_ratio = (dx * dy) / smaller_area;
5508 
5509  return area_ratio;
5510 }
5511 
5512 //----------------------------------------------------------------
5513 // find_inverse
5514 //
5515 // Given a forward transform and image space coordinates,
5516 // find mosaic space coordinates.
5517 //
5518 extern bool
5519 find_inverse(const pnt2d_t & tile_min, // tile space
5520  const pnt2d_t & tile_max, // tile space
5521  const base_transform_t * mosaic_to_tile, // forward transform
5522  const pnt2d_t & xy, // tile space
5523  pnt2d_t & uv, // mosaic space
5524  const unsigned int max_iterations = 100,
5525  const double min_step_scale = 1e-12,
5526  const double min_error_sqrd = 1e-16,
5527  const unsigned int pick_up_pace_steps = 5);
5528 
5529 //----------------------------------------------------------------
5530 // find_inverse
5531 //
5532 // Given a forward transform, an approximate inverse transform,
5533 // and image space coordinates, find mosaic space coordinates.
5534 //
5535 extern bool
5536 find_inverse(const pnt2d_t & tile_min, // tile space
5537  const pnt2d_t & tile_max, // tile space
5538  const base_transform_t * mosaic_to_tile, // forward transform
5539  const base_transform_t * tile_to_mosaic, // inverse transform
5540  const pnt2d_t & xy, // tile space
5541  pnt2d_t & uv, // mosaic space
5542  const unsigned int max_iterations = 100,
5543  const double min_step_scale = 1e-12,
5544  const double min_error_sqrd = 1e-16,
5545  const unsigned int pick_up_pace_steps = 5);
5546 
5547 //----------------------------------------------------------------
5548 // find_inverse
5549 //
5550 // Given a forward transform, an approximate inverse transform,
5551 // and image space coordinates, find mosaic space coordinates.
5552 //
5553 extern bool
5554 find_inverse(const base_transform_t * mosaic_to_tile, // forward transform
5555  const base_transform_t * tile_to_mosaic, // inverse transform
5556  const pnt2d_t & xy, // tile space
5557  pnt2d_t & uv, // mosaic space
5558  const unsigned int max_iterations = 100,
5559  const double min_step_scale = 1e-12,
5560  const double min_error_sqrd = 1e-16,
5561  const unsigned int pick_up_pace_steps = 5);
5562 
5563 //----------------------------------------------------------------
5564 // generate_landmarks
5565 //
5566 // Given a forward transform, generate a set of image space
5567 // coordinates and find their matching mosaic space coordinates.
5568 //
5569 // version 1 -- uniform jittered sampling over the tile
5570 // version 2 -- non-uniform sampling skewed towards the center
5571 // of the tile radially. This may be useful when
5572 // the transform is ill-behaved at the tile edges.
5573 //
5574 extern bool
5575 generate_landmarks(const pnt2d_t & tile_min,
5576  const pnt2d_t & tile_max,
5577  const mask_t * tile_mask,
5578  const base_transform_t * mosaic_to_tile,
5579  const unsigned int samples,
5580  std::vector<pnt2d_t> & xy,
5581  std::vector<pnt2d_t> & uv,
5582  const unsigned int version,
5583  const bool refine);
5584 
5585 //----------------------------------------------------------------
5586 // generate_landmarks
5587 //
5588 // Given a forward transform, generate a set of image space
5589 // coordinates and find their matching mosaic space coordinates.
5590 //
5591 // version 1 -- uniform jittered sampling over the tile
5592 // version 2 -- non-uniform sampling skewed towards the center
5593 // of the tile radially. This may be useful when
5594 // the transform is ill-behaved at the tile edges.
5595 //
5596 extern bool
5597 generate_landmarks(const image_t * tile,
5598  const mask_t * mask,
5599  const base_transform_t * mosaic_to_tile,
5600  const unsigned int samples,
5601  std::vector<pnt2d_t> & xy,
5602  std::vector<pnt2d_t> & uv,
5603  const unsigned int version = 1,
5604  const bool refine = false);
5605 
5606 //----------------------------------------------------------------
5607 // approx_transform
5608 //
5609 // Given a forward Legendre polynomial transform, solve for
5610 // transform parameters of the inverse Legendre polynomial transform.
5611 //
5612 // version 1 -- uniform jittered sampling over the tile
5613 // version 2 -- non-uniform sampling skewed towards the center
5614 // of the tile radially. This may be useful when
5615 // the transform is ill-behaved at the tile edges.
5616 //
5617 template <class legendre_transform_t>
5618 typename legendre_transform_t::Pointer
5619 approx_transform(const pnt2d_t & tile_min,
5620  const pnt2d_t & tile_max,
5621  const mask_t * tile_mask,
5622  const base_transform_t * forward,
5623  const unsigned int samples = 16,
5624  const unsigned int version = 1,
5625  const bool refine = false)
5626 {
5627  base_transform_t::Pointer inverse = forward->GetInverseTransform();
5628 // assert(inverse.GetPointer() != NULL);
5629  if (inverse.GetPointer() == NULL)
5630  {
5631  CORE_THROW_EXCEPTION("Null inverse transform");
5632  }
5633 
5634  // calculate the shift:
5635  pnt2d_t center = pnt2d(tile_min[0] + (tile_max[0] - tile_min[0]) / 2.0,
5636  tile_min[1] + (tile_max[1] - tile_min[1]) / 2.0);
5637  vec2d_t shift = center - inverse->TransformPoint(center);
5638 
5639  typename legendre_transform_t::Pointer approx =
5640  setup_transform<legendre_transform_t>(tile_min, tile_max);
5641  approx->setup_translation(shift[0], shift[1]);
5642 
5643  std::vector<pnt2d_t> xy;
5644  std::vector<pnt2d_t> uv;
5645  generate_landmarks(tile_min,
5646  tile_max,
5647  tile_mask,
5648  forward,
5649  samples,
5650  xy,
5651  uv,
5652  version,
5653  refine);
5654 
5655  unsigned int order = legendre_transform_t::Degree + 1;
5656  unsigned int loworder = std::min(2u, order);
5657 
5658 //#if 1
5659  approx->solve_for_parameters(0, loworder, uv, xy);
5660  approx->solve_for_parameters(loworder, order - loworder, uv, xy);
5661 //#else
5662 // approx->solve_for_parameters(0, order, uv, xy);
5663 //#endif
5664 
5665  return approx;
5666 }
5667 
5668 //----------------------------------------------------------------
5669 // solve_for_transform
5670 //
5671 // Given a forward transform and a gross translation vector for
5672 // the inverse mapping, solve for transform parameters of the
5673 // inverse Legendre polynomial transform.
5674 //
5675 // version 1 -- uniform jittered sampling over the tile
5676 // version 2 -- non-uniform sampling skewed towards the center
5677 // of the tile radially. This may be useful when
5678 // the transform is ill-behaved at the tile edges.
5679 //
5680 template <class legendre_transform_t>
5681 void
5682 solve_for_transform(const pnt2d_t & tile_min,
5683  const pnt2d_t & tile_max,
5684  const mask_t * tile_mask,
5685  const base_transform_t * mosaic_to_tile_0,
5686  typename legendre_transform_t::Pointer & mosaic_to_tile_1,
5687  const vec2d_t & shift,
5688  const unsigned int samples = 16,
5689  const unsigned int version = 1,
5690  const bool refine = false)
5691 {
5692  mosaic_to_tile_1->setup_translation(shift[0], shift[1]);
5693 
5694 //#if 0
5695 // // FIXME:
5696 // cerr << "SHIFT: " << shift << endl
5697 // << "ROUGH: " << mosaic_to_tile_1->GetParameters() << endl;
5698 //#endif
5699 
5700  std::vector<pnt2d_t> xy;
5701  std::vector<pnt2d_t> uv;
5702  generate_landmarks(tile_min,
5703  tile_max,
5704  tile_mask,
5705  mosaic_to_tile_0,
5706  samples,
5707  xy,
5708  uv,
5709  version,
5710  refine);
5711 
5712  unsigned int order = legendre_transform_t::Degree + 1;
5713  unsigned int loworder = std::min(2u, order);
5714 
5715 //#if 1
5716  mosaic_to_tile_1->solve_for_parameters(0, loworder, uv, xy);
5717  mosaic_to_tile_1->solve_for_parameters(loworder, order - loworder, uv, xy);
5718 //#else
5719 // mosaic_to_tile_1->solve_for_parameters(0, order, uv, xy);
5720 //#endif
5721 
5722 //#if 0
5723 // // FIXME:
5724 // cerr << "FINAL: " << mosaic_to_tile_1->GetParameters() << endl;
5725 //#endif
5726 }
5727 
5728 //----------------------------------------------------------------
5729 // solve_for_transform
5730 //
5731 // Given a forward transform and a gross translation vector for
5732 // the inverse mapping, solve for transform parameters of the
5733 // inverse Legendre polynomial transform.
5734 //
5735 // version 1 -- uniform jittered sampling over the tile
5736 // version 2 -- non-uniform sampling skewed towards the center
5737 // of the tile radially. This may be useful when
5738 // the transform is ill-behaved at the tile edges.
5739 //
5740 template <typename T, class legendre_transform_t>
5741 void
5742 solve_for_transform(const T * tile,
5743  const mask_t * mask,
5744  const base_transform_t * mosaic_to_tile_0,
5745  typename legendre_transform_t::Pointer & mosaic_to_tile_1,
5746  const vec2d_t & shift,
5747  const unsigned int samples = 16,
5748  const unsigned int version = 1,
5749  const bool refine = false)
5750 {
5751  typename T::SpacingType sp = tile->GetSpacing();
5752  typename T::SizeType sz = tile->GetLargestPossibleRegion().GetSize();
5753  const pnt2d_t min = tile->GetOrigin();
5754  const pnt2d_t max = min + vec2d(sp[0] * static_cast<double>(sz[0]),
5755  sp[1] * static_cast<double>(sz[1]));
5756 
5757  solve_for_transform<legendre_transform_t>(min,
5758  max,
5759  mask,
5760  mosaic_to_tile_0,
5761  mosaic_to_tile_1,
5762  shift,
5763  samples,
5764  version,
5765  refine);
5766 }
5767 
5768 //----------------------------------------------------------------
5769 // solve_for_transform
5770 //
5771 // Given a forward transform, solve for transform parameters of
5772 // the inverse Legendre polynomial transform.
5773 //
5774 // version 1 -- uniform jittered sampling over the tile
5775 // version 2 -- non-uniform sampling skewed towards the center
5776 // of the tile radially. This may be useful when
5777 // the transform is ill-behaved at the tile edges.
5778 //
5779 template <class legendre_transform_t>
5780 void
5781 solve_for_transform(const pnt2d_t & tile_min,
5782  const pnt2d_t & tile_max,
5783  const mask_t * tile_mask,
5784  const base_transform_t * mosaic_to_tile_0,
5785  typename legendre_transform_t::Pointer & mosaic_to_tile_1,
5786  const unsigned int samples = 16,
5787  const unsigned int version = 1,
5788  const bool refine = false)
5789 {
5790  base_transform_t::Pointer tile_to_mosaic = mosaic_to_tile_0->GetInverseTransform();
5791 // assert(tile_to_mosaic.GetPointer() != NULL);
5792  if (tile_to_mosaic.GetPointer() == NULL)
5793  {
5794  CORE_THROW_EXCEPTION("Null inverse transform");
5795  }
5796 
5797  // calculate the shift:
5798  pnt2d_t center = pnt2d(tile_min[0] + (tile_max[0] - tile_min[0]) / 2.0,
5799  tile_min[1] + (tile_max[1] - tile_min[1]) / 2.0);
5800  vec2d_t shift = center - tile_to_mosaic->TransformPoint(center);
5801 
5802  solve_for_transform<legendre_transform_t>(tile_min,
5803  tile_max,
5804  tile_mask,
5805  mosaic_to_tile_0,
5806  mosaic_to_tile_1,
5807  shift,
5808  samples,
5809  version,
5810  refine);
5811 }
5812 
5813 //----------------------------------------------------------------
5814 // solve_for_transform
5815 //
5816 // Given a forward transform, solve for transform parameters of
5817 // the inverse Legendre polynomial transform.
5818 //
5819 // version 1 -- uniform jittered sampling over the tile
5820 // version 2 -- non-uniform sampling skewed towards the center
5821 // of the tile radially. This may be useful when
5822 // the transform is ill-behaved at the tile edges.
5823 //
5824 template <typename T, class legendre_transform_t>
5825 void
5826 solve_for_transform(const T * tile,
5827  const mask_t * mask,
5828  const base_transform_t * mosaic_to_tile_0,
5829  typename legendre_transform_t::Pointer & mosaic_to_tile_1,
5830  const unsigned int samples = 16,
5831  const unsigned int version = 1,
5832  const bool refine = false)
5833 {
5834  base_transform_t::Pointer tile_to_mosaic = mosaic_to_tile_0->GetInverseTransform();
5835 // assert(tile_to_mosaic.GetPointer() != NULL);
5836  if (tile_to_mosaic.GetPointer() == NULL)
5837  {
5838  CORE_THROW_EXCEPTION("Null inverse transform");
5839  }
5840 
5841  typename T::SizeType sz = tile->GetLargestPossibleRegion().GetSize();
5842  typename T::SpacingType sp = tile->GetSpacing();
5843  pnt2d_t tile_min = tile->GetOrigin();
5844  pnt2d_t tile_max;
5845  tile_max[0] = tile_min[0] + sp[0] * static_cast<double>(sz[0]);
5846  tile_max[1] = tile_min[1] + sp[1] * static_cast<double>(sz[1]);
5847 
5848  return solve_for_transform<legendre_transform_t>(tile_min,
5849  tile_max,
5850  mask,
5851  mosaic_to_tile_0,
5852  mosaic_to_tile_1,
5853  samples,
5854  version,
5855  refine);
5856 }
5857 
5858 
5859 //----------------------------------------------------------------
5860 // std_mask
5861 //
5862 // Given image dimensions, generate a mask image.
5863 // use_std_mask -- the standard mask for the Robert Marc Lab data.
5864 //
5865 extern mask_t::Pointer
5866 std_mask(const itk::Point<double, 2> & origin,
5867  const itk::Vector<double, 2> & spacing,
5868  const itk::Size<2> & sz,
5869  bool use_std_mask = true);
5870 
5871 //----------------------------------------------------------------
5872 // std_mask
5873 //
5874 // Given an image, generate a matching mask image.
5875 // use_std_mask -- the standard mask for the Robert Marc Lab data.
5876 //
5877 template <typename T>
5878 mask_t::Pointer
5879 std_mask(const T * tile, bool use_std_mask = true)
5880 {
5881  return std_mask(tile->GetOrigin(),
5882  tile->GetSpacing(),
5883  tile->GetLargestPossibleRegion().GetSize(),
5884  use_std_mask);
5885 }
5886 
5887 //----------------------------------------------------------------
5888 // std_tile
5889 //
5890 // Load a mosaic tile image, reset the origin to zero, set pixel
5891 // spacing as specified. Downscale the image according to the
5892 // shrink factor. If clahe_slope is greater than 1 then process
5893 // the image with the CLAHE algorithm.
5894 //
5895 template <typename T>
5896 typename T::Pointer
5897 std_tile(const bfs::path& fn_image,
5898  const unsigned int & shrink_factor,
5899  const double & pixel_spacing,
5900  const double & clahe_slope,
5901  const unsigned int & clahe_window,
5902  const bool & blab)
5903 {
5904  typename T::Pointer image = load<T>(fn_image, blab);
5905 
5906  // reset the tile image origin and spacing:
5907  typename T::PointType origin = image->GetOrigin();
5908  origin[0] = 0;
5909  origin[1] = 0;
5910  image->SetOrigin(origin);
5911 
5912  typename T::SpacingType spacing = image->GetSpacing();
5913  spacing[0] = 1;
5914  spacing[1] = 1;
5915  image->SetSpacing(spacing);
5916 
5917  // don't blur the images unnecessarily:
5918  if (shrink_factor > 1)
5919  {
5920  image = shrink<T>(image, shrink_factor);
5921  }
5922 
5923  typename T::SpacingType sp = image->GetSpacing();
5924 
5925  sp[0] *= pixel_spacing;
5926  sp[1] *= pixel_spacing;
5927  image->SetSpacing(sp);
5928 
5929  if (clahe_slope > 1.0)
5930  {
5931  image = CLAHE<T>(image,
5932  clahe_window,
5933  clahe_window,
5934  clahe_slope,
5935  256, // histogram bins
5936  0, // new min
5937  1); // new max
5938  }
5939 
5940  return image;
5941 }
5942 
5943 //----------------------------------------------------------------
5944 // std_tile
5945 //
5946 // Load a mosaic tile image, reset the origin to zero, set pixel
5947 // spacing as specified. Downscale the image according to the
5948 // shrink factor.
5949 //
5950 template <typename T>
5951 typename T::Pointer
5952 std_tile(const bfs::path& fn_image,
5953  const unsigned int & shrink_factor,
5954  const double & pixel_spacing,
5955  bool blab)
5956 {
5957  return std_tile<T>(fn_image,
5958  shrink_factor,
5959  pixel_spacing,
5960  1.0, // clahe slope
5961  0, // clahe window size
5962  blab);
5963 }
5964 
5965 //----------------------------------------------------------------
5966 // save_volume_slice
5967 //
5968 // Save a volume slice transform. This is used by ir-stom-approx
5969 // to generate the .stom files.
5970 //
5971 template <class transform_t>
5972 void
5973 save_volume_slice(std::ostream & so,
5974  const unsigned int & index,
5975  const bfs::path & image,
5976  const double & sp_x,
5977  const double & sp_y,
5978  const transform_t * transform,
5979  const pnt2d_t & overall_min,
5980  const pnt2d_t & overall_max,
5981  const pnt2d_t & slice_min,
5982  const pnt2d_t & slice_max,
5983  const bool & flip)
5984 {
5985  std::ios::fmtflags old_flags = so.setf(std::ios::scientific);
5986  int old_precision = so.precision();
5987  so.precision(12);
5988 
5989  so << "index: " << index << std::endl
5990  << "overall_min: " << overall_min[0] << ' ' << overall_min[1] << std::endl
5991  << "overall_max: " << overall_max[0] << ' ' << overall_max[1] << std::endl
5992  << "slice_min: " << slice_min[0] << ' ' << slice_min[1] << std::endl
5993  << "slice_max: " << slice_max[0] << ' ' << slice_max[1] << std::endl
5994  << "flip: " << flip << std::endl
5995  << "sp: " << sp_x << ' ' << sp_y << std::endl
5996  << "image:" << std::endl
5997  << image << std::endl;
5998  save_transform(so, transform);
5999  so << std::endl;
6000 
6001  so.setf(old_flags);
6002  so.precision(old_precision);
6003 }
6004 
6005 //----------------------------------------------------------------
6006 // load_volume_slice
6007 //
6008 // Load a volume slice transform. This is used by ir-slice
6009 // to assemble a volume slice image from a given .stom file.
6010 //
6011 template <class transform_t>
6012 void
6013 load_volume_slice(std::istream & si,
6014  unsigned int & index,
6015  bfs::path & image,
6016  double & sp_x,
6017  double & sp_y,
6018  typename transform_t::Pointer & transform,
6019  pnt2d_t & overall_min,
6020  pnt2d_t & overall_max,
6021  pnt2d_t & slice_min,
6022  pnt2d_t & slice_max,
6023  bool & flip)
6024 {
6025  while (true)
6026  {
6027  std::string token;
6028  si >> token;
6029  if (si.eof() && token.size() == 0) break;
6030 
6031  if (token == "index:")
6032  {
6033  si >> index;
6034  }
6035  else if (token == "overall_min:")
6036  {
6037  si >> overall_min[0] >> overall_min[1];
6038  }
6039  else if (token == "overall_max:")
6040  {
6041  si >> overall_max[0] >> overall_max[1];
6042  }
6043  else if (token == "slice_min:")
6044  {
6045  si >> slice_min[0] >> slice_min[1];
6046  }
6047  else if (token == "slice_max:")
6048  {
6049  si >> slice_max[0] >> slice_max[1];
6050  }
6051  else if (token == "flip:")
6052  {
6053  si >> flip;
6054  }
6055  else if (token == "sp:")
6056  {
6057  si >> sp_x >> sp_y;
6058  }
6059  else if (token == "image:")
6060  {
6061  image.clear();
6062  while (image.empty())
6063  {
6064  std::string line;
6065  std::getline(si, line);
6066  image = line;
6067  if (! bfs::exists(image))
6068  {
6069  std::ostringstream oss;
6070  oss << "load_volume_slice cannot find " << image.string();
6071  CORE_LOG_WARNING(oss.str());
6072  }
6073  }
6074 
6075  itk::TransformBase::Pointer t_base = load_transform(si);
6076  transform = dynamic_cast<transform_t *>(t_base.GetPointer());
6077 // assert(transform.GetPointer() != NULL);
6078  if (transform.GetPointer() == NULL)
6079  {
6080  CORE_THROW_EXCEPTION("Null transform");
6081  }
6082  }
6083  else
6084  {
6085  std::ostringstream oss;
6086  oss << "WARNING: unknown token: '" << token << "', ignoring ...";
6087  CORE_LOG_WARNING(oss.str());
6088 
6089  }
6090  }
6091 }
6092 
6093 //----------------------------------------------------------------
6094 // calc_size
6095 //
6096 // Given a bounding box expressed in the image space and
6097 // pixel spacing, calculate corresponding image size.
6098 //
6099 extern image_t::SizeType
6100 calc_size(const pnt2d_t & min,
6101  const pnt2d_t & max,
6102  const double & spacing);
6103 
6104 //----------------------------------------------------------------
6105 // track_progress
6106 //
6107 // Sets the current progress for determining the overall percentage
6108 //
6109 inline static void
6110 track_progress(bool track)
6111 {
6112  //if ( track )
6113  // std::cout << "Tracking progress..." << std::endl;
6114  //else
6115  // std::cout << "Progress tracking disabled..." << std::endl;
6116 }
6117 
6118 //----------------------------------------------------------------
6119 // set_major_progress
6120 //
6121 // Sets the current progress for determining the overall percentage
6122 //
6123 inline static void
6124 set_major_progress(double major_percent)
6125 {
6126  // TODO: Draw out a nifty bar instead. This would be easier to read, and
6127  // better understood.
6128  //std::cout << "Tool Percentage: " << major_percent << std::endl;
6129 }
6130 
6131 //----------------------------------------------------------------
6132 // set_minor_progress
6133 //
6134 // Sets the current progress for determining the percentage of this
6135 // particular task.
6136 //
6137 inline static void
6138 set_minor_progress(double minor_percent, double major_percent)
6139 {
6140  // TODO: Draw out a nifty bar instead. This would be easier to read, and
6141  // better understood.
6142  //std::cout << "Task Percentage: " << minor_percent << " until " << major_percent << std::endl;
6143 }
6144 
6145 #endif // COMMON_HXX_
Definition: common.hxx:2696
Normalize an image by setting its mean to zero and variance to one.
Definition: itkNormalizeImageFilterWithMask.h:63
Definition: common.hxx:3512
Definition: the_transaction.hxx:55
Definition: the_thread_pool.hxx:105
Definition: common.hxx:619
Definition: the_thread_interface.hxx:57