00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "cmtkImagePairAffineRegistrationCommandLine.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 <Registration/cmtkRegistrationCallback.h>
00049 #include <Registration/cmtkProtocolCallback.h>
00050 #include <Registration/cmtkMakeInitialAffineTransformation.h>
00051
00052 #include <IO/cmtkVolumeIO.h>
00053 #include <IO/cmtkClassStream.h>
00054 #include <IO/cmtkClassStreamAffineXform.h>
00055 #include <IO/cmtkXformIO.h>
00056 #include <IO/cmtkAffineXformITKIO.h>
00057
00058 #ifdef CMTK_USE_SQLITE
00059 # include <Registration/cmtkImageXformDB.h>
00060 #endif
00061
00062 #ifdef HAVE_SYS_TYPES_H
00063 # include <sys/types.h>
00064 #endif
00065
00066 #ifdef HAVE_SYS_STAT_H
00067 # include <sys/stat.h>
00068 #endif
00069
00070 #ifdef HAVE_SYS_UTSNAME_H
00071 # include <sys/utsname.h>
00072 #endif
00073
00074 #ifdef _MSC_VER
00075 # include <direct.h>
00076 #endif
00077
00078 #include <stdio.h>
00079 #include <string.h>
00080 #include <iostream>
00081
00082 namespace
00083 cmtk
00084 {
00085
00088
00089 ImagePairAffineRegistrationCommandLine
00090 ::ImagePairAffineRegistrationCommandLine
00091 ( const int argc, const char* argv[] )
00092 : m_InitialXformPath( NULL ),
00093 m_ReformattedImagePath( NULL ),
00094 m_OutputPathITK( NULL ),
00095 #ifdef CMTK_USE_SQLITE
00096 m_UpdateDB( NULL ),
00097 #endif
00098 m_ProtocolFileName( NULL )
00099 {
00100 this->m_Metric = 0;
00101
00102 this->m_AutoMultiLevels = 0;
00103 this->m_CoarsestResolution = -1;
00104 this->m_MaxStepSize = 8;
00105 this->m_MinStepSize = 0.1;
00106 this->m_Sampling = 1.0;
00107 OutParametersName = OutMatrixName = Studylist = Time = NULL;
00108
00109 Verbose = 0;
00110
00111 bool forceOutsideFlag = false;
00112 Types::DataItem forceOutsideValue = 0;
00113
00114 const char* inStudylist = NULL;
00115 const char *InitialStudylist = NULL;
00116 Study1 = Study2 = NULL;
00117
00118 const char* clArg1 = NULL;
00119 const char* clArg2 = NULL;
00120
00121 try
00122 {
00123 CommandLine cl( CommandLine::PROPS_XML );
00124 cl.SetProgramInfo( CommandLine::PRG_TITLE, "Rigid and affine registration" );
00125 cl.SetProgramInfo( CommandLine::PRG_DESCR, "This program performs rigid and affine image registration using multi-resolution optimization of voxel-based image similarity measures." );
00126 cl.SetProgramInfo( CommandLine::PRG_CATEG, "CMTK.Registration.Experimental" );
00127
00128 typedef CommandLine::Key Key;
00129 cl.AddSwitch( Key( 'v', "verbose" ), &Verbose, true, "Verbose mode" )->SetProperties( CommandLine::PROPS_NOXML );
00130 cl.AddSwitch( Key( 'q', "quiet" ), &Verbose, false, "Quiet mode" )->SetProperties( CommandLine::PROPS_NOXML );
00131
00132 cl.BeginGroup( "Automation", "Automation Options" );
00133 cl.AddOption( Key( "auto-multi-levels" ), &this->m_AutoMultiLevels, "Automatic optimization and resolution parameter generation for <n> levels" );
00134
00135 cl.BeginGroup( "Optimization", "Optimization settings" );
00136 cl.AddOption( Key( "max-stepsize" ), &this->m_MaxStepSize, "Maximum optimizer step size, which determines search space exploration." );
00137 cl.AddOption( Key( "min-stepsize" ), &this->m_MinStepSize, "Minimum optimizer step size, which determines precision." );
00138 cl.AddOption( Key( "stepfactor" ), &this->m_OptimizerStepFactor, "Factor for search step size reduction. Must be > 0.0 and < 1.0" );
00139 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." );
00140 cl.EndGroup();
00141
00142 cl.BeginGroup( "Resolution", "Image resolution parameters" );
00143 cl.AddOption( Key( 's', "sampling" ), &this->m_Sampling, "Image sampling (finest resampled image resolution)" );
00144 cl.AddOption( Key( "coarsest" ), &this->m_CoarsestResolution, "Upper limit for image sampling in multiresolution hierarchy" );
00145
00146 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." );
00147 cl.EndGroup();
00148
00149 cl.BeginGroup( "Transformation", "Transformation parameters" );
00150 cl.AddVector( Key( "dofs" ), this->NumberDOFs, "Add number of degrees of freedom. This can be 3 (translation), 6 (rigid: translation and rotation), "
00151 "7 (rigid plus global scale), 9 (rigid plus anisotropic scales), 12 (rigid plus scales plus shears), or 603 (rigid plus shears, but no scale). "
00152 "This option can be repeated, in which case DOFs are used for successive optimization runs in the order that they appear." );
00153 cl.AddVector( Key( "dofs-final" ), this->NumberDOFsFinal, "Add number of degrees of freedom for final level only [can be repeated]" );
00154
00155 CommandLine::EnumGroup<MakeInitialAffineTransformation::Mode>::SmartPtr
00156 initGroup = cl.AddEnum( "init", &this->m_Initializer, "Select initializer for the affine trasnformation." );
00157 initGroup->AddSwitch( Key( "none" ), MakeInitialAffineTransformation::NONE, "Use input transformation, or identity transformation if none was provided ." );
00158 initGroup->AddSwitch( Key( "fov" ), MakeInitialAffineTransformation::FOV, "Align centers of field of view (or crop regions) using a translation." );
00159 initGroup->AddSwitch( Key( "com" ), MakeInitialAffineTransformation::COM, "Align centers of mass using a translation." );
00160 initGroup->AddSwitch( Key( "pax" ), MakeInitialAffineTransformation::PAX, "Align images by rotation using principal axes and translation using centers of mass." );
00161 initGroup->AddSwitch( Key( "physical" ), MakeInitialAffineTransformation::PHYS, "Align images by rotation using direction vectors stored in input images and translation using image origins." );
00162
00163 cl.AddOption( Key( "initial" ), &InitialStudylist, "Initialize transformation from given path" )->SetProperties( CommandLine::PROPS_XFORM );
00164 cl.AddSwitch( Key( "initial-is-inverse" ), &this->m_InitialXformIsInverse, true, "Invert initial transformation before initializing registration" );
00165 cl.EndGroup();
00166
00167 cl.BeginGroup( "Image data", "Image data" );
00168 CommandLine::EnumGroup<int>::SmartPtr
00169 metricGroup = cl.AddEnum( "registration-metric", &this->m_Metric, "Registration metric for motion estimation by image-to-image registration." );
00170 metricGroup->AddSwitch( Key( "nmi" ), 0, "Normalized Mutual Information metric" );
00171 metricGroup->AddSwitch( Key( "mi" ), 1, "Standard Mutual Information metric" );
00172 metricGroup->AddSwitch( Key( "cr" ), 2, "Correlation Ratio metric" );
00173 metricGroup->AddSwitch( Key( "rms" ), 3, "Root of Mean Squaresa metric (this is the square root of MSD)" );
00174 metricGroup->AddSwitch( Key( "msd" ), 4, "Mean Squared Difference metric" );
00175 metricGroup->AddSwitch( Key( "ncc" ), 5, "Normalized Cross Correlation metric" );
00176
00177 cl.BeginGroup( "Interpolation", "Floating Image Interpolation Options" );
00178 cmtk::CommandLine::EnumGroup<Interpolators::InterpolationEnum>::SmartPtr kernelGroup =
00179 cl.AddEnum( "interpolation", &this->m_FloatingImageInterpolation, "Interpolation method for floating image sampling:" );
00180 kernelGroup->AddSwitch( Key( "nearest-neighbor" ), Interpolators::NEAREST_NEIGHBOR, "Nearest neighbor interpolation (for intensity and label data)" );
00181 kernelGroup->AddSwitch( Key( "linear" ), Interpolators::LINEAR, "Trilinear interpolation" );
00182 kernelGroup->AddSwitch( Key( "cubic" ), Interpolators::CUBIC, "Tricubic interpolation" );
00183 kernelGroup->AddSwitch( Key( "cosine-sinc" ), Interpolators::COSINE_SINC, "Cosine-windowed sinc interpolation (most accurate but slowest)" );
00184 kernelGroup->AddSwitch( Key( "partial-volume" ), Interpolators::PARTIALVOLUME, "Partial volume interpolation (for label data)" );
00185 kernelGroup->AddSwitch( Key( "automatic" ), Interpolators::DEFAULT, "Select interpolation automatically based on data type: linear for grey-level data, nearest neighbor for label data." );
00186
00187 cl.AddSwitch( Key( "match-histograms" ), &this->m_MatchFltToRefHistogram, true, "Match floating image histogram to reference image histogram." );
00188 cl.AddOption( Key( "force-outside-value" ), &forceOutsideValue, "Force values outside field of view to this value rather than drop incomplete pixel pairs", &forceOutsideFlag );
00189
00190 this->m_PreprocessorRef.AttachToCommandLine( cl );
00191 this->m_PreprocessorFlt.AttachToCommandLine( cl );
00192
00193 cl.BeginGroup( "Output", "Output parameters" )->SetProperties( CommandLine::PROPS_NOXML );
00194 cl.AddOption( Key( 'o', "output" ), &this->Studylist, "Output path for final transformation" );
00195 cl.AddOption( Key( "write-matrix" ), &this->OutMatrixName, "Output path for final transformation in matrix format" );
00196 cl.AddOption( Key( "write-parameters" ), &this->OutParametersName, "Output path for final transformation in plain parameter list format" );
00197 cl.AddOption( Key( "write-protocol" ), &this->m_ProtocolFileName, "Optimization protocol output file name" );
00198 cl.AddOption( Key( "write-time" ), &this->Time, "Computation time statistics output file name" );
00199 cl.EndGroup();
00200
00201 cl.BeginGroup( "SlicerImport", "Import Results into Slicer" );
00202 cl.AddOption( Key( "write-itk" ), &this->m_OutputPathITK, "Output path for final transformation in ITK format" )
00203 ->SetProperties( CommandLine::PROPS_XFORM | CommandLine::PROPS_OUTPUT )
00204 ->SetAttribute( "reference", "FloatingImage" );
00205 cl.AddOption( Key( "write-reformatted" ), &this->m_ReformattedImagePath, "Write reformatted floating image." )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OUTPUT );
00206 cl.EndGroup();
00207
00208 #ifdef CMTK_USE_SQLITE
00209 cl.BeginGroup( "Database", "Image/Transformation Database" );
00210 cl.AddOption( Key( "db" ), &this->m_UpdateDB, "Path to image/transformation database that should be updated with the new registration and/or reformatted image." );
00211 cl.EndGroup();
00212 #endif
00213
00214 cl.AddParameter( &clArg1, "ReferenceImage", "Reference (fixed) image path" )->SetProperties( CommandLine::PROPS_IMAGE );
00215 cl.AddParameter( &clArg2, "FloatingImage", "Floating (moving) image path" )->SetProperties( CommandLine::PROPS_IMAGE | CommandLine::PROPS_OPTIONAL );
00216
00217 cl.Parse( argc, argv );
00218 }
00219 catch ( const CommandLine::Exception& ex )
00220 {
00221 StdErr << ex << "\n";
00222 throw cmtk::ExitException( 1 );
00223 }
00224
00225 if ( (this->m_OptimizerStepFactor <= 0) || (this->m_OptimizerStepFactor >= 1) )
00226 {
00227 StdErr << "ERROR: step factor value " << this->m_OptimizerStepFactor << " is invalid. Must be in range (0..1)\n";
00228 throw cmtk::ExitException( 1 );
00229 }
00230
00231 if ( clArg2 )
00232 {
00233 Study1 = const_cast<char*>( clArg1 );
00234 Study2 = const_cast<char*>( clArg2 );
00235 }
00236 else
00237 {
00238 inStudylist = clArg1;
00239 if ( InitialStudylist )
00240 {
00241 StdErr << "Transformation will be overriden by '--initial' list.\n";
00242 }
00243
00244 if ( Verbose )
00245 StdErr << "Reading input studylist " << inStudylist << ".\n";
00246
00247 ClassStream typedStream( MountPoints::Translate(inStudylist), "registration", ClassStream::READ );
00248 if ( ! typedStream.IsValid() )
00249 {
00250 StdErr << "Could not open studylist archive " << inStudylist << ".\n";
00251 throw cmtk::ExitException( 1 );
00252 }
00253
00254 typedStream.Seek ( "registration" );
00255 Study1 = typedStream.ReadString( "reference_study" );
00256 Study2 = typedStream.ReadString( "floating_study" );
00257 if ( Study2 )
00258 {
00259 AffineXform::SmartPtr affineXform;
00260 typedStream >> affineXform;
00261 this->SetInitialTransformation( affineXform );
00262 }
00263 else
00264 {
00265
00266 Study2 = typedStream.ReadString( "model_study" );
00267 AffineXform::SmartPtr affineXform;
00268 typedStream >> affineXform;
00269 this->SetInitialTransformation( affineXform->GetInverse() );
00270 }
00271
00272 typedStream.Close();
00273 }
00274
00275 if ( !Study1 )
00276 {
00277 StdErr << "ERROR: reference image path resolved to NULL.\n";
00278 throw cmtk::ExitException( 1 );
00279 }
00280
00281 if ( !Study2 )
00282 {
00283 StdErr << "ERROR: floating image path resolved to NULL.\n";
00284 throw cmtk::ExitException( 1 );
00285 }
00286
00287 UniformVolume::SmartPtr volume( VolumeIO::ReadOriented( Study1, Verbose ) );
00288 if ( !volume )
00289 {
00290 StdErr << "ERROR: volume " << this->Study1 << " could not be read\n";
00291 throw cmtk::ExitException( 1 );
00292 }
00293 this->SetVolume_1( UniformVolume::SmartPtr( this->m_PreprocessorRef.GetProcessedImage( volume ) ) );
00294
00295 volume = UniformVolume::SmartPtr( VolumeIO::ReadOriented( Study2, Verbose ) );
00296 if ( !volume )
00297 {
00298 StdErr << "ERROR: volume " << this->Study2 << " could not be read\n";
00299 throw cmtk::ExitException( 1 );
00300 }
00301 this->SetVolume_2( UniformVolume::SmartPtr( this->m_PreprocessorFlt.GetProcessedImage( volume ) ) );
00302
00303 if ( InitialStudylist )
00304 {
00305 Xform::SmartPtr xform( XformIO::Read( InitialStudylist, Verbose ) );
00306 if ( ! xform )
00307 {
00308 StdErr << "ERROR: could not read transformation from " << InitialStudylist << "\n";
00309 throw cmtk::ExitException( 1 );
00310 }
00311
00312 AffineXform::SmartPtr affine( AffineXform::SmartPtr::DynamicCastFrom( xform ) );
00313 if ( ! affine )
00314 {
00315 StdErr << "ERROR: transformation " << InitialStudylist << " is not affine.\n";
00316 throw cmtk::ExitException( 1 );
00317 }
00318
00319 if ( affine->m_MetaInformation[META_SPACE] != AnatomicalOrientation::ORIENTATION_STANDARD )
00320 {
00321 TransformChangeFromSpaceAffine toStandardSpace( *affine, *(this->m_Volume_1), *(this->m_Volume_2) );
00322 *affine = toStandardSpace.GetTransformation();
00323 affine->m_MetaInformation[META_SPACE] = AnatomicalOrientation::ORIENTATION_STANDARD;
00324 }
00325
00326 this->SetInitialTransformation( affine );
00327 }
00328
00329 if ( this->m_Initializer != MakeInitialAffineTransformation::NONE )
00330 {
00331 if ( inStudylist || InitialStudylist )
00332 {
00333 StdErr << "INFO: initial transformation was provided. Selected transformation initializer will be ignored.\n";
00334 }
00335 }
00336
00337 if ( this->m_ProtocolFileName )
00338 {
00339 RegistrationCallback::SmartPtr callback( new ProtocolCallback( this->m_ProtocolFileName ) );
00340 this->SetCallback( callback );
00341 }
00342
00343 if ( forceOutsideFlag )
00344 {
00345 this->SetForceOutside( true, forceOutsideValue );
00346 }
00347 }
00348
00349 CallbackResult
00350 ImagePairAffineRegistrationCommandLine::InitRegistration ()
00351 {
00352 CallbackResult Result = Superclass::InitRegistration();
00353 return Result;
00354 }
00355
00356 void
00357 ImagePairAffineRegistrationCommandLine::OutputResultMatrix( const char* matrixName ) const
00358 {
00359 Types::Coordinate matrix[4][4];
00360 this->GetTransformation()->GetMatrix( matrix );
00361
00362 FILE* mfile = fopen( matrixName, "w" );
00363 if ( mfile )
00364 {
00365 for ( int i = 0; i < 4; ++i )
00366 {
00367 fprintf( mfile, "%e\t%e\t%e\t%e\n", matrix[0][i], matrix[1][i], matrix[2][i], matrix[3][i] );
00368 }
00369 fclose( mfile );
00370 }
00371 }
00372
00373 void
00374 ImagePairAffineRegistrationCommandLine::OutputResultParameters
00375 ( const char* paramsName, const CoordinateVector& v ) const
00376 {
00377 FILE* pfile = fopen( paramsName, "w" );
00378 if ( pfile )
00379 {
00380 for ( unsigned int idx=0; idx < v.Dim; ++idx )
00381 fprintf( pfile, "#%d: %f\n", idx, v.Elements[idx] );
00382 fclose( pfile );
00383 }
00384 }
00385
00386 void
00387 ImagePairAffineRegistrationCommandLine::OutputResultList( const char* studyList ) const
00388 {
00389 ClassStream classStream( studyList, "studylist", ClassStream::WRITE );
00390 if ( !classStream.IsValid() ) return;
00391
00392 classStream.Begin( "studylist" );
00393 classStream.WriteInt( "num_sources", 2 );
00394 classStream.End();
00395
00396 classStream.Begin( "source" );
00397 classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study1 ) );
00398 classStream.End();
00399
00400 classStream.Begin( "source" );
00401 classStream.WriteString( "studyname", CompressedStream::GetBaseName( Study2 ) );
00402 classStream.End();
00403
00404 classStream.Close();
00405
00406 classStream.Open( studyList, "registration", ClassStream::WRITE );
00407
00408 classStream.Begin( "registration" );
00409 classStream.WriteString( "reference_study", CompressedStream::GetBaseName( Study1 ) );
00410 classStream.WriteString( "floating_study", CompressedStream::GetBaseName( Study2 ) );
00411
00412 classStream << *(this->GetTransformation());
00413
00414 classStream.End();
00415 classStream.Close();
00416
00417 classStream.Open( studyList, "settings", ClassStream::WRITE );
00418 classStream.WriteDouble( "exploration", this->m_MaxStepSize );
00419 classStream.WriteDouble( "accuracy", this->m_MinStepSize );
00420 classStream.WriteDouble( "min_sampling", this->m_Sampling );
00421 classStream.WriteDouble( "coarsest_resolution", this->m_CoarsestResolution );
00422 classStream.WriteInt( "metric", this->m_Metric );
00423 classStream.WriteDouble( "optimizer_step_factor", this->m_OptimizerStepFactor );
00424 classStream.WriteString( "initializer", MakeInitialAffineTransformation::GetModeName( this->m_Initializer ) );
00425
00426 this->m_PreprocessorRef.WriteSettings( classStream );
00427 this->m_PreprocessorFlt.WriteSettings( classStream );
00428
00429 classStream.Close();
00430
00431 classStream.Open( studyList, "statistics", ClassStream::WRITE );
00432 classStream.WriteDouble( "time", this->GetTotalElapsedTime() );
00433 classStream.WriteDouble( "walltime", this->GetTotalElapsedWalltime() );
00434 #ifdef CMTK_USE_THREADS
00435 classStream.WriteDouble( "thread_time", this->GetThreadTotalElapsedTime() );
00436 #endif
00437
00438 #ifndef _MSC_VER
00439 struct utsname name;
00440 if ( uname( &name ) >= 0 )
00441 {
00442 classStream.WriteString( "host", name.nodename );
00443 classStream.WriteString( "system", name.sysname );
00444 }
00445 #endif
00446 classStream.Close();
00447 }
00448
00449 void
00450 ImagePairAffineRegistrationCommandLine::OutputResult ( const CoordinateVector* v )
00451 {
00452 if ( Verbose )
00453 {
00454 fprintf( stderr, "\rResulting transformation parameters: \n" );
00455 for ( unsigned int idx=0; idx<v->Dim; ++idx )
00456 fprintf( stderr, "#%d: %f\n", idx, v->Elements[idx] );
00457 }
00458
00459 if ( this->OutMatrixName )
00460 {
00461 this->OutputResultMatrix( this->OutMatrixName );
00462 }
00463
00464 if ( this->OutParametersName )
00465 {
00466 this->OutputResultParameters( this->OutParametersName, *v );
00467 }
00468
00469 if ( this->Studylist )
00470 {
00471 this->OutputResultList( this->Studylist );
00472 }
00473
00474 if ( this->m_OutputPathITK )
00475 {
00476 TransformChangeToSpaceAffine toNative( *(this->GetTransformation()), *(this->m_Volume_1), *(this->m_Volume_2), AnatomicalOrientationBase::SPACE_ITK );
00477 AffineXformITKIO::Write( this->m_OutputPathITK, toNative.GetTransformation() );
00478 }
00479
00480 if ( this->m_ReformattedImagePath )
00481 {
00482 VolumeIO::Write( *(this->GetReformattedFloatingImage()), this->m_ReformattedImagePath, this->Verbose );
00483 }
00484
00485 #ifdef CMTK_USE_SQLITE
00486 if ( this->m_UpdateDB )
00487 {
00488 try
00489 {
00490 cmtk::ImageXformDB db( this->m_UpdateDB );
00491
00492 if ( this->m_ReformattedImagePath )
00493 {
00494 db.AddImage( this->m_ReformattedImagePath, this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ) );
00495 }
00496
00497 if ( this->Studylist )
00498 {
00499 if ( this->m_InitialXformPath )
00500 {
00501 db.AddRefinedXform( this->Studylist, true , this->m_InitialXformPath, this->m_InitialXformIsInverse );
00502 }
00503 else
00504 {
00505 db.AddImagePairXform( this->Studylist, true , this->m_ReferenceVolume->GetMetaInfo( META_FS_PATH ), this->m_FloatingVolume->GetMetaInfo( META_FS_PATH ) );
00506 }
00507 }
00508 }
00509 catch ( const cmtk::ImageXformDB::Exception& ex )
00510 {
00511 StdErr << "DB ERROR: " << ex.what() << " on database " << this->m_UpdateDB << "\n";
00512 }
00513 }
00514 #endif
00515 }
00516
00517 void
00518 ImagePairAffineRegistrationCommandLine::EnterResolution
00519 ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& f,
00520 const int index, const int total )
00521 {
00522 if ( Verbose )
00523 fprintf( stderr, "\rEntering resolution level %d out of %d...\n", index, total );
00524 this->Superclass::EnterResolution( v, f, index, total );
00525 }
00526
00527 CallbackResult
00528 ImagePairAffineRegistrationCommandLine::Register ()
00529 {
00530 const double baselineTime = Timers::GetTimeProcess();
00531 CallbackResult Result = Superclass::Register();
00532 const int elapsed = static_cast<int>( Timers::GetTimeProcess() - baselineTime );
00533
00534 if ( Time )
00535 {
00536 FILE *tfp = fopen( Time, "w" );
00537
00538 if ( tfp )
00539 {
00540 fprintf( tfp, "%d\n", elapsed );
00541 fclose( tfp );
00542 }
00543 else
00544 {
00545 std::cerr << "Could not open time file " << Time << "\n";
00546 }
00547 }
00548 return Result;
00549 }
00550
00551 }
00552