cmtkSplineWarpXform.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 1997-2010 Torsten Rohlfing
00004 //
00005 //  Copyright 2004-2011 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: 2731 $
00026 //
00027 //  $LastChangedDate: 2011-01-13 16:22:47 -0800 (Thu, 13 Jan 2011) $
00028 //
00029 //  $LastChangedBy: torstenrohlfing $
00030 //
00031 */
00032 
00033 #include "cmtkSplineWarpXform.h"
00034 
00035 #include <Base/cmtkMathUtil.h>
00036 #include <Base/cmtkBitVector.h>
00037 
00038 #include <vector>
00039 #include <cassert>
00040 
00041 namespace
00042 cmtk
00043 {
00044 
00047 
00048 SplineWarpXform::SplineWarpXform()
00049 {
00050   this->Init();
00051 }
00052 
00053 void SplineWarpXform::Init () 
00054 {
00055   this->GlobalScaling = 1.0;
00056 }
00057 
00058 SplineWarpXform::SplineWarpXform 
00059 ( const FixedVector<3,Types::Coordinate>& domain, const Types::Coordinate delta, const AffineXform* initialXform, const bool exactDelta  )
00060 {
00061   this->Init( domain, delta, initialXform, exactDelta );
00062 }
00063 
00064 void
00065 SplineWarpXform
00066 ::Init
00067 ( const Self::SpaceVectorType& domain, const Types::Coordinate delta, const AffineXform* initialXform, const bool exactDelta  )
00068 {
00069   this->Init();
00070   this->Domain = domain;
00071   this->m_InitialAffineXform = initialXform->Clone();
00072 
00073   if ( exactDelta ) 
00074     {
00075     for ( int dim=0; dim<3; ++dim ) 
00076       {
00077       Spacing[dim] = delta;
00078       this->m_Dims[dim] = static_cast<int>( 4 + (Domain[dim] / Spacing[dim]) );
00079       Domain[dim] = (this->m_Dims[dim] - 3) * Spacing[dim];
00080       }
00081     } 
00082   else
00083     {
00084     for ( int dim=0; dim<3; ++dim )
00085       this->m_Dims[dim] = 2 + std::max( 2, 1+static_cast<int>( domain[dim]/delta ) );
00086     }
00087   
00088   NumberOfControlPoints = this->m_Dims[0] * this->m_Dims[1] * this->m_Dims[2];
00089   this->AllocateParameterVector( 3 * NumberOfControlPoints );
00090   
00091   this->Update( exactDelta );
00092   this->InitControlPoints( this->m_InitialAffineXform );
00093 }
00094 
00095 SplineWarpXform::SplineWarpXform
00096 ( const FixedVector<3,Types::Coordinate>& domain, const Self::IndexType& dims, CoordinateVector::SmartPtr& parameters, const AffineXform* initialXform )
00097 {
00098   this->Init();
00099   this->Domain = domain;
00100   this->m_Dims = dims;
00101 
00102   if ( initialXform )
00103     {
00104     this->m_InitialAffineXform = initialXform->Clone();
00105     GlobalScaling = this->m_InitialAffineXform->GetGlobalScaling();
00106     } 
00107   else
00108     {
00109     this->m_InitialAffineXform = AffineXform::SmartPtr( NULL );
00110     }
00111 
00112   NumberOfControlPoints = this->m_Dims[0] * this->m_Dims[1] * this->m_Dims[2];
00113   this->m_NumberOfParameters = 3 * NumberOfControlPoints;
00114 
00115   if ( !parameters )
00116     this->m_ParameterVector = CoordinateVector::SmartPtr( new CoordinateVector( this->m_NumberOfParameters ) );
00117   else
00118     this->m_ParameterVector = parameters;
00119   this->m_Parameters = this->m_ParameterVector->Elements;
00120 
00121   this->Update( false /* exactDelta */ );
00122 
00123   if ( !parameters )
00124     this->InitControlPoints( this->m_InitialAffineXform );
00125 }
00126 
00127 void
00128 SplineWarpXform::InitControlPoints( const AffineXform* affineXform )
00129 {
00130   Types::Coordinate *ofs = this->m_Parameters;
00131   Types::Coordinate pZ = -Spacing[2];
00132   for ( int z=0; z<this->m_Dims[2]; ++z, pZ+=Spacing[2] ) 
00133     {
00134     Types::Coordinate pY = -Spacing[1];
00135     for ( int y=0; y<this->m_Dims[1]; ++y, pY+=Spacing[1] ) 
00136       {
00137       Types::Coordinate pX = -Spacing[0];
00138       for ( int x=0; x<this->m_Dims[0]; ++x, pX+=Spacing[0], ofs+=3 ) 
00139         {
00140         ofs[0] = pX;
00141         ofs[1] = pY;
00142         ofs[2] = pZ;
00143         }
00144       }
00145     }
00146   
00147   if ( affineXform ) 
00148     {
00149     ofs = this->m_Parameters;
00150     for ( unsigned int idx = 0; idx < NumberOfControlPoints; ++idx, ofs+=3 ) 
00151       {
00152       Self::SpaceVectorType p( ofs );
00153       affineXform->ApplyInPlace( p );
00154       ofs[0] = p[0];
00155       ofs[1] = p[1];
00156       ofs[2] = p[2];
00157       }
00158     
00159     affineXform->GetScales( this->InverseAffineScaling );
00160     GlobalScaling = affineXform->GetGlobalScaling();
00161     } 
00162   else
00163     {
00164     InverseAffineScaling[0] = InverseAffineScaling[1] = InverseAffineScaling[2] = GlobalScaling = 1.0;
00165     }
00166 }
00167 
00168 void
00169 SplineWarpXform::Update 
00170 ( const bool exactDelta ) 
00171 {
00172   this->WarpXform::Update();
00173 
00174   for ( int dim=0; dim<3; ++dim ) 
00175     {
00176     assert( this->m_Dims[dim] > 3 );
00177     if ( exactDelta ) 
00178       {
00179       InverseSpacing[dim] = 1.0 / Spacing[dim];
00180       } 
00181     else
00182       {
00183       Spacing[dim] = Domain[dim] / (this->m_Dims[dim]-3);
00184       InverseSpacing[dim] = 1.0*(this->m_Dims[dim]-3) / Domain[dim];
00185       }
00186     m_Offset[dim] = -Spacing[dim];
00187     }
00188   
00189   int dml = 0;
00190   for ( int dim = 0; dim<3; ++dim )
00191     for ( int m = 0; m < 4; ++m )
00192       for ( int l = 0; l < 4; ++l, ++dml )
00193         GridPointOffset[dml] = dim + l * nextJ + m * nextK;
00194 }
00195 
00196 SplineWarpXform* 
00197 SplineWarpXform::CloneVirtual() const
00198 {
00199   SplineWarpXform *newXform = new SplineWarpXform();
00200 
00201   newXform->m_ParameterVector = CoordinateVector::SmartPtr( this->m_ParameterVector->Clone() );
00202   newXform->m_Parameters = newXform->m_ParameterVector->Elements;
00203   newXform->m_NumberOfParameters = this->m_NumberOfParameters;
00204   newXform->NumberOfControlPoints = this->NumberOfControlPoints;
00205   
00206   newXform->m_Dims = this->m_Dims;
00207   newXform->Domain = this->Domain;
00208   memcpy( newXform->Spacing, Spacing, sizeof( newXform->Spacing ) );
00209   memcpy( newXform->InverseSpacing, InverseSpacing, sizeof( newXform->InverseSpacing ) );
00210   newXform->m_Offset = this->m_Offset;
00211 
00212   if ( this->m_ActiveFlags ) 
00213     {
00214     BitVector::SmartPtr activeFlags( this->m_ActiveFlags->Clone() );
00215     newXform->SetActiveFlags( activeFlags );
00216     }
00217   newXform->m_IgnoreEdge = this->m_IgnoreEdge;
00218   newXform->m_FastMode = this->m_FastMode;
00219 
00220   if ( this->m_InitialAffineXform ) 
00221     {
00222     newXform->m_InitialAffineXform = AffineXform::SmartPtr( this->m_InitialAffineXform->Clone() );
00223     }
00224   
00225   newXform->GlobalScaling = this->GlobalScaling;
00226 
00227   newXform->nextI = this->nextI;
00228   newXform->nextJ = this->nextJ;
00229   newXform->nextK = this->nextK;
00230   newXform->nextIJ = this->nextIJ;
00231   newXform->nextIK = this->nextIK;
00232   newXform->nextJK = this->nextJK;
00233   newXform->nextIJK = this->nextIJK;
00234   memcpy( newXform->GridPointOffset, this->GridPointOffset, sizeof( this->GridPointOffset ) );
00235 
00236   newXform->VolumeDims = this->VolumeDims;
00237 
00238   newXform->gX = this->gX;
00239   newXform->gY = this->gY;
00240   newXform->gZ = this->gZ;
00241 
00242   newXform->splineX = this->splineX;
00243   newXform->splineY = this->splineY;
00244   newXform->splineZ = this->splineZ;
00245 
00246   newXform->dsplineX = this->dsplineX;
00247   newXform->dsplineY = this->dsplineY;
00248   newXform->dsplineZ = this->dsplineZ;
00249 
00250   return newXform;
00251 }
00252 
00253 void
00254 SplineWarpXform::Refine()
00255 {
00256   if ( !this->m_ParameterVector ) return;
00257 
00258   Self::IndexType newDims;
00259   for ( int dim=0; dim<3; ++dim ) 
00260     newDims[dim] = 2 * this->m_Dims[dim] - 3;
00261 
00262   const int newNumSamples = newDims[0] * newDims[1] * newDims[2];
00263   const int newNumCoefficients = 3 * newNumSamples;
00264 
00265   CoordinateVector::SmartPtr newParameters( new CoordinateVector( newNumCoefficients ) );
00266   Types::Coordinate* newCoefficients = newParameters->Elements;
00267 
00268   Types::Coordinate newSpacing[3];
00269   for ( int dim=0; dim<3; ++dim ) 
00270     {
00271     newSpacing[dim] = Domain[dim] / (newDims[dim]-3);
00272     }
00273 
00274   // no linear refinement here
00275   const int newNextI = 3;
00276   const int newNextJ = newNextI * newDims[0];
00277   const int newNextK = newNextJ * newDims[1];
00278   const int newNextIJ = newNextJ + newNextI;
00279   const int newNextIK = newNextK + newNextI;
00280   const int newNextJK = newNextK + newNextJ;
00281   const int newNextIJK = newNextJK + newNextI;
00282 
00283   Types::Coordinate level0[3][3];
00284   memset( level0, 0, sizeof( level0 ) );
00285   Types::Coordinate level1[3];
00286   memset( level1, 0, sizeof( level1 ) );
00287 
00288   Types::Coordinate *ncoeff = newCoefficients;
00289   for ( int z = 0; z<newDims[2]; ++z ) 
00290     {
00291     for ( int y = 0; y<newDims[1]; ++y ) 
00292       {
00293       for ( int x = 0; x<newDims[0]; ++x ) 
00294         {
00295         const int oldX = ((x+1)/2), oldY = ((y+1)/2), oldZ = ((z+1)/2);
00296         const int oddX = x%2, oddY = y%2, oddZ = z%2;
00297         
00298         const Types::Coordinate *coeff = m_Parameters + oldX*nextI + oldY*nextJ + oldZ*nextK;
00299         
00300         for ( int dim=0; dim<3; ++dim, ++coeff, ++ncoeff ) 
00301           {       
00302           for ( int k=0; k<3; ++k ) 
00303             {
00304             int ofsJK = (k-1) * nextK - nextJ;
00305             for ( int j=0; j<3; ++j, ofsJK += nextJ ) 
00306               {
00307               if ( (oddY || j) && (oddZ || k) ) 
00308                 {
00309                 if ( oddX ) 
00310                   {
00311                   level0[k][j] = (coeff[ofsJK-nextI] + 6 * coeff[ofsJK] + coeff[ofsJK+nextI]) / 8;
00312                   } 
00313                 else
00314                   {
00315                   level0[k][j] = (coeff[ofsJK] + coeff[ofsJK+nextI]) / 2;
00316                   }
00317                 }
00318               }
00319             }
00320           
00321           for ( int k=0; k<3; ++k )
00322             {
00323             if ( oddZ || k )
00324               {
00325               if ( oddY ) 
00326                 {
00327                 level1[k] = (level0[k][0] + 6 * level0[k][1] + level0[k][2]) / 8;
00328                 } 
00329               else
00330                 {
00331                 level1[k] = (level0[k][1] + level0[k][2]) / 2;
00332                 }
00333               }
00334             }
00335           
00336           if ( oddZ ) 
00337             {
00338             *ncoeff = (level1[0] + 6 * level1[1] + level1[2]) / 8;
00339             } 
00340           else
00341             {
00342             *ncoeff = (level1[1] + level1[2]) / 2;
00343             } 
00344           }
00345         }
00346       }
00347     }
00348 
00349   NumberOfControlPoints = newNumSamples;
00350   this->m_NumberOfParameters = newNumCoefficients;
00351 
00352   this->m_ParameterVector = newParameters;
00353   this->m_Parameters = newCoefficients;
00354 
00355   this->DeleteParameterActiveFlags();
00356   this->m_Dims = newDims;
00357 
00358   for ( int dim=0; dim<3; ++dim ) 
00359     {
00360     assert( this->m_Dims[dim] > 1 );
00361     Spacing[dim] = newSpacing[dim];
00362     InverseSpacing[dim] = 1.0 / Spacing[dim];
00363     m_Offset[dim] = -Spacing[dim];
00364     }
00365   
00366   // MUST do this AFTER acutal refinement, as precomputed increments are used
00367   // for old grid.
00368   nextI = newNextI;
00369   nextJ = newNextJ;
00370   nextK = newNextK;
00371   nextIJ = newNextIJ;
00372   nextIK = newNextIK;
00373   nextJK = newNextJK;
00374   nextIJK = newNextIJK;
00375 
00376   int dml = 0;
00377   for ( int dim = 0; dim<3; ++dim )
00378     for ( int m = 0; m < 4; ++m )
00379       for ( int l = 0; l < 4; ++l, ++dml )
00380         GridPointOffset[dml] = dim + l * nextJ + m * nextK;
00381 
00382   if ( this->m_IgnoreEdge )
00383     this->m_IgnoreEdge = 2 * this->m_IgnoreEdge - 1;
00384 
00385   this->UnRegisterVolume();
00386 }
00387 
00388 void 
00389 SplineWarpXform::GetVolumeOfInfluence 
00390 ( const size_t idx, const Self::SpaceVectorType& fromVol, const Self::SpaceVectorType& toVol,
00391   Self::SpaceVectorType& fromVOI, Self::SpaceVectorType& toVOI, const int fastMode ) const
00392 {
00393   int relIdx = idx / 3;
00394 
00395   const int xyz[3] = { ( relIdx %  this->m_Dims[0] ), 
00396                        ( (relIdx /  this->m_Dims[0]) % this->m_Dims[1] ), 
00397                        ( (relIdx /  this->m_Dims[0]) / this->m_Dims[1] ) };
00398   
00399   Types::Coordinate xyzLow[3], xyzUp[3];
00400 
00401   if ( (fastMode==1) || (this->m_FastMode && (fastMode<0)) ) 
00402     {
00403     for ( int dim = 0; dim < 3; ++dim )
00404       {
00405       xyzLow[dim] = Spacing[dim] * std::max( 0, xyz[dim]-2 );
00406       xyzUp[dim] = Spacing[dim] * std::min( this->m_Dims[dim]-3, xyz[dim] );
00407       }
00408     } 
00409   else
00410     {
00411     for ( int dim = 0; dim < 3; ++dim )
00412       {
00413       xyzLow[dim] = Spacing[dim] * std::max( 0, xyz[dim]-3 );
00414       xyzUp[dim] = Spacing[dim] * std::min( this->m_Dims[dim]-2, xyz[dim]+1 );
00415       }
00416     }
00417   
00418   for ( int dim = 0; dim < 3; ++dim )
00419     {
00420     fromVOI[dim] = std::min( toVol[dim], std::max( xyzLow[dim], fromVol[dim]) );
00421     toVOI[dim] = std::max( fromVol[dim], std::min( xyzUp[dim], toVol[dim]) ); 
00422     }
00423 }
00424 
00425 void
00426 SplineWarpXform::RegisterVolumeAxis 
00427 ( const DataGrid::IndexType::ValueType dim, const Types::Coordinate delta, const Types::Coordinate origin, const int cpgDim, const Types::Coordinate invCpgSpacing,
00428   std::vector<int>& g, std::vector<Types::Coordinate>& spline, std::vector<Types::Coordinate>& dspline )
00429 {
00430   g.resize( dim+1 );
00431   spline.resize( 4*dim );
00432   dspline.resize( 4*dim );
00433 
00434   for ( int idx=0; idx<dim; ++idx ) 
00435     {
00436     const Types::Coordinate r = invCpgSpacing * (origin + delta * idx);
00437     g[idx] = std::min( static_cast<int>( r ), cpgDim-4 );
00438     const Types::Coordinate f = r - g[idx];
00439     for ( int k = 0; k < 4; ++k ) 
00440       {
00441       spline[4*idx+k] = CubicSpline::ApproxSpline( k, f );
00442       dspline[4*idx+k] = CubicSpline::DerivApproxSpline( k, f );
00443       }
00444     }
00445   // guard element
00446   g[dim] = -1;
00447 }
00448 
00449 void
00450 SplineWarpXform::RegisterVolumePoints
00451 ( const DataGrid::IndexType& volDims, const Self::SpaceVectorType& delta, const Self::SpaceVectorType& origin )
00452 {
00453   this->RegisterVolumeAxis( volDims[0], delta[0], origin[0], this->m_Dims[0], this->InverseSpacing[0], gX, splineX, dsplineX );
00454   this->RegisterVolumeAxis( volDims[1], delta[1], origin[1], this->m_Dims[1], this->InverseSpacing[1], gY, splineY, dsplineY );
00455   this->RegisterVolumeAxis( volDims[2], delta[2], origin[2], this->m_Dims[2], this->InverseSpacing[2], gZ, splineZ, dsplineZ );
00456 
00457   for ( int idx = 0; idx<volDims[0]; ++idx ) gX[idx] *= nextI;
00458   for ( int idx = 0; idx<volDims[1]; ++idx ) gY[idx] *= nextJ;
00459   for ( int idx = 0; idx<volDims[2]; ++idx ) gZ[idx] *= nextK;
00460 
00461   this->VolumeDims = volDims;
00462 }
00463 
00464 void SplineWarpXform::UnRegisterVolume()
00465 {
00466   gX.resize( 0 );
00467   gY.resize( 0 );
00468   gZ.resize( 0 );
00469 
00470   splineX.resize( 0 );
00471   splineY.resize( 0 );
00472   splineZ.resize( 0 );
00473 
00474   dsplineX.resize( 0 );
00475   dsplineY.resize( 0 );
00476   dsplineZ.resize( 0 );
00477 }
00478 
00479 SplineWarpXform::SpaceVectorType& 
00480 SplineWarpXform::GetDeformedControlPointPosition
00481 ( Self::SpaceVectorType& v, const int x, const int y, const int z) 
00482   const 
00483 {
00484   // Create a pointer to the front-lower-left corner of the c.p.g. cell.
00485   const Types::Coordinate* coeff = m_Parameters + 3 * ( (x-1) + this->m_Dims[0] * ((y-1) + this->m_Dims[1] * (z-1)) );  
00486   static const Types::Coordinate spline[3] = { 1.0/6, 4.0/6, 1.0/6 };
00487 
00488   for ( int dim = 0; dim<3; ++dim ) 
00489     {
00490     Types::Coordinate mm = 0;
00491     const Types::Coordinate *coeff_mm = coeff;
00492     
00493     for ( int m = 0; m < 3; ++m ) 
00494       {
00495       Types::Coordinate ll = 0;
00496       const Types::Coordinate *coeff_ll = coeff_mm;
00497       
00498       // Loop over 4 c.p.g. planes in y-direction.
00499       for ( int l = 0; l < 3; ++l ) 
00500         {
00501         Types::Coordinate kk = 0;
00502         const Types::Coordinate *coeff_kk = coeff_ll;
00503         
00504         // Loop over 4 c.p.g. planes in x-direction.
00505         for ( int k = 0; k < 3; ++k, coeff_kk+=3 ) 
00506           {
00507           kk += spline[k] * (*coeff_kk);
00508           }
00509         ll += spline[l] * kk;
00510         coeff_ll += nextJ;
00511         }       
00512       mm += spline[m] * ll;
00513       coeff_mm += nextK;
00514       }
00515     v[dim] = mm;
00516     ++coeff;
00517     }
00518   
00519   return v;
00520 }
00521 
00522 void
00523 SplineWarpXform
00524 ::GetTransformedGrid 
00525 ( Self::SpaceVectorType& v, const int idxX, const int idxY, const int idxZ ) const
00526 {
00527   const Types::Coordinate* coeff = this->m_Parameters + gX[idxX] + gY[idxY] + gZ[idxZ];
00528   const Types::Coordinate *spX = &splineX[idxX<<2], *spY = &splineY[idxY<<2], *spZ = &splineZ[idxZ<<2];
00529   
00530   for ( int dim = 0; dim<3; ++dim ) 
00531     {
00532     Types::Coordinate mm = 0;
00533     const Types::Coordinate *coeff_mm = coeff;
00534       for ( int m = 0; m < 4; ++m )
00535         {
00536         Types::Coordinate ll = 0;
00537         const Types::Coordinate *coeff_ll = coeff_mm;
00538         for ( int l = 0; l < 4; ++l ) 
00539           {
00540           Types::Coordinate kk = 0;
00541           const Types::Coordinate *coeff_kk = coeff_ll;
00542           for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) 
00543             {
00544             kk += spX[k] * (*coeff_kk);
00545             }
00546           ll += spY[l] * kk;
00547           coeff_ll += nextJ;
00548           }     
00549         mm += spZ[m] * ll;
00550         coeff_mm += nextK;
00551         }
00552       v[ dim ] = mm;
00553       ++coeff;
00554     }
00555 }
00556 
00557 void 
00558 SplineWarpXform::GetTransformedGridRow
00559 ( const int numPoints, Self::SpaceVectorType *const vIn, const int idxX, const int idxY, const int idxZ ) 
00560   const
00561 {
00562   Self::SpaceVectorType *v = vIn;
00563   const Types::Coordinate* coeff = m_Parameters + gX[idxX] + gY[idxY] + gZ[idxZ];
00564   const Types::Coordinate *spX = &splineX[idxX<<2], *spY = &splineY[idxY<<2], *spZ = &splineZ[idxZ<<2];
00565   
00566   // precompute the products of B_j(v) and B_k(w) for the 4 x 4 neighborhood
00567   // in y- and z-direction.
00568   Types::Coordinate sml[16], *psml = sml;
00569   for ( int m = 0; m < 4; ++m )
00570     {
00571     for ( int l = 0; l < 4; ++l, ++psml )
00572       {
00573       *psml = spZ[m] * spY[l];
00574       }
00575     }
00576   
00577   // determine the number of CPG cells on our way along the row
00578   const int numberOfCells = (gX[idxX + numPoints - 1] - gX[idxX]) / nextI + 4;
00579   
00580   // pre-compute the contributions of all control points in y- and z-direction
00581   // along the way
00582   Types::Coordinate phiComp;
00583 #ifdef CMTK_VAR_AUTO_ARRAYSIZE
00584   Types::Coordinate phiHat[3*numberOfCells]; // GNU compiler can have variable-sized automatic arrays
00585 #else
00586   std::vector<Types::Coordinate> phiHat( 3*numberOfCells );
00587 #endif
00588 
00589   const int *gpo;
00590   int phiIdx = 0;
00591   for ( int cell = 0; cell < numberOfCells; ++cell, coeff += nextI ) 
00592     {
00593     gpo = &this->GridPointOffset[0];
00594     for ( int dim = 0; dim < 3; ++dim, ++phiIdx ) 
00595       {
00596       phiComp = coeff[ *gpo ] * sml[0];
00597       ++gpo;
00598       for ( int ml = 1; ml < 16; ++ml, ++gpo ) 
00599         {
00600         phiComp += coeff[ *gpo ] * sml[ml];
00601         }
00602       phiHat[phiIdx] = phiComp;
00603       }
00604     }
00605   
00606   // start at the leftmost precomputed CPG cell
00607   int cellIdx = 0;
00608 
00609   // run over all points we're supposed to transform
00610   int i = idxX;
00611   for ( const int lastPoint = idxX + numPoints; i < lastPoint; ) 
00612     {
00613     // these change only when we enter a new cell
00614     const Types::Coordinate* phiPtr = &phiHat[3*cellIdx];
00615     
00616     // do everything inside one cell
00617     do 
00618       {
00619       Self::SpaceVectorType& vRef = *v;
00620       // compute transformed voxel by taking precomputed y- and z-contributions
00621       // and adding x. The loops to do this have been unrolled for increased
00622       // performance.
00623       vRef[0] = spX[0] * phiPtr[0] + spX[1] * phiPtr[3] + spX[2] * phiPtr[6] + spX[3] * phiPtr[9];
00624       vRef[1] = spX[0] * phiPtr[1] + spX[1] * phiPtr[4] + spX[2] * phiPtr[7] + spX[3] * phiPtr[10];
00625       vRef[2] = spX[0] * phiPtr[2] + spX[1] * phiPtr[5] + spX[2] * phiPtr[8] + spX[3] * phiPtr[11];
00626       
00627       // go to next voxel
00628       ++i;
00629       spX += 4;
00630       ++v;
00631       // repeat this until we leave current CPG cell.
00632       } 
00633     while ( ( gX[i-1] == gX[i] ) && ( i < lastPoint ) );
00634     
00635     // we've just left a cell -- shift index of precomputed control points
00636     // to the next one.
00637     ++cellIdx;
00638     }
00639 }
00640 
00641 Types::Coordinate
00642 SplineWarpXform
00643 ::GetGridEnergy() const
00644 {
00645   double energy = 0;
00646 
00647 #pragma omp parallel for reduction(+:energy)
00648   for ( int z = 1; z<this->m_Dims[2]-1; ++z )
00649     {
00650     for ( int y = 1; y<this->m_Dims[1]-1; ++y )
00651       {
00652       for ( int x = 1; x<this->m_Dims[0]-1; ++x ) 
00653         {
00654         const Types::Coordinate* coeff = this->m_Parameters + x * nextI + y * nextJ + z * nextK;
00655         energy += this->GetGridEnergy( coeff );
00656         }
00657       }
00658     }
00659   
00660   return energy / (( this->m_Dims[0] - 2 ) * ( this->m_Dims[1] - 2 ) * ( this->m_Dims[2] - 2 ));
00661 }
00662 
00663 Types::Coordinate
00664 SplineWarpXform
00665 ::GetGridEnergy( const Self::SpaceVectorType& v ) const
00666 {
00667   Types::Coordinate r[3], f[3];
00668   int grid[3];
00669   
00670   for ( int dim = 0; dim<3; ++dim ) 
00671     {
00672     r[dim] = InverseSpacing[dim] * v[dim];
00673     grid[dim] = std::min( static_cast<int>( r[dim] ), this->m_Dims[dim]-4 );
00674     f[dim] = std::max<Types::Coordinate>( 0, std::min<Types::Coordinate>( 1.0, r[dim] - grid[dim] ) );
00675     }
00676   
00677   const Types::Coordinate* coeff = this->m_Parameters + 3 * ( grid[0] + this->m_Dims[0] * (grid[1] + this->m_Dims[1] * grid[2]) );
00678 
00679   // Matrix of one-variable second-order derivatives.
00680   double J[3][3];
00681   memset( J, 0, sizeof( J ) );
00682 
00683   // Matrix of mixed second-order derivatives.
00684   double K[3][3];
00685   memset( K, 0, sizeof( K ) );
00686 
00687   for ( int dim = 0; dim<3; ++dim ) 
00688     {
00689     const Types::Coordinate *coeff_mm = coeff;
00690     for ( int m = 0; m < 3; ++m ) 
00691       {
00692       Types::Coordinate llJ[3] = { 0, 0, 0 };
00693       Types::Coordinate llK[3] = { 0, 0, 0 };
00694       const Types::Coordinate *coeff_ll = coeff_mm;
00695       for ( int l = 0; l < 3; ++l ) 
00696         {
00697         Types::Coordinate kkJ[3] = { 0, 0, 0 };
00698         Types::Coordinate kkK[3] = { 0, 0, 0 };
00699         const Types::Coordinate *coeff_kk = coeff_ll;
00700         for ( int k = 0; k < 3; ++k, coeff_kk += nextI ) 
00701           {
00702           const Types::Coordinate tmp = CubicSpline::ApproxSpline( k, f[0] ) * (*coeff_kk);
00703           kkJ[0] += CubicSpline::SecondDerivApproxSpline( k, f[0] ) * (*coeff_kk);
00704           kkJ[1] += tmp;
00705           kkJ[2] += tmp;
00706           
00707           const Types::Coordinate tmp2 = CubicSpline::DerivApproxSpline( k, f[0] ) * (*coeff_kk);
00708           kkK[0] += tmp2;
00709           kkK[1] += CubicSpline::ApproxSpline( k, f[0] ) * (*coeff_kk);
00710           kkK[2] += tmp2;
00711           }
00712 
00713         const Types::Coordinate tmp = CubicSpline::ApproxSpline( l, f[1] );
00714         llJ[0] += tmp * kkJ[0];
00715         llJ[1] += CubicSpline::SecondDerivApproxSpline( l, f[1] ) * kkJ[1];
00716         llJ[2] += tmp * kkJ[2];
00717         
00718         const Types::Coordinate tmp2 = CubicSpline::DerivApproxSpline( l, f[1] );
00719         llK[0] += tmp2 * kkK[0];
00720         llK[1] += CubicSpline::DerivApproxSpline( l, f[1] ) * kkK[1];
00721         llK[2] += tmp2 * kkK[2];
00722         coeff_ll += nextJ;
00723         }
00724 
00725       const Types::Coordinate tmp = CubicSpline::ApproxSpline( m, f[2] );
00726       J[0][dim] += tmp * llJ[0];
00727       J[1][dim] += CubicSpline::ApproxSpline( m, f[2] ) * llJ[1];
00728       J[2][dim] += tmp * llJ[2];
00729       
00730       const Types::Coordinate tmp2 = CubicSpline::DerivApproxSpline( m, f[2] );
00731       K[0][dim] += CubicSpline::ApproxSpline( m, f[2] ) * llK[0];
00732       K[1][dim] += tmp2 * llK[1];
00733       K[2][dim] += tmp2 * llK[2];
00734       coeff_mm += nextK;
00735       }
00736     ++coeff;
00737     }
00738   
00739   const double energy = 
00740     // single variable second-order derivatives
00741     MathUtil::Square( InverseSpacing[0] ) *
00742     ( J[0][0] * J[0][0] + J[0][1] * J[0][1] + J[0][2] * J[0][2] ) +
00743     MathUtil::Square( InverseSpacing[1] ) *
00744     ( J[1][0] * J[1][0] + J[1][1] * J[1][1] + J[1][2] * J[1][2] ) +
00745     MathUtil::Square( InverseSpacing[2] ) *
00746     ( J[2][0] * J[2][0] + J[2][1] * J[2][1] + J[2][2] * J[2][2] ) +
00747     // two-variable mixed derivatives
00748     2 * ( InverseSpacing[0] * InverseSpacing[1] *
00749           ( K[0][0] * K[0][0] + K[0][1] * K[0][1] + K[0][2] * K[0][2] ) +
00750           InverseSpacing[1] * InverseSpacing[2] *
00751           ( K[1][0] * K[1][0] + K[1][1] * K[1][1] + K[1][2] * K[1][2] ) +
00752           InverseSpacing[2] * InverseSpacing[0] *
00753           ( K[2][0] * K[2][0] + K[2][1] * K[2][1] + K[2][2] * K[2][2] )
00754           );
00755   
00756   return energy;
00757 }
00758 
00759 Types::Coordinate
00760 SplineWarpXform
00761 ::GetGridEnergy( const Types::Coordinate *cp ) const
00762 {
00763   const double   sp[3] = {  1.0/6, 2.0/3, 1.0/6 };
00764   const double  dsp[3] = { -1.0/2,     0, 1.0/2 };
00765   const double ddsp[3] = {      1,    -2,     1 };
00766 
00767   // Matrix of one-variable second-order derivatives.
00768   double J[3][3];
00769   memset( J, 0, sizeof( J ) );
00770 
00771   // Matrix of mixed second-order derivatives.
00772   double K[3][3];
00773   memset( K, 0, sizeof( K ) );
00774 
00775   const Types::Coordinate* coeff = cp - nextI - nextJ - nextK;
00776   for ( int dim = 0; dim<3; ++dim ) 
00777     {
00778     const Types::Coordinate *coeff_mm = coeff;
00779     for ( int m = 0; m < 3; ++m ) 
00780       {
00781       Types::Coordinate llJ[3] = { 0, 0, 0 };
00782       Types::Coordinate llK[3] = { 0, 0, 0 };
00783       const Types::Coordinate *coeff_ll = coeff_mm;
00784       for ( int l = 0; l < 3; ++l ) 
00785         {
00786         Types::Coordinate kkJ[3] = { 0, 0, 0 };
00787         Types::Coordinate kkK[3] = { 0, 0, 0 };
00788         const Types::Coordinate *coeff_kk = coeff_ll;
00789         for ( int k = 0; k < 3; ++k, coeff_kk += nextI ) 
00790           {
00791           const Types::Coordinate tmp = sp[k] * (*coeff_kk);
00792           kkJ[0] += ddsp[k] * (*coeff_kk);
00793           kkJ[1] += tmp;
00794           kkJ[2] += tmp;
00795           
00796           const Types::Coordinate tmp2 = dsp[k] * (*coeff_kk);
00797           kkK[0] += tmp2;
00798           kkK[1] +=  sp[k] * (*coeff_kk);
00799           kkK[2] += tmp2;
00800           }
00801         llJ[0] +=   sp[l] * kkJ[0];
00802         llJ[1] += ddsp[l] * kkJ[1];
00803         llJ[2] +=   sp[l] * kkJ[2];
00804         
00805         llK[0] +=  dsp[l] * kkK[0];
00806         llK[1] +=  dsp[l] * kkK[1];
00807         llK[2] +=   sp[l] * kkK[2];
00808         coeff_ll += nextJ;
00809         }       
00810       J[0][dim] +=   sp[m] * llJ[0];
00811       J[1][dim] +=   sp[m] * llJ[1];
00812       J[2][dim] += ddsp[m] * llJ[2];
00813       
00814       K[0][dim] +=   sp[m] * llK[0];
00815       K[1][dim] +=  dsp[m] * llK[1];
00816       K[2][dim] +=  dsp[m] * llK[2];
00817       coeff_mm += nextK;
00818       }
00819     ++coeff;
00820     }
00821   
00822   const double energy = 
00823     // single variable second-order derivatives
00824     MathUtil::Square( InverseSpacing[0] ) *
00825     ( J[0][0] * J[0][0] + J[0][1] * J[0][1] + J[0][2] * J[0][2] ) +
00826     MathUtil::Square( InverseSpacing[1] ) *
00827     ( J[1][0] * J[1][0] + J[1][1] * J[1][1] + J[1][2] * J[1][2] ) +
00828     MathUtil::Square( InverseSpacing[2] ) *
00829     ( J[2][0] * J[2][0] + J[2][1] * J[2][1] + J[2][2] * J[2][2] ) +
00830     // two-variable mixed derivatives
00831     2 * ( InverseSpacing[0] * InverseSpacing[1] *
00832           ( K[0][0] * K[0][0] + K[0][1] * K[0][1] + K[0][2] * K[0][2] ) +
00833           InverseSpacing[1] * InverseSpacing[2] *
00834           ( K[1][0] * K[1][0] + K[1][1] * K[1][1] + K[1][2] * K[1][2] ) +
00835           InverseSpacing[2] * InverseSpacing[0] *
00836           ( K[2][0] * K[2][0] + K[2][1] * K[2][1] + K[2][2] * K[2][2] )
00837           );
00838   
00839   return energy;
00840 }
00841 
00842 void 
00843 SplineWarpXform::GetGridEnergyDerivative
00844 ( double& lower, double& upper, const int param, const Types::Coordinate step )
00845   const
00846 {
00847   const int controlPointIdx = param / nextI;
00848   const unsigned short x =  ( controlPointIdx %  this->m_Dims[0] );
00849   const unsigned short y = ( (controlPointIdx /  this->m_Dims[0]) % this->m_Dims[1] );
00850   const unsigned short z = ( (controlPointIdx /  this->m_Dims[0]) / this->m_Dims[1] );
00851   
00852   const int thisDim = param % nextI;
00853   const Types::Coordinate* coeff = m_Parameters + param - thisDim;
00854   
00855   double ground = 0;
00856 
00857   const int iFrom = std::max<int>( -1, 1-x );
00858   const int jFrom = std::max<int>( -1, 1-y );
00859   const int kFrom = std::max<int>( -1, 1-z );
00860 
00861   const int iTo = std::min<int>( 1, this->m_Dims[0]-2-x );
00862   const int jTo = std::min<int>( 1, this->m_Dims[1]-2-y );
00863   const int kTo = std::min<int>( 1, this->m_Dims[2]-2-z );
00864 
00865   for ( int k = kFrom; k < kTo; ++k )
00866     for ( int j = jFrom; j < jTo; ++j )
00867       for ( int i = iFrom; i < iTo; ++i )
00868         {
00869         ground += this->GetGridEnergy( coeff + i*nextI + j*nextJ + k*nextK );
00870         }
00871 
00872   upper = -ground;
00873   lower = -ground;
00874   
00875   const Types::Coordinate oldCoeff = m_Parameters[param];
00876   m_Parameters[param] += step;
00877   for ( int k = kFrom; k < kTo; ++k )
00878     for ( int j = jFrom; j < jTo; ++j )
00879       for ( int i = iFrom; i < iTo; ++i )
00880         upper += this->GetGridEnergy( coeff + i*nextI + j*nextJ + k*nextK );
00881 
00882   m_Parameters[param] = oldCoeff - step;
00883   for ( int k = kFrom; k < kTo; ++k )
00884     for ( int j = jFrom; j < jTo; ++j )
00885       for ( int i = iFrom; i < iTo; ++i )
00886         lower += this->GetGridEnergy( coeff + i*nextI + j*nextJ + k*nextK );
00887 
00888   m_Parameters[param] = oldCoeff;
00889 
00890   upper /= NumberOfControlPoints;
00891   lower /= NumberOfControlPoints;
00892 }
00893 
00894 Types::Coordinate
00895 SplineWarpXform::GetInverseConsistencyError
00896 ( const WarpXform* inverse, const UniformVolume* volume,
00897   const DataGrid::RegionType* voi )
00898   const 
00899 {
00900   Self::SpaceVectorType v, vv;
00901   Types::Coordinate result = 0.0;
00902   int count = 0;
00903 
00904   DataGrid::RegionType myVoi;
00905   const DataGrid::RegionType *pVoi = &myVoi;
00906   if ( voi ) 
00907     {
00908     pVoi = voi;
00909     } 
00910   else
00911     {
00912     myVoi = volume->GetWholeImageRegion();
00913     }
00914 
00915   const int dX = 1 + static_cast<int>( this->Spacing[0] / 2 * volume->m_Delta[AXIS_X] );
00916   const int dY = 1 + static_cast<int>( this->Spacing[1] / 2 * volume->m_Delta[AXIS_Y] );
00917   const int dZ = 1 + static_cast<int>( this->Spacing[2] / 2 * volume->m_Delta[AXIS_Z] );
00918 
00919   const int startX = pVoi->From()[0] - (pVoi->From()[0] % dX);
00920   const int startY = pVoi->From()[1] - (pVoi->From()[1] % dY);
00921   const int startZ = pVoi->From()[2] - (pVoi->From()[2] % dZ);
00922 
00923   const size_t length = pVoi->To()[0] - startX;
00924 #ifdef CMTK_VAR_AUTO_ARRAYSIZE
00925   Self::SpaceVectorType vecArray[length];
00926 #else
00927   std::vector<Self::SpaceVectorType> vecArray( length );
00928 #endif
00929 
00930   for ( int z = startZ; z < pVoi->To()[2]; z += dZ ) 
00931     {
00932     for ( int y = startY; y < pVoi->To()[1]; y += dY ) 
00933       {
00934       Self::SpaceVectorType* pVec = &vecArray[0];
00935       this->GetTransformedGridRow( length, pVec, startX, y, z );
00936 
00937       for ( int x = startX; x < pVoi->To()[0]; x += dX, pVec += dX ) 
00938         {
00939         if ( inverse->InDomain( *pVec ) ) 
00940           {
00941           inverse->ApplyInPlace( *pVec );
00942           v = volume->GetGridLocation( x, y, z );
00943           v -= *pVec;
00944           result += v.RootSumOfSquares();
00945           ++count;
00946           }
00947         }
00948       }
00949     }
00950   
00951   return count ? result / count : 0.0;
00952 }
00953 
00954 Types::Coordinate* 
00955 SplineWarpXform::GetPureDeformation( const bool includeScale ) const
00956 {
00957   const size_t numberOfParameters = this->m_NumberOfParameters;
00958   Types::Coordinate* points = Memory::AllocateArray<Types::Coordinate>(  numberOfParameters  );
00959   memcpy( points, this->m_Parameters, sizeof( *points ) * numberOfParameters );
00960   
00961   AffineXform::SmartPtr xform( this->GetInitialAffineXform()->MakeInverse() );
00962 
00963   if ( includeScale ) 
00964     {
00965     xform->SetScales( 1.0, 1.0, 1.0 );
00966     }
00967 
00968   Types::Coordinate* ptr = points;
00969   Self::SpaceVectorType u;
00970   for ( size_t pointIdx = 0; pointIdx < numberOfParameters / 3; ++pointIdx, ptr += 3 ) 
00971     {
00972     Self::SpaceVectorType v( ptr );
00973     
00974     // undo affine transformation component
00975     xform->ApplyInPlace( v );
00976     
00977     // copy the result into ouput array
00978     for ( unsigned int dim = 0; dim < 3; ++dim ) 
00979       ptr[dim] = v[dim];
00980     }
00981   
00982   return points;
00983 }
00984 
00985 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines