1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 #ifndef itkKLMRegionGrowImageFilter_hxx
19 #define itkKLMRegionGrowImageFilter_hxx
20 #include "itkKLMRegionGrowImageFilter.h"
21 
22 namespace itk
23 {
24 template< typename TInputImage, typename TOutputImage >
25 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
KLMRegionGrowImageFilter(void)26 ::KLMRegionGrowImageFilter(void)
27 
28 {
29   m_InitialRegionMean.set_size(InputImageVectorDimension);
30   m_InitialRegionMean.fill(0);
31   this->SetMaximumNumberOfRegions(2);
32 }
33 
34 /**
35  * PrintSelf
36  */
37 template< typename TInputImage, typename TOutputImage >
38 void
39 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
PrintSelf(std::ostream & os,Indent indent) const40 ::PrintSelf(std::ostream & os, Indent indent) const
41 {
42   Superclass::PrintSelf(os, indent);
43 
44   os << indent << "KLM Region grow segmentation object" << std::endl;
45   os << indent << "KLM Region grow image filter object" << std::endl;
46   os << indent << "Maximum value of lambda parameter: " << m_MaximumLambda << std::endl;
47   os << indent << "Current internal value of lambda parameter: " << m_InternalLambda << std::endl;
48   os << indent << "Initial number of regions: " << m_InitialNumberOfRegions << std::endl;
49   os << indent << "Current number of regions: " << m_NumberOfRegions << std::endl;
50 } // end PrintSelf
51 
52 /*
53  * GenerateInputRequestedRegion method.
54  */
55 template< typename TInputImage, typename TOutputImage >
56 void
57 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
GenerateInputRequestedRegion()58 ::GenerateInputRequestedRegion()
59 {
60   // This filter requires all of the input image to be in the buffer
61   InputImagePointer inputPtr =
62     const_cast< InputImageType * >( this->GetInput() );
63 
64   if ( inputPtr )
65     {
66     inputPtr->SetRequestedRegionToLargestPossibleRegion();
67     }
68 }
69 
70 /**
71  * EnlargeOutputRequestedRegion method.
72  */
73 template< typename TInputImage, typename TOutputImage >
74 void
75 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
EnlargeOutputRequestedRegion(DataObject * output)76 ::EnlargeOutputRequestedRegion(
77   DataObject *output)
78 {
79   // This filter requires all of the output image to be in the buffer
80   TOutputImage *imgData;
81 
82   imgData = dynamic_cast< TOutputImage * >( output );
83   imgData->SetRequestedRegionToLargestPossibleRegion();
84 }
85 
86 template< typename TInputImage, typename TOutputImage >
87 void
88 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
GenerateData()89 ::GenerateData()
90 {
91   // Run the KLM algorithm
92   this->ApplyRegionGrowImageFilter();
93 
94   // Set the output labelled and allocate the memory
95   OutputImagePointer outputPtr = this->GetOutput();
96 
97   // Allocate the output buffer memory
98   outputPtr->SetBufferedRegion( outputPtr->GetRequestedRegion() );
99   outputPtr->Allocate();
100 
101   GenerateOutputImage();
102 } // end GenerateData
103 
104 template< typename TInputImage, typename TOutputImage >
105 void
106 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
GenerateOutputImage()107 ::GenerateOutputImage()
108 {
109   InputImageConstPointer inputImage     = this->GetInput();
110   InputImageSizeType     inputImageSize = inputImage->GetBufferedRegion().GetSize();
111 
112   GridSizeType gridSize = this->GetGridSize();
113 
114   InputImageSizeType numRegionsAlongDim;
115 
116   for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
117     {
118     numRegionsAlongDim[idim] = inputImageSize[idim] / gridSize[idim];
119     }
120 
121   // Walk through each atomic block and get the approximation image.
122   // This is needed as the region labels associated each atomic
123   // block (as it was for during the initialization stage), have changed.
124   // The new region label points to the region that has the up-to-date
125   // region intensity. After the updated region intensity is found,
126   // each pixel is updated with the mean approximation value.
127 
128   OutputImagePointer outputImage = this->GetOutput();
129   using OutputRegionType = typename TOutputImage::RegionType;
130   OutputRegionType region;
131   region.SetSize(gridSize);   // Constant grid size
132 
133   OutputImageIndexType tmpIndex;
134   tmpIndex.Fill(0);
135 
136   for ( unsigned int iregion = 0; iregion < m_InitialNumberOfRegions; iregion++ )
137     {
138     region.SetIndex(tmpIndex * gridSize);
139 
140     // Convert the mean region value to the correct output format
141 
142     MeanRegionIntensityType tmpMeanValue;
143     OutputImageVectorType   outMeanValue;
144     using OutputValueType = typename OutputImagePixelType::ValueType;
145 
146     tmpMeanValue = m_RegionsPointer[iregion]->GetMeanRegionIntensity();
147 
148     for ( unsigned int ivecdim = 0; ivecdim < InputImageVectorDimension; ivecdim++ )
149       {
150       outMeanValue[ivecdim] =
151         static_cast< OutputValueType >( tmpMeanValue[ivecdim] );
152       }
153 
154     // Fill the region with the mean value
155 
156     OutputImageIterator outputIt(outputImage, region);
157 
158     while ( !outputIt.IsAtEnd() )
159       {
160       outputIt.Set(outMeanValue);
161       ++outputIt;
162       }
163 
164     // Calculate next grid index
165     IndexValueType tmpVal = 1;
166     for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
167       {
168       tmpIndex[idim]++;
169       tmpVal *= numRegionsAlongDim[idim];
170       if ( ( iregion + 1 ) % tmpVal != 0 ) { break; }
171       tmpIndex[idim] = 0;
172       }
173     } // end iregion loop
174 }     // end GenerateOutputImage()
175 
176 template< typename TInputImage, typename TOutputImage >
177 typename KLMRegionGrowImageFilter< TInputImage, TOutputImage >::LabelImagePointer
178 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
GetLabelledImage()179 ::GetLabelledImage()
180 {
181   // Allocate the memory for the labelled image
182 
183   LabelImagePointer labelImagePtr = LabelImageType::New();
184 
185   typename LabelImageType::SizeType labelImageSize =
186     this->GetInput()->GetBufferedRegion().GetSize();
187 
188   LabelImageIndexType labelImageIndex;
189   labelImageIndex.Fill(0);
190 
191   typename LabelImageType::RegionType labelImageRegion;
192 
193   labelImageRegion.SetSize(labelImageSize);
194   labelImageRegion.SetIndex(labelImageIndex);
195 
196   labelImagePtr->SetLargestPossibleRegion(labelImageRegion);
197   labelImagePtr->SetBufferedRegion(labelImageRegion);
198   labelImagePtr->Allocate();
199 
200   labelImagePtr = GenerateLabelledImage(labelImagePtr);
201 
202   return labelImagePtr;
203 } // end GetLabelledImage()
204 
205 template< typename TInputImage, typename TOutputImage >
206 typename KLMRegionGrowImageFilter< TInputImage, TOutputImage >::LabelImagePointer
207 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
GenerateLabelledImage(LabelImageType * labelImagePtr)208 ::GenerateLabelledImage(LabelImageType *labelImagePtr)
209 {
210   InputImageConstPointer inputImage     = this->GetInput();
211   InputImageSizeType     inputImageSize = inputImage->GetBufferedRegion().GetSize();
212 
213   GridSizeType gridSize = this->GetGridSize();
214 
215   InputImageSizeType numRegionsAlongDim;
216 
217   for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
218     {
219     numRegionsAlongDim[idim] = inputImageSize[idim] / gridSize[idim];
220     }
221 
222   // Walk through each atomic block and get the new unique labels
223   // representing the final segmentation.
224   // This is needed as the region labels associated each atomic
225   // block (as it was during the initialization stage), have changed.
226 
227   InputRegionType region;
228   region.SetSize(gridSize);   // Constant grid size
229 
230   InputImageIndexType tmpIndex;
231   tmpIndex.Fill(0);
232 
233   for ( unsigned int iregion = 0; iregion < m_InitialNumberOfRegions; iregion++ )
234     {
235     region.SetIndex(tmpIndex * gridSize);
236 
237     // Fill the region with the label
238 
239     RegionLabelType newRegionLabel = m_RegionsPointer[iregion]->GetRegionLabel();
240 
241     LabelImageIterator labelIt(labelImagePtr, region);
242     while ( !labelIt.IsAtEnd() )
243       {
244       labelIt.Set(newRegionLabel);
245       ++labelIt;
246       }
247 
248     // Calculate next grid index
249     IndexValueType tmpVal = 1;
250     for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
251       {
252       tmpIndex[idim]++;
253       tmpVal *= numRegionsAlongDim[idim];
254       if ( ( iregion + 1 ) % tmpVal != 0 ) { break; }
255       tmpIndex[idim] = 0;
256       }
257     }
258 
259   // Return the reference to the labelled image
260   return labelImagePtr;
261 } // end GenerateLabelledImage()
262 
263 template< typename TInputImage, typename TOutputImage >
264 void
265 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
ApplyRegionGrowImageFilter()266 ::ApplyRegionGrowImageFilter()
267 {
268   this->ApplyKLM();
269 } // end ApplyRegionGrowImageFilter()
270 
271 template< typename TInputImage, typename TOutputImage >
272 void
273 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
ApplyKLM()274 ::ApplyKLM()
275 {
276   // Region merging based on the minimum value of the current
277   // lambdas associated with the borders. The border with the
278   // smallest lambda value will be taken out for region merging.
279   // This growing process is repeated until the number of current
280   // different regions is not bigger than the desired and the
281   // current minimum scale parameter is not less than the desired scale.
282 
283   this->InitializeKLM();
284 
285   while ( ( m_NumberOfRegions > this->GetMaximumNumberOfRegions() )
286           && ( m_InternalLambda  < m_MaximumLambda ) )
287     {
288     this->MergeRegions();
289     }
290 
291   this->ResolveRegions();
292 } // end ApplyKLM()
293 
294 template< typename TInputImage, typename TOutputImage >
295 void
296 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
InitializeKLM()297 ::InitializeKLM()
298 {
299   // Maximum number of regions requested must be greater than 0
300   if ( this->GetMaximumNumberOfRegions() <= 1 )
301     {
302     itkExceptionMacro(<< "Number of requested regions must be 2 or more");
303     }
304 
305   // This implementation requires the image dimensions to be
306   // multiples of the user specified grid sizes.
307 
308   InputImageConstPointer inputImage     = this->GetInput();
309   InputImageSizeType     inputImageSize = inputImage->GetBufferedRegion().GetSize();
310   GridSizeType           gridSize       = this->GetGridSize();
311   typename TInputImage::SpacingType
312   spacing        = inputImage->GetSpacing();
313 
314   for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
315     {
316     if ( gridSize[idim] == 0
317          || inputImageSize[idim] % gridSize[idim] != 0 )
318       {
319       itkExceptionMacro(<< "Invalid grid size");
320       }
321     }
322 
323   // Determine the regions first and intialize them
324 
325   InputImageSizeType numRegionsAlongDim;
326   for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
327     {
328     numRegionsAlongDim[idim] = inputImageSize[idim] / gridSize[idim];
329     }
330 
331   // Calculate the initial number of regions
332 
333   m_InitialNumberOfRegions = 1;
334   for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
335     {
336     m_InitialNumberOfRegions *= numRegionsAlongDim[idim];
337     }
338 
339   if ( m_InitialNumberOfRegions < this->GetMaximumNumberOfRegions() )
340     {
341     itkWarningMacro(<< "Number of initial image regions is less than requested: reduce granularity of the grid");
342     }
343 
344   // Set current number of regions
345 
346   m_NumberOfRegions = m_InitialNumberOfRegions;
347 
348   // Allocate and intialize memory to the regions in initial image block
349 
350   m_RegionsPointer.resize(m_InitialNumberOfRegions);
351   for ( unsigned int iregion = 0; iregion < m_InitialNumberOfRegions; iregion++ )
352     {
353     m_RegionsPointer[iregion] = KLMSegmentationRegion::New();
354     }
355 
356   // Label each region
357 
358   InputRegionType region;
359   region.SetSize(gridSize);   // Constant grid size
360 
361   InputImageIndexType tmpIndex;
362   tmpIndex.Fill(0);
363 
364   for ( RegionLabelType iregion = 0; iregion < m_InitialNumberOfRegions; iregion++ )
365     {
366     region.SetIndex(tmpIndex * gridSize);
367 
368     InitializeRegionParameters(region);
369     m_RegionsPointer[iregion]->SetRegionParameters(m_InitialRegionMean,
370                                                    m_InitialRegionArea, iregion + 1);
371 
372     // Calculate next grid index
373     IndexValueType tmpVal = 1;
374     for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
375       {
376       tmpIndex[idim]++;
377       tmpVal *= numRegionsAlongDim[idim];
378       if ( ( iregion + 1 ) % tmpVal != 0 ) { break; }
379       tmpIndex[idim] = 0;
380       }
381     } // end iregion loop
382 
383   // Determine the borders next and intialize them
384 
385   // Allocate and intialize memory to the borders
386 
387   unsigned int numberOfBorders = 0;
388   for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
389     {
390     unsigned int tmpVal = 1;
391     for ( unsigned int jdim = 0; jdim < InputImageDimension; jdim++ )
392       {
393       tmpVal *= ( jdim == idim ?
394                   numRegionsAlongDim[jdim] - 1 :
395                   numRegionsAlongDim[jdim] );
396       }
397     numberOfBorders += tmpVal;
398     }
399 
400   // Allow a single region to pass through; this memory would not be
401   // used but the memory allocation and free routine will throw
402   // exception otherwise.
403   if ( numberOfBorders == 0 )
404     {
405     itkExceptionMacro(<< "Number of initial regions must be 2 or more: reduce granularity of the grid");
406     }
407 
408   m_BordersPointer.resize(numberOfBorders);
409   for ( unsigned int k = 0; k < m_BordersPointer.size(); k++ )
410     {
411     m_BordersPointer[k] = KLMSegmentationBorder::New();
412     }
413 
414   /* the following initialization of the borders ensures that
415      each border is assigned region1 and region2 such that,
416      the label of region1 is less than the label of region2/
417      and, that when a new border is added to a region,
418      PushBack can be used for region1 and PushFront can be used
419      for region2.  This will ensure that the borders are
420      sorted by increased labels for region1 then region2 */
421 
422   m_TotalBorderLength = 0;
423   unsigned int borderCounter   = 0;
424 
425   // Along each dimension, visit every border between two regions
426 
427   for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
428     {
429     // along the dimension there is one less border than there are regions
430     InputImageSizeType numBordersAlongDim = numRegionsAlongDim;
431     numBordersAlongDim[idim]--;
432 
433     // compute number of borders to be seen this dimension and area of
434     // each border
435     unsigned int numBorderThisDim = 1;
436     double       borderLengthTmp  = 1;
437     for ( unsigned int jdim = 0; jdim < InputImageDimension; jdim++ )
438       {
439       numBorderThisDim *= numBordersAlongDim[jdim];
440       borderLengthTmp *= ( jdim == idim ? 1 : gridSize[jdim] * spacing[jdim] );
441       }
442 
443     // index to atomic region1 and atomic region2
444     InputImageIndexType indexRegion1;
445     InputImageIndexType indexRegion2;
446     indexRegion1.Fill(0);
447     indexRegion2.Fill(0);
448     indexRegion2[idim]++;
449 
450     for ( unsigned int iborder = 0; iborder < numBorderThisDim; iborder++ )
451       {
452       if ( borderCounter >= numberOfBorders )
453         {
454         itkExceptionMacro(<< "KLM initialization is incorrect");
455         } // end if
456 
457       // Load the border of interest
458       KLMSegmentationBorderPtr pcurrentBorder = m_BordersPointer[borderCounter];
459 
460       // Set the length of the border
461       pcurrentBorder->SetBorderLength(borderLengthTmp);
462 
463       // m_TotalBorderLength is used as a sanity check
464       m_TotalBorderLength += borderLengthTmp;
465 
466       // Find the two neighbor regions
467 
468       unsigned int intRegion1Index = 0;
469       unsigned int intRegion2Index = 0;
470       IndexValueType        tmpVal = 1;
471       for ( unsigned int jdim = 0; jdim < InputImageDimension; jdim++ )
472         {
473         intRegion1Index += indexRegion1[jdim] * tmpVal;
474         intRegion2Index += indexRegion2[jdim] * tmpVal;
475         tmpVal *= numRegionsAlongDim[jdim];
476         } // end jdim loop
477 
478       KLMSegmentationRegionPtr pRegion1 = m_RegionsPointer[intRegion1Index];
479       KLMSegmentationRegionPtr pRegion2 = m_RegionsPointer[intRegion2Index];
480 
481       // Attach the region border off lesser label value to region1
482       // Attach the region border of the greater label value to region2
483       pcurrentBorder->SetRegion1(pRegion1);
484       pcurrentBorder->SetRegion2(pRegion2);
485 
486       // The current border is linked to the region1 and region2
487       // Initialize the border in the region objects
488       pRegion1->PushBackRegionBorder(pcurrentBorder);
489       pRegion2->PushFrontRegionBorder(pcurrentBorder);
490 
491       // Compute the scale parameter lambda
492       pcurrentBorder->EvaluateLambda();
493 
494       // Increment the border counter to go to the next border
495       borderCounter++;
496 
497       // Calculate next indices to atomic region1 and atomic region2
498       tmpVal = 1;
499       for ( unsigned int jdim = 0; jdim < InputImageDimension; jdim++ )
500         {
501         indexRegion1[jdim]++;
502         tmpVal *= numBordersAlongDim[jdim];
503         if ( ( iborder + 1 ) % tmpVal != 0 ) { break; }
504         indexRegion1[jdim] = 0;
505         }
506       indexRegion2 = indexRegion1;
507       indexRegion2[idim]++;
508       }
509     }
510 
511   // For DEBUG purposes
512   if ( this->GetDebug() )
513     {
514     PrintAlgorithmRegionStats();
515     PrintAlgorithmBorderStats();
516     }
517 
518   // Verification of the initialization process
519 
520   double actualBorderLength = 0;
521   for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
522     {
523     double tmpDblVal = 1;
524     for ( unsigned int jdim = 0; jdim < InputImageDimension; jdim++ )
525       {
526       tmpDblVal *= ( jdim == idim ?
527                      numRegionsAlongDim[jdim] - 1 : inputImageSize[jdim] * spacing[jdim] );
528       }
529     actualBorderLength += tmpDblVal;
530     }
531 
532   if ( Math::NotAlmostEquals( m_TotalBorderLength, actualBorderLength ) )
533     {
534     itkExceptionMacro(<< "KLM initialization is incorrect");
535     } // end if
536   else
537     {
538     itkDebugMacro(<< "Passed initialization");
539     } // end else
540 
541   // Allocate memory to store the array of pointers that point to the
542   // static border objects
543 
544   m_BordersDynamicPointer.resize( m_BordersPointer.size() );
545   for ( unsigned int k = 0; k < m_BordersDynamicPointer.size(); k++ )
546     {
547     m_BordersDynamicPointer[k].m_Pointer = m_BordersPointer[k];
548     }
549 
550   // For DEBUG purposes
551   if ( this->GetDebug() )
552     {
553     for ( unsigned int k = 0; k < m_BordersDynamicPointer.size(); k++ )
554       {
555       itkDebugMacro(<< m_BordersDynamicPointer[k].m_Pointer);
556       }
557     }
558 
559   std::stable_sort( m_BordersDynamicPointer.begin(),
560                     ( m_BordersDynamicPointer.end() ),
561                     std::greater< KLMDynamicBorderArray< BorderType > >() );
562 
563   m_BorderCandidate = &( m_BordersDynamicPointer[m_BordersDynamicPointer.size() - 1] );
564   m_InternalLambda = m_BorderCandidate->m_Pointer->GetLambda();
565 
566   if ( m_InternalLambda < 0.0 )
567     {
568     itkExceptionMacro(<< "KLM initialization is incorrect");
569     }
570 } // end InitializeKLM()
571 
572 template< typename TInputImage, typename TOutputImage >
573 void
574 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
InitializeRegionParameters(InputRegionType region)575 ::InitializeRegionParameters(InputRegionType region)
576 {
577   // Get a pointer to the image
578   InputImageConstPointer inputImage = this->GetInput();
579 
580   // Set the iterators and the pixel type definition for the input image
581   InputImageConstIterator inputIt(inputImage, region);
582 
583   // Variable to store the input pixel vector value
584   InputImageVectorType inputPixelVec;
585 
586   // Calculate V[0] for the constant model facet for the Region Growing
587   // algorithm
588 
589   m_InitialRegionMean.fill(0);
590 
591   while ( ! inputIt.IsAtEnd() )
592     {
593     inputPixelVec = inputIt.Value();
594 
595     for ( unsigned int ivecdim = 0; ivecdim < InputImageVectorDimension; ivecdim++ )
596       {
597       m_InitialRegionMean[ivecdim] += inputPixelVec[ivecdim];
598       }
599 
600     ++inputIt;
601     }
602 
603   // Calculate the area and the mean associated with the region
604   GridSizeType gridSize = this->GetGridSize();
605   typename TInputImage::SpacingType spacing  = inputImage->GetSpacing();
606   m_InitialRegionArea = 1;
607   for ( unsigned int idim = 0; idim < InputImageDimension; idim++ )
608     {
609     m_InitialRegionArea *= gridSize[idim] * spacing[idim];
610     }
611   m_InitialRegionMean /= m_InitialRegionArea;
612 } // end InitializeRegionParameters
613 
614 template< typename TInputImage, typename TOutputImage >
615 void
616 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
MergeRegions()617 ::MergeRegions()
618 {
619   itkDebugMacro(<< "--------------------");
620   itkDebugMacro(<< "   Merging Regions  ");
621 
622   // Subtract border length before removing it
623   m_TotalBorderLength -= m_BorderCandidate->m_Pointer->GetBorderLength();
624   if ( m_TotalBorderLength <= 0 ) { itkExceptionMacro(<< "KLM algorithm error"); }
625 
626   // Two regions are associated with the candidate border
627   KLMSegmentationRegion *pRegion1;
628   KLMSegmentationRegion *pRegion2;
629 
630   pRegion1 = m_BorderCandidate->m_Pointer->GetRegion1();
631   pRegion2 = m_BorderCandidate->m_Pointer->GetRegion2();
632 
633   // For consistency, always assign smaller label: this affects
634   // GenerateOutputImage and GenerateLabelledImage
635   if ( pRegion1->GetRegionLabel() >= pRegion2->GetRegionLabel() )
636     {
637     itkExceptionMacro(<< "Invalid region labelling");
638     }
639 
640   // Add the new region's parameter data to the old.
641   pRegion1->CombineRegionParameters(pRegion2);
642 
643   // Remove the common region border from region 1 and region 2
644   pRegion1->DeleteRegionBorder(m_BorderCandidate->m_Pointer);
645   pRegion2->DeleteRegionBorder(m_BorderCandidate->m_Pointer);
646 
647   // Assign new equivalence label to the old region and update
648   // the region borders, this is needed for ResolveRegions()
649   pRegion2->ResetRegionLabelAndUpdateBorders(pRegion1);
650 
651   // Merge the borders and region borders of two regions
652   pRegion1->SpliceRegionBorders(pRegion2);
653 
654   // Do not need the old region borders anymore
655   pRegion2->DeleteAllRegionBorders();
656 
657   // Recompute the lambda's for all the borders of region1
658   pRegion1->UpdateRegionBorderLambda();
659 
660   // Remove the common region border from list of sorted borders.
661   // The BorderCandidate is always the last element.
662   // Set the iterator to very last value and then erase that location
663   m_BordersDynamicPointer.erase(m_BordersDynamicPointer.end() - 1);
664 
665   // Decrement for the one deleted border and a deleted region
666   m_NumberOfRegions--;
667   if ( m_BordersDynamicPointer.empty() ) { itkExceptionMacro(<< "KLM algorithm error"); }
668 
669   // For DEBUG purposes
670   if ( this->GetDebug() )
671     {
672     itkDebugMacro(<< "First Region ");
673     pRegion1->PrintRegionInfo();
674     itkDebugMacro(<< "Second Region ");
675     pRegion2->PrintRegionInfo();
676     }
677 
678   // If any duplicate borders are found during SpliceRegionBorders,
679   // lambda is set to -1.0, and pRegion1 and pRegion2 are set nullptr
680   // so that after this sort, the duplicate border will be the last
681   // entry in m_BordersDynamicPointer
682 
683   // Resort the border list based on the lambda values
684   std::stable_sort( m_BordersDynamicPointer.begin(),
685                     ( m_BordersDynamicPointer.end() ),
686                     std::greater< KLMDynamicBorderArray< BorderType > >() );
687 
688   // Assign new BorderCandidate (it is always the last element).
689   // Set Pointer to BorderCandidate to the last element
690   m_BorderCandidate = &( m_BordersDynamicPointer[m_BordersDynamicPointer.size() - 1] );
691   m_InternalLambda = m_BorderCandidate->m_Pointer->GetLambda();
692 
693   // Remove any duplicate borders found during SpliceRegionBorders:
694   // lambda = -1.0,  pRegion1 and pRegion2 = nullptr
695   while ( m_BorderCandidate->m_Pointer->GetRegion1() == nullptr
696           || m_BorderCandidate->m_Pointer->GetRegion2() == nullptr )
697     {
698     m_BordersDynamicPointer.erase(m_BordersDynamicPointer.end() - 1);
699 
700     // Decrement for the one deleted border
701     if ( m_BordersDynamicPointer.empty() ) { itkExceptionMacro(<< "KLM algorithm error"); }
702 
703     m_BorderCandidate = &( m_BordersDynamicPointer[m_BordersDynamicPointer.size() - 1] );
704     m_InternalLambda = m_BorderCandidate->m_Pointer->GetLambda();
705     }
706 } // end MergeRegions
707 
708 template< typename TInputImage, typename TOutputImage >
709 void
710 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
ResolveRegions()711 ::ResolveRegions()
712 {
713   InputImageConstPointer inputImage = this->GetInput();
714 
715   // Scan through the region labels to establish the correspondence
716   // between the final region (and label) and the initial regions.
717 
718   // Set up the unique label container class
719   using UnsignedIntVectorType = std::vector< RegionLabelType >;
720   UnsignedIntVectorType uniqueLabelsVec;
721 
722   // Resolve region labels to contain only unique labels.
723   // Go backward from largest to smallest region label
724   auto regionsPointerIt = m_RegionsPointer.rbegin();
725   auto regionsPointerItEnd = m_RegionsPointer.rend();
726   RegionLabelType iregion = m_InitialNumberOfRegions;
727   while ( regionsPointerIt != regionsPointerItEnd )
728     {
729     RegionLabelType origLabel = iregion;
730     iregion--;
731     RegionLabelType currLabel = m_RegionsPointer[iregion]->GetRegionLabel();
732 
733     // Unresolved chain
734     if ( currLabel != origLabel )
735       {
736       // Resolve a chain of equivalences by first finding the end of the chain
737       RegionLabelType uniqLabel = origLabel;
738       RegionLabelType tmpLabel  = currLabel;
739       while ( uniqLabel != tmpLabel )
740         {
741         // In memory, the regions go from 0 to label-1. Hence the -1 offset
742         uniqLabel = tmpLabel;
743         tmpLabel =  m_RegionsPointer[uniqLabel - 1]->GetRegionLabel();
744         } // end of chain (while loop)
745 
746       // Then re-walk the chain to change the label of each chain
747       // member to be the last one just retrieved (uniqLabel)
748       while ( currLabel != origLabel )
749         {
750         m_RegionsPointer[origLabel - 1]->SetRegionLabel(uniqLabel);
751         origLabel  = currLabel;
752         currLabel = m_RegionsPointer[origLabel - 1]->GetRegionLabel();
753         } // end of while ( currLabel != origLabel )
754       }   // end of the if condition for detecting unresolved chain
755 
756     else // The original label is unique, record it
757       {
758       uniqueLabelsVec.push_back(origLabel);
759       }
760 
761     regionsPointerIt++;
762     } // end of all regions
763 
764   // Sort the unique labels
765   std::sort( uniqueLabelsVec.begin(), uniqueLabelsVec.end() );
766 
767   // Remap sorted unique labels to consecutive values
768 
769   UnsignedIntVectorType remapLabelsVec(m_InitialNumberOfRegions, 0);
770 
771   UnsignedIntVectorType::iterator uniqueLabelsVecIterator;
772   uniqueLabelsVecIterator = uniqueLabelsVec.begin();
773 
774   RegionLabelType newLabelValue = 1;
775 
776   while ( uniqueLabelsVecIterator != uniqueLabelsVec.end() )
777     {
778     remapLabelsVec[*uniqueLabelsVecIterator - 1] = newLabelValue;
779     uniqueLabelsVecIterator++;
780     newLabelValue++;
781     }
782 
783   // Assign new consecutive labels
784   for ( iregion = 0; iregion < m_InitialNumberOfRegions; iregion++ )
785     {
786     RegionLabelType labelValue = m_RegionsPointer[iregion]->GetRegionLabel();
787 
788     newLabelValue = remapLabelsVec[labelValue - 1];
789     double                  newAreaValue = m_RegionsPointer[labelValue - 1]->GetRegionArea();
790     MeanRegionIntensityType newMeanValue =
791       m_RegionsPointer[labelValue - 1]->GetMeanRegionIntensity();
792 
793     m_RegionsPointer[iregion]->SetRegionParameters(newMeanValue,
794                                                    newAreaValue,
795                                                    newLabelValue);
796     } // end looping through the regions
797 }     // end ResolveRegions()
798 
799 template< typename TInputImage, typename TOutputImage >
800 void
801 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
PrintAlgorithmRegionStats()802 ::PrintAlgorithmRegionStats()
803 {
804   // Print the stats associated with all the regions
805   for ( unsigned int k = 0; k < m_InitialNumberOfRegions; k++ )
806     {
807     auto i = static_cast<int>( m_RegionsPointer[k]->GetRegionBorderSize() );
808     if ( i > 0 )
809       {
810       std::cout << "Stats for Region No: "
811                 << m_RegionsPointer[k]->GetRegionLabel()
812                 << std::endl;
813       m_RegionsPointer[k]->PrintRegionInfo();
814       }
815     } // end region printloop
816 }     // end PrintAlgorithmRegionStats
817 
818 template< typename TInputImage, typename TOutputImage >
819 void
820 KLMRegionGrowImageFilter< TInputImage, TOutputImage >
PrintAlgorithmBorderStats()821 ::PrintAlgorithmBorderStats()
822 {
823   // Print the stats associated with all the regions
824   for ( unsigned int k = 0; k < m_BordersDynamicPointer.size(); k++ )
825     {
826     std::cout << "Stats for Border No: " << ( k + 1 ) << std::endl;
827     m_BordersDynamicPointer[k].m_Pointer->PrintBorderInfo();
828     } // end region printloop
829 }     // end PrintAlgorithmBorderStats
830 }     // namespace itk
831 
832 #endif
833