Boost GIL


threshold.hpp
1 //
2 // Copyright 2019 Miral Shah <miralshah2211@gmail.com>
3 //
4 // Use, modification and distribution are subject to the Boost Software License,
5 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //
8 #ifndef BOOST_GIL_IMAGE_PROCESSING_THRESHOLD_HPP
9 #define BOOST_GIL_IMAGE_PROCESSING_THRESHOLD_HPP
10 
11 #include <limits>
12 #include <array>
13 #include <type_traits>
14 #include <cstddef>
15 #include <algorithm>
16 #include <vector>
17 #include <cmath>
18 
19 #include <boost/gil/image.hpp>
20 #include <boost/gil/extension/numeric/kernel.hpp>
21 #include <boost/gil/extension/numeric/convolve.hpp>
22 #include <boost/assert.hpp>
23 
24 namespace boost { namespace gil {
25 
26 namespace detail {
27 
28 template
29 <
30  typename SourceChannelT,
31  typename ResultChannelT,
32  typename SrcView,
33  typename DstView,
34  typename Operator
35 >
36 void threshold_impl(SrcView const& src_view, DstView const& dst_view, Operator const& threshold_op)
37 {
38  gil_function_requires<ImageViewConcept<SrcView>>();
39  gil_function_requires<MutableImageViewConcept<DstView>>();
40  static_assert(color_spaces_are_compatible
41  <
42  typename color_space_type<SrcView>::type,
43  typename color_space_type<DstView>::type
44  >::value, "Source and destination views must have pixels with the same color space");
45 
46  //iterate over the image chaecking each pixel value for the threshold
47  for (std::ptrdiff_t y = 0; y < src_view.height(); y++)
48  {
49  typename SrcView::x_iterator src_it = src_view.row_begin(y);
50  typename DstView::x_iterator dst_it = dst_view.row_begin(y);
51 
52  for (std::ptrdiff_t x = 0; x < src_view.width(); x++)
53  {
54  static_transform(src_it[x], dst_it[x], threshold_op);
55  }
56  }
57 }
58 
59 } //namespace boost::gil::detail
60 
68 {
69  regular,
70  inverse
71 };
72 
76 {
77  otsu,
78  triangle
79 };
80 
84 {
85  threshold,
86  zero
87 };
88 
89 enum class threshold_adaptive_method
90 {
91  mean,
92  gaussian
93 };
94 
106 template <typename SrcView, typename DstView>
108  SrcView const& src_view,
109  DstView const& dst_view,
110  typename channel_type<DstView>::type threshold_value,
111  typename channel_type<DstView>::type max_value,
113 )
114 {
115  //deciding output channel type and creating functor
116  using source_channel_t = typename channel_type<SrcView>::type;
117  using result_channel_t = typename channel_type<DstView>::type;
118 
119  if (direction == threshold_direction::regular)
120  {
121  detail::threshold_impl<source_channel_t, result_channel_t>(src_view, dst_view,
122  [threshold_value, max_value](source_channel_t px) -> result_channel_t {
123  return px > threshold_value ? max_value : 0;
124  });
125  }
126  else
127  {
128  detail::threshold_impl<source_channel_t, result_channel_t>(src_view, dst_view,
129  [threshold_value, max_value](source_channel_t px) -> result_channel_t {
130  return px > threshold_value ? 0 : max_value;
131  });
132  }
133 }
134 
145 template <typename SrcView, typename DstView>
147  SrcView const& src_view,
148  DstView const& dst_view,
149  typename channel_type<DstView>::type threshold_value,
151 )
152 {
153  //deciding output channel type and creating functor
154  using result_channel_t = typename channel_type<DstView>::type;
155 
156  result_channel_t max_value = std::numeric_limits<result_channel_t>::max();
157  threshold_binary(src_view, dst_view, threshold_value, max_value, direction);
158 }
159 
171 template <typename SrcView, typename DstView>
173  SrcView const& src_view,
174  DstView const& dst_view,
175  typename channel_type<DstView>::type threshold_value,
178 )
179 {
180  //deciding output channel type and creating functor
181  using source_channel_t = typename channel_type<SrcView>::type;
182  using result_channel_t = typename channel_type<DstView>::type;
183 
184  std::function<result_channel_t(source_channel_t)> threshold_logic;
185 
187  {
188  if (direction == threshold_direction::regular)
189  {
190  detail::threshold_impl<source_channel_t, result_channel_t>(src_view, dst_view,
191  [threshold_value](source_channel_t px) -> result_channel_t {
192  return px > threshold_value ? threshold_value : px;
193  });
194  }
195  else
196  {
197  detail::threshold_impl<source_channel_t, result_channel_t>(src_view, dst_view,
198  [threshold_value](source_channel_t px) -> result_channel_t {
199  return px > threshold_value ? px : threshold_value;
200  });
201  }
202  }
203  else
204  {
205  if (direction == threshold_direction::regular)
206  {
207  detail::threshold_impl<source_channel_t, result_channel_t>(src_view, dst_view,
208  [threshold_value](source_channel_t px) -> result_channel_t {
209  return px > threshold_value ? px : 0;
210  });
211  }
212  else
213  {
214  detail::threshold_impl<source_channel_t, result_channel_t>(src_view, dst_view,
215  [threshold_value](source_channel_t px) -> result_channel_t {
216  return px > threshold_value ? 0 : px;
217  });
218  }
219  }
220 }
221 
222 namespace detail{
223 
224 template <typename SrcView, typename DstView>
225 void otsu_impl(SrcView const& src_view, DstView const& dst_view, threshold_direction direction)
226 {
227  //deciding output channel type and creating functor
228  using source_channel_t = typename channel_type<SrcView>::type;
229 
230  std::array<std::size_t, 256> histogram{};
231  //initial value of min is set to maximum possible value to compare histogram data
232  //initial value of max is set to minimum possible value to compare histogram data
233  auto min = std::numeric_limits<source_channel_t>::max(),
234  max = std::numeric_limits<source_channel_t>::min();
235 
236  if (sizeof(source_channel_t) > 1 || std::is_signed<source_channel_t>::value)
237  {
238  //iterate over the image to find the min and max pixel values
239  for (std::ptrdiff_t y = 0; y < src_view.height(); y++)
240  {
241  typename SrcView::x_iterator src_it = src_view.row_begin(y);
242  for (std::ptrdiff_t x = 0; x < src_view.width(); x++)
243  {
244  if (src_it[x] < min) min = src_it[x];
245  if (src_it[x] > min) min = src_it[x];
246  }
247  }
248 
249  //making histogram
250  for (std::ptrdiff_t y = 0; y < src_view.height(); y++)
251  {
252  typename SrcView::x_iterator src_it = src_view.row_begin(y);
253 
254  for (std::ptrdiff_t x = 0; x < src_view.width(); x++)
255  {
256  histogram[((src_it[x] - min) * 255) / (max - min)]++;
257  }
258  }
259  }
260  else
261  {
262  //making histogram
263  for (std::ptrdiff_t y = 0; y < src_view.height(); y++)
264  {
265  typename SrcView::x_iterator src_it = src_view.row_begin(y);
266 
267  for (std::ptrdiff_t x = 0; x < src_view.width(); x++)
268  {
269  histogram[src_it[x]]++;
270  }
271  }
272  }
273 
274  //histData = histogram data
275  //sum = total (background + foreground)
276  //sumB = sum background
277  //wB = weight background
278  //wf = weight foreground
279  //varMax = tracking the maximum known value of between class variance
280  //mB = mu background
281  //mF = mu foreground
282  //varBeetween = between class variance
283  //http://www.labbookpages.co.uk/software/imgProc/otsuThreshold.html
284  //https://www.ipol.im/pub/art/2016/158/
285  std::ptrdiff_t total_pixel = src_view.height() * src_view.width();
286  std::ptrdiff_t sum_total = 0, sum_back = 0;
287  std::size_t weight_back = 0, weight_fore = 0, threshold = 0;
288  double var_max = 0, mean_back, mean_fore, var_intra_class;
289 
290  for (std::size_t t = 0; t < 256; t++)
291  {
292  sum_total += t * histogram[t];
293  }
294 
295  for (int t = 0; t < 256; t++)
296  {
297  weight_back += histogram[t]; // Weight Background
298  if (weight_back == 0) continue;
299 
300  weight_fore = total_pixel - weight_back; // Weight Foreground
301  if (weight_fore == 0) break;
302 
303  sum_back += t * histogram[t];
304 
305  mean_back = sum_back / weight_back; // Mean Background
306  mean_fore = (sum_total - sum_back) / weight_fore; // Mean Foreground
307 
308  // Calculate Between Class Variance
309  var_intra_class = weight_back * weight_fore * (mean_back - mean_fore) * (mean_back - mean_fore);
310 
311  // Check if new maximum found
312  if (var_intra_class > var_max) {
313  var_max = var_intra_class;
314  threshold = t;
315  }
316  }
317  if (sizeof(source_channel_t) > 1 && std::is_unsigned<source_channel_t>::value)
318  {
319  threshold_binary(src_view, dst_view, (threshold * (max - min) / 255) + min, direction);
320  }
321  else {
322  threshold_binary(src_view, dst_view, threshold, direction);
323  }
324 }
325 } //namespace detail
326 
327 template <typename SrcView, typename DstView>
328 void threshold_optimal
329 (
330  SrcView const& src_view,
331  DstView const& dst_view,
334 )
335 {
336  if (mode == threshold_optimal_value::otsu)
337  {
338  for (std::size_t i = 0; i < src_view.num_channels(); i++)
339  {
340  detail::otsu_impl
341  (nth_channel_view(src_view, i), nth_channel_view(dst_view, i), direction);
342  }
343  }
344 }
345 
346 namespace detail {
347 
348 template
349 <
350  typename SourceChannelT,
351  typename ResultChannelT,
352  typename SrcView,
353  typename DstView,
354  typename Operator
355 >
356 void adaptive_impl
357 (
358  SrcView const& src_view,
359  SrcView const& convolved_view,
360  DstView const& dst_view,
361  Operator const& threshold_op
362 )
363 {
364  //template argument validation
365  gil_function_requires<ImageViewConcept<SrcView>>();
366  gil_function_requires<MutableImageViewConcept<DstView>>();
367 
368  static_assert(color_spaces_are_compatible
369  <
370  typename color_space_type<SrcView>::type,
371  typename color_space_type<DstView>::type
372  >::value, "Source and destination views must have pixels with the same color space");
373 
374  //iterate over the image chaecking each pixel value for the threshold
375  for (std::ptrdiff_t y = 0; y < src_view.height(); y++)
376  {
377  typename SrcView::x_iterator src_it = src_view.row_begin(y);
378  typename SrcView::x_iterator convolved_it = convolved_view.row_begin(y);
379  typename DstView::x_iterator dst_it = dst_view.row_begin(y);
380 
381  for (std::ptrdiff_t x = 0; x < src_view.width(); x++)
382  {
383  static_transform(src_it[x], convolved_it[x], dst_it[x], threshold_op);
384  }
385  }
386 }
387 } //namespace boost::gil::detail
388 
389 template <typename SrcView, typename DstView>
390 void threshold_adaptive
391 (
392  SrcView const& src_view,
393  DstView const& dst_view,
394  typename channel_type<DstView>::type max_value,
395  std::size_t kernel_size,
396  threshold_adaptive_method method = threshold_adaptive_method::mean,
398  typename channel_type<DstView>::type constant = 0
399 )
400 {
401  BOOST_ASSERT_MSG((kernel_size % 2 != 0), "Kernel size must be an odd number");
402 
403  typedef typename channel_type<SrcView>::type source_channel_t;
404  typedef typename channel_type<DstView>::type result_channel_t;
405 
406  if (method == threshold_adaptive_method::mean)
407  {
408  std::vector<float> mean_kernel_values(kernel_size, 1.0f/kernel_size);
409  kernel_1d<float> kernel(mean_kernel_values.begin(), kernel_size, kernel_size/2);
410 
411  image<typename SrcView::value_type> temp_img(src_view.width(), src_view.height());
412  typename image<typename SrcView::value_type>::view_t temp_view = view(temp_img);
413  SrcView temp_conv(temp_view);
414 
415  convolve<pixel<float, typename SrcView::value_type::layout_t>>(
416  src_view, kernel, temp_view
417  );
418 
419  if (direction == threshold_direction::regular)
420  {
421  detail::adaptive_impl<source_channel_t, result_channel_t>(src_view, temp_conv, dst_view,
422  [max_value, constant](source_channel_t px, source_channel_t threshold) -> result_channel_t
423  { return px > (threshold - constant) ? max_value : 0; });
424  }
425  else
426  {
427  detail::adaptive_impl<source_channel_t, result_channel_t>(src_view, temp_conv, dst_view,
428  [max_value, constant](source_channel_t px, source_channel_t threshold) -> result_channel_t
429  { return px > (threshold - constant) ? 0 : max_value; });
430  }
431  }
432 }
433 
434 template <typename SrcView, typename DstView>
435 void threshold_adaptive
436 (
437  SrcView const& src_view,
438  DstView const& dst_view,
439  std::size_t kernel_size,
440  threshold_adaptive_method method = threshold_adaptive_method::mean,
442  int constant = 0
443 )
444 {
445  //deciding output channel type and creating functor
446  typedef typename channel_type<DstView>::type result_channel_t;
447 
448  result_channel_t max_value = std::numeric_limits<result_channel_t>::max();
449 
450  threshold_adaptive(src_view, dst_view, max_value, kernel_size, method, direction, constant);
451 }
452 
454 
455 }} //namespace boost::gil
456 
457 #endif //BOOST_GIL_IMAGE_PROCESSING_THRESHOLD_HPP
Definition: algorithm.hpp:30
void threshold_binary(SrcView const &src_view, DstView const &dst_view, typename channel_type< DstView >::type threshold_value, threshold_direction direction=threshold_direction::regular)
Applies fixed threshold to each pixel of image view. Performs image binarization by thresholding chan...
Definition: threshold.hpp:146
threshold_truncate_mode
TODO.
Definition: threshold.hpp:83
threshold_direction
Definition: threshold.hpp:67
threshold_optimal_value
Method of optimal threshold value calculation.
Definition: threshold.hpp:75
Consider values less than or equal to threshold value.
container interface over image view. Models ImageConcept, PixelBasedConcept
Definition: image.hpp:40
Definition: color_convert.hpp:31
const image< Pixel, IsPlanar, Alloc >::view_t & view(image< Pixel, IsPlanar, Alloc > &img)
Returns the non-constant-pixel view of an image.
Definition: image.hpp:443
void threshold_truncate(SrcView const &src_view, DstView const &dst_view, typename channel_type< DstView >::type threshold_value, threshold_truncate_mode mode=threshold_truncate_mode::threshold, threshold_direction direction=threshold_direction::regular)
Applies truncating threshold to each pixel of image view. Takes an image view and performes truncatin...
Definition: threshold.hpp:172
Consider values greater than threshold value.