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 "cmtkElasticRegistration.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/cmtkVoxelMatchingElasticFunctional.h>
00042 #include <Registration/cmtkSymmetricElasticFunctional.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 ElasticRegistration::ElasticRegistration ()
00058 : VoxelRegistration(),
00059 InitialWarpXform( NULL ),
00060 InverseWarpXform( NULL ),
00061 ForceSwitchVolumes( false ),
00062 m_MatchFltToRefHistogram( false ),
00063 m_RigidityConstraintMap( NULL ),
00064 m_InverseConsistencyWeight( 0.0 ),
00065 m_RelaxToUnfold( false ),
00066 m_ForceOutsideFlag( false ),
00067 m_ForceOutsideValue( 0.0 )
00068 {
00069 this->m_GridSpacing = 10;
00070 RestrictToAxes = NULL;
00071 this->m_RefineGrid = 0;
00072 RefinedGridAtLevel = -1;
00073 RefineGridCount = 0;
00074 this->m_DelayRefineGrid = 0;
00075 RefineDelayed = false;
00076 IgnoreEdge = 0;
00077 this->m_FastMode = false;
00078 this->m_AdaptiveFixParameters = 1;
00079 this->m_AdaptiveFixThreshFactor = 0.5;
00080 this->m_JacobianConstraintWeight = 0;
00081 this->m_RigidityConstraintWeight = 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 ElasticRegistration::InitRegistration ()
00091 {
00092 this->m_ReferenceVolume = this->m_Volume_1;
00093 this->m_FloatingVolume = this->m_Volume_2;
00094
00095 if ( this->m_MatchFltToRefHistogram )
00096 {
00097 this->GetVolume_2()->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(this->GetVolume_2()->GetData()), *(this->GetVolume_1()->GetData()) ) );
00098 }
00099
00100 if ( this->m_LandmarkErrorWeight != 0 )
00101 {
00102 LandmarkList::SmartPtr sourceLandmarks = this->m_ReferenceVolume->m_LandmarkList;
00103 LandmarkList::SmartPtr targetLandmarks = this->m_FloatingVolume->m_LandmarkList;
00104
00105 if ( sourceLandmarks && targetLandmarks )
00106 {
00107 this->m_LandmarkList = MatchedLandmarkList::SmartPtr( new MatchedLandmarkList( sourceLandmarks, targetLandmarks ) );
00108 fprintf( stderr, "Matched %d landmarks.\n", (int)this->m_LandmarkList->size() );
00109 }
00110 }
00111
00112 AffineXform::SmartPtr affineXform = this->m_InitialTransformation;
00113 AffineXform::SmartPtr initialInverse = AffineXform::SmartPtr::DynamicCastFrom( this->m_InitialTransformation->GetInverse() );
00114
00115 affineXform->ChangeCenter( this->m_FloatingVolume->GetCenterCropRegion() );
00116
00117 Types::Coordinate currSampling = std::max( this->m_Sampling, 2 * std::min( this->m_ReferenceVolume->GetMinDelta(), this->m_FloatingVolume->GetMinDelta()));
00118
00119
00120
00121 if ( InitialWarpXform )
00122 {
00123
00124
00125
00126 InitialWarpXform->SetIgnoreEdge( IgnoreEdge );
00127 InitialWarpXform->SetFastMode( this->m_FastMode );
00128
00129 this->m_Xform = Xform::SmartPtr::DynamicCastFrom( InitialWarpXform );
00130 }
00131 else
00132 {
00133 SplineWarpXform::SmartPtr warpXform( this->MakeWarpXform( this->m_ReferenceVolume->Size, affineXform ) );
00134
00135 if ( this->m_InverseConsistencyWeight > 0 )
00136 InverseWarpXform = SplineWarpXform::SmartPtr( this->MakeWarpXform( this->m_FloatingVolume->Size, initialInverse ) );
00137
00138
00139 this->m_Xform = Xform::SmartPtr::DynamicCastFrom( warpXform );
00140 }
00141
00142 if ( this->m_UseOriginalData )
00143 {
00144 Functional::SmartPtr nextFunctional( this->MakeFunctional( this->m_ReferenceVolume, this->m_FloatingVolume, this->m_RigidityConstraintMap ) );
00145 FunctionalStack.push( nextFunctional );
00146 }
00147
00148 if ( this->m_Exploration <= 0 )
00149 {
00150 const SplineWarpXform* warp = SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform );
00151 this->m_Exploration = 0.25 * std::max( warp->Spacing[0], std::max( warp->Spacing[1], warp->Spacing[2] ) );
00152 }
00153
00154 if ( this->CoarsestResolution <= 0 )
00155 this->CoarsestResolution = this->m_Exploration;
00156
00157 UniformVolume::SmartPtr currRef( this->m_ReferenceVolume );
00158 UniformVolume::SmartPtr currFlt( this->m_FloatingVolume );
00159
00160 for ( ;(currSampling<=this->CoarsestResolution); currSampling *= 2 )
00161 {
00162 UniformVolume::SmartPtr nextRef( new UniformVolume( *currRef, currSampling ) );
00163 UniformVolume::SmartPtr nextFlt( new UniformVolume( *currFlt, currSampling ) );
00164
00165 UniformVolume::SmartPtr nextRigidityMap;
00166 if ( this->m_RigidityConstraintMap )
00167 {
00168 nextRigidityMap = UniformVolume::SmartPtr( new UniformVolume( *this->m_RigidityConstraintMap, currSampling ) );
00169 }
00170
00171 Functional::SmartPtr nextFunctional( this->MakeFunctional( nextRef, nextFlt, nextRigidityMap ) );
00172 FunctionalStack.push( nextFunctional );
00173
00174 currRef = nextRef;
00175 currFlt = nextFlt;
00176 }
00177
00178 switch ( this->m_Algorithm )
00179 {
00180 case 0:
00181 this->m_Optimizer = Optimizer::SmartPtr( new BestNeighbourOptimizer( OptimizerStepFactor ) );
00182 break;
00183 case 1:
00184 case 2:
00185 this->m_Optimizer = Optimizer::SmartPtr( NULL );
00186 break;
00187 case 3:
00188 {
00189 BestDirectionOptimizer *optimizer = new BestDirectionOptimizer( OptimizerStepFactor );
00190 optimizer->SetUseMaxNorm( UseMaxNorm );
00191 this->m_Optimizer = Optimizer::SmartPtr( optimizer );
00192 break;
00193 }
00194 }
00195
00196 this->m_Optimizer->SetCallback( this->m_Callback );
00197
00198 return this->Superclass::InitRegistration();
00199 }
00200
00201 const SplineWarpXform::SmartPtr
00202 ElasticRegistration::MakeWarpXform
00203 ( const Vector3D& size, const AffineXform* initialAffine ) const
00204 {
00205 SplineWarpXform::SmartPtr warpXform( new SplineWarpXform( size, this->m_GridSpacing, initialAffine, this->m_ExactGridSpacing ) );
00206
00207 warpXform->SetIgnoreEdge( this->IgnoreEdge );
00208 warpXform->SetFastMode( this->m_FastMode );
00209 warpXform->SetParametersActive();
00210
00211 return warpXform;
00212 }
00213
00214 Functional*
00215 ElasticRegistration::MakeFunctional
00216 ( UniformVolume::SmartPtr& refVolume, UniformVolume::SmartPtr& fltVolume,
00217 UniformVolume::SmartPtr& rigidityMap ) const
00218 {
00219 if ( this->m_InverseConsistencyWeight > 0 )
00220 {
00221 SymmetricElasticFunctional *newFunctional = CreateSymmetricElasticFunctional( this->m_Metric, refVolume, fltVolume );
00222 newFunctional->SetInverseConsistencyWeight( this->m_InverseConsistencyWeight );
00223 newFunctional->SetAdaptiveFixParameters( this->m_AdaptiveFixParameters );
00224 newFunctional->SetAdaptiveFixThreshFactor( this->m_AdaptiveFixThreshFactor );
00225 newFunctional->SetJacobianConstraintWeight( this->m_JacobianConstraintWeight );
00226 newFunctional->SetRigidityConstraintWeight( this->m_RigidityConstraintWeight );
00227 newFunctional->SetGridEnergyWeight( this->m_GridEnergyWeight );
00228
00229 return newFunctional;
00230 }
00231 else
00232 {
00233 VoxelMatchingElasticFunctional *newFunctional = CreateElasticFunctional( this->m_Metric, refVolume, fltVolume );
00234 newFunctional->SetAdaptiveFixParameters( this->m_AdaptiveFixParameters );
00235 newFunctional->SetAdaptiveFixThreshFactor( this->m_AdaptiveFixThreshFactor );
00236 newFunctional->SetJacobianConstraintWeight( this->m_JacobianConstraintWeight );
00237 newFunctional->SetRigidityConstraintWeight( this->m_RigidityConstraintWeight );
00238 newFunctional->SetForceOutside( this->m_ForceOutsideFlag, this->m_ForceOutsideValue );
00239 newFunctional->SetActiveCoordinates( this->RestrictToAxes );
00240 if ( rigidityMap )
00241 {
00242 newFunctional->SetRigidityConstraintMap( rigidityMap );
00243 }
00244 newFunctional->SetGridEnergyWeight( this->m_GridEnergyWeight );
00245 if ( this->m_LandmarkList )
00246 {
00247 newFunctional->SetLandmarkErrorWeight( this->m_LandmarkErrorWeight );
00248 newFunctional->SetMatchedLandmarkList( this->m_LandmarkList );
00249 }
00250
00251 return newFunctional;
00252 }
00253 }
00254
00255 void
00256 ElasticRegistration::EnterResolution
00257 ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& functional,
00258 const int idx, const int total )
00259 {
00260 SplineWarpXform::SmartPtr warpXform = SplineWarpXform::SmartPtr::DynamicCastFrom( this->m_Xform );
00261
00262 float effGridEnergyWeight = this->m_GridEnergyWeight;
00263 float effJacobianConstraintWeight = this->m_JacobianConstraintWeight;
00264 float effRigidityConstraintWeight = this->m_RigidityConstraintWeight;
00265 float effInverseConsistencyWeight = this->m_InverseConsistencyWeight;
00266
00267 if ( (this->m_RelaxWeight > 0) && !this->RelaxationStep )
00268 {
00269 effGridEnergyWeight *= this->m_RelaxWeight;
00270 effJacobianConstraintWeight *= this->m_RelaxWeight;
00271 effRigidityConstraintWeight *= this->m_RelaxWeight;
00272 effInverseConsistencyWeight *= this->m_RelaxWeight;
00273 }
00274
00275
00276 SmartPointer<VoxelMatchingElasticFunctional> elasticFunctional = VoxelMatchingElasticFunctional::SmartPtr::DynamicCastFrom( functional );
00277 if ( elasticFunctional )
00278 {
00279 elasticFunctional->SetWarpXform( warpXform );
00280
00281 if ( this->m_RelaxToUnfold )
00282 warpXform->RelaxToUnfold();
00283
00284 elasticFunctional->SetGridEnergyWeight( effGridEnergyWeight );
00285 elasticFunctional->SetJacobianConstraintWeight( effJacobianConstraintWeight );
00286 elasticFunctional->SetRigidityConstraintWeight( effRigidityConstraintWeight );
00287 }
00288 else
00289 {
00290
00291 SmartPointer<SymmetricElasticFunctional> symmetricFunctional = SymmetricElasticFunctional::SmartPtr::DynamicCastFrom( functional );
00292 if ( symmetricFunctional )
00293 {
00294 symmetricFunctional->SetWarpXform( warpXform, this->InverseWarpXform );
00295
00296 if ( this->m_RelaxToUnfold )
00297 {
00298 warpXform->RelaxToUnfold();
00299 this->InverseWarpXform->RelaxToUnfold();
00300 }
00301
00302 symmetricFunctional->SetGridEnergyWeight( effGridEnergyWeight );
00303 symmetricFunctional->SetJacobianConstraintWeight( effJacobianConstraintWeight );
00304 symmetricFunctional->SetRigidityConstraintWeight( effRigidityConstraintWeight );
00305 symmetricFunctional->SetInverseConsistencyWeight( effInverseConsistencyWeight );
00306 }
00307 else
00308 {
00309
00310
00311 StdErr << "Fatal coding error: Non-elastic functional in ElasticRegistration::EnterResolution.\n";
00312 abort();
00313 }
00314 }
00315
00316 Superclass::EnterResolution( v, functional, idx, total );
00317 }
00318
00319 int
00320 ElasticRegistration::DoneResolution
00321 ( CoordinateVector::SmartPtr& v, Functional::SmartPtr& functional,
00322 const int idx, const int total )
00323 {
00324 if ( ( this->m_RelaxWeight > 0 ) && ! RelaxationStep )
00325 {
00326 RelaxationStep = true;
00327 this->Superclass::DoneResolution( v, functional, idx, total );
00328 return false;
00329 }
00330 else
00331 {
00332 RelaxationStep = false;
00333 }
00334
00335 bool repeat = ( ( idx == total ) && ( RefineGridCount < this->m_RefineGrid ) );
00336
00337 if ( (RefinedGridAtLevel != idx) || (idx==total) )
00338 {
00339 if ( RefineGridCount < this->m_RefineGrid )
00340 {
00341 if ( (!this->m_DelayRefineGrid) || RefineDelayed || ( idx == total ) )
00342 {
00343 WarpXform::SmartPtr warpXform = WarpXform::SmartPtr::DynamicCastFrom( this->m_Xform );
00344 if ( warpXform )
00345 {
00346 warpXform->Refine();
00347 if ( InverseWarpXform )
00348 InverseWarpXform->Refine();
00349 ++RefineGridCount;
00350 functional->GetParamVector( *v );
00351 if ( this->m_Callback )
00352 this->m_Callback->Comment( "Refined control point grid." );
00353 RefinedGridAtLevel = idx;
00354 }
00355 if ( this->m_DelayRefineGrid && ( idx > 1 ) ) repeat = true;
00356 RefineDelayed = false;
00357 }
00358 else
00359 {
00360 RefineDelayed = true;
00361 }
00362 }
00363 }
00364 else
00365 {
00366 RefineDelayed = true;
00367 }
00368
00369 return this->Superclass::DoneResolution( v, functional, idx, total ) && !repeat;
00370 }
00371
00372 const UniformVolume::SmartPtr
00373 ElasticRegistration::GetReformattedFloatingImage( Interpolators::InterpolationEnum interpolator )
00374 {
00375 ReformatVolume reformat;
00376 reformat.SetInterpolation( interpolator );
00377 reformat.SetReferenceVolume( this->m_Volume_1 );
00378 reformat.SetFloatingVolume( this->m_Volume_2 );
00379
00380 WarpXform::SmartPtr warpXform( this->GetTransformation() );
00381 reformat.SetWarpXform( warpXform );
00382
00383 return reformat.PlainReformat();
00384 }
00385
00386
00387 }