cmtkAffineRegistrationCommandLine.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 1997-2009 Torsten Rohlfing
00004 //
00005 //  Copyright 2004-2010 SRI International
00006 //
00007 //  This file is part of the Computational Morphometry Toolkit.
00008 //
00009 //  http://www.nitrc.org/projects/cmtk/
00010 //
00011 //  The Computational Morphometry Toolkit is free software: you can
00012 //  redistribute it and/or modify it under the terms of the GNU General Public
00013 //  License as published by the Free Software Foundation, either version 3 of
00014 //  the License, or (at your option) any later version.
00015 //
00016 //  The Computational Morphometry Toolkit is distributed in the hope that it
00017 //  will be useful, but WITHOUT ANY WARRANTY; without even the implied
00018 //  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 //  GNU General Public License for more details.
00020 //
00021 //  You should have received a copy of the GNU General Public License along
00022 //  with the Computational Morphometry Toolkit.  If not, see
00023 //  <http://www.gnu.org/licenses/>.
00024 //
00025 //  $Revision: 2530 $
00026 //
00027 //  $LastChangedDate: 2010-11-01 16:16:43 -0700 (Mon, 01 Nov 2010) $
00028 //
00029 //  $LastChangedBy: torstenrohlfing $
00030 //
00031 */
00032 
00033 #include "cmtkAffineRegistrationCommandLine.h"
00034 
00035 #include <System/cmtkConsole.h>
00036 #include <System/cmtkThreads.h>
00037 #include <System/cmtkTimers.h>
00038 #include <System/cmtkCommandLine.h>
00039 #include <System/cmtkExitException.h>
00040 #include <System/cmtkCompressedStream.h>
00041 #include <System/cmtkMountPoints.h>
00042 
00043 #include <Base/cmtkTypes.h>
00044 #include <Base/cmtkAnatomicalOrientation.h>
00045 #include <Base/cmtkTransformChangeToSpaceAffine.h>
00046 #include <Base/cmtkTransformChangeFromSpaceAffine.h>
00047 
00048 #include <IO/cmtkVolumeIO.h>
00049 #include <IO/cmtkClassStream.h>
00050 #include <IO/cmtkClassStreamAffineXform.h>
00051 #include <IO/cmtkXformIO.h>
00052 #include <IO/cmtkAffineXformITKIO.h>
00053 
00054 #include <Registration/cmtkRegistrationCallback.h>
00055 #include <Registration/cmtkProtocolCallback.h>
00056 
00057 #ifdef CMTK_USE_SQLITE
00058 #  include <Registration/cmtkImageXformDB.h>
00059 #endif
00060 
00061 #ifdef HAVE_SYS_TYPES_H
00062 #  include <sys/types.h>
00063 #endif
00064 
00065 #ifdef HAVE_SYS_STAT_H
00066 #  include <sys/stat.h>
00067 #endif
00068 
00069 #ifdef HAVE_SYS_UTSNAME_H
00070 #  include <sys/utsname.h>
00071 #endif
00072 
00073 #include <iostream>
00074 #include <stdio.h>
00075 #include <string.h>
00076 
00077 #ifdef _MSC_VER
00078 #  include <direct.h>
00079 #endif
00080 
00081 namespace 
00082 cmtk
00083 {
00084 
00087 
00088 AffineRegistrationCommandLine
00089 ::AffineRegistrationCommandLine 
00090 ( const int argc, const char* argv[] ) 
00091   : m_InitialXformPath( NULL ),
00092     m_ReformattedImagePath( NULL ),
00093     m_OutputPathITK( NULL ),
00094 #ifdef CMTK_USE_SQLITE
00095     m_UpdateDB( NULL ),
00096 #endif
00097     Protocol( NULL )
00098 {
00099   this->m_Metric = 0;
00100 
00101   this->m_AutoMultiLevels = 0;
00102   CoarsestResolution = -1;
00103   this->m_Exploration = 8;
00104   this->m_Accuracy = 0.1;
00105   this->m_Sampling = 1.0;
00106   OutParametersName = OutMatrixName = Studylist = Time = NULL;
00107   InitXlate = 0;
00108   this->m_NoSwitch = 0;
00109 
00110   Verbose = 0;
00111 
00112   const char *InitialStudylist = NULL;
00113   Study1 = Study2 = NULL;
00114 
00115   const char* clArg1 = NULL; // input studylist or reference image
00116   const char* clArg2 = NULL; // empty or floating image
00117 
00118   try 
00119     {
00120     CommandLine cl( CommandLine::PROPS_XML );
00121     cl.SetProgramInfo( CommandLine::PRG_TITLE, "Rigid and affine registration" );
00122     cl.SetProgramInfo( CommandLine::PRG_DESCR, "This program performs rigid and affine image registration using multi-resolution optimization of voxel-based image similarity measures." );
00123     cl.SetProgramInfo( CommandLine::PRG_CATEG, "CMTK.Registration" );
00124 
00125     typedef CommandLine::Key Key;
00126     cl.AddSwitch( Key( 'v', "verbose" ), &Verbose, true, "Verbose mode" )->SetProperties( CommandLine::PROPS_NOXML );
00127     cl.AddSwitch( Key( 'q', "quiet" ), &Verbose, false, "Quiet mode" )->SetProperties( CommandLine::PROPS_NOXML );
00128 
00129     cl.BeginGroup( "Automation", "Automation Options" );
00130     cl.AddOption( Key( "auto-multi-levels" ), &this->m_AutoMultiLevels, "Automatic optimization and resolution parameter generation for <n> levels" );
00131 
00132     cl.BeginGroup( "Optimization", "Optimization settings" );
00133     cl.AddOption( Key( 'e', "exploration" ), &this->m_Exploration, "Exploration [initial optimizer step size]" );
00134     cl.AddOption( Key( 'a', "accuracy" ), &this->m_Accuracy, "Accuracy [final optimizer step size]" );
00135     cl.AddOption( Key( 'f', "stepfactor" ), &this->OptimizerStepFactor, "Factor for search step size reduction. Must be > 0.0 and < 1.0" );
00136     cl.AddOption( Key( "delta-f-threshold" ), &this->m_DeltaFThreshold, "Optional threshold to terminate optimization (level) if relative change of target function drops below this value." );
00137     cl.EndGroup();
00138 
00139     cl.BeginGroup( "Resolution", "Image resolution parameters" );
00140     cl.AddOption( Key( 's', "sampling" ), &this->m_Sampling, "Image sampling (finest resampled image resolution)" );
00141     cl.AddOption( Key( "coarsest" ), &this->CoarsestResolution, "Upper limit for image sampling in multiresolution hierarchy" );
00142 
00143     cl.AddSwitch( Key( "omit-original-data" ), &this->m_UseOriginalData, false, "Do not use original data in full resolution, omit final stage in multiresolution hierarchy, thus reducing computation time." );
00144     cl.EndGroup();
00145 
00146     cl.BeginGroup( "Transformation", "Transformation parameters" );
00147     cl.AddVector( Key( "dofs" ), this->NumberDOFs, "Add number of degrees of freedom [can be repeated]" );
00148     cl.AddVector( Key( "dofs-final" ), this->NumberDOFsFinal, "Add number of degrees of freedom for final level only [can be repeated]" );
00149     
00150     cl.AddSwitch( Key( 'n', "no-switch" ), &this->m_NoSwitch, true, "Do not auto-switch reference and floating image for improved computational performance" );
00151     cl.AddSwitch( Key( 'i', "initxlate" ), &InitXlate, true, "Initialized transformation by translating floating image FOV center onto reference image FOV center" );
00152 
00153     cl.AddOption( Key( "initial" ), &InitialStudylist, "Initialize transformation from given path" )->SetProperties( CommandLine::PROPS_XFORM );
00154     cl.AddSwitch( Key( "initial-is-inverse" ), &this->m_InitialXformIsInverse, true, "Invert initial transformation before initializing registration" );
00155     cl.EndGroup();
00156 
00157     cl.BeginGroup( "Image data", "Image data" );
00158     CommandLine::EnumGroup<int>::SmartPtr
00159       metricGroup = cl.AddEnum( "registration-metric", &this->m_Metric, "Registration metric for motion estimation by image-to-image registration." );
00160     metricGroup->AddSwitch( Key( "nmi" ), 0, "Normalized Mutual Information metric" );
00161     metricGroup->AddSwitch( Key( "mi" ), 1, "Standard Mutual Information metric" );
00162     metricGroup->AddSwitch( Key( "cr" ), 2, "Correlation Ratio metric" );
00163     metricGroup->AddSwitch( Key( "msd" ), 4, "Mean Squared Difference metric" );
00164     metricGroup->AddSwitch( Key( "ncc" ), 5, "Normalized Cross Correlation metric" );
00165 
00166     cl.AddSwitch( Key( "match-histograms" ), &this->m_MatchFltToRefHistogram, true, "Match floating image histogram to reference image histogram." );
00167 
00168     this->m_PreprocessorRef.AttachToCommandLine( cl );
00169     this->m_PreprocessorFlt.AttachToCommandLine( cl );
00170 
00171     cl.BeginGroup( "Output", "Output parameters" )->SetProperties( CommandLine::PROPS_NOXML );
00172     cl.AddOption( Key( 'o', "outlist" ), &this->Studylist, "Output path for final transformation" );
00173     cl.AddOption( Key( "out-matrix" ), &this->OutMatrixName, "Output path for final transformation in matrix format" );
00174     cl.AddOption( Key( "out-parameters" ), &this->OutParametersName, "Output path for final transformation in plain parameter list format" );
00175     cl.AddOption( Key( 'p', "protocol" ), &this->Protocol, "Optimization protocol output file name" );
00176     cl.AddOption( Key( 't', "time" ), &this->Time, "Computation time statistics output file name" );
00177     cl.EndGroup();
00178 
00179     cl.BeginGroup( "SlicerImport", "Import Results into Slicer" );
00180     cl.AddOption( Key( "out-itk" ), &this->m_OutputPathITK, "Output path for final transformation in ITK format" )
00181       ->SetProperties( CommandLine::PROPS_XFORM | CommandLine::PROPS_OUTPUT )
00182       ->SetAttribute( "reference", "FloatingImage" );
00183     cl.AddOption( Key( "write-reformatted" ), &this->m_ReformattedImagePath, "Write reformatted floating image." )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT );
00184     cl.EndGroup();
00185     
00186 #ifdef CMTK_USE_SQLITE
00187     cl.BeginGroup( "Database", "Image/Transformation Database" );
00188     cl.AddOption( Key( "db" ), &this->m_UpdateDB, "Path to image/transformation database that should be updated with the new registration and/or reformatted image." );
00189     cl.EndGroup();
00190 #endif
00191 
00192     cl.AddParameter( &clArg1, "ReferenceImage", "Reference (fixed) image path" )->SetProperties( CommandLine::PROPS_IMAGE );
00193     cl.AddParameter( &clArg2, "FloatingImage", "Floating (moving) image path" )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OPTIONAL );
00194 
00195     cl.Parse( argc, argv );
00196     }
00197   catch ( const CommandLine::Exception& ex )
00198     {
00199     StdErr << ex << "\n";
00200     throw cmtk::ExitException( 1 );
00201     }
00202   
00203   if ( (OptimizerStepFactor <= 0) || (OptimizerStepFactor >= 1) ) 
00204     {
00205     StdErr << "ERROR: step factor value " << OptimizerStepFactor << " is invalid. Must be in range (0..1)\n";
00206     throw cmtk::ExitException( 1 );
00207     }
00208 
00209   this->SetInitialTransformation( AffineXform::SmartPtr( new AffineXform() ) );
00210     
00211   if ( clArg2 ) 
00212     {
00213     Study1 = const_cast<char*>( clArg1 );
00214     Study2 = const_cast<char*>( clArg2 );
00215     } 
00216   else
00217     {
00218     this->m_InitialXformPath = clArg1;
00219 
00220     if ( InitialStudylist ) 
00221       {
00222       StdErr << "Transformation will be overriden by '--initial' list.\n";
00223       }
00224     
00225     if ( Verbose )
00226       StdErr << "Reading input studylist " << this->m_InitialXformPath << ".\n";
00227     
00228     ClassStream typedStream( MountPoints::Translate(this->m_InitialXformPath), "registration", ClassStream::READ );
00229     if ( ! typedStream.IsValid() ) 
00230       {
00231       StdErr << "Could not open studylist archive " << this->m_InitialXformPath << ".\n";
00232       throw cmtk::ExitException( 1 );
00233       }
00234 
00235     typedStream.Seek ( "registration" );
00236     Study1 = typedStream.ReadString( "reference_study" );
00237     Study2 = typedStream.ReadString( "floating_study" );
00238     if ( Study2 )
00239       {
00240       AffineXform::SmartPtr affineXform;
00241       typedStream >> affineXform;
00242       this->SetInitialTransformation( affineXform );
00243       }
00244     else
00245       {
00246       // legacy studylists have inverse transformation in them
00247       Study2 = typedStream.ReadString( "model_study" );
00248       AffineXform::SmartPtr affineXform;
00249       typedStream >> affineXform;
00250       this->SetInitialTransformation( affineXform->GetInverse() );
00251       }
00252 
00253     typedStream.Close();
00254     }
00255 
00256   if ( !Study1 )
00257     {
00258     StdErr << "ERROR: reference image path resolved to NULL.\n";
00259     throw cmtk::ExitException( 1 );
00260     }
00261   
00262   if ( !Study2 )
00263     {
00264     StdErr << "ERROR: floating image path resolved to NULL.\n";
00265     throw cmtk::ExitException( 1 );
00266     }
00267   
00268   UniformVolume::SmartPtr volume( VolumeIO::ReadOriented( Study1, Verbose ) );
00269   if ( !volume )
00270     {
00271     StdErr << "ERROR: volume " << this->Study1 << " could not be read\n";
00272     throw cmtk::ExitException( 1 );
00273     }
00274   this->SetVolume_1( UniformVolume::SmartPtr( this->m_PreprocessorRef.GetProcessedImage( volume ) ) );
00275 
00276   volume = UniformVolume::SmartPtr( VolumeIO::ReadOriented( Study2, Verbose ) );
00277   if ( !volume )
00278     {
00279     StdErr << "ERROR: volume " << this->Study2 << " could not be read\n";
00280     throw cmtk::ExitException( 1 );
00281     }
00282   this->SetVolume_2(  UniformVolume::SmartPtr( this->m_PreprocessorFlt.GetProcessedImage( volume ) ) );
00283 
00284   if ( InitialStudylist ) 
00285     {
00286     this->m_InitialXformPath = InitialStudylist;
00287     Xform::SmartPtr xform( XformIO::Read( InitialStudylist, Verbose ) );
00288     if ( ! xform ) 
00289       {
00290       StdErr << "ERROR: could not read transformation from " << InitialStudylist << "\n";
00291       throw cmtk::ExitException( 1 );
00292       }
00293     
00294     AffineXform::SmartPtr affine( AffineXform::SmartPtr::DynamicCastFrom( xform ) );
00295     if ( ! affine )
00296       {
00297       StdErr << "ERROR: transformation " << InitialStudylist << " is not affine.\n";
00298       throw cmtk::ExitException( 1 );
00299       }
00300 
00301     if ( affine->m_MetaInformation[META_SPACE] != AnatomicalOrientation::ORIENTATION_STANDARD )
00302       {
00303       TransformChangeFromSpaceAffine toStandardSpace( *affine, *(this->m_Volume_1), *(this->m_Volume_2) );
00304       *affine = toStandardSpace.GetTransformation();
00305       affine->m_MetaInformation[META_SPACE] = AnatomicalOrientation::ORIENTATION_STANDARD;
00306       }
00307     
00308     this->SetInitialTransformation( affine );
00309     }
00310   
00311   if ( InitXlate ) 
00312     {
00313     if ( this->m_InitialXformPath ) 
00314       {
00315       StdErr << "WARNING: Initial transformation was taken from studylist. Switch --initxlate / -i will be ignored.\n";
00316       } 
00317     else
00318       {
00319       this->SetInitialAlignCenters();
00320       }
00321     }
00322 
00323   if ( this->m_AutoMultiLevels > 0 )
00324     {
00325     const Types::Coordinate minDelta = std::min( this->m_Volume_1->GetMinDelta(), this->m_Volume_2->GetMinDelta() );
00326     const Types::Coordinate maxDelta = std::max( this->m_Volume_1->GetMaxDelta(), this->m_Volume_2->GetMaxDelta() );
00327 
00328     this->m_Accuracy = 0.1 * minDelta;
00329     this->m_Sampling = maxDelta;
00330     this->m_Exploration = maxDelta * (1<<(this->m_AutoMultiLevels-1));
00331     }
00332   
00333   if ( Protocol ) 
00334     {
00335     RegistrationCallback::SmartPtr callback( new ProtocolCallback( Protocol ) );
00336     this->SetCallback( callback );
00337     }
00338 }
00339 
00340 CallbackResult
00341 AffineRegistrationCommandLine::InitRegistration ()
00342 {
00343   CallbackResult Result = AffineRegistration::InitRegistration();
00344   return Result;
00345 }
00346         
00347 void
00348 AffineRegistrationCommandLine::OutputResultMatrix( const char* matrixName ) const
00349 {
00350   Types::Coordinate matrix[4][4];
00351   this->GetTransformation()->GetMatrix( matrix );
00352 
00353   FILE* mfile = fopen( matrixName, "w" );
00354   if ( mfile )
00355     {
00356     for ( int i = 0; i < 4; ++i )
00357       {
00358       fprintf( mfile, "%e\t%e\t%e\t%e\n", matrix[0][i], matrix[1][i], matrix[2][i], matrix[3][i] );
00359       }
00360     fclose( mfile );
00361     }
00362 }
00363 
00364 void
00365 AffineRegistrationCommandLine::OutputResultParameters
00366 ( const char* paramsName, const CoordinateVector& v ) const
00367 {
00368   FILE* pfile = fopen( paramsName, "w" );
00369   if ( pfile )
00370     {
00371     for ( unsigned int idx=0; idx < v.Dim; ++idx )
00372       fprintf( pfile, "#%d: %f\n", idx, v.Elements[idx] );
00373     fclose( pfile );
00374     }
00375 }
00376 
00377 void
00378 AffineRegistrationCommandLine::OutputResultList( const char* studyList ) const
00379 {
00380   ClassStream classStream( studyList, "studylist", ClassStream::WRITE );
00381   if ( !classStream.IsValid() ) return;
00382   
00383   classStream.Begin( "studylist" );
00384   classStream.WriteInt( "num_sources", 2 );
00385   classStream.End();
00386     
00387   classStream.Begin( "source" );
00388   classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study1 ) );
00389   classStream.End();
00390     
00391   classStream.Begin( "source" );
00392   classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study2 ) );
00393   classStream.End();
00394     
00395   classStream.Close();
00396     
00397   classStream.Open( studyList, "registration", ClassStream::WRITE );
00398     
00399   classStream.Begin( "registration" );
00400   classStream.WriteString( "reference_study", CompressedStream::GetBaseName( Study1 ) );
00401   classStream.WriteString( "floating_study", CompressedStream::GetBaseName( Study2 ) );
00402     
00403   classStream << *(this->GetTransformation());
00404     
00405   classStream.End();
00406   classStream.Close();
00407     
00408   classStream.Open( studyList, "settings", ClassStream::WRITE );
00409   classStream.WriteDouble( "exploration", this->m_Exploration );
00410   classStream.WriteDouble( "accuracy", this->m_Accuracy );
00411   classStream.WriteDouble( "min_sampling", this->m_Sampling );
00412   classStream.WriteDouble( "coarsest_resolution", CoarsestResolution );
00413   classStream.WriteInt( "metric", this->m_Metric );
00414   classStream.WriteDouble( "optimizer_step_factor", OptimizerStepFactor );
00415   classStream.WriteBool( "no_switch", this->m_NoSwitch );
00416 
00417   this->m_PreprocessorRef.WriteSettings( classStream );  
00418   this->m_PreprocessorFlt.WriteSettings( classStream );  
00419 
00420   classStream.Close();
00421     
00422   classStream.Open( studyList, "statistics", ClassStream::WRITE );
00423   classStream.WriteDouble( "time", this->GetTotalElapsedTime() );
00424   classStream.WriteDouble( "walltime", this->GetTotalElapsedWalltime() );
00425 #ifdef CMTK_USE_THREADS
00426   classStream.WriteDouble( "thread_time", this->GetThreadTotalElapsedTime() );
00427 #endif
00428     
00429 #ifndef _MSC_VER
00430   struct utsname name;
00431   if ( uname( &name ) >= 0 ) 
00432     {
00433     classStream.WriteString( "host", name.nodename );
00434     classStream.WriteString( "system", name.sysname );
00435     }
00436 #endif
00437   classStream.Close();
00438 }
00439 
00440 void
00441 AffineRegistrationCommandLine::OutputResult ( const CoordinateVector* v )
00442 {
00443   if ( Verbose ) 
00444     {
00445     fprintf( stderr, "\rResulting transformation parameters: \n" );
00446     for ( unsigned int idx=0; idx<v->Dim; ++idx )
00447       fprintf( stderr, "#%d: %f\n", idx, v->Elements[idx] );
00448     }
00449   
00450   if ( this->OutMatrixName )
00451     {
00452     this->OutputResultMatrix( this->OutMatrixName );
00453     }
00454 
00455   if ( this->OutParametersName )
00456     {
00457     this->OutputResultParameters( this->OutParametersName, *v );
00458     }
00459 
00460   if ( this->Studylist ) 
00461     {
00462     this->OutputResultList( this->Studylist );
00463     }
00464 
00465   if ( this->m_OutputPathITK ) 
00466     {
00467     TransformChangeToSpaceAffine toNative( *(this->GetTransformation()), *(this->m_Volume_1), *(this->m_Volume_2), AnatomicalOrientationBase::SPACE_ITK );
00468     AffineXformITKIO::Write( this->m_OutputPathITK, toNative.GetTransformation() );
00469     }
00470 
00471   if ( this->m_ReformattedImagePath )
00472     {
00473     VolumeIO::Write( *(this->GetReformattedFloatingImage()), this->m_ReformattedImagePath, this->Verbose );
00474     }
00475 
00476 #ifdef CMTK_USE_SQLITE
00477   if ( this->m_UpdateDB )
00478     {
00479     try
00480       {
00481       cmtk::ImageXformDB db( this->m_UpdateDB );
00482       
00483       if ( this->m_ReformattedImagePath )
00484         {
00485         db.AddImage( this->m_ReformattedImagePath, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ) );
00486         }
00487       
00488       if ( this->Studylist )
00489         {
00490         if ( this->m_InitialXformPath ) 
00491           {
00492           db.AddRefinedXform( this->Studylist, true /*invertible*/, this->m_InitialXformPath, this->m_InitialXformIsInverse );
00493           }
00494         else
00495           {
00496           db.AddImagePairXform( this->Studylist, true /*invertible*/, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ), this->m_FloatingVolume->GetMetaInfo( META_FS_PATH ) );
00497           }
00498         }
00499       }
00500     catch ( const cmtk::ImageXformDB::Exception& ex )
00501       {
00502       StdErr << "DB ERROR: " << ex.what() << " on database " << this->m_UpdateDB << "\n";
00503       }
00504     }
00505 #endif
00506 }
00507 
00508 void
00509 AffineRegistrationCommandLine::EnterResolution
00510 ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f,
00511   const int index, const int total )
00512 {
00513   if ( Verbose )
00514     fprintf( stderr, "\rEntering resolution level %d out of %d...\n", index, total );
00515   this->Superclass::EnterResolution( v, f, index, total );
00516 }
00517 
00518 CallbackResult
00519 AffineRegistrationCommandLine::Register ()
00520 {
00521   const double baselineTime = Timers::GetTimeProcess();
00522   CallbackResult Result = Superclass::Register();
00523   const int elapsed = static_cast<int>( Timers::GetTimeProcess() - baselineTime );
00524 
00525   if ( Time ) 
00526     {
00527     FILE *tfp = fopen( Time, "w" );
00528     
00529     if ( tfp ) 
00530       {
00531       fprintf( tfp, "%d\n", elapsed );
00532       fclose( tfp );
00533       } 
00534     else 
00535       {
00536       std::cerr << "Could not open time file " << Time << "\n";
00537       }
00538     }
00539   return Result;
00540 }
00541 
00542 } // namespace cmtk
00543 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines