Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
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.