OTB  10.0.0
Orfeo Toolbox
otbMeanShiftSmoothingImageFilter.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 otbMeanShiftSmoothingImageFilter_hxx
23 #define otbMeanShiftSmoothingImageFilter_hxx
24 
26 #include "itkImageRegionIterator.h"
28 #include "otbMacro.h"
29 
30 #include "itkProgressReporter.h"
31 
32 
33 namespace otb
34 {
35 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
37  : m_RangeBandwidth(16.),
38  m_RangeBandwidthRamp(0),
39  m_SpatialBandwidth(3)
40  // , m_SpatialRadius(???)
41  ,
42  m_Threshold(1e-3),
43  m_MaxIterationNumber(10)
44  // , m_Kernel(...)
45  ,
46  m_NumberOfComponentsPerPixel(0)
47  // , m_JointImage(0)
48  // , m_ModeTable(0)
49  ,
50  m_ModeSearch(false),
51  m_ThreadIdNumberOfBits(0)
52 #if 0
53  , m_BucketOptimization(false)
54 #endif
55 {
56  this->DynamicMultiThreadingOff();
57  this->SetNumberOfRequiredOutputs(4);
58  this->SetNthOutput(0, OutputImageType::New());
59  this->SetNthOutput(1, OutputSpatialImageType::New());
60  this->SetNthOutput(2, OutputIterationImageType::New());
61  this->SetNthOutput(3, OutputLabelImageType::New());
62  m_GlobalShift.Fill(0);
63 }
64 
65 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
67 {
68 }
69 
70 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
73 {
74  return static_cast<const OutputSpatialImageType*>(this->itk::ProcessObject::GetOutput(1));
75 }
76 
77 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
80 {
81  return static_cast<OutputSpatialImageType*>(this->itk::ProcessObject::GetOutput(1));
82 }
83 
84 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
87 {
88  return static_cast<const OutputImageType*>(this->itk::ProcessObject::GetOutput(0));
89 }
90 
91 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
94 {
95  return static_cast<OutputImageType*>(this->itk::ProcessObject::GetOutput(0));
96 }
97 
98 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
101 {
102  return static_cast<OutputIterationImageType*>(this->itk::ProcessObject::GetOutput(2));
103 }
104 
105 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
108 {
109  return static_cast<OutputIterationImageType*>(this->itk::ProcessObject::GetOutput(2));
110 }
111 
112 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
115 {
116  return static_cast<OutputLabelImageType*>(this->itk::ProcessObject::GetOutput(3));
117 }
118 
119 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
122 {
123  return static_cast<OutputLabelImageType*>(this->itk::ProcessObject::GetOutput(3));
124 }
125 
126 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
128 {
129  typename OutputSpatialImageType::Pointer spatialOutputPtr = this->GetSpatialOutput();
130  typename OutputImageType::Pointer rangeOutputPtr = this->GetRangeOutput();
131  typename OutputIterationImageType::Pointer iterationOutputPtr = this->GetIterationOutput();
132  typename OutputLabelImageType::Pointer labelOutputPtr = this->GetLabelOutput();
133 
134  spatialOutputPtr->SetBufferedRegion(spatialOutputPtr->GetRequestedRegion());
135  spatialOutputPtr->Allocate();
136 
137  rangeOutputPtr->SetBufferedRegion(rangeOutputPtr->GetRequestedRegion());
138  rangeOutputPtr->Allocate();
139 
140  iterationOutputPtr->SetBufferedRegion(iterationOutputPtr->GetRequestedRegion());
141  iterationOutputPtr->Allocate();
142 
143  labelOutputPtr->SetBufferedRegion(labelOutputPtr->GetRequestedRegion());
144  labelOutputPtr->Allocate();
145 }
146 
147 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
149 {
150  Superclass::GenerateOutputInformation();
151 
152  m_NumberOfComponentsPerPixel = this->GetInput()->GetNumberOfComponentsPerPixel();
153 
154  if (this->GetSpatialOutput())
155  {
156  this->GetSpatialOutput()->SetNumberOfComponentsPerPixel(ImageDimension); // image lattice
157  }
158  if (this->GetRangeOutput())
159  {
160  this->GetRangeOutput()->SetNumberOfComponentsPerPixel(m_NumberOfComponentsPerPixel);
161  }
162 }
163 
164 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
166 {
167  // Call superclass implementation
168  Superclass::GenerateInputRequestedRegion();
169 
170  // Retrieve input pointers
171  InputImagePointerType inPtr = const_cast<TInputImage*>(this->GetInput());
172  OutputImagePointerType outRangePtr = this->GetRangeOutput();
173 
174  // Check pointers before using them
175  if (!inPtr || !outRangePtr)
176  {
177  return;
178  }
179 
180  // Retrieve requested region (TODO: check if we need to handle
181  // region for outHDispPtr)
182  RegionType outputRequestedRegion = outRangePtr->GetRequestedRegion();
183 
184  // Pad by the appropriate radius
185  RegionType inputRequestedRegion = outputRequestedRegion;
186 
187  // Initializes the spatial radius from kernel bandwidth
188  m_SpatialRadius.Fill(m_Kernel.GetRadius(m_SpatialBandwidth));
189 
190  InputSizeType margin;
191 
192  for (unsigned int comp = 0; comp < ImageDimension; ++comp)
193  {
194  margin[comp] = (m_MaxIterationNumber * m_SpatialRadius[comp]) + 1;
195  }
196 
197  inputRequestedRegion.PadByRadius(margin);
198 
199  // Crop the input requested region at the input's largest possible region
200  if (inputRequestedRegion.Crop(inPtr->GetLargestPossibleRegion()))
201  {
202  inPtr->SetRequestedRegion(inputRequestedRegion);
203  return;
204  }
205  else
206  {
207  // Couldn't crop the region (requested region is outside the largest
208  // possible region). Throw an exception.
209 
210  // store what we tried to request (prior to trying to crop)
211  inPtr->SetRequestedRegion(inputRequestedRegion);
212 
213  // build an exception
214  itk::InvalidRequestedRegionError e(__FILE__, __LINE__);
215  e.SetLocation(ITK_LOCATION);
216  e.SetDescription("Requested region is (at least partially) outside the largest possible region.");
217  e.SetDataObject(inPtr);
218  throw e;
219  }
220 }
221 
222 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
224 {
225  // typedef itk::ImageRegionConstIteratorWithIndex<InputImageType> InputIteratorWithIndexType;
226  // typedef itk::ImageRegionIterator<RealVectorImageType> JointImageIteratorType;
227 
228  OutputSpatialImagePointerType outSpatialPtr = this->GetSpatialOutput();
229  OutputImagePointerType outRangePtr = this->GetRangeOutput();
230  typename InputImageType::ConstPointer inputPtr = this->GetInput();
231  typename OutputIterationImageType::Pointer iterationOutput = this->GetIterationOutput();
232  typename OutputSpatialImageType::Pointer spatialOutput = this->GetSpatialOutput();
233 
234  // InputIndexType index;
235 
236 
237  m_SpatialRadius.Fill(m_Kernel.GetRadius(m_SpatialBandwidth));
238 
239  m_NumberOfComponentsPerPixel = this->GetInput()->GetNumberOfComponentsPerPixel();
240 
241  // Allocate output images
242  this->AllocateOutputs();
243 
244  // Initialize output images to zero
245  iterationOutput->FillBuffer(0);
246  OutputSpatialPixelType zero(spatialOutput->GetNumberOfComponentsPerPixel());
247  zero.Fill(0);
248  spatialOutput->FillBuffer(zero);
249 
250  // m_JointImage is the input data expressed in the joint spatial-range
251  // domain, i.e. spatial coordinates are concatenated to the range values.
252  // Moreover, pixel components in this image are normalized by their respective
253  // (spatial or range) bandwidth.
256 
257  typename JointImageFunctorType::Pointer jointImageFunctor = JointImageFunctorType::New();
258 
259  jointImageFunctor->SetInput(inputPtr);
260  jointImageFunctor->GetFunctor().Initialize(ImageDimension, m_NumberOfComponentsPerPixel, m_GlobalShift);
261  jointImageFunctor->GetOutput()->SetRequestedRegion(this->GetInput()->GetBufferedRegion());
262  jointImageFunctor->Update();
263  m_JointImage = jointImageFunctor->GetOutput();
264 
265 #if 0
266  if (m_BucketOptimization)
267  {
268  // Create bucket image
269  // Note: because values in the input m_JointImage are normalized, the
270  // rangeRadius argument is just 1
271  m_BucketImage = BucketImageType(static_cast<typename RealVectorImageType::ConstPointer> (m_JointImage),
272  m_JointImage->GetRequestedRegion(), m_Kernel.GetRadius(m_SpatialBandwidth), 1,
273  ImageDimension);
274  }
275 #endif
276  /*
277  // Allocate the joint domain image
278  m_JointImage = RealVectorImageType::New();
279  m_JointImage->SetNumberOfComponentsPerPixel(ImageDimension + m_NumberOfComponentsPerPixel);
280  m_JointImage->SetRegions(inputPtr->GetRequestedRegion());
281  m_JointImage->Allocate();
282 
283  InputIteratorWithIndexType inputIt(inputPtr, inputPtr->GetRequestedRegion());
284  JointImageIteratorType jointIt(m_JointImage, inputPtr->GetRequestedRegion());
285 
286  // Initialize the joint image with scaled values
287  inputIt.GoToBegin();
288  jointIt.GoToBegin();
289 
290  while (!inputIt.IsAtEnd())
291  {
292  typename InputImageType::PixelType const& inputPixel = inputIt.Get();
293  index = inputIt.GetIndex();
294 
295  RealVector & jointPixel = jointIt.Get();
296  for(unsigned int comp = 0; comp < ImageDimension; comp++)
297  {
298  jointPixel[comp] = index[comp] / m_SpatialBandwidth;
299  }
300  for(unsigned int comp = 0; comp < m_NumberOfComponentsPerPixel; comp++)
301  {
302  jointPixel[ImageDimension + comp] = inputPixel[comp] / m_RangeBandwidth;
303  }
304  // jointIt.Set(jointPixel);
305 
306  ++inputIt;
307  ++jointIt;
308  }
309  */
310 
311  // TODO don't create mode table iterator when ModeSearch is set to false
312  m_ModeTable = ModeTableImageType::New();
313  m_ModeTable->SetRegions(inputPtr->GetRequestedRegion());
314  m_ModeTable->Allocate();
315  m_ModeTable->FillBuffer(0);
316 
317  if (m_ModeSearch)
318  {
319  // Image to store the status at each pixel:
320  // 0 : no mode has been found yet
321  // 1 : a mode has been assigned to this pixel
322  // 2 : a mode will be assigned to this pixel
323 
324 
325  // Initialize counters for mode (also used for mode labeling)
326  // Most significant bits of label counters are used to identify the thread
327  // Id.
328  unsigned int numThreads;
329 
330  numThreads = this->GetNumberOfWorkUnits();
331  m_ThreadIdNumberOfBits = -1;
332  unsigned int n = numThreads;
333  while (n != 0)
334  {
335  n >>= 1;
336  m_ThreadIdNumberOfBits++;
337  }
338  if (m_ThreadIdNumberOfBits == 0)
339  m_ThreadIdNumberOfBits = 1; // minimum 1 bit
340  m_NumLabels.SetSize(numThreads);
341  for (unsigned int i = 0; i < numThreads; i++)
342  {
343  m_NumLabels[i] = static_cast<LabelType>(i) << (sizeof(LabelType) * 8 - m_ThreadIdNumberOfBits);
344  }
345  }
346 }
347 
348 // Calculates the mean shift vector at the position given by jointPixel
349 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
351  const typename RealVectorImageType::Pointer jointImage, const RealVector& jointPixel, const OutputRegionType& outputRegion, const RealVector& bandwidth,
352  RealVector& meanShiftVector)
353 {
354  const unsigned int jointDimension = ImageDimension + m_NumberOfComponentsPerPixel;
355 
356  InputIndexType inputIndex;
357  InputIndexType regionIndex;
358  InputSizeType regionSize;
359 
360  assert(meanShiftVector.GetSize() == jointDimension);
361  meanShiftVector.Fill(0);
362 
363  // Calculates current pixel neighborhood region, restricted to the output image region
364  for (unsigned int comp = 0; comp < ImageDimension; ++comp)
365  {
366  inputIndex[comp] = std::floor(jointPixel[comp] + 0.5) - m_GlobalShift[comp];
367 
368  regionIndex[comp] =
369  std::max(static_cast<long int>(outputRegion.GetIndex().GetElement(comp)), static_cast<long int>(inputIndex[comp] - m_SpatialRadius[comp] - 1));
370  const long int indexRight = std::min(static_cast<long int>(outputRegion.GetIndex().GetElement(comp) + outputRegion.GetSize().GetElement(comp) - 1),
371  static_cast<long int>(inputIndex[comp] + m_SpatialRadius[comp] + 1));
372 
373  regionSize[comp] = std::max(0l, indexRight - static_cast<long int>(regionIndex[comp]) + 1);
374  }
375 
376  RegionType neighborhoodRegion;
377  neighborhoodRegion.SetIndex(regionIndex);
378  neighborhoodRegion.SetSize(regionSize);
379 
380  RealType weightSum = 0;
381  RealVector shifts(jointDimension);
382 
383  // An iterator on the neighborhood of the current pixel (in joint
384  // spatial-range domain)
386  // itk::ImageRegionConstIterator<RealVectorImageType> it(jointImage, neighborhoodRegion);
387 
388  it.GoToBegin();
389  while (!it.IsAtEnd())
390  {
391  const RealType* jointNeighbor = it.GetPixelPointer();
392 
393  // Compute the squared norm of the difference
394  // This is the L2 norm, TODO: replace by the templated norm
395  RealType norm2 = 0;
396  for (unsigned int comp = 0; comp < jointDimension; comp++)
397  {
398  shifts[comp] = jointNeighbor[comp] - jointPixel[comp];
399  double d = shifts[comp] / bandwidth[comp];
400  norm2 += d * d;
401  }
402 
403  // Compute pixel weight from kernel
404  const RealType weight = m_Kernel(norm2);
405  /*
406  // The following code is an alternative way to compute norm2 and weight
407  // It separates the norms of spatial and range elements
408  RealType spatialNorm2;
409  RealType rangeNorm2;
410  spatialNorm2 = 0;
411  for (unsigned int comp = 0; comp < ImageDimension; comp++)
412  {
413  RealType d;
414  d = jointNeighbor[comp] - jointPixel[comp];
415  spatialNorm2 += d*d;
416  }
417 
418  if(spatialNorm2 >= 1.0)
419  {
420  weight = 0;
421  }
422  else
423  {
424  rangeNorm2 = 0;
425  for (unsigned int comp = 0; comp < m_NumberOfComponentsPerPixel; comp++)
426  {
427  RealType d;
428  d = jointNeighbor[ImageDimension + comp] - jointPixel[ImageDimension + comp];
429  rangeNorm2 += d*d;
430  }
431 
432  weight = (rangeNorm2 <= 1.0)? 1.0 : 0.0;
433  }
434  */
435 
436  // Update sum of weights
437  weightSum += weight;
438 
439  // Update mean shift vector
440  for (unsigned int comp = 0; comp < jointDimension; comp++)
441  {
442  meanShiftVector[comp] += weight * shifts[comp];
443  }
444 
445  ++it;
446  }
447 
448  if (weightSum > 0)
449  {
450  for (unsigned int comp = 0; comp < jointDimension; comp++)
451  {
452  meanShiftVector[comp] = meanShiftVector[comp] / weightSum;
453  }
454  }
455 }
456 
457 #if 0
458 // Calculates the mean shift vector at the position given by jointPixel
459 template<class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
461  const RealVector& jointPixel,
462  RealVector& meanShiftVector)
463 {
464  const unsigned int jointDimension = ImageDimension + m_NumberOfComponentsPerPixel;
465 
466  RealType weightSum = 0;
467 
468  for (unsigned int comp = 0; comp < jointDimension; comp++)
469  {
470  meanShiftVector[comp] = 0;
471  }
472 
473  RealVector jointNeighbor(ImageDimension + m_NumberOfComponentsPerPixel);
474 
475  InputIndexType index;
476  for (unsigned int dim = 0; dim < ImageDimension; ++dim)
477  {
478  index[dim] = jointPixel[dim] * m_SpatialBandwidth + 0.5;
479  }
480 
481  const std::vector<unsigned int>
482  neighborBuckets = m_BucketImage.GetNeighborhoodBucketListIndices(
483  m_BucketImage.BucketIndexToBucketListIndex(
484  m_BucketImage.GetBucketIndex(
485  jointPixel,
486  index)));
487 
488  unsigned int numNeighbors = m_BucketImage.GetNumberOfNeighborBuckets();
489  for (unsigned int neighborIndex = 0; neighborIndex < numNeighbors; ++neighborIndex)
490  {
491  const typename BucketImageType::BucketType & bucket = m_BucketImage.GetBucket(neighborBuckets[neighborIndex]);
492  if (bucket.empty()) continue;
493  typename BucketImageType::BucketType::const_iterator it = bucket.begin();
494  while (it != bucket.end())
495  {
496  jointNeighbor.SetData(const_cast<RealType*> (*it));
497 
498  // Compute the squared norm of the difference
499  // This is the L2 norm, TODO: replace by the templated norm
500  RealType norm2 = 0;
501  for (unsigned int comp = 0; comp < jointDimension; comp++)
502  {
503  const RealType d = jointNeighbor[comp] - jointPixel[comp];
504  norm2 += d * d;
505  }
506 
507  // Compute pixel weight from kernel
508  const RealType weight = m_Kernel(norm2);
509 
510  // Update sum of weights
511  weightSum += weight;
512 
513  // Update mean shift vector
514  for (unsigned int comp = 0; comp < jointDimension; comp++)
515  {
516  meanShiftVector[comp] += weight * jointNeighbor[comp];
517  }
518 
519  ++it;
520  }
521  }
522 
523  if (weightSum > 0)
524  {
525  for (unsigned int comp = 0; comp < jointDimension; comp++)
526  {
527  meanShiftVector[comp] = meanShiftVector[comp] / weightSum - jointPixel[comp];
528  }
529  }
530 }
531 #endif
532 
533 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
535  const OutputRegionType& outputRegionForThread, itk::ThreadIdType threadId)
536 {
537  // at the first iteration
538 
539 
540  // Retrieve output images pointers
541  typename OutputSpatialImageType::Pointer spatialOutput = this->GetSpatialOutput();
542  typename OutputImageType::Pointer rangeOutput = this->GetRangeOutput();
543  typename OutputIterationImageType::Pointer iterationOutput = this->GetIterationOutput();
544  typename OutputLabelImageType::Pointer labelOutput = this->GetLabelOutput();
545 
546  // Get input image pointer
547  typename InputImageType::ConstPointer input = this->GetInput();
548 
549  // defines input and output iterators
550  typedef itk::ImageRegionIterator<OutputImageType> OutputIteratorType;
551  typedef itk::ImageRegionIterator<OutputSpatialImageType> OutputSpatialIteratorType;
552  typedef itk::ImageRegionIterator<OutputIterationImageType> OutputIterationIteratorType;
553  typedef itk::ImageRegionIterator<OutputLabelImageType> OutputLabelIteratorType;
554 
555  const unsigned int jointDimension = ImageDimension + m_NumberOfComponentsPerPixel;
556 
557  typename OutputImageType::PixelType rangePixel(m_NumberOfComponentsPerPixel);
558  typename OutputSpatialImageType::PixelType spatialPixel(ImageDimension);
559 
560  RealVector jointPixel(jointDimension);
561 
562  RealVector bandwidth(jointDimension);
563  for (unsigned int comp = 0; comp < ImageDimension; comp++)
564  bandwidth[comp] = m_SpatialBandwidth;
565 
566  itk::ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels());
567 
568  RegionType const& requestedRegion = input->GetRequestedRegion();
569 
570  typedef itk::ImageRegionConstIteratorWithIndex<RealVectorImageType> JointImageIteratorType;
571  JointImageIteratorType jointIt(m_JointImage, outputRegionForThread);
572 
573  OutputIteratorType rangeIt(rangeOutput, outputRegionForThread);
574  OutputSpatialIteratorType spatialIt(spatialOutput, outputRegionForThread);
575  OutputIterationIteratorType iterationIt(iterationOutput, outputRegionForThread);
576  OutputLabelIteratorType labelIt(labelOutput, outputRegionForThread);
577 
578  typedef itk::ImageRegionIterator<ModeTableImageType> ModeTableImageIteratorType;
579  ModeTableImageIteratorType modeTableIt(m_ModeTable, outputRegionForThread);
580 
581  jointIt.GoToBegin();
582  rangeIt.GoToBegin();
583  spatialIt.GoToBegin();
584  iterationIt.GoToBegin();
585  modeTableIt.GoToBegin();
586  labelIt.GoToBegin();
587 
588  unsigned int iteration = 0;
589 
590  // Mean shift vector, updating the joint pixel at each iteration
591  RealVector meanShiftVector(jointDimension);
592 
593  // Variables used by mode search optimization
594  // List of indices where the current pixel passes through
595  std::vector<InputIndexType> pointList;
596  if (m_ModeSearch)
597  pointList.resize(m_MaxIterationNumber);
598  // Number of times an already processed candidate pixel is encountered, resulting in no
599  // further computation (Used for statistics only)
600  unsigned int numBreaks = 0;
601  // index of the current pixel updated during the mean shift loop
602  InputIndexType modeCandidate;
603 
604  for (; !jointIt.IsAtEnd(); ++jointIt, ++rangeIt, ++spatialIt, ++iterationIt, ++modeTableIt, ++labelIt, progress.CompletedPixel())
605  {
606 
607  // if pixel has been already processed (by mode search optimization), skip
608  typename ModeTableImageType::InternalPixelType const& currentPixelMode = modeTableIt.Get();
609  if (m_ModeSearch && currentPixelMode == 1)
610  {
611  numBreaks++;
612  continue;
613  }
614 
615  bool hasConverged = false;
616 
617  // get input pixel in the joint spatial-range domain (with components
618  // normalized by bandwidth)
619  const RealVector& jointPixelVal = jointIt.Get(); // Pixel in the joint spatial-range domain
620  for (unsigned int comp = 0; comp < jointDimension; comp++)
621  jointPixel[comp] = jointPixelVal[comp];
622 
623  for (unsigned int comp = ImageDimension; comp < jointDimension; comp++)
624  bandwidth[comp] = m_RangeBandwidthRamp * jointPixel[comp] + m_RangeBandwidth;
625 
626  // index of the currently processed output pixel
627  InputIndexType currentIndex = jointIt.GetIndex();
628 
629  // Number of points currently in the pointList
630  unsigned int pointCount = 0; // Note: used only in mode search optimization
631  iteration = 0;
632  while ((iteration < m_MaxIterationNumber) && (!hasConverged))
633  {
634 
635  if (m_ModeSearch)
636  {
637  // Find index of the pixel closest to the current jointPixel (not normalized by bandwidth)
638  for (unsigned int comp = 0; comp < ImageDimension; comp++)
639  {
640  modeCandidate[comp] = std::floor(jointPixel[comp] - m_GlobalShift[comp] + 0.5);
641  }
642  // Check status of candidate mode
643 
644  // If pixel candidate has status 0 (no mode assigned) or 1 (mode assigned)
645  // but not 2 (pixel in current search path), and pixel has actually moved
646  // from its initial position, and pixel candidate is inside the output
647  // region, then perform optimization tasks
648  if (modeCandidate != currentIndex && m_ModeTable->GetPixel(modeCandidate) != 2 && outputRegionForThread.IsInside(modeCandidate))
649  {
650  // Obtain the data point to see if it close to jointPixel
651  RealType diff = 0;
652  RealVector const& candidatePixel = m_JointImage->GetPixel(modeCandidate);
653  for (unsigned int comp = ImageDimension; comp < jointDimension; comp++)
654  {
655  const RealType d = (candidatePixel[comp] - jointPixel[comp]) / bandwidth[comp];
656  diff += d * d;
657  }
658 
659  if (diff < 0.5) // Spectral value is close enough
660  {
661  // If no mode has been associated to the candidate pixel then
662  // associate it to the upcoming mode
663  if (m_ModeTable->GetPixel(modeCandidate) == 0)
664  {
665  // Add the candidate to the list of pixels that will be assigned the
666  // finally calculated mode value
667  pointList[pointCount++] = modeCandidate;
668  m_ModeTable->SetPixel(modeCandidate, 2);
669  }
670  else // == 1
671  {
672  // The candidate pixel has already been assigned to a mode
673  // Assign the same value
674  rangePixel = rangeOutput->GetPixel(modeCandidate);
675  for (unsigned int comp = 0; comp < m_NumberOfComponentsPerPixel; comp++)
676  {
677  jointPixel[ImageDimension + comp] = rangePixel[comp];
678  }
679  // Update the mode table because pixel will be assigned just now
680  modeTableIt.Set(2); // m_ModeTable->SetPixel(currentIndex, 2);
681  // bypass further calculation
682  numBreaks++;
683  break;
684  }
685  }
686  }
687  } // end if (m_ModeSearch)
688 
689 // Calculate meanShiftVector
690 #if 0
691  if (m_BucketOptimization)
692  {
693  this->CalculateMeanShiftVectorBucket(jointPixel, meanShiftVector);
694  }
695  else
696  {
697 #endif
698  this->CalculateMeanShiftVector(m_JointImage, jointPixel, requestedRegion, bandwidth, meanShiftVector);
699 
700 #if 0
701  }
702 #endif
703 
704  // Compute mean shift vector squared norm (not normalized by bandwidth)
705  // and add mean shift vector to current joint pixel
706  double meanShiftVectorSqNorm = 0;
707  for (unsigned int comp = 0; comp < jointDimension; comp++)
708  {
709  const double v = meanShiftVector[comp];
710  meanShiftVectorSqNorm += v * v;
711  jointPixel[comp] += meanShiftVector[comp];
712  }
713 
714  // TODO replace SSD Test with templated metric
715  hasConverged = meanShiftVectorSqNorm < m_Threshold;
716  iteration++;
717  }
718 
719  for (unsigned int comp = 0; comp < m_NumberOfComponentsPerPixel; comp++)
720  {
721  rangePixel[comp] = jointPixel[ImageDimension + comp];
722  }
723 
724  for (unsigned int comp = 0; comp < ImageDimension; comp++)
725  {
726  spatialPixel[comp] = jointPixel[comp] - currentIndex[comp] - m_GlobalShift[comp];
727  }
728 
729  rangeIt.Set(rangePixel);
730  spatialIt.Set(spatialPixel);
731 
732  const typename OutputIterationImageType::PixelType iterationPixel = iteration;
733  iterationIt.Set(iterationPixel);
734 
735  if (m_ModeSearch)
736  {
737  // Update the mode table now that the current pixel has been assigned
738  modeTableIt.Set(1); // m_ModeTable->SetPixel(currentIndex, 1);
739 
740  // If the loop exited with hasConverged or too many iterations, then we have a new mode
741  LabelType label;
742  if (hasConverged || iteration == m_MaxIterationNumber)
743  {
744  m_NumLabels[threadId]++;
745  label = m_NumLabels[threadId];
746  }
747  else // the loop exited through a break. Use the already assigned mode label
748  {
749  label = labelOutput->GetPixel(modeCandidate);
750  }
751  labelIt.Set(label);
752 
753  // Also assign all points in the list to the same mode
754  for (unsigned int i = 0; i < pointCount; i++)
755  {
756  rangeOutput->SetPixel(pointList[i], rangePixel);
757  m_ModeTable->SetPixel(pointList[i], 1);
758  labelOutput->SetPixel(pointList[i], label);
759  }
760  }
761  else // if ModeSearch is not set LabelOutput can't be generated
762  {
763  LabelType labelZero = 0;
764  labelIt.Set(labelZero);
765  }
766  }
767  // std::cout << "numBreaks: " << numBreaks << " Break ratio: " << numBreaks / (RealType)outputRegionForThread.GetNumberOfPixels() << std::endl;
768 }
769 
770 /* after threaded convergence test */
771 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
773 {
774  typename OutputLabelImageType::Pointer labelOutput = this->GetLabelOutput();
775  typedef itk::ImageRegionIterator<OutputLabelImageType> OutputLabelIteratorType;
776  OutputLabelIteratorType labelIt(labelOutput, labelOutput->GetRequestedRegion());
777 
778  // Reassign mode labels
779  // Note: Labels are only computed when mode search optimization is enabled
780  if (m_ModeSearch)
781  {
782  // New labels will be consecutive. The following vector contains the new
783  // start label for each thread.
784  itk::VariableLengthVector<LabelType> newLabelOffset;
785  newLabelOffset.SetSize(this->GetNumberOfWorkUnits());
786  newLabelOffset[0] = 0;
787  for (itk::ThreadIdType i = 1; i < this->GetNumberOfWorkUnits(); i++)
788  {
789  // Retrieve the number of labels in the thread by removing the threadId
790  // from the most significant bits
791  LabelType localNumLabel =
792  m_NumLabels[i - 1] & ((static_cast<LabelType>(1) << (sizeof(LabelType) * 8 - m_ThreadIdNumberOfBits)) - static_cast<LabelType>(1));
793  newLabelOffset[i] = localNumLabel + newLabelOffset[i - 1];
794  }
795 
796  labelIt.GoToBegin();
797 
798  while (!labelIt.IsAtEnd())
799  {
800  LabelType const label = labelIt.Get();
801 
802  // Get threadId from most significant bits
803  const itk::ThreadIdType threadId = label >> (sizeof(LabelType) * 8 - m_ThreadIdNumberOfBits);
804 
805  // Relabeling
806  // First get the label number by removing the threadId bits
807  // Then add the label offset specific to the threadId
808  LabelType newLabel = label & ((static_cast<LabelType>(1) << (sizeof(LabelType) * 8 - m_ThreadIdNumberOfBits)) - static_cast<LabelType>(1));
809  newLabel += newLabelOffset[threadId];
810 
811  labelIt.Set(newLabel);
812  ++labelIt;
813  }
814  }
815 }
816 
817 template <class TInputImage, class TOutputImage, class TKernel, class TOutputIterationImage>
819 {
820  Superclass::PrintSelf(os, indent);
821  os << indent << "Spatial bandwidth: " << m_SpatialBandwidth << std::endl;
822  os << indent << "Range bandwidth: " << m_RangeBandwidth << std::endl;
823 }
824 
825 } // end namespace otb
826 
827 #endif
Creation of an "otb" image which contains metadata.
Definition: otbImage.h:92
itk::SmartPointer< Self > Pointer
Definition: otbImage.h:97
Superclass::InternalPixelType InternalPixelType
Definition: otbImage.h:120
static Pointer New()
itk::VariableLengthVector< RealType > RealVector
void ThreadedGenerateData(const OutputRegionType &outputRegionForThread, itk::ThreadIdType threadId) override
const OutputSpatialImageType * GetSpatialOutput() const
const OutputLabelImageType * GetLabelOutput() const
const OutputIterationImageType * GetIterationOutput() const
virtual void CalculateMeanShiftVector(const typename RealVectorImageType::Pointer inputImagePtr, const RealVector &jointPixel, const OutputRegionType &outputRegion, const RealVector &bandwidth, RealVector &meanShiftVector)
OutputSpatialImageType::PixelType OutputSpatialPixelType
OutputSpatialImageType::Pointer OutputSpatialImagePointerType
void PrintSelf(std::ostream &os, itk::Indent indent) const override
Creation of an "otb" vector image which contains metadata.
Superclass::PixelType PixelType
itk::SmartPointer< const Self > ConstPointer
itk::SmartPointer< Self > Pointer
static Pointer New()
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.