Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
itkTractsToVectorImageFilter.cpp
Go to the documentation of this file.
2 
3 // VTK
4 #include <vtkPolyLine.h>
5 #include <vtkCellArray.h>
6 #include <vtkCellData.h>
7 
8 // ITK
9 #include <itkTimeProbe.h>
10 #include <itkImageRegionIterator.h>
11 
12 // misc
13 #define _USE_MATH_DEFINES
14 #include <math.h>
15 #include <boost/progress.hpp>
16 
17 
18 namespace itk{
19 
20 static bool CompareVectorLengths(const vnl_vector_fixed< double, 3 >& v1, const vnl_vector_fixed< double, 3 >& v2)
21 {
22  return (v1.magnitude()>v2.magnitude());
23 }
24 
25 template< class PixelType >
27  m_AngularThreshold(0.7),
28  m_Epsilon(0.999),
29  m_MaskImage(NULL),
30  m_NormalizeVectors(false),
31  m_UseWorkingCopy(true),
32  m_MaxNumDirections(3),
33  m_SizeThreshold(0.3),
34  m_NumDirectionsImage(NULL),
35  m_CreateDirectionImages(true)
36 {
37  this->SetNumberOfRequiredOutputs(1);
38 }
39 
40 
41 template< class PixelType >
43 {
44 }
45 
46 
47 template< class PixelType >
48 vnl_vector_fixed<double, 3> TractsToVectorImageFilter< PixelType >::GetVnlVector(double point[])
49 {
50  vnl_vector_fixed<double, 3> vnlVector;
51  vnlVector[0] = point[0];
52  vnlVector[1] = point[1];
53  vnlVector[2] = point[2];
54  return vnlVector;
55 }
56 
57 
58 template< class PixelType >
59 itk::Point<double, 3> TractsToVectorImageFilter< PixelType >::GetItkPoint(double point[])
60 {
61  itk::Point<double, 3> itkPoint;
62  itkPoint[0] = point[0];
63  itkPoint[1] = point[1];
64  itkPoint[2] = point[2];
65  return itkPoint;
66 }
67 
68 template< class PixelType >
70 {
71  mitk::BaseGeometry::Pointer geometry = m_FiberBundle->GetGeometry();
72 
73  // calculate new image parameters
74  itk::Vector<double> spacing;
75  itk::Point<double> origin;
76  itk::Matrix<double, 3, 3> direction;
77  ImageRegion<3> imageRegion;
78  if (!m_MaskImage.IsNull())
79  {
80  spacing = m_MaskImage->GetSpacing();
81  imageRegion = m_MaskImage->GetLargestPossibleRegion();
82  origin = m_MaskImage->GetOrigin();
83  direction = m_MaskImage->GetDirection();
84  }
85  else
86  {
87  spacing = geometry->GetSpacing();
88  origin = geometry->GetOrigin();
89  mitk::BaseGeometry::BoundsArrayType bounds = geometry->GetBounds();
90  origin[0] += bounds.GetElement(0);
91  origin[1] += bounds.GetElement(2);
92  origin[2] += bounds.GetElement(4);
93 
94  for (int i=0; i<3; i++)
95  for (int j=0; j<3; j++)
96  direction[j][i] = geometry->GetMatrixColumn(i)[j];
97  imageRegion.SetSize(0, geometry->GetExtent(0));
98  imageRegion.SetSize(1, geometry->GetExtent(1));
99  imageRegion.SetSize(2, geometry->GetExtent(2));
100 
101 
102  m_MaskImage = ItkUcharImgType::New();
103  m_MaskImage->SetSpacing( spacing );
104  m_MaskImage->SetOrigin( origin );
105  m_MaskImage->SetDirection( direction );
106  m_MaskImage->SetRegions( imageRegion );
107  m_MaskImage->Allocate();
108  m_MaskImage->FillBuffer(1);
109  }
110  OutputImageType::RegionType::SizeType outImageSize = imageRegion.GetSize();
111  m_OutImageSpacing = m_MaskImage->GetSpacing();
112  m_ClusteredDirectionsContainer = ContainerType::New();
113 
114  // initialize num directions image
115  m_NumDirectionsImage = ItkUcharImgType::New();
116  m_NumDirectionsImage->SetSpacing( spacing );
117  m_NumDirectionsImage->SetOrigin( origin );
118  m_NumDirectionsImage->SetDirection( direction );
119  m_NumDirectionsImage->SetRegions( imageRegion );
120  m_NumDirectionsImage->Allocate();
121  m_NumDirectionsImage->FillBuffer(0);
122 
123  // initialize direction images
124  m_DirectionImageContainer = DirectionImageContainerType::New();
125 
126  // resample fiber bundle
127  double minSpacing = 1;
128  if(m_OutImageSpacing[0]<m_OutImageSpacing[1] && m_OutImageSpacing[0]<m_OutImageSpacing[2])
129  minSpacing = m_OutImageSpacing[0];
130  else if (m_OutImageSpacing[1] < m_OutImageSpacing[2])
131  minSpacing = m_OutImageSpacing[1];
132  else
133  minSpacing = m_OutImageSpacing[2];
134 
135  if (m_UseWorkingCopy)
136  m_FiberBundle = m_FiberBundle->GetDeepCopy();
137 
138  // resample fiber bundle for sufficient voxel coverage
139  m_FiberBundle->ResampleSpline(minSpacing/10);
140 
141  // iterate over all fibers
142  vtkSmartPointer<vtkPolyData> fiberPolyData = m_FiberBundle->GetFiberPolyData();
143  int numFibers = m_FiberBundle->GetNumFibers();
144  m_DirectionsContainer = ContainerType::New();
145 
146  VectorContainer< unsigned int, std::vector< double > >::Pointer peakLengths = VectorContainer< unsigned int, std::vector< double > >::New();
147 
148  MITK_INFO << "Generating directions from tractogram";
149  boost::progress_display disp(numFibers);
150  for( int i=0; i<numFibers; i++ )
151  {
152  ++disp;
153  vtkCell* cell = fiberPolyData->GetCell(i);
154  int numPoints = cell->GetNumberOfPoints();
155  vtkPoints* points = cell->GetPoints();
156  if (numPoints<2)
157  continue;
158 
159  vnl_vector_fixed<double, 3> dir;
160  itk::Point<double, 3> worldPos;
161  vnl_vector<double> v;
162 
163 
164  float fiberWeight = m_FiberBundle->GetFiberWeight(i);
165 
166  for( int j=0; j<numPoints-1; j++)
167  {
168  // get current position along fiber in world coordinates
169  double* temp = points->GetPoint(j);
170  worldPos = GetItkPoint(temp);
171  itk::Index<3> index;
172  m_MaskImage->TransformPhysicalPointToIndex(worldPos, index);
173  if (!m_MaskImage->GetLargestPossibleRegion().IsInside(index) || m_MaskImage->GetPixel(index)==0)
174  continue;
175 
176  // get fiber tangent direction at this position
177  v = GetVnlVector(temp);
178  dir = GetVnlVector(points->GetPoint(j+1))-v;
179  if (dir.is_zero())
180  continue;
181  dir.normalize();
182 
183  // add direction to container
184  unsigned int idx = index[0] + outImageSize[0]*(index[1] + outImageSize[1]*index[2]);
186  if (m_DirectionsContainer->IndexExists(idx))
187  {
188  peakLengths->ElementAt(idx).push_back(fiberWeight);
189 
190  dirCont = m_DirectionsContainer->GetElement(idx);
191  if (dirCont.IsNull())
192  {
193  dirCont = DirectionContainerType::New();
194  dirCont->push_back(dir);
195  m_DirectionsContainer->InsertElement(idx, dirCont);
196  }
197  else
198  dirCont->push_back(dir);
199  }
200  else
201  {
202  dirCont = DirectionContainerType::New();
203  dirCont->push_back(dir);
204  m_DirectionsContainer->InsertElement(idx, dirCont);
205 
206  std::vector< double > lengths; lengths.push_back(fiberWeight);
207  peakLengths->InsertElement(idx, lengths);
208  }
209  }
210  }
211 
212  vtkSmartPointer<vtkCellArray> m_VtkCellArray = vtkSmartPointer<vtkCellArray>::New();
213  vtkSmartPointer<vtkPoints> m_VtkPoints = vtkSmartPointer<vtkPoints>::New();
214 
215  itk::ImageRegionIterator<ItkUcharImgType> dirIt(m_NumDirectionsImage, m_NumDirectionsImage->GetLargestPossibleRegion());
216 
217  MITK_INFO << "Clustering directions";
218  boost::progress_display disp2(outImageSize[0]*outImageSize[1]*outImageSize[2]);
219  while(!dirIt.IsAtEnd())
220  {
221  ++disp2;
222  OutputImageType::IndexType index = dirIt.GetIndex();
223  int idx = index[0]+(index[1]+index[2]*outImageSize[1])*outImageSize[0];
224 
225  if (!m_DirectionsContainer->IndexExists(idx))
226  {
227  ++dirIt;
228  continue;
229  }
230  DirectionContainerType::Pointer dirCont = m_DirectionsContainer->GetElement(idx);
231  if (dirCont.IsNull() || dirCont->empty())
232  {
233  ++dirIt;
234  continue;
235  }
236 
237 // std::vector< double > lengths; lengths.resize(dirCont->size(), 1); // all peaks have size 1
239  if (m_MaxNumDirections>0)
240  {
241  directions = FastClustering(dirCont, peakLengths->GetElement(idx));
242  std::sort( directions->begin(), directions->end(), CompareVectorLengths );
243  }
244  else
245  directions = dirCont;
246 
247  unsigned int numDir = directions->size();
248  if (m_MaxNumDirections>0 && numDir>m_MaxNumDirections)
249  numDir = m_MaxNumDirections;
250 
251  int count = 0;
252  for (unsigned int i=0; i<numDir; i++)
253  {
254  vtkSmartPointer<vtkPolyLine> container = vtkSmartPointer<vtkPolyLine>::New();
255  itk::ContinuousIndex<double, 3> center;
256  center[0] = index[0];
257  center[1] = index[1];
258  center[2] = index[2];
259  itk::Point<double> worldCenter;
260  m_MaskImage->TransformContinuousIndexToPhysicalPoint( center, worldCenter );
261  DirectionType dir = directions->at(i);
262 
263  if (dir.magnitude()<m_SizeThreshold)
264  continue;
265  if (m_NormalizeVectors)
266  dir.normalize();
267  count++;
268 
269  if (m_CreateDirectionImages && i<10)
270  {
271  if (i==m_DirectionImageContainer->size())
272  {
274  directionImage->SetSpacing( spacing );
275  directionImage->SetOrigin( origin );
276  directionImage->SetDirection( direction );
277  directionImage->SetRegions( imageRegion );
278  directionImage->Allocate();
279  Vector< float, 3 > nullVec; nullVec.Fill(0.0);
280  directionImage->FillBuffer(nullVec);
281  m_DirectionImageContainer->InsertElement(i, directionImage);
282  }
283 
284  // set direction image pixel
285  ItkDirectionImageType::Pointer directionImage = m_DirectionImageContainer->GetElement(i);
286  Vector< float, 3 > pixel;
287  pixel.SetElement(0, dir[0]);
288  pixel.SetElement(1, dir[1]);
289  pixel.SetElement(2, dir[2]);
290  directionImage->SetPixel(index, pixel);
291  }
292 
293  // add direction to vector field (with spacing compensation)
294  itk::Point<double> worldStart;
295  worldStart[0] = worldCenter[0]-dir[0]/2*minSpacing;
296  worldStart[1] = worldCenter[1]-dir[1]/2*minSpacing;
297  worldStart[2] = worldCenter[2]-dir[2]/2*minSpacing;
298  vtkIdType id = m_VtkPoints->InsertNextPoint(worldStart.GetDataPointer());
299  container->GetPointIds()->InsertNextId(id);
300  itk::Point<double> worldEnd;
301  worldEnd[0] = worldCenter[0]+dir[0]/2*minSpacing;
302  worldEnd[1] = worldCenter[1]+dir[1]/2*minSpacing;
303  worldEnd[2] = worldCenter[2]+dir[2]/2*minSpacing;
304  id = m_VtkPoints->InsertNextPoint(worldEnd.GetDataPointer());
305  container->GetPointIds()->InsertNextId(id);
306  m_VtkCellArray->InsertNextCell(container);
307  }
308  dirIt.Set(count);
309  ++dirIt;
310  }
311 
312  vtkSmartPointer<vtkPolyData> directionsPolyData = vtkSmartPointer<vtkPolyData>::New();
313  directionsPolyData->SetPoints(m_VtkPoints);
314  directionsPolyData->SetLines(m_VtkCellArray);
315  m_OutputFiberBundle = mitk::FiberBundle::New(directionsPolyData);
316 }
317 
318 
319 template< class PixelType >
321 {
323  if (inDirs->size()<2)
324  return inDirs;
325 
326  DirectionType oldMean, currentMean;
327  std::vector< int > touched;
328 
329  // initialize
330  touched.resize(inDirs->size(), 0);
331  bool free = true;
332  currentMean = inDirs->at(0); // initialize first seed
333  currentMean.normalize();
334  double length = lengths.at(0);
335  touched[0] = 1;
336  std::vector< double > newLengths;
337  bool meanChanged = false;
338 
339  double max = 0;
340  while (free)
341  {
342  oldMean.fill(0.0);
343 
344  // start mean-shift clustering
345  double angle = 0;
346 
347  while (fabs(dot_product(currentMean, oldMean))<0.99)
348  {
349  oldMean = currentMean;
350  currentMean.fill(0.0);
351  for (unsigned int i=0; i<inDirs->size(); i++)
352  {
353  angle = dot_product(oldMean, inDirs->at(i));
354  if (angle>=m_AngularThreshold)
355  {
356  currentMean += inDirs->at(i);
357  if (meanChanged)
358  length += lengths.at(i);
359  touched[i] = 1;
360  meanChanged = true;
361  }
362  else if (-angle>=m_AngularThreshold)
363  {
364  currentMean -= inDirs->at(i);
365  if (meanChanged)
366  length += lengths.at(i);
367  touched[i] = 1;
368  meanChanged = true;
369  }
370  }
371  if(!meanChanged)
372  currentMean = oldMean;
373  else
374  currentMean.normalize();
375  }
376 
377  // found stable mean
378  outDirs->push_back(currentMean);
379  newLengths.push_back(length);
380  if (length>max)
381  max = length;
382 
383  // find next unused seed
384  free = false;
385  for (unsigned int i=0; i<touched.size(); i++)
386  if (touched[i]==0)
387  {
388  currentMean = inDirs->at(i);
389  free = true;
390  meanChanged = false;
391  length = lengths.at(i);
392  touched[i] = 1;
393  break;
394  }
395  }
396 
397  if (inDirs->size()==outDirs->size())
398  {
399  if (max>0)
400  for (unsigned int i=0; i<outDirs->size(); i++)
401  outDirs->SetElement(i, outDirs->at(i)*newLengths.at(i)/max);
402  return outDirs;
403  }
404  else
405  return FastClustering(outDirs, newLengths);
406 }
407 
408 
409 //template< class PixelType >
410 //std::vector< DirectionType > TractsToVectorImageFilter< PixelType >::Clustering(std::vector< DirectionType >& inDirs)
411 //{
412 // std::vector< DirectionType > outDirs;
413 // if (inDirs.empty())
414 // return outDirs;
415 // DirectionType oldMean, currentMean, workingMean;
416 
417 // std::vector< DirectionType > normalizedDirs;
418 // std::vector< int > touched;
419 // for (std::size_t i=0; i<inDirs.size(); i++)
420 // {
421 // normalizedDirs.push_back(inDirs[i]);
422 // normalizedDirs.back().normalize();
423 // }
424 
425 // // initialize
426 // double max = 0.0;
427 // touched.resize(inDirs.size(), 0);
428 // for (std::size_t j=0; j<inDirs.size(); j++)
429 // {
430 // currentMean = inDirs[j];
431 // oldMean.fill(0.0);
432 
433 // // start mean-shift clustering
434 // double angle = 0.0;
435 // int counter = 0;
436 // while ((currentMean-oldMean).magnitude()>0.0001)
437 // {
438 // counter = 0;
439 // oldMean = currentMean;
440 // workingMean = oldMean;
441 // workingMean.normalize();
442 // currentMean.fill(0.0);
443 // for (std::size_t i=0; i<normalizedDirs.size(); i++)
444 // {
445 // angle = dot_product(workingMean, normalizedDirs[i]);
446 // if (angle>=m_AngularThreshold)
447 // {
448 // currentMean += inDirs[i];
449 // counter++;
450 // }
451 // else if (-angle>=m_AngularThreshold)
452 // {
453 // currentMean -= inDirs[i];
454 // counter++;
455 // }
456 // }
457 // }
458 
459 // // found stable mean
460 // if (counter>0)
461 // {
462 // bool add = true;
463 // DirectionType normMean = currentMean;
464 // normMean.normalize();
465 // for (std::size_t i=0; i<outDirs.size(); i++)
466 // {
467 // DirectionType dir = outDirs[i];
468 // dir.normalize();
469 // if ((normMean-dir).magnitude()<=0.0001)
470 // {
471 // add = false;
472 // break;
473 // }
474 // }
475 
476 // currentMean /= counter;
477 // if (add)
478 // {
479 // double mag = currentMean.magnitude();
480 // if (mag>0)
481 // {
482 // if (mag>max)
483 // max = mag;
484 
485 // outDirs.push_back(currentMean);
486 // }
487 // }
488 // }
489 // }
490 
491 // if (m_NormalizeVectors)
492 // for (std::size_t i=0; i<outDirs.size(); i++)
493 // outDirs[i].normalize();
494 // else if (max>0)
495 // for (std::size_t i=0; i<outDirs.size(); i++)
496 // outDirs[i] /= max;
497 
498 // if (inDirs.size()==outDirs.size())
499 // return outDirs;
500 // else
501 // return FastClustering(outDirs);
502 //}
503 
504 
505 //template< class PixelType >
506 //TractsToVectorImageFilter< PixelType >::DirectionContainerType::Pointer TractsToVectorImageFilter< PixelType >::MeanShiftClustering(DirectionContainerType::Pointer dirCont)
507 //{
508 // DirectionContainerType::Pointer container = DirectionContainerType::New();
509 
510 // double max = 0;
511 // for (DirectionContainerType::ConstIterator it = dirCont->Begin(); it!=dirCont->End(); ++it)
512 // {
513 // vnl_vector_fixed<double, 3> mean = ClusterStep(dirCont, it.Value());
514 
515 // if (mean.is_zero())
516 // continue;
517 // bool addMean = true;
518 
519 // for (DirectionContainerType::ConstIterator it2 = container->Begin(); it2!=container->End(); ++it2)
520 // {
521 // vnl_vector_fixed<double, 3> dir = it2.Value();
522 // double angle = fabs(dot_product(mean, dir)/(mean.magnitude()*dir.magnitude()));
523 // if (angle>=m_Epsilon)
524 // {
525 // addMean = false;
526 // break;
527 // }
528 // }
529 
530 // if (addMean)
531 // {
532 // if (m_NormalizeVectors)
533 // mean.normalize();
534 // else if (mean.magnitude()>max)
535 // max = mean.magnitude();
536 // container->InsertElement(container->Size(), mean);
537 // }
538 // }
539 
540 // // max normalize voxel directions
541 // if (max>0 && !m_NormalizeVectors)
542 // for (std::size_t i=0; i<container->Size(); i++)
543 // container->ElementAt(i) /= max;
544 
545 // if (container->Size()<dirCont->Size())
546 // return MeanShiftClustering(container);
547 // else
548 // return container;
549 //}
550 
551 
552 //template< class PixelType >
553 //vnl_vector_fixed<double, 3> TractsToVectorImageFilter< PixelType >::ClusterStep(DirectionContainerType::Pointer dirCont, vnl_vector_fixed<double, 3> currentMean)
554 //{
555 // vnl_vector_fixed<double, 3> newMean; newMean.fill(0);
556 
557 // for (DirectionContainerType::ConstIterator it = dirCont->Begin(); it!=dirCont->End(); ++it)
558 // {
559 // vnl_vector_fixed<double, 3> dir = it.Value();
560 // double angle = dot_product(currentMean, dir)/(currentMean.magnitude()*dir.magnitude());
561 // if (angle>=m_AngularThreshold)
562 // newMean += dir;
563 // else if (-angle>=m_AngularThreshold)
564 // newMean -= dir;
565 // }
566 
567 // if (fabs(dot_product(currentMean, newMean)/(currentMean.magnitude()*newMean.magnitude()))>=m_Epsilon || newMean.is_zero())
568 // return newMean;
569 // else
570 // return ClusterStep(dirCont, newMean);
571 //}
572 }
573 
574 
575 
itk::SmartPointer< Self > Pointer
BoundingBoxType::BoundsArrayType BoundsArrayType
#define MITK_INFO
Definition: mitkLogMacros.h:22
vnl_vector_fixed< double, 3 > GetVnlVector(double point[3])
vnl_vector_fixed< double, 3 > DirectionType
itk::Point< double, 3 > GetItkPoint(double point[3])
static T max(T x, T y)
Definition: svm.cpp:70
void GenerateData() override
A version of GenerateData() specific for image processing filters.
static bool CompareVectorLengths(const vnl_vector_fixed< double, 3 > &v1, const vnl_vector_fixed< double, 3 > &v2)
Extracts the voxel-wise main directions of the input fiber bundle.
DirectionContainerType::Pointer FastClustering(DirectionContainerType::Pointer inDirs, std::vector< double > lengths)
cluster fiber directions
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.