cmtkUniformDistanceMap.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 2003 Calvin R. Maurer, Jr.
00004 //
00005 //  Copyright 1997-2009 Torsten Rohlfing
00006 //
00007 //  Copyright 2004-2010 SRI International
00008 //
00009 //  This file is part of the Computational Morphometry Toolkit.
00010 //
00011 //  http://www.nitrc.org/projects/cmtk/
00012 //
00013 //  The Computational Morphometry Toolkit is free software: you can
00014 //  redistribute it and/or modify it under the terms of the GNU General Public
00015 //  License as published by the Free Software Foundation, either version 3 of
00016 //  the License, or (at your option) any later version.
00017 //
00018 //  The Computational Morphometry Toolkit is distributed in the hope that it
00019 //  will be useful, but WITHOUT ANY WARRANTY; without even the implied
00020 //  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021 //  GNU General Public License for more details.
00022 //
00023 //  You should have received a copy of the GNU General Public License along
00024 //  with the Computational Morphometry Toolkit.  If not, see
00025 //  <http://www.gnu.org/licenses/>.
00026 //
00027 //  $Revision: 2398 $
00028 //
00029 //  $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $
00030 //
00031 //  $LastChangedBy: torstenrohlfing $
00032 //
00033 */
00034 
00035 #include "cmtkUniformDistanceMap.h"
00036 
00037 #include <Base/cmtkDataTypeTraits.h>
00038 
00039 #include <System/cmtkThreadPool.h>
00040 
00041 namespace
00042 cmtk
00043 {
00044 
00047 
00048 template<class TDistanceDataType>
00049 UniformDistanceMap<TDistanceDataType>
00050 ::UniformDistanceMap
00051 ( const UniformVolume& volume, const byte flags, const Types::DataItem value, const Types::DataItem window )
00052 {
00053   this->BuildDistanceMap( volume, flags, value, window );
00054 
00055   this->m_DistanceMap->m_IndexToPhysicalMatrix = volume.m_IndexToPhysicalMatrix;
00056 
00057   this->m_DistanceMap->SetOffset( volume.m_Offset );
00058   this->m_DistanceMap->m_MetaInformation = volume.m_MetaInformation;
00059 }
00060 
00061 template<class TDistanceDataType>
00062 void
00063 UniformDistanceMap<TDistanceDataType>
00064 ::BuildDistanceMap
00065 ( const UniformVolume& volume, const byte flags, const Types::DataItem value, const Types::DataItem window )
00066 {
00067   this->m_DistanceMap = UniformVolume::SmartPtr( new UniformVolume( volume.m_Dims, volume.Size ) );
00068     
00069   TypedArray::SmartPtr distanceArray = TypedArray::SmartPtr( TypedArray::Create( DataTypeTraits<DistanceDataType>::DataTypeID, volume.GetNumberOfPixels() ) );
00070   DistanceDataType *Distance = static_cast<DistanceDataType*>( distanceArray->GetDataPtr() );
00071 
00072   byte inside = ( flags & UniformDistanceMap::INSIDE ) ? 0 : 1;
00073   byte outside = 1 - inside;
00074 
00075   const TypedArray* Feature = volume.GetData();
00076 
00077   Types::DataItem c;
00078   DistanceDataType *p = Distance;
00079   if ( flags & UniformDistanceMap::VALUE_EXACT ) 
00080     {
00081     for ( size_t i = 0; i < volume.GetNumberOfPixels(); i++, p++ ) 
00082       {
00083       if ( Feature->Get( c, i ) )
00084         {
00085         *p = (c == value) ? inside : outside;
00086         }
00087       else
00088         {
00089         *p = outside;
00090         }
00091       }
00092     } 
00093   else if ( flags & UniformDistanceMap::VALUE_THRESHOLD ) 
00094     {
00095     for ( size_t i = 0; i < volume.GetNumberOfPixels(); i++, p++ ) 
00096       {
00097       if ( Feature->Get( c, i ) )
00098         {
00099         *p = (c >= value) ? inside : outside;
00100         }
00101       else
00102         {
00103         *p = outside;
00104         }
00105       }
00106     } 
00107   else if ( flags & UniformDistanceMap::VALUE_WINDOW ) 
00108     {
00109     for ( size_t i = 0; i < volume.GetNumberOfPixels(); i++, p++ ) 
00110       {
00111       if ( Feature->Get( c, i ) )
00112         {
00113         *p = (fabs(c - value)<=window) ? inside : outside;
00114         }
00115       else
00116         {
00117         *p = outside;
00118         }
00119       }
00120     } 
00121   else
00122     {
00123     for ( size_t i = 0; i < volume.GetNumberOfPixels(); i++, p++ ) 
00124       {
00125       if ( Feature->Get( c, i ) )
00126         {
00127         *p = (c) ? inside : outside;
00128         }
00129       else
00130         {
00131         *p = outside;
00132         }
00133       }
00134     }
00135   
00136   this->ComputeEDT( Distance );
00137 
00138   p = Distance;
00139   for ( size_t i = 0; i < volume.GetNumberOfPixels(); ++i, ++p ) 
00140     {
00141 #if defined(_MSC_VER) || defined(__SUNPRO_CC)
00142     *p = static_cast<DistanceDataType>( sqrt( (double)*p ) );
00143 #else
00144     *p = static_cast<DistanceDataType>( sqrt( *p ) );
00145 #endif
00146     }
00147   this->m_DistanceMap->SetData( distanceArray );
00148 }
00149 
00150 template<class TDistanceDataType>
00151 void
00152 UniformDistanceMap<TDistanceDataType>
00153 ::ComputeEDT( DistanceDataType *const distance )
00154 {
00155   ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool();
00156   const size_t numberOfThreads = threadPool.GetNumberOfThreads();
00157   const size_t numberOfTasks = 4 * numberOfThreads - 3;
00158   
00159   this->m_G.resize( numberOfThreads );
00160   this->m_H.resize( numberOfThreads );
00161   
00162   std::vector<typename Self::ThreadParametersEDT> params( numberOfTasks );
00163   for ( size_t idx = 0; idx < numberOfTasks; ++idx )
00164     {
00165     params[idx].thisObject = this;
00166     params[idx].m_Distance = distance;
00167     }
00168 
00169   threadPool.Run( ComputeEDTThreadPhase1, params );
00170   threadPool.Run( ComputeEDTThreadPhase2, params );
00171 }
00172 
00173 template<class TDistanceDataType>
00174 void
00175 UniformDistanceMap<TDistanceDataType>
00176 ::ComputeEDTThreadPhase1
00177 ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t )
00178 {
00179   ThreadParametersEDT* params = static_cast<ThreadParametersEDT*>( args );
00180   Self* This = params->thisObject;
00181   const Self* ThisConst = This;
00182 
00183   /* nXY is number of voxels in each plane (xy) */
00184   /* nXYZ is number of voxels in 3D image */
00185   const size_t nXY = ThisConst->m_DistanceMap->m_Dims[0] * ThisConst->m_DistanceMap->m_Dims[1];
00186   
00187   /* compute D_2 */
00188   /* call edtComputeEDT_2D for each plane */
00189   DistanceDataType *p = params->m_Distance + nXY * taskIdx;
00190   for ( int k = taskIdx; k < ThisConst->m_DistanceMap->m_Dims[2]; k += taskCnt, p += nXY * taskCnt ) 
00191     {
00192     This->ComputeEDT2D( p, This->m_G[threadIdx], This->m_H[threadIdx] );
00193     }
00194 }
00195 
00196 template<class TDistanceDataType>
00197 void
00198 UniformDistanceMap<TDistanceDataType>
00199 ::ComputeEDTThreadPhase2
00200 ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t )
00201 {
00202   ThreadParametersEDT* params = static_cast<ThreadParametersEDT*>( args );
00203   Self* This = params->thisObject;
00204   const Self* ThisConst = This;
00205 
00206   const size_t nXY = ThisConst->m_DistanceMap->m_Dims[0] * ThisConst->m_DistanceMap->m_Dims[1];
00207   /* compute D_3 */
00208   /* solve 1D problem for each column (z direction) */
00209   std::vector<DistanceDataType> f( This->m_DistanceMap->m_Dims[2] );
00210 
00211   for ( size_t i = taskIdx; i < nXY; i += taskCnt ) 
00212     {
00213     /* fill array f with D_2 distances in column */
00214     /* this is essentially line 4 in Procedure VoronoiEDT() in tPAMI paper */
00215     DistanceDataType *p = params->m_Distance + i;
00216     DistanceDataType *q = &f[0];
00217     for ( int k = 0; k < ThisConst->m_DistanceMap->m_Dims[2]; k++, p += nXY, q++) 
00218       {
00219       *q = *p;
00220       }
00221     
00222     /* call edtVoronoiEDT */
00223     if ( This->VoronoiEDT( &f[0], ThisConst->m_DistanceMap->m_Dims[2], static_cast<DistanceDataType>( ThisConst->m_DistanceMap->m_Delta[2] ), This->m_G[threadIdx], This->m_H[threadIdx] ) ) 
00224       {
00225       p = params->m_Distance + i;
00226       DistanceDataType *q = &f[0];
00227       for ( int k = 0; k < ThisConst->m_DistanceMap->m_Dims[2]; k++, p += nXY, q++ ) 
00228         {
00229         *p = *q;
00230         }
00231       }
00232     }
00233 }
00234 
00235 template<class TDistanceDataType>
00236 void
00237 UniformDistanceMap<TDistanceDataType>
00238 ::ComputeEDT2D
00239 ( DistanceDataType *const plane, std::vector<DistanceDataType>& gTemp, std::vector<DistanceDataType>& hTemp )
00240   /*
00241    * This procedure computes the squared EDT of a 2D binary image with
00242    * anisotropic voxels. See notes for edtComputeEDT_2D. The difference 
00243    * relative to edtComputeEDT_2D is that the edt is a float array instead of 
00244    * a long array, and there are additional parameters for the image voxel 
00245    * dimensions wX and wY.
00246    */
00247 {  
00248   /* compute D_1 as simple forward-and-reverse distance propagation */
00249   /* (instead of calling edtVoronoiEDT) */
00250   /* D_1 is distance to closest feature voxel in row (x direction) */
00251   /* it is possible to use a simple distance propagation for D_1  because */
00252   /* L_1 and L_2 norms are equivalent for 1D case */
00253   DistanceDataType *p;
00254   for ( int j = 0; j < this->m_DistanceMap->m_Dims[1]; j++ ) 
00255     {
00256     /* forward pass */
00257     p = plane + j * this->m_DistanceMap->m_Dims[0];
00258     DistanceDataType d = static_cast<DistanceDataType>( EDT_MAX_DISTANCE_SQUARED );
00259     for ( int i = 0; i < this->m_DistanceMap->m_Dims[0]; i++, p++ ) 
00260       {
00261       /* set d = 0 when we encounter a feature voxel */
00262       if ( *p ) 
00263         {
00264         *p = d = 0;
00265         }
00266       /* increment distance ... */
00267       else 
00268         if ( d != EDT_MAX_DISTANCE_SQUARED ) 
00269           {
00270           *p = ++d;
00271           }
00272       /* ... unless we haven't encountered a feature voxel yet */
00273         else
00274           {
00275           *p = static_cast<DistanceDataType>( EDT_MAX_DISTANCE_SQUARED );
00276           }
00277       }
00278     
00279     /* reverse pass */
00280     if ( *(--p) != EDT_MAX_DISTANCE_SQUARED ) 
00281       {
00282       DistanceDataType d = static_cast<DistanceDataType>( EDT_MAX_DISTANCE_SQUARED );
00283       for ( int i = this->m_DistanceMap->m_Dims[0] - 1; i >= 0; i--, p-- ) 
00284         {
00285         /* set d = 0 when we encounter a feature voxel */
00286         if ( *p == 0 ) 
00287           {
00288           d = 0;
00289           }
00290         /* increment distance after encountering a feature voxel */
00291         else
00292           if ( d != EDT_MAX_DISTANCE_SQUARED ) 
00293             {
00294             /* compare forward and reverse distances */
00295             if ( ++d < *p ) 
00296               {
00297               *p = d;
00298               }
00299             }
00300         
00301         /* square distance */
00302         /* (we use squared distance in rest of algorithm) */
00303         *p = static_cast<DistanceDataType>( *p * this->m_DistanceMap->m_Delta[0] );
00304         *p *= *p;
00305         }
00306       }
00307     }
00308   
00309   /* compute D_2 = squared EDT */
00310   /* solve 1D problem for each column (y direction) */
00311   std::vector<DistanceDataType> f( this->m_DistanceMap->m_Dims[1] );
00312   for ( int i = 0; i < this->m_DistanceMap->m_Dims[0]; i++ ) 
00313     {
00314     /* fill array f with D_1 distances in column */
00315     /* this is essentially line 4 in Procedure VoronoiEDT() in tPAMI paper */
00316     DistanceDataType *p = plane + i;
00317     DistanceDataType *q = &f[0];
00318     for ( int j = 0; j < this->m_DistanceMap->m_Dims[1]; j++, p += this->m_DistanceMap->m_Dims[0], q++) 
00319       {
00320       *q = *p;
00321       }
00322     
00323     /* call edtVoronoiEDT */
00324     if ( this->VoronoiEDT( &f[0], this->m_DistanceMap->m_Dims[1], static_cast<DistanceDataType>( this->m_DistanceMap->m_Delta[1] ), gTemp, hTemp  ) ) 
00325       {
00326       DistanceDataType *p = plane + i;
00327       DistanceDataType *q = &f[0];
00328       for ( int j = 0; j < this->m_DistanceMap->m_Dims[1]; j++, p += this->m_DistanceMap->m_Dims[0], q++ ) 
00329         {
00330         *p = *q;
00331         }
00332       }
00333     }
00334   
00335 } /* edtComputeEDT_2D_anisotropic */
00336 
00337 template<class TDistanceDataType>
00338 bool
00339 UniformDistanceMap<TDistanceDataType>
00340 ::VoronoiEDT
00341 ( DistanceDataType *const distanceSoFar, const int nSize, const DistanceDataType delta,
00342   std::vector<DistanceDataType>& gTemp, std::vector<DistanceDataType>& hTemp )
00343 {
00344   long i, l, n_S;
00345   DistanceDataType a, b, c, v, lhs, rhs;
00346   
00347   /* alloc arrays if this is first call to procedure, or if arrays */
00348   /* are too small and need to be reallocated */
00349   gTemp.resize( nSize );
00350   hTemp.resize( nSize );
00351   
00352   DistanceDataType* g = &(gTemp[0]);
00353   DistanceDataType* h = &(hTemp[0]);
00354 
00355   /* construct partial Voronoi diagram */
00356   /* this loop is lines 1-14 in Procedure edtVoronoiEDT() in tPAMI paper */
00357   /* note we use 0 indexing in this program whereas paper uses 1 indexing */
00358   DistanceDataType deltai = 0;
00359   for (i = 0, l = -1; i < nSize; i++, deltai += delta) 
00360     {
00361     /* line 4 */
00362     if ( distanceSoFar[i] != EDT_MAX_DISTANCE_SQUARED ) 
00363       {
00364       /* line 5 */
00365       if ( l < 1 ) 
00366         {
00367         /* line 6 */
00368         g[++l] = distanceSoFar[i];
00369         h[l] = deltai;
00370         }
00371       /* line 7 */
00372       else 
00373         {
00374         /* line 8 */
00375         while (l >= 1) 
00376           {
00377           /* compute removeEDT() in line 8 */
00378           v = h[l];
00379           a = v - h[l-1];
00380           b = deltai - v;
00381           c = a + b;
00382           /* compute Eq. 2 */
00383           if ((c*g[l] - b*g[l-1] - a*distanceSoFar[i] - a*b*c) > 0) 
00384             {
00385             /* line 9 */
00386             l--;
00387             } 
00388           else 
00389             {
00390             break;
00391             }
00392           }
00393         /* line 11 */
00394         g[++l] = distanceSoFar[i];
00395         h[l] = deltai;
00396         }
00397       }
00398     }
00399   /* query partial Voronoi diagram */
00400   /* this is lines 15-25 in Procedure edtVoronoiEDT() in tPAMI paper */
00401   /* lines 15-17 */
00402   if ((n_S = l + 1) == 0) 
00403     {
00404     return false;
00405     }
00406   
00407   /* lines 18-19 */
00408   deltai = 0;
00409   for (i = 0, l = 0; i < nSize; i++, deltai += delta) 
00410     {
00411     /* line 20 */
00412     /* we reduce number of arithmetic operations by taking advantage of */
00413     /* similarities in successive computations instead of treating them as */
00414     /* independent ones */
00415     a = h[l] -  deltai;
00416     lhs = g[l] + a * a;
00417     while (l < n_S - 1)
00418       {
00419       a = h[l+1] - deltai;
00420       rhs = g[l+1] + a * a;
00421       if (lhs > rhs) 
00422         {
00423         /* line 21 */
00424         l++;
00425         lhs = rhs;
00426         } 
00427       else
00428         {
00429         break;
00430         }
00431       }
00432     
00433     /* line 23 */
00434     /* we put distance into the 1D array that was passed; */
00435     /* must copy into EDT in calling procedure */
00436     distanceSoFar[i] = lhs;
00437     }
00438   
00439   /* line 25 */
00440   /* return 1 if we queried diagram, 0 if we returned because n_S = 0 */
00441   return true;
00442 }
00443 
00445 
00446 } // namespace cmtk
00447 
00448 template class cmtk::UniformDistanceMap<float>;
00449 template class cmtk::UniformDistanceMap<double>;
00450 template class cmtk::UniformDistanceMap<long int>;
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines