ANIMA  4.0
animaBlockMatchInitializer.hxx
Go to the documentation of this file.
1 #pragma once
3 
4 #include <itkImageRegionConstIterator.h>
5 #include <itkImageRegionIteratorWithIndex.h>
6 
7 #include <itkExpNegativeImageFilter.h>
8 #include <itkDanielssonDistanceMapImageFilter.h>
9 #include <itkGrayscaleDilateImageFilter.h>
10 #include <itkBinaryBallStructuringElement.h>
11 #include <itkPoolMultiThreader.h>
12 
13 namespace anima
14 {
15 
16 template <class PixelType, unsigned int NDimensions>
17 std::vector < typename BlockMatchingInitializer<PixelType,NDimensions>::ImageRegionType > &
20 {
21  this->Update();
22 
23  return m_Output;
24 }
25 
26 template <class PixelType, unsigned int NDimensions>
27 std::vector < typename BlockMatchingInitializer<PixelType,NDimensions>::PointType > &
30 {
31  this->Update();
32 
33  return m_OutputPositions;
34 }
35 
36 template <class PixelType, unsigned int NDimensions>
37 std::vector <unsigned int> &
39 {
40  if (m_MaskStartingIndexes.size() == 0)
41  this->Update();
42 
43  return m_MaskStartingIndexes;
44 }
45 
46 template <class PixelType, unsigned int NDimensions>
47 void
49 ::AddReferenceImage(itk::ImageBase <NDimensions> *refImage)
50 {
51  ScalarImageType * tmpPtr = dynamic_cast <ScalarImageType *> (refImage);
52 
53  if (tmpPtr)
54  {
55  m_ReferenceScalarImages.push_back(tmpPtr);
56  m_ReferenceScalarImages.back()->DisconnectPipeline();
57  }
58  else
59  {
60  m_ReferenceVectorImages.push_back(dynamic_cast <VectorImageType *> (refImage));
61  m_ReferenceVectorImages.back()->DisconnectPipeline();
62  }
63 }
64 
65 template <class PixelType, unsigned int NDimensions>
66 void
69 {
70  if (mask)
71  m_GenerationMasks.push_back(mask);
72 }
73 
74 template <class PixelType, unsigned int NDimensions>
75 itk::ImageBase <NDimensions> *
78 {
79  if (m_ReferenceScalarImages.size() != 0)
80  return m_ReferenceScalarImages[0];
81 
82  if (m_ReferenceVectorImages.size() != 0)
83  return m_ReferenceVectorImages[0];
84 
85  return NULL;
86 }
87 
88 template <class PixelType, unsigned int NDimensions>
91 {
92  if (val != m_PercentageKept)
93  {
94  m_PercentageKept = val;
95  m_UpToDate = false;
96  }
97 }
98 
99 template <class PixelType, unsigned int NDimensions>
102 {
103  if (val != m_ScalarVarianceThreshold)
104  {
105  m_ScalarVarianceThreshold = val;
106  m_UpToDate = false;
107  }
108 }
109 
110 template <class PixelType, unsigned int NDimensions>
113 {
114  if (val != m_OrientedModelVarianceThreshold)
115  {
116  m_OrientedModelVarianceThreshold = val;
117  m_UpToDate = false;
118  }
119 }
120 
121 template <class PixelType, unsigned int NDimensions>
124 {
125  if (val != m_RequestedRegion)
126  {
127  m_RequestedRegion = val;
128  m_UpToDate = false;
129  }
130 }
131 
132 template <class PixelType, unsigned int NDimensions>
134 ::SetBlockSpacing(unsigned int val)
135 {
136  if (val != m_BlockSpacing)
137  {
138  m_BlockSpacing = val;
139  m_UpToDate = false;
140  }
141 }
142 
143 template <class PixelType, unsigned int NDimensions>
145 ::SetBlockSize(unsigned int val)
146 {
147  if (val != m_BlockSize)
148  {
149  m_BlockSize = val;
150  m_UpToDate = false;
151  }
152 }
153 
154 template <class PixelType, unsigned int NDimensions>
157 {
158  if (m_GenerationMasks.size() == 0)
159  {
160  // Not taking origin, orientation, we don't care about it
161  MaskImagePointer wholeMask = MaskImageType::New();
162  wholeMask->Initialize();
163  wholeMask->SetRegions(m_RequestedRegion);
164  wholeMask->Allocate();
165  wholeMask->FillBuffer(1);
166 
167  m_GenerationMasks.push_back(wholeMask);
168  m_UpToDate = false;
169  }
170 
171  if (m_UpToDate)
172  return;
173 
174  m_Output.clear();
175  m_OutputPositions.clear();
176  m_MaskStartingIndexes.resize(m_GenerationMasks.size());
177  for (unsigned int i = 0;i < m_GenerationMasks.size();++i)
178  {
179  m_MaskStartingIndexes[i] = m_Output.size();
180  this->ComputeBlocksOnGenerationMask(i);
181  }
182 
183  m_UpToDate = true;
184 }
185 
186 template <class PixelType, unsigned int NDimensions>
187 void
189 ::ComputeBlocksOnGenerationMask(unsigned int maskIndex)
190 {
191  itk::PoolMultiThreader::Pointer threaderBlockGenerator = itk::PoolMultiThreader::New();
192 
193  BlockGeneratorThreadStruct *tmpStr = 0;
194  this->InitializeThreading(maskIndex,tmpStr);
195 
196  threaderBlockGenerator->SetNumberOfWorkUnits(this->GetNumberOfThreads());
197  threaderBlockGenerator->SetSingleMethod(this->ThreadBlockGenerator,tmpStr);
198  threaderBlockGenerator->SingleMethodExecute();
199 
200  m_Output.clear();
201  m_OutputPositions.clear();
202  unsigned int totalNumberOfBlocks = 0;
203  unsigned int realNumberOfBlocks = 0;
204 
205  for (unsigned int i = 0;i < this->GetNumberOfThreads();++i)
206  {
207  realNumberOfBlocks += tmpStr->tmpOutput[i].size();
208  totalNumberOfBlocks += tmpStr->totalNumberOfBlocks[i];
209  }
210 
211  double percentageBlocksKept = (double) realNumberOfBlocks / totalNumberOfBlocks;
212 
213  if (percentageBlocksKept > m_PercentageKept)
214  {
215  std::vector < std::pair <double, std::pair <PointType, ImageRegionType> > > sortVector;
216  for (unsigned int i = 0;i < this->GetNumberOfThreads();++i)
217  for (unsigned int j = 0;j < tmpStr->tmpOutput[i].size();++j)
218  {
219  std::pair <PointType, ImageRegionType> tmpPair(tmpStr->blocks_positions[i][j],tmpStr->tmpOutput[i][j]);
220  sortVector.push_back(std::make_pair(tmpStr->blocks_variances[i][j],tmpPair));
221  }
222 
223  unsigned int numRemoved = std::floor((1.0 - m_PercentageKept) * totalNumberOfBlocks);
224  std::partial_sort(sortVector.begin(),sortVector.begin() + numRemoved,sortVector.end(),pair_comparator());
225 
226  for (unsigned int i = numRemoved;i < sortVector.size();++i)
227  {
228  m_Output.push_back(sortVector[i].second.second);
229  m_OutputPositions.push_back(sortVector[i].second.first);
230  }
231  }
232  else
233  {
234  for (unsigned int i = 0;i < this->GetNumberOfThreads();++i)
235  {
236  m_Output.insert(m_Output.end(), tmpStr->tmpOutput[i].begin(), tmpStr->tmpOutput[i].end());
237  m_OutputPositions.insert(m_OutputPositions.end(), tmpStr->blocks_positions[i].begin(),
238  tmpStr->blocks_positions[i].end());
239  }
240  }
241 
242  delete tmpStr;
243 }
244 
245 template <class PixelType, unsigned int NDimensions>
246 void
248 ::InitializeThreading(unsigned int maskIndex, BlockGeneratorThreadStruct *&workStr)
249 {
250  ImageRegionType workRegion;
251  IndexType minIndex, maxIndex, tmpIndex;
252  minIndex = m_RequestedRegion.GetIndex() + m_RequestedRegion.GetSize();
253  maxIndex = m_RequestedRegion.GetIndex();
254  typedef itk::ImageRegionIteratorWithIndex <MaskImageType> MaskIteratorType;
255  MaskIteratorType maskItr(m_GenerationMasks[maskIndex],m_RequestedRegion);
256 
257  while (!maskItr.IsAtEnd())
258  {
259  if (maskItr.Get() != 0)
260  {
261  tmpIndex = maskItr.GetIndex();
262  for (unsigned int i = 0;i < NDimensions;++i)
263  {
264  if (tmpIndex[i] < minIndex[i])
265  minIndex[i] = tmpIndex[i];
266 
267  if (tmpIndex[i] > maxIndex[i])
268  maxIndex[i] = tmpIndex[i];
269  }
270  }
271 
272  ++maskItr;
273  }
274 
275  for (unsigned int i = 0;i < NDimensions;++i)
276  {
277  workRegion.SetIndex(i,minIndex[i]);
278  workRegion.SetSize(i,maxIndex[i] - minIndex[i] + 1);
279  }
280 
281  if (workStr == 0)
282  workStr = new BlockGeneratorThreadStruct;
283 
284  std::vector <unsigned int> totalNbBlocks(NDimensions);
285  workStr->blockStartOffsets.resize(NDimensions);
286 
287  for (unsigned int i = 0;i < NDimensions;++i)
288  {
289  totalNbBlocks[i] = std::floor((double)(workRegion.GetSize()[i] / this->GetBlockSpacing()) + 0.5);
290  if (totalNbBlocks[i] < 1)
291  totalNbBlocks[i] = 1;
292 
293  unsigned int spaceRequired = workRegion.GetSize()[i] - (totalNbBlocks[i] - 1) * this->GetBlockSpacing() - 1;
294 
295  workStr->blockStartOffsets[i] = workRegion.GetIndex()[i] + std::floor(spaceRequired / 2.0);
296  }
297 
298  unsigned int nb_blocks_per_thread = (unsigned int) std::floor((double)(totalNbBlocks[NDimensions-1] / this->GetNumberOfThreads()));
299  if (nb_blocks_per_thread < 1)
300  {
301  nb_blocks_per_thread = 1;
302  this->SetNumberOfThreads(totalNbBlocks[NDimensions-1]);
303  }
304 
305  workStr->startBlocks.resize(this->GetNumberOfThreads());
306  workStr->nb_blocks.resize(this->GetNumberOfThreads());
307  workStr->maskIndex = maskIndex;
308 
309  unsigned int currentCount = 0;
310  for (unsigned int i = 0;i < this->GetNumberOfThreads();++i)
311  {
312  std::vector <unsigned int> startBlock(NDimensions,0);
313  startBlock[NDimensions-1] = currentCount;
314  workStr->startBlocks[i] = startBlock;
315 
316  std::vector <unsigned int> numBlockVec(NDimensions);
317  for (unsigned int j = 0;j < NDimensions-1;++j)
318  numBlockVec[j] = totalNbBlocks[j];
319 
320  unsigned int numBlocksForThread = nb_blocks_per_thread;
321  if (numBlocksForThread + currentCount > totalNbBlocks[NDimensions-1])
322  numBlocksForThread = totalNbBlocks[NDimensions-1] - currentCount;
323 
324  //We may be off by a few planes of blocks at the end
325  if (i == this->GetNumberOfThreads()-1)
326  numBlocksForThread = totalNbBlocks[NDimensions-1] - currentCount;
327 
328  numBlockVec[NDimensions-1] = numBlocksForThread;
329  workStr->nb_blocks[i] = numBlockVec;
330 
331  currentCount += numBlocksForThread;
332  }
333 
334  workStr->Filter = this;
335  workStr->tmpOutput.resize(this->GetNumberOfThreads());
336  workStr->totalNumberOfBlocks.resize(this->GetNumberOfThreads());
337  workStr->blocks_positions.resize(this->GetNumberOfThreads());
338  workStr->blocks_variances.resize(this->GetNumberOfThreads());
339 
340  for (unsigned int i = 0;i < this->GetNumberOfThreads();++i)
341  {
342  workStr->tmpOutput[i].clear();
343  workStr->totalNumberOfBlocks[i] = 0;
344  workStr->blocks_positions[i].clear();
345  workStr->blocks_variances[i].clear();
346  }
347 }
348 
349 template <class PixelType, unsigned int NDimensions>
350 ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION
353 {
354  itk::MultiThreaderBase::WorkUnitInfo *threadArgs = (itk::MultiThreaderBase::WorkUnitInfo *)arg;
355 
356  unsigned int nbThread = threadArgs->WorkUnitID;
357 
358  BlockGeneratorThreadStruct *tmpStr = (BlockGeneratorThreadStruct *)threadArgs->UserData;
359 
360  tmpStr->Filter->RegionBlockGenerator(tmpStr,nbThread);
361 
362  return ITK_THREAD_RETURN_DEFAULT_VALUE;
363 }
364 
365 template <class PixelType, unsigned int NDimensions>
367 ::RegionBlockGenerator(BlockGeneratorThreadStruct *workStr, unsigned int threadId)
368 {
369  typename ImageRegionType::IndexType startPosition, blockPosition;
370  typename ImageRegionType::SizeType blockSize;
371  PointType blockOrigin;
372  ImageRegionType largestRegion = this->GetFirstReferenceImage()->GetLargestPossibleRegion();
373 
374  ImageRegionType tmpBlock;
375  int indexPos;
376  double blockVar = 0;
377  workStr->totalNumberOfBlocks[threadId] = 0;
378  workStr->blocks_variances[threadId].clear();
379  workStr->blocks_positions[threadId].clear();
380 
381  unsigned int block_half_size = std::floor ((this->GetBlockSize() - 1) / 2.0);
382 
383  bool continueLoop = true;
384  std::vector <unsigned int> positionCounter(NDimensions,0);
385 
386  while (continueLoop)
387  {
388  for (unsigned int i = 0;i < NDimensions;++i)
389  {
390  blockSize[i] = this->GetBlockSize();
391  indexPos = workStr->blockStartOffsets[i] + (workStr->startBlocks[threadId][i] + positionCounter[i])*this->GetBlockSpacing() - block_half_size;
392  if (indexPos < largestRegion.GetIndex()[i])
393  {
394  blockSize[i] += indexPos - largestRegion.GetIndex()[i];
395  indexPos = largestRegion.GetIndex()[i];
396  }
397  startPosition[i] = indexPos;
398  if (startPosition[i] + blockSize[i] > largestRegion.GetIndex()[i] + largestRegion.GetSize()[i])
399  blockSize[i] = largestRegion.GetIndex()[i] + largestRegion.GetSize()[i] - startPosition[i];
400  }
401 
402  tmpBlock.SetIndex(startPosition);
403  tmpBlock.SetSize(blockSize);
404  workStr->totalNumberOfBlocks[threadId]++;
405 
406  if (this->CheckBlockConditions(tmpBlock,blockVar,workStr,threadId))
407  {
408  for (unsigned int i = 0;i < NDimensions;++i)
409  blockPosition[i] = workStr->blockStartOffsets[i] + (workStr->startBlocks[threadId][i] + positionCounter[i])*this->GetBlockSpacing();
410 
411  if (m_GenerationMasks[workStr->maskIndex]->GetPixel(blockPosition) == 0)
412  {
413  continueLoop = this->ProgressCounter(positionCounter,workStr->nb_blocks[threadId]);
414  continue;
415  }
416 
417  workStr->tmpOutput[threadId].push_back(tmpBlock);
418  workStr->blocks_variances[threadId].push_back(blockVar);
419 
420  this->GetFirstReferenceImage()->TransformIndexToPhysicalPoint(blockPosition,blockOrigin);
421  workStr->blocks_positions[threadId].push_back(blockOrigin);
422  }
423 
424  continueLoop = this->ProgressCounter(positionCounter,workStr->nb_blocks[threadId]);
425  }
426 }
427 
428 template <class PixelType, unsigned int NDimensions>
429 bool
431 ::ProgressCounter(std::vector <unsigned int> &counter, std::vector <unsigned int> & bounds)
432 {
433  unsigned int pos = counter.size() - 1;
434 
435  bool needUpdate = false;
436  do
437  {
438  counter[pos]++;
439  needUpdate = false;
440 
441  if ((counter[pos] == bounds[pos])&&(pos > 0))
442  {
443  counter[pos] = 0;
444  --pos;
445  needUpdate = true;
446  }
447  } while (needUpdate);
448 
449  if (counter[pos] >= bounds[pos])
450  return false;
451 
452  return true;
453 }
454 
455 template <class PixelType, unsigned int NDimensions>
456 bool
458 ::CheckBlockConditions(ImageRegionType &region, double &blockVariance, BlockGeneratorThreadStruct *workStr,
459  unsigned int threadId)
460 {
461  ImageRegionType refRegion = this->GetFirstReferenceImage()->GetLargestPossibleRegion();
462 
463  for (unsigned int i = 0;i < NDimensions;++i)
464  {
465  if (refRegion.GetIndex()[i] > region.GetIndex()[i])
466  return false;
467 
468  if (refRegion.GetIndex()[i] + refRegion.GetSize()[i] < region.GetIndex()[i] + region.GetSize()[i])
469  return false;
470  }
471 
472  blockVariance = 0;
473  double tmpVar = 0;
474 
475  for (unsigned int i = 0;i < m_ReferenceScalarImages.size();++i)
476  {
477  if (!this->CheckScalarVariance(m_ReferenceScalarImages[i],region,tmpVar))
478  return false;
479 
480  if (tmpVar > blockVariance)
481  blockVariance = tmpVar;
482  }
483 
484  for (unsigned int i = 0;i < m_ReferenceVectorImages.size();++i)
485  {
486  if (!this->CheckOrientedModelVariance(i,region,tmpVar,workStr,threadId))
487  return false;
488 
489  if (tmpVar > blockVariance)
490  blockVariance = tmpVar;
491  }
492 
493  // Reaches here if block respects criterions on all images
494  return true;
495 }
496 
497 template <class PixelType, unsigned int NDimensions>
499 ::CheckOrientedModelVariance(unsigned int imageIndex, ImageRegionType &region, double &blockVariance,
500  BlockGeneratorThreadStruct *workStr, unsigned int threadId)
501 {
502  VectorImageType *refImage = m_ReferenceVectorImages[imageIndex];
503  itk::ImageRegionConstIterator <VectorImageType> refItr(refImage,region);
504  typedef typename VectorImageType::PixelType VectorType;
505 
506  unsigned int nbPts = 0;
507  unsigned int vectorSize = refImage->GetNumberOfComponentsPerPixel();
508 
509  VectorType meanVal(vectorSize);
510  meanVal.Fill(0);
511  std::vector <double> blockCovariance(vectorSize, 0.0);
512 
513  while (!refItr.IsAtEnd())
514  {
515  VectorType tmpVal = refItr.Get();
516  meanVal += tmpVal;
517 
518  ++nbPts;
519  ++refItr;
520  }
521 
522  meanVal /= nbPts;
523 
524  refItr.GoToBegin();
525 
526  while (!refItr.IsAtEnd())
527  {
528  VectorType tmpVal = refItr.Get();
529 
530  for (unsigned int j = 0;j < vectorSize;++j)
531  blockCovariance[j] += (tmpVal[j] - meanVal[j]) * (tmpVal[j] - meanVal[j]);
532 
533  ++refItr;
534  }
535 
536  blockVariance = 0;
537 
538  if (nbPts <= 1)
539  return false;
540 
541  for (unsigned int j = 0;j < vectorSize;++j)
542  {
543  double tmp = blockCovariance[j] / (nbPts - 1.0);
544  blockVariance += tmp;
545  }
546 
547  blockVariance /= vectorSize;
548 
549  if (blockVariance > this->GetOrientedModelVarianceThreshold())
550  return true;
551  else
552  return false;
553 }
554 
555 template <class PixelType, unsigned int NDimensions>
557 ::CheckScalarVariance(ScalarImageType *refImage, ImageRegionType &region, double &blockVariance)
558 {
559  itk::ImageRegionConstIterator <ScalarImageType> refItr(refImage,region);
560  blockVariance = 0;
561  double meanVal = 0;
562 
563  unsigned int nbPts = 0;
564  while (!refItr.IsAtEnd())
565  {
566  double tmpVal = refItr.Get();
567  meanVal += tmpVal;
568 
569  ++nbPts;
570  ++refItr;
571  }
572 
573  if (nbPts <= 1)
574  return false;
575 
576  meanVal /= nbPts;
577  refItr.GoToBegin();
578  while (!refItr.IsAtEnd())
579  {
580  double tmpVal = refItr.Get();
581  blockVariance += (meanVal - tmpVal) * (meanVal - tmpVal);
582 
583  ++refItr;
584  }
585 
586  blockVariance /= (nbPts - 1.0);
587 
588  if (blockVariance > this->GetScalarVarianceThreshold())
589  return true;
590  else
591  return false;
592 }
593 
594 }// end of namespace anima
itk::Image< unsigned char, NDimensions > MaskImageType
void RegionBlockGenerator(BlockGeneratorThreadStruct *workStr, unsigned int threadId)
virtual bool CheckOrientedModelVariance(unsigned int imageIndex, ImageRegionType &region, double &blockVariance, BlockGeneratorThreadStruct *workStr, unsigned int threadId)
virtual void AddReferenceImage(itk::ImageBase< NDimensions > *refImage)
std::vector< PointType > & GetOutputPositions()
void SetRequestedRegion(const ImageRegionType &val)
virtual void InitializeThreading(unsigned int maskIndex, BlockGeneratorThreadStruct *&workStr)
bool CheckScalarVariance(ScalarImageType *refImage, ImageRegionType &region, double &blockVariance)
void ComputeBlocksOnGenerationMask(unsigned int maskIndex)
static ITK_THREAD_RETURN_FUNCTION_CALL_CONVENTION ThreadBlockGenerator(void *arg)
ScalarImageType::RegionType ImageRegionType
itk::VectorImage< PixelType, NDimensions > VectorImageType
itk::Image< PixelType, NDimensions > ScalarImageType
bool ProgressCounter(std::vector< unsigned int > &counter, std::vector< unsigned int > &bounds)
std::vector< unsigned int > & GetMaskStartingIndexes()
bool CheckBlockConditions(ImageRegionType &region, double &blockVariance, BlockGeneratorThreadStruct *workStr, unsigned int threadId)
itk::ImageBase< NDimensions > * GetFirstReferenceImage()
std::vector< ImageRegionType > & GetOutput()