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