1 // =========================================================================
2 // @author Leonardo Florez Valencia (florez-l@javeriana.edu.co)
3 // =========================================================================
7 #include <tclap/CmdLine.h>
9 #include <itkAndImageFilter.h>
10 #include <itkBinaryThresholdImageFilter.h>
11 #include <itkMinimumMaximumImageCalculator.h>
12 #include <itkInvertIntensityImageFilter.h>
13 #include <itkHessianRecursiveGaussianImageFilter.h>
14 #include <itkHessian3DToVesselnessMeasureImageFilter.h>
16 #include <fpa/Common/SliceBySliceRandomWalker.h>
17 #include <fpa/Filters/Image/ExtractSkeleton.h>
18 #include <fpa/Filters/Image/Mori.h>
19 #include <fpa/Filters/Image/RandomWalker.h>
20 #include <fpa/Functors/Dijkstra/Image/Gaussian.h>
22 #include "MoriLabelling.h"
25 // -------------------------------------------------------------------------
28 : m_UndefinedLabel( 0 ),
32 this->m_StrArgs[ "input" ] = TStrArg( "", "I", true );
33 this->m_StrArgs[ "vesselness" ] = TStrArg( "", "V", false );
34 this->m_StrArgs[ "mori" ] = TStrArg( "", "M", false );
35 this->m_StrArgs[ "labels" ] = TStrArg( "", "L", false );
36 this->m_StrArgs[ "fastrw" ] = TStrArg( "", "F", false );
37 this->m_StrArgs[ "slicerw" ] = TStrArg( "", "R", false );
38 this->m_StrArgs[ "andrw" ] = TStrArg( "", "A", false );
39 this->m_StrArgs[ "fastrw_skeleton" ] = TStrArg( "", "G", false );
40 this->m_StrArgs[ "slicerw_skeleton" ] = TStrArg( "", "U", false );
41 this->m_StrArgs[ "andrw_skeleton" ] = TStrArg( "", "W", false );
42 this->m_StrArgs[ "fastrw_points" ] = TStrArg( "", "B", false );
43 this->m_StrArgs[ "slicerw_points" ] = TStrArg( "", "C", false );
44 this->m_StrArgs[ "andrw_points" ] = TStrArg( "", "D", false );
45 this->m_StrArgs[ "seed_file" ] = TStrArg( "", "P", false );
46 this->m_StrArgs[ "seed" ] = TStrArg( "", "S", false );
47 this->m_StrArgs[ "seed_type" ] = TStrArg( "point", "T", false );
49 this->m_DblArgs[ "beta" ] = TDblArg( 2.5, "b", false );
50 this->m_DblArgs[ "epsilon" ] = TDblArg( 1e-5, "e", false );
51 this->m_DblArgs[ "vesselness_sigma" ] = TDblArg( 0.5, "a", false );
52 this->m_DblArgs[ "vesselness_alpha1" ] = TDblArg( 0.5, "c", false );
53 this->m_DblArgs[ "vesselness_alpha2" ] = TDblArg( 2, "d", false );
54 this->m_DblArgs[ "mori_min_thr" ] = TDblArg( -850, "f", false );
55 this->m_DblArgs[ "mori_kernel" ] = TDblArg( 20, "g", false );
56 this->m_DblArgs[ "mori_signal_thr" ] = TDblArg( 100, "i", false );
57 this->m_DblArgs[ "mori_signal_influence" ] = TDblArg( 0.5, "j", false );
58 this->m_DblArgs[ "mori_lower" ] = TDblArg( -1024, "k", false );
59 this->m_DblArgs[ "mori_upper" ] = TDblArg( 0, "l", false );
60 this->m_DblArgs[ "mori_delta" ] = TDblArg( 1, "m", false );
61 this->m_DblArgs[ "slicerw_thr" ] = TDblArg( 5, "n", false );
62 this->m_DblArgs[ "fastrw_thr" ] = TDblArg( 5, "o", false );
63 this->m_DblArgs[ "labels_upper_thr" ] = TDblArg( -400, "p", false );
66 // -------------------------------------------------------------------------
72 // -------------------------------------------------------------------------
73 void CTBronchi::Process::
74 ParseArguments( int argc, char* argv[] )
76 typedef TCLAP::ValueArg< TString > _TStrArg;
77 typedef TCLAP::ValueArg< TScalar > _TDblArg;
78 std::map< TString, _TStrArg* > strArgs;
79 std::map< TString, _TDblArg* > dblArgs;
82 TStrArgs::iterator sIt = this->m_StrArgs.begin( );
83 for( ; sIt != this->m_StrArgs.end( ); ++sIt )
84 strArgs[ sIt->first ] = new _TStrArg(
85 std::get< 1 >( sIt->second ),
86 sIt->first, sIt->first,
87 std::get< 2 >( sIt->second ),
88 std::get< 0 >( sIt->second ),
89 TString( "string (" ) + std::get< 0 >( sIt->second ) + ")"
92 TDblArgs::iterator dIt = this->m_DblArgs.begin( );
93 for( ; dIt != this->m_DblArgs.end( ); ++dIt )
96 v << "value (" << std::get< 0 >( dIt->second ) << ")";
97 dblArgs[ dIt->first ] = new _TDblArg(
98 std::get< 1 >( dIt->second ),
99 dIt->first, dIt->first,
100 std::get< 2 >( dIt->second ),
101 std::get< 0 >( dIt->second ),
108 std::map< TString, _TStrArg* >::iterator saIt;
109 std::map< TString, _TDblArg* >::iterator daIt;
112 TCLAP::CmdLine cmd( "CTBronchi pipeline", ' ', "1.0.0" );
113 for( saIt = strArgs.begin( ); saIt != strArgs.end( ); ++saIt )
114 cmd.add( *( saIt->second ) );
115 for( daIt = dblArgs.begin( ); daIt != dblArgs.end( ); ++daIt )
116 cmd.add( *( daIt->second ) );
117 cmd.parse( argc, argv );
119 catch( TCLAP::ArgException& err )
121 std::stringstream msg;
122 msg << err.error( ) << " " << err.argId( );
123 throw std::runtime_error( msg.str( ) );
128 for( saIt = strArgs.begin( ); saIt != strArgs.end( ); ++saIt )
130 std::get< 0 >( this->m_StrArgs[ saIt->first ] ) =
131 saIt->second->getValue( );
135 for( daIt = dblArgs.begin( ); daIt != dblArgs.end( ); ++daIt )
137 std::get< 0 >( this->m_DblArgs[ daIt->first ] ) =
138 daIt->second->getValue( );
143 // Compute base filenames
144 TString bname = std::get< 0 >( this->m_StrArgs[ "input" ] );
145 bname = bname.substr( 0, bname.find_last_of( "." ) );
146 const unsigned int N = 6;
147 const unsigned int M = 6;
148 TString names[ N + M ] =
163 for( unsigned int i = 0; i < N; ++i )
164 if( std::get< 0 >( this->m_StrArgs[ names[ i ] ] ) == "" )
165 std::get< 0 >( this->m_StrArgs[ names[ i ] ] ) =
166 bname + "_" + names[ i ] + ".mha";
167 for( unsigned int i = 0; i < M; ++i )
168 if( std::get< 0 >( this->m_StrArgs[ names[ i + N ] ] ) == "" )
169 std::get< 0 >( this->m_StrArgs[ names[ i + N ] ] ) =
170 bname + "_" + names[ i + N ] + ".txt";
173 // -------------------------------------------------------------------------
174 void CTBronchi::Process::
177 this->_Input( this->m_Input );
178 this->_Vesselness( this->m_Input, this->m_Vesselness );
179 this->_Mori( this->m_Input, this->m_Mori, this->m_Seed );
180 this->_MoriLabelling(
181 this->m_Input, this->m_Mori, this->m_Vesselness, this->m_Labels
183 this->_FastRW( this->m_Input, this->m_Labels, this->m_FastRW );
185 this->m_Input, this->m_Mori, this->m_Vesselness, this->m_SliceRW
187 this->_AndImages( this->m_FastRW, this->m_SliceRW, this->m_AndRW );
189 this->m_SliceRW, this->m_SliceRWSkeleton,
190 std::get< 0 >( this->m_StrArgs[ "slicerw_skeleton" ] ),
191 std::get< 0 >( this->m_StrArgs[ "slicerw_points" ] )
194 this->m_AndRW, this->m_AndRWSkeleton,
195 std::get< 0 >( this->m_StrArgs[ "andrw_skeleton" ] ),
196 std::get< 0 >( this->m_StrArgs[ "andrw_points" ] )
199 this->m_FastRW, this->m_FastRWSkeleton,
200 std::get< 0 >( this->m_StrArgs[ "fastrw_skeleton" ] ),
201 std::get< 0 >( this->m_StrArgs[ "fastrw_points" ] )
205 // -------------------------------------------------------------------------
206 template< class _TImage >
207 void CTBronchi::Process::
208 _Input( _TImage& input )
210 TString sname = std::get< 0 >( this->m_StrArgs[ "seed_file" ] );
211 TString seed = std::get< 0 >( this->m_StrArgs[ "seed" ] );
212 TString stype = std::get< 0 >( this->m_StrArgs[ "seed_type" ] );
213 if( sname == "" && seed == "" )
214 throw std::runtime_error( "No seed given." );
217 std::ifstream str( sname.c_str( ) );
218 str.seekg( 0, std::ios::end );
219 seed.reserve( str.tellg( ) );
220 str.seekg( 0, std::ios::beg );
222 std::istreambuf_iterator< char >( str ),
223 std::istreambuf_iterator< char >( )
230 std::istringstream sSeed( seed );
233 if( stype == "point" )
234 sSeed >> pnt[ 0 ] >> pnt[ 1 ] >> pnt[ 2 ];
236 sSeed >> idx[ 0 ] >> idx[ 1 ] >> idx[ 2 ];
239 TString iname = std::get< 0 >( this->m_StrArgs[ "input" ] );
240 double t = input.Load( iname );
242 std::runtime_error( TString( "Could not read \"" ) + iname + "\"" );
243 std::cout << "\"" << iname << "\" read in " << t << " s." << std::endl;
246 if( stype == "point" )
247 input.Get( )->TransformPhysicalPointToIndex( pnt, idx );
249 input.Get( )->TransformIndexToPhysicalPoint( idx, pnt );
250 this->m_Seed = TSeed( idx, pnt );
253 // -------------------------------------------------------------------------
254 template< class _TInput, class _TVesselness >
255 void CTBronchi::Process::
256 _Vesselness( _TInput& input, _TVesselness& vesselness )
258 TString iname = std::get< 0 >( this->m_StrArgs[ "vesselness" ] );
259 double t = vesselness.Load( iname );
265 typedef itk::MinimumMaximumImageCalculator< typename _TInput::TImage > _TMinMax;
266 typename _TMinMax::Pointer minMax = _TMinMax::New( );
267 minMax->SetImage( input.Get( ) );
270 // Invert intensities
271 typedef CTBronchi::Filter< itk::InvertIntensityImageFilter< typename _TInput::TImage > > _TInverter;
273 inverter.Get( )->SetInput( input.Get( ) );
274 inverter.Get( )->SetMaximum( minMax->GetMaximum( ) );
275 double t1 = inverter.Update( );
277 std::cout << "Inversion executed in " << t1 << " s" << std::endl;
279 // Compute hessian image
280 typedef CTBronchi::Filter< itk::HessianRecursiveGaussianImageFilter< typename _TInput::TImage > > _THessian;
282 hessian.Get( )->SetInput( inverter.Get( )->GetOutput( ) );
283 hessian.Get( )->SetSigma(
284 std::get< 0 >( this->m_DblArgs[ "vesselness_sigma" ] )
286 t1 = hessian.Update( );
288 std::cout << "Hessian executed in " << t1 << " s" << std::endl;
291 typedef CTBronchi::Filter< itk::Hessian3DToVesselnessMeasureImageFilter< typename _TVesselness::TPixel > > _TVesselnessFilter;
292 _TVesselnessFilter vFilter;
293 vFilter.Get( )->SetInput( hessian.Get( )->GetOutput( ) );
294 vFilter.Get( )->SetAlpha1(
295 std::get< 0 >( this->m_DblArgs[ "vesselness_alpha1" ] )
297 vFilter.Get( )->SetAlpha2(
298 std::get< 0 >( this->m_DblArgs[ "vesselness_alpha2" ] )
300 t1 = vFilter.Update( );
302 std::cout << "Hessian measure computed in " << t1 << " s." << std::endl;
304 vesselness.Set( vFilter.Get( )->GetOutput( ) );
305 t1 = vesselness.Save( iname );
307 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
310 std::cout << "Vesselness computed in " << t << " s." << std::endl;
313 // -------------------------------------------------------------------------
314 template< class _TInput, class _TMori, class _TSeed >
315 void CTBronchi::Process::
316 _Mori( _TInput& input, _TMori& mori, _TSeed& seed )
318 TString iname = std::get< 0 >( this->m_StrArgs[ "mori" ] );
319 double t = mori.Load( iname );
325 typedef CTBronchi::Filter< fpa::Filters::Image::Mori< TPixelImage::TImage, TLabelImage::TImage > > _TMoriFilter;
326 _TMoriFilter mFilter;
327 mFilter.Get( )->SetInput( input.Get( ) );
328 mFilter.Get( )->SetSeed( seed.second );
329 mFilter.Get( )->SetInsideValue( this->m_InsideLabel );
330 mFilter.Get( )->SetOutsideValue( this->m_UndefinedLabel );
331 mFilter.Get( )->SetMinimumThreshold(
332 std::get< 0 >( this->m_DblArgs[ "mori_min_thr" ] )
334 mFilter.Get( )->SetSignalKernelSize(
335 std::get< 0 >( this->m_DblArgs[ "mori_kernel" ] )
337 mFilter.Get( )->SetSignalThreshold(
338 std::get< 0 >( this->m_DblArgs[ "mori_signal_thr" ] )
340 mFilter.Get( )->SetSignalInfluence(
341 std::get< 0 >( this->m_DblArgs[ "mori_signal_influence" ] )
343 mFilter.Get( )->SetThresholds(
344 std::get< 0 >( this->m_DblArgs[ "mori_lower" ] ),
345 std::get< 0 >( this->m_DblArgs[ "mori_upper" ] ),
346 std::get< 0 >( this->m_DblArgs[ "mori_delta" ] )
348 double t1 = mFilter.Update( );
350 std::cout << "Segmentation computed in " << t1 << " s." << std::endl;
352 mori.Set( mFilter.Get( )->GetOutput( ) );
353 t1 = mori.Save( iname );
355 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
358 std::cout << "Mori segmentation computed in " << t << " s." << std::endl;
361 // -------------------------------------------------------------------------
362 template< class _TInput, class _TMori, class _TVesselness, class _TLabels >
363 void CTBronchi::Process::
365 _TInput& input, _TMori& mori, _TVesselness& vesselness, _TLabels& labels
368 TString iname = std::get< 0 >( this->m_StrArgs[ "labels" ] );
369 double t = labels.Load( iname );
375 typedef CTBronchi::Filter< CTBronchi::MoriLabelling< typename _TInput::TImage, typename _TLabels::TImage, typename _TVesselness::TImage > > _TLabelling;
376 _TLabelling labelling;
377 labelling.Get( )->SetInput( input.Get( ) );
378 labelling.Get( )->SetInputLabels( mori.Get( ) );
379 labelling.Get( )->SetInputVesselness( vesselness.Get( ) );
380 labelling.Get( )->SetVesselnessThreshold( std::get< 0 >( this->m_DblArgs[ "fastrw_thr" ] ) );
381 labelling.Get( )->SetUpperThreshold( std::get< 0 >( this->m_DblArgs[ "labels_upper_thr" ] ) );
382 labelling.Get( )->SetInsideValue( this->m_InsideLabel );
383 labelling.Get( )->SetOutsideValue( this->m_OutsideLabel );
384 labelling.Get( )->SetFillValue( this->m_OutsideLabel );
385 double t1 = labelling.Update( );
387 std::cout << "Labelling computed in " << t1 << " s." << std::endl;
389 labels.Set( labelling.Get( )->GetOutput( ) );
390 t1 = labels.Save( iname );
392 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
395 std::cout << "Mori labelling computed in " << t << " s." << std::endl;
398 // -------------------------------------------------------------------------
399 template< class _TInput, class _TLabels, class _TFastRW >
400 void CTBronchi::Process::
401 _FastRW( _TInput& input, _TLabels& labels, _TFastRW& fastrw )
403 TString iname = std::get< 0 >( this->m_StrArgs[ "fastrw" ] );
404 double t = fastrw.Load( iname );
409 // Prepare weight functor
410 typedef fpa::Functors::Dijkstra::Image::Gaussian< typename _TInput::TImage, typename _TFastRW::TPixel > TWeight;
411 typename TWeight::Pointer weight = TWeight::New( );
412 weight->SetBeta( std::get< 0 >( this->m_DblArgs[ "beta" ] ) );
413 weight->SetEpsilon( std::get< 0 >( this->m_DblArgs[ "epsilon" ] ) );
416 typedef CTBronchi::Filter< fpa::Filters::Image::RandomWalker< typename _TInput::TImage, typename _TLabels::TImage, typename _TFastRW::TPixel > > TFilter;
418 filter.Get( )->SetInputImage( input.Get( ) );
419 filter.Get( )->SetInputLabels( labels.Get( ) );
420 filter.Get( )->SetWeightFunction( weight );
421 double t1 = filter.Update( );
423 std::cout << "Fast random walker executed in " << t1 << " s" << std::endl;
426 typedef CTBronchi::Filter< itk::BinaryThresholdImageFilter< typename _TLabels::TImage, typename _TLabels::TImage > > _TExtract;
428 extract.Get( )->SetInput( filter.Get( )->GetOutputLabels( ) );
429 extract.Get( )->SetInsideValue( this->m_InsideLabel );
430 extract.Get( )->SetOutsideValue( this->m_UndefinedLabel );
431 extract.Get( )->SetLowerThreshold( this->m_InsideLabel );
432 extract.Get( )->SetUpperThreshold( this->m_InsideLabel );
433 t1 = extract.Update( );
435 std::cout << "Extract labels executed in " << t1 << " s" << std::endl;
437 fastrw.Set( extract.Get( )->GetOutput( ) );
438 t1 = fastrw.Save( iname );
440 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
443 std::cout << "Fast random walker computed in " << t << " s." << std::endl;
446 // -------------------------------------------------------------------------
447 template< class _TInput, class _TLabels, class _TVesselness, class _TSliceRW >
448 void CTBronchi::Process::
450 _TInput& input, _TLabels& labels, _TVesselness& vesselness, _TSliceRW& slicerw
453 TString iname = std::get< 0 >( this->m_StrArgs[ "slicerw" ] );
454 double t = slicerw.Load( iname );
460 typedef CTBronchi::Filter< fpa::Common::SliceBySliceRandomWalker< typename _TInput::TImage, typename _TLabels::TImage, typename _TVesselness::TImage > > TFilter;
462 filter.Get( )->SetInput( input.Get( ) );
463 filter.Get( )->SetInputLabels( labels.Get( ) );
464 filter.Get( )->SetInputVesselness( vesselness.Get( ) );
465 filter.Get( )->SetBeta( std::get< 0 >( this->m_DblArgs[ "beta" ] ) );
466 filter.Get( )->SetVesselnessThreshold( std::get< 0 >( this->m_DblArgs[ "slicerw_thr" ] ) );
467 filter.Get( )->SetEpsilon( std::get< 0 >( this->m_DblArgs[ "epsilon" ] ) );
468 double t1 = filter.Update( );
470 std::cout << "Extract labels executed in " << t1 << " s" << std::endl;
472 slicerw.Set( filter.Get( )->GetOutput( ) );
473 t1 = slicerw.Save( iname );
475 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
478 std::cout << "Slice by slice random walker computed in " << t << " s." << std::endl;
481 // -------------------------------------------------------------------------
482 template< class _TInput >
483 void CTBronchi::Process::
484 _AndImages( _TInput& a, _TInput& b, _TInput& c )
486 TString iname = std::get< 0 >( this->m_StrArgs[ "andrw" ] );
487 double t = c.Load( iname );
493 typedef CTBronchi::Filter< itk::AndImageFilter< typename _TInput::TImage > > TFilter;
495 filter.Get( )->SetInput( 0, a.Get( ) );
496 filter.Get( )->SetInput( 1, b.Get( ) );
497 double t1 = filter.Update( );
499 std::cout << "And segmentations executed in " << t1 << " s" << std::endl;
501 c.Set( filter.Get( )->GetOutput( ) );
502 t1 = c.Save( iname );
504 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
507 std::cout << "And segmentations computed in " << t << " s." << std::endl;
510 // -------------------------------------------------------------------------
511 template< class _TInput, class _TSkeleton >
512 void CTBronchi::Process::
514 _TInput& a, _TSkeleton& s,
515 const TString& fname,
519 double t = s.Load( fname );
524 typedef CTBronchi::Filter< fpa::Filters::Image::ExtractSkeleton< typename _TInput::TImage > > TFilter;
526 filter.Get( )->SetInput( a.Get( ) );
527 filter.Get( )->SeedFromMaximumDistanceOff( );
528 filter.Get( )->SetSeed( this->m_Seed.first );
529 filter.Get( )->GetDistanceMap( )->InsideIsPositiveOn( );
530 filter.Get( )->GetDistanceMap( )->SquaredDistanceOff( );
531 filter.Get( )->GetDistanceMap( )->UseImageSpacingOn( );
532 double t1 = filter.Update( );
534 std::cout << "Skeleton executed in " << t1 << " s" << std::endl;
536 s.Set( filter.Get( )->GetOutput( ) );
537 t1 = s.Save( fname );
539 std::cout << "\"" << fname << "\" saved in " << t1 << " s." << std::endl;
542 std::stringstream ePointsStr;
543 std::vector< typename _TInput::TImage::IndexType > ePoints =
544 filter.Get( )->GetEndPoints( );
545 for( typename _TInput::TImage::IndexType i: ePoints )
547 for( unsigned int d = 0; d < _TInput::VDim; ++d )
548 ePointsStr << i[ d ] << " ";
549 ePointsStr << std::endl;
553 std::ofstream ePointsF( pname.c_str( ), std::ofstream::binary );
555 throw std::runtime_error(
556 TString( "Unable to write skeleton to \"" ) + pname + "\""
558 ePointsF.write( ePointsStr.str( ).c_str( ), ePointsStr.str( ).size( ) );
561 std::cout << "Skeleton computed in " << t << " s." << std::endl;