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

Generated at Sun Jun 16 2013 00:34:26 for Orfeo Toolbox with doxygen 1.8.3.1