Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
itkMLBSTrackingFilter.cpp
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 #ifndef __itkMLBSTrackingFilter_txx
18 #define __itkMLBSTrackingFilter_txx
19 
20 #include <time.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 
24 #include "itkMLBSTrackingFilter.h"
25 #include <itkImageRegionConstIterator.h>
26 #include <itkImageRegionConstIteratorWithIndex.h>
27 #include <itkImageRegionIterator.h>
28 #include <itkImageFileWriter.h>
29 
30 #define _USE_MATH_DEFINES
31 #include <math.h>
32 
33 namespace itk {
34 
35 template< int ShOrder, int NumImageFeatures >
38  : m_PauseTracking(false)
39  , m_AbortTracking(false)
40  , m_FiberPolyData(NULL)
41  , m_Points(NULL)
42  , m_Cells(NULL)
43  , m_AngularThreshold(0.7)
44  , m_StepSize(0)
45  , m_MaxLength(10000)
46  , m_MinTractLength(20.0)
47  , m_MaxTractLength(400.0)
48  , m_SeedsPerVoxel(1)
49  , m_RandomSampling(false)
50  , m_SamplingDistance(-1)
51  , m_NumberOfSamples(50)
52  , m_StoppingRegions(NULL)
53  , m_SeedImage(NULL)
54  , m_MaskImage(NULL)
55  , m_AposterioriCurvCheck(false)
56  , m_RemoveWmEndFibers(false)
57  , m_AvoidStop(true)
58  , m_DemoMode(false)
59 {
60  this->SetNumberOfRequiredInputs(1);
61 }
62 
63 template< int ShOrder, int NumImageFeatures >
65 ::RoundToNearest(double num) {
66  return (num > 0.0) ? floor(num + 0.5) : ceil(num - 0.5);
67 }
68 
69 template< int ShOrder, int NumImageFeatures >
71 {
72  m_InputImage = const_cast<InputImageType*>(this->GetInput(0));
73  m_ForestHandler.InitForTracking();
74 
75  m_FiberPolyData = PolyDataType::New();
78 
79  std::vector< double > m_ImageSpacing; m_ImageSpacing.resize(3);
80  m_ImageSpacing[0] = m_InputImage->GetSpacing()[0];
81  m_ImageSpacing[1] = m_InputImage->GetSpacing()[1];
82  m_ImageSpacing[2] = m_InputImage->GetSpacing()[2];
83 
84  double minSpacing;
85  if(m_ImageSpacing[0]<m_ImageSpacing[1] && m_ImageSpacing[0]<m_ImageSpacing[2])
86  minSpacing = m_ImageSpacing[0];
87  else if (m_ImageSpacing[1] < m_ImageSpacing[2])
88  minSpacing = m_ImageSpacing[1];
89  else
90  minSpacing = m_ImageSpacing[2];
91 
92  m_StepSize *= minSpacing;
93  if (m_StepSize<mitk::eps)
94  m_StepSize = 0.5*minSpacing;
95 
96  m_SamplingDistance *= minSpacing;
97  if (m_SamplingDistance<mitk::eps)
98  m_SamplingDistance = minSpacing*0.5;
99 
100  m_PolyDataContainer.clear();
101  for (unsigned int i=0; i<this->GetNumberOfThreads(); i++)
102  {
104  m_PolyDataContainer.push_back(poly);
105  }
106 
107  if (m_StoppingRegions.IsNull())
108  {
109  m_StoppingRegions = ItkUcharImgType::New();
110  m_StoppingRegions->SetSpacing( m_InputImage->GetSpacing() );
111  m_StoppingRegions->SetOrigin( m_InputImage->GetOrigin() );
112  m_StoppingRegions->SetDirection( m_InputImage->GetDirection() );
113  m_StoppingRegions->SetRegions( m_InputImage->GetLargestPossibleRegion() );
114  m_StoppingRegions->Allocate();
115  m_StoppingRegions->FillBuffer(0);
116  }
117 
118  if (m_SeedImage.IsNull())
119  {
120  m_SeedImage = ItkUcharImgType::New();
121  m_SeedImage->SetSpacing( m_InputImage->GetSpacing() );
122  m_SeedImage->SetOrigin( m_InputImage->GetOrigin() );
123  m_SeedImage->SetDirection( m_InputImage->GetDirection() );
124  m_SeedImage->SetRegions( m_InputImage->GetLargestPossibleRegion() );
125  m_SeedImage->Allocate();
126  m_SeedImage->FillBuffer(1);
127  }
128 
129  if (m_MaskImage.IsNull())
130  {
131  // initialize mask image
132  m_MaskImage = ItkUcharImgType::New();
133  m_MaskImage->SetSpacing( m_InputImage->GetSpacing() );
134  m_MaskImage->SetOrigin( m_InputImage->GetOrigin() );
135  m_MaskImage->SetDirection( m_InputImage->GetDirection() );
136  m_MaskImage->SetRegions( m_InputImage->GetLargestPossibleRegion() );
137  m_MaskImage->Allocate();
138  m_MaskImage->FillBuffer(1);
139  }
140  else
141  std::cout << "MLBSTrackingFilter: using mask image" << std::endl;
142 
143  if (m_AngularThreshold<0.0)
144  m_AngularThreshold = 0.5*minSpacing;
145  m_BuildFibersReady = 0;
146  m_BuildFibersFinished = false;
147  m_Threads = 0;
148  m_Tractogram.clear();
149  m_SamplingPointset = mitk::PointSet::New();
150  m_AlternativePointset = mitk::PointSet::New();
151  m_StartTime = std::chrono::system_clock::now();
152 
153  std::cout << "MLBSTrackingFilter: Angular threshold: " << m_AngularThreshold << std::endl;
154  std::cout << "MLBSTrackingFilter: Stepsize: " << m_StepSize << " mm" << std::endl;
155  std::cout << "MLBSTrackingFilter: Seeds per voxel: " << m_SeedsPerVoxel << std::endl;
156  std::cout << "MLBSTrackingFilter: Max. sampling distance: " << m_SamplingDistance << " mm" << std::endl;
157  std::cout << "MLBSTrackingFilter: Number of samples: " << m_NumberOfSamples << std::endl;
158  std::cout << "MLBSTrackingFilter: Max. tract length: " << m_MaxTractLength << " mm" << std::endl;
159  std::cout << "MLBSTrackingFilter: Min. tract length: " << m_MinTractLength << " mm" << std::endl;
160  std::cout << "MLBSTrackingFilter: Starting streamline tracking using " << this->GetNumberOfThreads() << " threads." << std::endl;
161 }
162 
163 template< int ShOrder, int NumImageFeatures >
164 void MLBSTrackingFilter< ShOrder, NumImageFeatures >::CalculateNewPosition(itk::Point<double, 3>& pos, vnl_vector_fixed<double, 3>& dir)
165 {
166  // vnl_matrix_fixed< double, 3, 3 > rot = m_FeatureImage->GetDirection().GetTranspose();
167  // dir = rot*dir;
168 
169  dir *= m_StepSize;
170  pos[0] += dir[0];
171  pos[1] += dir[1];
172  pos[2] += dir[2];
173 }
174 
175 template< int ShOrder, int NumImageFeatures >
177 ::IsValidPosition(itk::Point<double, 3> &pos)
178 {
179  typename FeatureImageType::IndexType idx;
180  m_InputImage->TransformPhysicalPointToIndex(pos, idx);
181  if (!m_InputImage->GetLargestPossibleRegion().IsInside(idx) || m_MaskImage->GetPixel(idx)==0)
182  return false;
183 
184  return true;
185 }
186 
187 template< int ShOrder, int NumImageFeatures >
189 {
190  return (double)(rand()%((int)(10000*(max-min))) + 10000*min)/10000;
191 }
192 
193 template< int ShOrder, int NumImageFeatures >
194 vnl_vector_fixed<double,3> MLBSTrackingFilter< ShOrder, NumImageFeatures >::GetNewDirection(itk::Point<double, 3> &pos, vnl_vector_fixed<double, 3>& olddir)
195 {
196  if (m_DemoMode)
197  {
198  m_SamplingPointset->Clear();
199  m_AlternativePointset->Clear();
200  }
201  vnl_vector_fixed<double,3> direction; direction.fill(0);
202 
203  ItkUcharImgType::IndexType idx;
204  m_StoppingRegions->TransformPhysicalPointToIndex(pos, idx);
205  if (m_StoppingRegions->GetLargestPossibleRegion().IsInside(idx) && m_StoppingRegions->GetPixel(idx)>0)
206  return direction;
207 
208  if (m_MaskImage.IsNotNull() && ((m_MaskImage->GetLargestPossibleRegion().IsInside(idx) && m_MaskImage->GetPixel(idx)<=0) || !m_MaskImage->GetLargestPossibleRegion().IsInside(idx)) )
209  return direction;
210 
211  if (olddir.magnitude()>0)
212  olddir.normalize();
213 
214  int candidates = 0; // number of directions with probability > 0
215  double w = 0; // weight of the direction predicted at each sampling point
216  if (IsValidPosition(pos))
217  {
218  direction = m_ForestHandler.Classify(pos, candidates, olddir, m_AngularThreshold, w, m_MaskImage); // get direction proposal at current streamline position
219  direction *= w; // HERE WE ARE WEIGHTING AGAIN EVEN THOUGH THE OUTPUT DIRECTIONS ARE ALREADY WEIGHTED!!! THE EFFECT OF THIS HAS YET TO BE EVALUATED.
220  }
221 
223  itk::Point<double, 3> sample_pos;
224  int alternatives = 1;
225  for (int i=0; i<m_NumberOfSamples; i++)
226  {
227  vnl_vector_fixed<double,3> d;
228 
229  if (m_RandomSampling)
230  {
231  d[0] = GetRandDouble();
232  d[1] = GetRandDouble();
233  d[2] = GetRandDouble();
234  d.normalize();
235  d *= GetRandDouble(0,m_SamplingDistance);
236  }
237  else
238  {
239  d = probeVecs.GetDirection(i)*m_SamplingDistance;
240  }
241 
242  sample_pos[0] = pos[0] + d[0];
243  sample_pos[1] = pos[1] + d[1];
244  sample_pos[2] = pos[2] + d[2];
245  if(m_DemoMode)
246  m_SamplingPointset->InsertPoint(i, sample_pos);
247 
248  candidates = 0;
249  vnl_vector_fixed<double,3> tempDir; tempDir.fill(0.0);
250  if (IsValidPosition(sample_pos))
251  tempDir = m_ForestHandler.Classify(sample_pos, candidates, olddir, m_AngularThreshold, w, m_MaskImage); // sample neighborhood
252  if (candidates>0 && tempDir.magnitude()>0.001)
253  {
254  direction += tempDir*w;
255  }
256  else if (m_AvoidStop && candidates==0 && olddir.magnitude()>0) // out of white matter
257  {
258  double dot = dot_product(d, olddir);
259  if (dot >= 0.0) // in front of plane defined by pos and olddir
260  d = -d + 2*dot*olddir; // reflect
261  else
262  d = -d; // invert
263 
264  // look a bit further into the other direction
265  sample_pos[0] = pos[0] + d[0];
266  sample_pos[1] = pos[1] + d[1];
267  sample_pos[2] = pos[2] + d[2];
268  if(m_DemoMode)
269  m_AlternativePointset->InsertPoint(alternatives, sample_pos);
270  alternatives++;
271  candidates = 0;
272  vnl_vector_fixed<double,3> tempDir; tempDir.fill(0.0);
273  if (IsValidPosition(sample_pos))
274  tempDir = m_ForestHandler.Classify(sample_pos, candidates, olddir, m_AngularThreshold, w, m_MaskImage); // sample neighborhood
275 
276  if (candidates>0 && tempDir.magnitude()>0.001) // are we back in the white matter?
277  {
278  direction += d; // go into the direction of the white matter
279  direction += tempDir*w; // go into the direction of the white matter direction at this location
280  }
281  }
282  }
283 
284  if (direction.magnitude()>0.001)
285  {
286  direction.normalize();
287  olddir[0] = direction[0];
288  olddir[1] = direction[1];
289  olddir[2] = direction[2];
290  }
291  else
292  direction.fill(0);
293 
294  return direction;
295 }
296 
297 template< int ShOrder, int NumImageFeatures >
298 double MLBSTrackingFilter< ShOrder, NumImageFeatures >::FollowStreamline(itk::Point<double, 3> pos, vnl_vector_fixed<double,3> dir, FiberType* fib, double tractLength, bool front)
299 {
300  vnl_vector_fixed<double,3> dirOld = dir;
301  dirOld = dir;
302 
303  for (int step=0; step< m_MaxLength/2; step++)
304  {
305 
306  // get new position
307  CalculateNewPosition(pos, dir);
308 
309  // is new position inside of image and mask
310  if (m_AbortTracking) // if not end streamline
311  {
312  return tractLength;
313  }
314  else // if yes, add new point to streamline
315  {
316  tractLength += m_StepSize;
317  if (front)
318  fib->push_front(pos);
319  else
320  fib->push_back(pos);
321 
322  if (m_AposterioriCurvCheck)
323  {
324  int curv = CheckCurvature(fib, front); // TODO: Move into classification ???
325  if (curv>0)
326  {
327  tractLength -= m_StepSize*curv;
328  while (curv>0)
329  {
330  if (front)
331  fib->pop_front();
332  else
333  fib->pop_back();
334  curv--;
335  }
336  return tractLength;
337  }
338  }
339 
340  if (tractLength>m_MaxTractLength)
341  return tractLength;
342  }
343  if (m_DemoMode) // CHECK: warum sind die samplingpunkte der streamline in der visualisierung immer einen schritt voras?
344  {
345  m_Mutex.Lock();
346  m_BuildFibersReady++;
347  m_Tractogram.push_back(*fib);
348  BuildFibers(true);
349  m_Stop = true;
350  m_Mutex.Unlock();
351 
352  while (m_Stop){
353  }
354  }
355  dir = GetNewDirection(pos, dirOld);
356 
357  while (m_PauseTracking){}
358 
359  if (dir.magnitude()<0.0001)
360  return tractLength;
361  }
362  return tractLength;
363 }
364 
365 template< int ShOrder, int NumImageFeatures >
367 {
368  double m_Distance = 5;
369  if (fib->size()<3)
370  return 0;
371 
372  double dist = 0;
373  std::vector< vnl_vector_fixed< float, 3 > > vectors;
374  vnl_vector_fixed< float, 3 > meanV; meanV.fill(0);
375  double dev = 0;
376 
377  if (front)
378  {
379  int c=0;
380  while(dist<m_Distance && c<fib->size()-1)
381  {
382  itk::Point<double> p1 = fib->at(c);
383  itk::Point<double> p2 = fib->at(c+1);
384 
385  vnl_vector_fixed< float, 3 > v;
386  v[0] = p2[0]-p1[0];
387  v[1] = p2[1]-p1[1];
388  v[2] = p2[2]-p1[2];
389  dist += v.magnitude();
390  v.normalize();
391  vectors.push_back(v);
392  if (c==0)
393  meanV += v;
394  c++;
395  }
396  }
397  else
398  {
399  int c=fib->size()-1;
400  while(dist<m_Distance && c>0)
401  {
402  itk::Point<double> p1 = fib->at(c);
403  itk::Point<double> p2 = fib->at(c-1);
404 
405  vnl_vector_fixed< float, 3 > v;
406  v[0] = p2[0]-p1[0];
407  v[1] = p2[1]-p1[1];
408  v[2] = p2[2]-p1[2];
409  dist += v.magnitude();
410  v.normalize();
411  vectors.push_back(v);
412  if (c==fib->size()-1)
413  meanV += v;
414  c--;
415  }
416  }
417  meanV.normalize();
418 
419  for (int c=0; c<vectors.size(); c++)
420  {
421  double angle = dot_product(meanV, vectors.at(c));
422  if (angle>1.0)
423  angle = 1.0;
424  if (angle<-1.0)
425  angle = -1.0;
426  dev += acos(angle)*180/M_PI;
427  }
428  if (vectors.size()>0)
429  dev /= vectors.size();
430 
431  if (dev<30)
432  return 0;
433  else
434  return vectors.size();
435 }
436 
437 template< int ShOrder, int NumImageFeatures >
439 {
440  m_Mutex.Lock();
441  m_Threads++;
442  m_Mutex.Unlock();
443  typedef ImageRegionConstIterator< ItkUcharImgType > MaskIteratorType;
444  MaskIteratorType sit(m_SeedImage, regionForThread );
445  MaskIteratorType mit(m_MaskImage, regionForThread );
446 
447  sit.GoToBegin();
448  mit.GoToBegin();
449  itk::Point<double> worldPos;
450  while( !sit.IsAtEnd() )
451  {
452  if (sit.Value()==0 || mit.Value()==0)
453  {
454  ++sit;
455  ++mit;
456  continue;
457  }
458 
459  for (int s=0; s<m_SeedsPerVoxel; s++)
460  {
461  FiberType fib;
462  double tractLength = 0;
463  typename FeatureImageType::IndexType index = sit.GetIndex();
464  itk::ContinuousIndex<double, 3> start;
465  unsigned int counter = 0;
466 
467  if (m_SeedsPerVoxel>1)
468  {
469  start[0] = index[0]+GetRandDouble(-0.5, 0.5);
470  start[1] = index[1]+GetRandDouble(-0.5, 0.5);
471  start[2] = index[2]+GetRandDouble(-0.5, 0.5);
472  }
473  else
474  {
475  start[0] = index[0];
476  start[1] = index[1];
477  start[2] = index[2];
478  }
479 
480  // get staring position
481  m_SeedImage->TransformContinuousIndexToPhysicalPoint( start, worldPos );
482 
483  // get starting direction
484  int candidates = 0;
485  double prob = 0;
486  vnl_vector_fixed<double,3> dirOld; dirOld.fill(0.0);
487  vnl_vector_fixed<double,3> dir; dir.fill(0.0);
488  if (IsValidPosition(worldPos))
489  dir = m_ForestHandler.Classify(worldPos, candidates, dirOld, 0, prob, m_MaskImage);
490  if (dir.magnitude()<0.0001)
491  continue;
492 
493  // forward tracking
494  tractLength = FollowStreamline(worldPos, dir, &fib, 0, false);
495  fib.push_front(worldPos);
496 
497  if (m_RemoveWmEndFibers)
498  {
499  itk::Point<double> check = fib.back();
500  dirOld.fill(0.0);
501  vnl_vector_fixed<double,3> check2 = GetNewDirection(check, dirOld);
502  if (check2.magnitude()>0.001)
503  {
504  MITK_INFO << "Detected WM ending. Discarding fiber.";
505  continue;
506  }
507  }
508 
509  // backward tracking
510  tractLength = FollowStreamline(worldPos, -dir, &fib, tractLength, true);
511  counter = fib.size();
512 
513  if (m_RemoveWmEndFibers)
514  {
515  itk::Point<double> check = fib.front();
516  dirOld.fill(0.0);
517  vnl_vector_fixed<double,3> check2 = GetNewDirection(check, dirOld);
518  if (check2.magnitude()>0.001)
519  {
520  MITK_INFO << "Detected WM ending. Discarding fiber.";
521  continue;
522  }
523  }
524 
525  if (tractLength<m_MinTractLength || counter<2)
526  continue;
527 
528  m_Mutex.Lock();
529  m_Tractogram.push_back(fib);
530  m_Mutex.Unlock();
531 
532  if (m_AbortTracking)
533  break;
534  }
535  if (m_AbortTracking)
536  break;
537  ++sit;
538  ++mit;
539  }
540  m_Threads--;
541  std::cout << "Thread " << threadId << " finished tracking" << std::endl;
542 }
543 
544 template< int ShOrder, int NumImageFeatures >
546 {
547  if (m_BuildFibersReady<m_Threads && check)
548  return;
549 
550  m_FiberPolyData = vtkSmartPointer<vtkPolyData>::New();
551  vtkSmartPointer<vtkCellArray> vNewLines = vtkSmartPointer<vtkCellArray>::New();
552  vtkSmartPointer<vtkPoints> vNewPoints = vtkSmartPointer<vtkPoints>::New();
553 
554  for (int i=0; i<m_Tractogram.size(); i++)
555  {
556  vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New();
557  FiberType fib = m_Tractogram.at(i);
558  for (FiberType::iterator it = fib.begin(); it!=fib.end(); ++it)
559  {
560  vtkIdType id = vNewPoints->InsertNextPoint((*it).GetDataPointer());
561  container->GetPointIds()->InsertNextId(id);
562  }
563  vNewLines->InsertNextCell(container);
564  }
565 
566  if (check)
567  for (int i=0; i<m_BuildFibersReady; i++)
568  m_Tractogram.pop_back();
569  m_BuildFibersReady = 0;
570 
571  m_FiberPolyData->SetPoints(vNewPoints);
572  m_FiberPolyData->SetLines(vNewLines);
573  m_BuildFibersFinished = true;
574 }
575 
576 template< int ShOrder, int NumImageFeatures >
578 {
579  MITK_INFO << "Generating polydata ";
580  BuildFibers(false);
581  MITK_INFO << "done";
582 
583  m_EndTime = std::chrono::system_clock::now();
584  std::chrono::hours hh = std::chrono::duration_cast<std::chrono::hours>(m_EndTime - m_StartTime);
585  std::chrono::minutes mm = std::chrono::duration_cast<std::chrono::minutes>(m_EndTime - m_StartTime);
586  std::chrono::seconds ss = std::chrono::duration_cast<std::chrono::seconds>(m_EndTime - m_StartTime);
587  mm %= 60;
588  ss %= 60;
589  MITK_INFO << "Tracking took " << hh.count() << "h, " << mm.count() << "m and " << ss.count() << "s";
590 }
591 
592 }
593 
594 #endif // __itkDiffusionQballPrincipleDirectionsImageFilter_txx
Superclass::InputImageRegionType InputImageRegionType
double FollowStreamline(itk::Point< double, 3 > pos, vnl_vector_fixed< double, 3 > dir, FiberType *fib, double tractLength, bool front)
Start streamline in one direction.
double GetRandDouble(double min=-1, double max=1)
#define MITK_INFO
Definition: mitkLogMacros.h:22
void ThreadedGenerateData(const InputImageRegionType &outputRegionForThread, ThreadIdType threadId) override
static Pointer New()
vtkSmartPointer< vtkPolyData > PolyDataType
void CalculateNewPosition(itk::Point< double, 3 > &pos, vnl_vector_fixed< double, 3 > &dir)
Calculate next integration step.
int CheckCurvature(FiberType *fib, bool front)
bool IsValidPosition(itk::Point< double, 3 > &pos)
Are we outside of the mask image?
vnl_vector_fixed< double, 3 > GetNewDirection(itk::Point< double, 3 > &pos, vnl_vector_fixed< double, 3 > &olddir)
Determine new direction by sample voting at the current position taking the last progression directio...
Represents an ODF for Q-Ball imaging.
static vnl_vector_fixed< double, 3 > GetDirection(int i)
void AfterThreadedGenerateData() override
std::deque< itk::Point< double > > FiberType
static T max(T x, T y)
Definition: svm.cpp:70
Superclass::InputImageType InputImageType
static T min(T x, T y)
Definition: svm.cpp:67
MITKCORE_EXPORT const ScalarType eps
void BeforeThreadedGenerateData() override
double RoundToNearest(double num)
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.