cmtkImagePairNonrigidRegistration.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 2004-2010 SRI International
00004 //
00005 //  Copyright 1997-2009 Torsten Rohlfing
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: 2660 $
00026 //
00027 //  $LastChangedDate: 2010-12-14 12:35:50 -0800 (Tue, 14 Dec 2010) $
00028 //
00029 //  $LastChangedBy: torsten_at_home $
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   // If no initial transformation exists, create one from the defined
00114   // parameters.
00115   if ( InitialWarpXform ) 
00116     {
00117     // If we have an initial transformation from somewhere, use that.
00118     // This will override all registration parameters otherwise defined,
00119     // for example grid spacing and deformation type.
00120     InitialWarpXform->SetIgnoreEdge( IgnoreEdge );
00121     InitialWarpXform->SetFastMode( this->m_FastMode );
00122     // MIPSpro needs explicit.
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     // MIPSpro needs explicit:
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 /*copyData*/ ) );
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 /*copyData*/ ) );
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     // for resample if not final, original resolution
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   // handle simple nonrigid functional
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     // handle inverse-consistent nonrigid functional
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       // neither simple nor inverse-consistent functional: something went
00311       // badly wrong.
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; // repeat with a relaxation step.
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 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines