Commit ec0dae84 authored by Mathias Goldau's avatar Mathias Goldau
Browse files

[ADD #157] Basic Clustering support ported from the FAnToM Algo

parent bd396fc7
......@@ -38,6 +38,7 @@
#include "../modules/navigationSlices/WNavigationSliceModule.h"
#include "../modules/fiberDisplay/WFiberDisplay.h"
#include "../modules/fiberCulling/WFiberCulling.h"
#include "../modules/fiberClustering/WFiberClustering.h"
#include "../common/WException.h"
#include "../graphicsEngine/WGraphicsEngine.h"
......@@ -189,6 +190,7 @@ void WKernel::loadModules()
shared_ptr< WModule > m = shared_ptr< WModule >( new WNavigationSliceModule() );
// shared_ptr< WModule > m = shared_ptr< WModule >( new WFiberDisplay() );
// shared_ptr< WModule > m = shared_ptr< WModule >( new WFiberCulling() );
// shared_ptr< WModule > m = shared_ptr< WModule >( new WFiberClustering() );
WLogger::getLogger()->addLogMessage( "Loading module: " + m->getName(), "Kernel", LL_DEBUG );
m_modules.push_back( m );
......
......@@ -100,8 +100,7 @@ public:
friend std::ostream& operator<<( std::ostream& os, const WLine &rhs )
{
using string_utils::operator<<;
os << rhs.m_points;
return os;
return os << rhs.m_points;
}
protected:
......
ADD_SUBDIRECTORY( fiberDisplay )
ADD_SUBDIRECTORY( fiberCulling )
ADD_SUBDIRECTORY( fiberClustering )
ADD_SUBDIRECTORY( marchingCubes )
FILE( GLOB_RECURSE MODULES_SRC "*.[c,h]pp" )
# Unit tests
IF( CXXTEST_FOUND )
CXXTEST_ADD_TESTS_FROM_LIST( "${MODULES_SRC}"
"kernel"
)
ENDIF( CXXTEST_FOUND )
//---------------------------------------------------------------------------
//
// 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/>.
//
//---------------------------------------------------------------------------
#include <cassert>
#include <algorithm>
#include "WDXtLookUpTable.h"
WDXtLookUpTable::WDXtLookUpTable( size_t dim )
: _data( ( dim * ( dim-1 ) ) / 2, 0.0 ),
_dim( dim )
{
}
double& WDXtLookUpTable::operator()( size_t i, size_t j )
{
assert( i != j );
if( i > j )
{
std::swap( i, j );
}
return _data.at( i * _dim + j - ( i + 1 ) * ( i + 2 ) / 2 );
}
//---------------------------------------------------------------------------
//
// 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 WDXTLOOKUPTABLE_H
#define WDXTLOOKUPTABLE_H
#include <vector>
/**
* Represents a symmetric matrix-like look up table, meaning it stores only
* the elements inside the triangular matrix without the main diagonal.
*
* So in case of a NxN matrix there are only (N^2-N)/2 elements to store.
*
* The reason why this is named DXt look up table is, since is it used as
* look up table for dSt and dLt fiber distance metrics.
*/
class WDXtLookUpTable
{
public:
/**
* Generates new look up table.
*
* \param n the dimmension of the square matrix
*/
explicit WDXtLookUpTable( size_t dim );
/**
* Element acces operator as if the elements where stored as a matrix.
* Note! Acessing elements of the main diagonal is forbidden!
*/
double& operator()( size_t i, size_t j );
private:
/**
* Internal data structure to store the elements.
*/
std::vector< double > _data;
size_t _dim; //!< Matrix dimension
};
#endif // WDXTLOOKUPTABLE_H
//---------------------------------------------------------------------------
//
// 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/>.
//
//---------------------------------------------------------------------------
#include <list>
#include <vector>
#include <boost/shared_ptr.hpp>
#include "WFiberCluster.h"
#include "../../common/WLimits.h"
#include "../../dataHandler/WDataSetFibers.h"
WFiberCluster::WFiberCluster( const boost::shared_ptr< WDataSetFibers > fibs )
{
m_fibs = fibs;
}
WFiberCluster::WFiberCluster( size_t index, const boost::shared_ptr< WDataSetFibers > fibs )
{
m_memberIndices.push_back( index );
m_fibs = fibs;
}
bool WFiberCluster::empty() const
{
return m_memberIndices.empty();
}
void WFiberCluster::merge( WFiberCluster& other ) // NOLINT
{
std::list< size_t >::const_iterator cit = other.m_memberIndices.begin();
for( ; cit != other.m_memberIndices.end(); ++cit)
{
m_memberIndices.push_back( *cit );
}
// make sure that those indices aren't occuring anywhere else
other.clear();
}
double WFiberCluster::minDistance( const WFiberCluster& other, const double proximity_t ) const
{
double result = wlimits::MAX_DOUBLE;
double dist;
std::list< size_t >::const_iterator thisIdx = m_memberIndices.begin();
for( ; thisIdx != m_memberIndices.end(); ++thisIdx )
{
std::list< size_t >::const_iterator otherIdx = other.m_memberIndices.begin();
for( ; otherIdx != other.m_memberIndices.end(); ++otherIdx )
{
dist = (*m_fibs)[ *thisIdx ].dLt( (*m_fibs)[ *otherIdx ], proximity_t );
if( dist < result )
{
result = dist;
}
}
}
assert( result < wlimits::MAX_DOUBLE );
return result;
}
void WFiberCluster::updateClusterIndices( std::vector< size_t >& cid, // NOLINT
const size_t newCID ) const
{
assert( !this->empty() );
std::list< size_t >::const_iterator cit = m_memberIndices.begin();
for( ; cit != m_memberIndices.end(); ++cit )
{
cid.at( *cit ) = newCID;
}
}
void WFiberCluster::sort()
{
m_memberIndices.sort();
}
size_t WFiberCluster::size() const
{
return m_memberIndices.size();
}
void WFiberCluster::clear()
{
m_memberIndices.clear();
}
void WFiberCluster::setColor( WColor color )
{
m_color = color;
}
// void WFiberCluster::paintIntoFgePrimitive( FgeLineStrips *lstrips) const
// {
// typedef std::vector< FArray > Fiber;
// lstrips->setNewColor( m_color );
// std::list< size_t >::const_iterator fibID = m_memberIndices.begin();
// for( ; fibID != m_memberIndices.end(); ++fibID )
// {
// const Fiber& fiber = (*m_fibs)[ *fibID ].getIntermediateSteps();
// Fiber::const_iterator vertex = fiber.begin();
// for( ; vertex != fiber.end(); ++vertex )
// {
// lstrips->setNewVertex( *vertex );
// }
// lstrips->setNewStrip();
// }
// }
//
// FgeLineStrips* WFiberCluster::createNewFgePrimitive() const
// {
// FgeLineStrips *lstrips = new FgeLineStrips;
// paintIntoFgePrimitive( lstrips );
// return lstrips;
// }
bool WFiberCluster::operator==( const WFiberCluster& other ) const
{
return m_memberIndices == other.m_memberIndices;
}
bool WFiberCluster::operator!=( const WFiberCluster& other ) const
{
return m_memberIndices != other.m_memberIndices;
}
const std::list< size_t > WFiberCluster::getIndices() const
{
return m_memberIndices;
}
std::ostream& operator<<( std::ostream& os, const WFiberCluster& c )
{
using string_utils::operator<<;
return os << c.getIndices();
}
//---------------------------------------------------------------------------
//
// 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 WFIBERCLUSTER_H
#define WFIBERCLUSTER_H
#include <list>
#include <vector>
#include <boost/shared_ptr.hpp>
#include "../../common/WColor.h"
#include "../../dataHandler/WDataSetFibers.h"
/**
* Represents a cluster of indices of a WDataSetFibers.
*/
class WFiberCluster
{
public:
/**
* Constructs an cluster with one fiber and a reference to the fiber dataset
* to compute the intercluster distance.
*/
WFiberCluster( size_t index, const boost::shared_ptr< WDataSetFibers > fibs );
/**
* Constructs an empty cluster with reference to the fiber dataset.
*/
explicit WFiberCluster( const boost::shared_ptr< WDataSetFibers > fibs );
/**
* Returns true if there are no fibers in that cluster, false otherwise.
*/
bool empty() const;
/**
* Merge the fibers of the other cluster with the fibers of this cluster.
* Afterwards the other cluster is empty.
*/
void merge( WFiberCluster &other ); // NOLINT
/**
* Returns a copy of all indices inside this cluster
*/
const std::list< size_t > getIndices() const;
/**
* Find the minimal distance for any pair of fibers where one fiber is
* in this cluster and the other is in the other cluster.
*
* \param proximity_t Fiber distances below this threshold are not
* conisdered
*/
double minDistance( const WFiberCluster& other,
const double proximity_t = 0.0 ) const;
/**
* ToDo(math): Refactor! use merge function instead
* For all indices in this cluster, update their cluster number in the
* given cid-vector with the given newCid cluster number.
*/
void updateClusterIndices( std::vector< size_t >& cid, const size_t newCid ) const; // NOLINT
/**
* Sort the indices of fibers associated with this cluster in ascending
* order.
*/
void sort();
/**
* \return Number of fibers associated with this cluster.
*/
size_t size() const;
/**
* Make this cluster empty. Note: The real fibers from fiber dataset are
* not deleted.
*/
void clear();
/**
* Sets the color of which all fibers of this clusters should be painted
* with.
*/
void setColor( WColor color );
// Maybe generate here geodes?
// /**
// * Paint every fiber into the given FGE primitive.
// */
// void paintIntoFgePrimitive( FgeLineStrips *lstrips) const;
//
// /**
// * Allocate new FGE primitive, paint all fibers to it and return the
// * reference of it.
// */
// FgeLineStrips* createNewFgePrimitive() const;
/**
* \return true If both clusters having same fibers in same order!
*/
bool operator==( const WFiberCluster& other ) const;
/**
* The opposite of the operator==
*/
bool operator!=( const WFiberCluster& other ) const;
private:
/**
* All indices in this set are members of this cluster
*/
std::list< size_t > m_memberIndices;
/**
* Reference to the real fibers, so painting is possible.
*/
boost::shared_ptr< WDataSetFibers > m_fibs;
/**
* Color which is used to paint the members of this cluster.
*/
WColor m_color;
};
std::ostream& operator<<( std::ostream& os, const WFiberCluster& c );
#endif // WFIBERCLUSTER_H
//---------------------------------------------------------------------------
//
// 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/>.
//
//---------------------------------------------------------------------------
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include <osg/Geode>
#include <osg/Geometry>
#include "WFiberClustering.h"
#include "../../math/WFiber.h"
#include "../../common/WColor.h"
#include "../../common/WLogger.h"
#include "../../common/WStatusReport.h"
#include "../../dataHandler/WDataHandler.h"
#include "../../dataHandler/WSubject.h"
#include "../../dataHandler/WDataSetFibers.h"
#include "../../dataHandler/WLoaderManager.h"
#include "../../kernel/WKernel.h"
#include "../../utils/WColorUtils.h"
WFiberClustering::WFiberClustering()
: WModule(),
m_maxDistance_t( 0.0 ),
m_dLtTableExists( false ),
m_minClusterSize( 10 ),
m_separatePrimitives( true ),
m_proximity_t( 0.0 ),
m_lastFibsSize( 0 )
{
}
WFiberClustering::~WFiberClustering()
{
}
const std::string WFiberClustering::getName() const
{
return std::string( "FiberClustering" );
}
const std::string WFiberClustering::getDescription() const
{
return std::string( "Clusters fibers from a WDataSetFibers" );
}
void WFiberClustering::threadMain()
{
boost::shared_ptr< WDataHandler > dataHandler;
// TODO(math): fix this hack when possible by using an input connector.
while( !WKernel::getRunningKernel() )
{
sleep( 1 );
}
while( !( dataHandler = WKernel::getRunningKernel()->getDataHandler() ) )
{
sleep( 1 );
}
while( !dataHandler->getNumberOfSubjects() )
{
sleep( 1 );
}
assert( m_fibs = boost::shared_dynamic_cast< WDataSetFibers >( dataHandler->getSubject( 0 )->getDataSet( 0 ) ) );
checkDLtLookUpTable();
cluster();
paint();
// Since the modules run in a separate thread: such loops are possible
while ( !m_FinishRequested )
{
// do fancy stuff
sleep( 1 );
}
}
void WFiberClustering::checkDLtLookUpTable()
{
if( m_dLtTableExists )
{
if( m_fibs->size() != m_lastFibsSize )
{
// throw away old invalid table
m_dLtTable.reset();
m_dLtTableExists = false;
}
}
}
void WFiberClustering::cluster()
{
std::cout << "Start clustering with " << m_fibs->size() << " fibers." << std::endl;
m_clusters.clear(); // remove evtl. old clustering
size_t numFibers = m_fibs->size();
std::vector< size_t > cid( numFibers, 0 ); // cluster number for each fib where it belongs to
for( size_t i = 0; i < numFibers; ++i )
{
m_clusters.push_back( WFiberCluster( i, m_fibs ) );
cid[i] = i;
}
if( !m_dLtTableExists ) // Refactor: methode mit bool returnvalue aufrufen
{
m_dLtTable.reset( new WDXtLookUpTable( numFibers ) );
}
for( size_t i = 0; i < numFibers; ++i ) // loop over all "symmetric" fibers pairs
{
const wmath::WFiber &q = (*m_fibs)[i];
for( size_t j = i + 1; j < numFibers; ++j )
{
const wmath::WFiber& r = (*m_fibs)[j];
if( cid[i] != cid[j] ) // both fibs are in different clusters
{
if( !m_dLtTableExists )
{
(*m_dLtTable)( i, j ) = q.dLt( r, m_proximity_t );
}
double dLt = (*m_dLtTable)( i, j );
if( dLt < m_maxDistance_t ) // q and r provide an inter-cluster-link
{
size_t qID = cid[i];
size_t rID = cid[j];