Commit 214b4d93 by Sebastian Eichelbaum

### [MERGE]

parents 6ca6e726 e97af1d7
 ... ... @@ -22,531 +22,5 @@ // //--------------------------------------------------------------------------- #include #include #include #include #include "core/common/WLogger.h" #include "core/common/math/WGeometryFunctions.h" #include "../exceptions/WPreconditionNotMet.h" #include "linearAlgebra/WLinearAlgebra.h" #include "WLinearAlgebraFunctions.h" #include "WMatrix.h" #include "WTensorSym.h" #include "WUnitSphereCoordinates.h" #include "WSymmetricSphericalHarmonic.h" WSymmetricSphericalHarmonic::WSymmetricSphericalHarmonic() : m_order( 0 ), m_SHCoefficients( 0 ) { } WSymmetricSphericalHarmonic::WSymmetricSphericalHarmonic( const WValue& SHCoefficients ) : m_SHCoefficients( SHCoefficients ) { // calculate order from SHCoefficients.size: // - this is done by reversing the R=(l+1)*(l+2)/2 formula from the Descoteaux paper double q = std::sqrt( 0.25 + 2.0 * static_cast( m_SHCoefficients.size() ) ) - 1.5; m_order = static_cast( q ); } WSymmetricSphericalHarmonic::~WSymmetricSphericalHarmonic() { } double WSymmetricSphericalHarmonic::getValue( double theta, double phi ) const { double result = 0.0; int j = 0; const double rootOf2 = std::sqrt( 2.0 ); for( int k = 0; k <= static_cast( m_order ); k+=2 ) { // m = 1 .. k for( int m = 1; m <= k; m++ ) { j = ( k*k+k+2 ) / 2 + m; result += m_SHCoefficients[ j-1 ] * rootOf2 * static_cast( std::pow( -1.0, m+1 ) ) * boost::math::spherical_harmonic_i( k, m, theta, phi ); } // m = 0 result += m_SHCoefficients[ ( k*k+k+2 ) / 2 - 1 ] * boost::math::spherical_harmonic_r( k, 0, theta, phi ); // m = -k .. -1 for( int m = -k; m < 0; m++ ) { j = ( k*k+k+2 ) / 2 + m; result += m_SHCoefficients[ j-1 ] * rootOf2 * boost::math::spherical_harmonic_r( k, -m, theta, phi ); } } return result; } double WSymmetricSphericalHarmonic::getValue( const WUnitSphereCoordinates& coordinates ) const { return getValue( coordinates.getTheta(), coordinates.getPhi() ); } const WValue& WSymmetricSphericalHarmonic::getCoefficients() const { return m_SHCoefficients; } WValue< double > WSymmetricSphericalHarmonic::getCoefficientsSchultz() const { WValue< double > res( m_SHCoefficients.size() ); size_t k = 0; for( int l = 0; l <= static_cast< int >( m_order ); l += 2 ) { for( int m = -l; m <= l; ++m ) { res[ k ] = m_SHCoefficients[ k ]; if( m < 0 && ( ( -m ) % 2 == 1 ) ) { res[ k ] *= -1.0; } else if( m > 0 && ( m % 2 == 0 ) ) { res[ k ] *= -1.0; } ++k; } } return res; } WValue< std::complex< double > > WSymmetricSphericalHarmonic::getCoefficientsComplex() const { WValue< std::complex< double > > res( m_SHCoefficients.size() ); size_t k = 0; double r = 1.0 / sqrt( 2.0 ); std::complex< double > i( 0.0, -1.0 ); for( int l = 0; l <= static_cast< int >( m_order ); l += 2 ) { for( int m = -l; m <= l; ++m ) { if( m == 0 ) { res[ k ] = m_SHCoefficients[ k ]; } else if( m < 0 ) { res[ k ] += i * r * m_SHCoefficients[ k - 2 * m ]; res[ k ] += ( -m % 2 == 1 ? -r : r ) * m_SHCoefficients[ k ]; } else if( m > 0 ) { res[ k ] += i * ( -m % 2 == 0 ? -r : r ) * m_SHCoefficients[ k ]; res[ k ] += r * m_SHCoefficients[ k - 2 * m ]; } ++k; } } return res; } double WSymmetricSphericalHarmonic::calcGFA( std::vector< WUnitSphereCoordinates > const& orientations ) const { double n = static_cast< double >( orientations.size() ); double d = 0.0; double gfa = 0.0; double mean = 0.0; std::vector< double > v( orientations.size() ); for( std::size_t i = 0; i < orientations.size(); ++i ) { v[ i ] = getValue( orientations[ i ] ); mean += v[ i ]; } mean /= n; for( std::size_t i = 0; i < orientations.size(); ++i ) { double f = v[ i ]; // we use gfa as a temporary here gfa += f * f; f -= mean; d += f * f; } if( gfa == 0.0 || n <= 1.0 ) { return 0.0; } // this is the real gfa gfa = sqrt( ( n * d ) / ( ( n - 1.0 ) * gfa ) ); WAssert( gfa >= -0.01 && gfa <= 1.01, "" ); if( gfa < 0.0 ) { return 0.0; } else if( gfa > 1.0 ) { return 1.0; } return gfa; } double WSymmetricSphericalHarmonic::calcGFA( WMatrix< double > const& B ) const { // sh coeffs WMatrix< double > s( B.getNbCols(), 1 ); WAssert( B.getNbCols() == m_SHCoefficients.size(), "" ); for( std::size_t k = 0; k < B.getNbCols(); ++k ) { s( k, 0 ) = m_SHCoefficients[ k ]; } s = B * s; WAssert( s.getNbRows() == B.getNbRows(), "" ); WAssert( s.getNbCols() == 1u, "" ); double n = static_cast< double >( s.getNbRows() ); double d = 0.0; double gfa = 0.0; double mean = 0.0; for( std::size_t i = 0; i < s.getNbRows(); ++i ) { mean += s( i, 0 ); } mean /= n; for( std::size_t i = 0; i < s.getNbRows(); ++i ) { double f = s( i, 0 ); // we use gfa as a temporary here gfa += f * f; f -= mean; d += f * f; } if( gfa == 0.0 || n <= 1.0 ) { return 0.0; } // this is the real gfa gfa = sqrt( ( n * d ) / ( ( n - 1.0 ) * gfa ) ); WAssert( gfa >= -0.01 && gfa <= 1.01, "" ); if( gfa < 0.0 ) { return 0.0; } else if( gfa > 1.0 ) { return 1.0; } return gfa; } void WSymmetricSphericalHarmonic::applyFunkRadonTransformation( WMatrix< double > const& frtMat ) { WAssert( frtMat.getNbCols() == m_SHCoefficients.size(), "" ); WAssert( frtMat.getNbRows() == m_SHCoefficients.size(), "" ); // Funk-Radon-Transformation as in Descoteaux's thesis for( size_t j = 0; j < m_SHCoefficients.size(); j++ ) { m_SHCoefficients[ j ] *= frtMat( j, j ); } } size_t WSymmetricSphericalHarmonic::getOrder() const { return m_order; } WMatrix WSymmetricSphericalHarmonic::getSHFittingMatrix( const std::vector< WVector3d >& orientations, int order, double lambda, bool withFRT ) { // convert euclid 3d orientations/gradients to sphere coordinates std::vector< WUnitSphereCoordinates > sphereCoordinates; for( std::vector< WVector3d >::const_iterator it = orientations.begin(); it != orientations.end(); it++ ) { sphereCoordinates.push_back( WUnitSphereCoordinates( *it ) ); } return WSymmetricSphericalHarmonic::getSHFittingMatrix( sphereCoordinates, order, lambda, withFRT ); } WMatrix WSymmetricSphericalHarmonic::getSHFittingMatrix( const std::vector< WUnitSphereCoordinates >& orientations, int order, double lambda, bool withFRT ) { WMatrix B( WSymmetricSphericalHarmonic::calcBaseMatrix( orientations, order ) ); WMatrix Bt( B.transposed() ); WMatrix result( Bt*B ); if( lambda != 0.0 ) { WMatrix L( WSymmetricSphericalHarmonic::calcSmoothingMatrix( order ) ); result += lambda*L; } result = pseudoInverse( result )*Bt; if( withFRT ) { WMatrix P( WSymmetricSphericalHarmonic::calcFRTMatrix( order ) ); return ( P * result ); } return result; } WMatrix WSymmetricSphericalHarmonic::getSHFittingMatrixForConstantSolidAngle( const std::vector< WVector3d >& orientations, int order, double lambda ) { // convert euclid 3d orientations/gradients to sphere coordinates std::vector< WUnitSphereCoordinates > sphereCoordinates; for( std::vector< WVector3d >::const_iterator it = orientations.begin(); it != orientations.end(); it++ ) { sphereCoordinates.push_back( WUnitSphereCoordinates( *it ) ); } return WSymmetricSphericalHarmonic::getSHFittingMatrixForConstantSolidAngle( sphereCoordinates, order, lambda ); } WMatrix WSymmetricSphericalHarmonic::getSHFittingMatrixForConstantSolidAngle( const std::vector< WUnitSphereCoordinates >& orientations, int order, double lambda ) { WMatrix B( WSymmetricSphericalHarmonic::calcBaseMatrix( orientations, order ) ); WMatrix Bt( B.transposed() ); WMatrix result( Bt*B ); // regularisation if( lambda != 0.0 ) { WMatrix L( WSymmetricSphericalHarmonic::calcSmoothingMatrix( order ) ); result += lambda*L; } result = pseudoInverse( result )*Bt; // multiply with eigenvalues of coefficients / Laplace-Beltrami operator WMatrix LB( WSymmetricSphericalHarmonic::calcMatrixWithEigenvalues( order ) ); wlog::debug( "" ) << "LB: " << LB; result = LB*result; wlog::debug( "" ) << "LB*result: " << result; // apply FRT WMatrix P( WSymmetricSphericalHarmonic::calcFRTMatrix( order ) ); result = P * result; wlog::debug( "" ) << "P: " << P; wlog::debug( "" ) << "P*result: " << result; // correction factor double correctionFactor = 1.0 / ( 16.0 * std::pow( piDouble, 2 ) ); result *= correctionFactor; return result; } WMatrix WSymmetricSphericalHarmonic::calcBaseMatrix( const std::vector< WUnitSphereCoordinates >& orientations, int order ) { WMatrix B( orientations.size(), ( ( order + 1 ) * ( order + 2 ) ) / 2 ); // calc B Matrix like in the 2007 Descoteaux paper ("Regularized, Fast, and Robust Analytical Q-Ball Imaging") int j = 0; const double rootOf2 = std::sqrt( 2.0 ); for(uint32_t row = 0; row < orientations.size(); row++ ) { const double theta = orientations[row].getTheta(); const double phi = orientations[row].getPhi(); for( int k = 0; k <= order; k+=2 ) { // m = 1 .. k for( int m = 1; m <= k; m++ ) { j = ( k * k + k + 2 ) / 2 + m; B( row, j-1 ) = rootOf2 * static_cast( std::pow( static_cast( -1 ), m + 1 ) ) * boost::math::spherical_harmonic_i( k, m, theta, phi ); } // m = 0 B( row, ( k * k + k + 2 ) / 2 - 1 ) = boost::math::spherical_harmonic_r( k, 0, theta, phi ); // m = -k .. -1 for( int m = -k; m < 0; m++ ) { j = ( k * k + k + 2 ) / 2 + m; B( row, j-1 ) = rootOf2*boost::math::spherical_harmonic_r( k, -m, theta, phi ); } } } return B; } WMatrix< std::complex< double > > WSymmetricSphericalHarmonic::calcComplexBaseMatrix( std::vector< WUnitSphereCoordinates > const& orientations, int order ) { WMatrix< std::complex< double > > B( orientations.size(), ( ( order + 1 ) * ( order + 2 ) ) / 2 ); for( std::size_t row = 0; row < orientations.size(); row++ ) { const double theta = orientations[ row ].getTheta(); const double phi = orientations[ row ].getPhi(); int j = 0; for( int k = 0; k <= order; k += 2 ) { for( int m = -k; m < 0; m++ ) { B( row, j ) = boost::math::spherical_harmonic( k, m, theta, phi ); ++j; } B( row, j ) = boost::math::spherical_harmonic( k, 0, theta, phi ); ++j; for( int m = 1; m <= k; m++ ) { B( row, j ) = boost::math::spherical_harmonic( k, m, theta, phi ); ++j; } } } return B; } WValue WSymmetricSphericalHarmonic::calcEigenvalues( size_t order ) { size_t R = ( ( order + 1 ) * ( order + 2 ) ) / 2; std::size_t i = 0; WValue result( R ); for( size_t k = 0; k <= order; k += 2 ) { for( int m = -static_cast< int >( k ); m <= static_cast< int >( k ); ++m ) { result[ i ] = -static_cast< double > ( k * ( k + 1 ) ); ++i; } } return result; } WMatrix WSymmetricSphericalHarmonic::calcMatrixWithEigenvalues( size_t order ) { WValue eigenvalues( WSymmetricSphericalHarmonic::calcEigenvalues( order ) ); WMatrix L( eigenvalues.size(), eigenvalues.size() ); for( std::size_t i = 0; i < eigenvalues.size(); ++i ) { L( i, i ) = eigenvalues[ i ]; } return L; } WMatrix WSymmetricSphericalHarmonic::calcSmoothingMatrix( size_t order ) { WValue eigenvalues( WSymmetricSphericalHarmonic::calcEigenvalues( order ) ); WMatrix L( eigenvalues.size(), eigenvalues.size() ); for( std::size_t i = 0; i < eigenvalues.size(); ++i ) { L( i, i ) = std::pow( eigenvalues[ i ], 2 ); } return L; } WMatrix WSymmetricSphericalHarmonic::calcFRTMatrix( size_t order ) { size_t R = ( ( order + 1 ) * ( order + 2 ) ) / 2; std::size_t i = 0; WMatrix< double > result( R, R ); for( size_t k = 0; k <= order; k += 2 ) { double h = 2.0 * piDouble * static_cast< double >( std::pow( static_cast< double >( -1 ), static_cast< double >( k / 2 ) ) ) * static_cast< double >( oddFactorial( ( k <= 1 ) ? 1 : k - 1 ) ) / static_cast( evenFactorial( k ) ); for( int m = -static_cast< int >( k ); m <= static_cast< int >( k ); ++m ) { result( i, i ) = h; ++i; } } return result; } WMatrix< double > WSymmetricSphericalHarmonic::calcSHToTensorSymMatrix( std::size_t order ) { std::vector< WVector3d > vertices; std::vector< unsigned int > triangles; // calc necessary tesselation level int level = static_cast< int >( log( static_cast< float >( ( order + 1 ) * ( order + 2 ) ) ) / 2.0f + 1.0f ); tesselateIcosahedron( &vertices, &triangles, level ); std::vector< WUnitSphereCoordinates > orientations; for( std::vector< WVector3d >::const_iterator cit = vertices.begin(); cit != vertices.end(); cit++ ) { // avoid linear dependent orientations if( ( *cit )[ 0 ] >= 0.0001 ) { orientations.push_back( WUnitSphereCoordinates( *cit ) ); } } return WSymmetricSphericalHarmonic::calcSHToTensorSymMatrix( order, orientations ); } WMatrix< double > WSymmetricSphericalHarmonic::calcSHToTensorSymMatrix( std::size_t order, const std::vector< WUnitSphereCoordinates >& orientations ) { std::size_t numElements = ( order + 1 ) * ( order + 2 ) / 2; WPrecondEquals( order % 2, 0u ); WPrecondLess( numElements, orientations.size() + 1 ); // store first numElements orientations as 3d-vectors std::vector< WVector3d > directions( numElements ); for( std::size_t i = 0; i < numElements; ++i ) { directions[ i ] = orientations[ i ].getEuclidean(); } // initialize an index array std::vector< std::size_t > indices( order, 0 ); // calc tensor evaluation matrix WMatrix< double > TEMat( numElements, numElements ); for( std::size_t j = 0; j < numElements; ++j ) // j is the 'permutation index' { // stores how often each value is represented in the index array std::size_t amount[ 3 ] = { 0, 0, 0 }; for( std::size_t k = 0; k < order; ++k ) { ++amount[ indices[ k ] ]; } // from WTensorSym.h std::size_t multiplicity = calcSupersymmetricTensorMultiplicity( order, amount[ 0 ], amount[ 1 ], amount[ 2 ] ); for( std::size_t i = 0; i < numElements; ++i ) // i is the 'direction index' { TEMat( i, j ) = multiplicity; for( std::size_t k = 0; k < order; ++k ) { TEMat( i, j ) *= directions[ i ][ indices[ k ] ]; } } // from TensorBase.h positionIterateSortedOneStep( order, 3, indices ); } directions.clear(); // we do not want more orientations than nessessary std::vector< WUnitSphereCoordinates > ori2( orientations.begin(), orientations.begin() + numElements ); WMatrix< double > p = pseudoInverse( TEMat ); WAssert( ( TEMat*p ).isIdentity( 1e-3 ), "Test of inverse matrix failed. Are the given orientations linear independent?" ); return p * calcBaseMatrix( ori2, order ); } void WSymmetricSphericalHarmonic::normalize() { double scale = 0.0; if( m_SHCoefficients.size() > 0 ) scale = std::sqrt( 4.0 * piDouble ) * m_SHCoefficients[0]; for( size_t i = 0; i < m_SHCoefficients.size(); i++ ) { m_SHCoefficients[ i ] /= scale; } } // NOLINT
This diff is collapsed.
 ... ... @@ -22,55 +22,4 @@ // //--------------------------------------------------------------------------- #include #include "WUnitSphereCoordinates.h" WUnitSphereCoordinates::WUnitSphereCoordinates() : m_theta( 0.0 ), m_phi( 0.0 ) { } WUnitSphereCoordinates::WUnitSphereCoordinates( double theta, double phi ) : m_theta( theta ), m_phi( phi ) { } WUnitSphereCoordinates::WUnitSphereCoordinates( WVector3d vector ) { vector = normalize( vector ); // calculate angles m_theta = std::acos( vector[2] ); m_phi = std::atan2( vector[1], vector[0] ); } WUnitSphereCoordinates::~WUnitSphereCoordinates() { } double WUnitSphereCoordinates::getTheta() const { return m_theta; } double WUnitSphereCoordinates::getPhi() const { return m_phi; } void WUnitSphereCoordinates::setTheta( double theta ) {