cmtkElasticRegistration.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 1997-2010 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: 2664 $
00026 //
00027 //  $LastChangedDate: 2010-12-14 21:33:44 -0800 (Tue, 14 Dec 2010) $
00028 //
00029 //  $LastChangedBy: torsten_at_home $
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   // If no initial transformation exists, create one from the defined
00120   // parameters.
00121   if ( InitialWarpXform ) 
00122     {
00123     // If we have an initial transformation from somewhere, use that.
00124     // This will override all registration parameters otherwise defined,
00125     // for example grid spacing and deformation type.
00126     InitialWarpXform->SetIgnoreEdge( IgnoreEdge );
00127     InitialWarpXform->SetFastMode( this->m_FastMode );
00128     // MIPSpro needs explicit.
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     // MIPSpro needs explicit:
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 //    newFunctional->SetForceOutside( this->m_ForceOutsideFlag, this->m_ForceOutsideValue );
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   // handle simple elastic functional
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     // handle inverse-consistent elastic functional
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       // neither simple nor inverse-consistent functional: something went
00310       // badly wrong.
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; // repeat with a relaxation step.
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 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines