Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
itkGibbsTrackingFilter.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 #include "itkGibbsTrackingFilter.h"
17 
18 // MITK
22 #include <mitkFiberBuilder.h>
24 //#include <mitkEnergyComputer.h>
27 
28 // ITK
29 #include <itkImageDuplicator.h>
30 #include <itkResampleImageFilter.h>
31 #include <itkTimeProbe.h>
32 
33 // MISC
34 #include <fstream>
35 // #include <QFile>
36 #include <tinyxml.h>
37 #include <math.h>
38 #include <boost/progress.hpp>
39 #include <boost/lexical_cast.hpp>
40 #include <boost/algorithm/string.hpp>
41 
42 namespace itk{
43 
44 template< class ItkQBallImageType >
46  m_StartTemperature(0.1),
47  m_EndTemperature(0.001),
48  m_Iterations(1e9),
49  m_CurrentIteration(0.0),
50  m_CurrentStep(0),
51  m_ParticleWeight(0),
52  m_ParticleWidth(0),
53  m_ParticleLength(0),
54  m_ConnectionPotential(10),
55  m_InexBalance(0),
56  m_ParticlePotential(0.2),
57  m_MinFiberLength(10),
58  m_AbortTracking(false),
59  m_NumAcceptedFibers(0),
60  m_BuildFibers(false),
61  m_ProposalAcceptance(0),
62  m_CurvatureThreshold(0.7),
63  m_DuplicateImage(true),
64  m_NumParticles(0),
65  m_NumConnections(0),
66  m_RandomSeed(-1),
67  m_LoadParameterFile(""),
68  m_LutPath(""),
69  m_IsInValidState(true)
70 {
71 
72 }
73 
74 template< class ItkQBallImageType >
76 {
77 
78 }
79 
80 // fill output fiber bundle datastructure
81 template< class ItkQBallImageType >
83 {
84  if (!m_AbortTracking)
85  {
86  m_BuildFibers = true;
87  while (m_BuildFibers){}
88  }
89 
90  return m_FiberPolyData;
91 }
92 
93 template< class ItkQBallImageType >
94 void
97 {
98  MITK_INFO << "GibbsTrackingFilter: estimating particle weight";
99 
100  float minSpacing;
101  if(m_QBallImage->GetSpacing()[0]<m_QBallImage->GetSpacing()[1] && m_QBallImage->GetSpacing()[0]<m_QBallImage->GetSpacing()[2])
102  minSpacing = m_QBallImage->GetSpacing()[0];
103  else if (m_QBallImage->GetSpacing()[1] < m_QBallImage->GetSpacing()[2])
104  minSpacing = m_QBallImage->GetSpacing()[1];
105  else
106  minSpacing = m_QBallImage->GetSpacing()[2];
107  float m_ParticleLength = 1.5*minSpacing;
108  float m_ParticleWidth = 0.5*minSpacing;
109 
110  // seed random generators
112  if (m_RandomSeed>-1)
113  randGen->SetSeed(m_RandomSeed);
114  else
115  randGen->SetSeed();
116 
117  // instantiate all necessary components
118  SphereInterpolator* interpolator = new SphereInterpolator(m_LutPath);
119  // handle lookup table not found cases
120  if( !interpolator->IsInValidState() )
121  {
122  m_IsInValidState = false;
123  m_AbortTracking = true;
124  m_BuildFibers = false;
125  mitkThrow() << "Unable to load lookup tables.";
126  }
127  ParticleGrid* particleGrid = new ParticleGrid(m_MaskImage, m_ParticleLength, m_ParticleGridCellCapacity);
128  GibbsEnergyComputer* encomp = new GibbsEnergyComputer(m_QBallImage, m_MaskImage, particleGrid, interpolator, randGen);
129 
130  // EnergyComputer* encomp = new EnergyComputer(m_QBallImage, m_MaskImage, particleGrid, interpolator, randGen);
131  MetropolisHastingsSampler* sampler = new MetropolisHastingsSampler(particleGrid, encomp, randGen, m_CurvatureThreshold);
132 
133  float alpha = log(m_EndTemperature/m_StartTemperature);
134  m_ParticleWeight = 0.01;
135  int ppv = 0;
136  // main loop
137  int neededParts = 3000;
138  while (ppv<neededParts)
139  {
140  if (ppv<1000)
141  m_ParticleWeight /= 2;
142  else
143  m_ParticleWeight = ppv*m_ParticleWeight/neededParts;
144 
145  encomp->SetParameters(m_ParticleWeight,m_ParticleWidth,m_ConnectionPotential*m_ParticleLength*m_ParticleLength,m_CurvatureThreshold,m_InexBalance,m_ParticlePotential);
146  for( int step = 0; step < 10; step++ )
147  {
148  // update temperatur for simulated annealing process
149  float temperature = m_StartTemperature * exp(alpha*(((1.0)*step)/((1.0)*10)));
150  sampler->SetTemperature(temperature);
151 
152  for (unsigned long i=0; i<10000; i++)
153  sampler->MakeProposal();
154  }
155  ppv = particleGrid->m_NumParticles;
156  particleGrid->ResetGrid();
157  }
158  delete sampler;
159  delete encomp;
160  delete particleGrid;
161  delete interpolator;
162 
163  MITK_INFO << "GibbsTrackingFilter: finished estimating particle weight";
164 }
165 
166 // perform global tracking
167 template< class ItkQBallImageType >
169 {
170  TimeProbe preClock; preClock.Start();
171  // check if input is qball or tensor image and generate qball if necessary
172  if (m_QBallImage.IsNull() && m_TensorImage.IsNotNull())
173  {
175  filter->SetInput( m_TensorImage );
176  filter->Update();
177  m_QBallImage = filter->GetOutput();
178  }
179  else if (m_DuplicateImage) // generate local working copy of QBall image (if not disabled)
180  {
181  typedef itk::ImageDuplicator< ItkQBallImageType > DuplicateFilterType;
183  duplicator->SetInputImage( m_QBallImage );
184  duplicator->Update();
185  m_QBallImage = duplicator->GetOutput();
186  }
187 
188  // perform mean subtraction on odfs
189  typedef ImageRegionIterator< ItkQBallImageType > InputIteratorType;
190  InputIteratorType it(m_QBallImage, m_QBallImage->GetLargestPossibleRegion() );
191  it.GoToBegin();
192  while (!it.IsAtEnd())
193  {
194  itk::OrientationDistributionFunction<float, QBALL_ODFSIZE> odf(it.Get().GetDataPointer());
195  float mean = odf.GetMeanValue();
196  odf -= mean;
197  it.Set(odf.GetDataPointer());
198  ++it;
199  }
200 
201  // check if mask image is given if it needs resampling
202  PrepareMaskImage();
203 
204  // load parameter file
205  LoadParameters();
206 
207  // prepare parameters
208  float minSpacing;
209  if(m_QBallImage->GetSpacing()[0]<m_QBallImage->GetSpacing()[1] && m_QBallImage->GetSpacing()[0]<m_QBallImage->GetSpacing()[2])
210  minSpacing = m_QBallImage->GetSpacing()[0];
211  else if (m_QBallImage->GetSpacing()[1] < m_QBallImage->GetSpacing()[2])
212  minSpacing = m_QBallImage->GetSpacing()[1];
213  else
214  minSpacing = m_QBallImage->GetSpacing()[2];
215 
216  if(m_ParticleLength == 0)
217  m_ParticleLength = 1.5*minSpacing;
218  if(m_ParticleWidth == 0)
219  m_ParticleWidth = 0.5*minSpacing;
220 
221  if(m_ParticleWeight == 0)
222  EstimateParticleWeight();
223 
224  float alpha = log(m_EndTemperature/m_StartTemperature);
225 
226  if (m_CurvatureThreshold < mitk::eps)
227  m_CurvatureThreshold = 0;
228 
229  // seed random generators
231  if (m_RandomSeed>-1)
232  randGen->SetSeed(m_RandomSeed);
233  else
234  randGen->SetSeed();
235 
236  // load sphere interpolator to evaluate the ODFs
237  SphereInterpolator* interpolator = new SphereInterpolator(m_LutPath);
238 
239  // handle lookup table not found cases
240  if( !interpolator->IsInValidState() )
241  {
242  m_IsInValidState = false;
243  m_AbortTracking = true;
244  m_BuildFibers = false;
245  mitkThrow() << "Unable to load lookup tables.";
246  }
247  // initialize the actual tracking components (ParticleGrid, Metropolis Hastings Sampler and Energy Computer)
248  ParticleGrid* particleGrid;
249  GibbsEnergyComputer* encomp;
250  MetropolisHastingsSampler* sampler;
251  try{
252  particleGrid = new ParticleGrid(m_MaskImage, m_ParticleLength, m_ParticleGridCellCapacity);
253  encomp = new GibbsEnergyComputer(m_QBallImage, m_MaskImage, particleGrid, interpolator, randGen);
254  encomp->SetParameters(m_ParticleWeight,m_ParticleWidth,m_ConnectionPotential*m_ParticleLength*m_ParticleLength,m_CurvatureThreshold,m_InexBalance,m_ParticlePotential);
255  sampler = new MetropolisHastingsSampler(particleGrid, encomp, randGen, m_CurvatureThreshold);
256  }
257  catch(...)
258  {
259  MITK_ERROR << "Particle grid allocation failed. Not enough memory? Try to increase the particle length.";
260  m_IsInValidState = false;
261  m_AbortTracking = true;
262  m_BuildFibers = false;
263  return;
264  }
265 
266  MITK_INFO << "----------------------------------------";
267  MITK_INFO << "Iterations: " << m_Iterations;
268  MITK_INFO << "Particle length: " << m_ParticleLength;
269  MITK_INFO << "Particle width: " << m_ParticleWidth;
270  MITK_INFO << "Particle weight: " << m_ParticleWeight;
271  MITK_INFO << "Start temperature: " << m_StartTemperature;
272  MITK_INFO << "End temperature: " << m_EndTemperature;
273  MITK_INFO << "In/Ex balance: " << m_InexBalance;
274  MITK_INFO << "Min. fiber length: " << m_MinFiberLength;
275  MITK_INFO << "Curvature threshold: " << m_CurvatureThreshold;
276  MITK_INFO << "Random seed: " << m_RandomSeed;
277  MITK_INFO << "----------------------------------------";
278 
279  // main loop
280  preClock.Stop();
281  TimeProbe clock; clock.Start();
282  m_NumAcceptedFibers = 0;
283  m_CurrentIteration = 0;
284  boost::progress_display disp(m_Iterations);
285  if (!m_AbortTracking)
286  while (m_CurrentIteration<m_Iterations)
287  {
288  ++disp;
289  m_CurrentIteration++;
290  if (m_AbortTracking)
291  break;
292 
293  // update temperatur for simulated annealing process
294  float temperature = m_StartTemperature * exp(alpha*m_CurrentIteration/m_Iterations);
295  sampler->SetTemperature(temperature);
296 
297 
298  sampler->MakeProposal();
299 
300  m_ProposalAcceptance = (float)sampler->GetNumAcceptedProposals()/m_CurrentIteration;
301  m_NumParticles = particleGrid->m_NumParticles;
302  m_NumConnections = particleGrid->m_NumConnections;
303 
304  if (m_BuildFibers)
305  {
306  FiberBuilder fiberBuilder(particleGrid, m_MaskImage);
307  m_FiberPolyData = fiberBuilder.iterate(m_MinFiberLength);
308  m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines();
309  m_BuildFibers = false;
310  }
311 
312  if (m_AbortTracking)
313  break;
314  }
315  if (m_AbortTracking)
316  {
317  FiberBuilder fiberBuilder(particleGrid, m_MaskImage);
318  m_FiberPolyData = fiberBuilder.iterate(m_MinFiberLength);
319  m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines();
320  }
321  clock.Stop();
322 
323  delete sampler;
324  delete encomp;
325  delete interpolator;
326  delete particleGrid;
327  m_AbortTracking = true;
328  m_BuildFibers = false;
329 
330  int h = clock.GetTotal()/3600;
331  int m = ((int)clock.GetTotal()%3600)/60;
332  int s = (int)clock.GetTotal()%60;
333  MITK_INFO << "GibbsTrackingFilter: finished gibbs tracking in " << h << "h, " << m << "m and " << s << "s";
334  m = (int)preClock.GetTotal()/60;
335  s = (int)preClock.GetTotal()%60;
336  MITK_INFO << "GibbsTrackingFilter: preparation of the data took " << m << "m and " << s << "s";
337  MITK_INFO << "GibbsTrackingFilter: " << m_NumAcceptedFibers << " fibers accepted";
338 
339 // sampler->PrintProposalTimes();
340 
341  SaveParameters();
342 }
343 
344 template< class ItkQBallImageType >
346 {
347  if(m_MaskImage.IsNull())
348  {
349  MITK_INFO << "GibbsTrackingFilter: generating default mask image";
350  m_MaskImage = ItkFloatImageType::New();
351  m_MaskImage->SetSpacing( m_QBallImage->GetSpacing() );
352  m_MaskImage->SetOrigin( m_QBallImage->GetOrigin() );
353  m_MaskImage->SetDirection( m_QBallImage->GetDirection() );
354  m_MaskImage->SetRegions( m_QBallImage->GetLargestPossibleRegion() );
355  m_MaskImage->Allocate();
356  m_MaskImage->FillBuffer(1.0);
357  }
358  else if ( m_MaskImage->GetLargestPossibleRegion().GetSize()[0]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[0] ||
359  m_MaskImage->GetLargestPossibleRegion().GetSize()[1]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[1] ||
360  m_MaskImage->GetLargestPossibleRegion().GetSize()[2]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[2] ||
361  m_MaskImage->GetSpacing()[0]!=m_QBallImage->GetSpacing()[0] ||
362  m_MaskImage->GetSpacing()[1]!=m_QBallImage->GetSpacing()[1] ||
363  m_MaskImage->GetSpacing()[2]!=m_QBallImage->GetSpacing()[2] )
364  {
365  MITK_INFO << "GibbsTrackingFilter: resampling mask image";
366  typedef itk::ResampleImageFilter< ItkFloatImageType, ItkFloatImageType, float > ResamplerType;
368  resampler->SetOutputSpacing( m_QBallImage->GetSpacing() );
369  resampler->SetOutputOrigin( m_QBallImage->GetOrigin() );
370  resampler->SetOutputDirection( m_QBallImage->GetDirection() );
371  resampler->SetSize( m_QBallImage->GetLargestPossibleRegion().GetSize() );
372 
373  resampler->SetInput( m_MaskImage );
374  resampler->SetDefaultPixelValue(0.0);
375  resampler->Update();
376  m_MaskImage = resampler->GetOutput();
377  MITK_INFO << "GibbsTrackingFilter: resampling finished";
378  }
379 }
380 
381 // load tracking paramters from xml file (.gtp)
382 template< class ItkQBallImageType >
384 {
385  m_AbortTracking = true;
386  try
387  {
388  if( m_LoadParameterFile.length()==0 )
389  {
390  m_AbortTracking = false;
391  return true;
392  }
393 
394  MITK_INFO << "GibbsTrackingFilter: loading parameter file " << m_LoadParameterFile;
395 
396  TiXmlDocument doc( m_LoadParameterFile );
397  doc.LoadFile();
398 
399  TiXmlHandle hDoc(&doc);
400  TiXmlElement* pElem;
401  TiXmlHandle hRoot(0);
402 
403  pElem = hDoc.FirstChildElement().Element();
404  hRoot = TiXmlHandle(pElem);
405  pElem = hRoot.FirstChildElement("parameter_set").Element();
406 
407  string iterations(pElem->Attribute("iterations"));
408  m_Iterations = boost::lexical_cast<double>(iterations);
409 
410  string particleLength(pElem->Attribute("particle_length"));
411  m_ParticleLength = boost::lexical_cast<float>(particleLength);
412 
413  string particleWidth(pElem->Attribute("particle_width"));
414  m_ParticleWidth = boost::lexical_cast<float>(particleWidth);
415 
416  string partWeight(pElem->Attribute("particle_weight"));
417  m_ParticleWeight = boost::lexical_cast<float>(partWeight);
418 
419  string startTemp(pElem->Attribute("temp_start"));
420  m_StartTemperature = boost::lexical_cast<float>(startTemp);
421 
422  string endTemp(pElem->Attribute("temp_end"));
423  m_EndTemperature = boost::lexical_cast<float>(endTemp);
424 
425  string inExBalance(pElem->Attribute("inexbalance"));
426  m_InexBalance = boost::lexical_cast<float>(inExBalance);
427 
428  string fiberLength(pElem->Attribute("fiber_length"));
429  m_MinFiberLength = boost::lexical_cast<float>(fiberLength);
430 
431  string curvThres(pElem->Attribute("curvature_threshold"));
432  m_CurvatureThreshold = cos(boost::lexical_cast<float>(curvThres)*M_PI/180);
433  m_AbortTracking = false;
434  MITK_INFO << "GibbsTrackingFilter: parameter file loaded successfully";
435  return true;
436  }
437  catch(...)
438  {
439  MITK_INFO << "GibbsTrackingFilter: could not load parameter file";
440  return false;
441  }
442 }
443 
444 // save current tracking paramters to xml file (.gtp)
445 template< class ItkQBallImageType >
447 {
448  try
449  {
450  if( m_SaveParameterFile.length()==0 )
451  {
452  MITK_INFO << "GibbsTrackingFilter: no filename specified to save parameters";
453  return true;
454  }
455 
456  MITK_INFO << "GibbsTrackingFilter: saving parameter file " << m_SaveParameterFile;
457 
458  TiXmlDocument documentXML;
459  TiXmlDeclaration* declXML = new TiXmlDeclaration( "1.0", "", "" );
460  documentXML.LinkEndChild( declXML );
461 
462  TiXmlElement* mainXML = new TiXmlElement("global_tracking_parameter_file");
463  mainXML->SetAttribute("file_version", "0.1");
464  documentXML.LinkEndChild(mainXML);
465 
466  TiXmlElement* paramXML = new TiXmlElement("parameter_set");
467  paramXML->SetAttribute("iterations", boost::lexical_cast<string>(m_Iterations));
468  paramXML->SetAttribute("particle_length", boost::lexical_cast<string>(m_ParticleLength));
469  paramXML->SetAttribute("particle_width", boost::lexical_cast<string>(m_ParticleWidth));
470  paramXML->SetAttribute("particle_weight", boost::lexical_cast<string>(m_ParticleWeight));
471  paramXML->SetAttribute("temp_start", boost::lexical_cast<string>(m_StartTemperature));
472  paramXML->SetAttribute("temp_end", boost::lexical_cast<string>(m_EndTemperature));
473  paramXML->SetAttribute("inexbalance", boost::lexical_cast<string>(m_InexBalance));
474  paramXML->SetAttribute("fiber_length", boost::lexical_cast<string>(m_MinFiberLength));
475  paramXML->SetAttribute("curvature_threshold", boost::lexical_cast<string>(m_CurvatureThreshold));
476  mainXML->LinkEndChild(paramXML);
477 
478  if(!boost::algorithm::ends_with(m_SaveParameterFile, ".gtp"))
479  m_SaveParameterFile.append(".gtp");
480  documentXML.SaveFile( m_SaveParameterFile );
481 
482  MITK_INFO << "GibbsTrackingFilter: parameter file saved successfully";
483  return true;
484  }
485  catch(...)
486  {
487  MITK_INFO << "GibbsTrackingFilter: could not save parameter file";
488  return false;
489  }
490 }
491 
492 }
493 
494 
495 
496 
itk::SmartPointer< Self > Pointer
#define MITK_INFO
Definition: mitkLogMacros.h:22
#define MITK_ERROR
Definition: mitkLogMacros.h:24
Contains and manages particles.
ComponentType GetMeanValue() const
FiberPolyDataType GetFiberBundle()
Output fibers.
Lookuptable based trilinear interpolation of spherically arranged scalar values.
vtkSmartPointer< vtkPolyData > iterate(int minFiberLength)
vtkSmartPointer< vtkPolyData > FiberPolyDataType
#define mitkThrow()
Gnerates actual fiber structure (vtkPolyData) from the particle grid content.
void SetParameters(float particleWeight, float particleWidth, float connectionPotential, float curvThres, float inexBalance, float particlePotential)
Generates ne proposals of particle configurations.
MITKCORE_EXPORT const ScalarType eps
ODF lookuptable based energy computer.
void MakeProposal()
make proposal for birth/death/shift/connection of particles
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.