OpenWalnut  1.5.0dev
WMFiberSelection.cpp
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 #include <algorithm>
26 #include <memory>
27 #include <string>
28 #include <vector>
29 
30 #include <boost/tuple/tuple.hpp>
31 #include <osg/Geode>
32 #include <osg/Group>
33 #include <osg/Material>
34 #include <osg/ShapeDrawable>
35 #include <osg/StateAttribute>
36 
37 #include "WMFiberSelection.h"
38 #include "WMFiberSelection.xpm"
39 #include "core/common/WColor.h"
40 #include "core/kernel/WKernel.h"
41 
42 // This line is needed by the module loader to actually find your module.
43 W_LOADABLE_MODULE( WMFiberSelection )
44 
46  WModule()
47 {
48 }
49 
51 {
52  // Cleanup!
53 }
54 
55 std::shared_ptr< WModule > WMFiberSelection::factory() const
56 {
57  return std::shared_ptr< WModule >( new WMFiberSelection() );
58 }
59 
60 const char** WMFiberSelection::getXPMIcon() const
61 {
62  return fiberSelection_xpm;
63 }
64 
65 const std::string WMFiberSelection::getName() const
66 {
67  // Specify your module name here. This name must be UNIQUE!
68  return "Fiber Selection";
69 }
70 
71 const std::string WMFiberSelection::getDescription() const
72 {
73  // Specify your module description here. Be detailed. This text is read by the user.
74  return "Select fibers in a given dataset using two additional datasets as volume of interest. The purpose of this module is to allow the user"
75  "to select a bunch of fibers which go through two regions defined by two scalar datasets. A fiber goes through both volumes of interest"
76  "if and only if at least one of the vertices of a fiber is inside a voxel of the scalar datasets with a value greater than a specified"
77  "threshold. Currently, this only works good for large regions and/or densely sampled fibers.";
78 }
79 
81 {
82  // The input fiber dataset
83  m_fiberInput = std::shared_ptr< WModuleInputData < WDataSetFibers > >(
84  new WModuleInputData< WDataSetFibers >( shared_from_this(), "fibers", "The fiber dataset to select fibers from" )
85  );
86 
87  // As properties, every connector needs to be added to the list of connectors.
89 
90  // The first VOI dataset
91  m_voi1Input = std::shared_ptr< WModuleInputData < WDataSetSingle > >(
92  new WModuleInputData< WDataSetSingle >( shared_from_this(), "VOI1", "The first volume of interest." )
93  );
94 
95  // As properties, every connector needs to be added to the list of connectors.
97 
98  // The second VOI dataset
99  m_voi2Input = std::shared_ptr< WModuleInputData < WDataSetSingle > >(
100  new WModuleInputData< WDataSetSingle >( shared_from_this(), "VOI2", "The second volume of interest." )
101  );
102 
103  // As properties, every connector needs to be added to the list of connectors.
105 
106  // the selected fibers go to this output
107  m_fiberOutput = std::shared_ptr< WModuleOutputData < WDataSetFibers > >(
108  new WModuleOutputData< WDataSetFibers >( shared_from_this(),
109  "out", "The fiber dataset only containing fibers which got through both volume of interest." )
110  );
111 
112  // As above: make it known.
114 
115  // the selected fibers (as clusters) go to this output
116  m_clusterOutput = std::shared_ptr< WModuleOutputData < WFiberCluster > >(
117  new WModuleOutputData< WFiberCluster >( shared_from_this(),
118  "cluster", "The cluster dataset only containing fibers which got through both volume of interest." )
119  );
120 
121  // As above: make it known.
123 
124  // call WModules initialization
126 }
127 
129 {
130  // used to notify about changed properties
131  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
132 
133  // the threshold for testing whether a fiber vertex is inside a volume of interest.
134  m_voi1Threshold = m_properties->addProperty( "VOI1 threshold", "The threshold uses for determining whether a fiber is inside the first VOI"
135  "dataset or not.", 5.0, m_propCondition );
136  m_voi2Threshold = m_properties->addProperty( "VOI2 threshold", "The threshold uses for determining whether a fiber is inside the second VOI"
137  "dataset or not.", 5.0, m_propCondition );
138  m_cutFibers = m_properties->addProperty( "Cut fibers", "Cut the fibers after they gone through both VOI.", true, m_propCondition );
139 
140  m_preferShortestPath = m_properties->addProperty( "Prefer shortest path", "Determines whether the fibers should be cut on the entry and "
141  "exit of a VOI. This should prevent the fibers from going deep into the VOI's.", false, m_propCondition );
142 
144 }
145 
146 /**
147  * Namespace for the FibTrace structure.
148  */
149 namespace
150 {
151  /**
152  * This structure stores some tracing information along the fiber. It is defined in this file scope as local types can't be used as template
153  * parameters. (but it is needed in a list)
154  */
155  typedef struct
156  {
157  size_t idx; //!< current index
158  bool which; //!< true if isInside is true for VOI1, for VOI2 it is false
159  // size_t lastIdx; //!< the last index , idx-1 <- not needed
160  bool isInside; //!< true if idx is inside a VOI
161  bool wasInside; //!< true if idx-1 was inside a VOI
162  }
163  FibTrace;
164 }
165 
167 {
168  //
169  m_moduleState.setResetable( true, true );
170  m_moduleState.add( m_fiberInput->getDataChangedCondition() );
171  m_moduleState.add( m_voi1Input->getDataChangedCondition() );
172  m_moduleState.add( m_voi2Input->getDataChangedCondition() );
174 
175  // Signal ready state. Now your module can be connected by the container, which owns the module.
176  ready();
177 
178  // Now wait for data
179  while( !m_shutdownFlag() )
180  {
182 
183  // woke up since the module is requested to finish
184  if( m_shutdownFlag() )
185  {
186  break;
187  }
188 
189  std::shared_ptr< WDataSetFibers > newFibers = m_fiberInput->getData();
190  std::shared_ptr< WDataSetSingle > newVoi1 = m_voi1Input->getData();
191  std::shared_ptr< WDataSetSingle > newVoi2 = m_voi2Input->getData();
192 
193  bool dataChanged = ( m_fibers != newFibers ) || ( m_voi1 != newVoi1 ) ||( m_voi2 != newVoi2 );
194  bool dataValid = ( newFibers && newVoi1 && newVoi2 );
195  bool propChanged = ( m_cutFibers->changed() || m_voi1Threshold->changed() || m_voi2Threshold->changed() || m_preferShortestPath->changed() );
196 
197  // cleanup if no valid data is available
198  if( !dataValid )
199  {
200  debugLog() << "Resetting output.";
201 
202  // remove my refs to the data
203  m_fibers.reset();
204  m_voi1.reset();
205  m_voi2.reset();
206 
207  // reset outputs too
208  m_fiberOutput->reset();
209  m_clusterOutput->reset();
210  }
211 
212  if( ( propChanged || dataChanged ) && dataValid )
213  {
214  debugLog() << "Data received. Recalculating.";
215 
216  // replace old data
217  m_fibers = newFibers;
218  m_voi1 = newVoi1;
219  m_voi2 = newVoi2;
220 
221  // get the needed properties
222  double voi1Threshold = m_voi1Threshold->get( true );
223  double voi2Threshold = m_voi2Threshold->get( true );
224  bool cutFibers = m_cutFibers->get( true );
225  bool preferShortestPath = m_preferShortestPath->get( true );
226 
227  // get the fiber definitions
228  std::shared_ptr< std::vector< size_t > > fibStart = m_fibers->getLineStartIndexes();
229  std::shared_ptr< std::vector< size_t > > fibLen = m_fibers->getLineLengths();
230  std::shared_ptr< std::vector< float > > fibVerts = m_fibers->getVertices();
231 
232  // currently, both grids need to be the same
233  // the grid of voi1 and voi2 is needed here
234  std::shared_ptr< WGridRegular3D > grid1 = std::dynamic_pointer_cast< WGridRegular3D >( m_voi1->getGrid() );
235  std::shared_ptr< WGridRegular3D > grid2 = std::dynamic_pointer_cast< WGridRegular3D >( m_voi2->getGrid() );
236 
237  // the list of fibers
238  std::vector< boost::tuple< size_t, size_t, size_t > > matches; // a match contains the fiber ID, the start vertex ID and the stop ID
239 
240  // progress indication
241  std::shared_ptr< WProgress > progress1( new WProgress( "Checking fibers against ", fibStart->size() ) );
242  m_progress->addSubProgress( progress1 );
243 
244  // there are several scenarios possible, how the VOIs can be. They can intersect each other, one being inside the other or they might
245  // be spatial distinct. To handle all those scenarios, the fiber segments get interpreted as some kind of ray and a list of all hit
246  // points with one of the VOIs is stored in a list and can be handled afterwards in several ways.
247 
248  // for each fiber:
249  debugLog() << "Iterating over all fibers.";
250  for( size_t fidx = 0; fidx < fibStart->size() ; ++fidx )
251  {
252  ++*progress1;
253 
254  // the start vertex index
255  size_t sidx = fibStart->at( fidx ) * 3;
256 
257  // the length of the fiber
258  size_t len = fibLen->at( fidx );
259 
260  // trace information for both VOI
261  FibTrace current1 = { 0, true, false, false }; // NOLINT
262  FibTrace current2 = { 0, false, false, false }; // NOLINT
263  std::vector< FibTrace > hits1;
264  std::vector< FibTrace > hits2;
265 
266  // walk along the fiber
267  for( size_t k = 0; k < len; ++k )
268  {
269  float x = fibVerts->at( ( 3 * k ) + sidx );
270  float y = fibVerts->at( ( 3 * k ) + sidx + 1 );
271  float z = fibVerts->at( ( 3 * k ) + sidx + 2 );
272 
273  // get the voxel id
274  int voxel1 = grid1->getVoxelNum( WPosition( x, y, z ) );
275  int voxel2 = grid2->getVoxelNum( WPosition( x, y, z ) );
276  if( ( voxel1 < 0 ) || ( voxel2 < 0 ) )
277  {
278  warnLog() << "Fiber vertex (" << x << "," << y << "," << z << ") not in VOI1 or VOI2 grid. Ignoring vertex.";
279  continue;
280  }
281 
282  // get the value
283  double value1 = m_voi1->getSingleRawValue( voxel1 );
284  double value2 = m_voi2->getSingleRawValue( voxel2 );
285 
286  // update trace structs for both VOI
287  current1.idx = k;
288  current1.wasInside = current1.isInside;
289  current1.isInside = ( value1 >= voi1Threshold );
290  current2.idx = k;
291  current2.wasInside = current2.isInside;
292  current2.isInside = ( value2 >= voi2Threshold );
293 
294  // VOI1 hit?
295  if( current1.wasInside ^ current1.isInside )
296  {
297  hits1.push_back( current1 );
298  }
299  // VOI2 Hit?
300  if( current2.wasInside ^ current2.isInside )
301  {
302  hits2.push_back( current2 );
303  }
304  }
305 
306  // Now, a list of all intersections of the current fiber "fidx" with the VOI's is available. There are three possibilities. One
307  // VOI is inside the other, the VOI's intersect each other or the VOI's are spatially distinct.
308 
309  // If one VOI completely inside another, ignore the fiber since it does not really make sense. So we only need to handle the
310  // other two cases
311 
312  if( !preferShortestPath )
313  {
314  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
315  // Strategy 1: Always use the longest path
316  // - this is a good strategy for both remaining cases
317  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
318 
319  // always use the longest fiber path
320  if( ( hits1.size() >= 1 ) && ( hits2.size() >= 1 ) )
321  {
322  debugLog() << "Fiber " << fidx << " inside VOI1 and VOI2.";
323  matches.push_back( boost::make_tuple( fidx, std::min( hits1[ 0 ].idx, hits2[ 0 ].idx ),
324  std::max( hits1[ hits1.size() - 1 ].idx, hits2[ hits2.size() - 1 ].idx ) ) );
325  }
326  }
327  else
328  {
329  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
330  // Strategy 2: Find the shortest path between the exit-hit of VOI1 and entry-hit of VOI2
331  // - this should create fibers if very similar length
332  // - might be problematic with overlapping VOI's
333  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
334 
335  // always use the longest fiber path
336  if( ( hits1.size() >= 1 ) && ( hits2.size() >= 1 ) )
337  {
338  debugLog() << "Fiber " << fidx << " inside VOI1 and VOI2.";
339  matches.push_back( boost::make_tuple( fidx, std::min( hits1[ 0 ].idx, hits2[ hits2.size() - 1 ].idx ),
340  std::max( hits1[ hits1.size() - 1 ].idx, hits2[ 0 ].idx ) ) );
341  }
342  }
343  }
344  debugLog() << "Iterating over all fibers: done!";
345  progress1->finish();
346 
347  // give some feedback
348  infoLog() << "Found " << matches.size() << " fibers going through VOI1 and VOI2.";
349 
350  // combine it to a new fiber dataset
351  // the required arrays:
352  std::shared_ptr< std::vector< float > > newFibVerts( new std::vector< float >() );
353  std::shared_ptr< std::vector< size_t > > newFibStart( new std::vector< size_t >() );
354  std::shared_ptr< std::vector< size_t > > newFibLen( new std::vector< size_t >() );
355  std::shared_ptr< std::vector< size_t > > newFibVertsRev( new std::vector< size_t >() );
356 
357  progress1 = std::shared_ptr< WProgress >( new WProgress( "Creating Output Dataset.", matches.size() ) );
358  m_progress->addSubProgress( progress1 );
359  debugLog() << "Creating new Fiber Dataset and Cluster.";
360 
361  // the cluster which gets iteratively build
362  std::shared_ptr< WFiberCluster > newFiberCluster( new WFiberCluster() );
363 
364  // add each match to the above arrays
365  size_t curVertIdx = 0; // the current index in the vertex array
366  size_t curRealFibIdx = 0; // since some fibers get discarded, the for loop counter "i" is not the correct fiber index
367  for( size_t i = 0; i < matches.size(); ++i )
368  {
369  ++*progress1;
370 
371  // start index and length of original fiber
372  size_t sidx = fibStart->at( matches[ i ].get< 0 >() ) * 3;
373  size_t len = fibLen->at( matches[ i ].get< 0 >() );
374 
375  if( cutFibers )
376  {
377  // if the fibers should be cut: add an offset to the start index and recalculate the length
378  sidx = sidx + ( matches[ i ].get< 1 >() * 3 );
379  len = matches[ i ].get< 2 >() - matches[ i ].get< 1 >();
380  }
381 
382  // discard fibers with less than one line segment
383  if( len < 2 )
384  {
385  continue;
386  }
387 
388  // set the new length
389  newFibLen->push_back( len );
390 
391  // set new start index
392  newFibStart->push_back( curVertIdx );
393 
394  // copy the index to the cluster
395  WFiberCluster a( curRealFibIdx );
396  newFiberCluster->merge( a );
397 
398  // copy the fiber vertices
399  for( size_t vi = 0; vi < len; ++vi )
400  {
401  curVertIdx++;
402 
403  newFibVerts->push_back( fibVerts->at( sidx + ( 3 * vi ) ) );
404  newFibVerts->push_back( fibVerts->at( sidx + ( 3 * vi ) + 1 ) );
405  newFibVerts->push_back( fibVerts->at( sidx + ( 3 * vi ) + 2 ) );
406  newFibVertsRev->push_back( curRealFibIdx );
407  newFibVertsRev->push_back( curRealFibIdx );
408  newFibVertsRev->push_back( curRealFibIdx );
409  }
410 
411  curRealFibIdx++;
412  }
413  progress1->finish();
414 
415  // create the new dataset
416  std::shared_ptr< WDataSetFibers > newFibers( new WDataSetFibers( newFibVerts, newFibStart, newFibLen, newFibVertsRev ) );
417 
418  // create a WDataSetFiberVector which is needed for the WFiberCluster
419  progress1 = std::shared_ptr< WProgress >( new WProgress( "Creating Output Vector Dataset." ) );
420  m_progress->addSubProgress( progress1 );
421  debugLog() << "Creating Fiber Vector Dataset.";
422 
423  std::shared_ptr< WDataSetFiberVector > newFiberVector( new WDataSetFiberVector( newFibers )
424  );
425  newFiberCluster->setDataSetReference( newFiberVector );
426  newFiberCluster->generateCenterLine();
427  progress1->finish();
428 
429  // finally -> update the output
430  m_fiberOutput->updateData( newFibers );
431  m_clusterOutput->updateData( newFiberCluster );
432  }
433  }
434 }
435 
437 {
438  // handle activation
439 
440  // Always call WModule's activate!
442 }
443 
virtual void wait() const
Wait for the condition.
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
virtual void add(std::shared_ptr< WCondition > condition)
Adds another condition to the set of conditions to wait for.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
Represents a simple set of WFibers.
Represents a simple set of WFibers.
Represents a cluster of indices of a WDataSetFiberVector.
Definition: WFiberCluster.h:45
This module handles selection of fibers based on two volumes of interest.
virtual const std::string getDescription() const
Gives back a description of this module.
std::shared_ptr< WDataSetSingle > m_voi2
The VOI2 dataset (from m_voi2Input).
WPropDouble m_voi1Threshold
VOI1 threshold.
std::shared_ptr< WModuleOutputData< WFiberCluster > > m_clusterOutput
The cluster dataset created from m_fiberOutput.
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
virtual ~WMFiberSelection()
Destructor.
std::shared_ptr< WModuleInputData< WDataSetFibers > > m_fiberInput
The fiber dataset which is going to be filtered.
WPropBool m_cutFibers
Cut the fibers when they are outside the VOI?
WPropDouble m_voi2Threshold
VOI2 threshold.
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
std::shared_ptr< WModuleInputData< WDataSetSingle > > m_voi1Input
The first VOI.
virtual const std::string getName() const
Gives back the name of this module.
virtual void connectors()
Initialize the connectors this module is using.
virtual void activate()
Callback for m_active.
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
std::shared_ptr< WDataSetFibers > m_fibers
The fiber dataset (from m_fiberInput).
std::shared_ptr< WModuleOutputData< WDataSetFibers > > m_fiberOutput
The output connector used to provide the calculated data to other modules.
std::shared_ptr< WModuleInputData< WDataSetSingle > > m_voi2Input
The second VOI.
virtual void properties()
Initialize the properties for this module.
std::shared_ptr< WDataSetSingle > m_voi1
The VOI1 dataset (from m_voi1Input).
WPropBool m_preferShortestPath
Should the fibers be cut to avoid having them inside the VOI.
virtual void moduleMain()
Entry point after loading the module.
WMFiberSelection()
Default constructor.
Class offering an instantiate-able data connection between modules.
Class offering an instantiate-able data connection between modules.
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
virtual void properties()
Initialize properties in this function.
Definition: WModule.cpp:212
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
void addConnector(std::shared_ptr< WModuleInputConnector > con)
Adds the specified connector to the list of inputs.
Definition: WModule.cpp:108
wlog::WStreamedLogger warnLog() const
Logger instance for comfortable warning- logs.
Definition: WModule.cpp:580
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
virtual void activate()
Callback for m_active.
Definition: WModule.cpp:220
std::shared_ptr< WProgressCombiner > m_progress
Progress indicator used as parent for all progress' of this module.
Definition: WModule.h:652
wlog::WStreamedLogger infoLog() const
Logger instance for comfortable info logging.
Definition: WModule.cpp:565
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
This only is a 3d double vector.
Class managing progress inside of modules.
Definition: WProgress.h:42
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.