OTB  10.0.0
Orfeo Toolbox
otbLabelImageSmallRegionMergingFilter.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2024 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 otbLabelImageSmallRegionMergingFilter_hxx
23 #define otbLabelImageSmallRegionMergingFilter_hxx
24 
26 #include "itkConstShapedNeighborhoodIterator.h"
27 #include "itkProgressReporter.h"
28 
29 namespace otb
30 {
31 template <class TInputLabelImage>
33 {
34  this->DynamicMultiThreadingOff();
35 }
36 
37 template <class TInputLabelImage>
39 {
40  m_LabelPopulation = labelPopulation;
41 
42  // Initialize m_CorrespondingMap to the identity (i.e. m[label] = label)
43  for (auto label : m_LabelPopulation)
44  {
45  m_LUT[label.first] = label.first;
46  }
47 }
48 
49 template <class TInputLabelImage>
52 {
53  return m_LabelPopulation;
54 }
55 
56 
57 template <class TInputLabelImage>
59 {
60  m_LabelStatistic = labelStatistic;
61 }
62 
63 template <class TInputLabelImage>
66 {
67  return m_LabelStatistic;
68 }
69 
70 template <class TInputLabelImage>
73 {
74  return m_LUT;
75 }
76 
77 template <class TInputLabelImage>
79 {
80  m_NeighboursMapsTmp.clear();
81  m_NeighboursMapsTmp.resize(this->GetNumberOfWorkUnits());
82 }
83 
84 template <class TInputLabelImage>
86 {
87  NeighboursMapType neighboursMap;
88 
89  // Merge the neighbours maps from all threads
90  for (unsigned int threadId = 0; threadId < this->GetNumberOfWorkUnits(); threadId++)
91  {
92  for (auto const& neighbours : m_NeighboursMapsTmp[threadId])
93  {
94  neighboursMap[neighbours.first].insert(neighbours.second.begin(), neighbours.second.end());
95  }
96  }
97 
98  // For each label of the label map, find the "closest" connected label,
99  // according to the euclidean distance between the corresponding
100  // m_labelStatistic elements.
101  for (auto const& neighbours : neighboursMap)
102  {
103  double proximity = itk::NumericTraits<double>::max();
104  InputLabelType label = neighbours.first;
105  InputLabelType closestNeighbour = label;
106  auto const& statsLabel = m_LabelStatistic[label];
107 
108  for (auto const& neighbour : neighbours.second)
109  {
110  auto const& statsNeighbour = m_LabelStatistic[neighbour];
111  assert(statsLabel.Size() == statsNeighbour.Size());
112 
113  double distance = (statsLabel - statsNeighbour).GetSquaredNorm();
114  if (distance < proximity)
115  {
116  proximity = distance;
117  closestNeighbour = neighbour;
118  }
119  }
120 
121  auto curLabelLUT = FindCorrespondingLabel(label);
122  auto adjLabelLUT = FindCorrespondingLabel(closestNeighbour);
123 
124  // Keep the smallest label (this prevents infinite loop in the LUT
125  // (like LUT[i]=j and LUT[j]=i)
126  if (curLabelLUT < adjLabelLUT)
127  {
128  m_LUT[adjLabelLUT] = curLabelLUT;
129  }
130  else
131  {
132  m_LUT[curLabelLUT] = adjLabelLUT;
133  }
134  }
135 
136  // Update the LUT
137  for (auto& label : m_LUT)
138  {
139  label.second = FindCorrespondingLabel(label.first);
140  }
141 
142  // Update statistics : for each newly merged segments, sum the population, and
143  // recompute the mean.
144  for (auto const& label : m_LUT)
145  {
146  if ((label.second != label.first) && (m_LabelPopulation[label.first] != 0))
147  {
148  // Cache values to reduce number of lookups
149  auto const& populationFirst = m_LabelPopulation[label.first];
150  auto const& populationSecond = m_LabelPopulation[label.second];
151  auto const& statisticFirst = m_LabelStatistic[label.first];
152  auto const& statisticSecond = m_LabelStatistic[label.second];
153 
154  m_LabelStatistic[label.second] = ((statisticFirst * populationFirst) + (statisticSecond * populationSecond)) / (populationFirst + populationSecond);
155 
156  m_LabelPopulation[label.second] += populationFirst;
157 
158  // Do not use this label anymore
159  m_LabelPopulation[label.first] = 0;
160  }
161  }
162 }
163 
164 template <class TInputLabelImage>
168 {
169  auto correspondingLabel = m_LUT[label];
170  while (label != correspondingLabel)
171  {
172  label = correspondingLabel;
173  correspondingLabel = m_LUT[correspondingLabel];
174  }
175  return correspondingLabel;
176 }
177 
178 template <class TInputLabelImage>
180 {
181  // call the superclass' implementation of this method
182  Superclass::GenerateInputRequestedRegion();
183 
184  // get pointers to the input
185  auto inputPtr = const_cast<TInputLabelImage*>(this->GetInput());
186 
187 
188  // get a copy of the input requested region
189  auto inputRequestedRegion = inputPtr->GetRequestedRegion();
190 
191  // pad the input requested region by the operator radius
192  inputRequestedRegion.PadByRadius(1);
193  // crop the input requested region at the input's largest possible region
194  if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion()))
195  {
196  inputPtr->SetRequestedRegion(inputRequestedRegion);
197  return;
198  }
199  else
200  {
201  // Couldn't crop the region (requested region is outside the largest
202  // possible region). Throw an exception.
203 
204  // store what we tried to request (prior to trying to crop)
205  inputPtr->SetRequestedRegion(inputRequestedRegion);
206 
207  // build an exception
208  itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
209  e.SetLocation(ITK_LOCATION);
210  e.SetDescription(
211  "Requested region is (at least partially) outside the "
212  "largest possible region.");
213  e.SetDataObject(inputPtr);
214  throw e;
215  }
216 }
217 
218 template <class TInputLabelImage>
219 void PersistentLabelImageSmallRegionMergingFilter<TInputLabelImage>::ThreadedGenerateData(const RegionType& outputRegionForThread, itk::ThreadIdType threadId)
220 {
221  using IteratorType = itk::ImageRegionConstIterator<TInputLabelImage>;
222  using NeighborhoodIteratorType = itk::ConstShapedNeighborhoodIterator<TInputLabelImage>;
223 
224  typename NeighborhoodIteratorType::RadiusType radius;
225  radius.Fill(1);
226 
227  auto labelImage = this->GetInput();
228 
229  IteratorType it(labelImage, outputRegionForThread);
230  NeighborhoodIteratorType itN(radius, labelImage, outputRegionForThread);
231 
232  // 4 connected Neighborhood (top, bottom, left and right)
233  typename IteratorType::OffsetType top = {{0, -1}};
234  itN.ActivateOffset(top);
235  typename IteratorType::OffsetType bottom = {{0, 1}};
236  itN.ActivateOffset(bottom);
237  typename IteratorType::OffsetType right = {{1, 0}};
238  itN.ActivateOffset(right);
239  typename IteratorType::OffsetType left = {{-1, 0}};
240  itN.ActivateOffset(left);
241 
242  for (it.GoToBegin(); !it.IsAtEnd(); ++it, ++itN)
243  {
244  assert(!itN.IsAtEnd());
245  int currentLabel = m_LUT[it.Get()];
246 
247  if (m_LabelPopulation[currentLabel] == m_Size)
248  {
249  for (auto ci = itN.Begin(); !ci.IsAtEnd(); ci++)
250  {
251  int neighbourLabel = m_LUT[ci.Get()];
252  if (neighbourLabel != currentLabel)
253  m_NeighboursMapsTmp[threadId][currentLabel].insert(neighbourLabel);
254  }
255  }
256  }
257 }
258 
259 template <class TInputLabelImage>
261 {
262  Superclass::PrintSelf(os, indent);
263 }
264 
265 template <class TInputLabelImage>
267 {
269 }
270 
271 template <class TInputLabelImage>
273 {
274  m_SmallRegionMergingFilter->GetFilter()->SetInput(labelImage);
275 }
276 
277 template <class TInputLabelImage>
279 {
280  m_SmallRegionMergingFilter->GetFilter()->SetLabelPopulation(labelPopulation);
281 }
282 
283 template <class TInputLabelImage>
286 {
287  return m_SmallRegionMergingFilter->GetFilter()->GetLabelPopulation();
288 }
289 
290 template <class TInputLabelImage>
292 {
293  m_SmallRegionMergingFilter->GetFilter()->SetLabelStatistic(labelStatistic);
294 }
295 
296 template <class TInputLabelImage>
299 {
300  return m_SmallRegionMergingFilter->GetFilter()->GetLabelStatistic();
301 }
302 
303 template <class TInputLabelImage>
305 {
306  return m_SmallRegionMergingFilter->GetFilter()->GetLUT();
307 }
308 
309 template <class TInputLabelImage>
311 {
312  this->GenerateData();
313 }
314 
315 template <class TInputLabelImage>
317 {
318  this->SetProgress(0.0);
319 
320  // Update the filter for all sizes.
321  for (unsigned int size = 1; size < m_MinSize; size++)
322  {
323  m_SmallRegionMergingFilter->GetFilter()->SetSize(size);
324  m_SmallRegionMergingFilter->Update();
325  this->UpdateProgress(static_cast<double>(size + 1) / m_MinSize);
326  }
327 }
328 
329 
330 } // end namespace otb
331 
332 #endif
PersistentLabelImageSmallRegionMergingFilterType::LabelPopulationType LabelPopulationType
PersistentLabelImageSmallRegionMergingFilterType::LabelStatisticType LabelStatisticType
LabelImageSmallRegionMergingFilterType::Pointer m_SmallRegionMergingFilter
void SetInputLabelImage(const TInputLabelImage *labelImage)
void SetLabelStatistic(LabelStatisticType const &labelStatistic)
void SetLabelPopulation(LabelPopulationType const &labelPopulation)
PersistentLabelImageSmallRegionMergingFilterType::LUTType LUTType
void SetLabelStatistic(LabelStatisticType const &labelStatistic)
void SetLabelPopulation(LabelPopulationType const &labelPopulation)
std::unordered_map< InputLabelType, std::set< InputLabelType > > NeighboursMapType
void ThreadedGenerateData(const RegionType &outputRegionForThread, itk::ThreadIdType threadId) override
std::unordered_map< InputLabelType, RealVectorPixelType > LabelStatisticType
std::unordered_map< InputLabelType, double > LabelPopulationType
std::unordered_map< InputLabelType, InputLabelType > LUTType
void PrintSelf(std::ostream &os, itk::Indent indent) const override
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.