OTB  10.0.0
Orfeo Toolbox
otbLabelImageToLabelMapWithAdjacencyFilter.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1999-2011 Insight Software Consortium
3  * Copyright (C) 2005-2024 Centre National d'Etudes Spatiales (CNES)
4  *
5  * This file is part of Orfeo Toolbox
6  *
7  * https://www.orfeo-toolbox.org/
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 #ifndef otbLabelImageToLabelMapWithAdjacencyFilter_hxx
23 #define otbLabelImageToLabelMapWithAdjacencyFilter_hxx
24 
25 #include "otbMacro.h" //for
27 #include "itkNumericTraits.h"
28 #include "itkProgressReporter.h"
29 #include "itkImageLinearConstIteratorWithIndex.h"
30 #include "itkImageRegionConstIteratorWithIndex.h"
31 
32 namespace otb
33 {
34 
35 template <class TInputImage, class TOutputImage>
37 {
38  this->DynamicMultiThreadingOff();
39  m_BackgroundValue = itk::NumericTraits<OutputImagePixelType>::NonpositiveMin();
40 }
41 
42 template <class TInputImage, class TOutputImage>
44 {
45  for (unsigned int idx = 0; idx < this->GetNumberOfInputs(); ++idx)
46  {
47  InputImagePointer input = const_cast<InputImageType*>(this->GetInput(idx));
48  if (!input.IsNull())
49  {
50  input->SetRequestedRegionToLargestPossibleRegion();
51  // Check whether the input is an image of the appropriate
52  // dimension (use ProcessObject's version of the GetInput()
53  // method since it returns the input as a pointer to a
54  // DataObject as opposed to the subclass version which
55  // static_casts the input to an TInputImage).
56 
57  // Use the function object RegionCopier to copy the output region
58  // to the input. The default region copier has default implementations
59  // to handle the cases where the input and output are the same
60  // dimension, the input a higher dimension than the output, and the
61  // input a lower dimension than the output.
62  InputImageRegionType inputRegion;
63  this->CallCopyOutputRegionToInputRegion(inputRegion, this->GetOutput()->GetRequestedRegion());
64  input->SetRequestedRegion(inputRegion);
65  }
66  }
67 }
68 
69 
70 template <class TInputImage, class TOutputImage>
72 {
73 }
74 
75 
76 template <class TInputImage, class TOutputImage>
78 {
79  // init the temp images - one per thread
80  m_TemporaryImages.resize(this->GetNumberOfWorkUnits());
81  // Clear previous adjacency map
82  m_TemporaryAdjacencyMaps.resize(this->GetNumberOfWorkUnits());
83 
84  for (unsigned int i = 0; i < this->GetNumberOfWorkUnits(); ++i)
85  {
86  if (i == 0)
87  {
88  // the first one is the output image
89  m_TemporaryImages[0] = this->GetOutput();
90  }
91  else
92  {
93  // the other must be created
94  m_TemporaryImages[i] = OutputImageType::New();
95  }
96 
97  // set the minimum data needed to create the objects properly
98  m_TemporaryImages[i]->SetBackgroundValue(m_BackgroundValue);
99  }
100 }
101 
102 
103 template <class TInputImage, class TOutputImage>
105 {
106  // Insert label1 in v neighbors
107  if (m_TemporaryAdjacencyMaps[threadId].find(label2) != m_TemporaryAdjacencyMaps[threadId].end())
108  {
109  m_TemporaryAdjacencyMaps[threadId][label2].insert(label1);
110  }
111  else
112  {
113  AdjacentLabelsContainerType newContainer;
114  newContainer.insert(label1);
115  m_TemporaryAdjacencyMaps[threadId][label2] = newContainer;
116  }
117  // Insert label2 in label1s neighbors
118  if (m_TemporaryAdjacencyMaps[threadId].find(label1) != m_TemporaryAdjacencyMaps[threadId].end())
119  {
120  m_TemporaryAdjacencyMaps[threadId][label1].insert(label2);
121  }
122  else
123  {
124  AdjacentLabelsContainerType newContainer;
125  newContainer.insert(label2);
126  m_TemporaryAdjacencyMaps[threadId][label1] = newContainer;
127  }
128 }
129 
130 template <class TInputImage, class TOutputImage>
132 {
133  // Get the first label
134  typename RLEVectorType::const_iterator it = line.begin();
135  LabelType previousLabel = it->label;
136  ++it;
137 
138  // Iterates on remaining RLE
139  while (it != line.end())
140  {
141  // Get the new label
142  LabelType nextLabel = it->label;
143 
144  // Add the adjacency
145  this->AddAdjacency(previousLabel, nextLabel, threadId);
146 
147  // Store previous label
148  previousLabel = nextLabel;
149  ++it;
150  }
151 }
152 
153 template <class TInputImage, class TOutputImage>
155  itk::ThreadIdType threadId)
156 {
157  // Provided to disable fully connected if needed
158  long offset = 1;
159 
160  // Iterate on line1
161  for (typename RLEVectorType::const_iterator it1 = line1.begin(); it1 != line1.end(); ++it1)
162  {
163  // Delimitate RLE1
164  long start1 = it1->where[0];
165  long end1 = start1 + it1->length - 1;
166  LabelType label1 = it1->label;
167 
168  // Iterate on line2
169  for (typename RLEVectorType::const_iterator it2 = line2.begin(); it2 != line2.end(); ++it2)
170  {
171  // Delimitate RLE2
172  long start2 = it2->where[0];
173  long end2 = start2 + it2->length - 1;
174  LabelType label2 = it2->label;
175 
176  // If labels are different
177  if (label1 != label2)
178  {
179  // Check adjacency
180  if (((start1 >= start2 - offset) && (start1 <= end2 + offset)) || ((end1 >= start2 - offset) && (end1 <= end2 + offset)) ||
181  ((start2 >= start1 - offset) && (start2 <= end1 + offset)) || ((end2 >= start1 - offset) && (end2 <= end1 + offset)))
182  {
183  // Add the adjacency
184  this->AddAdjacency(label1, label2, threadId);
185  }
186  }
187  }
188  }
189 }
190 
191 template <class TInputImage, class TOutputImage>
193  itk::ThreadIdType threadId)
194 {
195  itk::ProgressReporter progress(this, threadId, regionForThread.GetNumberOfPixels());
196 
197  typedef itk::ImageLinearConstIteratorWithIndex<InputImageType> InputLineIteratorType;
198 
199  InputLineIteratorType it(this->GetInput(), regionForThread);
200  it.SetDirection(0);
201 
202  RLEVectorType currentLine;
203  RLEVectorType previousLine;
204 
205  // Parse previous line if exists
206  typename InputImageType::RegionType previousLineRegion;
207  typename InputImageType::SizeType previousLineRegionSize;
208  typename InputImageType::IndexType previousLineRegionIndex;
209 
210  previousLineRegionIndex = regionForThread.GetIndex();
211  previousLineRegionIndex[1]--;
212 
213  previousLineRegionSize = regionForThread.GetSize();
214  previousLineRegionSize[1] = 1;
215 
216  previousLineRegion.SetIndex(previousLineRegionIndex);
217  previousLineRegion.SetSize(previousLineRegionSize);
218 
219  // If previous line is still in image
220  if (previousLineRegion.Crop(this->GetInput()->GetRequestedRegion()))
221  {
222  // Build an iterator
223  itk::ImageRegionConstIteratorWithIndex<InputImageType> pIt(this->GetInput(), previousLineRegion);
224  pIt.GoToBegin();
225 
226  // Iterate on line
227  while (!pIt.IsAtEnd())
228  {
229  const InputImagePixelType& v = pIt.Get();
230 
231  if (v != m_BackgroundValue)
232  {
233  // We've hit the start of a run
234  IndexType idx = pIt.GetIndex();
235  long length = 1;
236  ++pIt;
237  while (!pIt.IsAtEnd() && pIt.Get() == v)
238  {
239  ++length;
240  ++pIt;
241  }
242  previousLine.push_back(RLE(idx, length, v));
243  }
244  else
245  {
246  // go the the next pixel
247  ++pIt;
248  }
249  }
250  }
251 
252  for (it.GoToBegin(); !it.IsAtEnd(); it.NextLine())
253  {
254  // Go to beginning of line
255  it.GoToBeginOfLine();
256 
257  // Clear the previous current line
258  currentLine.clear();
259 
260  // Iterate on line
261  while (!it.IsAtEndOfLine())
262  {
263  const InputImagePixelType& v = it.Get();
264 
265  if (v != m_BackgroundValue)
266  {
267  // We've hit the start of a run
268  IndexType idx = it.GetIndex();
269  long length = 1;
270  ++it;
271  while (!it.IsAtEndOfLine() && it.Get() == v)
272  {
273  ++length;
274  ++it;
275  }
276  // create the run length object to go in the vector
277  m_TemporaryImages[threadId]->SetLine(idx, length, v);
278  currentLine.push_back(RLE(idx, length, v));
279  }
280  else
281  {
282  // go the the next pixel
283  ++it;
284  }
285  }
286 
287  // if no label object is present on current line we skip the process
288  if (currentLine.size() > 0)
289  {
290  // Parse lines for adjacency
291  this->ParseLine(currentLine, threadId);
292  this->ParseConsecutiveLines(previousLine, currentLine, threadId);
293  }
294 
295  // Store previous line
296  previousLine = currentLine;
297  }
298 }
299 
300 
301 template <class TInputImage, class TOutputImage>
303 {
304 
305  OutputImageType* output = this->GetOutput();
306 
307  // merge the lines from the temporary images in the output image
308  // don't use the first image - that's the output image
309  for (unsigned int i = 1; i < this->GetNumberOfWorkUnits(); ++i)
310  {
311  typedef typename OutputImageType::LabelObjectVectorType LabelObjectVectorType;
312  const LabelObjectVectorType& labelObjectContainer = m_TemporaryImages[i]->GetLabelObjects();
313 
314  for (typename LabelObjectVectorType::const_iterator it = labelObjectContainer.begin(); it != labelObjectContainer.end(); ++it)
315  {
316  LabelObjectType* labelObject = *it;
317  if (output->HasLabel(labelObject->GetLabel()))
318  {
319  // merge the lines in the output's object
320  // ITKv4 iterate over the 2 LabelObjects src and output
321  typedef typename LabelObjectType::ConstLineIterator IteratorType;
322 
323  typename LabelObjectType::LabelObjectType* dest = output->GetLabelObject(labelObject->GetLabel());
324 
325  IteratorType srcIt;
326  srcIt = IteratorType(labelObject);
327  srcIt.GoToBegin();
328 
329  // ITKv4 Iterate over all lines of the source and add them to the
330  // output labelObject (can use the insert method over the
331  // container anymore (it is now private)
332  while (!srcIt.IsAtEnd())
333  {
334  dest->AddLine(srcIt.GetLine());
335  ++srcIt;
336  }
337  }
338  else
339  {
340  // simply take the object
341  output->AddLabelObject(labelObject);
342  }
343  }
344  }
345 
346  // Merge adjacency tables
347  AdjacencyMapType adjMap = m_TemporaryAdjacencyMaps[0];
348 
349  // For each remaining thread
350  for (itk::ThreadIdType threadId = 1; threadId < this->GetNumberOfWorkUnits(); ++threadId)
351  {
352  // For each label in the thread adjacency map
353  for (typename AdjacencyMapType::const_iterator mit = m_TemporaryAdjacencyMaps[threadId].begin(); mit != m_TemporaryAdjacencyMaps[threadId].end(); ++mit)
354  {
355  // If the label exists in the main map
356  if (adjMap.find(mit->first) != adjMap.end())
357  {
358  // We need to merge
359  AdjacentLabelsContainerType adjLabels1 = adjMap[mit->first];
360  AdjacentLabelsContainerType adjLabels2 = mit->second;
361  std::vector<LabelType> mergedLabels(adjLabels1.size() + adjLabels2.size(), 0);
362 
363  // Merge
364  typename std::vector<LabelType>::const_iterator vend =
365  set_union(adjLabels1.begin(), adjLabels1.end(), adjLabels2.begin(), adjLabels2.end(), mergedLabels.begin());
366 
367  AdjacentLabelsContainerType mergedLabelsSet;
368 
369  for (typename std::vector<LabelType>::const_iterator vit = mergedLabels.begin(); vit != vend; ++vit)
370  {
371  mergedLabelsSet.insert(*vit);
372  }
373 
374  // Set the final result
375  adjMap[mit->first] = mergedLabelsSet;
376  }
377  else
378  {
379  // Set the labels
380  adjMap[mit->first] = mit->second;
381  }
382  }
383  }
384 
385  // Set the adjacency map to the output
386  output->SetAdjacencyMap(adjMap);
387 
388  // release the data in the temp images
389  m_TemporaryImages.clear();
390  m_TemporaryAdjacencyMaps.clear();
391 }
392 
393 
394 template <class TInputImage, class TOutputImage>
396 {
397  Superclass::PrintSelf(os, indent);
398 
399  os << indent << "BackgroundValue: " << static_cast<typename itk::NumericTraits<OutputImagePixelType>::PrintType>(m_BackgroundValue) << std::endl;
400 }
401 
402 } // end namespace otb
403 #endif
void AddAdjacency(LabelType label1, LabelType label2, itk::ThreadIdType threadId)
void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId) override
void PrintSelf(std::ostream &os, itk::Indent indent) const override
void ParseConsecutiveLines(const RLEVectorType &line1, const RLEVectorType &line2, itk::ThreadIdType threadId)
OutputImageType::AdjacentLabelsContainerType AdjacentLabelsContainerType
void ParseLine(const RLEVectorType &line, itk::ThreadIdType threadId)
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
string_view find(string_view const &haystack, string_view const &needle)