cmtkUniformVolume.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 1997-2009 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: 2751 $
00026 //
00027 //  $LastChangedDate: 2011-01-14 15:48:39 -0800 (Fri, 14 Jan 2011) $
00028 //
00029 //  $LastChangedBy: torstenrohlfing $
00030 //
00031 */
00032 
00033 #include "cmtkUniformVolume.h"
00034 
00035 #include <Base/cmtkAffineXform.h>
00036 #include <Base/cmtkEigenSystemSymmetricMatrix3x3.h>
00037 
00038 namespace
00039 cmtk
00040 {
00041 
00042 UniformVolume::UniformVolume
00043 ( const DataGrid::IndexType& dims, const Self::CoordinateVectorType& size, TypedArray::SmartPtr& data )
00044 {
00045   this->SetData( data );
00046   this->SetDims( dims );
00047   
00048   for ( int i=0; i<3; ++i ) {
00049     Size[i] = static_cast<Types::Coordinate>( size[i] );
00050     this->m_Delta[i] = ( this->m_Dims[i] == 1 ) ? 0 : Size[i] / (this->m_Dims[i] - 1);
00051   }
00052 
00053   this->CropRegion() = this->GetWholeImageRegion();
00054   this->CreateDefaultIndexToPhysicalMatrix();
00055 }
00056 
00057 UniformVolume::UniformVolume
00058 ( const DataGrid::IndexType& dims, const Types::Coordinate deltaX, const Types::Coordinate deltaY, const Types::Coordinate deltaZ, TypedArray::SmartPtr& data )
00059 {
00060   this->SetData( data );
00061   this->SetDims( dims );
00062 
00063   this->m_Delta[0] = deltaX;
00064   this->m_Delta[1] = deltaY;
00065   this->m_Delta[2] = deltaZ;
00066 
00067   for ( int i=0; i<3; ++i )
00068     Size[i] = this->m_Delta[i] * (this->m_Dims[i]-1);
00069 
00070   this->CropRegion() = this->GetWholeImageRegion();
00071   this->CreateDefaultIndexToPhysicalMatrix();
00072 }
00073 
00074 UniformVolume::UniformVolume 
00075 ( const UniformVolume& other, const Types::Coordinate resolution, const bool allowUpsampling ) 
00076 {
00077   Self::IndexType newDims;
00078   for ( int dim=0; dim<3; ++dim ) 
00079     {
00080     Size[dim] = other.Size[dim];
00081     int new_dims=(int) (Size[dim]/resolution)+1;
00082     if ( allowUpsampling || (new_dims<=other.m_Dims[dim]) ) 
00083       {
00084       newDims[dim]=new_dims;
00085       this->m_Delta[dim]=Size[dim]/(new_dims-1);
00086       } 
00087     else
00088       {
00089       if ( other.m_Dims[dim] == 1 ) 
00090         {
00091         this->m_Delta[dim] = Size[dim];
00092         newDims[dim] = 1;
00093         } 
00094       else 
00095         {
00096         this->m_Delta[dim] = other.m_Delta[dim];
00097         newDims[dim] = ((int)(Size[dim]/this->m_Delta[dim])) + 1;
00098         Size[dim] = (newDims[dim]-1) * this->m_Delta[dim];
00099         }
00100       }
00101     }
00102   
00103   this->SetDims( newDims );
00104   TypedArray::SmartPtr resampledData( this->Resample( other ) );
00105   this->SetData( resampledData   );
00106   
00107   this->m_IndexToPhysicalMatrix = other.m_IndexToPhysicalMatrix;
00108 
00109   this->SetHighResCropRegion( other.GetHighResCropRegion() );
00110   this->SetOffset( other.m_Offset );
00111   this->m_MetaInformation = other.m_MetaInformation;
00112 }
00113 
00114 UniformVolume*
00115 UniformVolume::CloneVirtual( const bool copyData )
00116 {
00117   if ( copyData ) 
00118     {
00119     return this->CloneVirtual();
00120     } 
00121   else 
00122     {
00123     UniformVolume *result = this->CloneGridVirtual();
00124     result->SetData( this->GetData() );
00125     
00126     return result;
00127     }
00128 }
00129 
00130 UniformVolume*
00131 UniformVolume::CloneVirtual() const
00132 {
00133   UniformVolume *result = this->CloneGridVirtual();
00134   
00135   if ( this->GetData() )
00136     {
00137     TypedArray::SmartPtr clonedData( this->GetData()->Clone() );
00138     result->SetData( clonedData );
00139     }
00140   else
00141     {
00142     result->SetData( TypedArray::SmartPtr::Null );
00143     }
00144   
00145   return result;
00146 }
00147 
00148 UniformVolume*
00149 UniformVolume::CloneGridVirtual() const
00150 {
00151   UniformVolume* clone = new UniformVolume( this->m_Dims, Size );
00152   clone->SetOffset( this->m_Offset );
00153   clone->m_MetaInformation = this->m_MetaInformation;
00154   clone->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix;
00155   return clone;
00156 }
00157 
00158 UniformVolume* 
00159 UniformVolume::GetDownsampledAndAveraged( const int downsample, const bool approxIsotropic ) const
00160 {
00161   if ( approxIsotropic )
00162     {
00163     const Types::Coordinate minDelta = std::min<Types::Coordinate>( this->m_Delta[0], std::min<Types::Coordinate>( this->m_Delta[1], this->m_Delta[2] ) );
00164     const int downsampleByAxis[3] = { std::max<int>( 1, downsample / std::max<int>( 1, static_cast<int>(this->m_Delta[0] / minDelta) ) ),
00165                                       std::max<int>( 1, downsample / std::max<int>( 1, static_cast<int>(this->m_Delta[1] / minDelta) ) ),
00166                                       std::max<int>( 1, downsample / std::max<int>( 1, static_cast<int>(this->m_Delta[2] / minDelta) ) ) };
00167     return this->GetDownsampledAndAveraged( downsampleByAxis );
00168     }
00169   else
00170     {
00171     const int downsampleByAxis[3] = { downsample, downsample, downsample };
00172     return this->GetDownsampledAndAveraged( downsampleByAxis );
00173     }
00174 }
00175 
00176 UniformVolume* 
00177 UniformVolume::GetDownsampledAndAveraged( const int (&downsample)[3] ) const
00178 {
00179   DataGrid::SmartPtr newDataGrid( this->DataGrid::GetDownsampledAndAveraged( downsample ) );
00180   TypedArray::SmartPtr newData = newDataGrid->GetData();
00181 
00182   // create downsample grid
00183   UniformVolume* dsVolume = new UniformVolume( newDataGrid->GetDims(), downsample[0] * this->m_Delta[0], downsample[1] * this->m_Delta[1], downsample[2] * this->m_Delta[2], newData );
00184 
00185   // compute shift of volume origin
00186   const Types::Coordinate shift[3] = { (downsample[0]-1)*this->m_Delta[0]/2, (downsample[1]-1)*this->m_Delta[1]/2, (downsample[2]-1)*this->m_Delta[2]/2 };
00187   
00188   // apply shift to origin
00189   Self::CoordinateVectorType offset( this->m_Offset );
00190   offset += Self::CoordinateVectorType( shift );
00191   dsVolume->SetOffset( offset );
00192   
00193   // set crop region while considering new image offset
00194   dsVolume->SetHighResCropRegion( this->GetHighResCropRegion() );
00195 
00196   dsVolume->m_MetaInformation = this->m_MetaInformation;
00197   dsVolume->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix;
00198 
00199   // apply offset shift to index-to-physical matrix
00200   for ( int axis = 0; axis < 3; ++axis )
00201     for ( int i = 0; i < 3; ++i )
00202       {
00203       dsVolume->m_IndexToPhysicalMatrix[3][i] += (downsample[i]-1) * dsVolume->m_IndexToPhysicalMatrix[axis][i] / 2;
00204       // also update voxel size
00205       dsVolume->m_IndexToPhysicalMatrix[axis][i] *= downsample[i];
00206       }
00207   
00208   return dsVolume;
00209 }
00210 
00211 UniformVolume* 
00212 UniformVolume::GetInterleavedSubVolume
00213 ( const int axis, const int factor, const int idx ) const
00214 {
00215   Self::IndexType dims;
00216   Self::CoordinateVectorType size;
00217 
00218   for ( int dim = 0; dim < 3; ++dim )
00219     {
00220     dims[dim] = this->m_Dims[dim];
00221     size[dim] = this->Size[ dim ];
00222     }
00223   dims[axis] = this->m_Dims[axis] / factor;
00224   if ( this->m_Dims[axis] % factor > idx )
00225     ++dims[axis];
00226   size[axis] = (dims[axis]-1) * factor * this->m_Delta[axis];
00227   
00228   Self::CoordinateVectorType offset( Self::CoordinateVectorType::Init( 0 ) );
00229   offset[axis] = idx * this->m_Delta[axis];
00230   
00231   UniformVolume* volume = new UniformVolume( dims, size );
00232   volume->SetOffset( offset );
00233   for ( int i = 0; i < dims[axis]; ++i )
00234     {
00235     ScalarImage::SmartPtr slice( this->GetOrthoSlice( axis, idx + i * factor ) );
00236     volume->SetOrthoSlice( axis, i, slice );
00237     }
00238   
00239   volume->m_MetaInformation = this->m_MetaInformation;
00240 
00241   volume->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix;
00242   // update coordinate offset according to sub-volume index
00243   for ( int i = 0; i < 3; ++i )
00244     volume->m_IndexToPhysicalMatrix[3][i] += idx * volume->m_IndexToPhysicalMatrix[axis][i];
00245   // scale direction vector along axis
00246   for ( int i = 0; i < 3; ++i )
00247     volume->m_IndexToPhysicalMatrix[axis][i] *= factor;
00248 
00249   if ( this->GetData()->GetPaddingFlag() )
00250     {
00251     volume->GetData()->SetPaddingValue( this->GetData()->GetPaddingValue() );
00252     }
00253   
00254   return volume;
00255 }
00256 
00257 UniformVolume* 
00258 UniformVolume::GetInterleavedPaddedSubVolume
00259 ( const int axis, const int factor, const int idx ) const
00260 {
00261   int sDims = this->m_Dims[axis] / factor;
00262   if ( this->m_Dims[axis] % factor > idx )
00263     ++sDims;
00264 
00265   UniformVolume* volume = new UniformVolume( this->m_Dims, this->Size );
00266   (volume->CreateDataArray( this->GetData()->GetType() ))->Fill( 0.0 );
00267   volume->SetOffset( this->m_Offset );
00268   for ( int i = 0; i < sDims; ++i )
00269     {
00270     const size_t sliceIdx = idx + i * factor;
00271     ScalarImage::SmartPtr slice( this->GetOrthoSlice( axis, sliceIdx ) );
00272     volume->SetOrthoSlice( axis, sliceIdx, slice );
00273     }
00274   
00275   volume->m_MetaInformation = this->m_MetaInformation;
00276   volume->m_IndexToPhysicalMatrix = this->m_IndexToPhysicalMatrix;
00277   return volume;
00278 }
00279 
00280 const UniformVolume::RegionType
00281 UniformVolume::GetGridRange
00282 ( const Self::CoordinateVectorType& fromVOI, const Self::CoordinateVectorType& toVOI ) const
00283 {
00284   Self::IndexType from, to;
00285 
00286   for ( size_t i = 0; i < 3; ++i )
00287     {
00288     from[i] = std::max<IndexType::ValueType>( 0, static_cast<IndexType::ValueType>( (fromVOI[i]-this->m_Offset[i]) / this->m_Delta[i] ) );
00289     to[i] = 1+std::min( this->m_Dims[i]-1, 1+static_cast<IndexType::ValueType>( (toVOI[i]-this->m_Offset[i]) / this->m_Delta[i] ) );
00290     }
00291 
00292   return UniformVolume::RegionType( from, to );
00293 }
00294 
00295 void
00296 UniformVolume::Mirror ( const int axis )
00297 {
00298   this->DataGrid::ApplyMirrorPlane( axis );
00299   
00300   this->CropRegion().From()[ axis ] = this->m_Dims[ axis ] - 1 - this->CropRegion().From()[ axis ];
00301   this->CropRegion().To()[ axis ] = this->m_Dims[ axis ] - 1 - this->CropRegion().To()[ axis ];
00302 }
00303 
00304 ScalarImage*
00305 UniformVolume::GetOrthoSlice
00306 ( const int axis, const unsigned int plane ) const
00307 {
00308   ScalarImage* sliceImage = DataGrid::GetOrthoSlice( axis, plane );
00309   sliceImage->SetImageSlicePosition( this->GetPlaneCoord( axis, plane ) );
00310 
00311   Self::CoordinateVectorType imageOffset( Self::CoordinateVectorType::Init( 0 ) );
00312   Self::CoordinateVectorType directionX( Self::CoordinateVectorType::Init( 0 ) );
00313   Self::CoordinateVectorType directionY( Self::CoordinateVectorType::Init( 0 ) );
00314   switch ( axis ) 
00315     {
00316     case AXIS_X:
00317       sliceImage->SetPixelSize( this->GetDelta( AXIS_Y ), this->GetDelta( AXIS_Z ) );
00318       imageOffset[0] = this->GetPlaneCoord( AXIS_X, plane );
00319       directionX[1] = 1;
00320       directionY[2] = 1;
00321       break;
00322     case AXIS_Y:
00323       sliceImage->SetPixelSize( this->GetDelta( AXIS_X ), this->GetDelta( AXIS_Z ) );
00324       imageOffset[1] = this->GetPlaneCoord( AXIS_X, plane );
00325       directionX[0] = 1;
00326       directionY[2] = 1;
00327       break;
00328     case AXIS_Z:
00329       sliceImage->SetPixelSize( this->GetDelta( AXIS_X ), this->GetDelta( AXIS_Y ) );
00330       imageOffset[2] = this->GetPlaneCoord( AXIS_X, plane );
00331       directionX[0] = 1;
00332       directionY[1] = 1;
00333       break;
00334     }
00335 
00336   sliceImage->SetImageDirectionX( directionX );
00337   sliceImage->SetImageDirectionY( directionY );
00338   
00339   sliceImage->SetImageOrigin( imageOffset );
00340   return sliceImage;
00341 }
00342 
00343 UniformVolume*
00344 UniformVolume::ExtractSliceVirtual
00345 ( const int axis, const int plane ) const
00346 {
00347   DataGrid::SmartPtr sliceGrid( this->DataGrid::ExtractSliceVirtual( axis, plane ) );
00348   Self* sliceVolume = new Self( sliceGrid->m_Dims, this->m_Delta[0], this->m_Delta[1], this->m_Delta[2], sliceGrid->m_Data );
00349 
00350   sliceVolume->m_Offset = this->m_Offset;
00351   sliceVolume->m_Offset[axis] += plane * this->m_Delta[axis];
00352 
00353   return sliceVolume;
00354 }
00355 
00356 ScalarImage*
00357 UniformVolume::GetNearestOrthoSlice
00358 ( const int axis, const Types::Coordinate location ) const
00359 {
00360   return this->GetOrthoSlice( axis, this->GetCoordIndex( axis, location ) );
00361 }
00362 
00363 ScalarImage*
00364 UniformVolume::GetOrthoSliceInterp
00365 ( const int axis, const Types::Coordinate location ) const
00366 {
00367   unsigned int baseSliceIndex = this->GetCoordIndex( axis, location );
00368 
00369   const Types::Coordinate baseSliceLocation = this->GetPlaneCoord( axis, baseSliceIndex );
00370   const Types::Coordinate nextSliceLocation = this->GetPlaneCoord( axis, baseSliceIndex+1 ); 
00371 
00372   // only bother to interpolate if we're more than 1% towards the next slice.
00373   if ( ((location - baseSliceLocation) / (nextSliceLocation-baseSliceLocation )) < 0.01 )
00374     return this->GetOrthoSlice( axis, baseSliceIndex );
00375 
00376   // only bother to interpolate if we're more than 1% towards the next slice.
00377   if ( ((nextSliceLocation - location) / (nextSliceLocation-baseSliceLocation )) < 0.01 )
00378     return this->GetOrthoSlice( axis, baseSliceIndex+1 );
00379 
00380   ScalarImage* image0 = this->GetOrthoSlice( axis, baseSliceIndex );
00381   ScalarImage* image1 = this->GetOrthoSlice( axis, baseSliceIndex+1 );
00382 
00383   TypedArray::SmartPtr data0 = image0->GetPixelData();
00384   TypedArray::SmartPtr data1 = image1->GetPixelData();
00385 
00386   Types::Coordinate weight0 = (nextSliceLocation - location) / (nextSliceLocation - baseSliceLocation );
00387 
00388   Types::DataItem value0, value1;
00389   for ( unsigned int idx = 0; idx < data0->GetDataSize(); ++idx ) 
00390     {
00391     if ( data0->Get( value0, idx ) && data1->Get( value1, idx ) ) 
00392       {
00393       data0->Set( weight0 * value0 + (1-weight0) * value1, idx );
00394       } 
00395     else
00396       {
00397       data0->SetPaddingAt( idx );
00398       }
00399     }
00400   delete image1;
00401   
00402   image0->SetImageSlicePosition( location );
00403   image0->SetImageOrigin( weight0 * image0->GetImageOrigin() + (1-weight0) * image1->GetImageOrigin() );
00404   
00405   return image0;
00406 }
00407 
00408 void
00409 UniformVolume
00410 ::GetPrincipalAxes( Matrix3x3<Types::Coordinate>& directions, Self::CoordinateVectorType& centerOfMass ) const
00411 {
00412   centerOfMass = this->GetCenterOfMass();
00413   const Types::Coordinate xg = centerOfMass[0];
00414   const Types::Coordinate yg = centerOfMass[1];
00415   const Types::Coordinate zg = centerOfMass[2];
00416 
00417   Matrix3x3<Types::Coordinate> inertiaMatrix;
00418 
00419   Types::DataItem ixx = 0, iyy = 0, izz = 0, ixy = 0, iyz = 0, izx = 0;
00420   for ( int k = 0; k < this->m_Dims[2]; ++k )
00421     {
00422     const Types::Coordinate Dz = this->GetPlaneCoord( AXIS_Z, k ) - zg;
00423     const Types::Coordinate Dz2 = Dz * Dz;
00424     for ( int j = 0; j < this->m_Dims[1]; ++j )
00425       {
00426       const Types::Coordinate Dy = this->GetPlaneCoord( AXIS_Y, j ) - yg;
00427       const Types::Coordinate Dy2 = Dy * Dy;
00428       for ( int i = 0; i < this->m_Dims[0]; ++i )
00429         {
00430         const Types::Coordinate Dx = this->GetPlaneCoord( AXIS_X, i ) - xg;
00431         const Types::Coordinate Dx2 = Dx * Dx;
00432 
00433         Types::DataItem v;
00434         if ( this->GetDataAt( v, i, j, k ) )
00435           {
00436           ixx += v * ( Dy2 + Dz2 );
00437           iyy += v * ( Dz2 + Dx2 );
00438           izz += v * ( Dx2 + Dy2 );
00439           
00440           ixy += v * Dx * Dy;
00441           iyz += v * Dy * Dz;
00442           izx += v * Dz * Dx;
00443           }
00444         }
00445       }
00446     }
00447   inertiaMatrix[0][0] = ixx;
00448   inertiaMatrix[0][1] = -ixy;
00449   inertiaMatrix[0][2] = -izx;
00450   
00451   inertiaMatrix[1][0] = -ixy;
00452   inertiaMatrix[1][1] = iyy;
00453   inertiaMatrix[1][2] = -iyz;
00454   
00455   inertiaMatrix[2][0] = -izx;
00456   inertiaMatrix[2][1] = -iyz;
00457   inertiaMatrix[2][2] = izz;
00458 
00459   const EigenSystemSymmetricMatrix3x3<Types::Coordinate> eigensystem( inertiaMatrix );
00460   for ( int n = 0; n < 3; ++n )
00461     {
00462     const Self::CoordinateVectorType v = eigensystem.GetNthEigenvector( n );
00463     for ( int i = 0; i < 3; ++i )
00464       {
00465       directions[n][i] = v[i];
00466       }
00467     }
00468   
00469   // correct for negative determinant
00470   const Types::Coordinate det = directions.Determinant();
00471   for ( int i = 0; i < 3; i++ )
00472     {
00473     directions[2][i] *= det;
00474     }
00475    
00476   for ( int i = 0; i < 3; i++ )
00477     {
00478     const Types::Coordinate norm = sqrt( directions[i][0]*directions[i][0] + directions[i][1]*directions[i][1] + directions[i][2]*directions[i][2] );
00479     for ( int j = 0; j < 3; j++ )
00480       {
00481       directions[i][j] /= norm;
00482       }
00483     }
00484 }
00485 
00486 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines