1 // =========================================================================
2 // @author Leonardo Florez Valencia (florez-l@javeriana.edu.co)
3 // =========================================================================
8 #include <tclap/CmdLine.h>
10 #include <itkAndImageFilter.h>
11 #include <itkBinaryThresholdImageFilter.h>
12 #include <itkMinimumMaximumImageCalculator.h>
13 #include <itkInvertIntensityImageFilter.h>
14 #include <itkHessianRecursiveGaussianImageFilter.h>
15 #include <itkHessian3DToVesselnessMeasureImageFilter.h>
17 #include <fpa/Common/SliceBySliceRandomWalker.h>
18 #include <fpa/Filters/Image/ExtractSkeleton.h>
19 #include <fpa/Filters/Image/Mori.h>
20 #include <fpa/Filters/Image/RandomWalker.h>
21 #include <fpa/Functors/Dijkstra/Image/Gaussian.h>
23 #include "MoriLabelling.h"
26 // -------------------------------------------------------------------------
29 : m_UndefinedLabel( 0 ),
33 this->m_StrArgs[ "input" ] = TStrArg( "", "I", true );
34 this->m_StrArgs[ "vesselness" ] = TStrArg( "", "V", false );
35 this->m_StrArgs[ "mori" ] = TStrArg( "", "M", false );
36 this->m_StrArgs[ "labels" ] = TStrArg( "", "L", false );
37 this->m_StrArgs[ "fastrw" ] = TStrArg( "", "F", false );
38 this->m_StrArgs[ "slicerw" ] = TStrArg( "", "R", false );
39 this->m_StrArgs[ "andrw" ] = TStrArg( "", "A", false );
40 this->m_StrArgs[ "fastrw_skeleton" ] = TStrArg( "", "G", false );
41 this->m_StrArgs[ "slicerw_skeleton" ] = TStrArg( "", "U", false );
42 this->m_StrArgs[ "andrw_skeleton" ] = TStrArg( "", "W", false );
43 this->m_StrArgs[ "fastrw_points" ] = TStrArg( "", "B", false );
44 this->m_StrArgs[ "slicerw_points" ] = TStrArg( "", "C", false );
45 this->m_StrArgs[ "andrw_points" ] = TStrArg( "", "D", false );
46 this->m_StrArgs[ "points" ] = TStrArg( "", "E", false );
47 this->m_StrArgs[ "seed_file" ] = TStrArg( "", "P", false );
48 this->m_StrArgs[ "seed" ] = TStrArg( "", "S", false );
49 this->m_StrArgs[ "seed_type" ] = TStrArg( "point", "T", false );
51 this->m_DblArgs[ "beta" ] = TDblArg( 2.5, "b", false );
52 this->m_DblArgs[ "epsilon" ] = TDblArg( 1e-5, "e", false );
53 this->m_DblArgs[ "vesselness_sigma" ] = TDblArg( 0.5, "a", false );
54 this->m_DblArgs[ "vesselness_alpha1" ] = TDblArg( 0.5, "c", false );
55 this->m_DblArgs[ "vesselness_alpha2" ] = TDblArg( 2, "d", false );
56 this->m_DblArgs[ "mori_min_thr" ] = TDblArg( -850, "f", false );
57 this->m_DblArgs[ "mori_kernel" ] = TDblArg( 20, "g", false );
58 this->m_DblArgs[ "mori_signal_thr" ] = TDblArg( 100, "i", false );
59 this->m_DblArgs[ "mori_signal_influence" ] = TDblArg( 0.5, "j", false );
60 this->m_DblArgs[ "mori_lower" ] = TDblArg( -1024, "k", false );
61 this->m_DblArgs[ "mori_upper" ] = TDblArg( 0, "l", false );
62 this->m_DblArgs[ "mori_delta" ] = TDblArg( 1, "m", false );
63 this->m_DblArgs[ "slicerw_thr" ] = TDblArg( 5, "n", false );
64 this->m_DblArgs[ "fastrw_thr" ] = TDblArg( 5, "o", false );
65 this->m_DblArgs[ "labels_upper_thr" ] = TDblArg( -400, "p", false );
68 // -------------------------------------------------------------------------
74 // -------------------------------------------------------------------------
75 void CTBronchi::Process::
76 ParseArguments( int argc, char* argv[] )
78 typedef TCLAP::ValueArg< TString > _TStrArg;
79 typedef TCLAP::ValueArg< TScalar > _TDblArg;
80 std::map< TString, _TStrArg* > strArgs;
81 std::map< TString, _TDblArg* > dblArgs;
84 TStrArgs::iterator sIt = this->m_StrArgs.begin( );
85 for( ; sIt != this->m_StrArgs.end( ); ++sIt )
86 strArgs[ sIt->first ] = new _TStrArg(
87 std::get< 1 >( sIt->second ),
88 sIt->first, sIt->first,
89 std::get< 2 >( sIt->second ),
90 std::get< 0 >( sIt->second ),
91 TString( "string (" ) + std::get< 0 >( sIt->second ) + ")"
94 TDblArgs::iterator dIt = this->m_DblArgs.begin( );
95 for( ; dIt != this->m_DblArgs.end( ); ++dIt )
98 v << "value (" << std::get< 0 >( dIt->second ) << ")";
99 dblArgs[ dIt->first ] = new _TDblArg(
100 std::get< 1 >( dIt->second ),
101 dIt->first, dIt->first,
102 std::get< 2 >( dIt->second ),
103 std::get< 0 >( dIt->second ),
110 std::map< TString, _TStrArg* >::iterator saIt;
111 std::map< TString, _TDblArg* >::iterator daIt;
114 TCLAP::CmdLine cmd( "CTBronchi pipeline", ' ', "1.0.0" );
115 for( saIt = strArgs.begin( ); saIt != strArgs.end( ); ++saIt )
116 cmd.add( *( saIt->second ) );
117 for( daIt = dblArgs.begin( ); daIt != dblArgs.end( ); ++daIt )
118 cmd.add( *( daIt->second ) );
119 cmd.parse( argc, argv );
121 catch( TCLAP::ArgException& err )
123 std::stringstream msg;
124 msg << err.error( ) << " " << err.argId( );
125 throw std::runtime_error( msg.str( ) );
130 for( saIt = strArgs.begin( ); saIt != strArgs.end( ); ++saIt )
132 std::get< 0 >( this->m_StrArgs[ saIt->first ] ) =
133 saIt->second->getValue( );
137 for( daIt = dblArgs.begin( ); daIt != dblArgs.end( ); ++daIt )
139 std::get< 0 >( this->m_DblArgs[ daIt->first ] ) =
140 daIt->second->getValue( );
145 // Compute base filenames
146 TString bname = std::get< 0 >( this->m_StrArgs[ "input" ] );
147 bname = bname.substr( 0, bname.find_last_of( "." ) );
148 const unsigned int N = 6;
149 const unsigned int M = 7;
150 TString names[ N + M ] =
166 for( unsigned int i = 0; i < N; ++i )
167 if( std::get< 0 >( this->m_StrArgs[ names[ i ] ] ) == "" )
168 std::get< 0 >( this->m_StrArgs[ names[ i ] ] ) =
169 bname + "_" + names[ i ] + ".mha";
170 for( unsigned int i = 0; i < M; ++i )
171 if( std::get< 0 >( this->m_StrArgs[ names[ i + N ] ] ) == "" )
172 std::get< 0 >( this->m_StrArgs[ names[ i + N ] ] ) =
173 bname + "_" + names[ i + N ] + ".txt";
176 // -------------------------------------------------------------------------
177 void CTBronchi::Process::
180 this->_Input( this->m_Input );
181 this->_Vesselness( this->m_Input, this->m_Vesselness );
182 this->_Mori( this->m_Input, this->m_Mori, this->m_Seed );
183 this->_MoriLabelling(
184 this->m_Input, this->m_Mori, this->m_Vesselness, this->m_Labels
186 this->_FastRW( this->m_Input, this->m_Labels, this->m_FastRW );
188 this->m_Input, this->m_Mori, this->m_Vesselness, this->m_SliceRW
190 this->_AndImages( this->m_FastRW, this->m_SliceRW, this->m_AndRW );
192 this->m_FastRW, this->m_FastRWSkeleton, this->m_FastRWPoints, 2,
193 std::get< 0 >( this->m_StrArgs[ "fastrw_skeleton" ] )
196 this->m_SliceRW, this->m_SliceRWSkeleton, this->m_SliceRWPoints, 1,
197 std::get< 0 >( this->m_StrArgs[ "slicerw_skeleton" ] )
200 this->m_AndRW, this->m_AndRWSkeleton, this->m_AndRWPoints, 0,
201 std::get< 0 >( this->m_StrArgs[ "andrw_skeleton" ] )
204 TEndPoints eval_points;
206 this->m_FastRWPoints, 3,
207 this->m_SliceRWPoints, 3,
208 this->m_AndRWPoints, 4,
212 if( eval_points.size( ) > 0 )
214 std::stringstream ePointsStr;
215 for( TEndPoint ep: eval_points )
217 for( unsigned int d = 0; d < Self::Dim; ++d )
218 ePointsStr << ep.first[ d ] << " ";
219 ePointsStr << ep.second << std::endl;
223 std::string pname = std::get< 0 >( this->m_StrArgs[ "points" ] );
224 std::ofstream ePointsF( pname.c_str( ), std::ofstream::binary );
226 throw std::runtime_error(
227 TString( "Unable to write skeleton to \"" ) + pname + "\""
229 ePointsF.write( ePointsStr.str( ).c_str( ), ePointsStr.str( ).size( ) );
234 // -------------------------------------------------------------------------
235 template< class _TImage >
236 void CTBronchi::Process::
237 _Input( _TImage& input )
239 TString sname = std::get< 0 >( this->m_StrArgs[ "seed_file" ] );
240 TString seed = std::get< 0 >( this->m_StrArgs[ "seed" ] );
241 TString stype = std::get< 0 >( this->m_StrArgs[ "seed_type" ] );
242 if( sname == "" && seed == "" )
243 throw std::runtime_error( "No seed given." );
246 std::ifstream str( sname.c_str( ) );
247 str.seekg( 0, std::ios::end );
248 seed.reserve( str.tellg( ) );
249 str.seekg( 0, std::ios::beg );
251 std::istreambuf_iterator< char >( str ),
252 std::istreambuf_iterator< char >( )
259 std::istringstream sSeed( seed );
262 if( stype == "point" )
263 sSeed >> pnt[ 0 ] >> pnt[ 1 ] >> pnt[ 2 ];
265 sSeed >> idx[ 0 ] >> idx[ 1 ] >> idx[ 2 ];
268 TString iname = std::get< 0 >( this->m_StrArgs[ "input" ] );
269 double t = input.Load( iname );
271 std::runtime_error( TString( "Could not read \"" ) + iname + "\"" );
272 std::cout << "\"" << iname << "\" read in " << t << " s." << std::endl;
275 if( stype == "point" )
276 input.Get( )->TransformPhysicalPointToIndex( pnt, idx );
278 input.Get( )->TransformIndexToPhysicalPoint( idx, pnt );
279 this->m_Seed = TSeed( idx, pnt );
282 // -------------------------------------------------------------------------
283 template< class _TInput, class _TVesselness >
284 void CTBronchi::Process::
285 _Vesselness( _TInput& input, _TVesselness& vesselness )
287 TString iname = std::get< 0 >( this->m_StrArgs[ "vesselness" ] );
288 double t = vesselness.Load( iname );
294 typedef itk::MinimumMaximumImageCalculator< typename _TInput::TImage > _TMinMax;
295 typename _TMinMax::Pointer minMax = _TMinMax::New( );
296 minMax->SetImage( input.Get( ) );
299 // Invert intensities
300 typedef CTBronchi::Filter< itk::InvertIntensityImageFilter< typename _TInput::TImage > > _TInverter;
302 inverter.Get( )->SetInput( input.Get( ) );
303 inverter.Get( )->SetMaximum( minMax->GetMaximum( ) );
304 double t1 = inverter.Update( );
306 std::cout << "Inversion executed in " << t1 << " s" << std::endl;
308 // Compute hessian image
309 typedef CTBronchi::Filter< itk::HessianRecursiveGaussianImageFilter< typename _TInput::TImage > > _THessian;
311 hessian.Get( )->SetInput( inverter.Get( )->GetOutput( ) );
312 hessian.Get( )->SetSigma(
313 std::get< 0 >( this->m_DblArgs[ "vesselness_sigma" ] )
315 t1 = hessian.Update( );
317 std::cout << "Hessian executed in " << t1 << " s" << std::endl;
320 typedef CTBronchi::Filter< itk::Hessian3DToVesselnessMeasureImageFilter< typename _TVesselness::TPixel > > _TVesselnessFilter;
321 _TVesselnessFilter vFilter;
322 vFilter.Get( )->SetInput( hessian.Get( )->GetOutput( ) );
323 vFilter.Get( )->SetAlpha1(
324 std::get< 0 >( this->m_DblArgs[ "vesselness_alpha1" ] )
326 vFilter.Get( )->SetAlpha2(
327 std::get< 0 >( this->m_DblArgs[ "vesselness_alpha2" ] )
329 t1 = vFilter.Update( );
331 std::cout << "Hessian measure computed in " << t1 << " s." << std::endl;
333 vesselness.Set( vFilter.Get( )->GetOutput( ) );
334 t1 = vesselness.Save( iname );
336 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
339 std::cout << "Vesselness computed in " << t << " s." << std::endl;
342 // -------------------------------------------------------------------------
343 template< class _TInput, class _TMori, class _TSeed >
344 void CTBronchi::Process::
345 _Mori( _TInput& input, _TMori& mori, _TSeed& seed )
347 TString iname = std::get< 0 >( this->m_StrArgs[ "mori" ] );
348 double t = mori.Load( iname );
354 typedef CTBronchi::Filter< fpa::Filters::Image::Mori< TPixelImage::TImage, TLabelImage::TImage > > _TMoriFilter;
355 _TMoriFilter mFilter;
356 mFilter.Get( )->SetInput( input.Get( ) );
357 mFilter.Get( )->SetSeed( seed.second );
358 mFilter.Get( )->SetInsideValue( this->m_InsideLabel );
359 mFilter.Get( )->SetOutsideValue( this->m_UndefinedLabel );
360 mFilter.Get( )->SetMinimumThreshold(
361 std::get< 0 >( this->m_DblArgs[ "mori_min_thr" ] )
363 mFilter.Get( )->SetSignalKernelSize(
364 std::get< 0 >( this->m_DblArgs[ "mori_kernel" ] )
366 mFilter.Get( )->SetSignalThreshold(
367 std::get< 0 >( this->m_DblArgs[ "mori_signal_thr" ] )
369 mFilter.Get( )->SetSignalInfluence(
370 std::get< 0 >( this->m_DblArgs[ "mori_signal_influence" ] )
372 mFilter.Get( )->SetThresholds(
373 std::get< 0 >( this->m_DblArgs[ "mori_lower" ] ),
374 std::get< 0 >( this->m_DblArgs[ "mori_upper" ] ),
375 std::get< 0 >( this->m_DblArgs[ "mori_delta" ] )
377 double t1 = mFilter.Update( );
379 std::cout << "Segmentation computed in " << t1 << " s." << std::endl;
381 mori.Set( mFilter.Get( )->GetOutput( ) );
382 t1 = mori.Save( iname );
384 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
387 std::cout << "Mori segmentation computed in " << t << " s." << std::endl;
390 // -------------------------------------------------------------------------
391 template< class _TInput, class _TMori, class _TVesselness, class _TLabels >
392 void CTBronchi::Process::
394 _TInput& input, _TMori& mori, _TVesselness& vesselness, _TLabels& labels
397 TString iname = std::get< 0 >( this->m_StrArgs[ "labels" ] );
398 double t = labels.Load( iname );
404 typedef CTBronchi::Filter< CTBronchi::MoriLabelling< typename _TInput::TImage, typename _TLabels::TImage, typename _TVesselness::TImage > > _TLabelling;
405 _TLabelling labelling;
406 labelling.Get( )->SetInput( input.Get( ) );
407 labelling.Get( )->SetInputLabels( mori.Get( ) );
408 labelling.Get( )->SetInputVesselness( vesselness.Get( ) );
409 labelling.Get( )->SetVesselnessThreshold( std::get< 0 >( this->m_DblArgs[ "fastrw_thr" ] ) );
410 labelling.Get( )->SetUpperThreshold( std::get< 0 >( this->m_DblArgs[ "labels_upper_thr" ] ) );
411 labelling.Get( )->SetInsideValue( this->m_InsideLabel );
412 labelling.Get( )->SetOutsideValue( this->m_OutsideLabel );
413 labelling.Get( )->SetFillValue( this->m_OutsideLabel );
414 double t1 = labelling.Update( );
416 std::cout << "Labelling computed in " << t1 << " s." << std::endl;
418 labels.Set( labelling.Get( )->GetOutput( ) );
419 t1 = labels.Save( iname );
421 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
424 std::cout << "Mori labelling computed in " << t << " s." << std::endl;
427 // -------------------------------------------------------------------------
428 template< class _TInput, class _TLabels, class _TFastRW >
429 void CTBronchi::Process::
430 _FastRW( _TInput& input, _TLabels& labels, _TFastRW& fastrw )
432 TString iname = std::get< 0 >( this->m_StrArgs[ "fastrw" ] );
433 double t = fastrw.Load( iname );
438 // Prepare weight functor
439 typedef fpa::Functors::Dijkstra::Image::Gaussian< typename _TInput::TImage, typename _TFastRW::TPixel > TWeight;
440 typename TWeight::Pointer weight = TWeight::New( );
441 weight->SetBeta( std::get< 0 >( this->m_DblArgs[ "beta" ] ) );
442 weight->SetEpsilon( std::get< 0 >( this->m_DblArgs[ "epsilon" ] ) );
445 typedef CTBronchi::Filter< fpa::Filters::Image::RandomWalker< typename _TInput::TImage, typename _TLabels::TImage, typename _TFastRW::TPixel > > TFilter;
447 filter.Get( )->SetInputImage( input.Get( ) );
448 filter.Get( )->SetInputLabels( labels.Get( ) );
449 filter.Get( )->SetWeightFunction( weight );
450 double t1 = filter.Update( );
452 std::cout << "Fast random walker executed in " << t1 << " s" << std::endl;
455 typedef CTBronchi::Filter< itk::BinaryThresholdImageFilter< typename _TLabels::TImage, typename _TLabels::TImage > > _TExtract;
457 extract.Get( )->SetInput( filter.Get( )->GetOutputLabels( ) );
458 extract.Get( )->SetInsideValue( this->m_InsideLabel );
459 extract.Get( )->SetOutsideValue( this->m_UndefinedLabel );
460 extract.Get( )->SetLowerThreshold( this->m_InsideLabel );
461 extract.Get( )->SetUpperThreshold( this->m_InsideLabel );
462 t1 = extract.Update( );
464 std::cout << "Extract labels executed in " << t1 << " s" << std::endl;
466 fastrw.Set( extract.Get( )->GetOutput( ) );
467 t1 = fastrw.Save( iname );
469 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
472 std::cout << "Fast random walker computed in " << t << " s." << std::endl;
475 // -------------------------------------------------------------------------
476 template< class _TInput, class _TLabels, class _TVesselness, class _TSliceRW >
477 void CTBronchi::Process::
479 _TInput& input, _TLabels& labels, _TVesselness& vesselness, _TSliceRW& slicerw
482 TString iname = std::get< 0 >( this->m_StrArgs[ "slicerw" ] );
483 double t = slicerw.Load( iname );
489 typedef CTBronchi::Filter< fpa::Common::SliceBySliceRandomWalker< typename _TInput::TImage, typename _TLabels::TImage, typename _TVesselness::TImage > > TFilter;
491 filter.Get( )->SetInput( input.Get( ) );
492 filter.Get( )->SetInputLabels( labels.Get( ) );
493 filter.Get( )->SetInputVesselness( vesselness.Get( ) );
494 filter.Get( )->SetBeta( std::get< 0 >( this->m_DblArgs[ "beta" ] ) );
495 filter.Get( )->SetVesselnessThreshold( std::get< 0 >( this->m_DblArgs[ "slicerw_thr" ] ) );
496 filter.Get( )->SetEpsilon( std::get< 0 >( this->m_DblArgs[ "epsilon" ] ) );
497 double t1 = filter.Update( );
499 std::cout << "Extract labels executed in " << t1 << " s" << std::endl;
501 slicerw.Set( filter.Get( )->GetOutput( ) );
502 t1 = slicerw.Save( iname );
504 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
507 std::cout << "Slice by slice random walker computed in " << t << " s." << std::endl;
510 // -------------------------------------------------------------------------
511 template< class _TInput >
512 void CTBronchi::Process::
513 _AndImages( _TInput& a, _TInput& b, _TInput& c )
515 TString iname = std::get< 0 >( this->m_StrArgs[ "andrw" ] );
516 double t = c.Load( iname );
522 typedef CTBronchi::Filter< itk::AndImageFilter< typename _TInput::TImage > > TFilter;
524 filter.Get( )->SetInput( 0, a.Get( ) );
525 filter.Get( )->SetInput( 1, b.Get( ) );
526 double t1 = filter.Update( );
528 std::cout << "And segmentations executed in " << t1 << " s" << std::endl;
530 c.Set( filter.Get( )->GetOutput( ) );
531 t1 = c.Save( iname );
533 std::cout << "\"" << iname << "\" saved in " << t1 << " s." << std::endl;
536 std::cout << "And segmentations computed in " << t << " s." << std::endl;
539 // -------------------------------------------------------------------------
540 template< class _TInput, class _TSkeleton, class _TIndices >
541 void CTBronchi::Process::
543 _TInput& a, _TSkeleton& s,
544 _TIndices& e, unsigned int id,
548 double t = s.Load( fname );
553 typedef CTBronchi::Filter< fpa::Filters::Image::ExtractSkeleton< typename _TInput::TImage > > TFilter;
555 filter.Get( )->SetInput( a.Get( ) );
556 filter.Get( )->SeedFromMaximumDistanceOff( );
557 filter.Get( )->SetSeed( this->m_Seed.first );
558 filter.Get( )->GetDistanceMap( )->InsideIsPositiveOn( );
559 filter.Get( )->GetDistanceMap( )->SquaredDistanceOff( );
560 filter.Get( )->GetDistanceMap( )->UseImageSpacingOn( );
561 double t1 = filter.Update( );
563 std::cout << "Skeleton executed in " << t1 << " s" << std::endl;
565 s.Set( filter.Get( )->GetOutput( ) );
566 t1 = s.Save( fname );
568 std::cout << "\"" << fname << "\" saved in " << t1 << " s." << std::endl;
571 std::vector< TIndex > ePoints = filter.Get( )->GetEndPoints( );
573 for( TIndex p: ePoints )
574 e.insert( TEndPoint( p, id ) );
577 std::cout << "Skeleton computed in " << t << " s." << std::endl;
580 // -------------------------------------------------------------------------
581 template< class _TIndices >
582 void CTBronchi::Process::
584 _TIndices& a, unsigned int ca,
585 _TIndices& b, unsigned int cb,
586 _TIndices& c, unsigned int cc,
590 std::random_device rd;
591 std::mt19937 gen( rd( ) );
593 while( d.size( ) < ca )
595 std::uniform_int_distribution< > dis( 0, a.size( ) - 1 );
596 unsigned int N = dis( gen );
597 typename _TIndices::const_iterator it = a.begin( );
598 for( unsigned int i = 0; i < N; ++i, ++it );
602 while( d.size( ) < ca + cb )
604 std::uniform_int_distribution< > dis( 0, b.size( ) - 1 );
605 unsigned int N = dis( gen );
606 typename _TIndices::const_iterator it = b.begin( );
607 for( unsigned int i = 0; i < N; ++i, ++it );
611 while( d.size( ) < ca + cb + cc )
613 std::uniform_int_distribution< > dis( 0, c.size( ) - 1 );
614 unsigned int N = dis( gen );
615 typename _TIndices::const_iterator it = c.begin( );
616 for( unsigned int i = 0; i < N; ++i, ++it );