OpenWalnut  1.5.0dev
WSymmetricSphericalHarmonic.h
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 #ifndef WSYMMETRICSPHERICALHARMONIC_H
26 #define WSYMMETRICSPHERICALHARMONIC_H
27 
28 #include <stdint.h>
29 
30 #include <cmath>
31 #include <vector>
32 
33 #include <boost/math/special_functions/spherical_harmonic.hpp>
34 
35 #include "core/common/WLogger.h"
36 #include "core/common/math/WGeometryFunctions.h"
37 #include "../exceptions/WPreconditionNotMet.h"
38 #include "linearAlgebra/WVectorFixed.h"
39 #include "WLinearAlgebraFunctions.h"
40 #include "WMath.h"
41 #include "WMatrix.h"
42 #include "WTensorSym.h"
43 #include "WUnitSphereCoordinates.h"
44 #include "WValue.h"
45 
46 
47 /**
48  * Class for symmetric spherical harmonics
49  * The index scheme of the coefficients/basis values is like in the Descoteaux paper "Regularized, Fast, and Robust Analytical Q-Ball Imaging".
50  */
51 template< typename T > class WSymmetricSphericalHarmonic // NOLINT
52 {
53 // friend class WSymmetricSphericalHarmonicTest;
54 public:
55  /**
56  * Default constructor.
57  */
59 
60  /**
61  * Constructor.
62  * \param SHCoefficients the initial coefficients (stored like in the mentioned Descoteaux paper).
63  */
64  explicit WSymmetricSphericalHarmonic( const WValue< T >& SHCoefficients );
65 
66  /**
67  * Destructor.
68  */
70 
71  /**
72  * Return the value on the sphere.
73  * \param theta angle for the position on the unit sphere
74  * \param phi angle for the position on the unit sphere
75  *
76  * \return value on sphere
77  */
78  T getValue( T theta, T phi ) const;
79 
80  /**
81  * Return the value on the sphere.
82  * \param coordinates for the position on the unit sphere
83  *
84  * \return value on sphere
85  */
86  T getValue( const WUnitSphereCoordinates< T >& coordinates ) const;
87 
88  /**
89  * Returns the used coefficients (stored like in the mentioned 2007 Descoteaux paper).
90  *
91  * \return coefficient list
92  */
93  const WValue< T >& getCoefficients() const;
94 
95  /**
96  * Returns the coefficients for Schultz' SH base.
97  *
98  * \return coefficient list
99  */
101 
102  /**
103  * Returns the coefficients for the complex base.
104  *
105  * \return coefficiend list
106  */
108 
109  /**
110  * Set the coeffs from complex base coeffs.
111  *
112  * \param coeffs The complex coefficients, sorted by order, then phase, ascending.
113  * \param order The order of the spherical harmonic.
114  */
115  void setFromComplex( WValue< std::complex< T > > const& coeffs, int order );
116 
117  /**
118  * Applies the Funk-Radon-Transformation. This is faster than matrix multiplication.
119  * ( O(n) instead of O(n²) )
120  *
121  * \param frtMat the frt matrix as calculated by calcFRTMatrix()
122  */
123  void applyFunkRadonTransformation( WMatrix< T > const& frtMat );
124 
125  /**
126  * Return the order of the spherical harmonic.
127  *
128  * \return order of SH
129  */
130  size_t getOrder() const;
131 
132  /**
133  * Calculate the generalized fractional anisotropy for this ODF.
134  *
135  * See: David S. Tuch, "Q-Ball Imaging", Magn. Reson. Med. 52, 2004, 1358-1372
136  *
137  * \note this only makes sense if this is an ODF, meaning funk-radon-transform was applied etc.
138  *
139  * \param orientations A vector of unit sphere coordinates.
140  *
141  * \return The generalized fractional anisotropy.
142  */
143  T calcGFA( std::vector< WUnitSphereCoordinates< T > > const& orientations ) const;
144 
145  /**
146  * Calculate the generalized fractional anisotropy for this ODF. This version of
147  * the function uses precomputed base functions (because calculating the base function values
148  * is rather expensive). Use this version if you want to compute the GFA for multiple ODFs
149  * with the same base functions. The base function Matrix can be computed using \see calcBMatrix().
150  *
151  * See: David S. Tuch, "Q-Ball Imaging", Magn. Reson. Med. 52, 2004, 1358-1372
152  *
153  * \note this only makes sense if this is an ODF, meaning funk-radon-transform was applied etc.
154  *
155  * \param B The matrix of SH base functions.
156  *
157  * \return The generalized fractional anisotropy.
158  */
159  T calcGFA( WMatrix< T > const& B ) const;
160 
161  /**
162  * This calculates the transformation/fitting matrix T like in the 2007 Descoteaux paper. The orientations are given as WMatrixFixed< T, 3, 1 >.
163  * \param orientations The vector with the used orientation on the unit sphere (usually the gradients of the HARDI)
164  * \param order The order of the spherical harmonics intended to create
165  * \param lambda Regularization parameter for smoothing matrix
166  * \param withFRT include the Funk-Radon-Transformation?
167  * \return Transformation matrix
168  */
169  static WMatrix< T > getSHFittingMatrix( const std::vector< WMatrixFixed< T, 3, 1 > >& orientations,
170  int order,
171  T lambda,
172  bool withFRT );
173 
174  /**
175  * This calculates the transformation/fitting matrix T like in the 2007 Descoteaux paper. The orientations are given as WUnitSphereCoordinates< T >.
176  * \param orientations The vector with the used orientation on the unit sphere (usually the gradients of the HARDI)
177  * \param order The order of the spherical harmonics intended to create
178  * \param lambda Regularization parameter for smoothing matrix
179  * \param withFRT include the Funk-Radon-Transformation?
180  * \return Transformation matrix
181  */
182  static WMatrix< T > getSHFittingMatrix( const std::vector< WUnitSphereCoordinates< T > >& orientations,
183  int order,
184  T lambda,
185  bool withFRT );
186 
187  /**
188  * This calculates the transformation/fitting matrix T like in the 2010 Aganj paper. The orientations are given as WUnitSphereCoordinates< T >.
189  * \param orientations The vector with the used orientation on the unit sphere (usually the gradients of the HARDI)
190  * \param order The order of the spherical harmonics intended to create
191  * \param lambda Regularization parameter for smoothing matrix
192  * \return Transformation matrix
193  */
194  static WMatrix< T > getSHFittingMatrixForConstantSolidAngle( const std::vector< WMatrixFixed< T, 3, 1 > >& orientations,
195  int order,
196  T lambda );
197 
198  /**
199  * This calculates the transformation/fitting matrix T like in the 2010 Aganj paper. The orientations are given as WUnitSphereCoordinates< T >.
200  * \param orientations The vector with the used orientation on the unit sphere (usually the gradients of the HARDI)
201  * \param order The order of the spherical harmonics intended to create
202  * \param lambda Regularization parameter for smoothing matrix
203  * \return Transformation matrix
204  */
205  static WMatrix< T > getSHFittingMatrixForConstantSolidAngle( const std::vector< WUnitSphereCoordinates< T > >& orientations,
206  int order,
207  T lambda );
208 
209  /**
210  * Calculates the base matrix B like in the dissertation of Descoteaux.
211  * \param orientations The vector with the used orientation on the unit sphere (usually the gradients of the HARDI)
212  * \param order The order of the spherical harmonics intended to create
213  * \return The base Matrix B
214  */
215  static WMatrix< T > calcBaseMatrix( const std::vector< WUnitSphereCoordinates< T > >& orientations, int order );
216 
217  /**
218  * Calculates the base matrix B for the complex spherical harmonics.
219  * \param orientations The vector with the used orientation on the unit sphere (usually the gradients of the HARDI)
220  * \param order The order of the spherical harmonics intended to create
221  * \return The base Matrix B
222  */
223  static WMatrix< std::complex< T > > calcComplexBaseMatrix( std::vector< WUnitSphereCoordinates< T > > const& orientations,
224  int order );
225  /**
226  * Calc eigenvalues for SH elements.
227  * \param order The order of the spherical harmonic
228  * \return The eigenvalues of the coefficients
229  */
230  static WValue< T > calcEigenvalues( size_t order );
231 
232  /**
233  * Calc matrix with the eigenvalues of the SH elements on its diagonal.
234  * \param order The order of the spherical harmonic
235  * \return The matrix with the eigenvalues of the coefficients
236  */
237  static WMatrix< T > calcMatrixWithEigenvalues( size_t order );
238 
239  /**
240  * This calcs the smoothing matrix L from the 2007 Descoteaux Paper "Regularized, Fast, and Robust Analytical Q-Ball Imaging"
241  * \param order The order of the spherical harmonic
242  * \return The smoothing matrix L
243  */
244  static WMatrix< T > calcSmoothingMatrix( size_t order );
245 
246  /**
247  * Calculates the Funk-Radon-Transformation-Matrix P from the 2007 Descoteaux Paper "Regularized, Fast, and Robust Analytical Q-Ball Imaging"
248  * \param order The order of the spherical harmonic
249  * \return The Funk-Radon-Matrix P
250  */
251  static WMatrix< T > calcFRTMatrix( size_t order );
252 
253  /**
254  * Calculates a matrix that converts spherical harmonics to symmetric tensors of equal or lower order.
255  *
256  * \param order The order of the symmetric tensor.
257  *
258  * \return the conversion matrix
259  */
260  static WMatrix< T > calcSHToTensorSymMatrix( std::size_t order );
261 
262  /**
263  * Calculates a matrix that converts spherical harmonics to symmetric tensors of equal or lower order.
264  *
265  * \param order The order of the symmetric tensor.
266  * \param orientations A vector of at least (orderTensor+1) * (orderTensor+2) / 2 orientations.
267  *
268  * \return the conversion matrix
269  */
270  static WMatrix< T > calcSHToTensorSymMatrix( std::size_t order, const std::vector< WUnitSphereCoordinates< T > >& orientations );
271 
272  /**
273  * Normalize this SH in place.
274  */
275  void normalize();
276 
277 protected:
278 private:
279  /** order of the spherical harmonic */
280  size_t m_order;
281 
282  /** coefficients of the spherical harmonic */
284 };
285 
286 template< typename T >
288  m_order( 0 ),
289  m_SHCoefficients( 0 )
290 {
291 }
292 
293 template< typename T >
295  m_SHCoefficients( SHCoefficients )
296 {
297  // calculate order from SHCoefficients.size:
298  // - this is done by reversing the R=(l+1)*(l+2)/2 formula from the Descoteaux paper
299  T q = std::sqrt( 0.25 + 2.0 * static_cast< T >( m_SHCoefficients.size() ) ) - 1.5;
300  m_order = static_cast<size_t>( q );
301 }
302 
303 template< typename T >
305 {
306 }
307 
308 template< typename T >
310 {
311  T result = 0.0;
312  int j = 0;
313  const T rootOf2 = std::sqrt( 2.0 );
314  for( int k = 0; k <= static_cast<int>( m_order ); k+=2 )
315  {
316  // m = 1 .. k
317  for( int m = 1; m <= k; m++ )
318  {
319  j = ( k*k+k+2 ) / 2 + m;
320  result += m_SHCoefficients[ j-1 ] * rootOf2 *
321  static_cast< T >( std::pow( -1.0, m+1 ) ) * boost::math::spherical_harmonic_i( k, m, theta, phi );
322  }
323  // m = 0
324  result += m_SHCoefficients[ ( k*k+k+2 ) / 2 - 1 ] * boost::math::spherical_harmonic_r( k, 0, theta, phi );
325  // m = -k .. -1
326  for( int m = -k; m < 0; m++ )
327  {
328  j = ( k*k+k+2 ) / 2 + m;
329  result += m_SHCoefficients[ j-1 ] * rootOf2 * boost::math::spherical_harmonic_r( k, -m, theta, phi );
330  }
331  }
332  return result;
333 }
334 
335 template< typename T >
337 {
338  return getValue( coordinates.getTheta(), coordinates.getPhi() );
339 }
340 
341 template< typename T >
343 {
344  return m_SHCoefficients;
345 }
346 
347 template< typename T >
349 {
350  WValue< T > res( m_SHCoefficients.size() );
351  size_t k = 0;
352  for( int l = 0; l <= static_cast< int >( m_order ); l += 2 )
353  {
354  for( int m = -l; m <= l; ++m )
355  {
356  res[ k ] = m_SHCoefficients[ k ];
357  if( m < 0 && ( ( -m ) % 2 == 1 ) )
358  {
359  res[ k ] *= -1.0;
360  }
361  else if( m > 0 && ( m % 2 == 0 ) )
362  {
363  res[ k ] *= -1.0;
364  }
365  ++k;
366  }
367  }
368  return res;
369 }
370 
371 template< typename T >
373 {
374  WValue< std::complex< T > > res( m_SHCoefficients.size() );
375  size_t k = 0;
376  T r = 1.0 / sqrt( 2.0 );
377  std::complex< T > i( 0.0, -1.0 );
378  for( int l = 0; l <= static_cast< int >( m_order ); l += 2 )
379  {
380  for( int m = -l; m <= l; ++m )
381  {
382  if( m == 0 )
383  {
384  res[ k ] = m_SHCoefficients[ k ];
385  }
386  else if( m < 0 )
387  {
388  res[ k ] += i * r * m_SHCoefficients[ k - 2 * m ];
389  res[ k ] += ( -m % 2 == 1 ? -r : r ) * m_SHCoefficients[ k ];
390  }
391  else if( m > 0 )
392  {
393  res[ k ] += i * ( -m % 2 == 0 ? -r : r ) * m_SHCoefficients[ k ];
394  res[ k ] += r * m_SHCoefficients[ k - 2 * m ];
395  }
396  ++k;
397  }
398  }
399  return res;
400 }
401 
402 template< typename T >
403 void WSymmetricSphericalHarmonic< T >::setFromComplex( WValue< std::complex< T > > const& coeffs, int order )
404 {
405  if( order < 0 || order % 2 == 1 )
406  {
407  return;
408  }
409 
410  m_order = order;
411  int elements = ( m_order + 1 ) * ( m_order + 2 ) / 2;
412  m_SHCoefficients.resize( elements );
413  for( int k = 0; k < elements; ++k )
414  {
415  m_SHCoefficients[ k ] = 0.0;
416  }
417 
418  size_t k = 0;
419  T r = 1.0 / sqrt( 2.0 );
420  std::complex< T > i( 0.0, -1.0 );
421  for( int l = 0; l <= static_cast< int >( m_order ); l += 2 )
422  {
423  if( ( l + 1 ) * ( l + 2 ) > 2 * static_cast< int >( coeffs.size() ) )
424  {
425  break;
426  }
427 
428  for( int m = -l; m <= l; ++m )
429  {
430  if( m == 0 )
431  {
432  m_SHCoefficients[ k ] = coeffs[ k ].real();
433  }
434  else if( m < 0 )
435  {
436  m_SHCoefficients[ k ] = std::complex< T >( r * coeffs[ k - 2 * m ] ).real();
437  m_SHCoefficients[ k ] += std::complex< T >( ( -m % 2 == 0 ? r : -r ) * coeffs[ k ] ).real();
438  }
439  else if( m > 0 )
440  {
441  m_SHCoefficients[ k ] = std::complex< T >( i * ( m % 2 == 1 ? -r : r ) * coeffs[ k ] ).real();
442  m_SHCoefficients[ k ] += std::complex< T >( -r * i * coeffs[ k - 2 * m ] ).real();
443  }
444  ++k;
445  }
446  }
447 }
448 
449 template< typename T >
450 T WSymmetricSphericalHarmonic< T >::calcGFA( std::vector< WUnitSphereCoordinates< T > > const& orientations ) const
451 {
452  T n = static_cast< T >( orientations.size() );
453  T d = 0.0;
454  T gfa = 0.0;
455  T mean = 0.0;
456  std::vector< T > v( orientations.size() );
457 
458  for( std::size_t i = 0; i < orientations.size(); ++i )
459  {
460  v[ i ] = getValue( orientations[ i ] );
461  mean += v[ i ];
462  }
463  mean /= n;
464 
465  for( std::size_t i = 0; i < orientations.size(); ++i )
466  {
467  T f = v[ i ];
468  // we use gfa as a temporary here
469  gfa += f * f;
470  f -= mean;
471  d += f * f;
472  }
473 
474  if( gfa == 0.0 || n <= 1.0 )
475  {
476  return 0.0;
477  }
478  // this is the real gfa
479  gfa = sqrt( ( n * d ) / ( ( n - 1.0 ) * gfa ) );
480 
481  WAssert( gfa >= -0.01 && gfa <= 1.01, "" );
482  if( gfa < 0.0 )
483  {
484  return 0.0;
485  }
486  else if( gfa > 1.0 )
487  {
488  return 1.0;
489  }
490  return gfa;
491 }
492 
493 template< typename T >
495 {
496  // sh coeffs
497  WMatrix< T > s( B.getNbCols(), 1 );
498  WAssert( B.getNbCols() == m_SHCoefficients.size(), "" );
499  for( std::size_t k = 0; k < B.getNbCols(); ++k )
500  {
501  s( k, 0 ) = m_SHCoefficients[ k ];
502  }
503  s = B * s;
504  WAssert( s.getNbRows() == B.getNbRows(), "" );
505  WAssert( s.getNbCols() == 1u, "" );
506 
507  T n = static_cast< T >( s.getNbRows() );
508  T d = 0.0;
509  T gfa = 0.0;
510  T mean = 0.0;
511  for( std::size_t i = 0; i < s.getNbRows(); ++i )
512  {
513  mean += s( i, 0 );
514  }
515  mean /= n;
516 
517  for( std::size_t i = 0; i < s.getNbRows(); ++i )
518  {
519  T f = s( i, 0 );
520  // we use gfa as a temporary here
521  gfa += f * f;
522  f -= mean;
523  d += f * f;
524  }
525 
526  if( gfa == 0.0 || n <= 1.0 )
527  {
528  return 0.0;
529  }
530  // this is the real gfa
531  gfa = sqrt( ( n * d ) / ( ( n - 1.0 ) * gfa ) );
532 
533  WAssert( gfa >= -0.01 && gfa <= 1.01, "" );
534  if( gfa < 0.0 )
535  {
536  return 0.0;
537  }
538  else if( gfa > 1.0 )
539  {
540  return 1.0;
541  }
542  return gfa;
543 }
544 
545 template< typename T >
547 {
548  WAssert( frtMat.getNbCols() == m_SHCoefficients.size(), "" );
549  WAssert( frtMat.getNbRows() == m_SHCoefficients.size(), "" );
550  // Funk-Radon-Transformation as in Descoteaux's thesis
551  for( size_t j = 0; j < m_SHCoefficients.size(); j++ )
552  {
553  m_SHCoefficients[ j ] *= frtMat( j, j );
554  }
555 }
556 
557 template< typename T >
559 {
560  return m_order;
561 }
562 
563 template< typename T >
565  int order,
566  T lambda,
567  bool withFRT )
568 {
569  // convert euclid 3d orientations/gradients to sphere coordinates
570  std::vector< WUnitSphereCoordinates< T > > sphereCoordinates;
571  for( typename std::vector< WMatrixFixed< T, 3, 1 > >::const_iterator it = orientations.begin(); it != orientations.end(); it++ )
572  {
573  sphereCoordinates.push_back( WUnitSphereCoordinates< T >( *it ) );
574  }
575  return WSymmetricSphericalHarmonic< T >::getSHFittingMatrix( sphereCoordinates, order, lambda, withFRT );
576 }
577 
578 template< typename T >
580  int order,
581  T lambda,
582  bool withFRT )
583 {
585  WMatrix< T > Bt( B.transposed() );
586  WMatrix< T > result( Bt*B );
587  if( lambda != 0.0 )
588  {
590  result += lambda*L;
591  }
592 
593  result = pseudoInverse( result )*Bt;
594  if( withFRT )
595  {
597  return ( P * result );
598  }
599  return result;
600 }
601 
602 template< typename T >
604  int order,
605  T lambda )
606 {
607  // convert euclid 3d orientations/gradients to sphere coordinates
608  std::vector< WUnitSphereCoordinates< T > > sphereCoordinates;
609  for( typename std::vector< WMatrixFixed< T, 3, 1 > >::const_iterator it = orientations.begin(); it != orientations.end(); it++ )
610  {
611  sphereCoordinates.push_back( WUnitSphereCoordinates< T >( *it ) );
612  }
613  return WSymmetricSphericalHarmonic< T >::getSHFittingMatrixForConstantSolidAngle( sphereCoordinates, order, lambda );
614 }
615 
616 template< typename T >
618  const std::vector< WUnitSphereCoordinates< T > >& orientations,
619  int order,
620  T lambda )
621 {
623  WMatrix< T > Bt( B.transposed() );
624  WMatrix< T > result( Bt*B );
625 
626  // regularisation
627  if( lambda != 0.0 )
628  {
630  result += lambda*L;
631  }
632 
633  result = pseudoInverse( result )*Bt;
634 
635  // multiply with eigenvalues of coefficients / Laplace-Beltrami operator
637  wlog::debug( "" ) << "LB: " << LB;
638  result = LB*result;
639  wlog::debug( "" ) << "LB*result: " << result;
640 
641  // apply FRT
643  result = P * result;
644  wlog::debug( "" ) << "P: " << P;
645  wlog::debug( "" ) << "P*result: " << result;
646 
647  // correction factor
648  T correctionFactor = 1.0 / ( 16.0 * std::pow( static_cast< T >( pi() ), 2 ) );
649  result *= correctionFactor;
650 
651  return result;
652 }
653 
654 
655 template< typename T >
657  int order )
658 {
659  WMatrix< T > B( orientations.size(), ( ( order + 1 ) * ( order + 2 ) ) / 2 );
660 
661  // calc B Matrix like in the 2007 Descoteaux paper ("Regularized, Fast, and Robust Analytical Q-Ball Imaging")
662  int j = 0;
663  const T rootOf2 = std::sqrt( 2.0 );
664  for( uint32_t row = 0; row < orientations.size(); row++ )
665  {
666  const T theta = orientations[row].getTheta();
667  const T phi = orientations[row].getPhi();
668  for( int k = 0; k <= order; k+=2 )
669  {
670  // m = 1 .. k
671  for( int m = 1; m <= k; m++ )
672  {
673  j = ( k * k + k + 2 ) / 2 + m;
674  B( row, j-1 ) = rootOf2 * static_cast< T >( std::pow( static_cast< T >( -1 ), m + 1 ) )
675  * boost::math::spherical_harmonic_i( k, m, theta, phi );
676  }
677  // m = 0
678  B( row, ( k * k + k + 2 ) / 2 - 1 ) = boost::math::spherical_harmonic_r( k, 0, theta, phi );
679  // m = -k .. -1
680  for( int m = -k; m < 0; m++ )
681  {
682  j = ( k * k + k + 2 ) / 2 + m;
683  B( row, j-1 ) = rootOf2*boost::math::spherical_harmonic_r( k, -m, theta, phi );
684  }
685  }
686  }
687  return B;
688 }
689 
690 template< typename T >
693 {
694  WMatrix< std::complex< T > > B( orientations.size(), ( ( order + 1 ) * ( order + 2 ) ) / 2 );
695 
696  for( std::size_t row = 0; row < orientations.size(); row++ )
697  {
698  const T theta = orientations[ row ].getTheta();
699  const T phi = orientations[ row ].getPhi();
700 
701  int j = 0;
702  for( int k = 0; k <= order; k += 2 )
703  {
704  for( int m = -k; m < 0; m++ )
705  {
706  B( row, j ) = boost::math::spherical_harmonic( k, m, theta, phi );
707  ++j;
708  }
709  B( row, j ) = boost::math::spherical_harmonic( k, 0, theta, phi );
710  ++j;
711  for( int m = 1; m <= k; m++ )
712  {
713  B( row, j ) = boost::math::spherical_harmonic( k, m, theta, phi );
714  ++j;
715  }
716  }
717  }
718  return B;
719 }
720 
721 template< typename T >
723 {
724  size_t R = ( ( order + 1 ) * ( order + 2 ) ) / 2;
725  std::size_t i = 0;
726  WValue< T > result( R );
727  for( size_t k = 0; k <= order; k += 2 )
728  {
729  for( int m = -static_cast< int >( k ); m <= static_cast< int >( k ); ++m )
730  {
731  result[ i ] = -static_cast< T > ( k * ( k + 1 ) );
732  ++i;
733  }
734  }
735  return result;
736 }
737 
738 template< typename T >
740 {
742  WMatrix< T > L( eigenvalues.size(), eigenvalues.size() );
743  for( std::size_t i = 0; i < eigenvalues.size(); ++i )
744  {
745  L( i, i ) = eigenvalues[ i ];
746  }
747  return L;
748 }
749 
750 template< typename T >
752 {
754  WMatrix< T > L( eigenvalues.size(), eigenvalues.size() );
755  for( std::size_t i = 0; i < eigenvalues.size(); ++i )
756  {
757  L( i, i ) = std::pow( eigenvalues[ i ], 2 );
758  }
759  return L;
760 }
761 
762 template< typename T >
764 {
765  size_t R = ( ( order + 1 ) * ( order + 2 ) ) / 2;
766  std::size_t i = 0;
767  WMatrix< T > result( R, R );
768  for( size_t k = 0; k <= order; k += 2 )
769  {
770  T h = 2.0 * static_cast< T >( pi() ) * static_cast< T >( std::pow( static_cast< T >( -1 ), static_cast< T >( k / 2 ) ) ) *
771  static_cast< T >( oddFactorial( ( k <= 1 ) ? 1 : k - 1 ) ) / static_cast< T >( evenFactorial( k ) );
772  for( int m = -static_cast< int >( k ); m <= static_cast< int >( k ); ++m )
773  {
774  result( i, i ) = h;
775  ++i;
776  }
777  }
778  return result;
779 }
780 
781 template< typename T >
783 {
784  std::vector< WVector3d > vertices;
785  std::vector< unsigned int > triangles;
786  // calc necessary tesselation level
787  int level = static_cast< int >( log( static_cast< float >( ( order + 1 ) * ( order + 2 ) ) ) / 2.0f + 1.0f );
788  tesselateIcosahedron( &vertices, &triangles, level );
789  std::vector< WUnitSphereCoordinates< T > > orientations;
790  for( typename std::vector< WVector3d >::const_iterator cit = vertices.begin(); cit != vertices.end(); cit++ )
791  {
792  // avoid linear dependent orientations
793  if( ( *cit )[ 0 ] >= 0.0001 )
794  {
795  orientations.push_back( WUnitSphereCoordinates< T >( WMatrixFixed< T, 3, 1 >( *cit ) ) );
796  }
797  }
798  return WSymmetricSphericalHarmonic< T >::calcSHToTensorSymMatrix( order, orientations );
799 }
800 
801 template< typename T >
803  const std::vector< WUnitSphereCoordinates< T > >& orientations )
804 {
805  std::size_t numElements = ( order + 1 ) * ( order + 2 ) / 2;
806  WPrecondEquals( order % 2, 0u );
807  WPrecondLess( numElements, orientations.size() + 1 );
808 
809  // store first numElements orientations as 3d-vectors
810  std::vector< WMatrixFixed< T, 3, 1 > > directions( numElements );
811  for( std::size_t i = 0; i < numElements; ++i )
812  {
813  directions[ i ] = orientations[ i ].getEuclidean();
814  }
815 
816  // initialize an index array
817  std::vector< std::size_t > indices( order, 0 );
818 
819  // calc tensor evaluation matrix
820  WMatrix< T > TEMat( numElements, numElements );
821  for( std::size_t j = 0; j < numElements; ++j ) // j is the 'permutation index'
822  {
823  // stores how often each value is represented in the index array
824  std::size_t amount[ 3 ] = { 0, 0, 0 };
825  for( std::size_t k = 0; k < order; ++k )
826  {
827  ++amount[ indices[ k ] ];
828  }
829 
830  // from WTensorSym.h
831  std::size_t multiplicity = calcSupersymmetricTensorMultiplicity( order, amount[ 0 ], amount[ 1 ], amount[ 2 ] );
832  for( std::size_t i = 0; i < numElements; ++i ) // i is the 'direction index'
833  {
834  TEMat( i, j ) = multiplicity;
835  for( std::size_t k = 0; k < order; ++k )
836  {
837  TEMat( i, j ) *= directions[ i ][ indices[ k ] ];
838  }
839  }
840 
841  // from TensorBase.h
842  positionIterateSortedOneStep( order, 3, indices );
843  }
844  directions.clear();
845 
846  // we do not want more orientations than nessessary
847  std::vector< WUnitSphereCoordinates< T > > ori2( orientations.begin(), orientations.begin() + numElements );
848 
849  WMatrix< T > p = pseudoInverse( TEMat );
850 
851  WAssert( ( TEMat*p ).isIdentity( 1e-3 ), "Test of inverse matrix failed. Are the given orientations linear independent?" );
852 
853  return p * calcBaseMatrix( ori2, order );
854 }
855 
856 template< typename T >
858 {
859  T scale = 0.0;
860  if( m_SHCoefficients.size() > 0 )
861  {
862  scale = std::sqrt( 4.0 * static_cast< T >( pi() ) ) * m_SHCoefficients[0];
863  }
864  for( size_t i = 0; i < m_SHCoefficients.size(); i++ )
865  {
866  m_SHCoefficients[ i ] /= scale;
867  }
868 }
869 
870 #endif // WSYMMETRICSPHERICALHARMONIC_H
Matrix template class with variable number of rows and columns.
Definition: WMatrix.h:44
size_t getNbRows() const
Get number of rows.
Definition: WMatrix.h:375
WMatrix transposed() const
Returns the transposed matrix.
Definition: WMatrix.h:447
size_t getNbCols() const
Get number of columns.
Definition: WMatrix.h:383
Class for symmetric spherical harmonics The index scheme of the coefficients/basis values is like in ...
const WValue< T > & getCoefficients() const
Returns the used coefficients (stored like in the mentioned 2007 Descoteaux paper).
static WMatrix< T > calcSHToTensorSymMatrix(std::size_t order)
Calculates a matrix that converts spherical harmonics to symmetric tensors of equal or lower order.
WSymmetricSphericalHarmonic()
Default constructor.
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.
static WMatrix< T > calcSmoothingMatrix(size_t order)
This calcs the smoothing matrix L from the 2007 Descoteaux Paper "Regularized, Fast,...
void setFromComplex(WValue< std::complex< T > > const &coeffs, int order)
Set the coeffs from complex base coeffs.
static WValue< T > calcEigenvalues(size_t order)
Calc eigenvalues for SH elements.
static WMatrix< T > calcBaseMatrix(const std::vector< WUnitSphereCoordinates< T > > &orientations, int order)
Calculates the base matrix B like in the dissertation of Descoteaux.
size_t m_order
order of the spherical harmonic
T calcGFA(std::vector< WUnitSphereCoordinates< T > > const &orientations) const
Calculate the generalized fractional anisotropy for this ODF.
static WMatrix< T > calcFRTMatrix(size_t order)
Calculates the Funk-Radon-Transformation-Matrix P from the 2007 Descoteaux Paper "Regularized,...
size_t getOrder() const
Return the order of the spherical harmonic.
virtual ~WSymmetricSphericalHarmonic()
Destructor.
void applyFunkRadonTransformation(WMatrix< T > const &frtMat)
Applies the Funk-Radon-Transformation.
WValue< T > getCoefficientsSchultz() const
Returns the coefficients for Schultz' SH base.
static WMatrix< T > calcMatrixWithEigenvalues(size_t order)
Calc matrix with the eigenvalues of the SH elements on its diagonal.
void normalize()
Normalize this SH in place.
static WMatrix< T > getSHFittingMatrix(const std::vector< WMatrixFixed< T, 3, 1 > > &orientations, int order, T lambda, bool withFRT)
This calculates the transformation/fitting matrix T like in the 2007 Descoteaux paper.
WValue< T > m_SHCoefficients
coefficients of the spherical harmonic
WValue< std::complex< T > > getCoefficientsComplex() const
Returns the coefficients for the complex base.
static WMatrix< std::complex< T > > calcComplexBaseMatrix(std::vector< WUnitSphereCoordinates< T > > const &orientations, int order)
Calculates the base matrix B for the complex spherical harmonics.
T getValue(T theta, T phi) const
Return the value on the sphere.
This class stores coordinates on the unit sphere.
T getTheta() const
Return the theta angle.
T getPhi() const
Return the phi angle.
Base class for all higher level values like tensors, vectors, matrices and so on.
Definition: WValue.h:41
size_t size() const
Get number of components the value consists of.
Definition: WValue.h:116
WStreamedLogger debug(const std::string &source)
Logging a debug message.
Definition: WLogger.h:331