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 "cmtkImagePairNonrigidRegistration.h"
00034
00035 #include <Base/cmtkLandmarkList.h>
00036 #include <Base/cmtkMatchedLandmarkList.h>
00037 #include <Base/cmtkUniformVolume.h>
00038 #include <Base/cmtkSplineWarpXform.h>
00039 #include <Base/cmtkTypedArrayFunctionHistogramMatching.h>
00040
00041 #include <Registration/cmtkImagePairNonrigidRegistrationFunctional.h>
00042 #include <Registration/cmtkImagePairSymmetricNonrigidRegistrationFunctional.h>
00043 #include <Registration/cmtkOptimizer.h>
00044 #include <Registration/cmtkBestNeighbourOptimizer.h>
00045 #include <Registration/cmtkBestDirectionOptimizer.h>
00046 #include <Registration/cmtkReformatVolume.h>
00047
00048 #include <algorithm>
00049
00050 namespace
00051 cmtk
00052 {
00053
00056
00057 ImagePairNonrigidRegistration::ImagePairNonrigidRegistration ()
00058 : InitialWarpXform( NULL ),
00059 InverseWarpXform( NULL ),
00060 m_MatchFltToRefHistogram( false ),
00061 m_RepeatMatchFltToRefHistogram( false ),
00062 m_InverseConsistencyWeight( 0.0 ),
00063 m_RelaxToUnfold( false )
00064 {
00065 this->m_Metric = 0;
00066 this->m_Algorithm = 3;
00067
00068 this->m_GridSpacing = 15;
00069 this->m_ExactGridSpacing = 0;
00070 this->m_GridSpacing = 10;
00071 RestrictToAxes = NULL;
00072 this->m_RefineGrid = 0;
00073 RefinedGridAtLevel = -1;
00074 RefineGridCount = 0;
00075 this->m_DelayRefineGrid = 0;
00076 RefineDelayed = false;
00077 IgnoreEdge = 0;
00078 this->m_FastMode = false;
00079 this->m_AdaptiveFixParameters = 1;
00080 this->m_AdaptiveFixThreshFactor = 0.5;
00081 this->m_JacobianConstraintWeight = 0;
00082 this->m_GridEnergyWeight = 0;
00083 this->m_RelaxWeight = -1;
00084 this->m_LandmarkErrorWeight = 0;
00085 this->m_InverseConsistencyWeight = 0.0;
00086 RelaxationStep = false;
00087 }
00088
00089 CallbackResult
00090 ImagePairNonrigidRegistration::InitRegistration ()
00091 {
00092 this->m_ReferenceVolume = this->m_Volume_1;
00093 this->m_FloatingVolume = this->m_Volume_2;
00094
00095 MatchedLandmarkList::SmartPtr mll( NULL );
00096 if ( this->m_LandmarkErrorWeight != 0 )
00097 {
00098 LandmarkList::SmartPtr sourceLandmarks = this->m_ReferenceVolume->m_LandmarkList;
00099 LandmarkList::SmartPtr targetLandmarks = this->m_FloatingVolume->m_LandmarkList;
00100
00101 if ( sourceLandmarks && targetLandmarks )
00102 {
00103 this->m_MatchedLandmarks = MatchedLandmarkList::SmartPtr( new MatchedLandmarkList( sourceLandmarks, targetLandmarks ) );
00104 fprintf( stderr, "Matched %d landmarks.\n", (int)this->m_MatchedLandmarks->size() );
00105 }
00106 }
00107
00108 Vector3D center = this->m_FloatingVolume->GetCenterCropRegion();
00109 this->m_InitialTransformation->ChangeCenter( center );
00110
00111 Types::Coordinate currSampling = std::max( this->m_Sampling, 2 * std::min( this->m_ReferenceVolume->GetMinDelta(), this->m_FloatingVolume->GetMinDelta()));
00112
00113
00114
00115 if ( InitialWarpXform )
00116 {
00117
00118
00119
00120 InitialWarpXform->SetIgnoreEdge( IgnoreEdge );
00121 InitialWarpXform->SetFastMode( this->m_FastMode );
00122
00123 this->m_Xform = Xform::SmartPtr::DynamicCastFrom( InitialWarpXform );
00124 }
00125 else
00126 {
00127 SplineWarpXform::SmartPtr warpXform( this->MakeWarpXform( this->m_ReferenceVolume->Size, this->m_InitialTransformation ) );
00128
00129 if ( this->m_InverseConsistencyWeight > 0 )
00130 InverseWarpXform = SplineWarpXform::SmartPtr( this->MakeWarpXform( this->m_FloatingVolume->Size, this->m_InitialTransformation->GetInverse() ) );
00131
00132
00133 this->m_Xform = Xform::SmartPtr::DynamicCastFrom( warpXform );
00134 }
00135
00136 if ( this->m_MaxStepSize <= 0 )
00137 {
00138 const SplineWarpXform* warp = SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform );
00139 this->m_MaxStepSize = 0.25 * std::max( warp->Spacing[0], std::max( warp->Spacing[1], warp->Spacing[2] ) );
00140 }
00141
00142 if ( this->m_CoarsestResolution <= 0 )
00143 this->m_CoarsestResolution = this->m_MaxStepSize;
00144
00145 if ( this->m_UseOriginalData )
00146 {
00147 this->m_ParameterStack.push( Self::LevelParameters::SmartPtr( new Self::LevelParameters( -1 ) ) );
00148 }
00149
00150 for ( ;(currSampling<=this->m_CoarsestResolution); currSampling *= 2 )
00151 {
00152 this->m_ParameterStack.push( Self::LevelParameters::SmartPtr( new Self::LevelParameters( currSampling ) ) );
00153 }
00154
00155 switch ( this->m_Algorithm )
00156 {
00157 case 0:
00158 this->m_Optimizer = Optimizer::SmartPtr( new BestNeighbourOptimizer( this->m_OptimizerStepFactor ) );
00159 break;
00160 case 1:
00161 case 2:
00162 this->m_Optimizer = Optimizer::SmartPtr( NULL );
00163 break;
00164 case 3:
00165 {
00166 BestDirectionOptimizer *optimizer = new BestDirectionOptimizer( this->m_OptimizerStepFactor );
00167 optimizer->SetUseMaxNorm( this->m_UseMaxNorm );
00168 this->m_Optimizer = Optimizer::SmartPtr( optimizer );
00169 break;
00170 }
00171 }
00172
00173 this->m_Optimizer->SetCallback( this->m_Callback );
00174
00175 return this->Superclass::InitRegistration();
00176 }
00177
00178 SplineWarpXform::SmartPtr
00179 ImagePairNonrigidRegistration::MakeWarpXform
00180 ( const UniformVolume::CoordinateVectorType& size, const AffineXform* initialAffine ) const
00181 {
00182 SplineWarpXform::SmartPtr warpXform( new SplineWarpXform( size, this->m_GridSpacing, initialAffine, this->m_ExactGridSpacing ) );
00183
00184 warpXform->SetIgnoreEdge( this->IgnoreEdge );
00185 warpXform->SetFastMode( this->m_FastMode );
00186
00187 return warpXform;
00188 }
00189
00190 Functional*
00191 ImagePairNonrigidRegistration::MakeFunctional
00192 ( const int level, const Superclass::LevelParameters* parameters )
00193 {
00194 const Self::LevelParameters* levelParameters = dynamic_cast<const Self::LevelParameters*>( parameters );
00195 if ( ! levelParameters )
00196 {
00197 StdErr << "CODING ERROR: wrong RTTI for 'parameters'\n";
00198 exit( 1 );
00199 }
00200
00201 WarpXform::SmartPtr warpXform = WarpXform::SmartPtr::DynamicCastFrom( this->m_Xform );
00202 if ( ! warpXform )
00203 {
00204 StdErr << "CODING ERROR: wrong RTTI for 'this->m_Xform'\n";
00205 exit( 1 );
00206 }
00207
00208 UniformVolume::SmartPtr referenceVolume( this->m_ReferenceVolume );
00209 UniformVolume::SmartPtr floatingVolume( this->m_FloatingVolume );
00210 if ( !level && this->m_MatchFltToRefHistogram )
00211 {
00212 floatingVolume = UniformVolume::SmartPtr( floatingVolume->Clone( true ) );
00213 floatingVolume->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(floatingVolume->GetData()), *(referenceVolume->GetData()) ) );
00214 }
00215 else
00216 {
00217 if ( this->m_RepeatMatchFltToRefHistogram )
00218 {
00219 floatingVolume = UniformVolume::SmartPtr( floatingVolume->Clone( true ) );
00220 UniformVolume::SmartPtr reformat( this->GetReformattedFloatingImage( Interpolators::NEAREST_NEIGHBOR ) );
00221 floatingVolume->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(reformat->GetData()), *(referenceVolume->GetData()) ) );
00222 }
00223 }
00224
00225 if ( levelParameters->m_Resolution > 0 )
00226 {
00227
00228 referenceVolume = UniformVolume::SmartPtr( new UniformVolume( *referenceVolume, levelParameters->m_Resolution ) );
00229 floatingVolume = UniformVolume::SmartPtr( new UniformVolume( *floatingVolume, levelParameters->m_Resolution ) );
00230 }
00231
00232 if ( this->m_InverseConsistencyWeight > 0 )
00233 {
00234 ImagePairSymmetricNonrigidRegistrationFunctional *newFunctional =
00235 ImagePairSymmetricNonrigidRegistrationFunctional::Create( this->m_Metric, referenceVolume, floatingVolume, this->m_FloatingImageInterpolation );
00236 newFunctional->SetInverseConsistencyWeight( this->m_InverseConsistencyWeight );
00237 newFunctional->SetAdaptiveFixParameters( this->m_AdaptiveFixParameters );
00238 newFunctional->SetAdaptiveFixThreshFactor( this->m_AdaptiveFixThreshFactor );
00239 newFunctional->SetJacobianConstraintWeight( this->m_JacobianConstraintWeight );
00240 newFunctional->SetGridEnergyWeight( this->m_GridEnergyWeight );
00241
00242 return newFunctional;
00243 }
00244 else
00245 {
00246 ImagePairNonrigidRegistrationFunctional *newFunctional =
00247 ImagePairNonrigidRegistrationFunctional::Create( this->m_Metric, referenceVolume, floatingVolume, this->m_FloatingImageInterpolation );
00248 newFunctional->SetActiveCoordinates( this->RestrictToAxes );
00249 newFunctional->SetAdaptiveFixParameters( this->m_AdaptiveFixParameters );
00250 newFunctional->SetAdaptiveFixThreshFactor( this->m_AdaptiveFixThreshFactor );
00251 newFunctional->SetJacobianConstraintWeight( this->m_JacobianConstraintWeight );
00252 newFunctional->SetForceOutside( this->m_ForceOutsideFlag, this->m_ForceOutsideValue );
00253 newFunctional->SetGridEnergyWeight( this->m_GridEnergyWeight );
00254 if ( this->m_MatchedLandmarks )
00255 {
00256 newFunctional->SetLandmarkErrorWeight( this->m_LandmarkErrorWeight );
00257 newFunctional->SetMatchedLandmarkList( this->m_MatchedLandmarks );
00258 }
00259
00260 return newFunctional;
00261 }
00262 }
00263
00264 void
00265 ImagePairNonrigidRegistration::EnterResolution
00266 ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& functional,
00267 const int idx, const int total )
00268 {
00269 float effGridEnergyWeight = this->m_GridEnergyWeight;
00270 float effJacobianConstraintWeight = this->m_JacobianConstraintWeight;
00271 float effInverseConsistencyWeight = this->m_InverseConsistencyWeight;
00272
00273 if ( (this->m_RelaxWeight > 0) && !this->RelaxationStep )
00274 {
00275 effGridEnergyWeight *= this->m_RelaxWeight;
00276 effJacobianConstraintWeight *= this->m_RelaxWeight;
00277 effInverseConsistencyWeight *= this->m_RelaxWeight;
00278 }
00279
00280 SplineWarpXform::SmartPtr warpXform = SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform );
00281
00282
00283 SmartPointer<ImagePairNonrigidRegistrationFunctional> nonrigidFunctional = ImagePairNonrigidRegistrationFunctional::SmartPtr::DynamicCastFrom( functional );
00284 if ( nonrigidFunctional )
00285 {
00286 if ( this->m_RelaxToUnfold )
00287 warpXform->RelaxToUnfold();
00288 nonrigidFunctional->SetWarpXform( warpXform );
00289 nonrigidFunctional->SetGridEnergyWeight( effGridEnergyWeight );
00290 nonrigidFunctional->SetJacobianConstraintWeight( effJacobianConstraintWeight );
00291 }
00292 else
00293 {
00294
00295 SmartPointer<ImagePairSymmetricNonrigidRegistrationFunctional> symmetricFunctional = ImagePairSymmetricNonrigidRegistrationFunctional::SmartPtr::DynamicCastFrom( functional );
00296 if ( symmetricFunctional )
00297 {
00298 if ( this->m_RelaxToUnfold )
00299 {
00300 warpXform->RelaxToUnfold();
00301 this->InverseWarpXform->RelaxToUnfold();
00302 }
00303 symmetricFunctional->SetWarpXform( warpXform, this->InverseWarpXform );
00304 symmetricFunctional->SetGridEnergyWeight( effGridEnergyWeight );
00305 symmetricFunctional->SetJacobianConstraintWeight( effJacobianConstraintWeight );
00306 symmetricFunctional->SetInverseConsistencyWeight( effInverseConsistencyWeight );
00307 }
00308 else
00309 {
00310
00311
00312 StdErr << "Fatal coding error: Non-nonrigid functional in ImagePairNonrigidRegistration::EnterResolution.\n";
00313 abort();
00314 }
00315 }
00316
00317 Superclass::EnterResolution( v, functional, idx, total );
00318 }
00319
00320 int
00321 ImagePairNonrigidRegistration::DoneResolution
00322 ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& functional,
00323 const int idx, const int total )
00324 {
00325 if ( ( this->m_RelaxWeight > 0 ) && ! RelaxationStep )
00326 {
00327 RelaxationStep = true;
00328 this->Superclass::DoneResolution( v, functional, idx, total );
00329 return false;
00330 }
00331 else
00332 {
00333 RelaxationStep = false;
00334 }
00335
00336 bool repeat = ( ( idx == total ) && ( RefineGridCount < this->m_RefineGrid ) );
00337
00338 if ( (RefinedGridAtLevel != idx) || (idx==total) )
00339 {
00340 if ( RefineGridCount < this->m_RefineGrid )
00341 {
00342 if ( (!this->m_DelayRefineGrid) || RefineDelayed || ( idx == total ) )
00343 {
00344 WarpXform::SmartPtr warpXform = WarpXform::SmartPtr::DynamicCastFrom( this->m_Xform );
00345 if ( warpXform )
00346 {
00347 warpXform->Refine();
00348 if ( InverseWarpXform )
00349 InverseWarpXform->Refine();
00350 ++RefineGridCount;
00351 functional->GetParamVector( *v );
00352 if ( this->m_Callback )
00353 this->m_Callback->Comment( "Refined control point grid." );
00354 RefinedGridAtLevel = idx;
00355 }
00356 if ( this->m_DelayRefineGrid && ( idx > 1 ) ) repeat = true;
00357 RefineDelayed = false;
00358 }
00359 else
00360 {
00361 RefineDelayed = true;
00362 }
00363 }
00364 }
00365 else
00366 {
00367 RefineDelayed = true;
00368 }
00369
00370 return this->Superclass::DoneResolution( v, functional, idx, total ) && !repeat;
00371 }
00372
00373 const UniformVolume::SmartPtr
00374 ImagePairNonrigidRegistration::GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator ) const
00375 {
00376 ReformatVolume reformat;
00377 reformat.SetInterpolation( interpolator );
00378 reformat.SetReferenceVolume( this->m_ReferenceVolume );
00379 reformat.SetFloatingVolume( this->m_FloatingVolume );
00380
00381 WarpXform::SmartPtr warpXform( this->GetTransformation() );
00382 reformat.SetWarpXform( warpXform );
00383
00384 if ( this->m_ForceOutsideFlag )
00385 {
00386 reformat.SetPaddingValue( this->m_ForceOutsideValue );
00387 }
00388
00389 UniformVolume::SmartPtr result = reformat.PlainReformat();
00390
00391 if ( this->m_ForceOutsideFlag )
00392 {
00393 result->GetData()->ClearPaddingFlag();
00394 }
00395 return result;
00396 }
00397
00398
00399 }