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
00034
00035
00036 #include <Registration/cmtkGroupwiseRegistrationFunctionalBase.h>
00037
00038 #include <Base/cmtkMathUtil.h>
00039 #include <IO/cmtkVolumeIO.h>
00040 #include <System/cmtkConsole.h>
00041 #include <System/cmtkThreadPool.h>
00042
00043 #include <Base/cmtkAnatomicalOrientation.h>
00044
00045 #include <Base/cmtkInterpolator.h>
00046 #include <Base/cmtkTypedArrayFunctionHistogramMatching.h>
00047 #include <Base/cmtkUniformVolumeFilter.h>
00048 #include <Registration/cmtkReformatVolume.h>
00049
00050 #ifdef CMTK_BUILD_MPI
00051 # include <mpi.h>
00052 # include <IO/cmtkMPI.h>
00053 #endif
00054
00055 namespace
00056 cmtk
00057 {
00058
00061
00062 GroupwiseRegistrationFunctionalBase
00063 ::GroupwiseRegistrationFunctionalBase()
00064 : m_FreeAndRereadImages( false ),
00065 m_ForceZeroSum( false ),
00066 m_ForceZeroSumFirstN( 0 ),
00067 m_ActiveImagesFrom( 0 ),
00068 m_ActiveImagesTo( 0 ),
00069 m_ActiveXformsFrom( 0 ),
00070 m_ActiveXformsTo( 0 ),
00071 m_TemplateNumberOfPixels( 0 ),
00072 m_ProbabilisticSampleDensity( -1.0 ),
00073 m_ProbabilisticSampleUpdatesAfter( 1000000 ),
00074 m_ProbabilisticSampleUpdatesSince( 0 ),
00075 m_GaussianSmoothImagesSigma( -1.0 ),
00076 m_UserBackgroundValue( 0 ),
00077 m_UserBackgroundFlag( false ),
00078 m_ParametersPerXform( 0 ),
00079 m_RepeatIntensityHistogramMatching( false )
00080 {
00081 this->m_NumberOfThreads = ThreadPool::GetGlobalThreadPool().GetNumberOfThreads();
00082 this->m_NumberOfTasks = 4 * this->m_NumberOfThreads - 3;
00083
00084 this->m_Data.clear();
00085 #ifdef CMTK_BUILD_MPI
00086 this->m_RankMPI = MPI::COMM_WORLD.Get_rank();
00087 this->m_SizeMPI = MPI::COMM_WORLD.Get_size();
00088 #endif
00089 }
00090
00091 GroupwiseRegistrationFunctionalBase::~GroupwiseRegistrationFunctionalBase()
00092 {
00093 if ( this->m_Data.size() )
00094 {
00095 const size_t numberOfImages = this->m_ImageVector.size();
00096 for ( size_t i = 0; i < numberOfImages; ++i )
00097 {
00098 if ( this->m_Data[i] )
00099 Memory::DeleteArray( this->m_Data[i] );
00100 }
00101 }
00102 }
00103
00104 void
00105 GroupwiseRegistrationFunctionalBase::CreateTemplateGridFromTargets
00106 ( const std::vector<UniformVolume::SmartPtr>& targets, const int downsample )
00107 {
00108 Types::Coordinate templateSize[3] = {0,0,0};
00109 UniformVolume::IndexType templateDims;
00110 Types::Coordinate templateDelta = 1e10;
00111
00112 for ( size_t i = 0; i < targets.size(); ++i )
00113 {
00114 for ( int dim = 0; dim < 3; ++dim )
00115 {
00116 templateSize[dim] = std::max( templateSize[dim], targets[i]->Size[dim] );
00117 }
00118 templateDelta = std::min( templateDelta, targets[i]->GetMinDelta() );
00119 }
00120
00121 for ( int dim = 0; dim < 3; ++dim )
00122 {
00123 templateDims[dim] = 1 + static_cast<int>( templateSize[dim] / templateDelta );
00124 templateSize[dim] = (templateDims[dim]-1) * templateDelta;
00125 }
00126
00127 UniformVolume::SmartPtr templateGrid( new UniformVolume( templateDims, FixedVector<3,Types::Coordinate>( templateSize ) ) );
00128 this->SetTemplateGrid( templateGrid, downsample );
00129 }
00130
00131 void
00132 GroupwiseRegistrationFunctionalBase::CreateTemplateGrid
00133 ( const DataGrid::IndexType& dims, const UniformVolume::CoordinateVectorType& deltas )
00134 {
00135 UniformVolume::SmartPtr templateGrid( new UniformVolume( dims, deltas ) );
00136 this->SetTemplateGrid( templateGrid );
00137 }
00138
00139 void
00140 GroupwiseRegistrationFunctionalBase::SetTemplateGrid
00141 ( UniformVolume::SmartPtr& templateGrid,
00142 const int downsample,
00143 const bool useTemplateData )
00144 {
00145 this->m_TemplateGrid = templateGrid->Clone();
00146 this->m_UseTemplateData = useTemplateData;
00147
00148 if ( this->m_UseTemplateData && ! this->m_TemplateGrid->GetData() )
00149 {
00150 UniformVolume::SmartPtr readImage( VolumeIO::ReadOriented( templateGrid->m_MetaInformation[META_FS_PATH].c_str(), false ) );
00151 this->m_TemplateGrid->SetData( readImage->GetData() );
00152 }
00153
00154 if ( ! this->m_TemplateGrid->MetaKeyExists( META_IMAGE_ORIENTATION ) )
00155 {
00156 this->m_TemplateGrid->m_MetaInformation[META_IMAGE_ORIENTATION] = AnatomicalOrientation::ORIENTATION_STANDARD;
00157 }
00158 if ( ! this->m_TemplateGrid->MetaKeyExists( META_IMAGE_ORIENTATION_ORIGINAL ) )
00159 {
00160 this->m_TemplateGrid->m_MetaInformation[META_IMAGE_ORIENTATION_ORIGINAL] = AnatomicalOrientation::ORIENTATION_STANDARD;
00161 }
00162 if ( ! this->m_TemplateGrid->MetaKeyExists( META_SPACE ) )
00163 {
00164 this->m_TemplateGrid->SetMetaInfo( META_SPACE, AnatomicalOrientation::ORIENTATION_STANDARD );
00165 }
00166 if ( ! this->m_TemplateGrid->MetaKeyExists( META_SPACE_ORIGINAL ) )
00167 {
00168 this->m_TemplateGrid->SetMetaInfo( META_SPACE_ORIGINAL, AnatomicalOrientation::ORIENTATION_STANDARD );
00169 }
00170
00171 if ( this->m_UseTemplateData )
00172 {
00173 this->m_TemplateGrid = UniformVolume::SmartPtr( this->PrepareSingleImage( this->m_TemplateGrid ) );
00174 }
00175
00176 if ( downsample > 1 )
00177 {
00178 this->m_TemplateGrid = UniformVolume::SmartPtr( this->m_TemplateGrid->GetDownsampledAndAveraged( downsample, true ) );
00179 }
00180 this->m_TemplateNumberOfPixels = this->m_TemplateGrid->GetNumberOfPixels();
00181
00182 if ( this->m_UseTemplateData )
00183 {
00184 this->CopyTemplateData();
00185 }
00186
00187 this->PrepareTargetImages();
00188 }
00189
00190
00191 void
00192 GroupwiseRegistrationFunctionalBase
00193 ::AllocateStorage()
00194 {
00195 if ( !this->m_TemplateGrid )
00196 {
00197 StdErr << "FATAL: must set template grid for groupwise registration before allocating storage\n";
00198 exit( 1 );
00199 }
00200
00201 const size_t numberOfImages = this->m_OriginalImageVector.size();
00202
00203 if ( this->m_TemplateNumberOfPixels )
00204 {
00205 if ( (this->m_ProbabilisticSampleDensity > 0) && (this->m_ProbabilisticSampleDensity < 1) )
00206 this->m_TemplateNumberOfSamples = static_cast<size_t>( this->m_ProbabilisticSampleDensity * this->m_TemplateNumberOfPixels);
00207 else
00208 this->m_TemplateNumberOfSamples = this->m_TemplateNumberOfPixels;
00209
00210 if ( this->m_Data.size() )
00211 {
00212 for ( size_t i = 0; i < numberOfImages; ++i )
00213 {
00214 if ( this->m_Data[i] )
00215 Memory::DeleteArray( this->m_Data[i] );
00216 }
00217 }
00218
00219 this->m_Data.resize( numberOfImages );
00220 for ( size_t i = 0; i < numberOfImages; ++i )
00221 {
00222 this->m_Data[i] = Memory::AllocateArray<byte>( this->m_TemplateNumberOfSamples );
00223 }
00224
00225 this->m_TempData.resize( this->m_TemplateNumberOfSamples );
00226 }
00227 }
00228
00229 void
00230 GroupwiseRegistrationFunctionalBase
00231 ::SetTargetImages
00232 ( std::vector<UniformVolume::SmartPtr>& tImages )
00233 {
00234 this->m_OriginalImageVector = tImages;
00235
00236 this->m_ActiveImagesFrom = 0;
00237 this->m_ActiveImagesTo = tImages.size();
00238
00239 this->m_ActiveXformsFrom = 0;
00240 this->m_ActiveXformsTo = tImages.size();
00241
00242 this->m_ProbabilisticSampleUpdatesSince = 0;
00243 }
00244
00245 UniformVolume::SmartPtr
00246 GroupwiseRegistrationFunctionalBase
00247 ::PrepareSingleImage( UniformVolume::SmartPtr& image )
00248 {
00249 if ( !image->GetData() )
00250 {
00251 UniformVolume::SmartPtr readImage( VolumeIO::ReadOriented( image->m_MetaInformation[META_FS_PATH].c_str(), false ) );
00252 image->SetData( readImage->GetData() );
00253 }
00254
00255 TypedArray::SmartPtr data;
00256 if ( this->m_GaussianSmoothImagesSigma > 0 )
00257 {
00258 data = UniformVolumeFilter( image ).GetDataGaussFiltered( Units::GaussianSigma( this->m_GaussianSmoothImagesSigma * this->m_TemplateGrid->GetMinDelta() ) );
00259
00260 if ( this->m_FreeAndRereadImages )
00261 {
00262 image->SetData( TypedArray::SmartPtr::Null );
00263 }
00264 }
00265 else
00266 {
00267 if ( this->m_FreeAndRereadImages )
00268 {
00269 data = image->GetData();
00270 image->SetData( TypedArray::SmartPtr::Null );
00271 }
00272 else
00273 {
00274 data = image->GetData()->Clone();
00275 }
00276 }
00277
00278 UniformVolume::SmartPtr newTargetImage = image->CloneGrid();
00279 newTargetImage->SetData( data );
00280 return newTargetImage;
00281 }
00282
00283 void
00284 GroupwiseRegistrationFunctionalBase
00285 ::PrepareTargetImages()
00286 {
00287 this->m_ImageVector.resize( this->m_OriginalImageVector.size() );
00288 for ( size_t i = 0; i < this->m_OriginalImageVector.size(); ++i )
00289 {
00290 this->m_ImageVector[i] = this->PrepareSingleImage( this->m_OriginalImageVector[i] );
00291 }
00292 }
00293
00294 void
00295 GroupwiseRegistrationFunctionalBase::GetParamVector( CoordinateVector& v )
00296 {
00297 v.SetDim( this->ParamVectorDim() );
00298
00299 for ( size_t idx = 0; idx < this->m_XformVector.size(); ++idx )
00300 {
00301 this->m_XformVector[idx]->GetParamVector( v, idx * this->m_ParametersPerXform );
00302 }
00303 }
00304
00305 void
00306 GroupwiseRegistrationFunctionalBase::SetParamVector( CoordinateVector& v )
00307 {
00308 size_t offset = 0;
00309 for ( size_t xIdx = 0; xIdx < this->m_XformVector.size(); ++xIdx )
00310 {
00311 CoordinateVector vv( this->m_ParametersPerXform, v.Elements + offset, false );
00312 offset += this->m_ParametersPerXform;
00313 this->m_XformVector[xIdx]->SetParamVector( vv );
00314 }
00315 }
00316
00317 void
00318 GroupwiseRegistrationFunctionalBase::SetParamVector
00319 ( CoordinateVector& v, const size_t xformIdx )
00320 {
00321 const size_t offset = this->m_ParametersPerXform * xformIdx;
00322 CoordinateVector vv( this->m_ParametersPerXform, v.Elements + offset, false );
00323 this->m_XformVector[xformIdx]->SetParamVector( vv );
00324 }
00325
00326 void
00327 GroupwiseRegistrationFunctionalBase
00328 ::SetParameter( const size_t param, const Types::Coordinate value )
00329 {
00330 this->m_XformVector[param / this->m_ParametersPerXform]->SetParameter( param % this->m_ParametersPerXform, value );
00331 }
00332
00333 void
00334 GroupwiseRegistrationFunctionalBase
00335 ::SetParameter( const size_t xform, const size_t param, const Types::Coordinate value )
00336 {
00337 this->m_XformVector[xform]->SetParameter( param, value );
00338 }
00339
00340 GroupwiseRegistrationFunctionalBase::ReturnType
00341 GroupwiseRegistrationFunctionalBase::EvaluateAt( CoordinateVector& v )
00342 {
00343 if ( (this->m_ProbabilisticSampleDensity > 0) && (this->m_ProbabilisticSampleDensity < 1) )
00344 {
00345 if ( !this->m_ProbabilisticSampleUpdatesSince )
00346 this->UpdateProbabilisticSamples();
00347 (++this->m_ProbabilisticSampleUpdatesSince) %= this->m_ProbabilisticSampleUpdatesAfter;
00348 }
00349
00350 this->SetParamVector( v );
00351 this->InterpolateAllImages();
00352
00353 return this->Evaluate();
00354 }
00355
00356 GroupwiseRegistrationFunctionalBase::ReturnType
00357 GroupwiseRegistrationFunctionalBase::EvaluateWithGradient
00358 ( CoordinateVector& v, CoordinateVector& g, const Types::Coordinate step )
00359 {
00360 const Self::ReturnType baseValue = this->EvaluateAt( v );
00361
00362 for ( size_t param = 0; param < this->ParamVectorDim(); ++param )
00363 {
00364 g[param] = 0.0;
00365
00366 const size_t imageIndex = param / this->m_ParametersPerXform;
00367 const size_t paramIndex = param % this->m_ParametersPerXform;
00368
00369 const Types::Coordinate pStep = this->GetParamStep( param, step );
00370 if ( pStep > 0 )
00371 {
00372 byte* tmp = this->m_Data[imageIndex];
00373 this->m_Data[imageIndex] = &(this->m_TempData[0]);
00374
00375 const Types::Coordinate p0 = v[param];
00376
00377 this->SetParameter( imageIndex, paramIndex, p0 + pStep );
00378 this->InterpolateImage( imageIndex, this->m_Data[imageIndex] );
00379 const Self::ReturnType upper = this->Evaluate();
00380
00381 this->SetParameter( imageIndex, paramIndex, p0 - pStep );
00382 this->InterpolateImage( imageIndex, this->m_Data[imageIndex] );
00383 const Self::ReturnType lower = this->Evaluate();
00384
00385 this->m_Data[imageIndex] = tmp;
00386 this->SetParameter( imageIndex, paramIndex, p0 );
00387
00388 if ( (upper > baseValue) || (lower > baseValue) )
00389 {
00390 g[param] = (upper - lower);
00391 }
00392 }
00393 }
00394
00395 if ( this->m_ForceZeroSum )
00396 {
00397 this->ForceZeroSumGradient( g );
00398 }
00399
00400 return baseValue;
00401 }
00402
00403 void
00404 GroupwiseRegistrationFunctionalBase::UpdateProbabilisticSamples()
00405 {
00406 #ifdef CMTK_BUILD_MPI
00407 const size_t samplesPerNode = 1 + (this->m_TemplateNumberOfSamples / this->m_SizeMPI);
00408 std::vector<size_t> nodeSamples( samplesPerNode );
00409 this->m_ProbabilisticSamples.resize( samplesPerNode * this->m_SizeMPI );
00410 #else
00411 this->m_ProbabilisticSamples.resize( this->m_TemplateNumberOfSamples );
00412 #endif
00413
00414
00415 #ifdef CMTK_BUILD_MPI
00416 const size_t firstSample = 0;
00417 const size_t lastSample = samplesPerNode;
00418 #else
00419 const size_t firstSample = 0;
00420 const size_t lastSample = this->m_TemplateNumberOfSamples;
00421 #endif
00422
00423 for ( size_t i = firstSample; i < lastSample; ++i )
00424 {
00425 const size_t sample = static_cast<size_t>( this->m_TemplateNumberOfPixels * MathUtil::UniformRandom() );
00426 #ifdef CMTK_BUILD_MPI
00427 nodeSamples[i] = sample;
00428 #else
00429 this->m_ProbabilisticSamples[i] = sample;
00430 #endif
00431 }
00432
00433 #ifdef CMTK_BUILD_MPI
00434 MPI::COMM_WORLD.Allgather( &nodeSamples[0], sizeof( nodeSamples[0] ) * samplesPerNode, MPI::CHAR, &this->m_ProbabilisticSamples[0], sizeof( nodeSamples[0] ) * samplesPerNode, MPI::CHAR );
00435 #endif
00436
00437 #ifdef CMTK_BUILD_MPI
00438 this->m_ProbabilisticSamples.resize( this->m_TemplateNumberOfSamples );
00439 #endif
00440 }
00441
00442 void
00443 GroupwiseRegistrationFunctionalBase
00444 ::InterpolateAllImages()
00445 {
00446 #ifdef CMTK_BUILD_MPI
00447
00448 for ( size_t idx = this->m_ActiveImagesFrom + this->m_RankMPI; idx < this->m_ActiveImagesTo; idx += this->m_SizeMPI )
00449 {
00450 this->InterpolateImage( idx, this->m_Data[idx] );
00451 }
00452
00453
00454 for ( size_t idx = this->m_ActiveImagesFrom; idx < this->m_ActiveImagesTo; ++idx )
00455 {
00456 #ifdef DEBUG_COMM
00457 fprintf( stderr, "%d\tBroadcasting reformated image %d with root %d\n", (int)this->m_RankMPI, (int)idx, (int)((idx-this->m_ActiveImagesFrom) % this->m_SizeMPI) );
00458 #endif
00459 MPI::COMM_WORLD.Bcast( this->m_Data[idx], this->m_TemplateNumberOfSamples, MPI::CHAR, (idx - this->m_ActiveImagesFrom) % this->m_SizeMPI );
00460 }
00461 #else
00462 for ( size_t idx = this->m_ActiveImagesFrom; idx < this->m_ActiveImagesTo; ++idx )
00463 {
00464 this->InterpolateImage( idx, this->m_Data[idx] );
00465 }
00466 #endif
00467 }
00468
00469 void
00470 GroupwiseRegistrationFunctionalBase
00471 ::ForceZeroSumGradient( CoordinateVector& g ) const
00472 {
00473 const size_t numberOfXforms = this->m_XformVector.size();
00474 const size_t zeroSumFirstN = this->m_ForceZeroSumFirstN ? this->m_ForceZeroSumFirstN : numberOfXforms;
00475
00476 #pragma omp parallel for
00477 for ( size_t param = 0; param < this->m_ParametersPerXform; ++param )
00478 {
00479 Types::Coordinate avg = 0;
00480
00481 size_t pparam = param;
00482 for ( size_t idx = 0; idx < zeroSumFirstN; ++idx, pparam += this->m_ParametersPerXform )
00483 {
00484 avg += g[pparam];
00485 }
00486
00487 avg *= 1.0 / zeroSumFirstN;
00488
00489 pparam = param;
00490 for ( size_t idx = 0; idx < numberOfXforms; ++idx, pparam += this->m_ParametersPerXform )
00491 {
00492 g[pparam] -= avg;
00493 }
00494 }
00495
00496
00497 const Types::Coordinate gMax = g.MaxNorm();
00498 if ( gMax < 1e-3 )
00499 {
00500 g.Clear();
00501 }
00502 }
00503
00504 bool
00505 GroupwiseRegistrationFunctionalBase
00506 ::Wiggle()
00507 {
00508 bool wiggle = false;
00509
00510 if ( (this->m_ProbabilisticSampleDensity > 0) && (this->m_ProbabilisticSampleDensity < 1) )
00511 {
00512 this->m_ProbabilisticSampleUpdatesSince = 0;
00513 wiggle = true;
00514 }
00515
00516 if ( this->m_RepeatIntensityHistogramMatching )
00517 {
00518 TypedArray::SmartPtr referenceData = this->m_TemplateGrid->GetData();
00519 if ( !this->m_UseTemplateData )
00520 referenceData = TypedArray::SmartPtr::Null;
00521
00522 for ( size_t i = 0; i < this->m_OriginalImageVector.size(); ++i )
00523 {
00524 UniformVolume::SmartPtr scaledImage;
00525 if ( this->m_OriginalImageVector[i]->GetData() )
00526 {
00527 scaledImage = UniformVolume::SmartPtr( this->m_OriginalImageVector[i]->Clone( true ) );
00528 }
00529 else
00530 {
00531 scaledImage = UniformVolume::SmartPtr( VolumeIO::ReadOriented( this->m_OriginalImageVector[i]->m_MetaInformation[META_FS_PATH].c_str(), false ) );
00532 }
00533
00534 UniformVolume::SmartPtr reformatImage( this->GetReformattedImage( scaledImage, i ) );
00535 if ( referenceData )
00536 {
00537 scaledImage->GetData()->ApplyFunctionObject( TypedArrayFunctionHistogramMatching( *(reformatImage->GetData()), *referenceData ) );
00538 }
00539 else
00540 {
00541 referenceData = reformatImage->GetData();
00542 }
00543
00544 this->m_ImageVector[i] = UniformVolume::SmartPtr( this->PrepareSingleImage( scaledImage ) );
00545 }
00546 this->InterpolateAllImages();
00547 wiggle = true;
00548 }
00549
00550 return wiggle;
00551 }
00552
00553 void
00554 GroupwiseRegistrationFunctionalBase
00555 ::CopyTemplateData()
00556 {
00557 const TypedArray* dataArray = this->m_TemplateGrid->GetData();
00558
00559 if ( dataArray )
00560 {
00561 const size_t size = dataArray->GetDataSize();
00562 this->m_TemplateData.resize( size );
00563
00564 for ( size_t i = 0; i < size; ++i )
00565 {
00566 Types::DataItem value;
00567 if ( dataArray->Get( value, i ) )
00568 this->m_TemplateData[i] = static_cast<byte>( value );
00569 else
00570 this->m_TemplateData[i] = this->m_PaddingValue;
00571 }
00572 }
00573 }
00574
00575 void
00576 GroupwiseRegistrationFunctionalBase
00577 ::DebugWriteImages()
00578 {
00579 this->InterpolateAllImages();
00580 UniformVolume::SmartPtr writeVolume( this->m_TemplateGrid->CloneGrid() );
00581 writeVolume->CreateDataArray( TYPE_BYTE );
00582
00583 for ( size_t i = 0; i < this->m_TemplateNumberOfPixels; ++i )
00584 {
00585 writeVolume->SetDataAt( this->m_TemplateData[i], i );
00586 }
00587 VolumeIO::Write( *writeVolume, "template.nii", true );
00588
00589 for ( size_t n = 0; n < this->m_ImageVector.size(); ++n )
00590 {
00591 for ( size_t i = 0; i < this->m_TemplateNumberOfPixels; ++i )
00592 {
00593 writeVolume->SetDataAt( this->m_Data[n][i], i );
00594 }
00595
00596 char path[PATH_MAX];
00597 sprintf( path, "target%02d.nii", static_cast<int>( n ) );
00598 VolumeIO::Write( *writeVolume, path, true );
00599 }
00600 }
00601
00602 const UniformVolume::SmartPtr
00603 GroupwiseRegistrationFunctionalBase
00604 ::GetReformattedImage( const UniformVolume::SmartPtr& targetGrid, const size_t idx ) const
00605 {
00606 ReformatVolume reformat;
00607 reformat.SetInterpolation( Interpolators::LINEAR );
00608 reformat.SetReferenceVolume( targetGrid );
00609 reformat.SetFloatingVolume( this->m_OriginalImageVector[idx] );
00610
00611 reformat.SetWarpXform( WarpXform::SmartPtr::DynamicCastFrom( this->m_XformVector[idx] ) );
00612 reformat.SetAffineXform( AffineXform::SmartPtr::DynamicCastFrom( this->m_XformVector[idx] ) );
00613
00614 if ( this->m_UserBackgroundFlag )
00615 {
00616 reformat.SetPaddingValue( this->m_UserBackgroundValue );
00617 }
00618
00619 UniformVolume::SmartPtr result = reformat.PlainReformat();
00620
00621 if ( this->m_UserBackgroundFlag )
00622 {
00623 result->GetData()->ClearPaddingFlag();
00624 }
00625 return result;
00626 }
00627
00628 }