Orfeo Toolbox  3.16
itkWatershedSegmenter.txx
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: Insight Segmentation & Registration Toolkit
4  Module: $RCSfile: itkWatershedSegmenter.txx,v $
5  Language: C++
6  Date: $Date: 2009-01-27 19:30:19 $
7  Version: $Revision: 1.37 $
8 
9  Copyright (c) Insight Software Consortium. All rights reserved.
10  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
11 
12  This software is distributed WITHOUT ANY WARRANTY; without even
13  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14  PURPOSE. See the above copyright notices for more information.
15 
16 =========================================================================*/
17 #ifndef __itkWatershedSegmenter_txx
18 #define __itkWatershedSegmenter_txx
19 
21 #include "itkImageRegionIterator.h"
24 #include <stack>
25 #include <list>
26 
27 namespace itk
28 {
29 namespace watershed
30 {
31 
32 /*
33  ----------------------------------------------------------------------------
34  Algorithm methods
35  ----------------------------------------------------------------------------
36 */
37 
38 
39 template <class TInputImage>
41 {
42  if (m_Connectivity.index != 0)
43  {
44  delete[] m_Connectivity.index;
45  }
46  if (m_Connectivity.direction !=0 )
47  {
48  delete[] m_Connectivity.direction;
49  }
50 }
51 
52 
53 template <class TInputImage>
55 {
56  //
57  // Allocate all the necessary temporary data structures and variables that
58  // will be used in this algorithm. Also re-initialize some temporary data
59  // structures that may have been used in previous updates of this filter.
60  //
61  unsigned int i;
62 
63  this->UpdateProgress(0.0);
64  if (m_DoBoundaryAnalysis == false)
65  {
66  this->GetSegmentTable()->Clear();
67  this->SetCurrentLabel(1);
68  }
69 
70  flat_region_table_t flatRegions;
71 
72  typename InputImageType::Pointer input = this->GetInputImage();
73  typename OutputImageType::Pointer output = this->GetOutputImage();
74  typename BoundaryType::Pointer boundary = this->GetBoundary();
75 
76  // ------------------------------------------------------------------------
77  //
78  // HERE ARE THE ASSUMPTIONS ABOUT REGION SIZES FOR NOW. WHEN THE PIPELINE
79  // FULLY SUPPORTS STREAMING, THESE WILL NEED TO BE CHANGED ACCORDINGLY.
80  //
81  // 1) All region sizes are equivalent. There is no distinction among
82  // regions. The region size is assumed to be padded one pixel out along each
83  // chunk face unless that face touches an actual data set boundary.
84  //
85  // 2) The ivar m_LargestPossibleRegion represents the actual size of the data
86  // set. This has to be set by the user since the pipeline sometimes clobbers
87  // the actual LargestPossibleRegion (?).
88  //
89  // -------------------------------------------------------------------------
90 
91  //
92  // Generate the "face" regions A that constitute our shared boundary with
93  // another chunk. Also determine which face regions B lie on a the true
94  // dataset boundary. The faces corresponding to B will need to be padded
95  // out a pixel when we threshold so that we can construct the retaining wall
96  // along those faces.
97  //
98  ImageRegionType regionToProcess = output->GetRequestedRegion();
99  ImageRegionType largestPossibleRegion = this->GetLargestPossibleRegion();
100  ImageRegionType thresholdImageRegion = regionToProcess;
101  ImageRegionType thresholdLargestPossibleRegion
102  = this->GetLargestPossibleRegion();
103 
104  // First we have to find the boundaries and adjust the threshold image size
105  typename ImageRegionType::IndexType tidx= thresholdImageRegion.GetIndex();
106  typename ImageRegionType::SizeType tsz = thresholdImageRegion.GetSize();
107  typename ImageRegionType::IndexType tlidx= thresholdLargestPossibleRegion.GetIndex();
108  typename ImageRegionType::SizeType tlsz = thresholdLargestPossibleRegion.GetSize();
109  for (i = 0; i < ImageDimension; ++i)
110  {
111  ImageRegionType reg;
112  typename ImageRegionType::IndexType idx = regionToProcess.GetIndex();
113  typename ImageRegionType::SizeType sz = regionToProcess.GetSize();
114 
115  // Set LOW face
116  idx[i] = regionToProcess.GetIndex()[i];
117  sz[i] = 1;
118  reg.SetSize(sz);
119  reg.SetIndex(idx);
120 
121  if (reg.GetIndex()[i] == largestPossibleRegion.GetIndex()[i])
122  {
123  // This is facing a true data set boundary
124  tsz[i] += 1; // we need to pad our threshold image on this face
125  tidx[i] -= 1;
126  tlsz[i] += 1; // we need to pad our threshold image on this face
127  tlidx[i] -= 1;
128 
129  boundary->SetValid(false, i, 0);
130  }
131  else
132  {
133  // This is an overlap with another data chunk in the data set
134  // Mark this boundary face as valid.
135  boundary->SetValid(true, i, 0);
136  }
137 
138  // Set HIGH face
139  idx[i] = (regionToProcess.GetIndex()[i]+regionToProcess.GetSize()[i]) - 1;
140  reg.SetSize(sz);
141  reg.SetIndex(idx);
142  if ( (reg.GetIndex()[i] + reg.GetSize()[i])
143  == (largestPossibleRegion.GetIndex()[i]
144  + largestPossibleRegion.GetSize()[i]) )
145  {
146  // This is facing a true data set boundary
147  tsz[i] += 1; // we need to pad our threshold image on this face
148  tlsz[i] += 1; // we need to pad our threshold image on this face
149  boundary->SetValid(false, i, 1);
150  }
151  else
152  {
153  // This is an overlap with another data chunk in the data set
154  // Mark this face as valid in the boundary.
155  boundary->SetValid(true, i, 1);
156  }
157  }
158  thresholdImageRegion.SetSize(tsz);
159  thresholdImageRegion.SetIndex(tidx);
160  thresholdLargestPossibleRegion.SetSize(tlsz);
161  thresholdLargestPossibleRegion.SetIndex(tlidx);
162 
163  // Now create and allocate the threshold image. We need a single pixel
164  // border around the NxM region we are segmenting. This means that for faces
165  // that have no overlap into another chunk, we have to pad the image.
166  typename InputImageType::Pointer thresholdImage = InputImageType::New();
167 
168  thresholdImage->SetLargestPossibleRegion(thresholdLargestPossibleRegion);
169  thresholdImage->SetBufferedRegion(thresholdImageRegion);
170  thresholdImage->SetRequestedRegion(thresholdImageRegion);
171  thresholdImage->Allocate();
172 
173  // Now threshold the image. First we calculate the dynamic range of
174  // the input. Then, the threshold operation clamps the lower
175  // intensity values at the prescribed threshold. If the data is
176  // integral, then any intensity at NumericTraits<>::max() is reduced
177  // by one intensity value. This allows the watershed algorithm to
178  // build a barrier around the image with values above the maximum
179  // intensity value which trivially stop the steepest descent search
180  // for local minima without requiring expensive boundary conditions.
181  //
182  //
183  InputPixelType minimum, maximum;
184  Self::MinMax(input, regionToProcess, minimum, maximum);
185  // cap the maximum in the image so that we can always define a pixel
186  // value that is one greater than the maximum value in the image.
187  if (NumericTraits<InputPixelType>::is_integer
188  && maximum == NumericTraits<InputPixelType>::max())
189  {
190  maximum -= NumericTraits<InputPixelType>::One;
191  }
192  // threshold the image.
193  Self::Threshold(thresholdImage, input, regionToProcess, regionToProcess,
194  static_cast<InputPixelType>((m_Threshold * (maximum - minimum)) + minimum));
195 
196 
197  //
198  // Redefine the regionToProcess in terms of the threshold image. The region
199  // to process represents all the pixels contained within the 1 pixel padded
200  // boundary of the threshold image.
201  //
202  typename ImageRegionType::SizeType irsz;
203  typename ImageRegionType::IndexType iridx;
204  for (i = 0; i < ImageDimension; ++i)
205  {
206  irsz[i] = thresholdImageRegion.GetSize()[i] - 2;
207  iridx[i] = thresholdImageRegion.GetIndex()[i] + 1;
208  }
209  regionToProcess.SetIndex(iridx);
210  regionToProcess.SetSize(irsz);
211 
212  //
213  // Initialize the connectivity information that will be used by the
214  // segmentation algorithm.
215  //
216  this->GenerateConnectivity();
217 
218  //
219  // Store the regionToProcess in the RequestedRegion of the threshold image.
220  // We are now completely done with the input image. The input image memory
221  // can be released at this point if need be.
222  //
223  thresholdImage->SetRequestedRegion(regionToProcess);
224  this->ReleaseInputs();
225 
226  //
227  // At this point we are ready to define the output
228  // buffer and allocate memory for the output image.
229  //
230  output->SetBufferedRegion(thresholdImage->GetBufferedRegion());
231  output->Allocate();
232  Self::SetOutputImageValues(output, output->GetBufferedRegion(), Self::NULL_LABEL);
233 
234  //
235  // Now we can create appropriate boundary regions for analyzing the
236  // flow at the boundaries from the requested region of the threshold
237  // image.
238  //
239  typename BoundaryType::IndexType b_idx;
240  ImageRegionType reg_b;
241  typename ImageRegionType::IndexType idx_b;
242  typename ImageRegionType::SizeType sz_b;
243 
244  for (b_idx.first = 0; b_idx.first < ImageDimension; ++b_idx.first)
245  {
246  for (b_idx.second = 0; b_idx.second < 2; ++b_idx.second)
247  {
248  if (boundary->GetValid(b_idx) == false) continue;
249  idx_b = thresholdImage->GetRequestedRegion().GetIndex();
250  sz_b = thresholdImage->GetRequestedRegion().GetSize();
251 
252  if (b_idx.second == 1) // HIGH face must adjust start index
253  idx_b[b_idx.first] += sz_b[b_idx.first] - 1;
254 
255  sz_b[b_idx.first] = 1;
256 
257  reg_b.SetIndex(idx_b);
258  reg_b.SetSize(sz_b);
259 
260  boundary->GetFace(b_idx)->SetLargestPossibleRegion(reg_b);
261  boundary->GetFace(b_idx)->SetRequestedRegion(reg_b);
262  boundary->GetFace(b_idx)->SetBufferedRegion(reg_b);
263  boundary->GetFace(b_idx)->Allocate();
264  }
265  }
266  this->UpdateProgress(0.1);
267 
268 
269  //
270  // Analyze the flow at the boundaries. This method labels all the boundary
271  // pixels that flow out of this chunk (either through gradient descent or
272  // flat-region connectivity) and constructs the appropriate Boundary
273  // data structures.
274  //
275  if (m_DoBoundaryAnalysis == true)
276  {
277  this->InitializeBoundary();
278  this->AnalyzeBoundaryFlow(thresholdImage, flatRegions, maximum +
279  NumericTraits<InputPixelType>::One);
280  }
281 
282  this->UpdateProgress(0.2);
283 
284  //
285  // Build a ``retaining wall'' around the image so that gradient descent
286  // analysis can be done without worrying about boundaries.
287  //
288  // All overlap boundary information will be overwritten, but is no longer
289  // needed now.
290  //
291  this->BuildRetainingWall( thresholdImage,
292  thresholdImage->GetBufferedRegion(),
293  maximum + NumericTraits<InputPixelType>::One );
294 
295  //
296  // Label all the local minima pixels in the image. This function also
297  // labels flat regions, defined as regions where connected pixels all have
298  // the same value.
299  //
300  this->LabelMinima(thresholdImage, thresholdImage->GetRequestedRegion(),
301  flatRegions, maximum + NumericTraits<InputPixelType>::One);
302  this->UpdateProgress(0.3);
303 
304  this->GradientDescent(thresholdImage, thresholdImage->GetRequestedRegion());
305  this->UpdateProgress(0.4);
306 
307  this->DescendFlatRegions(flatRegions, thresholdImage->GetRequestedRegion());
308  this->UpdateProgress(0.5);
309 
310  this->UpdateSegmentTable(thresholdImage, thresholdImage->GetRequestedRegion());
311  this->UpdateProgress(0.6);
312 
313  if (m_DoBoundaryAnalysis == true)
314  { this->CollectBoundaryInformation(flatRegions); }
315  this->UpdateProgress(0.7);
316 
317  if (m_SortEdgeLists == true)
318  { this->GetSegmentTable()->SortEdgeLists(); }
319  this->UpdateProgress(0.8);
320 
321  this->GetSegmentTable()->SetMaximumDepth(maximum - minimum);
322  this->UpdateProgress(1.0);
323 
324 }
325 
326 template <class TInputImage>
329 {
330  typename OutputImageType::Pointer output = this->GetOutputImage();
331  typename BoundaryType::Pointer boundary = this->GetBoundary();
332 
335 
336  typename BoundaryType::face_t::Pointer face;
337  typedef typename BoundaryType::flat_hash_t flats_t;
338  typename BoundaryType::flat_hash_t *flats;
339  typename BoundaryType::flat_hash_t::iterator flats_it;
340  typename BoundaryType::flat_region_t flr;
341  typename flat_region_table_t::iterator flrt_it;
342 
343  typename BoundaryType::IndexType idx;
344  ImageRegionType region;
345 
346  for (idx.first = 0; idx.first < ImageDimension; (idx.first)++)
347  {
348  for (idx.second = 0; idx.second < 2; (idx.second)++)
349  {
350  if (boundary->GetValid(idx) == false) continue;
351 
352  face = boundary->GetFace(idx);
353  flats = boundary->GetFlatHash(idx);
354  region = face->GetRequestedRegion();
355 
356  // Grab all the labels of the boundary pixels.
358  region);
359  labelIt = ImageRegionIterator<OutputImageType> (output, region);
360  faceIt = faceIt.Begin();
361  labelIt = labelIt.Begin();
362  while (! faceIt.IsAtEnd() )
363  {
364  faceIt.Value().label = labelIt.Get();
365 
366  // Is this a flat region that flows out?
367  flrt_it = flatRegions.find(labelIt.Get());
368  if ( faceIt.Get().flow != NULL_FLOW
369  && flrt_it != flatRegions.end() )
370  {
371  // Have we already entered this
372  // flat region into the boundary?
373  flats_it = flats->find(labelIt.Get());
374  if (flats_it == flats->end()) // NO
375  {
376  flr.bounds_min = (*flrt_it).second.bounds_min;
377  flr.min_label = *((*flrt_it).second.min_label_ptr);
378  flr.value = (*flrt_it).second.value;
379  flr.offset_list.push_back(
380  face->ComputeOffset(faceIt.GetIndex()));
381  flats->insert(
382  BoundaryFlatHashValueType(labelIt.Get(), flr));
383  flr.offset_list.clear();
384  }
385  else // YES
386  {
387  (*flats_it).second.offset_list.push_back(face->ComputeOffset(faceIt.GetIndex()));
388  }
389  }
390 
391  ++faceIt;
392  ++labelIt;
393  }
394  }
395  }
396 }
397 
398 template <class TInputImage>
401 {
403  typename BoundaryType::face_t::Pointer face;
404  typename BoundaryType::face_pixel_t fps;
405  BoundaryIndexType idx;
406 
407  fps.flow = NULL_FLOW;
408  fps.label = NULL_LABEL;
409 
410  for (idx.first = 0; idx.first < ImageDimension; ++(idx.first))
411  {
412  for (idx.second = 0; idx.second < 2; ++(idx.second))
413  {
414  if (this->GetBoundary()->GetValid(idx) == false) continue;
415  this->GetBoundary()->GetFlatHash(idx)->clear();
416  face = this->GetBoundary()->GetFace(idx);
418  (face, face->GetBufferedRegion());
419  for (faceIt = faceIt.Begin(); ! faceIt.IsAtEnd(); ++faceIt)
420  faceIt.Set(fps);
421  }
422  }
423 }
424 
425 template <class TInputImage>
428  flat_region_table_t &flatRegions,
429  InputPixelType max)
430 {
431  //
432  // NOTE: For ease of initial implementation, this method does
433  // not support arbitrary connectivity across boundaries (yet). 10-8-01 jc
434  //
435  unsigned int nCenter, i, nPos, cPos;
436  bool isSteepest;
440 
441  BoundaryIndexType idx;
442  ImageRegionType region;
444 
445  typename BoundaryType::face_pixel_t fps;
446  flat_region_t tempFlatRegion;
447 
448  typename OutputImageType::Pointer output = this->GetOutputImage();
449  typename BoundaryType::Pointer boundary = this->GetBoundary();
450 
451  for (i = 0; i < ImageDimension; ++i)
452  {
453  rad[i] = 1;
454  }
455  fps.label = NULL_LABEL;
456 
458 
459 
460  // Process each boundary region.
461  for (idx.first = 0; idx.first < ImageDimension; ++(idx.first))
462  {
463  for (idx.second = 0; idx.second < 2; ++(idx.second))
464  {
465  // Skip irrelevant boundaries
466  if (boundary->GetValid(idx) == false) continue;
467 
468  typename BoundaryType::face_t::Pointer face = boundary->GetFace(idx);
469  region = face->GetRequestedRegion();
470 
471  searchIt
472  = ConstNeighborhoodIterator<InputImageType> (rad, thresholdImage, region);
473  labelIt = NeighborhoodIterator<OutputImageType> (rad, output, region);
475 
476  nCenter = searchIt.Size() / 2;
477  searchIt.GoToBegin();
478  labelIt.GoToBegin();
479 
480  if ((idx).second == 0)
481  {
482  // Low face
483  cPos = m_Connectivity.index[(idx).first];
484  }
485  else
486  {
487  // High face
488  cPos = m_Connectivity.index[(ImageDimension - 1)
489  + (ImageDimension - (idx).first)];
490  }
491 
492  while ( ! searchIt.IsAtEnd() )
493  {
494  // Is this a flat connection?
495  if ( searchIt.GetPixel(nCenter) == searchIt.GetPixel(cPos) )
496  {
497  // Fill in the boundary flow information.
498  // Labels will be collected later.
499  fps.flow = static_cast<short>(cPos);
500  faceIt.Set(fps);
501 
502  // Are we touching flat regions
503  // that have already been labeled?
504  bool _labeled = false;
505  bool _connected = false;
506  for ( i=0; i <m_Connectivity.size; i++)
507  {
508  nPos = m_Connectivity.index[i];
509  if ( searchIt.GetPixel(nCenter) == searchIt.GetPixel(nPos)
510  && labelIt.GetPixel(nPos) != Self::NULL_LABEL
511  && labelIt.GetPixel(nPos) != labelIt.GetPixel(nCenter)
512  )
513  {
514  _connected = true;
515  if (_labeled == false)
516  {
517  labelIt.SetPixel(nCenter,
518  labelIt.GetPixel(nPos));
519  _labeled = true;
520  }
521  else
522  {
523  eqTable->Add(labelIt.GetPixel(nCenter), labelIt.GetPixel(nPos));
524  }
525  }
526  }
527  if (_connected == false ) // Add a new flat region.
528  {
529  labelIt.SetPixel(nCenter, m_CurrentLabel);
530 
531  // Add a flat region to the (global) flat region table
532  tempFlatRegion.bounds_min = max;
533  tempFlatRegion.min_label_ptr = output->GetBufferPointer() +
534  output->ComputeOffset(labelIt.GetIndex());
535  tempFlatRegion.value = searchIt.GetPixel(nCenter);
536  tempFlatRegion.is_on_boundary= true;
537  flatRegions[m_CurrentLabel] = tempFlatRegion;
538 
539  m_CurrentLabel++;
540  }
541  }
542  else // Is cPos the path of steepest descent?
543  {
544  if ( searchIt.GetPixel(cPos) < searchIt.GetPixel(nCenter) )
545  {
546  isSteepest = true;
547  for (i = 0; i < m_Connectivity.size; i++)
548  {
549  nPos = m_Connectivity.index[i];
550  if (searchIt.GetPixel(nPos) < searchIt.GetPixel(cPos))
551  {
552  isSteepest = false;
553  break;
554  }
555  }
556  }
557  else isSteepest = false;
558 
559  if (isSteepest == true)
560  {
561  // Label this pixel. It will be safely treated as a local
562  // minimum by the rest of the segmentation algorithm.
563  labelIt.SetPixel(nCenter, m_CurrentLabel);
564 
565  // Add the connectivity information
566  // to the boundary data structure.
567  fps.flow = static_cast<short>(cPos);
568  faceIt.Set(fps);
569 
570  // Since we've labeled this pixel, we need to check to
571  // make sure this is not also a flat region. If it is,
572  // then it must be entered into the flat region table
573  // or we could have problems later on.
574  for (i = 0; i < m_Connectivity.size; i++)
575  {
576  nPos = m_Connectivity.index[i];
577  if ( searchIt.GetPixel(nPos) ==
578  searchIt.GetPixel(nCenter) )
579  {
580  tempFlatRegion.bounds_min = max;
581  tempFlatRegion.min_label_ptr =
582  output->GetBufferPointer() +
583  output->ComputeOffset(labelIt.GetIndex());
584  tempFlatRegion.value =
585  searchIt.GetPixel(nCenter);
586  tempFlatRegion.is_on_boundary = false;
587  flatRegions[m_CurrentLabel] = tempFlatRegion;
588  break;
589  }
590  }
591  m_CurrentLabel++;
592  }
593  }
594 
595  ++searchIt;
596  ++labelIt;
597  ++faceIt;
598  }
599  }
600  }
601 
602  eqTable->Flatten();
603 
604  // Now relabel any equivalent regions in the boundaries.
605  for (idx.first = 0; idx.first < ImageDimension; ++(idx.first))
606  {
607  for (idx.second = 0; idx.second < 2; ++(idx.second))
608  {
609  // Skip irrelevant boundaries
610  if (boundary->GetValid(idx) == false) continue;
611 
612  typename BoundaryType::face_t::Pointer face = boundary->GetFace(idx);
613  region = face->GetRequestedRegion();
614 
615  Self::RelabelImage(output, region, eqTable);
616  }
617  }
618 
619  // Merge the flat regions in the table
620  Self::MergeFlatRegions(flatRegions, eqTable);
621 }
622 
623 template <class TInputImage>
626 {
627  unsigned int i, j, nSize, nCenter, stride;
628  int d;
629 
630  //
631  // Creates city-block style connectivity. 4-Neighbors in 2D. 6-Neighbors in
632  // 3D, etc... Order of creation MUST be lowest index to highest index in the
633  // neighborhood. I.e. for 4 connectivity,
634  //
635  // * 1 *
636  // 2 * 3
637  // * 4 *
638  //
639  // Algorithms assume this order to the connectivity.
640  //
642  for (i = 0; i < ImageDimension; ++i)
643  {
644  rad[i] = 1;
645  }
646  ConstNeighborhoodIterator<InputImageType> it(rad, this->GetInputImage(),
647  this->GetInputImage()->GetRequestedRegion());
648  nSize = it.Size();
649  nCenter = nSize >> 1;
650 
651 
652  for (i =0; i < m_Connectivity.size; i++) // initialize move list
653  {
654  for (j =0; j < ImageDimension; j++)
655  {
656  m_Connectivity.direction[i][j] = 0;
657  }
658  }
659  i = 0;
660  for (d = ImageDimension-1; d >=0; d--)
661  {
662  stride = it.GetStride(d);
663  m_Connectivity.index[i] = nCenter - stride;
664  m_Connectivity.direction[i][d] = -1;
665  i++;
666  }
667  for (d = 0; d < static_cast<int>(ImageDimension); d++)
668  {
669  stride = it.GetStride(d);
670  m_Connectivity.index[i] = nCenter + stride;
671  m_Connectivity.direction[i][d] = 1;
672  i++;
673  }
674 }
675 
676 template <class TInputImage>
679  typename Self::flat_region_table_t &flatRegions, InputPixelType Max)
680 {
681  unsigned int i, nSize, nCenter, nPos = 0;
682  bool foundSinglePixelMinimum, foundFlatRegion;
683  InputPixelType maxValue = Max;
684 
685  flat_region_t tempFlatRegion;
686  typename flat_region_table_t::iterator flatPtr;
687  InputPixelType currentValue;
689 
690  typename OutputImageType::Pointer output = this->GetOutputImage();
691 
692 
693  // Set up the iterators.
695  for (i = 0; i < ImageDimension; ++i)
696  {
697  rad[i] = 1;
698  }
699  ConstNeighborhoodIterator<InputImageType> searchIt(rad, img, region);
700  NeighborhoodIterator<OutputImageType> labelIt(rad, output, region);
701  nSize = searchIt.Size();
702  nCenter = nSize >> 1;
703 
704  // Sweep through the images. Label all local minima
705  // and record information for all the flat regions.
706  for (searchIt.GoToBegin(), labelIt.GoToBegin();
707  ! searchIt.IsAtEnd(); ++searchIt, ++labelIt)
708  {
709  foundSinglePixelMinimum = true;
710  foundFlatRegion = false;
711 
712  // If this pixel has been labeled already,
713  // skip directly to the next iteration.
714  if ( labelIt.GetPixel(nCenter) != Self::NULL_LABEL ) continue;
715 
716  // Compare current pixel value with its neighbors.
717  currentValue = searchIt.GetPixel(nCenter);
718 
719  for (i = 0; i < m_Connectivity.size; ++i)
720  {
721  nPos = m_Connectivity.index[i];
722  if ( currentValue == searchIt.GetPixel(nPos) )
723  {
724  foundFlatRegion = true;
725  break;
726  }
727  else if ( currentValue > searchIt.GetPixel(nPos) )
728  {
729  foundSinglePixelMinimum = false;
730  }
731  }
732 
733  if (foundFlatRegion)
734  {
735  if ( labelIt.GetPixel(nPos) != Self::NULL_LABEL )// If the flat region is already
736  { // labeled, label this to match.
737  labelIt.SetPixel(nCenter, labelIt.GetPixel(nPos));
738  }
739  else // Add a new flat region to the table.
740  { // Initialize its contents.
741  labelIt.SetPixel(nCenter, m_CurrentLabel);
742  nPos = m_Connectivity.index[0];
743 
744  tempFlatRegion.bounds_min = maxValue;
745  tempFlatRegion.min_label_ptr = labelIt[nPos];
746  tempFlatRegion.value = currentValue;
747  flatRegions[m_CurrentLabel] = tempFlatRegion;
748  m_CurrentLabel = m_CurrentLabel + 1;
749  }
750 
751  // While we're at it, check to see if we have just linked two flat
752  // regions with the same height value. Save that info for later.
753  for ( i++; i <m_Connectivity.size; ++i)
754  {
755  nPos = m_Connectivity.index[i];
756  if ( searchIt.GetPixel(nCenter) == searchIt.GetPixel(nPos)
757  && labelIt.GetPixel(nPos) != Self::NULL_LABEL
758  && labelIt.GetPixel(nPos) != labelIt.GetPixel(nCenter)
759  )
760  {
761  equivalentLabels->Add(labelIt.GetPixel(nCenter),
762  labelIt.GetPixel(nPos));
763  }
764  }
765  }
766  else if (foundSinglePixelMinimum)
767  {
768  labelIt.SetPixel(nCenter, m_CurrentLabel);
769  m_CurrentLabel = m_CurrentLabel + 1;
770  }
771  }
772 
773  // Merge the flat regions that we identified as connected components.
774  Self::MergeFlatRegions(flatRegions, equivalentLabels);
775 
776  // Relabel the image with the merged regions.
777  Self::RelabelImage(output, region, equivalentLabels);
778 
779  equivalentLabels->Clear();
780 
781  // Now make another pass to establish the
782  // boundary values for the flat regions.
783  for (searchIt.GoToBegin(), labelIt.GoToBegin();
784  ! searchIt.IsAtEnd(); ++searchIt, ++labelIt)
785  {
786  flatPtr = flatRegions.find( labelIt.GetPixel(nCenter) );
787  if ( flatPtr != flatRegions.end() ) // If we are in a flat region
788  { // Search the connectivity neighborhood
789  // for lesser boundary pixels.
790  for (i = 0; i < m_Connectivity.size; ++i)
791  {
792  nPos = m_Connectivity.index[i];
793 
794  if ( labelIt.GetPixel(nPos) != labelIt.GetPixel(nCenter) &&
795  searchIt.GetPixel(nPos) < (*flatPtr).second.bounds_min )
796  { // If this is a boundary pixel && has a lesser value than
797  // the currently recorded value...
798  (*flatPtr).second.bounds_min = searchIt.GetPixel(nPos);
799  (*flatPtr).second.min_label_ptr = labelIt[nPos];
800  }
801  if ( searchIt.GetPixel(nCenter) == searchIt.GetPixel(nPos) )
802  {
803  if ( labelIt.GetPixel(nPos) != NULL_LABEL )
804  {
805  // Pick up any equivalencies we missed before.
806  equivalentLabels->Add(labelIt.GetPixel(nCenter),
807  labelIt.GetPixel(nPos));
808  }
809  // If the following is encountered, it means that there is a
810  // logic flaw in the first pass of this algorithm where flat
811  // regions are initially detected and linked.
812 #ifndef NDEBUG
813  else itkDebugMacro("An unexpected but non-fatal error has occurred.");
814 #endif
815  }
816 
817  }
818  }
819  }
820 
821  // Merge the flat regions that we identified as connected components.
822  Self::MergeFlatRegions(flatRegions, equivalentLabels);
823 
824  // Relabel the image with the merged regions.
825  Self::RelabelImage(output, region, equivalentLabels);
826 }
827 
828 template <class TInputImage>
831  ImageRegionType region)
832 {
833  typename OutputImageType::Pointer output = this->GetOutputImage();
834 
835  InputPixelType minVal;
836  unsigned int i, nPos;
837  typename InputImageType::OffsetType moveIndex;
838  unsigned long newLabel;
839  std::stack< unsigned long * > updateStack;
840 
841  //
842  // Set up our iterators.
843  //
846  for (i = 0; i < ImageDimension; ++i)
847  {
848  rad[i] = 1;
849  zeroRad[i] = 0;
850  }
852  valueIt(rad, img, region);
854  labelIt(zeroRad, output, region);
855  ImageRegionIterator<OutputImageType> it(output, region);
856 
857  //
858  // Sweep through the image and trace all unlabeled
859  // pixels to a labeled region
860  //
861  for (it = it.Begin(); ! it.IsAtEnd(); ++it)
862  {
863  if ( it.Get() == NULL_LABEL )
864  {
865  valueIt.SetLocation(it.GetIndex());
866  labelIt.SetLocation(it.GetIndex());
867  newLabel = NULL_LABEL; // Follow the path of steep-
868  while( newLabel == NULL_LABEL ) // est descent until a label
869  { // is found.
870  updateStack.push(labelIt.GetCenterPointer());
871  minVal = valueIt.GetPixel(m_Connectivity.index[0]);
872  moveIndex = m_Connectivity.direction[0];
873  for (unsigned int ii = 1; ii < m_Connectivity.size; ++ii)
874  {
875  nPos = m_Connectivity.index[ii];
876  if ( valueIt.GetPixel(nPos) < minVal)
877  {
878  minVal = valueIt.GetPixel(nPos);
879  moveIndex = m_Connectivity.direction[ii];
880  }
881  }
882  valueIt += moveIndex;
883  labelIt += moveIndex;
884  newLabel = labelIt.GetPixel(0);
885  }
886 
887  while( ! updateStack.empty() ) // Update all the pixels we've traversed
888  {
889  *(updateStack.top()) = newLabel;
890  updateStack.pop();
891  }
892  }
893  }
894 
895 }
896 
897 template <class TInputImage>
900  ImageRegionType imageRegion)
901 {
902 
903  typename OutputImageType::Pointer output = this->GetOutputImage();
904  // Assumes all pixels are labeled in the image. Steps through the flat
905  // regions and equates each one with the label at its lowest boundary
906  // point. Flat basins are preserved as their own regions. The output image is
907  // relabeled to reflect these equivalencies.
909 
910  for (typename flat_region_table_t::const_iterator region = flatRegionTable.begin();
911  region != flatRegionTable.end(); ++region)
912  {
913  if ( ((*region).second.bounds_min < (*region).second.value)
914  && (! (*region).second.is_on_boundary) )
915  {
916  equivalentLabels->Add((*region).first, *((*region).second.min_label_ptr));
917  }
918  }
919 
920  equivalentLabels->Flatten();
921  Self::RelabelImage(output, imageRegion, equivalentLabels);
922 }
923 
924 
925 template <class TInputImage>
928 {
929  edge_table_hash_t edgeHash;
930  edge_table_t tempEdgeTable;
931 
932  typename edge_table_hash_t::iterator edge_table_entry_ptr;
933  typename edge_table_t::iterator edge_ptr;
934 
935  unsigned int i, nPos;
937  typename SegmentTableType::segment_t *segment_ptr;
938  typename SegmentTableType::segment_t temp_segment;
939  unsigned long segment_label;
940 
941  InputPixelType lowest_edge;
942 
943  // Grab the data we need.
944  typename OutputImageType::Pointer output = this->GetOutputImage();
945  typename SegmentTableType::Pointer segments = this->GetSegmentTable();
946 
947  // Set up some iterators.
948  for (i = 0; i < ImageDimension; i++)
949  {
950  hoodRadius[i] = 1;
951  }
952  ConstNeighborhoodIterator<InputImageType> searchIt(hoodRadius,input,region);
953  NeighborhoodIterator<OutputImageType> labelIt(hoodRadius, output, region);
954 
955  unsigned long hoodCenter = searchIt.Size() >> 1;
956 
957  for (searchIt.GoToBegin(), labelIt.GoToBegin(); ! searchIt.IsAtEnd();
958  ++searchIt, ++labelIt)
959  {
960  segment_label = labelIt.GetPixel(hoodCenter);
961 
962  // Find the segment corresponding to this label
963  // and update its minimum value if necessary.
964  segment_ptr = segments->Lookup(segment_label);
965  edge_table_entry_ptr = edgeHash.find(segment_label);
966  if (segment_ptr == 0) // This segment not yet identified.
967  { // So add it to the table.
968  temp_segment.min = searchIt.GetPixel(hoodCenter);
969  segments->Add(segment_label, temp_segment);
970  typedef typename edge_table_hash_t::value_type ValueType;
971  edgeHash.insert(ValueType(segment_label,
972  tempEdgeTable) );
973 
974  edge_table_entry_ptr = edgeHash.find(segment_label);
975  }
976  else if (searchIt.GetPixel(hoodCenter) < segment_ptr->min)
977  {
978  segment_ptr->min = searchIt.GetPixel(hoodCenter);
979  }
980 
981  // Look up each neighboring segment in this segment's edge table.
982  // If an edge exists, compare (and reset) the minimum edge value.
983  // Note that edges are located *between* two adjacent pixels and
984  // the value is taken to be the maximum of the two adjacent pixel
985  // values.
986  for (i = 0; i < m_Connectivity.size; ++i)
987  {
988  nPos = m_Connectivity.index[i];
989  if (labelIt.GetPixel(nPos) != segment_label
990  && labelIt.GetPixel(nPos) != NULL_LABEL)
991  {
992  if (searchIt.GetPixel(nPos) < searchIt.GetPixel(hoodCenter))
993  lowest_edge = searchIt.GetPixel(hoodCenter); // We want the
994  else lowest_edge = searchIt.GetPixel(nPos); // max of the
995  // adjacent pixels
996 
997  edge_ptr = (*edge_table_entry_ptr).second.find(labelIt.GetPixel(nPos));
998  if ( edge_ptr == (*edge_table_entry_ptr).second.end() )
999  { // This edge has not been identified yet.
1000  typedef typename edge_table_t::value_type ValueType;
1001  (*edge_table_entry_ptr).second.insert(
1002  ValueType(labelIt.GetPixel(nPos), lowest_edge) );
1003  }
1004  else if (lowest_edge < (*edge_ptr).second)
1005  {
1006  (*edge_ptr).second = lowest_edge;
1007  }
1008  }
1009  }
1010 
1011  }
1012 
1013  //
1014  // Copy all of the edge tables into the edge lists of the
1015  // segment table.
1016  //
1017  unsigned long listsz;
1018  typename SegmentTableType::edge_list_t::iterator list_ptr;
1019  for (edge_table_entry_ptr = edgeHash.begin();
1020  edge_table_entry_ptr != edgeHash.end();
1021  edge_table_entry_ptr++)
1022  {
1023  // Lookup the corresponding segment entry
1024  segment_ptr = segments->Lookup((*edge_table_entry_ptr).first);
1025  if ( segment_ptr == 0 )
1026  {
1027  itkGenericExceptionMacro ( << "UpdateSegmentTable:: An unexpected and fatal error has occurred.");
1028  }
1029 
1030  // Copy into the segment list
1031  listsz = static_cast<unsigned long>( (*edge_table_entry_ptr).second.size() );
1032  segment_ptr->edge_list.resize(listsz);
1033  edge_ptr = (*edge_table_entry_ptr).second.begin();
1034  list_ptr = segment_ptr->edge_list.begin();
1035  while ( edge_ptr != (*edge_table_entry_ptr).second.end() )
1036  {
1037  list_ptr->label = (*edge_ptr).first;
1038  list_ptr->height= (*edge_ptr).second;
1039  edge_ptr++;
1040  list_ptr++;
1041  }
1042 
1043  // Clean up memory as we go
1044  (*edge_table_entry_ptr).second.clear();
1045  }
1046 }
1047 
1048 template <class TInputImage>
1051  ImageRegionType region,
1052  InputPixelType value)
1053 {
1054  unsigned int i;
1055  typename ImageRegionType::SizeType sz;
1056  typename ImageRegionType::IndexType idx;
1057  ImageRegionType reg;
1058 
1059  // Loop through the dimensions and populate the LOW and HIGH faces regions.
1060  for (i = 0; i < ImageDimension; ++i)
1061  {
1062  idx = region.GetIndex(); // LOW face
1063  sz = region.GetSize();
1064  sz[i] = 1;
1065  reg.SetIndex(idx);
1066  reg.SetSize(sz);
1067  Segmenter::SetInputImageValues(img, reg, value);
1068  idx[i] = region.GetSize()[i] + region.GetIndex()[i] - 1; // HIGH face
1069  reg.SetIndex(idx);
1070  Segmenter::SetInputImageValues(img, reg, value);
1071  }
1072 }
1073 
1074 /*
1075  ----------------------------------------------------------------------------
1076  Algorithm helper methods and debugging methods
1077  ----------------------------------------------------------------------------
1078 */
1079 template <class TInputImage>
1082  ImageRegionType region,
1083  InputPixelType value)
1084 {
1085  ImageRegionIterator<InputImageType> it(img, region);
1086  it = it.Begin();
1087  while (! it.IsAtEnd() )
1088  {
1089  it.Set(value);
1090  ++it;
1091  }
1092 }
1093 
1094 
1095 template <class TInputImage>
1098  ImageRegionType region,
1099  unsigned long value)
1100 {
1101  ImageRegionIterator<OutputImageType> it(img, region);
1102  it = it.Begin();
1103  while (! it.IsAtEnd() )
1104  {
1105  it.Set(value);
1106  ++it;
1107  }
1108 }
1109 
1110 template <class TInputImage>
1113  InputPixelType &min, InputPixelType &max)
1114 {
1115  ImageRegionIterator<InputImageType> it(img, region);
1116  it = it.Begin();
1117  min = it.Value();
1118  max = it.Value();
1119  while (! it.IsAtEnd())
1120  {
1121  if (it.Get() > max) max = it.Get();
1122  if (it.Get() < min) min = it.Get();
1123  ++it;
1124  }
1125 }
1126 
1127 template <class TInputImage>
1130  EquivalencyTable::Pointer eqTable)
1131 {
1132  // Note that the labels must have no interdependencies. That is,
1133  // every key must map to a value that is not itself a key in the
1134  // table. This means that you must always merge label->first with
1135  // label->second (a to b). EquivalencyTable can be converted to this
1136  // format with its Flatten() method.
1137  eqTable->Flatten();
1138 
1139  typename flat_region_table_t::iterator a, b;
1140  for (EquivalencyTable::ConstIterator it = eqTable->Begin();
1141  it != eqTable->End(); ++it)
1142  {
1143  if ( ((a = regions.find((*it).first)) == regions.end())
1144  || ((b = regions.find((*it).second)) == regions.end()) )
1145  {
1146  itkGenericExceptionMacro ( << "MergeFlatRegions:: An unexpected and fatal error has occurred.");
1147  }
1148 
1149  if ((*a).second.bounds_min < (*b).second.bounds_min)
1150  {
1151  (*b).second.bounds_min = (*a).second.bounds_min;
1152  (*b).second.min_label_ptr = (*a).second.min_label_ptr;
1153  }
1154 
1155  regions.erase(a);
1156  }
1157 }
1158 
1159 template <class TInputImage>
1162  ImageRegionType region,
1163  EquivalencyTable::Pointer eqTable)
1164 {
1165  eqTable->Flatten();
1166 
1167  unsigned long temp;
1168  ImageRegionIterator<OutputImageType> it(img, region);
1169 
1170  it = it.Begin();
1171  while ( !it.IsAtEnd() )
1172  {
1173  temp = eqTable->Lookup(it.Get());
1174  if (temp != it.Get()) { it.Set(temp);}
1175  ++it;
1176  }
1177 }
1178 
1179 template <class TInputImage>
1182  InputImageTypePointer source,
1183  const ImageRegionType source_region,
1184  const ImageRegionType destination_region,
1185  InputPixelType threshold)
1186 {
1187  ImageRegionIterator<InputImageType> dIt(destination, destination_region);
1188  ImageRegionIterator<InputImageType> sIt(source, source_region);
1189 
1190  dIt = dIt.Begin();
1191  sIt = sIt.Begin();
1192 
1193  // Assumes that source_region and destination region are the same size. Does
1194  // no checking!!
1195  if (NumericTraits<InputPixelType>::is_integer)
1196  {
1197  // integral data type, if any pixel is at the maximum possible
1198  // value for the data type, then drop the value by one intensity
1199  // value. This the watershed algorithm to construct a "barrier" or
1200  // "wall" around the image that will stop the watershed without
1201  // requiring a expensive boundary condition checks.
1202  while ( ! dIt.IsAtEnd() )
1203  {
1204  InputPixelType tmp = sIt.Get();
1205  if ( tmp < threshold )
1206  {
1207  dIt.Set(threshold);
1208  }
1209  else if (tmp == NumericTraits<InputPixelType>::max())
1210  {
1211  dIt.Set(tmp - NumericTraits<InputPixelType>::One);
1212  }
1213  else
1214  {
1215  dIt.Set(tmp);
1216  }
1217  ++dIt;
1218  ++sIt;
1219  }
1220  }
1221  else
1222  {
1223  // floating point data, no need to worry about overflow
1224  while ( ! dIt.IsAtEnd() )
1225  {
1226  if ( sIt.Get() < threshold )
1227  {
1228  dIt.Set(threshold);
1229  }
1230  else
1231  {
1232  dIt.Set(sIt.Get());
1233  }
1234  ++dIt;
1235  ++sIt;
1236  }
1237  }
1238 }
1239 
1240 /*
1241  ----------------------------------------------------------------------------
1242  Pipeline methods
1243  ----------------------------------------------------------------------------
1244 */
1245 template<class TInputImage>
1248 ::MakeOutput(unsigned int idx)
1249 {
1250  if (idx == 0)
1251  { return static_cast<DataObject*>(OutputImageType::New().GetPointer());}
1252  else if (idx == 1)
1253  { return static_cast<DataObject*>(SegmentTableType::New().GetPointer());}
1254  else if (idx == 2)
1255  { return static_cast<DataObject*>(BoundaryType::New().GetPointer());}
1256  else return 0;
1257 }
1258 
1259 
1260 template <class TInputImage>
1261 void
1263 {
1264  unsigned int i;
1265  // call the superclass' implementation of this method
1266  Superclass::UpdateOutputInformation();
1267 
1268  // get pointers to the input and output
1269  typename InputImageType::Pointer inputPtr = this->GetInputImage();
1270  typename OutputImageType::Pointer outputPtr = this->GetOutputImage();
1271 
1272  if ( !inputPtr || !outputPtr )
1273  {
1274  return;
1275  }
1276  // we need to compute the output spacing, the output image size, and the
1277  // output image start index
1278  const typename InputImageType::SizeType& inputSize
1279  = inputPtr->GetLargestPossibleRegion().GetSize();
1280  const typename InputImageType::IndexType& inputStartIndex
1281  = inputPtr->GetLargestPossibleRegion().GetIndex();
1282 
1283  typename OutputImageType::SizeType outputSize;
1284  typename OutputImageType::IndexType outputStartIndex;
1285 
1286  for (i = 0; i < OutputImageType::ImageDimension; i++)
1287  {
1288  outputSize[i] = inputSize[i];
1289  outputStartIndex[i] = inputStartIndex[i];
1290  }
1291 
1292  typename OutputImageType::RegionType outputLargestPossibleRegion;
1293  outputLargestPossibleRegion.SetSize( outputSize );
1294  outputLargestPossibleRegion.SetIndex( outputStartIndex );
1295 
1296  outputPtr->SetLargestPossibleRegion( outputLargestPossibleRegion );
1297 }
1298 
1299 template <class TInputImage>
1301 {
1302  // call the superclass' implementation of this method
1303  Superclass::GenerateInputRequestedRegion();
1304 
1305  // get pointers to the input and output
1306  typename InputImageType::Pointer inputPtr = this->GetInputImage();
1307  typename OutputImageType::Pointer outputPtr = this->GetOutputImage();
1308 
1309  if ( !inputPtr || !outputPtr )
1310  {
1311  return;
1312  }
1313 
1314  //
1315  // FOR NOW WE'LL JUST SET THE INPUT REGION TO THE OUTPUT REGION
1316  // AND OVERRIDE THIS LATER
1317  //
1318  inputPtr->SetRequestedRegion( outputPtr->GetRequestedRegion() );
1319 
1320 }
1321 
1322 template <class TInputImage>
1323 void
1326 {
1327  // Only the Image output need to be propagated through.
1328  // No choice but to use RTTI here.
1329  ImageBase<ImageDimension> *imgData;
1331  imgData = dynamic_cast<ImageBase<ImageDimension> * >(output);
1332  typename TInputImage::RegionType c_reg;
1333 
1334  if (imgData)
1335  {
1336  std::vector<ProcessObject::DataObjectPointer>::size_type idx;
1337  for (idx = 0; idx < this->GetOutputs().size(); ++idx)
1338  {
1339  if (this->GetOutputs()[idx] && this->GetOutputs()[idx] != output)
1340  {
1341  op = dynamic_cast<ImageBase<ImageDimension>
1342  *>(this->GetOutputs()[idx].GetPointer());
1343 
1344  if (op) this->GetOutputs()[idx]->SetRequestedRegion(output);
1345  }
1346  }
1347  }
1348 }
1349 
1350 template <class TInputImage>
1353 {
1354  m_Threshold = 0.0;
1355  m_MaximumFloodLevel = 1.0;
1356  m_CurrentLabel = 1;
1357  m_DoBoundaryAnalysis = false;
1358  m_SortEdgeLists = true;
1359  m_Connectivity.direction = 0;
1360  m_Connectivity.index = 0;
1361  typename OutputImageType::Pointer img
1362  = static_cast<OutputImageType*>(this->MakeOutput(0).GetPointer());
1363  typename SegmentTableType::Pointer st
1364  = static_cast<SegmentTableType*>(this->MakeOutput(1).GetPointer());
1365  typename BoundaryType::Pointer bd
1366  = static_cast<BoundaryType*>(this->MakeOutput(2).GetPointer());
1367  this->SetNumberOfRequiredOutputs(3);
1368  this->ProcessObject::SetNthOutput(0, img.GetPointer());
1369  this->ProcessObject::SetNthOutput(1, st.GetPointer());
1370  this->ProcessObject::SetNthOutput(2, bd.GetPointer());
1371 
1372  // Allocate memory for connectivity
1373  m_Connectivity.size = 2 * ImageDimension;
1374  m_Connectivity.index = new unsigned int[m_Connectivity.size];
1375  m_Connectivity.direction
1376  = new typename InputImageType::OffsetType[m_Connectivity.size];
1377 }
1378 
1379 template<class TInputImage>
1380 void
1382 ::PrintSelf(std::ostream& os, Indent indent) const
1383 {
1384  Superclass::PrintSelf(os,indent);
1385  os << indent << "SortEdgeLists: " << m_SortEdgeLists << std::endl;
1386  os << indent << "DoBoundaryAnalysis: " << m_DoBoundaryAnalysis << std::endl;
1387  os << indent << "Threshold: " << m_Threshold << std::endl;
1388  os << indent << "MaximumFloodLevel: " << m_MaximumFloodLevel << std::endl;
1389  os << indent << "CurrentLabel: " << m_CurrentLabel << std::endl;
1390 }
1391 
1392 }// end namespace watershed
1393 }// end namespace itk
1394 
1395 #endif

Generated at Sun Feb 3 2013 00:14:59 for Orfeo Toolbox with doxygen 1.8.1.1