00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "cmtkVolumeFromSlices.h"
00034
00035 #include <System/cmtkProgress.h>
00036
00037 #include <Base/cmtkVolume.h>
00038 #include <Base/cmtkUniformVolume.h>
00039 #include <Base/cmtkAffineXform.h>
00040 #include <Base/cmtkMathUtil.h>
00041 #include <Base/cmtkAnatomicalOrientation.h>
00042
00043 #include <math.h>
00044
00045 namespace
00046 cmtk
00047 {
00048
00051
00052 void
00053 VolumeFromSlices::InitSequence
00054 ( const ScalarImage* image, const unsigned int numberOfSlices )
00055 {
00056 Padding = false;
00057
00058 Spacing[0] = image->GetPixelSize( AXIS_X );
00059 Spacing[1] = image->GetPixelSize( AXIS_Y );
00060
00061 ImagePosition = image->GetImageOrigin();
00062
00063 Dims[0] = image->GetDims()[AXIS_X];
00064 Dims[1] = image->GetDims()[AXIS_Y];
00065 Dims[2] = numberOfSlices;
00066
00067 BytesPerPixel = image->GetPixelData()->GetItemSize();
00068 DataType = image->GetPixelData()->GetType();
00069
00070 DataSize = Dims[0] * Dims[1] * Dims[2];
00071
00072 VolumeDataArray = TypedArray::Create( image->GetPixelData()->GetType(), DataSize );
00073
00074
00075 for ( unsigned int idx = 0; idx<3; ++idx )
00076 Points[idx] = Memory::AllocateArray<Types::Coordinate>( Dims[idx] );
00077
00078
00079 for ( unsigned int dim=0; dim<2; ++dim )
00080 {
00081 for ( int idx=0; idx < Dims[dim]; ++idx )
00082 {
00083 Points[dim][idx] = idx * Spacing[dim];
00084 }
00085
00086 Size[dim] = (Dims[dim]-1) * Spacing[dim];
00087 }
00088 }
00089
00090 char*
00091 VolumeFromSlices::AllocDataArray
00092 ( const int bytesPerPixel, const int dataSize ) const
00093 {
00094 return Memory::AllocateArray<char>( bytesPerPixel * dataSize );
00095 }
00096
00097 TypedArray::SmartPtr
00098 VolumeFromSlices::EncapDataArray ( const ScalarDataType dtype, void *const data, const int data_size )
00099 const
00100 {
00101 return TypedArray::Create( dtype, data, data_size, true , Padding, &PaddingValue );
00102 }
00103
00104 const char*
00105 VolumeFromSlices::FillPlane
00106 ( unsigned int& plane, const ScalarImage* image )
00107 {
00108 char* rawDataPtr = static_cast<char*>( VolumeDataArray->GetDataPtr() );
00109
00110 const size_t bytesPerBlock = BytesPerPixel * Dims[0] * Dims[1];
00111 for ( int planeIdx = 0; planeIdx < image->GetNumberOfFrames(); ++planeIdx, ++plane )
00112 {
00113 const char *check = this->CheckImage( plane, image, planeIdx );
00114 if ( check ) return check;
00115
00116 memcpy( rawDataPtr + bytesPerBlock * plane, image->GetPixelData()->GetDataPtr(), bytesPerBlock );
00117
00118
00119 Types::Coordinate slicePosition = (ImagePosition - FirstImagePosition).RootSumOfSquares();
00120 slicePosition = 1e-6 * ( MathUtil::Round( 1e+6 * slicePosition) );
00121 Points[2][plane] = slicePosition;
00122 }
00123
00124 return NULL;
00125 }
00126
00127 UniformVolume::SmartPtr
00128 VolumeFromSlices::FinishVolume ( Types::Coordinate& sliceOffset, int& sliceDirection )
00129 {
00130 Types::Coordinate *next_point = Points[2];
00131
00132 sliceOffset = next_point[0];
00133 sliceDirection = MathUtil::Sign(next_point[1]-sliceOffset);
00134
00135 Types::Coordinate previous_plane = sliceOffset;
00136
00137
00138
00139 *next_point = 0;
00140 int idx;
00141 for ( idx=1, ++next_point; idx < Dims[2]; ++idx, ++next_point )
00142 {
00143 Types::Coordinate next_plane = *next_point;
00144 (*next_point) = *(next_point-1)+fabs(next_plane-previous_plane);
00145 previous_plane = next_plane;
00146 }
00147
00148 Size[2] = *(next_point-1);
00149
00150
00151
00152 if ( !VolumeDataArray )
00153 VolumeDataArray = TypedArray::SmartPtr( this->EncapDataArray( SelectDataTypeInteger( BytesPerPixel, SignBit ), RawData, DataSize ) );
00154
00155 const Types::Coordinate* aux[] = { Points[0], Points[1], Points[2] };
00156 UniformVolume::SmartPtr Result = this->ConstructVolume( Dims, Size, aux, VolumeDataArray );
00157
00158
00159 VolumeDataArray = TypedArray::SmartPtr::Null;
00160
00161 for ( idx = 0; idx<3; ++idx )
00162 Memory::DeleteArray( Points[idx] );
00163
00164 Result->m_MetaInformation[META_SPACE] = Result->m_MetaInformation[META_SPACE_ORIGINAL] = "LPS";
00165
00166
00167 const Types::Coordinate spacing[3] = { (Size[0] / (Dims[0]-1)), (Size[1] / (Dims[1]-1)), (Size[2] / (Dims[2]-1)) };
00168
00169 this->ImageOrientation[0] *= spacing[0] / this->ImageOrientation[0].RootSumOfSquares();
00170 this->ImageOrientation[1] *= spacing[1] / this->ImageOrientation[1].RootSumOfSquares();
00171 this->IncrementVector *= spacing[2] / this->IncrementVector.RootSumOfSquares();
00172
00173 const Types::Coordinate directions[3][3] =
00174 {
00175 { this->ImageOrientation[0][0], this->ImageOrientation[0][1], this->ImageOrientation[0][2] },
00176 { this->ImageOrientation[1][0], this->ImageOrientation[1][1], this->ImageOrientation[1][2] },
00177 { this->IncrementVector[0], this->IncrementVector[1], this->IncrementVector[2] }
00178 };
00179
00180 const Matrix3x3<Types::Coordinate> m3( directions );
00181 Matrix4x4<Types::Coordinate> m4( m3 );
00182 for ( int i = 0; i < 3; ++i )
00183 m4[3][i] = this->FirstImagePosition[i];
00184
00185 Result->m_IndexToPhysicalMatrix = m4;
00186 const std::string orientationString0 = Result->GetOrientationFromDirections();
00187 Result->ChangeCoordinateSpace( AnatomicalOrientation::ORIENTATION_STANDARD );
00188
00189 const std::string orientationString = Result->GetOrientationFromDirections();
00190 Result->m_MetaInformation[META_SPACE_UNITS_STRING] = "mm";
00191 Result->m_MetaInformation[META_IMAGE_ORIENTATION] = orientationString;
00192 Result->m_MetaInformation[META_IMAGE_ORIENTATION_ORIGINAL] = orientationString;
00193
00194 return Result;
00195 }
00196
00197 const char*
00198 VolumeFromSlices::CheckImage
00199 ( const int plane, const ScalarImage* image, const unsigned int frame )
00200 {
00201 if ( ( this->Dims[0] != image->GetDims()[AXIS_X] ) || ( this->Dims[1] != image->GetDims()[AXIS_Y] ) )
00202 return "Image size mismatch";
00203
00204 if ( ( fabs( image->GetPixelSize( AXIS_X ) - Spacing[0] ) > CMTK_MAX_CALIB_ERROR ) ||
00205 ( fabs( image->GetPixelSize( AXIS_Y ) - Spacing[1] ) > CMTK_MAX_CALIB_ERROR ) )
00206 return "Calibration mismatch";
00207
00208
00209 if ( plane == 0 )
00210 {
00211 FirstImagePosition = ImagePosition = image->GetImageOrigin( frame );
00212 ImageOrientation[0] = image->GetImageDirectionX();
00213 ImageOrientation[1] = image->GetImageDirectionY();
00214 return NULL;
00215 }
00216
00217
00218 for ( unsigned int dim = 0; dim<3; ++dim )
00219 {
00220 if ( ( fabs( ImageOrientation[0][dim] - image->GetImageDirectionX()[dim] ) > CMTK_MAX_CALIB_ERROR ) ||
00221 ( fabs( ImageOrientation[1][dim] - image->GetImageDirectionY()[dim] ) > CMTK_MAX_CALIB_ERROR ) )
00222 return "Non-parallel image planes";
00223 }
00224
00225
00226 ScalarImage::SpaceVectorType imageToImage = image->GetImageOrigin( frame ) - ImagePosition;
00227
00228 if ( imageToImage.MaxAbsValue() < CMTK_MAX_LOCALIZE_ERROR )
00229 {
00230 StdErr.printf( "Two slices at position (%f,%f,%f)\n", (float)ImagePosition[0], (float)ImagePosition[1], (float)ImagePosition[2] );
00231 return "Encountered two slices in identical location.";
00232 }
00233 else
00234 imageToImage /= imageToImage.MaxAbsValue();
00235
00236
00237
00238 const Types::Coordinate scalarX = fabs( imageToImage * ImageOrientation[0] );
00239 const Types::Coordinate scalarY = fabs( imageToImage * ImageOrientation[1] );
00240 if ( (scalarX > CMTK_MAX_ANGLE_ERROR) || (scalarY > CMTK_MAX_ANGLE_ERROR) )
00241 {
00242 fprintf( stderr, "errX = %f, errY = %f, thresh = %f\n", scalarX, scalarY, CMTK_MAX_ANGLE_ERROR );
00243 return "Data grid must be orthogonal.";
00244 }
00245
00246
00247 if ( plane == 1 )
00248 IncrementVector = imageToImage;
00249
00250 else
00251 {
00252
00253 if ( (imageToImage - IncrementVector).MaxAbsValue() > CMTK_MAX_LOCALIZE_ERROR )
00254 {
00255
00256 if ( ( (imageToImage * IncrementVector) > 0 ) )
00257
00258 return "Field-of-view mismatch";
00259 else
00260
00261 return "Encountered altering slice direction.";
00262 }
00263 }
00264
00265
00266 ImagePosition = image->GetImageOrigin( frame );
00267
00268 return NULL;
00269 }
00270
00271 UniformVolume::SmartPtr
00272 VolumeFromSlices::ConstructVolume
00273 ( const DataGrid::IndexType& dims, const UniformVolume::CoordinateVectorType& size, const Types::Coordinate *points[3], TypedArray::SmartPtr& data ) const
00274 {
00275 bool isUniform = true;
00276 Types::Coordinate error = 0;
00277 for ( unsigned int dim=0; (dim<3) && isUniform; ++dim )
00278 {
00279 Types::Coordinate delta = points[dim][1] - points[dim][0];
00280 for ( int idx=2; (idx<dims[dim]) && isUniform; ++idx )
00281 {
00282 if ( fabs( delta - (points[dim][idx] - points[dim][idx-1]) ) > (0.0001 * delta ) )
00283 isUniform = false;
00284 error = fabs( delta - (points[dim][idx] - points[dim][idx-1]) );
00285 }
00286 }
00287
00288 if ( !isUniform )
00289 {
00290 StdErr << "WARNING: not a uniform volume (error = " << error << ")\n";
00291 }
00292 return UniformVolume::SmartPtr( new UniformVolume( dims, size, data ) );
00293 }
00294
00295 }