//---------------------------------------------------------------------------
//
// 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 .
//
//---------------------------------------------------------------------------
#include
#include
#include
#include
#include "WMDistanceMap.h"
#include "../../kernel/WKernel.h"
#include "../../kernel/WModuleFactory.h"
#include "../../dataHandler/WSubject.h"
#include "../../dataHandler/WGridRegular3D.h"
#include "../../common/WProgress.h"
WMDistanceMap::WMDistanceMap():
WModule()
{
// WARNING: initializing connectors inside the constructor will lead to an exception.
// NOTE: Do not use the module factory inside this constructor. This will cause a dead lock as the module factory is locked
// during construction of this instance and can then not be used to create another instance (Isosurface in this case).
}
WMDistanceMap::~WMDistanceMap()
{
// cleanup
removeConnectors();
}
boost::shared_ptr< WModule > WMDistanceMap::factory() const
{
return boost::shared_ptr< WModule >( new WMDistanceMap() );
}
const std::string WMDistanceMap::getName() const
{
return "Distance Map";
}
const std::string WMDistanceMap::getDescription() const
{
return "Computes a smoothed version of the dataset and a distance map on it.";
}
void WMDistanceMap::moduleMain()
{
// use the m_input "data changed" flag
m_moduleState.setResetable( true, true );
m_moduleState.add( m_input->getDataChangedCondition() );
// signal ready state
ready();
// loop until the module container requests the module to quit
while ( !m_shutdownFlag() )
{
// acquire data from the input connector
m_dataSet = m_input->getData();
if ( !m_dataSet.get() )
{
// OK, the output has not yet sent data
// NOTE: see comment at the end of this while loop for m_moduleState
debugLog() << "Waiting for data ...";
m_moduleState.wait();
continue;
}
// found some data
debugLog() << "Data changed. Updating ...";
boost::shared_ptr< WValueSet< float > > distanceMapValueSet = createOffset( m_dataSet );
m_distanceMapDataSet = boost::shared_ptr< WDataSetSingle >( new WDataSetSingle( distanceMapValueSet, m_dataSet->getGrid() ) );
WLogger::getLogger()->addLogMessage( "Done!", "Distance Map", LL_INFO );
// update the output
m_output->updateData( m_distanceMapDataSet );
// wait for new data change event or quit event
m_moduleState.wait();
}
}
void WMDistanceMap::connectors()
{
// initialize connectors
m_input = boost::shared_ptr >(
new WModuleInputData< WDataSetSingle >( shared_from_this(),
"in", "Dataset to compute distance map for." )
);
// add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
addConnector( m_input );
m_output = boost::shared_ptr >(
new WModuleOutputData< WDataSetSingle >( shared_from_this(),
"out", "Distance map for the input data set." )
);
// add it to the list of connectors. Please note, that a connector NOT added via addConnector will not work as expected.
addConnector( m_output );
// call WModule's initialization
WModule::connectors();
}
void WMDistanceMap::properties()
{
// no properties
}
template< typename T > boost::shared_ptr< WValueSet< float > > makeFloatValueSetHelper( boost::shared_ptr< WValueSet< T > > inSet )
{
assert( inSet->dimension() == 1 );
assert( inSet->order() == 0 );
std::vector< float > data( inSet->size() );
for( unsigned int i = 0; i < inSet->size(); ++i )
{
data[i] = static_cast< float >( inSet->getScalar( i ) );
}
boost::shared_ptr< WValueSet< float > > outSet;
outSet = boost::shared_ptr< WValueSet< float > >( new WValueSet< float >( ( *inSet ).order(), ( *inSet ).dimension(), data, W_DT_FLOAT ) );
return outSet;
}
boost::shared_ptr< WValueSet< float > > makeFloatValueSet( boost::shared_ptr< WValueSetBase > inSet )
{
assert( inSet->dimension() == 1 );
assert( inSet->order() == 0 );
switch( inSet->getDataType() )
{
case W_DT_UNSIGNED_CHAR:
return makeFloatValueSetHelper( boost::shared_dynamic_cast< WValueSet< unsigned char > >( inSet ) );
break;
case W_DT_INT16:
return makeFloatValueSetHelper( boost::shared_dynamic_cast< WValueSet< int16_t > >( inSet ) );
break;
case W_DT_SIGNED_INT:
return makeFloatValueSetHelper( boost::shared_dynamic_cast< WValueSet< int32_t > >( inSet ) );
break;
case W_DT_FLOAT:
return boost::shared_dynamic_cast< WValueSet< float > >( inSet );
break;
default:
assert( false && "Unknow data type in makeFloatDataSet" );
}
}
boost::shared_ptr< WValueSet< float > > WMDistanceMap::createOffset( boost::shared_ptr< const WDataSetSingle > dataSet )
{
std::vector floatDataset;
// wiebel: I know that this is not the most speed and memory efficient way to deal with different data types.
// However, it seems the most feasible at the moment (2009-11-24).
boost::shared_ptr< WValueSet< float > > valueSet = makeFloatValueSet( ( *dataSet ).getValueSet() );
assert( valueSet );
boost::shared_ptr< WGridRegular3D > grid = boost::shared_dynamic_cast< WGridRegular3D >( ( *dataSet ).getGrid() );
assert( grid );
// TODO(wiebel): we should be able to do all this without the value vector.
const std::vector< float >* source = valueSet->rawDataVectorPointer();
int b, r, c, bb, rr, r0, b0, c0;
int i, istart, iend;
int nbands, nrows, ncols, npixels;
int d, d1, d2, cc1, cc2;
float u, dmin, dmax;
bool *srcpix;
double g, *array;
nbands = grid->getNbCoordsZ();
nrows = grid->getNbCoordsY();
ncols = grid->getNbCoordsX();
#ifdef _WIN32
npixels = max( nbands, nrows );
#else
npixels = std::max( nbands, nrows );
#endif
array = new double[npixels];
npixels = nbands * nrows * ncols;
floatDataset.resize( npixels );
for( int i = 0; i < npixels; ++i)
{
floatDataset[i] = 0.0;
}
bool* bitmask = new bool[npixels];
for( int i = 0; i < npixels; ++i)
{
if (source->at(i) < 0.01)
bitmask[i] = true;
else
bitmask[i] = false;
}
dmax = 999999999.0;
// first pass
boost::shared_ptr< WProgress > progress1 = boost::shared_ptr< WProgress >(
new WProgress( "Distance Map", nbands + nbands + nrows + nbands + nbands + nbands )
);
m_progress->addSubProgress( progress1 );
for( b = 0; b < nbands; ++b)
{
++*progress1;
for( r = 0; r < nrows; ++r)
{
for( c = 0; c < ncols; ++c)
{
//if (VPixel(src,b,r,c,VBit) == 1)
if( bitmask[b * nrows * ncols + r * ncols + c] )
{
floatDataset[b * nrows * ncols + r * ncols + c] = 0;
continue;
}
srcpix = bitmask + b * nrows * ncols + r * ncols + c;
cc1 = c;
while (cc1 < ncols && *srcpix++ == 0)
cc1++;
d1 = ( cc1 >= ncols ? ncols : ( cc1 - c ) );
srcpix = bitmask + b * nrows * ncols + r * ncols + c;
cc2 = c;
while( cc2 >= 0 && *srcpix-- == 0 )
cc2--;
d2 = ( cc2 <= 0 ? ncols : ( c - cc2 ) );
if( d1 <= d2 )
{
d = d1;
c0 = cc1;
}
else
{
d = d2;
c0 = cc2;
}
floatDataset[b * nrows * ncols + r * ncols + c] = static_cast< float >( d * d );
}
}
}
// second pass
for( b = 0; b < nbands; b++ )
{
++*progress1;
for( c = 0; c < ncols; c++ )
{
for( r = 0; r < nrows; r++ )
array[r] = static_cast< double >( floatDataset[b * nrows * ncols + r * ncols + c] );
for( r = 0; r < nrows; r++ )
{
if( bitmask[b * nrows * ncols + r * ncols + c] == 1 )
continue;
dmin = dmax;
r0 = r;
g = sqrt( array[r] );
istart = r - static_cast< int >( g );
if( istart < 0 )
istart = 0;
iend = r + static_cast< int >( g + 1 );
if( iend >= nrows )
iend = nrows;
for( rr = istart; rr < iend; rr++ )
{
u = array[rr] + ( r - rr ) * ( r - rr );
if( u < dmin )
{
dmin = u;
r0 = rr;
}
}
floatDataset[b * nrows * ncols + r * ncols + c] = dmin;
}
}
}
// third pass
for( r = 0; r < nrows; r++ )
{
++*progress1;
for( c = 0; c < ncols; c++ )
{
for( b = 0; b < nbands; b++ )
array[b] = static_cast< double >( floatDataset[b * nrows * ncols + r * ncols + c] );
for( b = 0; b < nbands; b++ )
{
if (bitmask[b * nrows * ncols + r * ncols + c] == 1)
continue;
dmin = dmax;
b0 = b;
g = sqrt( array[b] );
istart = b - static_cast< int >( g - 1 );
if( istart < 0 )
istart = 0;
iend = b + static_cast< int >( g + 1 );
if( iend >= nbands )
iend = nbands;
for( bb = istart; bb < iend; bb++ )
{
u = array[bb] + ( b - bb ) * ( b - bb );
if( u < dmin )
{
dmin = u;
b0 = bb;
}
}
floatDataset[b * nrows * ncols + r * ncols + c] = dmin;
}
}
}
delete[] array;
float max = 0;
for( i = 0; i < npixels; ++i)
{
floatDataset[i] = sqrt( static_cast< double >( floatDataset[i] ) );
if (floatDataset[i] > max)
max = floatDataset[i];
}
for( i = 0; i < npixels; ++i)
{
floatDataset[i] = floatDataset[i] / max;
}
// filter with gauss
// create the filter kernel
double sigma = 4;
int dim = static_cast< int >( ( 3.0 * sigma + 1 ) );
int n = 2* dim + 1;
double step = 1;
float* kernel = new float[n];
double sum = 0;
double x = -static_cast< float >( dim );
double uu;
for( int i = 0; i < n; ++i)
{
uu = xxgauss( x, sigma );
sum += uu;
kernel[i] = uu;
x += step;
}
/* normalize */
for( int i = 0; i < n; ++i)
{
uu = kernel[i];
uu /= sum;
kernel[i] = uu;
}
d = n / 2;
float* float_pp;
std::vector tmp( npixels );
int c1, cc;
for( int i = 0; i < npixels; ++i )
{
tmp[i] = 0.0;
}
for( b = 0; b < nbands; ++b )
{
++*progress1;
for( r = 0; r < nrows; ++r )
{
for( c = d; c < ncols - d; ++c )
{
float_pp = kernel;
sum = 0;
c0 = c - d;
c1 = c + d;
for( cc = c0; cc <= c1; cc++ )
{
x = floatDataset[b * nrows * ncols + r * ncols + cc];
sum += x * ( *float_pp++ );
}
tmp[b * nrows * ncols + r * ncols + c] = sum;
}
}
}
int r1;
for( b = 0; b < nbands; ++b )
{
++*progress1;
for( r = d; r < nrows - d; ++r )
{
for( c = 0; c < ncols; ++c )
{
float_pp = kernel;
sum = 0;
r0 = r - d;
r1 = r + d;
for( rr = r0; rr <= r1; rr++ )
{
x = tmp[b * nrows * ncols + rr * ncols + c];
sum += x * ( *float_pp++ );
}
floatDataset[b * nrows * ncols + r * ncols + c] = sum;
}
}
}
int b1;
for( b = d; b < nbands - d; ++b )
{
++*progress1;
for( r = 0; r < nrows; ++r )
{
for( c = 0; c < ncols; ++c )
{
float_pp = kernel;
sum = 0;
b0 = b - d;
b1 = b + d;
for( bb = b0; bb <= b1; bb++ )
{
x = floatDataset[bb * nrows * ncols + r * ncols + c];
sum += x * ( *float_pp++ );
}
tmp[b * nrows * ncols + r * ncols + c] = sum;
}
}
}
delete[] bitmask;
delete[] kernel;
floatDataset = tmp;
boost::shared_ptr< WValueSet< float > > resultValueSet;
resultValueSet = boost::shared_ptr< WValueSet< float > >(
new WValueSet< float >( valueSet->order(), valueSet->dimension(), floatDataset, W_DT_FLOAT ) );
progress1->finish();
return resultValueSet;
}
double WMDistanceMap::xxgauss( double x, double sigma )
{
double y, z, a = 2.506628273;
z = x / sigma;
y = exp( ( double ) -z * z * 0.5 ) / ( sigma * a );
return y;
}