Commit 8489397d authored by Sebastian Eichelbaum's avatar Sebastian Eichelbaum

[ADD] adding image space tensor LIC algorithm. Complete and working implementation.

parent 41cd68c2
This diff is collapsed.
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#ifndef WMIMAGESPACETENSORLIC_H
#define WMIMAGESPACETENSORLIC_H
#include <string>
#include <vector>
#include "core/dataHandler/WDataSetVector.h"
#include "core/graphicsEngine/WTriangleMesh.h"
#include "core/graphicsEngine/WGEManagedGroupNode.h"
#include "core/kernel/WModule.h"
#include "core/kernel/WModuleInputData.h"
/**
* This module takes a second order symmetric tensor dataset and uses it to apply an image space based (fast) fabric-like LIC to an arbitrary
* surface. The surface can be specified as tri mesh or, if not specified, slices.
*
* \ingroup modules
*/
class WMImageSpaceTensorLIC: public WModule
{
public:
/**
* Default constructor.
*/
WMImageSpaceTensorLIC();
/**
* Destructor.
*/
virtual ~WMImageSpaceTensorLIC();
/**
* Gives back the name of this module.
* \return the module's name.
*/
virtual const std::string getName() const;
/**
* Gives back a description of this module.
* \return description to module.
*/
virtual const std::string getDescription() const;
/**
* Due to the prototype design pattern used to build modules, this method returns a new instance of this method. NOTE: it
* should never be initialized or modified in some other way. A simple new instance is required.
*
* \return the prototype used to create every module in OpenWalnut.
*/
virtual boost::shared_ptr< WModule > factory() const;
protected:
/**
* Entry point after loading the module. Runs in separate thread.
*/
virtual void moduleMain();
/**
* Initialize the connectors this module is using.
*/
virtual void connectors();
/**
* Initialize the properties for this module.
*/
virtual void properties();
private:
/**
* Initializes the needed geodes, transformations and vertex arrays. This needs to be done once for each new dataset.
*
* \param grid the grid to places the slices in
* \param mesh the mesh to use if not NULL and m_useSlices is false
*/
void initOSG( boost::shared_ptr< WGridRegular3D > grid, boost::shared_ptr< WTriangleMesh > mesh );
/**
* The input connector containing the DTI field whose derived field is used for LIC.
*/
boost::shared_ptr< WModuleInputData< WDataSetVector > > m_evec1In;
/**
* The input connector containing the DTI field whose derived field is used for LIC.
*/
boost::shared_ptr< WModuleInputData< WDataSetVector > > m_evec2In;
/**
* The input connector containing the DTI field whose derived field is used for LIC.
*/
boost::shared_ptr< WModuleInputData< WDataSetVector > > m_evalsIn;
/**
* The input containing the surface on which the LIC should be applied on
*/
boost::shared_ptr< WModuleInputData< WTriangleMesh > > m_meshIn;
/**
* A property allowing the user to select whether the slices or the mesh should be used
*/
WPropSelection m_geometrySelection;
/**
* A list of items that can be selected using m_geometrySelection.
*/
boost::shared_ptr< WItemSelection > m_geometrySelections;
/**
* A condition used to notify about changes in several properties.
*/
boost::shared_ptr< WCondition > m_propCondition;
/**
* The Geode containing all the slices and the mesh
*/
osg::ref_ptr< WGEGroupNode > m_output;
/**
* Scene root node.
*/
osg::ref_ptr< WGEManagedGroupNode > m_root;
WPropGroup m_sliceGroup; //!< the group contains several slice properties
WPropGroup m_geometryGroup; //!< the group contains several input geometry parameters
WPropGroup m_licGroup; //!< the group contains several LIC properties
WPropBool m_useSlices; //!< indicates whether the vector should be shown on slices or input geometry
WPropInt m_xPos; //!< x position of the slice
WPropInt m_yPos; //!< y position of the slice
WPropInt m_zPos; //!< z position of the slice
WPropBool m_showonX; //!< indicates whether the vector should be shown on slice X
WPropBool m_showonY; //!< indicates whether the vector should be shown on slice Y
WPropBool m_showonZ; //!< indicates whether the vector should be shown on slice Z
WPropBool m_showHUD; //!< indicates whether to show the texture HUD
WPropBool m_useEdges; //!< indicates whether to show the edges
WPropColor m_useEdgesColor; //!< indicated whether the edges (if enabled) should be black or white or green or red or ....
WPropDouble m_useEdgesStep; //!< define the steepness of the step function used to blend in the edge color.
WPropBool m_useLight; //!< indicates whether to use Phong
WPropDouble m_lightIntensity; //!< light intensity
WPropInt m_numIters; //!< the number of iterations done per frame
WPropDouble m_cmapRatio; //!< the ratio between colormap and LIC
WPropDouble m_projectionAngleThreshold; //!< the angle threshold between surface and vector before clipping the vector.
/**
* The group for more advanced LIC features
*/
WPropGroup m_advancedLicGroup;
/**
* The resolution scaling for the noise
*/
WPropDouble m_noiseRes;
/**
* Clipp according to FA.
*/
WPropDouble m_faClip;
};
#endif // WMIMAGESPACETENSORLIC_H
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV-Leipzig and CNCF-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
// This file's purpose is to provide a list of modules and additional extensions as entry point for OpenWalnut's module loader.
// Both functAdd your modules here. If you miss this step, OpenWalnut will not be able to load your modules/extensions.
#include <boost/shared_ptr.hpp>
#include <core/kernel/WModule.h>
#include "WMImageSpaceTensorLIC.h"
/**
* This function is called by OpenWalnut, when loading your library to learn about the modules you provide. The function is called with a given
* list reference, where you add all of your modules. Modules which are not registered this way, cannot be used in OpenWalnut. As this is called
* before loading any project file or running any module, it is ensured that you can rely on the modules provided here in your project files and
* other modules.
*
* \note this function is optional. You can remove it if you do not need it.
*
* \param m the list of modules. Add you module instances into this list.
*/
extern "C" void WLoadModule( WModuleList& m ) // NOLINT
{
// This line is needed by the module loader to actually find your module. You need to add this to your module too. Do NOT add a ";" here.
m.push_back( boost::shared_ptr< WModule >( new WMImageSpaceTensorLIC ) );
}
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV-Leipzig and CNCF-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#include <iostream>
#include <vector>
#include <boost/random.hpp>
#include <core/common/exceptions/WPreconditionNotMet.h>
#include "WTuringTextureCreator.h"
WTuringTextureCreator::WTuringTextureCreator( std::size_t numThreads )
: m_numIterations( 100 ),
m_spotIrregularity( 0.1 ),
m_spotSize( 0.1 )
{
WPrecond( numThreads > 0, "" );
for( std::size_t k = 0; k < numThreads; ++k )
{
m_threads.push_back( boost::shared_ptr< TextureThread >( new TextureThread( k, numThreads ) ) );
}
}
WTuringTextureCreator::~WTuringTextureCreator()
{
}
void WTuringTextureCreator::setNumIterations( std::size_t iter )
{
WPrecond( iter > 0, "Invalid number of iterations!" );
m_numIterations = iter;
}
void WTuringTextureCreator::setSpotIrregularity( float irr )
{
WPrecond( irr >= 0.0f && irr <= 1.0f, "Spot irregularity must be in [0,1]!" );
m_spotIrregularity = irr;
}
void WTuringTextureCreator::setSpotSize( float size )
{
WPrecond( size >= 0.0f && size <= 1.0f, "Spot size must be in [0,1]!" );
m_spotSize = size;
}
osg::ref_ptr< WGETexture3D > WTuringTextureCreator::create( std::size_t sizeX, std::size_t sizeY, std::size_t sizeZ )
{
// some constants, maybe change to parameters
float const spotFactor = ( 0.02f + 0.58f * ( 1.0f - m_spotSize ) ) / 15.0f;
float const d1 = 0.125f;
float const d2 = 0.03125f;
float const speed = 1.0f;
std::vector< float > concentration1( sizeX * sizeY * sizeZ, 4.0f );
std::vector< float > concentration2( sizeX * sizeY * sizeZ, 4.0f );
std::vector< float > delta1( sizeX * sizeY * sizeZ, 0.0f );
std::vector< float > delta2( sizeX * sizeY * sizeZ, 0.0f );
std::vector< float > noise( sizeX * sizeY * sizeZ );
boost::mt19937 generator( std::time( 0 ) );
float noiseRange = 0.1f + 4.9f * m_spotIrregularity;
boost::uniform_real< float > dist( 12.0f - noiseRange, 12.0f + noiseRange );
boost::variate_generator< boost::mt19937&, boost::uniform_real< float > > rand( generator, dist );
// initialize noise
for( std::size_t k = 0; k < sizeX * sizeY * sizeZ; ++k )
{
noise[ k ] = rand();
}
// iterate
for( std::size_t iter = 0; iter < m_numIterations; ++iter )
{
std::cout << "iteration: " << iter << std::endl;
// step one: calculate change in concentration across the volume
for( std::size_t k = 0; k < m_threads.size(); ++k )
{
m_threads[ k ]->setTextureSize( sizeX, sizeY, sizeZ );
m_threads[ k ]->setSpotFactor( spotFactor );
m_threads[ k ]->setDiffusionConstants( d1, d2 );
m_threads[ k ]->setBufferPointers( &concentration1, &concentration2, &noise, &delta1, &delta2 );
m_threads[ k ]->run();
}
for( std::size_t k = 0; k < m_threads.size(); ++k )
{
m_threads[ k ]->wait();
}
// applying the change in concentration is not too costly
for( std::size_t k = 0; k < sizeX * sizeY * sizeZ; ++k )
{
concentration1[ k ] += speed * delta1[ k ];
concentration2[ k ] += speed * delta2[ k ];
}
}
// allocate new osg::Image for the texture data
osg::ref_ptr< osg::Image > img = new osg::Image;
img->allocateImage( sizeX, sizeY, sizeZ, GL_LUMINANCE, GL_UNSIGNED_BYTE );
// find min and max
float c1min = *std::min_element( concentration1.begin(), concentration1.end() );
float c1max = *std::max_element( concentration1.begin(), concentration1.end() );
// copy to image
for( std::size_t k = 0; k < sizeX * sizeY * sizeZ; ++k )
{
img->data()[ k ] = 255.0f * ( concentration1[ k ] - c1min ) / ( c1max - c1min );
}
return osg::ref_ptr< WGETexture< osg::Texture3D > >( new WGETexture< osg::Texture3D >( img ) );
}
WTuringTextureCreator::TextureThread::TextureThread( std::size_t id, std::size_t max )
: WThreadedRunner(),
m_id( id ),
m_maxThreads( max ),
m_sizeX( 2 ),
m_sizeY( 2 ),
m_sizeZ( 2 ),
m_spotFactor( 0.5 ),
m_diffusionConstant1( 0.5 ),
m_diffusionConstant2( 0.5 )
{
}
WTuringTextureCreator::TextureThread::~TextureThread()
{
}
void WTuringTextureCreator::TextureThread::setDiffusionConstants( float d1, float d2 )
{
WPrecond( d1 >= 0.0 && d1 <= 1.0, "" );
WPrecond( d2 >= 0.0 && d2 <= 1.0, "" );
m_diffusionConstant1 = d1;
m_diffusionConstant2 = d2;
}
void WTuringTextureCreator::TextureThread::setSpotFactor( float spotFactor )
{
WPrecond( spotFactor > 0.0, "" );
m_spotFactor = spotFactor;
}
void WTuringTextureCreator::TextureThread::setTextureSize( std::size_t sizeX, std::size_t sizeY, std::size_t sizeZ )
{
WPrecond( sizeX > 0, "" );
WPrecond( sizeY > 0, "" );
WPrecond( sizeZ > 0, "" );
m_sizeX = sizeX;
m_sizeY = sizeY;
m_sizeZ = sizeZ;
}
void WTuringTextureCreator::TextureThread::threadMain()
{
WPrecond( m_concentration1 != 0, "Invalid pointer!" );
WPrecond( m_concentration2 != 0, "Invalid pointer!" );
WPrecond( m_noise != 0, "Invalid pointer!" );
WPrecond( m_delta1 != 0, "Invalid pointer!" );
WPrecond( m_delta2 != 0, "Invalid pointer!" );
std::size_t const numVoxels = m_sizeX * m_sizeY * m_sizeZ;
std::size_t start = m_id * ( numVoxels / m_maxThreads );
std::size_t end = ( m_id + 1 ) * ( numVoxels / m_maxThreads );
if( m_id == m_maxThreads - 1 )
{
end = numVoxels;
}
for( std::size_t idx = start; idx < end; ++idx )
{
std::size_t i = idx % m_sizeX;
std::size_t j = ( idx / m_sizeX ) % m_sizeY;
std::size_t k = ( idx / m_sizeX ) / m_sizeY;
std::size_t iNext = ( i + 1 ) % m_sizeX;
std::size_t iPrev = ( i + m_sizeX - 1 ) % m_sizeX;
std::size_t jNext = ( j + 1 ) % m_sizeY;
std::size_t jPrev = ( j + m_sizeY - 1 ) % m_sizeY;
std::size_t kNext = ( k + 1 ) % m_sizeZ;
std::size_t kPrev = ( k + m_sizeZ - 1 ) % m_sizeZ;
// estimate change in concentrations using 3d laplace filter
float dc1 = 0.0;
dc1 += ( *m_concentration1 )[ iPrev + j * m_sizeX + k * m_sizeX * m_sizeY ];
dc1 += ( *m_concentration1 )[ iNext + j * m_sizeX + k * m_sizeX * m_sizeY ];
dc1 += ( *m_concentration1 )[ i + jPrev * m_sizeX + k * m_sizeX * m_sizeY ];
dc1 += ( *m_concentration1 )[ i + jNext * m_sizeX + k * m_sizeX * m_sizeY ];
dc1 += ( *m_concentration1 )[ i + j * m_sizeX + kPrev * m_sizeX * m_sizeY ];
dc1 += ( *m_concentration1 )[ i + j * m_sizeX + kNext * m_sizeX * m_sizeY ];
dc1 -= 6.0f * ( *m_concentration1 )[ idx ];
float dc2 = 0.0;
dc2 += ( *m_concentration2 )[ iPrev + j * m_sizeX + k * m_sizeX * m_sizeY ];
dc2 += ( *m_concentration2 )[ iNext + j * m_sizeX + k * m_sizeX * m_sizeY ];
dc2 += ( *m_concentration2 )[ i + jPrev * m_sizeX + k * m_sizeX * m_sizeY ];
dc2 += ( *m_concentration2 )[ i + jNext * m_sizeX + k * m_sizeX * m_sizeY ];
dc2 += ( *m_concentration2 )[ i + j * m_sizeX + kPrev * m_sizeX * m_sizeY ];
dc2 += ( *m_concentration2 )[ i + j * m_sizeX + kNext * m_sizeX * m_sizeY ];
dc2 -= 6.0f * ( *m_concentration2 )[ idx ];
// diffusion
( *m_delta1 )[ idx ] = m_spotFactor * ( 16.0f - ( *m_concentration1 )[ idx ] * ( *m_concentration2 )[ idx ] ) + m_diffusionConstant1 * dc1;
( *m_delta2 )[ idx ] = m_spotFactor * ( ( *m_concentration1 )[ idx ] * ( *m_concentration2 )[ idx ] - ( *m_concentration2 )[ idx ] - ( *m_noise )[ idx ] ) + m_diffusionConstant2 * dc2;
}
}
void WTuringTextureCreator::TextureThread::setBufferPointers( std::vector< float > const* concentration1, std::vector< float > const* concentration2,
std::vector< float > const* noise, std::vector< float >* delta1, std::vector< float >* delta2 )
{
WPrecond( concentration1 != 0, "Invalid pointer!" );
WPrecond( concentration2 != 0, "Invalid pointer!" );
WPrecond( noise != 0, "Invalid pointer!" );
WPrecond( delta1 != 0, "Invalid pointer!" );
WPrecond( delta2 != 0, "Invalid pointer!" );
m_concentration1 = concentration1;
m_concentration2 = concentration2;
m_noise = noise;
m_delta1 = delta1;
m_delta2 = delta2;
}
//---------------------------------------------------------------------------
//
// Project: OpenWalnut ( http://www.openwalnut.org )
//
// Copyright 2009 OpenWalnut Community, BSV-Leipzig and CNCF-CBS
// For more information see http://www.openwalnut.org/copying
//
// This file is part of OpenWalnut.
//
// OpenWalnut is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// OpenWalnut is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
//
//---------------------------------------------------------------------------
#ifndef WTURINGTEXTURECREATOR_H
#define WTURINGTEXTURECREATOR_H
#include <vector>
#include <boost/thread.hpp>
#include <core/common/WThreadedRunner.h>
#include <core/graphicsEngine/WGETexture.h>
class WTuringTextureCreator
{
public:
WTuringTextureCreator( std::size_t numThreads = boost::thread::hardware_concurrency() );
~WTuringTextureCreator();
osg::ref_ptr< WGETexture3D > create( std::size_t sizeX, std::size_t sizeY, std::size_t sizeZ );
void setSpotSize( float size );
void setSpotIrregularity( float irr );
void setNumIterations( std::size_t iter );
private:
class TextureThread : public WThreadedRunner
{
public:
TextureThread( std::size_t id, std::size_t max );
~TextureThread();
void setTextureSize( std::size_t sizeX, std::size_t sizeY, std::size_t sizeZ );
void setSpotFactor( float spotFactor );
void setDiffusionConstants( float d1, float d2 );
void setBufferPointers( std::vector< float > const* concentration1, std::vector< float > const* concentration2,
std::vector< float > const* noise, std::vector< float >* delta1, std::vector< float >* delta2 );
virtual void threadMain();
private:
std::size_t m_id;
std::size_t m_maxThreads;
std::size_t m_sizeX;
std::size_t m_sizeY;
std::size_t m_sizeZ;
float m_spotFactor;
float m_diffusionConstant1;
float m_diffusionConstant2;
std::vector< float > const* m_concentration1;
std::vector< float > const* m_concentration2;
std::vector< float > const* m_noise;
std::vector< float >* m_delta1;
std::vector< float >* m_delta2;
};
float m_numIterations;
float m_spotIrregularity;
float m_spotSize;
std::vector< boost::shared_ptr< TextureThread > > m_threads;
};
#endif // WTURINGTEXTURECREATOR_H