Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
itkDiffusionKurtosisReconstructionImageFilter.cxx
Go to the documentation of this file.
1 /*===================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics.
7 All rights reserved.
8 
9 This software is distributed WITHOUT ANY WARRANTY; without
10 even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 A PARTICULAR PURPOSE.
12 
13 See LICENSE.txt or http://www.mitk.org for details.
14 
15 ===================================================================*/
16 
17 
18 #ifndef DIFFUSIONKURTOSISRECONSTRUCTIONIMAGEFILTER_CXX
19 #define DIFFUSIONKURTOSISRECONSTRUCTIONIMAGEFILTER_CXX
20 
22 
23 #include <itkImageRegionConstIterator.h>
24 #include <itkImageRegionConstIteratorWithIndex.h>
25 #include <itkImageRegionIteratorWithIndex.h>
26 #include <itkImageRegionIterator.h>
27 
28 #include <itkVectorIndexSelectionCastImageFilter.h>
29 #include <itkComposeImageFilter.h>
30 #include <itkDiscreteGaussianImageFilter.h>
31 
32 template< class TInputPixelType>
34  const vnl_vector<double>& bvalues,
35  vnl_vector<double>& result,
37 {
38  // check for length
39  assert( input.Size() == bvalues.size() );
40 
41  // assembly data vectors for fitting
42  auto bvalueIter = bvalues.begin();
43  unsigned int unused_values = 0;
44  while( bvalueIter != bvalues.end() )
45  {
46  if( *bvalueIter < vnl_math::eps && kf_config.omit_bzero )
47  {
48  ++unused_values;
49  }
50  ++bvalueIter;
51  }
52 
53  // initialize data vectors with the estimated size (after filtering)
54  vnl_vector<double> fit_measurements( input.Size() - unused_values, 0 );
55  vnl_vector<double> fit_bvalues( input.Size() - unused_values, 0 );
56 
57  bvalueIter = bvalues.begin();
58  unsigned int running_index = 0;
59  unsigned int skip_count = 0;
60  while( bvalueIter != bvalues.end() )
61  {
62  // skip b=0 if the corresponding flag is set
63  if( ( kf_config.omit_bzero && *bvalueIter < vnl_math::eps )
64  // skip bvalues higher than the limit provided, if the flag is activated
65  || ( kf_config.exclude_high_b && *bvalueIter > kf_config.b_upper_threshold ) )
66  {
67  ++skip_count;
68  }
69  else
70  {
71  fit_measurements[ running_index - skip_count ] = input.GetElement(running_index);
72  fit_bvalues[ running_index - skip_count] = *bvalueIter;
73  }
74 
75  ++running_index;
76  ++bvalueIter;
77  }
78 
79 
80  MITK_DEBUG("KurtosisFilter.FitSingleVoxel.Meas") << fit_measurements;
81  MITK_DEBUG("KurtosisFilter.FitSingleVoxel.Bval") << fit_bvalues;
82 
83  // perform fit on data vectors
84  if( kf_config.omit_bzero )
85  {
86  itk::kurtosis_fit_omit_unweighted kurtosis_cost_fn( fit_measurements.size() );
87  // configuration
88  kurtosis_cost_fn.set_fit_logscale( static_cast<bool>(kf_config.fit_scale) );
89  kurtosis_cost_fn.initialize( fit_measurements, fit_bvalues );
90 
91  if( kf_config.use_K_limits)
92  {
93  kurtosis_cost_fn.set_K_bounds( kf_config.K_limits );
94  }
95 
96  vnl_levenberg_marquardt nonlinear_fit( kurtosis_cost_fn );
97  nonlinear_fit.minimize( result );
98  }
99  else
100  {
101  itk::kurtosis_fit_lsq_function kurtosis_cost_fn( fit_measurements.size() );
102  // configuration
103  kurtosis_cost_fn.set_fit_logscale( static_cast<bool>(kf_config.fit_scale) );
104  kurtosis_cost_fn.initialize( fit_measurements, fit_bvalues );
105 
106  if( kf_config.use_K_limits)
107  {
108  kurtosis_cost_fn.set_K_bounds( kf_config.K_limits );
109  }
110 
111 
112  vnl_levenberg_marquardt nonlinear_fit( kurtosis_cost_fn );
113  nonlinear_fit.minimize(result);
114  }
115 
116  MITK_DEBUG("KurtosisFilter.FitSingleVoxel.Rslt") << result;
117 }
118 
119 
120 
121 template< class TInputPixelType, class TOutputPixelType>
124  : m_ReferenceBValue(-1),
125  m_OmitBZero(false),
126  m_MaskImage(nullptr),
127  m_ApplyPriorSmoothing(false),
128  m_SmoothingSigma(1.5),
129  m_MaxFitBValue( 3000 ),
130  m_UseKBounds( false ),
131  m_ScaleForFitting( STRAIGHT )
132 {
133  this->m_InitialPosition = vnl_vector<double>(3, 0);
134  this->m_InitialPosition[2] = 1000.0; // S_0
135  this->m_InitialPosition[0] = 0.001; // D
136  this->m_InitialPosition[1] = 1; // K
137 
138  this->m_KurtosisBounds.fill(0);
139 
140  // single input image
141  this->SetNumberOfRequiredInputs(1);
142 
143  // two output images (D, K)
144  this->SetNumberOfRequiredOutputs(2);
145  typename OutputImageType::Pointer outputPtr1 = OutputImageType::New();
146  typename OutputImageType::Pointer outputPtr2 = OutputImageType::New();
147 
148  this->SetNthOutput(0, outputPtr1.GetPointer() );
149  this->SetNthOutput(1, outputPtr2.GetPointer() );
150 
151 }
152 
153 template< class TInputPixelType, class TOutputPixelType>
156 {
157  // first call superclass
158  Superclass::GenerateOutputInformation();
159 }
160 
161 template< class TInputPixelType, class TOutputPixelType>
164 {
165  this->m_MaskImage = mask;
166 }
167 
168 template< class TInputPixelType, class TOutputPixelType>
171 {
172  // if we have a region set, convert it to a mask image, which is to be used as default for telling
173  // we need to set the image anyway, so by default the mask is overall 1
174  if( m_MaskImage.IsNull() )
175  {
176  m_MaskImage = MaskImageType::New();
177  m_MaskImage->SetRegions( this->GetInput()->GetLargestPossibleRegion() );
178  m_MaskImage->CopyInformation( this->GetInput() );
179  m_MaskImage->Allocate();
180 
181  if( this->m_MapOutputRegion.GetNumberOfPixels() > 0 )
182  {
183 
184  m_MaskImage->FillBuffer(0);
185 
186  typedef itk::ImageRegionIteratorWithIndex< MaskImageType > MaskIteratorType;
187  MaskIteratorType maskIter( this->m_MaskImage, this->m_MapOutputRegion );
188  maskIter.GoToBegin();
189 
190  while( !maskIter.IsAtEnd() )
191  {
192  maskIter.Set( 1 );
193  ++maskIter;
194  }
195 
196  }
197  else
198  {
199  m_MaskImage->FillBuffer(1);
200  }
201 
202 
203  }
204 
205 
206  // apply smoothing to the input image
207  if( this->m_ApplyPriorSmoothing )
208  {
209  // filter typedefs
210  typedef itk::DiscreteGaussianImageFilter< itk::Image<TInputPixelType, 3 >, itk::Image<TInputPixelType, 3 > > GaussianFilterType;
211  typedef itk::VectorIndexSelectionCastImageFilter< InputImageType, itk::Image<TInputPixelType, 3 > > IndexSelectionType;
212  typedef itk::ComposeImageFilter< itk::Image<TInputPixelType, 3>, InputImageType > ComposeFilterType;
213 
214 
215  auto vectorImage = this->GetInput();
216  typename IndexSelectionType::Pointer indexSelectionFilter = IndexSelectionType::New();
217  indexSelectionFilter->SetInput( vectorImage );
218 
219  typename ComposeFilterType::Pointer vec_composer = ComposeFilterType::New();
220 
221  for( unsigned int i=0; i<vectorImage->GetVectorLength(); ++i)
222  {
223  typename GaussianFilterType::Pointer gaussian_filter = GaussianFilterType::New();
224 
225  indexSelectionFilter->SetIndex( i );
226 
227  gaussian_filter->SetInput( indexSelectionFilter->GetOutput() );
228  gaussian_filter->SetVariance( m_SmoothingSigma );
229 
230  vec_composer->SetInput(i, gaussian_filter->GetOutput() );
231 
232  gaussian_filter->Update();
233  }
234 
235  try
236  {
237  vec_composer->Update();
238  }
239  catch(const itk::ExceptionObject &e)
240  {
241  mitkThrow() << "[VectorImage.GaussianSmoothing] !! Failed with ITK Exception: " << e.what();
242  }
243 
244  this->m_ProcessedInputImage = vec_composer->GetOutput();
245  }
246  else
247  {
248  this->m_ProcessedInputImage = const_cast<InputImageType*>( this->GetInput() );
249  }
250 }
251 
252 template< class TInputPixelType, class TOutputPixelType>
255 {
256  /* // initialize buffer to zero overall, but don't forget the requested region pointer
257  for( unsigned int i=0; i<this->GetNumberOfOutputs(); ++i)
258  {
259  typename OutputImageType::Pointer output = this->GetOutput(i);
260 
261  // after generating, set the buffered region to max, we have taken care about filling it with valid data in
262  // UpdateOutputInformation, if not set, a writer (or any filter using the LargestPossibleRegion() during update may
263  // complain about not getting the requested region
264  output->SetBufferedRegion( output->GetLargestPossibleRegion() );
265 
266  std::cout << "[DiffusionKurtosisReconstructionImageFilter.After]" << output->GetLargestPossibleRegion() << std::endl;
267  }*/
268 
269 }
270 
271 template< class TInputPixelType, class TOutputPixelType>
275 {
276  // initialize bvalues from reference value and the gradients provided on input
277  this->SetReferenceBValue(bvalue);
278  this->SetGradientDirections( gradients );
279 
280  // call the other method
281  return this->GetSnapshot( input, this->m_BValues, kf_conf );
282 }
283 
284 
285 template< class TInputPixelType, class TOutputPixelType>
289 {
290  // initialize
291  vnl_vector<double> initial_position;
292  bool omit_bzero = kf_conf.omit_bzero;
293 
294  if( !omit_bzero )
295  {
296  initial_position.set_size(2);
297  initial_position[0] = this->m_InitialPosition[0];
298  initial_position[1] = this->m_InitialPosition[1];
299  }
300  else
301  {
302  initial_position.set_size(3);
303  initial_position = this->m_InitialPosition;
304  }
305 
306  // fit
307  FitSingleVoxel( input, bvalues, initial_position, kf_conf );
308 
309  // assembly result snapshot
310  KurtosisSnapshot result;
311  result.m_D = initial_position[0];
312  result.m_K = initial_position[1];
313 
314  if( omit_bzero )
315  {
316  result.m_f = 1 - initial_position[2] / std::fmax(0.01, input.GetElement(0));
317  result.m_BzeroFit = initial_position[2];
318  }
319  else
320  result.m_f = 1;
321 
322  // assembly data vectors for fitting
323  auto bvalueIter = bvalues.begin();
324  unsigned int unused_values = 0;
325  while( bvalueIter != bvalues.end() )
326  {
327  if( *bvalueIter < vnl_math::eps && omit_bzero )
328  {
329  ++unused_values;
330  }
331  ++bvalueIter;
332  }
333 
334  // initialize data vectors with the estimated size (after filtering)
335  vnl_vector<double> fit_measurements( input.Size() - unused_values, 0 );
336  vnl_vector<double> fit_bvalues( input.Size() - unused_values, 0 );
337 
338  // original data
339  vnl_vector<double> orig_measurements( input.Size(), 0 );
340 
341  bvalueIter = bvalues.begin();
342  unsigned int running_index = 0;
343  unsigned int skip_count = 0;
344  while( bvalueIter != bvalues.end() )
345  {
346  if( *bvalueIter < vnl_math::eps && omit_bzero )
347  {
348  ++skip_count;
349  }
350  else
351  {
352  fit_measurements[ running_index - skip_count ] = input.GetElement(running_index);
353  fit_bvalues[ running_index - skip_count ] = *bvalueIter;
354  }
355 
356  orig_measurements[ running_index ] = input.GetElement(running_index);
357 
358  ++running_index;
359  ++bvalueIter;
360  }
361 
362  result.fit_bvalues = fit_bvalues;
363  result.fit_measurements = fit_measurements;
364 
365  result.bvalues = bvalues;
366  result.measurements = orig_measurements;
367 
368  result.m_fittedBZero = omit_bzero;
369 
370  return result;
371 }
372 
373 template< class TInputPixelType, class TOutputPixelType>
376 {
377  if( this->m_ReferenceBValue < 0)
378  {
379  itkExceptionMacro( << "Specify reference b-value prior to setting the gradient directions." );
380  }
381 
382  if( gradients->Size() == 0 )
383  {
384  itkExceptionMacro( << "Empty gradient directions container retrieved" );
385  }
386 
387  vnl_vector<double> vnl_bvalues( gradients->Size(), 0 );
388 
389  // initialize the b-values
390  auto grIter = gradients->Begin();
391  unsigned int index = 0;
392  while( grIter != gradients->End() )
393  {
394  const double twonorm = grIter.Value().two_norm();
395  vnl_bvalues( index++ ) = this->m_ReferenceBValue * twonorm * twonorm;
396 
397  ++grIter;
398  }
399 
400  this->m_BValues = vnl_bvalues;
401 
402 
403 }
404 
405 template< class TInputPixelType, class TOutputPixelType>
407 ::SetInitialSolution(const vnl_vector<double>& x0 )
408 {
409  unsigned int param_size = 2 + static_cast<int>( this->m_OmitBZero );
410  assert( x0.size() == param_size );
411 
412  this->m_InitialPosition = x0;
413 }
414 
415 template< class TInputPixelType, class TOutputPixelType>
417 ::ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, ThreadIdType /*threadId*/)
418 {
419  typename OutputImageType::Pointer dImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0));
420  itk::ImageRegionIteratorWithIndex< OutputImageType > dImageIt(dImage, outputRegionForThread);
421  dImageIt.GoToBegin();
422 
423  typename OutputImageType::Pointer kImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(1));
424  itk::ImageRegionIteratorWithIndex< OutputImageType > kImageIt(kImage, outputRegionForThread);
425  kImageIt.GoToBegin();
426 
427  typedef itk::ImageRegionConstIteratorWithIndex< InputImageType > InputIteratorType;
428  InputIteratorType inputIter( m_ProcessedInputImage, outputRegionForThread );
429  inputIter.GoToBegin();
430 
431  typedef itk::ImageRegionConstIteratorWithIndex< MaskImageType > MaskIteratorType;
432  MaskIteratorType maskIter( this->m_MaskImage, outputRegionForThread );
433  maskIter.GoToBegin();
434 
435  KurtosisFitConfiguration fit_config;
436  fit_config.omit_bzero = this->m_OmitBZero;
437  fit_config.fit_scale = this->m_ScaleForFitting;
438 
439  fit_config.use_K_limits = this->m_UseKBounds;
440  fit_config.K_limits = this->m_KurtosisBounds;
441 
442  vnl_vector<double> initial_position;
443  if( !this->m_OmitBZero )
444  {
445  initial_position.set_size(2);
446  initial_position[0] = this->m_InitialPosition[0];
447  initial_position[1] = this->m_InitialPosition[1];
448 
449  }
450  else
451  {
452  initial_position.set_size(3);
453  initial_position = this->m_InitialPosition;
454  }
455 
456  while( !inputIter.IsAtEnd() )
457  {
458  // set (reset) each iteration
459  vnl_vector<double> result = initial_position;
460 
461  // fit single voxel (if inside mask )
462  if( maskIter.Get() > 0 )
463  {
464  FitSingleVoxel( inputIter.Get(), this->m_BValues, result, fit_config );
465  }
466  else
467  {
468  result.fill(0);
469  }
470 
471  // regardless the fit type, the parameters are always in the first two position
472  // of the results vector
473  dImageIt.Set( result[0] );
474  kImageIt.Set( result[1] );
475 
476  //std::cout << "[Kurtosis.Fit]" << inputIter.GetIndex() << " --> " << dImageIt.GetIndex() << " result: " << result << std::endl;
477 
478  ++maskIter;
479  ++inputIter;
480  ++dImageIt;
481  ++kImageIt;
482  }
483 
484 }
485 
486 #endif // guards
A fitting function handling the unweighted signal b_0 as a fitted parameter.
itk::SmartPointer< Self > Pointer
#define MITK_DEBUG
Definition: mitkLogMacros.h:26
Struct describing a result (and the data) of a Kurtosis model fit.
#define mitkThrow()
KurtosisSnapshot GetSnapshot(const itk::VariableLengthVector< TInputPixelType > &input, vnl_vector< double > bvalues, KurtosisFitConfiguration kf_conf)
void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, ThreadIdType threadId) override
void SetGradientDirections(GradientDirectionContainerType::Pointer gradients)
MITKCORE_EXPORT const ScalarType eps
static void FitSingleVoxel(const itk::VariableLengthVector< TInputPixelType > &input, const vnl_vector< double > &bvalues, vnl_vector< double > &result, itk::KurtosisFitConfiguration kf_config)
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.