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