OTB  9.0.0
Orfeo Toolbox
otbLabelImageRegionPruningFilter.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2022 Centre National d'Etudes Spatiales (CNES)
3  *
4  * This file is part of Orfeo Toolbox
5  *
6  * https://www.orfeo-toolbox.org/
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 
22 #ifndef otbLabelImageRegionPruningFilter_hxx
23 #define otbLabelImageRegionPruningFilter_hxx
24 
26 #include "itkImageRegionConstIteratorWithIndex.h"
27 #include "itkImageRegionIterator.h"
28 #include "itkProgressReporter.h"
29 
30 
31 namespace otb
32 {
33 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
35 {
36  m_NumberOfComponentsPerPixel = 0;
37  m_MinRegionSize = 100;
38 
39  this->SetNumberOfRequiredInputs(2);
40 
41  this->SetNumberOfRequiredOutputs(2);
42  this->SetNthOutput(0, OutputLabelImageType::New());
43  this->SetNthOutput(1, OutputClusteredImageType::New());
44 }
45 
46 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
48  const TInputLabelImage* labelImage)
49 {
50  // Process object is not const-correct so the const casting is required.
51  this->SetNthInput(0, const_cast<TInputLabelImage*>(labelImage));
52 }
53 
54 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
56  const TInputSpectralImage* spectralImage)
57 {
58  // Process object is not const-correct so the const casting is required.
59  this->SetNthInput(1, const_cast<TInputSpectralImage*>(spectralImage));
60 }
61 
62 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
64 {
65  return dynamic_cast<TInputLabelImage*>(itk::ProcessObject::GetInput(0));
66 }
67 
68 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
70 {
71  return dynamic_cast<TInputSpectralImage*>(itk::ProcessObject::GetInput(1));
72 }
73 
74 
75 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
77 {
78 }
79 
80 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
82 {
83  if (this->GetNumberOfOutputs() < 1)
84  {
85  return nullptr;
86  }
87  return static_cast<OutputLabelImageType*>(this->itk::ProcessObject::GetOutput(0));
88 }
89 
90 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
92 {
93  if (this->GetNumberOfOutputs() < 1)
94  {
95  return 0;
96  }
97  return static_cast<OutputLabelImageType*>(this->itk::ProcessObject::GetOutput(0));
98 }
99 
100 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
103 {
104  if (this->GetNumberOfOutputs() < 2)
105  {
106  return nullptr;
107  }
108  return static_cast<OutputClusteredImageType*>(this->itk::ProcessObject::GetOutput(1));
109 }
110 
111 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
114 {
115  if (this->GetNumberOfOutputs() < 2)
116  {
117  return 0;
118  }
119  return static_cast<OutputClusteredImageType*>(this->itk::ProcessObject::GetOutput(1));
120 }
121 
122 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
124 {
125  Superclass::GenerateOutputInformation();
126 
127  unsigned int numberOfComponentsPerPixel = this->GetInputSpectralImage()->GetNumberOfComponentsPerPixel();
128 
129  if (this->GetClusteredOutput())
130  {
131  this->GetClusteredOutput()->SetNumberOfComponentsPerPixel(numberOfComponentsPerPixel);
132  }
133 }
134 
135 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
137  itk::DataObject* itkNotUsed(output))
138 {
139  // This filter requires all of the output images in the buffer.
140  for (unsigned int j = 0; j < this->GetNumberOfOutputs(); j++)
141  {
142  if (this->itk::ProcessObject::GetOutput(j))
143  {
144  this->itk::ProcessObject::GetOutput(j)->SetRequestedRegionToLargestPossibleRegion();
145  }
146  }
147 }
148 
149 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
151 {
152  typename InputSpectralImageType::Pointer spectralImage = this->GetInputSpectralImage();
153  typename InputLabelImageType::Pointer inputLabelImage = this->GetInputLabelImage();
154  typename OutputLabelImageType::Pointer outputLabelImage = this->GetLabelOutput();
155  typename OutputClusteredImageType::Pointer outputClusteredImage = this->GetClusteredOutput();
156 
157  // Allocate output
158  outputLabelImage->SetBufferedRegion(outputLabelImage->GetRequestedRegion());
159  outputLabelImage->Allocate();
160  outputClusteredImage->SetBufferedRegion(outputClusteredImage->GetRequestedRegion());
161  outputClusteredImage->Allocate();
162 
163  m_NumberOfComponentsPerPixel = spectralImage->GetNumberOfComponentsPerPixel();
164 
165  // std::cout << "Copy input label image to output label image" << std::endl;
166  // Copy input label image to output label image
167  typename itk::ImageRegionConstIterator<InputLabelImageType> inputIt(inputLabelImage, outputLabelImage->GetRequestedRegion());
168  typename itk::ImageRegionIterator<OutputLabelImageType> outputIt(outputLabelImage, outputLabelImage->GetRequestedRegion());
169  inputIt.GoToBegin();
170  outputIt.GoToBegin();
171  while (!inputIt.IsAtEnd())
172  {
173  outputIt.Set(inputIt.Get());
174  ++inputIt;
175  ++outputIt;
176  }
177 
178  RegionAdjacencyMapType regionAdjacencyMap = LabelImageToRegionAdjacencyMap(outputLabelImage);
179  unsigned int regionCount = regionAdjacencyMap.size() - 1;
180 
181  // Initialize arrays for mode information
182  m_CanonicalLabels.clear();
183  m_CanonicalLabels.resize(regionCount + 1);
184 
185  m_Modes.clear();
186  m_Modes.reserve(regionCount + 1);
187  for (unsigned int i = 0; i < regionCount + 1; ++i)
188  {
189  m_Modes.push_back(SpectralPixelType(m_NumberOfComponentsPerPixel));
190  }
191 
192  m_PointCounts.clear();
193  m_PointCounts.resize(regionCount + 1); // = std::vector<unsigned int>(regionCount+1);
194 
195 
196  // Associate each label to a spectral value, a canonical label and a point count
197  typename itk::ImageRegionConstIterator<InputLabelImageType> inputItWithIndex(inputLabelImage, outputLabelImage->GetRequestedRegion());
198  inputItWithIndex.GoToBegin();
199  while (!inputItWithIndex.IsAtEnd())
200  {
201  LabelType label = inputItWithIndex.Get();
202  // if label has not been initialized yet ..
203  if (m_PointCounts[label] == 0)
204  {
205  // m_CanonicalLabels[label] = label;
206  m_Modes[label] = spectralImage->GetPixel(inputItWithIndex.GetIndex());
207  }
208  m_PointCounts[label]++;
209  ++inputItWithIndex;
210  }
211  // Region Pruning
212 
213  bool finishedPruning = false;
214  unsigned int pruneIterations = 0;
215  unsigned int minRegionCount = 0;
216  do
217  {
218 
219  minRegionCount = 0;
220 
221 
222  // Initialize Canonical Labels
223  for (LabelType curLabel = 1; curLabel <= regionCount; ++curLabel)
224  {
225  m_CanonicalLabels[curLabel] = curLabel;
226  /* if(m_PointCounts[curLabel] <= m_MinRegionSize)
227  {
228  minRegionCount++;
229  }*/
230  }
231  // std::cout<<minRegionCount<<" regions are too be fused"<<std::endl;
232  // Iterate over all regions
233 
234  for (LabelType curLabel = 1; curLabel <= regionCount; ++curLabel)
235  {
236 
237  if ((m_PointCounts[curLabel] == 0) || (m_PointCounts[curLabel] > m_MinRegionSize))
238  {
239  // do not process empty regions
240  continue;
241  }
242  minRegionCount++;
243  // std::cout<<" point in label "<<curLabel<<" "<<m_PointCounts[curLabel]<<std::endl;
244  const SpectralPixelType& curSpectral = m_Modes[curLabel];
245 
246  // Iterate over all adjacent regions and check for merge
247  typename AdjacentLabelsContainerType::const_iterator adjIt = regionAdjacencyMap[curLabel].begin();
248 
249  LabelType neighborCandidate = 0;
250  RealType bestNorm2 = itk::NumericTraits<float>::max();
251 
252  while (adjIt != regionAdjacencyMap[curLabel].end())
253  {
254  LabelType adjLabel = *adjIt;
255  assert(adjLabel <= regionCount);
256  const SpectralPixelType& adjSpectral = m_Modes[adjLabel];
257  // Check condition to merge regions
258 
259 
260  // find the spectrally nearest adjacent label
261 
262  RealType norm2 = 0;
263  for (unsigned int comp = 0; comp < m_NumberOfComponentsPerPixel; ++comp)
264  {
265  RealType e;
266  e = (curSpectral[comp] - adjSpectral[comp]);
267  norm2 += e * e;
268  }
269  if (norm2 < bestNorm2)
270  {
271  bestNorm2 = norm2;
272  neighborCandidate = adjLabel;
273  };
274  ++adjIt;
275  } // end of loop over adjacent labels
276 
277  if (neighborCandidate != 0)
278  {
279  // Find canonical label for current region
280  LabelType curCanLabel = curLabel; // m_CanonicalLabels[curLabel];
281  while (m_CanonicalLabels[curCanLabel] != curCanLabel)
282  {
283  curCanLabel = m_CanonicalLabels[curCanLabel];
284  }
285 
286  // Find canonical label for adjacent region
287  LabelType adjCanLabel = neighborCandidate; // m_CanonicalLabels[curLabel];
288  while (m_CanonicalLabels[adjCanLabel] != adjCanLabel)
289  {
290  adjCanLabel = m_CanonicalLabels[adjCanLabel];
291  }
292 
293  // Assign same canonical label to both regions
294  if (curCanLabel < adjCanLabel)
295  {
296  m_CanonicalLabels[adjCanLabel] = curCanLabel;
297  }
298  else
299  {
300  m_CanonicalLabels[m_CanonicalLabels[curCanLabel]] = adjCanLabel;
301  m_CanonicalLabels[curCanLabel] = adjCanLabel;
302  }
303  }
304 
305 
306  } // end of loop over labels
307 
308  // std::cout << "Simplify the table of canonical labels" << std::endl;
309  /* Simplify the table of canonical labels */
310  for (LabelType i = 1; i < regionCount + 1; ++i)
311  {
312  LabelType can = i;
313  while (m_CanonicalLabels[can] != can)
314  {
315  can = m_CanonicalLabels[can];
316  }
317  m_CanonicalLabels[i] = can;
318  }
319 
320  // std::cout << "merge regions with same canonical label" << std::endl;
321  /* Merge regions with same canonical label */
322  /* - update modes and point counts */
323  std::vector<SpectralPixelType> newModes;
324  newModes.reserve(regionCount + 1); //(regionCount+1, SpectralPixelType(m_NumberOfComponentsPerPixel));
325  for (unsigned int i = 0; i < regionCount + 1; ++i)
326  {
327  newModes.push_back(SpectralPixelType(m_NumberOfComponentsPerPixel));
328  }
329  std::vector<unsigned int> newPointCounts(regionCount + 1);
330 
331  for (unsigned int i = 1; i < regionCount + 1; ++i)
332  {
333  newModes[i].Fill(0);
334  newPointCounts[i] = 0;
335  }
336 
337  for (unsigned int i = 1; i < regionCount + 1; ++i)
338  {
339  LabelType canLabel = m_CanonicalLabels[i];
340  unsigned int nPoints = m_PointCounts[i];
341  for (unsigned int comp = 0; comp < m_NumberOfComponentsPerPixel; ++comp)
342  {
343  newModes[canLabel][comp] += nPoints * m_Modes[i][comp];
344  }
345  newPointCounts[canLabel] += nPoints;
346  }
347 
348  // std::cout << "re-labeling" << std::endl;
349  /* re-labeling */
350  std::vector<LabelType> newLabels(regionCount + 1);
351  std::vector<bool> newLabelSet(regionCount + 1);
352  for (unsigned int i = 1; i < regionCount + 1; ++i)
353  {
354  newLabelSet[i] = false;
355  }
356 
357  LabelType label = 0;
358  for (unsigned int i = 1; i < regionCount + 1; ++i)
359  {
360  LabelType canLabel = m_CanonicalLabels[i];
361  if (newLabelSet[canLabel] == false)
362  {
363  newLabelSet[canLabel] = true;
364  label++;
365  newLabels[canLabel] = label;
366  unsigned int nPoints = newPointCounts[canLabel];
367  for (unsigned int comp = 0; comp < m_NumberOfComponentsPerPixel; ++comp)
368  {
369  m_Modes[label][comp] = newModes[canLabel][comp] / nPoints;
370  }
371 
372  m_PointCounts[label] = newPointCounts[canLabel];
373  }
374  }
375 
376  unsigned int oldRegionCount = regionCount;
377 
378  regionCount = label;
379 
380  /* reassign labels in label image */
381  outputIt.GoToBegin();
382  while (!outputIt.IsAtEnd())
383  {
384 
385  LabelType l = outputIt.Get();
386  LabelType canLabel;
387  itkAssertOrThrowMacro(m_CanonicalLabels[l] <= oldRegionCount, "Found a label greater than region count") canLabel = newLabels[m_CanonicalLabels[l]];
388  outputIt.Set(canLabel);
389  ++outputIt;
390  }
391 
392 
393  finishedPruning = !minRegionCount || regionCount == 1 || pruneIterations >= 10;
394 
395  // only one iteration for now
396 
397  if (!finishedPruning)
398  {
399  /* Update adjacency table */
400  regionAdjacencyMap = LabelImageToRegionAdjacencyMap(outputLabelImage);
401  }
402 
403  pruneIterations++;
404  } while (!finishedPruning);
405 
406  // end of main iteration loop
407  // std::cout << "merge iterations: " << mergeIterations << std::endl;
408  // std::cout << "number of label objects: " << regionCount << std::endl;
409 
410  // Generate clustered output
411  itk::ImageRegionIterator<OutputClusteredImageType> outputClusteredIt(outputClusteredImage, outputClusteredImage->GetRequestedRegion());
412  outputClusteredIt.GoToBegin();
413  outputIt.GoToBegin();
414  while (!outputClusteredIt.IsAtEnd())
415  {
416  LabelType label = outputIt.Get();
417  const SpectralPixelType& p = m_Modes[label];
418  outputClusteredIt.Set(p);
419  ++outputClusteredIt;
420  ++outputIt;
421  }
422 }
423 
424 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
426  itk::Indent indent) const
427 {
428  Superclass::PrintSelf(os, indent);
429  os << indent << "Minimum Region Size: " << m_MinRegionSize << std::endl;
430 }
431 
432 
433 template <class TInputLabelImage, class TInputSpectralImage, class TOutputLabelImage, class TOutputClusteredImage>
436  typename OutputLabelImageType::Pointer labelImage)
437 {
438  // declare the output map
440 
441  // Find the maximum label value
442  itk::ImageRegionConstIterator<OutputLabelImageType> it(labelImage, labelImage->GetRequestedRegion());
443  it.GoToBegin();
444  LabelType maxLabel = 0;
445  while (!it.IsAtEnd())
446  {
447  LabelType label = it.Get();
448  maxLabel = std::max(maxLabel, label);
449  ++it;
450  }
451 
452  // Set the size of the adjacency map
453  ram.resize(maxLabel + 1);
454 
455  // set the image region without bottom and right borders so that bottom and
456  // right neighbors always exist
457  RegionType regionWithoutBottomRightBorders = labelImage->GetRequestedRegion();
458  SizeType size = regionWithoutBottomRightBorders.GetSize();
459  for (unsigned int d = 0; d < ImageDimension; ++d)
460  size[d] -= 1;
461  regionWithoutBottomRightBorders.SetSize(size);
462  itk::ImageRegionConstIteratorWithIndex<OutputLabelImageType> inputIt(labelImage, regionWithoutBottomRightBorders);
463 
464  inputIt.GoToBegin();
465  while (!inputIt.IsAtEnd())
466  {
467  const InputIndexType& index = inputIt.GetIndex();
468  LabelType label = inputIt.Get();
469 
470  // check neighbors
471  for (unsigned int d = 0; d < ImageDimension; ++d)
472  {
473  InputIndexType neighborIndex = index;
474  neighborIndex[d]++;
475 
476  LabelType neighborLabel = labelImage->GetPixel(neighborIndex);
477 
478  // add adjacency if different labels
479  if (neighborLabel != label)
480  {
481  ram[label].insert(neighborLabel);
482  ram[neighborLabel].insert(label);
483  }
484  }
485  ++inputIt;
486  }
487 
488  return ram;
489 }
490 
491 } // end namespace otb
492 
493 #endif
otb::LabelImageRegionPruningFilter::PrintSelf
void PrintSelf(std::ostream &os, itk::Indent indent) const override
Definition: otbLabelImageRegionPruningFilter.hxx:425
otb::LabelImageRegionPruningFilter::EnlargeOutputRequestedRegion
void EnlargeOutputRequestedRegion(itk::DataObject *output) override
Definition: otbLabelImageRegionPruningFilter.hxx:136
otb::LabelImageRegionPruningFilter::GetLabelOutput
const OutputLabelImageType * GetLabelOutput() const
Definition: otbLabelImageRegionPruningFilter.hxx:81
otb::LabelImageRegionPruningFilter::InputIndexType
InputImageType::IndexType InputIndexType
Definition: otbLabelImageRegionPruningFilter.h:68
otb::LabelImageRegionPruningFilter::SetInputSpectralImage
void SetInputSpectralImage(const InputSpectralImageType *spectralImage)
Definition: otbLabelImageRegionPruningFilter.hxx:55
otb::LabelImageRegionPruningFilter::~LabelImageRegionPruningFilter
~LabelImageRegionPruningFilter() override
Definition: otbLabelImageRegionPruningFilter.hxx:76
otb::LabelImageRegionPruningFilter::LabelImageRegionPruningFilter
LabelImageRegionPruningFilter()
Definition: otbLabelImageRegionPruningFilter.hxx:34
otb
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
Definition: otbJoinContainer.h:32
otb::LabelImageRegionPruningFilter::RegionAdjacencyMapType
std::vector< AdjacentLabelsContainerType > RegionAdjacencyMapType
Definition: otbLabelImageRegionPruningFilter.h:93
otb::LabelImageRegionPruningFilter::OutputClusteredImageType
TOutputClusteredImage OutputClusteredImageType
Definition: otbLabelImageRegionPruningFilter.h:86
otb::LabelImageRegionPruningFilter::GenerateData
void GenerateData() override
Definition: otbLabelImageRegionPruningFilter.hxx:150
otb::LabelImageRegionPruningFilter::LabelType
InputLabelType LabelType
Definition: otbLabelImageRegionPruningFilter.h:91
otb::LabelImageRegionPruningFilter::RegionType
InputImageType::RegionType RegionType
Definition: otbLabelImageRegionPruningFilter.h:72
otb::LabelImageRegionPruningFilter::SizeType
InputImageType::SizeType SizeType
Definition: otbLabelImageRegionPruningFilter.h:73
otbLabelImageRegionPruningFilter.h
otb::LabelImageRegionPruningFilter::GetClusteredOutput
const OutputClusteredImageType * GetClusteredOutput() const
Definition: otbLabelImageRegionPruningFilter.hxx:102
otb::LabelImageRegionPruningFilter::GetInputSpectralImage
InputSpectralImageType * GetInputSpectralImage()
Definition: otbLabelImageRegionPruningFilter.hxx:69
otb::LabelImageRegionPruningFilter::SpectralPixelType
TInputSpectralImage::PixelType SpectralPixelType
Definition: otbLabelImageRegionPruningFilter.h:76
otb::LabelImageRegionPruningFilter::GenerateOutputInformation
void GenerateOutputInformation(void) override
Definition: otbLabelImageRegionPruningFilter.hxx:123
otb::LabelImageRegionPruningFilter::GetInputLabelImage
InputLabelImageType * GetInputLabelImage()
Definition: otbLabelImageRegionPruningFilter.hxx:63
otb::LabelImageRegionPruningFilter::RealType
double RealType
Definition: otbLabelImageRegionPruningFilter.h:54
otb::LabelImageRegionPruningFilter::SetInputLabelImage
void SetInputLabelImage(const InputLabelImageType *labelImage)
Definition: otbLabelImageRegionPruningFilter.hxx:47
otb::LabelImageRegionPruningFilter::LabelImageToRegionAdjacencyMap
RegionAdjacencyMapType LabelImageToRegionAdjacencyMap(typename OutputLabelImageType::Pointer inputLabelImage)
Definition: otbLabelImageRegionPruningFilter.hxx:435
otb::LabelImageRegionPruningFilter::OutputLabelImageType
TOutputLabelImage OutputLabelImageType
Definition: otbLabelImageRegionPruningFilter.h:78