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
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.