[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

slic.hxx VIGRA

1 /************************************************************************/
2 /* */
3 /* Copyright 2012-2013 by Ullrich Koethe and Thorsten Beier */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35 
36 #ifndef VIGRA_SLIC_HXX
37 #define VIGRA_SLIC_HXX
38 
39 #include "multi_array.hxx"
40 #include "multi_convolution.hxx"
41 #include "multi_labeling.hxx"
42 #include "numerictraits.hxx"
43 #include "accumulator.hxx"
44 #include "array_vector.hxx"
45 
46 namespace vigra {
47 
48 /** \addtogroup Superpixels
49 */
50 //@{
51 
52 /********************************************************/
53 /* */
54 /* generateSlicSeeds */
55 /* */
56 /********************************************************/
57 
58 /** \brief Generate seeds for SLIC superpixel computation in arbitrary dimensions.
59 
60  The source array \a src must be a scalar boundary indicator such as the gradient
61  magnitude. Seeds are initially placed on a regular Cartesian grid with spacing
62  \a seedDist und then moved to the point with smallest boundary indicator within
63  a search region of radius \a searchRadius around the initial position. The resulting
64  points are then marked in the output array \a seeds by consecutive labels.
65 
66  The function returns the number of selected seeds, which equals the largest seed label
67  because labeling starts at 1.
68 
69  <b> Declaration:</b>
70 
71  use arbitrary-dimensional arrays:
72  \code
73  namespace vigra {
74  template <unsigned int N, class T, class S1,
75  class Label, class S2>
76  unsigned int
77  generateSlicSeeds(MultiArrayView<N, T, S1> const & src,
78  MultiArrayView<N, Label, S2> seeds,
79  unsigned int seedDist,
80  unsigned int searchRadius = 1);
81  }
82  \endcode
83 
84  <b> Usage:</b>
85 
86  <b>\#include</b> <vigra/slic.hxx><br>
87  Namespace: vigra
88 
89  \code
90  MultiArray<2, RGBValue<float> > src(Shape2(w, h));
91  ... // fill src image
92 
93  // transform image to Lab color space
94  transformImage(srcImageRange(src), destImage(src), RGBPrime2LabFunctor<float>());
95 
96  // compute image gradient magnitude at scale 1.0 as a boundary indicator
97  MultiArray<2, float> grad(src.shape());
98  gaussianGradientMagnitude(srcImageRange(src), destImage(grad), 1.0);
99 
100  MultiArray<2, unsigned int> seeds(src.shape());
101  int seedDistance = 15;
102 
103  // place seeds on a grid with distance 15, but then move it to the lowest gradient
104  // poistion in a 3x3 window
105  generateSlicSeeds(grad, seeds, seedDistance);
106  \endcode
107 
108  For more details and examples see slicSuperpixels().
109 */
110 doxygen_overloaded_function(template <...> unsigned int generateSlicSeeds)
111 
112 template <unsigned int N, class T, class S1,
113  class Label, class S2>
114 unsigned int
115 generateSlicSeeds(MultiArrayView<N, T, S1> const & boundaryIndicatorImage,
116  MultiArrayView<N, Label, S2> seeds,
117  unsigned int seedDist,
118  unsigned int searchRadius = 1)
119 {
120  typedef typename MultiArrayShape<N>::type Shape;
121 
122  seeds.init(0);
123  Shape shape(boundaryIndicatorImage.shape()),
124  seedShape(floor(shape / double(seedDist))),
125  offset((shape - (seedShape - Shape(1))*seedDist) / 2);
126 
127  unsigned int label = 0;
128  MultiCoordinateIterator<N> iter(seedShape),
129  end = iter.getEndIterator();
130  for(; iter != end; ++iter)
131  {
132  // define search window around current seed center
133  Shape center = (*iter)*seedDist + offset;
134  Shape startCoord = max(Shape(0), center-Shape(searchRadius));
135  Shape endCoord = min(center+Shape(searchRadius+1), shape);
136 
137  // find the coordinate of minimum boundary indicator in window
138  using namespace acc;
139  AccumulatorChain<CoupledArrays<N, T>,
140  Select<WeightArg<1>, Coord<ArgMinWeight> > > a;
141  extractFeatures(boundaryIndicatorImage.subarray(startCoord, endCoord), a);
142 
143  // add seed at minimum position, if not already occupied
144  Shape minCoord = get<Coord<ArgMinWeight> >(a) + startCoord;
145  if(seeds[minCoord] == 0)
146  seeds[minCoord] = ++label;
147  }
148  return label;
149 }
150 
151 /** \brief Options object for slicSuperpixels().
152 
153  <b> Usage:</b>
154 
155  see slicSuperpixels() for detailed examples.
156 */
158 {
159  /** \brief Create options object with default settings.
160 
161  Defaults are: perform 10 iterations, determine a size limit for superpixels automatically.
162  */
164  : iter(10),
165  sizeLimit(0)
166  {}
167 
168  /** \brief Number of iterations.
169 
170  Default: 10
171  */
172  SlicOptions & iterations(unsigned int i)
173  {
174  iter = i;
175  return *this;
176  }
177 
178  /** \brief Minimum superpixel size.
179 
180  If you set this to 1, no size filtering will be performed.
181 
182  Default: 0 (determine size limit automatically as <tt>average size / 4</tt>)
183  */
184  SlicOptions & minSize(unsigned int s)
185  {
186  sizeLimit = s;
187  return *this;
188  }
189 
190  unsigned int iter;
191  unsigned int sizeLimit;
192 };
193 
194 namespace detail {
195 
196 template <unsigned int N, class T, class Label>
197 class Slic
198 {
199  public:
200  //
201  typedef MultiArrayView<N, T> DataImageType;
202  typedef MultiArrayView<N, Label> LabelImageType;
203  typedef typename DataImageType::difference_type ShapeType;
204  typedef typename PromoteTraits<
205  typename NormTraits<T>::NormType,
206  typename NormTraits<MultiArrayIndex>::NormType
207  >::Promote DistanceType;
208 
209  Slic(DataImageType dataImage,
210  LabelImageType labelImage,
211  DistanceType intensityScaling,
212  int maxRadius,
213  SlicOptions const & options = SlicOptions());
214 
215  unsigned int execute();
216 
217  private:
218  void updateAssigments();
219  unsigned int postProcessing();
220 
221  typedef MultiArray<N,DistanceType> DistanceImageType;
222 
223  ShapeType shape_;
224  DataImageType dataImage_;
225  LabelImageType labelImage_;
226  DistanceImageType distance_;
227  int max_radius_;
228  DistanceType normalization_;
229  SlicOptions options_;
230 
231  typedef acc::Select<acc::DataArg<1>, acc::LabelArg<2>, acc::Mean, acc::RegionCenter> Statistics;
232  typedef acc::AccumulatorChainArray<CoupledArrays<N, T, Label>, Statistics> RegionFeatures;
233  RegionFeatures clusters_;
234 };
235 
236 
237 
238 template <unsigned int N, class T, class Label>
239 Slic<N, T, Label>::Slic(
240  DataImageType dataImage,
241  LabelImageType labelImage,
242  DistanceType intensityScaling,
243  int maxRadius,
244  SlicOptions const & options)
245 : shape_(dataImage.shape()),
246  dataImage_(dataImage),
247  labelImage_(labelImage),
248  distance_(shape_),
249  max_radius_(maxRadius),
250  normalization_(sq(intensityScaling) / sq(max_radius_)),
251  options_(options)
252 {
253  clusters_.ignoreLabel(0);
254 }
255 
256 template <unsigned int N, class T, class Label>
257 unsigned int Slic<N, T, Label>::execute()
258 {
259  // Do SLIC
260  for(size_t i=0; i<options_.iter; ++i)
261  {
262  // update mean for each cluster
263  clusters_.reset();
264  extractFeatures(dataImage_, labelImage_, clusters_);
265 
266  // update which pixels get assigned to which cluster
267  updateAssigments();
268  }
269 
270  return postProcessing();
271 }
272 
273 template <unsigned int N, class T, class Label>
274 void
275 Slic<N, T, Label>::updateAssigments()
276 {
277  using namespace acc;
278  distance_.init(NumericTraits<DistanceType>::max());
279  for(unsigned int c=1; c<=clusters_.maxRegionLabel(); ++c)
280  {
281  if(get<Count>(clusters_, c) == 0) // label doesn't exist
282  continue;
283 
284  typedef typename LookupTag<RegionCenter, RegionFeatures>::value_type CenterType;
285  CenterType center = get<RegionCenter>(clusters_, c);
286 
287  // get ROI limits around region center
288  ShapeType pixelCenter(round(center)),
289  startCoord(max(ShapeType(0), pixelCenter - ShapeType(max_radius_))),
290  endCoord(min(shape_, pixelCenter + ShapeType(max_radius_+1)));
291  center -= startCoord; // need center relative to ROI
292 
293  // setup iterators for ROI
294  typedef typename CoupledArrays<N, T, Label, DistanceType>::IteratorType Iterator;
295  Iterator iter = createCoupledIterator(dataImage_, labelImage_, distance_).
296  restrictToSubarray(startCoord, endCoord),
297  end = iter.getEndIterator();
298 
299  // only pixels within the ROI can be assigned to a cluster
300  for(; iter != end; ++iter)
301  {
302  // compute distance between cluster center and pixel
303  DistanceType spatialDist = squaredNorm(center-iter.point());
304  DistanceType colorDist = squaredNorm(get<Mean>(clusters_, c)-iter.template get<1>());
305  DistanceType dist = colorDist + normalization_*spatialDist;
306  // update label?
307  if(dist < iter.template get<3>())
308  {
309  iter.template get<2>() = static_cast<Label>(c);
310  iter.template get<3>() = dist;
311  }
312  }
313  }
314 }
315 
316 template <unsigned int N, class T, class Label>
317 unsigned int
318 Slic<N, T, Label>::postProcessing()
319 {
320  // get rid of regions below a size limit
321  MultiArray<N,Label> tmpLabelImage(labelImage_);
322  unsigned int maxLabel = labelMultiArray(tmpLabelImage, labelImage_, DirectNeighborhood);
323 
324  unsigned int sizeLimit = options_.sizeLimit == 0
325  ? (unsigned int)(0.25 * labelImage_.size() / maxLabel)
326  : options_.sizeLimit;
327  if(sizeLimit == 1)
328  return maxLabel;
329 
330  // determine region size
331  using namespace acc;
332  AccumulatorChainArray<CoupledArrays<N, Label>, Select<LabelArg<1>, Count> > sizes;
333  extractFeatures(labelImage_, sizes);
334 
335  typedef GridGraph<N, undirected_tag> Graph;
336  Graph graph(labelImage_.shape(), DirectNeighborhood);
337 
338  typedef typename Graph::NodeIt graph_scanner;
339  typedef typename Graph::OutBackArcIt neighbor_iterator;
340 
341  vigra::UnionFindArray<Label> regions(maxLabel+1);
342  ArrayVector<unsigned char> done(maxLabel+1, false);
343 
344  // make sure that all regions exceed the sizeLimit
345  for (graph_scanner node(graph); node != lemon::INVALID; ++node)
346  {
347  Label label = labelImage_[*node];
348 
349  if(done[label])
350  continue; // already processed
351 
352  if(get<Count>(sizes, label) < sizeLimit)
353  {
354  // region is too small => merge into a neighbor
355  for (neighbor_iterator arc(graph, node); arc != lemon::INVALID; ++arc)
356  {
357  Label other = labelImage_[graph.target(*arc)];
358  if(label != other)
359  {
360  regions.makeUnion(label, other);
361  done[label] = true;
362  break;
363  }
364  }
365  }
366  else
367  {
368  done[label] = true;
369  }
370  }
371 
372  // make labels contiguous after possible merging
373  Label newMaxLabel = regions.makeContiguous();
374  for (graph_scanner node(graph); node != lemon::INVALID; ++node)
375  {
376  labelImage_[*node] = regions.findLabel(labelImage_[*node]);
377  }
378 
379  return (unsigned int)newMaxLabel;
380 }
381 
382 } // namespace detail
383 
384 
385 /** \brief Compute SLIC superpixels in arbitrary dimensions.
386 
387  This function implements the algorithm described in
388 
389  R. Achanta et al.: <em>"SLIC Superpixels Compared to State-of-the-Art
390  Superpixel Methods"</em>, IEEE Trans. Patt. Analysis Mach. Intell. 34(11):2274-2281, 2012
391 
392  The value type <tt>T</tt> of the source array \a src must provide the necessary functionality
393  to compute averages and squared distances (i.e. it must fulfill the requirements of a
394  \ref LinearSpace and support squaredNorm(T const &)). This is true for all scalar types as well as
395  \ref vigra::TinyVector and \ref vigra::RGBValue. The output array \a labels will be filled
396  with labels designating membership of each point in one of the superpixel regions.
397 
398  The output array can optionally contain seeds (which will be overwritten by the output)
399  to give you full control over seed placement. If \a labels is empty, seeds will be created
400  automatically by an internal call to generateSlicSeeds().
401 
402  The parameter \a seedDistance specifies the radius of the window around each seed (or, more
403  precisely, around the present regions centers) where the algorithm looks for potential members
404  of the corresponding superpixel. It thus places an upper limit on the superpixel size. When seeds
405  are computed automatically, this parameter also determines the grid spacing for seed placement.
406 
407  The parameter \a intensityScaling is used to normalize (i.e. divide) the color/intensity difference
408  before it is compared with the spatial distance. This corresponds to parameter <i>m</i> in equation
409  (2) of the paper.
410 
411  The options object can be used to specify the number of iterations (<tt>SlicOptions::iterations()</tt>)
412  and an explicit minimal superpixel size (<tt>SlicOptions::minSize()</tt>). By default, the algorithm
413  merges all regions that are smaller than 1/4 of the average superpixel size.
414 
415  The function returns the number of superpixels, which equals the largest label
416  because labeling starts at 1.
417 
418  <b> Declaration:</b>
419 
420  use arbitrary-dimensional arrays:
421  \code
422  namespace vigra {
423  template <unsigned int N, class T, class S1,
424  class Label, class S2,
425  class DistanceType>
426  unsigned int
427  slicSuperpixels(MultiArrayView<N, T, S1> const & src,
428  MultiArrayView<N, Label, S2> labels,
429  DistanceType intensityScaling,
430  unsigned int seedDistance,
431  SlicOptions const & options = SlicOptions());
432  }
433  \endcode
434 
435  <b> Usage:</b>
436 
437  <b>\#include</b> <vigra/slic.hxx><br>
438  Namespace: vigra
439 
440  \code
441  MultiArray<2, RGBValue<float> > src(Shape2(w, h));
442  ... // fill src image
443 
444  // transform image to Lab color space
445  transformMultiArray(srcMultiArrayRange(src), destMultiArray(src), RGBPrime2LabFunctor<float>());
446 
447  MultiArray<2, unsigned int> labels(src.shape());
448  int seedDistance = 15;
449  double intensityScaling = 20.0;
450 
451  // compute seeds automatically, perform 40 iterations, and scale intensity differences
452  // down to 1/20 before comparing with spatial distances
453  slicSuperpixels(src, labels, intensityScaling, seedDistance, SlicOptions().iterations(40));
454  \endcode
455 
456  This works for arbitrary-dimensional arrays.
457 */
458 doxygen_overloaded_function(template <...> unsigned int slicSuperpixels)
459 
460 template <unsigned int N, class T, class S1,
461  class Label, class S2,
462  class DistanceType>
463 unsigned int
464 slicSuperpixels(MultiArrayView<N, T, S1> const & src,
465  MultiArrayView<N, Label, S2> labels,
466  DistanceType intensityScaling,
467  unsigned int seedDistance,
468  SlicOptions const & options = SlicOptions())
469 {
470  if(!labels.any())
471  {
472  typedef typename NormTraits<T>::NormType TmpType;
473  MultiArray<N, TmpType> grad(src.shape());
474  gaussianGradientMagnitude(src, grad, 1.0);
475  generateSlicSeeds(grad, labels, seedDistance);
476  }
477  return detail::Slic<N, T, Label>(src, labels, intensityScaling, seedDistance, options).execute();
478 }
479 
480 //@}
481 
482 } // namespace vigra
483 
484 #endif // VIGRA_SLIC_HXX
unsigned int generateSlicSeeds(...)
Generate seeds for SLIC superpixel computation in arbitrary dimensions.
PowerSum< 0 > Count
Alias. Count.
Definition: accumulator-grammar.hxx:157
MultiArrayShape< actual_dimension >::type difference_type
Definition: multi_array.hxx:739
unsigned int labelImage(...)
Find the connected components of a segmented image.
FFTWComplex< R >::SquaredNormType squaredNorm(const FFTWComplex< R > &a)
squared norm (= squared magnitude)
Definition: fftw3.hxx:1044
int round(FixedPoint< IntBits, FracBits > v)
rounding to the nearest integer.
Definition: fixedpoint.hxx:683
unsigned int slicSuperpixels(...)
Compute SLIC superpixels in arbitrary dimensions.
SlicOptions()
Create options object with default settings.
Definition: slic.hxx:163
NumericTraits< T >::Promote sq(T t)
The square function.
Definition: mathutil.hxx:382
DivideByCount< Sum > Mean
Alias. Mean.
Definition: accumulator-grammar.hxx:173
Coord< Mean > RegionCenter
Alias. Region center.
Definition: accumulator-grammar.hxx:223
void extractFeatures(...)
void gaussianGradientMagnitude(...)
Calculate the gradient magnitude by means of a 1st derivatives of Gaussian filter.
doxygen_overloaded_function(template<...> void separableConvolveBlockwise) template< unsigned int N
Separated convolution on ChunkedArrays.
Options object for slicSuperpixels().
Definition: slic.hxx:157
int floor(FixedPoint< IntBits, FracBits > v)
rounding down.
Definition: fixedpoint.hxx:667
use only direct neighbors
Definition: multi_fwd.hxx:187
unsigned int labelMultiArray(...)
Find the connected components of a MultiArray with arbitrary many dimensions.
SlicOptions & iterations(unsigned int i)
Number of iterations.
Definition: slic.hxx:172
SlicOptions & minSize(unsigned int s)
Minimum superpixel size.
Definition: slic.hxx:184

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.11.1 (Fri May 19 2017)