OpenWalnut  1.5.0dev
WMHARDIToSphericalHarmonics.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <cmath>
26 #include <fstream>
27 #include <iostream>
28 #include <memory>
29 #include <string>
30 #include <utility> // for std::pair
31 #include <vector>
32 
33 #include <boost/math/special_functions/spherical_harmonic.hpp>
34 #include <boost/thread/thread.hpp>
35 
36 #include "WMHARDIToSphericalHarmonics.h"
37 #include "WMHARDIToSphericalHarmonics.xpm"
38 #include "WSphericalHarmonicsCoefficientsThread.h"
39 #include "core/common/WAssert.h"
40 #include "core/common/WLimits.h"
41 #include "core/common/WProgress.h"
42 #include "core/common/math/WLinearAlgebraFunctions.h"
43 #include "core/common/math/WMatrix.h"
44 #include "core/common/math/WSymmetricSphericalHarmonic.h"
45 #include "core/common/math/WUnitSphereCoordinates.h"
46 #include "core/common/math/linearAlgebra/WVectorFixed.h"
47 #include "core/dataHandler/WDataHandler.h"
48 #include "core/dataHandler/WSubject.h"
49 #include "core/kernel/WKernel.h"
50 
51 W_LOADABLE_MODULE( WMHARDIToSphericalHarmonics )
52 
54  WModule()
55 {
56 }
57 
59 {
61 }
62 
63 std::shared_ptr< WModule > WMHARDIToSphericalHarmonics::factory() const
64 {
65  return std::shared_ptr< WModule >( new WMHARDIToSphericalHarmonics() );
66 }
67 
69 {
70  return WMHARDIToSphericalHarmonics_xpm;
71 }
72 
73 const std::string WMHARDIToSphericalHarmonics::getName() const
74 {
75  return "Spherical Harmonic Calculator";
76 }
77 
79 {
80  return "This module creates a Spherical Harmonics data set from raw HARDI data ";
81 }
82 
84 {
85  enum RECONSTRUCTION_TYPE
86  {
87  DEFAULT = 0,
88  CSA = 1
89  };
90  RECONSTRUCTION_TYPE reconstructionType( DEFAULT );
91 
92  m_moduleState.setResetable( true, true );
93  m_moduleState.add( m_input->getDataChangedCondition() );
95 
96  ready();
97  debugLog() << "Module is now ready.";
98 
99  debugLog() << "Entering main loop";
100  while( !m_shutdownFlag() )
101  {
102  debugLog() << "Waiting ...";
104 
105  if( m_shutdownFlag() )
106  {
107  break;
108  }
109 
110  std::shared_ptr< WDataSetRawHARDI > newDataSet = m_input->getData();
111  bool dataChanged = ( m_dataSet != newDataSet );
112  bool dataValid = ( newDataSet != NULL );
113 
114  switch( m_reconstructionTypeProp->get( true ).getItemIndexOfSelected( 0 ) )
115  {
116  case 0:
117  m_doFunkRadonTransformation->setHidden( false );
118  m_doErrorCalculation->setHidden( false );
119  m_doResidualCalculation->setHidden( false );
120  m_doNormalisation->setHidden( false );
121  m_CSADelta1->setHidden();
122  m_CSADelta2->setHidden();
123  reconstructionType = DEFAULT;
124  break;
125  case 1:
126  m_doFunkRadonTransformation->setHidden();
127  m_doErrorCalculation->setHidden();
128  m_doResidualCalculation->setHidden();
129  m_doNormalisation->setHidden();
130  m_CSADelta1->setHidden( false );
131  m_CSADelta2->setHidden( false );
132  reconstructionType = CSA;
133  break;
134  }
135 
136  if( dataChanged && dataValid )
137  {
138  debugLog() << "Received Data.";
139  m_dataSet = newDataSet;
140 
141  // calculate order from SHCoefficients.size:
142  // - this is done by reversing the R=(l+1)*(l+2)/2 formula from the Descoteaux paper
143  double q = std::sqrt( 0.25 + 2.0 * static_cast<double>( m_dataSet->getNumberOfMeasurements() ) ) - 1.5;
144  m_order->setMax( static_cast<unsigned int>( q ) % 2 == 1 ? static_cast<unsigned int>( q ) - 3 : static_cast<unsigned int>( q ) - 2 );
145 
146  // reset to order 2 if the input hardi data does not allow for higher orders
147  m_order->ensureValidity( 2 );
148  }
149 
150 // if( dataChanged && dataValid )
151  if( dataValid )
152  {
153  debugLog() << "Data changed. Recalculating output.";
154 
155  // Calculate new data
156  std::shared_ptr< WDataSetSphericalHarmonics > newData;
157 
158  // **********************************************
159  // * Determine usable gradients and its indices *
160  // **********************************************
161  // store indices for S0 signal
162  std::vector< size_t > S0Indexes;
163  // determine a set of indices of which the gradient is not zero
164  debugLog() << "Determine usable gradients." << std::endl;
165  std::vector< size_t > validIndices;
166  for( size_t i = 0; i < m_dataSet->getNumberOfMeasurements(); i++ )
167  {
168  const WVector3d& grad = m_dataSet->getGradient( i );
169  if( ( grad[ 0 ] != 0.0 ) || ( grad[ 1 ] != 0.0 ) || ( grad[ 2 ] != 0.0 ) )
170  {
171  validIndices.push_back( i );
172  }
173  else
174  {
175  S0Indexes.push_back( i );
176  }
177  }
178  debugLog() << "Found " << validIndices.size() << " usable gradients." << std::endl;
179  debugLog() << "Found " << S0Indexes.size() << " zero gradients." << std::endl;
180 
181  if( S0Indexes.size() == 0 )
182  {
183  errorLog() << "No entry with zero gradient. Can't get S0 (basis) signal.";
184  continue;
185  }
186 
187  // build vector with gradients != 0
188  std::vector< WVector3d > gradients;
189  for( std::vector< size_t >::const_iterator it = validIndices.begin(); it != validIndices.end(); it++ )
190  {
191  gradients.push_back( m_dataSet->getGradient( *it ) );
192  }
193  int order = m_order->get( true );
194 
195 
197  parameter.m_valueSet = m_dataSet->getValueSet();
198  parameter.m_validIndices = validIndices;
199  parameter.m_S0Indexes = S0Indexes;
200  parameter.m_order = order;
201  parameter.m_gradients = gradients;
202  parameter.m_doResidualCalculation = m_doResidualCalculation->get( true );
203  parameter.m_doErrorCalculation = m_doErrorCalculation->get( true ) || parameter.m_doResidualCalculation;
204  // parameter.m_regularisationFactorLambda = m_regularisationFactorLambda->get( true );
205  parameter.m_bDiffusionWeightingFactor = m_dataSet->getDiffusionBValue();
206  parameter.m_normalize = m_doNormalisation->get( true );
207  parameter.m_csa = ( reconstructionType == CSA );
208  parameter.m_doFunkRadonTransformation = m_doFunkRadonTransformation->get( true );
209  WMatrix< double > transformMatrix(
211  order,
212  m_regularisationFactorLambda->get( true ),
213  parameter.m_doFunkRadonTransformation ) );
214  if( reconstructionType == CSA )
215  {
216  parameter.m_doResidualCalculation = false;
217  parameter.m_doErrorCalculation = false;
218  parameter.m_normalize = false;
219  parameter.m_doFunkRadonTransformation = false;
220  parameter.m_CSADelta1 = m_CSADelta1->get( true );
221  parameter.m_CSADelta2 = m_CSADelta2->get( true );
223  gradients,
224  order,
225  m_regularisationFactorLambda->get( true ) );
226  }
227  parameter.m_TransformMatrix = std::shared_ptr< WMatrix< double > >( new WMatrix< double >( transformMatrix ) );
228 
229  //to show progess
230  parameter.m_progress = std::shared_ptr< WProgress >( new WProgress( "Creating Spherical Harmonics",
231  m_dataSet->getValueSet()->size() ) );
232  m_progress->addSubProgress( parameter.m_progress );
233 
234  debugLog() << "Starting calculation.";
235 
236  HARDICalculation hc( parameter, m_multiThreaded->get( true ), m_dataSet->getGrid(), gradients );
237  HARDICalculation::result_type res = m_dataSet->getValueSet()->applyFunction( hc );
238 
239  debugLog() << "Got results.";
240 
241  // notify output about new data
242  m_output->updateData( res.first );
243 
244  // create final output data
245  if( parameter.m_doResidualCalculation )
246  {
247  m_outputResiduals->updateData( res.second );
248  }
249  }
250  }
251 
252  debugLog() << "Finished.";
253 }
254 
256 {
257  // initialize connectors
258  m_input = std::shared_ptr< WModuleInputData < WDataSetRawHARDI > >(
259  new WModuleInputData< WDataSetRawHARDI >( shared_from_this(), "in", "Dataset to compute the spherical harmonics for." ) );
260 
261  // add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
263 
264  m_output = std::shared_ptr< WModuleOutputData< WDataSetSphericalHarmonics > >(
265  new WModuleOutputData< WDataSetSphericalHarmonics >( shared_from_this(), "out", "The grid with the calculated spherical harmonics." ) );
266 
268 
269  m_outputResiduals = std::shared_ptr< WModuleOutputData< WDataSetRawHARDI > >(
270  new WModuleOutputData< WDataSetRawHARDI >( shared_from_this(), "residualsOut", "The residual of the reprojection." ) );
271 
273 
274  // call WModules initialization
276 }
277 
279 {
280  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
281 
282 
283  m_reconstructionTypes = std::shared_ptr< WItemSelection >( new WItemSelection() );
284  m_reconstructionTypes->addItem( "default", "default" );
285  m_reconstructionTypes->addItem( "Constant solid angle", "Constant Solid Angle" );
286  m_reconstructionTypeProp = m_properties->addProperty( "Reconstruction Type",
287  "Which reconstruction type will be used?",
288  m_reconstructionTypes->getSelector( 0 ),
289  m_propCondition );
290 
292 
293  m_order = m_properties->addProperty( "Order of Spherical Harmonics",
294  "Order of Spherical Harmonics",
295  4,
296  m_propCondition );
297 
298  m_doFunkRadonTransformation = m_properties->addProperty( "Funk-Radon-Transformation",
299  "Apply the Funk-Radon-Transformation to the calculated Spherical Harmonics.",
300  false,
301  m_propCondition );
302 
303  m_doErrorCalculation = m_properties->addProperty( "Error Calculation",
304  "Calculate the reprojection error of the spherical harmonics.",
305  false,
306  m_propCondition );
307 
308  m_doResidualCalculation = m_properties->addProperty( "Residual Calculation",
309  "Indicating whether the reprojection errors is stored into a separate dataset.",
310  false,
311  m_propCondition );
312 
313  m_regularisationFactorLambda = m_properties->addProperty( "Regularisation Factor Lambda",
314  "Factor for Regularisation",
315  0.0,
316  m_propCondition );
317 
318  m_regularisationFactorLambda->setMin( 0.0 );
319  m_regularisationFactorLambda->setMax( 100000.0 );
320 
321  m_multiThreaded = m_properties->addProperty( "Multi-Threaded", "Calculate spherical harmonics in multiple threads.", true, m_propCondition );
322 
323  m_doNormalisation = m_properties->addProperty( "Normalizsation",
324  "Normalise the HARDI measurements to 0 to 1 for each voxel.",
325  false,
326  m_propCondition );
327 
328  m_CSADelta1 = m_properties->addProperty( "delta 1",
329  "delta1 threshold",
330  0.01,
331  m_propCondition );
332  m_CSADelta1->setMin( 0.0 );
333  m_CSADelta1->setMax( 1.0 );
334 
335  m_CSADelta2 = m_properties->addProperty( "delta 2",
336  "delta2 threshold",
337  0.01,
338  m_propCondition );
339  m_CSADelta2->setMin( 0.0 );
340  m_CSADelta2->setMax( 1.0 );
341 
342 // vista-daten
343  // order Gesamtfehler
344  // 0 8,7%
345  // 1 8,7%
346  // 2 6,5%
347  // 3 6,5%
348  // 4 5,8%
349  // 5 5,8%
350  // 6 4,9%
351  // 8 3,3%
352 
353 // nifty-daten
354  // order Gesamtfehler
355  // 0
356  // 2 inf
357  // 4 5,8%
358  // 6 4,9%
359  // 8 3,3%
360 
361  m_order->setMin( 0 );
362  m_order->setMax( 4 );
363  m_order->addConstraint( std::shared_ptr< evenInt >( new evenInt ) );
364 }
365 
367  const WPVBaseTypes::PV_INT& value )
368 {
369  return ( value % 2 == 0 );
370 }
371 
372 std::shared_ptr< WPropertyVariable< WPVBaseTypes::PV_INT >::PropertyConstraint > WMHARDIToSphericalHarmonics::evenInt::clone()
373 {
374  return std::shared_ptr< WPropertyVariable< WPVBaseTypes::PV_INT >::PropertyConstraint >( new WMHARDIToSphericalHarmonics::evenInt( *this ) );
375 }
376 
378  bool multiThreaded, std::shared_ptr< WGrid > grid,
379  std::vector< WVector3d > const& gradients )
380  : m_parameter( threadParams ),
381  m_multiThreaded( multiThreaded ),
382  m_grid( grid ),
383  m_gradients( gradients )
384 {
385 }
386 
388 {
389 }
virtual void wait() const
Wait for the condition.
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
virtual void add(std::shared_ptr< WCondition > condition)
Adds another condition to the set of conditions to wait for.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
A class containing a list of named items.
Functor that does multithreaded spherical harmonic fitting.
HARDICalculation(WSphericalHarmonicsCoefficientsThread<>::ThreadParameter threadParams, bool multiThreaded, std::shared_ptr< WGrid > grid, std::vector< WVector3d > const &gradients)
Constructor.
This class is derived from PropertyConstraint and ensures that only even values are valid.
virtual bool accept(std::shared_ptr< WPropertyVariable< WPVBaseTypes::PV_INT > > property, const WPVBaseTypes::PV_INT &value)
You need to overwrite this method.
virtual std::shared_ptr< WPropertyVariable< WPVBaseTypes::PV_INT >::PropertyConstraint > clone()
Method to clone the constraint and create a new one with the correct dynamic type.
Module for the creation of a spherical harmonic data set from raw HARDI data.
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
virtual const std::string getName() const
Gives back the name of this module.
WPropInt m_order
Property holding the order of the spherical harmonics.
std::shared_ptr< WModuleOutputData< WDataSetSphericalHarmonics > > m_output
Output connector provided by this module.
WPropBool m_doNormalisation
Property indicating whether the measurements are normalized from 0 to 1 for each voxel.
WPropSelection m_reconstructionTypeProp
To choose the reconstruction type.
std::shared_ptr< WDataSetRawHARDI > m_dataSet
This is a pointer to the dataset the module is currently working on.
std::shared_ptr< WModuleInputData< WDataSetRawHARDI > > m_input
Input connector required by this module.
virtual void properties()
Initialize the properties for this module.
WPropBool m_doResidualCalculation
Property indicating whether the reprojection error (measurement relative to spherical harmonic calcul...
WMHARDIToSphericalHarmonics()
Standard constructor.
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
virtual const std::string getDescription() const
Gives back a description of this module.
WPropBool m_doFunkRadonTransformation
Property indicating whether to do the Funk-Radon-transformation with calculated spherical harmonics.
virtual void moduleMain()
Entry point after loading the module.
WPropDouble m_CSADelta1
Delta1 value for the constant solid angle reconstruction.
WPropBool m_doErrorCalculation
Property indicating whether a reprojection error of the spherical harmonics is calculated.
std::shared_ptr< WModuleOutputData< WDataSetRawHARDI > > m_outputResiduals
The reprojection error for each measurement.
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
WPropBool m_multiThreaded
Property indicating whether the spherical harmonics calculation is done multithreaded.
std::shared_ptr< WItemSelection > m_reconstructionTypes
A list of the selectable reconstruction types.
WPropDouble m_regularisationFactorLambda
Property holding the regularisation factor lambda.
virtual void connectors()
Initialize the connectors this module is using.
WPropDouble m_CSADelta2
Delta2 value for the constant solid angle reconstruction.
Class offering an instantiate-able data connection between modules.
Class offering an instantiate-able data connection between modules.
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
void removeConnectors()
Removes all connectors properly.
Definition: WModule.cpp:194
void addConnector(std::shared_ptr< WModuleInputConnector > con)
Adds the specified connector to the list of inputs.
Definition: WModule.cpp:108
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
wlog::WStreamedLogger errorLog() const
Logger instance for comfortable error logging.
Definition: WModule.cpp:570
std::shared_ptr< WProgressCombiner > m_progress
Progress indicator used as parent for all progress' of this module.
Definition: WModule.h:652
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
Class managing progress inside of modules.
Definition: WProgress.h:42
A named property class with a concrete type.
Module for the creation of a spherical harmonic data set from raw HARDI data.
Class for symmetric spherical harmonics The index scheme of the coefficients/basis values is like in ...
static WMatrix< T > getSHFittingMatrixForConstantSolidAngle(const std::vector< WMatrixFixed< T, 3, 1 > > &orientations, int order, T lambda)
This calculates the transformation/fitting matrix T like in the 2010 Aganj paper.
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
int32_t PV_INT
base type used for every WPVInt
void addTo(WPropSelection prop)
Add the PC_SELECTONLYONE constraint to the property.