cmtkSplineWarpXform_Jacobian.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: 2459 $
00026 //
00027 //  $LastChangedDate: 2010-10-19 07:53:52 -0700 (Tue, 19 Oct 2010) $
00028 //
00029 //  $LastChangedBy: torsten_at_home $
00030 //
00031 */
00032 
00033 #include "cmtkSplineWarpXform.h"
00034 
00035 #include <Base/cmtkMathUtil.h>
00036 #include <System/cmtkThreadPool.h>
00037 
00038 #include <Base/cmtkRegionIndexIterator.h>
00039 
00040 #include <vector>
00041 
00042 namespace
00043 cmtk
00044 {
00045 
00048 
00049 CoordinateMatrix3x3
00050 SplineWarpXform::GetJacobian( const Self::SpaceVectorType& v ) const
00051 {
00052   CoordinateMatrix3x3 J;
00053   this->GetJacobian( v, J );
00054   return J;
00055 }
00056 
00057 void
00058 SplineWarpXform::GetJacobianRow
00059 ( CoordinateMatrix3x3 *const array, const int x, const int y, const int z, const size_t numberOfPoints ) 
00060   const
00061 {
00062   const Types::Coordinate *spX = &splineX[x<<2], *spY = &splineY[y<<2], *spZ = &splineZ[z<<2];
00063   const Types::Coordinate *dspX = &dsplineX[x<<2], *dspY = &dsplineY[y<<2], *dspZ = &dsplineZ[z<<2];
00064   const Types::Coordinate *coeff = this->m_Parameters + gX[x] + gY[y] + gZ[z];
00065 
00066   // precompute the products of B_j(v) and B_k(w) for the 4 x 4 neighborhood
00067   // in y- and z-direction.
00068   Types::Coordinate smlX[16], smlY[16], smlZ[16];
00069   for ( int i = 0, m = 0; m < 4; ++m )
00070     {
00071     for ( int l = 0; l < 4; ++l, ++i )
00072       {
00073       smlX[i] =  spZ[m] *  spY[l];
00074       smlY[i] =  spZ[m] * dspY[l];
00075       smlZ[i] = dspZ[m] *  spY[l];
00076       }
00077     }
00078   
00079   // determine the number of CPG cells on our way along the row
00080   const int numberOfCells = (gX[x + numberOfPoints - 1] - gX[x]) / nextI + 4;
00081   
00082   // pre-compute the contributions of all control points in y- and z-direction
00083   // along the way
00084   Types::Coordinate phiCompX, phiCompY, phiCompZ;
00085 
00086 #ifdef CMTK_VAR_AUTO_ARRAYSIZE
00087   Types::Coordinate phiHatX[3*numberOfCells];
00088   Types::Coordinate phiHatY[3*numberOfCells];
00089   Types::Coordinate phiHatZ[3*numberOfCells];
00090 #else
00091   std::vector<Types::Coordinate> phiHatX(3*numberOfCells);
00092   std::vector<Types::Coordinate> phiHatY(3*numberOfCells);
00093   std::vector<Types::Coordinate> phiHatZ(3*numberOfCells);
00094 #endif
00095 
00096   const int *gpo;
00097   int phiIdx = 0;
00098   for ( int cell = 0; cell < numberOfCells; ++cell, coeff += nextI ) 
00099     {
00100     gpo = &GridPointOffset[0];
00101     for ( int dim = 0; dim < 3; ++dim, ++phiIdx ) 
00102       {
00103       phiCompX = phiCompY = phiCompZ = 0;
00104       for ( int ml = 0; ml < 16; ++ml, ++gpo ) 
00105         {
00106         phiCompX += coeff[ *gpo ] * smlX[ml];
00107         phiCompY += coeff[ *gpo ] * smlY[ml];
00108         phiCompZ += coeff[ *gpo ] * smlZ[ml];
00109         }
00110       phiHatX[phiIdx] = phiCompX;
00111       phiHatY[phiIdx] = phiCompY;
00112       phiHatZ[phiIdx] = phiCompZ;
00113       }
00114     }
00115   
00116   // start at the leftmost precomputed CPG cell
00117   int cellIdx = 0;
00118 
00119   CoordinateMatrix3x3 J;
00120   // run over all points we're supposed to transform
00121   int i = x;
00122   for ( const int lastPoint = x + numberOfPoints; i < lastPoint; ) 
00123     {
00124     // these change only when we enter a new cell
00125 #ifdef CMTK_VAR_AUTO_ARRAYSIZE
00126     const Types::Coordinate* phiPtrX = phiHatX + 3*cellIdx;
00127     const Types::Coordinate* phiPtrY = phiHatY + 3*cellIdx;
00128     const Types::Coordinate* phiPtrZ = phiHatZ + 3*cellIdx;
00129 #else
00130     const Types::Coordinate* phiPtrX = &phiHatX[3*cellIdx];
00131     const Types::Coordinate* phiPtrY = &phiHatY[3*cellIdx];
00132     const Types::Coordinate* phiPtrZ = &phiHatZ[3*cellIdx];
00133 #endif
00134 
00135     // do everything inside one cell
00136     do 
00137       {
00138       // compute transformed voxel by taking precomputed y- and z-contributions
00139       // and adding x. The loops to do this have been unrolled for increased
00140       // performance.
00141       J[0][0] = this->InverseSpacing[0] * ( dspX[0] * phiPtrX[0] + dspX[1] * phiPtrX[3] + dspX[2] * phiPtrX[6] + dspX[3] * phiPtrX[9] );
00142       J[0][1] = this->InverseSpacing[0] * ( dspX[0] * phiPtrX[1] + dspX[1] * phiPtrX[4] + dspX[2] * phiPtrX[7] + dspX[3] * phiPtrX[10] );
00143       J[0][2] = this->InverseSpacing[0] * ( dspX[0] * phiPtrX[2] + dspX[1] * phiPtrX[5] + dspX[2] * phiPtrX[8] + dspX[3] * phiPtrX[11] );
00144 
00145       J[1][0] = this->InverseSpacing[1] * ( spX[0] * phiPtrY[0] + spX[1] * phiPtrY[3] + spX[2] * phiPtrY[6] + spX[3] * phiPtrY[9] );
00146       J[1][1] = this->InverseSpacing[1] * ( spX[0] * phiPtrY[1] + spX[1] * phiPtrY[4] + spX[2] * phiPtrY[7] + spX[3] * phiPtrY[10] );
00147       J[1][2] = this->InverseSpacing[1] * ( spX[0] * phiPtrY[2] + spX[1] * phiPtrY[5] + spX[2] * phiPtrY[8] + spX[3] * phiPtrY[11] );
00148 
00149       J[2][0] = this->InverseSpacing[2] * ( spX[0] * phiPtrZ[0] + spX[1] * phiPtrZ[3] + spX[2] * phiPtrZ[6] + spX[3] * phiPtrZ[9] );
00150       J[2][1] = this->InverseSpacing[2] * ( spX[0] * phiPtrZ[1] + spX[1] * phiPtrZ[4] + spX[2] * phiPtrZ[7] + spX[3] * phiPtrZ[10] );
00151       J[2][2] = this->InverseSpacing[2] * ( spX[0] * phiPtrZ[2] + spX[1] * phiPtrZ[5] + spX[2] * phiPtrZ[8] + spX[3] * phiPtrZ[11] );
00152 
00153       array[i-x].Set( &J[0][0] );
00154 
00155       // go to next voxel
00156       ++i;
00157       spX += 4;
00158       dspX += 4;
00159       // repeat this until we leave current CPG cell.
00160     } while ( ( gX[i-1] == gX[i] ) && ( i < lastPoint ) );
00161 
00162     // we've just left a cell -- shift index of precomputed control points
00163     // to the next one.
00164     ++cellIdx;
00165   }
00166 }
00167 
00168 void
00169 SplineWarpXform::GetJacobianAtControlPoint
00170 ( const Types::Coordinate* cp, CoordinateMatrix3x3& J ) const
00171 {
00172   J.Fill( 0.0 );
00173   
00174   const double  sp[3] = {  1.0/6, 2.0/3, 1.0/6 };
00175   const double dsp[3] = { -1.0/2,     0, 1.0/2 };
00176 
00177   const Types::Coordinate* coeff = cp - nextI - nextJ - nextK;
00178   for ( int dim = 0; dim<3; ++dim ) {
00179     const Types::Coordinate *coeff_mm = coeff;
00180     for ( int m = 0; m < 3; ++m ) {
00181       Types::Coordinate ll[3] = { 0, 0, 0 };
00182       const Types::Coordinate *coeff_ll = coeff_mm;
00183       for ( int l = 0; l < 3; ++l ) {
00184         Types::Coordinate kk[3] = { 0, 0, 0 };
00185         const Types::Coordinate *coeff_kk = coeff_ll;
00186         for ( int k = 0; k < 3; ++k, coeff_kk += nextI ) {
00187           kk[0] += dsp[k] * (*coeff_kk);
00188           kk[1] +=  sp[k] * (*coeff_kk);
00189           kk[2] +=  sp[k] * (*coeff_kk);
00190         }
00191         ll[0] +=  sp[l] * kk[0];
00192         ll[1] += dsp[l] * kk[1];
00193         ll[2] +=  sp[l] * kk[2];
00194         coeff_ll += nextJ;
00195       } 
00196       J[0][dim] +=  sp[m] * ll[0];
00197       J[1][dim] +=  sp[m] * ll[1];
00198       J[2][dim] += dsp[m] * ll[2];
00199       coeff_mm += nextK;
00200     }
00201     ++coeff;
00202   }
00203   for ( int i = 0; i<3; ++i ) 
00204     {
00205     for ( int j = 0; j<3; ++j )
00206       J[i][j] *= this->InverseSpacing[i];
00207     }
00208 }
00209 
00210 void
00211 SplineWarpXform::GetJacobian
00212 ( const Self::SpaceVectorType& v, CoordinateMatrix3x3& J ) const
00213 {
00214   Types::Coordinate r[3], f[3];
00215   int grid[3];
00216   
00217   for ( int dim = 0; dim<3; ++dim ) 
00218     {
00219     r[dim] = this->InverseSpacing[dim] * v[dim];
00220     grid[dim] = std::min( static_cast<int>( r[dim] ), this->m_Dims[dim]-4 );
00221     f[dim] = std::max<Types::Coordinate>( 0, std::min<Types::Coordinate>( 1.0, r[dim] - grid[dim] ) );
00222     }
00223 
00224   const Types::Coordinate* coeff = this->m_Parameters + 3 * ( grid[0] + this->m_Dims[0] * (grid[1] + this->m_Dims[1] * grid[2]) );
00225   
00226   // loop over the three components of the coordinate transformation function,
00227   // x, y, z.
00228   for ( int dim = 0; dim<3; ++dim, ++coeff ) 
00229     {
00230     const Types::Coordinate *coeff_mm = coeff;
00231     for ( int m = 0; m < 4; ++m, coeff_mm += nextK ) 
00232       {
00233       Types::Coordinate ll[3] = { 0, 0, 0 };
00234       const Types::Coordinate *coeff_ll = coeff_mm;
00235       for ( int l = 0; l < 4; ++l, coeff_ll += nextJ ) 
00236         {
00237         Types::Coordinate kk[3] = { 0, 0, 0 };
00238         const Types::Coordinate *coeff_kk = coeff_ll;
00239         
00240         for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) 
00241           {
00242           // dT / dx
00243           kk[0] += CubicSpline::DerivApproxSpline( k, f[0] ) * (*coeff_kk);
00244           // dT / dy
00245           const Types::Coordinate tmp = CubicSpline::ApproxSpline( k, f[0] ) * (*coeff_kk);
00246           kk[1] += tmp;
00247           // dT / dz
00248           kk[2] += tmp;
00249           }
00250         
00251         // dT / dx
00252         const Types::Coordinate tmp = CubicSpline::ApproxSpline( l, f[1] );
00253         ll[0] += tmp * kk[0];
00254         // dT / dy
00255         ll[1] += CubicSpline::DerivApproxSpline( l, f[1] ) * kk[1];
00256         // dT / dz
00257         ll[2] += tmp * kk[2];
00258         }
00259 
00260       // dT / dx
00261       const Types::Coordinate tmp = CubicSpline::ApproxSpline( m, f[2] );
00262       J[dim][0] += tmp * ll[0];
00263       // dT / dy
00264       J[dim][1] += tmp * ll[1];
00265       // dT / dz
00266       J[dim][2] += CubicSpline::DerivApproxSpline( m, f[2] ) * ll[2];
00267       }
00268     }
00269 
00270   // scale with grid spacing to normalize Jacobian (chain rule of derivation)
00271   for ( int i = 0; i<3; ++i ) 
00272     {
00273     for ( int j = 0; j<3; ++j )
00274       J[i][j] *= this->InverseSpacing[i];
00275     }
00276 }
00277 
00278 Types::Coordinate
00279 SplineWarpXform::GetJacobianDeterminant
00280 ( const int x, const int y, const int z ) const
00281 {
00282   const Types::Coordinate *spX = &splineX[x<<2], *spY = &splineY[y<<2], *spZ = &splineZ[z<<2];
00283   const Types::Coordinate *dspX = &dsplineX[x<<2], *dspY = &dsplineY[y<<2], *dspZ = &dsplineZ[z<<2];
00284   const Types::Coordinate *coeff = this->m_Parameters + gX[x] + gY[y] + gZ[z];
00285   
00286   double J[3][3];
00287   memset( J, 0, sizeof( J ) );
00288   for ( int dim = 0; dim<3; ++dim ) 
00289     {
00290     const Types::Coordinate *coeff_mm = coeff;
00291     for ( int m = 0; m < 4; ++m ) 
00292       {
00293       Types::Coordinate ll[3] = { 0, 0, 0 };
00294       const Types::Coordinate *coeff_ll = coeff_mm;
00295       for ( int l = 0; l < 4; ++l ) 
00296         {
00297         Types::Coordinate kk[3] = { 0, 0, 0 };
00298         const Types::Coordinate *coeff_kk = coeff_ll;
00299         for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) 
00300           {
00301           kk[0] += dspX[k] * (*coeff_kk);
00302           kk[1] += spX[k];
00303           kk[2] += spX[k];
00304           }
00305         ll[0] += spY[l] * kk[0];
00306         ll[1] += dspY[l] * kk[1];
00307         ll[2] += spY[l] * kk[2];
00308         coeff_ll += nextJ;
00309         }       
00310       J[0][dim] += spZ[m] * ll[0];
00311       J[1][dim] += spZ[m] * ll[1];
00312       J[2][dim] += dspZ[m] * ll[2];
00313       coeff_mm += nextK;
00314       }
00315     ++coeff;
00316     }
00317   
00318   return InverseSpacing[0] * InverseSpacing[1] * InverseSpacing[2] * 
00319     ( J[0][0] * (J[1][1]*J[2][2] - J[1][2]*J[2][1]) + 
00320       J[0][1] * (J[1][2]*J[2][0] - J[1][0]*J[2][2]) + 
00321       J[0][2] * (J[1][0]*J[2][1] - J[1][1]*J[2][0]) );
00322 }
00323 
00324 Types::Coordinate
00325 SplineWarpXform::GetJacobianDeterminant
00326 ( const Self::SpaceVectorType& v ) const
00327 {
00328   Types::Coordinate r[3], f[3];
00329   int grid[3];
00330   
00331   double J[3][3];
00332   memset( J, 0, sizeof( J ) );
00333 
00334   for ( int dim = 0; dim<3; ++dim ) 
00335     {
00336     r[dim] = InverseSpacing[dim] * v[dim];
00337     grid[dim] = std::min( static_cast<int>( r[dim] ), this->m_Dims[dim]-4 );
00338     f[dim] = std::max<Types::Coordinate>( 0, std::min<Types::Coordinate>( 1.0, r[dim] - grid[dim] ) );
00339     }
00340   
00341   const Types::Coordinate* coeff = this->m_Parameters + 3 * ( grid[0] + this->m_Dims[0] * (grid[1] + this->m_Dims[1] * grid[2]) );
00342   
00343   for ( int dim = 0; dim<3; ++dim ) 
00344     {
00345     const Types::Coordinate *coeff_mm = coeff;
00346     for ( int m = 0; m < 4; ++m ) 
00347       {
00348       Types::Coordinate ll[3] = { 0, 0, 0 };
00349       const Types::Coordinate *coeff_ll = coeff_mm;
00350       for ( int l = 0; l < 4; ++l ) 
00351         {
00352         Types::Coordinate kk[3] = { 0, 0, 0 };
00353         const Types::Coordinate *coeff_kk = coeff_ll;
00354         for ( int k = 0; k < 4; ++k, coeff_kk+=3 ) 
00355           {
00356           kk[0] += CubicSpline::DerivApproxSpline( k, f[0] ) * (*coeff_kk);
00357           const Types::Coordinate tmp = CubicSpline::ApproxSpline( k, f[0] ) * (*coeff_kk);
00358           kk[1] += tmp;
00359           kk[2] += tmp;
00360           }
00361         const Types::Coordinate tmp = CubicSpline::ApproxSpline( l, f[1] );
00362         ll[0] += tmp * kk[0];
00363         ll[1] += CubicSpline::DerivApproxSpline( l, f[1] ) * kk[1];
00364         ll[2] += tmp * kk[2];
00365         coeff_ll += nextJ;
00366         }       
00367       const Types::Coordinate tmp = CubicSpline::ApproxSpline( m, f[2] );
00368       J[0][dim] += tmp * ll[0];
00369       J[1][dim] += tmp * ll[1];
00370       J[2][dim] += CubicSpline::DerivApproxSpline( m, f[2] ) * ll[2];
00371       coeff_mm += nextK;
00372       }
00373     ++coeff;
00374     }
00375   
00376   return InverseSpacing[0] * InverseSpacing[1] * InverseSpacing[2] * 
00377     ( J[0][0] * (J[1][1]*J[2][2] - J[1][2]*J[2][1]) + 
00378       J[0][1] * (J[1][2]*J[2][0] - J[1][0]*J[2][2]) + 
00379       J[0][2] * (J[1][0]*J[2][1] - J[1][1]*J[2][0]) );
00380 }
00381 
00382 void
00383 SplineWarpXform::GetJacobianDeterminantRow
00384 ( double *const values, const int x, const int y, const int z, 
00385   const size_t numberOfPoints ) 
00386   const
00387 {
00388   const Types::Coordinate *spX = &splineX[x<<2], *spY = &splineY[y<<2], *spZ = &splineZ[z<<2];
00389   const Types::Coordinate *dspX = &dsplineX[x<<2], *dspY = &dsplineY[y<<2], *dspZ = &dsplineZ[z<<2];
00390   const Types::Coordinate *coeff = this->m_Parameters + gX[x] + gY[y] + gZ[z];
00391 
00392   const Types::Coordinate globalInverseSpacing = InverseSpacing[0] * InverseSpacing[1] * InverseSpacing[2];
00393 
00394   // precompute the products of B_j(v) and B_k(w) for the 4 x 4 neighborhood
00395   // in y- and z-direction.
00396   Types::Coordinate smlX[16], smlY[16], smlZ[16];
00397   for ( int i = 0, m = 0; m < 4; ++m )
00398     {
00399     for ( int l = 0; l < 4; ++l, ++i )
00400       {
00401       smlX[i] =  spZ[m] *  spY[l];
00402       smlY[i] =  spZ[m] * dspY[l];
00403       smlZ[i] = dspZ[m] *  spY[l];
00404       }
00405     }
00406   
00407   // determine the number of CPG cells on our way along the row
00408   const int numberOfCells = (gX[x + numberOfPoints - 1] - gX[x]) / nextI + 4;
00409   
00410   // pre-compute the contributions of all control points in y- and z-direction
00411   // along the way
00412   Types::Coordinate phiCompX, phiCompY, phiCompZ;
00413 
00414 #ifdef CMTK_VAR_AUTO_ARRAYSIZE
00415   Types::Coordinate phiHatX[3*numberOfCells];
00416   Types::Coordinate phiHatY[3*numberOfCells];
00417   Types::Coordinate phiHatZ[3*numberOfCells];
00418 #else
00419   std::vector<Types::Coordinate> phiHatX(3*numberOfCells);
00420   std::vector<Types::Coordinate> phiHatY(3*numberOfCells);
00421   std::vector<Types::Coordinate> phiHatZ(3*numberOfCells);
00422 #endif
00423 
00424   const int *gpo;
00425   int phiIdx = 0;
00426   for ( int cell = 0; cell < numberOfCells; ++cell, coeff += nextI ) 
00427     {
00428     gpo = &GridPointOffset[0];
00429     for ( int dim = 0; dim < 3; ++dim, ++phiIdx ) 
00430       {
00431       phiCompX = phiCompY = phiCompZ = 0;
00432       for ( int ml = 0; ml < 16; ++ml, ++gpo ) 
00433         {
00434         phiCompX += coeff[ *gpo ] * smlX[ml];
00435         phiCompY += coeff[ *gpo ] * smlY[ml];
00436         phiCompZ += coeff[ *gpo ] * smlZ[ml];
00437         }
00438       phiHatX[phiIdx] = phiCompX;
00439       phiHatY[phiIdx] = phiCompY;
00440       phiHatZ[phiIdx] = phiCompZ;
00441       }
00442     }
00443   
00444   // start at the leftmost precomputed CPG cell
00445   int cellIdx = 0;
00446 
00447   Types::Coordinate JXX, JXY, JXZ, JYX, JYY, JYZ, JZX, JZY, JZZ;
00448   // run over all points we're supposed to transform
00449   int i = x;
00450   for ( const int lastPoint = x + numberOfPoints; i < lastPoint; ) 
00451     {
00452     // these change only when we enter a new cell
00453 #ifdef CMTK_VAR_AUTO_ARRAYSIZE
00454     const Types::Coordinate* phiPtrX = phiHatX + 3*cellIdx;
00455     const Types::Coordinate* phiPtrY = phiHatY + 3*cellIdx;
00456     const Types::Coordinate* phiPtrZ = phiHatZ + 3*cellIdx;
00457 #else
00458     const Types::Coordinate* phiPtrX = &phiHatX[3*cellIdx];
00459     const Types::Coordinate* phiPtrY = &phiHatY[3*cellIdx];
00460     const Types::Coordinate* phiPtrZ = &phiHatZ[3*cellIdx];
00461 #endif
00462 
00463     // do everything inside one cell
00464     do 
00465       {
00466       // compute transformed voxel by taking precomputed y- and z-contributions
00467       // and adding x. The loops to do this have been unrolled for increased
00468       // performance.
00469       JXX = dspX[0] * phiPtrX[0] + dspX[1] * phiPtrX[3] + dspX[2] * phiPtrX[6] + dspX[3] * phiPtrX[9];
00470       JXY = dspX[0] * phiPtrX[1] + dspX[1] * phiPtrX[4] + dspX[2] * phiPtrX[7] + dspX[3] * phiPtrX[10];
00471       JXZ = dspX[0] * phiPtrX[2] + dspX[1] * phiPtrX[5] + dspX[2] * phiPtrX[8] + dspX[3] * phiPtrX[11];
00472 
00473       JYX = spX[0] * phiPtrY[0] + spX[1] * phiPtrY[3] + spX[2] * phiPtrY[6] + spX[3] * phiPtrY[9];
00474       JYY = spX[0] * phiPtrY[1] + spX[1] * phiPtrY[4] + spX[2] * phiPtrY[7] + spX[3] * phiPtrY[10];
00475       JYZ = spX[0] * phiPtrY[2] + spX[1] * phiPtrY[5] + spX[2] * phiPtrY[8] + spX[3] * phiPtrY[11];
00476 
00477       JZX = spX[0] * phiPtrZ[0] + spX[1] * phiPtrZ[3] + spX[2] * phiPtrZ[6] + spX[3] * phiPtrZ[9];
00478       JZY = spX[0] * phiPtrZ[1] + spX[1] * phiPtrZ[4] + spX[2] * phiPtrZ[7] + spX[3] * phiPtrZ[10];
00479       JZZ = spX[0] * phiPtrZ[2] + spX[1] * phiPtrZ[5] + spX[2] * phiPtrZ[8] + spX[3] * phiPtrZ[11];
00480 
00481       values[i-x] = globalInverseSpacing * 
00482         ( JXX * (JYY*JZZ - JYZ*JZY) + JXY * (JYZ*JZX - JYX*JZZ) + JXZ * (JYX*JZY - JYY*JZX) );
00483 
00484       // go to next voxel
00485       ++i;
00486       spX += 4;
00487       dspX += 4;
00488       // repeat this until we leave current CPG cell.
00489     } while ( ( gX[i-1] == gX[i] ) && ( i < lastPoint ) );
00490 
00491     // we've just left a cell -- shift index of precomputed control points
00492     // to the next one.
00493     ++cellIdx;
00494   }
00495 }
00496 
00497 Types::Coordinate
00498 SplineWarpXform::JacobianDeterminant ( const Types::Coordinate *cp ) const
00499 {
00500   CoordinateMatrix3x3 J;
00501   this->GetJacobianAtControlPoint( cp, J );
00502   
00503   return InverseSpacing[0] * InverseSpacing[1] * InverseSpacing[2] * 
00504     ( J[0][0] * (J[1][1]*J[2][2] - J[1][2]*J[2][1]) + 
00505       J[0][1] * (J[1][2]*J[2][0] - J[1][0]*J[2][2]) + 
00506       J[0][2] * (J[1][0]*J[2][1] - J[1][1]*J[2][0]) );
00507 }
00508 
00509 void
00510 SplineWarpXform
00511 ::GetJacobianConstraintThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t )
00512 {
00513   Self::JacobianConstraintThreadInfo *info = static_cast<Self::JacobianConstraintThreadInfo*>( args );
00514 
00515   const SplineWarpXform *me = info->thisObject;
00516 
00517   const int pixelsPerRow = me->VolumeDims[0];
00518   std::vector<double> valuesJ( pixelsPerRow );
00519 
00520   const int rowCount = ( me->VolumeDims[1] * me->VolumeDims[2] );
00521   const int rowFrom = ( rowCount / taskCnt ) * taskIdx;
00522   const int rowTo = ( taskIdx == (taskCnt-1) ) ? rowCount : ( rowCount/taskCnt ) * (taskIdx+1);
00523   int rowsToDo = rowTo - rowFrom;
00524 
00525   int yFrom = rowFrom % me->VolumeDims[1];
00526   int zFrom = rowFrom / me->VolumeDims[2];
00527 
00528   double constraint = 0;
00529   for ( int z = zFrom; (z < me->VolumeDims[2]) && rowsToDo; ++z ) 
00530     {
00531     for ( int y = yFrom; (y < me->VolumeDims[1]) && rowsToDo; yFrom = 0, ++y, --rowsToDo ) 
00532       {
00533       me->GetJacobianDeterminantRow( &(valuesJ[0]), 0, y, z, pixelsPerRow );
00534       for ( int x = 0; x < pixelsPerRow; ++x ) 
00535         {
00536         constraint += fabs( log ( valuesJ[x] / me->GlobalScaling ) );
00537         }
00538       }
00539     }
00540   
00541   // Divide by number of control points to normalize with respect to the
00542   // number of local Jacobians in the computation.
00543   info->Constraint = constraint;
00544 }
00545 
00546 void
00547 SplineWarpXform
00548 ::GetJacobianFoldingConstraintThread( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t )
00549 {
00550   Self::JacobianConstraintThreadInfo *info = static_cast<Self::JacobianConstraintThreadInfo*>( args );
00551 
00552   const SplineWarpXform *me = info->thisObject;
00553 
00554   const int pixelsPerRow = me->VolumeDims[0];
00555   std::vector<double> valuesJ( pixelsPerRow );
00556 
00557   const int rowCount = ( me->VolumeDims[1] * me->VolumeDims[2] );
00558   const int rowFrom = ( rowCount / taskCnt ) * taskIdx;
00559   const int rowTo = ( taskIdx == (taskCnt-1) ) ? rowCount : ( rowCount/taskCnt ) * (taskIdx+1);
00560   int rowsToDo = rowTo - rowFrom;
00561 
00562   int yFrom = rowFrom % me->VolumeDims[1];
00563   int zFrom = rowFrom / me->VolumeDims[2];
00564 
00565   double constraint = 0;
00566   for ( int z = zFrom; (z < me->VolumeDims[2]) && rowsToDo; ++z ) 
00567     {
00568     for ( int y = yFrom; (y < me->VolumeDims[1]) && rowsToDo; yFrom = 0, ++y, --rowsToDo ) 
00569       {
00570       me->GetJacobianDeterminantRow( &(valuesJ[0]), 0, y, z, pixelsPerRow );
00571       for ( int x = 0; x < pixelsPerRow; ++x ) 
00572         {
00573         constraint += fabs( me->GlobalScaling / valuesJ[x] + valuesJ[x] / me->GlobalScaling - 2 );
00574         }
00575       }
00576     }
00577   
00578   // Divide by number of control points to normalize with respect to the
00579   // number of local Jacobians in the computation.
00580   info->Constraint = constraint;
00581 }
00582 
00583 Types::Coordinate
00584 SplineWarpXform::GetJacobianConstraint () const
00585 {
00586   ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool();
00587   const size_t numberOfThreads = threadPool.GetNumberOfThreads();
00588   const size_t numberOfTasks = std::min<size_t>( 4 * numberOfThreads - 3, this->m_Dims[2] );
00589   
00590   // Info blocks for parallel tasks that evaulate the constraint.
00591   std::vector<Self::JacobianConstraintThreadInfo> constraintTaskInfo( numberOfTasks );
00592   for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) 
00593     {
00594     constraintTaskInfo[taskIdx].thisObject = this;
00595     }
00596   
00597   threadPool.Run( Self::GetJacobianConstraintThread, constraintTaskInfo );
00598   
00599   double constraint = 0;
00600   for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) 
00601     {
00602     constraint += constraintTaskInfo[taskIdx].Constraint;
00603     }
00604   
00605   // Divide by number of control points to normalize with respect to the
00606   // number of local Jacobians in the computation.
00607   return constraint / ( VolumeDims[0] * VolumeDims[1] * VolumeDims[2] );
00608 }
00609 
00610 Types::Coordinate
00611 SplineWarpXform::GetJacobianFoldingConstraint () const
00612 {
00613   ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool();
00614   const size_t numberOfThreads = threadPool.GetNumberOfThreads();
00615   const size_t numberOfTasks = std::min<size_t>( 4 * numberOfThreads - 3, this->m_Dims[2] );
00616 
00617   // Info blocks for parallel threads that evaulate the constraint.
00618   std::vector<Self::JacobianConstraintThreadInfo> constraintTaskInfo( numberOfTasks );
00619   for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) 
00620     {
00621     constraintTaskInfo[taskIdx].thisObject = this;
00622     }
00623   
00624   threadPool.Run( Self::GetJacobianFoldingConstraintThread, constraintTaskInfo );
00625   
00626   double constraint = 0;
00627   for ( size_t taskIdx = 0; taskIdx < numberOfTasks; ++taskIdx ) 
00628     {
00629     constraint += constraintTaskInfo[taskIdx].Constraint;
00630     }
00631   
00632   // Divide by number of control points to normalize with respect to the
00633   // number of local Jacobians in the computation.
00634   return constraint / ( VolumeDims[0] * VolumeDims[1] * VolumeDims[2] );
00635 }
00636 
00637 Types::Coordinate
00638 SplineWarpXform::GetJacobianConstraintSparse () const
00639 {
00640   double Constraint = 0;
00641 
00642   const Types::Coordinate* coeff = this->m_Parameters + nextI + nextJ + nextK;
00643   for ( int z = 1; z<this->m_Dims[2]-1; ++z, coeff+=2*nextJ )
00644     for ( int y = 1; y<this->m_Dims[1]-1; ++y, coeff+=2*nextI )
00645       for ( int x = 1; x<this->m_Dims[0]-1; ++x, coeff+=nextI )
00646         Constraint += fabs( log ( this->GetJacobianDeterminant( Self::SpaceVectorType( coeff ) ) / GlobalScaling ) );
00647   
00648   // Divide by number of control points to normalize with respect to the
00649   // number of local Jacobians in the computation.
00650   return (double)(Constraint / this->NumberOfControlPoints);
00651 }
00652 
00653 void 
00654 SplineWarpXform::GetJacobianConstraintDerivative
00655 ( double& lower, double& upper, const int param, const DataGrid::RegionType& voi, const Types::Coordinate step ) const
00656 {
00657   const int pixelsPerRow = voi.To()[0] - voi.From()[0];
00658   std::vector<double> valuesJ( pixelsPerRow );
00659   
00660   double ground = 0;
00661 
00662   for ( int k = voi.From()[2]; k < voi.To()[2]; ++k )
00663     for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) 
00664       {
00665       this->GetJacobianDeterminantRow( &(valuesJ[0]), voi.From()[0], j, k, pixelsPerRow );
00666       for ( int i = 0; i < pixelsPerRow; ++i )
00667         ground += fabs( log( valuesJ[i] / GlobalScaling ) );
00668       }
00669   
00670   upper = -ground;
00671   lower = -ground;
00672   
00673   const Types::Coordinate oldCoeff = this->m_Parameters[param];
00674   this->m_Parameters[param] += step;
00675   for ( int k = voi.From()[2]; k < voi.To()[2]; ++k )
00676     for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) 
00677       {
00678       this->GetJacobianDeterminantRow( &(valuesJ[0]), voi.From()[0], j, k, pixelsPerRow );
00679       for ( int i = 0; i < pixelsPerRow; ++i )
00680         {
00681         upper += fabs( log( valuesJ[i] / GlobalScaling ) );
00682         }
00683       }
00684   
00685   this->m_Parameters[param] = oldCoeff - step;
00686   for ( int k = voi.From()[2]; k < voi.To()[2]; ++k )
00687     for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) 
00688       {
00689       this->GetJacobianDeterminantRow( &(valuesJ[0]), voi.From()[0], j, k, pixelsPerRow );
00690       for ( int i = 0; i < pixelsPerRow; ++i )
00691         {
00692         lower += fabs( log( valuesJ[i] / GlobalScaling ) );
00693         }
00694       }
00695   this->m_Parameters[param] = oldCoeff;
00696   
00697   const double invVolume = 1.0 / voi.Size();
00698   upper *= invVolume;
00699   lower *= invVolume;
00700 }
00701 
00702 void 
00703 SplineWarpXform::GetJacobianFoldingConstraintDerivative
00704 ( double& lower, double& upper, const int param, const DataGrid::RegionType& voi, const Types::Coordinate step ) const
00705 {
00706   const int pixelsPerRow = voi.To()[0] - voi.From()[0];
00707   std::vector<double> valuesJ( pixelsPerRow );
00708   
00709   double ground = 0;
00710 
00711   for ( int k = voi.From()[2]; k < voi.To()[2]; ++k )
00712     for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) 
00713       {
00714       this->GetJacobianDeterminantRow( &(valuesJ[0]), voi.From()[0], j, k, pixelsPerRow );
00715       for ( int i = 0; i < pixelsPerRow; ++i )
00716         ground += fabs( this->GlobalScaling / valuesJ[i] + valuesJ[i] / this->GlobalScaling - 2 );
00717       }
00718   
00719   upper = -ground;
00720   lower = -ground;
00721   
00722   const Types::Coordinate oldCoeff = this->m_Parameters[param];
00723   this->m_Parameters[param] += step;
00724   for ( int k = voi.From()[2]; k < voi.To()[2]; ++k )
00725     for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) 
00726       {
00727       this->GetJacobianDeterminantRow( &(valuesJ[0]), voi.From()[0], j, k, pixelsPerRow );
00728       for ( int i = 0; i < pixelsPerRow; ++i )
00729         {
00730         upper += fabs( this->GlobalScaling / valuesJ[i] + valuesJ[i] / this->GlobalScaling - 2 );
00731 
00732         }
00733       }
00734   
00735   this->m_Parameters[param] = oldCoeff - step;
00736   for ( int k = voi.From()[2]; k < voi.To()[2]; ++k )
00737     for ( int j = voi.From()[1]; j < voi.To()[1]; ++j ) 
00738       {
00739       this->GetJacobianDeterminantRow( &(valuesJ[0]), voi.From()[0], j, k, pixelsPerRow );
00740       for ( int i = 0; i < pixelsPerRow; ++i )
00741         {
00742         lower += fabs( this->GlobalScaling / valuesJ[i] + valuesJ[i] / this->GlobalScaling - 2 );
00743         }
00744       }
00745   this->m_Parameters[param] = oldCoeff;
00746   
00747   const double invVolume = 1.0 / voi.Size();
00748   upper *= invVolume;
00749   lower *= invVolume;
00750 }
00751 
00752 void 
00753 SplineWarpXform::GetJacobianConstraintDerivative
00754 ( double& lower, double& upper, const int param, const Types::Coordinate step )
00755   const
00756 {
00757   const int controlPointIdx = param / nextI;
00758   const unsigned short x =  ( controlPointIdx %  this->m_Dims[0] );
00759   const unsigned short y = ( (controlPointIdx /  this->m_Dims[0]) % this->m_Dims[1] );
00760   const unsigned short z = ( (controlPointIdx /  this->m_Dims[0]) / this->m_Dims[1] );
00761   
00762   const int thisDim = param % nextI;
00763   const Types::Coordinate* coeff = this->m_Parameters + param - thisDim;
00764   
00765   double ground = 0;
00766 
00767   const int iFrom = std::max( -1, 1-x );
00768   const int jFrom = std::max( -1, 1-y );
00769   const int kFrom = std::max( -1, 1-z );
00770 
00771   const int iTo = std::min( 1, this->m_Dims[0]-2-x );
00772   const int jTo = std::min( 1, this->m_Dims[1]-2-y );
00773   const int kTo = std::min( 1, this->m_Dims[2]-2-z );
00774 
00775   for ( int k = kFrom; k < kTo; ++k )
00776     for ( int j = jFrom; j < jTo; ++j )
00777       for ( int i = iFrom; i < iTo; ++i )
00778         ground += fabs( log( this->GetJacobianDeterminant( Self::SpaceVectorType( coeff + i*nextI + j*nextJ + k*nextK ) ) / GlobalScaling ) );
00779 
00780   upper = -ground;
00781   lower = -ground;
00782 
00783   const Types::Coordinate oldCoeff = this->m_Parameters[param];
00784   this->m_Parameters[param] += step;
00785   for ( int k = kFrom; k < kTo; ++k )
00786     for ( int j = jFrom; j < jTo; ++j )
00787       for ( int i = iFrom; i < iTo; ++i )
00788         upper += fabs( log( this->GetJacobianDeterminant( Self::SpaceVectorType( coeff + i*nextI + j*nextJ + k*nextK ) ) / GlobalScaling ) );
00789 
00790   this->m_Parameters[param] = oldCoeff - step;
00791   for ( int k = kFrom; k < kTo; ++k )
00792     for ( int j = jFrom; j < jTo; ++j )
00793       for ( int i = iFrom; i < iTo; ++i )
00794         lower += fabs( log( this->GetJacobianDeterminant( Self::SpaceVectorType( coeff + i*nextI + j*nextJ + k*nextK ) ) / GlobalScaling ) );
00795   this->m_Parameters[param] = oldCoeff;
00796 
00797   upper /= this->NumberOfControlPoints;
00798   lower /= this->NumberOfControlPoints;
00799 }
00800 
00801 void
00802 SplineWarpXform::RelaxToUnfold()
00803 {
00804   std::vector<byte> cpList( this->NumberOfControlPoints );
00805   std::vector<double> jacobiansRow( this->VolumeDims[0] );
00806 
00807   bool isFolded = true;
00808   while ( isFolded )
00809     {
00810     // check all Jacobian determinant values to see if grid is folded, and what control points are affected
00811     isFolded = false;
00812     std::fill( cpList.begin(), cpList.end(), 0 );
00813 
00814     for ( int k = 0; k < this->VolumeDims[2]; ++k )
00815       {
00816       for ( int j = 0; j < this->VolumeDims[1]; ++j )
00817         {
00818         this->GetJacobianDeterminantRow( &(jacobiansRow[0]), 0, j, k, this->VolumeDims[0] );
00819         for ( int i = 0; i < this->VolumeDims[0]; ++i )
00820           {
00821           if ( jacobiansRow[i] <= 0 )
00822             {
00823             isFolded = true;
00824             cpList[ (this->gX[i] + this->gY[j] + this->gZ[k])/3 ] = 1;
00825             }
00826           }
00827         }
00828       }
00829 
00830     if ( isFolded )
00831       {
00832       // Expand affected control points to their 4x4x4 neighbourhoods on all sides.
00833       for ( int k = 0; k < this->m_Dims[2]; ++k )
00834         {
00835         for ( int j = 0; j < this->m_Dims[1]; ++j )
00836           {
00837           for ( int i = 0; i < this->m_Dims[0]; ++i )
00838             {
00839             if ( cpList[ (i * this->nextI + j * this->nextJ + k * this->nextK)/3 ] == 1 )
00840               {
00841               for ( int kk = std::max( 0, k-3 ); kk < std::min( k+4, this->m_Dims[2] ); ++kk )
00842                 {
00843                 for ( int jj = std::max( 0, j-3 ); jj < std::min( j+4, this->m_Dims[1] ); ++jj )
00844                   {
00845                   for ( int ii = std::max( 0, i-3 ); ii < std::min( i+4, this->m_Dims[0] ); ++ii )
00846                     {
00847                     const size_t cpIdx = (ii * this->nextI + jj * this->nextJ + kk * this->nextK)/3;
00848                     if ( cpList[ cpIdx ] != 1 )
00849                       {
00850                       cpList[ cpIdx ] = 2;
00851                       }
00852                     }
00853                   }
00854                 }
00855               }
00856             }
00857           }
00858         }
00859 
00860       // Get pure deformation at each control point.
00861       std::vector<Types::Coordinate> pureDeformation( 3 * this->NumberOfControlPoints );
00862 
00863       size_t param = 0;
00864       for ( int k = 0; k < this->m_Dims[2]; ++k )
00865         {
00866         for ( int j = 0; j < this->m_Dims[1]; ++j )
00867           {
00868           for ( int i = 0; i < this->m_Dims[0]; ++i, param+=3 )
00869             {
00870             Self::SpaceVectorType cp( this->m_Parameters+param );
00871             this->m_InitialAffineXform->GetInverse()->ApplyInPlace( cp );
00872 
00873             Self::SpaceVectorType cp0;
00874             this->GetOriginalControlPointPosition( cp0, i, j, k );
00875 
00876             cp -= cp0;
00877             for ( int dim = 0; dim < 3; ++dim )
00878               pureDeformation[param+dim] = cp[dim];
00879             }
00880           }
00881         }
00882       
00883       // regularize the affected control points
00884       std::vector<Types::Coordinate> smoothed( this->NumberOfControlPoints );
00885       for ( int dim = 0; dim < 3; ++dim )
00886         {
00887         // get all control point positions for the "dim" component
00888         size_t cp = 0;
00889         for ( int k = 0; k < this->m_Dims[2]; ++k )
00890           {
00891           for ( int j = 0; j < this->m_Dims[1]; ++j )
00892             {
00893             for ( int i = 0; i < this->m_Dims[0]; ++i, ++cp )
00894               {
00895               if ( cpList[cp] )
00896                 {
00897                 // if this is a control point that needs regularizing, do it
00898                 Types::Coordinate weight = 0;
00899                 FixedVector<3,Types::Coordinate> delta;             
00900                 for ( int kk = std::max( 0, k-3 ); kk < std::min( k+4, this->m_Dims[2] ); ++kk )
00901                   {
00902                   delta[2] = k-kk;
00903                   for ( int jj = std::max( 0, j-3 ); jj < std::min( j+4, this->m_Dims[1] ); ++jj )
00904                     {
00905                     delta[1] = j-jj;
00906                     for ( int ii = std::max( 0, i-3 ); ii < std::min( i+4, this->m_Dims[0] ); ++ii )
00907                       {
00908                       delta[0] = i-ii;
00909                       
00910                       const Types::Coordinate w = exp( -delta.SumOfSquares() );
00911                       smoothed[cp] = w * pureDeformation[ dim + ii*this->nextI + jj*this->nextJ + kk*this->nextK ];
00912                       weight += w;
00913                       }
00914                     }
00915                   }
00916                 if ( weight > 0 )
00917                   smoothed[cp] /= weight;
00918                 }
00919               else
00920                 {
00921                 // if this control point does not need regularizing, keep it as is
00922                 smoothed[cp] = pureDeformation[dim+cp*3];
00923                 }
00924               }
00925             }
00926           }
00927         
00928         // copy modified control point position component back
00929         for ( size_t cp = 0; cp < this->NumberOfControlPoints; ++cp )
00930           {
00931           pureDeformation[dim+cp*3] = smoothed[cp];
00932           }
00933         }
00934       
00935       // put offsets and affine component back
00936       param = 0;
00937       for ( int k = 0; k < this->m_Dims[2]; ++k )
00938         {
00939         for ( int j = 0; j < this->m_Dims[1]; ++j )
00940           {
00941           for ( int i = 0; i < this->m_Dims[0]; ++i, param+=3 )
00942             {
00943             Self::SpaceVectorType cp0;
00944             this->GetOriginalControlPointPosition( cp0, i, j, k );
00945             
00946             Self::SpaceVectorType cp( &(pureDeformation[0])+param );
00947             cp += cp0;
00948             
00949             this->m_InitialAffineXform->ApplyInPlace( cp );
00950             
00951             for ( int dim = 0; dim < 3; ++dim )
00952               this->m_Parameters[param+dim] = cp[dim];
00953             }
00954           }
00955         }
00956       }
00957     }
00958 }
00959 
00960 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines