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 "cmtkCongealingFunctional.h"
00034
00035 #include <Base/cmtkMathUtil.h>
00036 #include <Registration/cmtkScaleHistogramValueTrait.h>
00037
00038 namespace
00039 cmtk
00040 {
00041
00044
00045 template<class TXform>
00046 CongealingFunctional<TXform>::CongealingFunctional()
00047 : m_NeedsUpdateStandardDeviationByPixel( true )
00048 {
00049 this->SetNumberOfHistogramBins( this->m_HistogramBins );
00050 }
00051
00052 template<class TXform>
00053 CongealingFunctional<TXform>::~CongealingFunctional()
00054 {
00055 for ( size_t idx = 0; idx < this->m_HistogramKernel.size(); ++idx )
00056 if ( this->m_HistogramKernel[idx] )
00057 Memory::DeleteArray( this->m_HistogramKernel[idx] );
00058 this->m_HistogramKernel.clear();
00059 }
00060
00061 template<class TXform>
00062 void
00063 CongealingFunctional<TXform>
00064 ::SetNumberOfHistogramBins( const size_t numberOfHistogramBins )
00065 {
00066 this->m_HistogramBins = numberOfHistogramBins;
00067 this->m_HistogramKernelRadiusMax = this->m_HistogramBins / 2;
00068 this->CreateGaussianKernels();
00069
00070 this->Superclass::SetNumberOfHistogramBins( numberOfHistogramBins );
00071 }
00072
00073 template<class TXform>
00074 void
00075 CongealingFunctional<TXform>::CreateGaussianKernels()
00076 {
00077 for ( size_t idx = 0; idx < this->m_HistogramKernel.size(); ++idx )
00078 if ( this->m_HistogramKernel[idx] )
00079 Memory::DeleteArray( this->m_HistogramKernel[idx] );
00080
00081 this->m_HistogramKernel.resize( this->m_HistogramKernelRadiusMax+1 );
00082 this->m_HistogramKernelRadius.resize( this->m_HistogramKernelRadiusMax+1 );
00083 for ( size_t idx = 0; idx <= this->m_HistogramKernelRadiusMax; ++idx )
00084 {
00085 const size_t radius = idx + 1;
00086 const double sigma = idx;
00087
00088 this->m_HistogramKernelRadius[idx] = radius;
00089 this->m_HistogramKernel[idx] = Memory::AllocateArray<HistogramBinType>( radius );
00090
00091 if ( idx < 1.0 )
00092 {
00093 this->m_HistogramKernel[idx][0] = cmtk::ScaleHistogramValueTrait<HistogramBinType>::Scale( 1.0 );
00094 for ( size_t i = 1; i < radius; ++i )
00095 this->m_HistogramKernel[idx][i] = cmtk::ScaleHistogramValueTrait<HistogramBinType>::Scale( 0.0 );
00096 }
00097 else
00098 {
00099 const double normFactor = 1.0/(sqrt(2*M_PI) * sigma);
00100 for ( size_t i = 0; i < radius; ++i )
00101 {
00102 this->m_HistogramKernel[idx][i] = cmtk::ScaleHistogramValueTrait<HistogramBinType>::Scale( normFactor * exp( -MathUtil::Square( 1.0 * i / sigma ) / 2 ) );
00103 }
00104 }
00105 }
00106 }
00107
00108 template<class TXform>
00109 void
00110 CongealingFunctional<TXform>::SetTemplateGrid
00111 ( UniformVolume::SmartPtr& templateGrid,
00112 const int downsample,
00113 const bool useTemplateData )
00114 {
00115 this->Superclass::SetTemplateGrid( templateGrid, downsample, useTemplateData );
00116 this->m_NeedsUpdateStandardDeviationByPixel = true;
00117 }
00118
00119 template<class TXform>
00120 typename CongealingFunctional<TXform>::ReturnType
00121 CongealingFunctional<TXform>::Evaluate()
00122 {
00123 if ( this->m_NeedsUpdateStandardDeviationByPixel )
00124 this->UpdateStandardDeviationByPixel();
00125
00126 double entropy = 0;
00127 unsigned int count = 0;
00128
00129 this->m_ThreadHistograms.resize( this->m_NumberOfThreads );
00130
00131 std::vector<EvaluateThreadParameters> params( this->m_NumberOfTasks );
00132 for ( size_t idx = 0; idx < this->m_NumberOfTasks; ++idx )
00133 params[idx].thisObject = this;
00134
00135 ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool();
00136 if ( this->m_ProbabilisticSamples.size() )
00137 threadPool.Run( Self::EvaluateProbabilisticThread, params );
00138 else
00139 threadPool.Run( Self::EvaluateThread, params );
00140
00141
00142 for ( size_t task = 0; task < this->m_NumberOfTasks; ++task )
00143 {
00144 entropy += params[task].m_Entropy;
00145 count += params[task].m_Count;
00146 }
00147
00148 #ifdef CMTK_BUILD_MPI
00149 double partialEntropy = entropy;
00150 MPI::COMM_WORLD.Allreduce( &partialEntropy, &entropy, 1, MPI::DOUBLE, MPI::SUM );
00151
00152 unsigned int partialCount = count;
00153 MPI::COMM_WORLD.Allreduce( &partialCount, &count, 1, MPI::UNSIGNED, MPI::SUM );
00154 #endif
00155
00156 if ( count )
00157 return static_cast<typename Self::ReturnType>( entropy / count );
00158 else
00159 return -FLT_MAX;
00160 }
00161
00162 template<class TXform>
00163 void
00164 CongealingFunctional<TXform>::EvaluateThread
00165 ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t )
00166 {
00167 EvaluateThreadParameters* threadParameters = static_cast<EvaluateThreadParameters*>( args );
00168
00169 Self* This = threadParameters->thisObject;
00170 const Self* ThisConst = threadParameters->thisObject;
00171
00172 HistogramType& histogram = This->m_ThreadHistograms[threadIdx];
00173 histogram.Resize( ThisConst->m_HistogramBins + 2 * ThisConst->m_HistogramKernelRadiusMax, false );
00174
00175 double entropy = 0;
00176 unsigned int count = 0;
00177
00178 const size_t numberOfPixels = ThisConst->m_TemplateNumberOfPixels;
00179 #ifdef CMTK_BUILD_MPI
00180 const size_t pixelsPerThread = 1+numberOfPixels / ( taskCnt * ThisConst->m_SizeMPI );
00181 const size_t pixelFrom = ( taskIdx + ThisConst->m_RankMPI * taskCnt ) * pixelsPerThread;
00182 const size_t pixelTo = std::min( numberOfPixels, pixelFrom + pixelsPerThread );
00183 #else
00184 const size_t pixelsPerThread = 1+(numberOfPixels / taskCnt);
00185 const size_t pixelFrom = taskIdx * pixelsPerThread;
00186 const size_t pixelTo = std::min( numberOfPixels, pixelFrom + pixelsPerThread );
00187 #endif
00188
00189 const size_t imagesFrom = ThisConst->m_ActiveImagesFrom;
00190 const size_t imagesTo = ThisConst->m_ActiveImagesTo;
00191 const byte paddingValue = ThisConst->m_PaddingValue;
00192
00193 for ( size_t ofs = pixelFrom; ofs < pixelTo; ++ofs )
00194 {
00195 histogram.Reset();
00196 const size_t kernelIdx = ThisConst->m_StandardDeviationByPixel[ofs];
00197 const size_t kernelRadius = ThisConst->m_HistogramKernelRadius[kernelIdx];
00198 const HistogramBinType* kernel = ThisConst->m_HistogramKernel[kernelIdx];
00199
00200 bool fullCount = true;
00201 if ( ThisConst->m_UseTemplateData )
00202 {
00203 const byte templateValue = ThisConst->m_TemplateData[ofs];
00204 if ( (fullCount = (templateValue != paddingValue )) )
00205 {
00206 histogram.AddWeightedSymmetricKernel( templateValue, kernelRadius, kernel );
00207 }
00208 }
00209
00210 for ( size_t idx = imagesFrom; (idx < imagesTo) && fullCount; ++idx )
00211 {
00212 const byte value = ThisConst->m_Data[idx][ofs];
00213 if ( value != paddingValue )
00214 {
00215 histogram.AddWeightedSymmetricKernel( value, kernelRadius, kernel );
00216 }
00217 else
00218 {
00219 fullCount = false;
00220 }
00221 }
00222
00223 if ( fullCount )
00224 {
00225 entropy -= histogram.GetEntropy();
00226 ++count;
00227 }
00228 }
00229
00230 threadParameters->m_Entropy = entropy;
00231 threadParameters->m_Count = count;
00232 }
00233
00234 template<class TXform>
00235 void
00236 CongealingFunctional<TXform>::EvaluateProbabilisticThread
00237 ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t threadIdx, const size_t )
00238 {
00239 EvaluateThreadParameters* threadParameters = static_cast<EvaluateThreadParameters*>( args );
00240
00241 Self* This = threadParameters->thisObject;
00242 const Self* ThisConst = threadParameters->thisObject;
00243
00244 HistogramType& histogram = This->m_ThreadHistograms[threadIdx];
00245 histogram.Resize( ThisConst->m_HistogramBins + 2 * ThisConst->m_HistogramKernelRadiusMax, false );
00246
00247 double entropy = 0;
00248 unsigned int count = 0;
00249
00250 const size_t imagesFrom = ThisConst->m_ActiveImagesFrom;
00251 const size_t imagesTo = ThisConst->m_ActiveImagesTo;
00252 const byte paddingValue = ThisConst->m_PaddingValue;
00253
00254 const size_t numberOfSamples = ThisConst->m_ProbabilisticSamples.size();
00255 #ifdef CMTK_BUILD_MPI
00256 const size_t samplesPerThread = numberOfSamples / ( taskCnt * ThisConst->m_SizeMPI );
00257 const size_t sampleFrom = ( taskIdx + ThisConst->m_RankMPI * taskCnt ) * samplesPerThread;
00258 const size_t sampleTo = std::min( numberOfSamples, sampleFrom + samplesPerThread );
00259 #else
00260 const size_t samplesPerThread = numberOfSamples / taskCnt;
00261 const size_t sampleFrom = taskIdx * samplesPerThread;
00262 const size_t sampleTo = std::min( numberOfSamples, sampleFrom + samplesPerThread );
00263 #endif
00264
00265 for ( size_t sample = sampleFrom; sample < sampleTo; ++sample )
00266 {
00267 histogram.Reset();
00268 bool fullCount = true;
00269
00270 const size_t kernelIdx = ThisConst->m_StandardDeviationByPixel[sample];
00271 const size_t kernelRadius = ThisConst->m_HistogramKernelRadius[kernelIdx];
00272 const HistogramBinType* kernel = ThisConst->m_HistogramKernel[kernelIdx];
00273
00274 if ( ThisConst->m_UseTemplateData )
00275 {
00276 const byte templateValue = ThisConst->m_TemplateData[sample];
00277 if ( (fullCount = (templateValue != paddingValue)) )
00278 {
00279 histogram.AddWeightedSymmetricKernel( templateValue, kernelRadius, kernel );
00280 }
00281 }
00282
00283 for ( size_t idx = imagesFrom; (idx < imagesTo) && fullCount; ++idx )
00284 {
00285 const byte value = ThisConst->m_Data[idx][sample];
00286 if ( value != paddingValue )
00287 {
00288 histogram.AddWeightedSymmetricKernel( value, kernelRadius, kernel );
00289 }
00290 else
00291 {
00292 fullCount = false;
00293 }
00294 }
00295
00296 if ( fullCount )
00297 {
00298 entropy -= histogram.GetEntropy();
00299 ++count;
00300 }
00301 else
00302 {
00303 }
00304 }
00305
00306 threadParameters->m_Entropy = entropy;
00307 threadParameters->m_Count = count;
00308 }
00309
00310 template<class TXform>
00311 bool
00312 CongealingFunctional<TXform>
00313 ::Wiggle()
00314 {
00315 bool wiggle = this->Superclass::Wiggle();
00316
00317 if ( wiggle )
00318 {
00319 this->m_NeedsUpdateStandardDeviationByPixel = true;
00320 }
00321
00322 return wiggle;
00323 }
00324
00325 template<class TXform>
00326 void
00327 CongealingFunctional<TXform>
00328 ::UpdateStandardDeviationByPixel()
00329 {
00330 if ( this->m_ProbabilisticSamples.size() )
00331 {
00332 const size_t numberOfSamples = this->m_ProbabilisticSamples.size();
00333 #ifdef CMTK_BUILD_MPI
00334 const size_t samplesPerNode = (numberOfSamples+this->m_SizeMPI-1) / this->m_SizeMPI;
00335 this->m_StandardDeviationByPixel.resize( samplesPerNode * this->m_SizeMPI );
00336 this->m_StandardDeviationByPixelMPI.resize( samplesPerNode );
00337 #else
00338 this->m_StandardDeviationByPixel.resize( numberOfSamples );
00339 #endif
00340 }
00341 else
00342 {
00343 const size_t numberOfPixels = this->m_TemplateNumberOfPixels;
00344 #ifdef CMTK_BUILD_MPI
00345 const size_t pixelsPerNode = (numberOfPixels+this->m_SizeMPI-1) / this->m_SizeMPI;
00346 this->m_StandardDeviationByPixel.resize( pixelsPerNode * this->m_SizeMPI );
00347 this->m_StandardDeviationByPixelMPI.resize( pixelsPerNode );
00348 #else
00349 this->m_StandardDeviationByPixel.resize( numberOfPixels );
00350 #endif
00351 }
00352
00353 std::vector<ThreadParametersType> params( this->m_NumberOfTasks );
00354 for ( size_t idx = 0; idx < this->m_NumberOfTasks; ++idx )
00355 params[idx].thisObject = this;
00356
00357 ThreadPool& threadPool = ThreadPool::GetGlobalThreadPool();
00358 threadPool.Run( Self::UpdateStandardDeviationByPixelThreadFunc, params );
00359
00360 #ifdef CMTK_BUILD_MPI
00361 MPI::COMM_WORLD.Allgather( &this->m_StandardDeviationByPixelMPI[0], this->m_StandardDeviationByPixelMPI.size(),
00362 MPI::CHAR, &this->m_StandardDeviationByPixel[0], this->m_StandardDeviationByPixelMPI.size(), MPI::CHAR );
00363 #endif
00364
00365 this->m_NeedsUpdateStandardDeviationByPixel = false;
00366 }
00367
00368 template<class TXform>
00369 void
00370 CongealingFunctional<TXform>
00371 ::UpdateStandardDeviationByPixelThreadFunc
00372 ( void *const args, const size_t taskIdx, const size_t taskCnt, const size_t, const size_t )
00373 {
00374 ThreadParametersType* taskParameters = static_cast<ThreadParametersType*>( args );
00375
00376 Self* This = taskParameters->thisObject;
00377 const Self* ThisConst = taskParameters->thisObject;
00378
00379 const size_t imagesFrom = ThisConst->m_ActiveImagesFrom;
00380 const size_t imagesTo = ThisConst->m_ActiveImagesTo;
00381 const byte paddingValue = ThisConst->m_PaddingValue;
00382
00383 if ( ThisConst->m_ProbabilisticSamples.size() )
00384 {
00385 const size_t numberOfSamples = ThisConst->m_ProbabilisticSamples.size();
00386 #ifdef CMTK_BUILD_MPI
00387 const size_t samplesPerNode = (numberOfSamples+ThisConst->m_SizeMPI-1) / ThisConst->m_SizeMPI;
00388 const size_t samplesPerTask = 1 + (samplesPerNode / taskCnt);
00389 const size_t sampleFromNode = ThisConst->m_RankMPI * samplesPerNode;
00390 const size_t sampleFrom = sampleFromNode + taskIdx * samplesPerTask;
00391 const size_t sampleTo = std::min( numberOfSamples, std::min( sampleFromNode + samplesPerNode, sampleFrom + samplesPerTask ) );
00392 size_t mpiSmpl = taskIdx * samplesPerTask;
00393 #else
00394 const size_t samplesPerTask = 1 + (numberOfSamples / taskCnt );
00395 const size_t sampleFrom = taskIdx * samplesPerTask;
00396 const size_t sampleTo = std::min( numberOfSamples, sampleFrom + samplesPerTask );
00397 #endif
00398
00399 for ( size_t smpl = sampleFrom; smpl < sampleTo; ++smpl )
00400 {
00401 double sum = 0, sumsq = 0;
00402 unsigned int count = 0;
00403
00404 if ( ThisConst->m_UseTemplateData )
00405 {
00406 const byte templateValue = ThisConst->m_TemplateData[smpl];
00407 if ( templateValue != paddingValue )
00408 {
00409 sum += templateValue;
00410 sumsq += templateValue * templateValue;
00411 ++count;
00412 }
00413 }
00414
00415 for ( size_t idx = imagesFrom; idx < imagesTo; ++idx )
00416 {
00417 const byte value = ThisConst->m_Data[idx][smpl];
00418 if ( value != paddingValue )
00419 {
00420 const double data = static_cast<double>( value );
00421 sum += data;
00422 sumsq += data * data;
00423 ++count;
00424 }
00425 }
00426
00427 if ( count )
00428 {
00429 const double mu = sum / count;
00430 const byte sdev = std::min<byte>( ThisConst->m_HistogramKernelRadiusMax, (byte)(sqrt(( count * mu * mu - 2 * mu * sum + sumsq ) / (count-1)) ) );
00431
00432 #ifdef CMTK_BUILD_MPI
00433 This->m_StandardDeviationByPixelMPI[mpiSmpl++] = sdev;
00434 #else
00435 This->m_StandardDeviationByPixel[smpl] = sdev;
00436 #endif
00437 }
00438 else
00439 {
00440 This->m_StandardDeviationByPixel[smpl] = 0;
00441 }
00442 }
00443 }
00444 else
00445 {
00446 const size_t numberOfPixels = ThisConst->m_TemplateNumberOfPixels;
00447 #ifdef CMTK_BUILD_MPI
00448 const size_t pixelsPerNode = (numberOfPixels+ThisConst->m_SizeMPI-1) / ThisConst->m_SizeMPI;
00449 const size_t pixelsPerTask = 1 + (pixelsPerNode / taskCnt);
00450 const size_t pixelFromNode = ThisConst->m_RankMPI * pixelsPerNode;
00451 const size_t pixelFrom = pixelFromNode + taskIdx * pixelsPerTask;
00452 const size_t pixelTo = std::min( numberOfPixels, std::min( pixelFromNode + pixelsPerNode, pixelFrom + pixelsPerTask ) );
00453 size_t mpiPx = taskIdx * pixelsPerTask;
00454 #else
00455 const size_t pixelsPerTask = 1 + (numberOfPixels / taskCnt);
00456 const size_t pixelFrom = taskIdx * pixelsPerTask;
00457 const size_t pixelTo = std::min( numberOfPixels, pixelFrom + pixelsPerTask );
00458 #endif
00459 for ( size_t px = pixelFrom; px < pixelTo; ++px )
00460 {
00461 double sum = 0, sumsq = 0;
00462 unsigned int count = 0;
00463
00464 if ( ThisConst->m_UseTemplateData )
00465 {
00466 const byte templateValue = ThisConst->m_TemplateData[px];
00467 if ( templateValue != paddingValue )
00468 {
00469 sum += templateValue;
00470 sumsq += templateValue * templateValue;
00471 ++count;
00472 }
00473 }
00474
00475 for ( size_t idx = imagesFrom; idx < imagesTo; ++idx )
00476 {
00477 const byte value = ThisConst->m_Data[idx][px];
00478 if ( value != paddingValue )
00479 {
00480 const double data = static_cast<double>( value );
00481 sum += data;
00482 sumsq += data * data;
00483 ++count;
00484 }
00485 }
00486
00487 if ( count )
00488 {
00489 const double mu = sum / count;
00490 const byte sdev = std::min<byte>( ThisConst->m_HistogramKernelRadiusMax, (byte)(sqrt(( count * mu * mu - 2 * mu * sum + sumsq ) / (count-1)) ) );
00491 #ifdef CMTK_BUILD_MPI
00492 This->m_StandardDeviationByPixelMPI[mpiPx++] = sdev;
00493 #else
00494 This->m_StandardDeviationByPixel[px] = sdev;
00495 #endif
00496 }
00497 else
00498 {
00499 This->m_StandardDeviationByPixel[px] = 0;
00500 }
00501 }
00502 }
00503 }
00504
00506
00507 }
00508
00509 #include <Base/cmtkAffineXform.h>
00510 #include <Base/cmtkSplineWarpXform.h>
00511
00512 template class cmtk::CongealingFunctional<cmtk::AffineXform>;
00513 template class cmtk::CongealingFunctional<cmtk::SplineWarpXform>;