Boost GIL


convolve.hpp
1 //
2 // Copyright 2005-2007 Adobe Systems Incorporated
3 // Copyright 2019 Miral Shah <miralshah2211@gmail.com>
4 // Copyright 2019-2021 Pranam Lashkari <plashkari628@gmail.com>
5 //
6 // Distributed under the Boost Software License, Version 1.0
7 // See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt
9 //
10 #ifndef BOOST_GIL_IMAGE_PROCESSING_CONVOLVE_HPP
11 #define BOOST_GIL_IMAGE_PROCESSING_CONVOLVE_HPP
12 
13 #include <boost/gil/image_processing/kernel.hpp>
14 
15 #include <boost/gil/algorithm.hpp>
16 #include <boost/gil/image_view_factory.hpp>
17 #include <boost/gil/metafunctions.hpp>
18 #include <boost/gil/pixel_numeric_operations.hpp>
19 
20 #include <boost/assert.hpp>
21 
22 #include <algorithm>
23 #include <cstddef>
24 #include <functional>
25 #include <type_traits>
26 #include <vector>
27 
28 namespace boost { namespace gil {
29 
30 // 2D spatial separable convolutions and cross-correlations
31 
32 namespace detail {
33 
50 template
51 <
52  typename PixelAccum,
53  typename SrcView,
54  typename Kernel,
55  typename DstView,
56  typename Correlator
57 >
58 void correlate_rows_impl(
59  SrcView const& src_view,
60  Kernel const& kernel,
61  DstView const& dst_view,
62  boundary_option option,
63  Correlator correlator)
64 {
65  BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions());
66  BOOST_ASSERT(kernel.size() != 0);
67 
68  if(kernel.size() == 1)
69  {
70  // Reduces to a multiplication
71  view_multiplies_scalar<PixelAccum>(src_view, *kernel.begin(), dst_view);
72  return;
73  }
74 
75  using src_pixel_ref_t = typename pixel_proxy<typename SrcView::value_type>::type;
76  using dst_pixel_ref_t = typename pixel_proxy<typename DstView::value_type>::type;
77  using x_coord_t = typename SrcView::x_coord_t;
78  using y_coord_t = typename SrcView::y_coord_t;
79 
80  x_coord_t const width = src_view.width();
81  y_coord_t const height = src_view.height();
82  if (width == 0)
83  return;
84 
85  PixelAccum acc_zero;
86  pixel_zeros_t<PixelAccum>()(acc_zero);
87  if (option == boundary_option::output_ignore || option == boundary_option::output_zero)
88  {
89  typename DstView::value_type dst_zero;
90  pixel_assigns_t<PixelAccum, dst_pixel_ref_t>()(acc_zero, dst_zero);
91  if (width < static_cast<x_coord_t>(kernel.size()))
92  {
93  if (option == boundary_option::output_zero)
94  fill_pixels(dst_view, dst_zero);
95  }
96  else
97  {
98  std::vector<PixelAccum> buffer(width);
99  for (y_coord_t y = 0; y < height; ++y)
100  {
101  assign_pixels(src_view.row_begin(y), src_view.row_end(y), &buffer.front());
102  typename DstView::x_iterator it_dst = dst_view.row_begin(y);
103  if (option == boundary_option::output_zero)
104  std::fill_n(it_dst, kernel.left_size(), dst_zero);
105  it_dst += kernel.left_size();
106  correlator(&buffer.front(), &buffer.front() + width + 1 - kernel.size(),
107  kernel.begin(), it_dst);
108  it_dst += width + 1 - kernel.size();
109  if (option == boundary_option::output_zero)
110  std::fill_n(it_dst, kernel.right_size(), dst_zero);
111  }
112  }
113  }
114  else
115  {
116  std::vector<PixelAccum> buffer(width + kernel.size() - 1);
117  for (y_coord_t y = 0; y < height; ++y)
118  {
119  PixelAccum *it_buffer = &buffer.front();
120  if (option == boundary_option::extend_padded)
121  {
122  assign_pixels(
123  src_view.row_begin(y) - kernel.left_size(),
124  src_view.row_end(y) + kernel.right_size(),
125  it_buffer);
126  }
127  else if (option == boundary_option::extend_zero)
128  {
129  std::fill_n(it_buffer, kernel.left_size(), acc_zero);
130  it_buffer += kernel.left_size();
131  assign_pixels(src_view.row_begin(y), src_view.row_end(y), it_buffer);
132  it_buffer += width;
133  std::fill_n(it_buffer, kernel.right_size(), acc_zero);
134  }
135  else if (option == boundary_option::extend_constant)
136  {
137  PixelAccum filler;
138  pixel_assigns_t<src_pixel_ref_t, PixelAccum>()(*src_view.row_begin(y), filler);
139  std::fill_n(it_buffer, kernel.left_size(), filler);
140  it_buffer += kernel.left_size();
141  assign_pixels(src_view.row_begin(y), src_view.row_end(y), it_buffer);
142  it_buffer += width;
143  pixel_assigns_t<src_pixel_ref_t, PixelAccum>()(src_view.row_end(y)[-1], filler);
144  std::fill_n(it_buffer, kernel.right_size(), filler);
145  }
146 
147  correlator(
148  &buffer.front(), &buffer.front() + width,
149  kernel.begin(),
150  dst_view.row_begin(y));
151  }
152  }
153 }
154 
158 template <typename PixelAccum>
160 {
161 public:
162  correlator_n(std::size_t size) : size_(size) {}
163 
164  template <typename SrcIterator, typename KernelIterator, typename DstIterator>
165  void operator()(
166  SrcIterator src_begin,
167  SrcIterator src_end,
168  KernelIterator kernel_begin,
169  DstIterator dst_begin)
170  {
171  correlate_pixels_n<PixelAccum>(src_begin, src_end, kernel_begin, size_, dst_begin);
172  }
173 
174 private:
175  std::size_t size_{0};
176 };
177 
181 template <std::size_t Size, typename PixelAccum>
183 {
184  template <typename SrcIterator, typename KernelIterator, typename DstIterator>
185  void operator()(
186  SrcIterator src_begin,
187  SrcIterator src_end,
188  KernelIterator kernel_begin,
189  DstIterator dst_begin)
190  {
191  correlate_pixels_k<Size, PixelAccum>(src_begin, src_end, kernel_begin, dst_begin);
192  }
193 };
194 
195 } // namespace detail
196 
205 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
206 BOOST_FORCEINLINE
207 void correlate_rows(
208  SrcView const& src_view,
209  Kernel const& kernel,
210  DstView const& dst_view,
211  boundary_option option = boundary_option::extend_zero)
212 {
213  detail::correlate_rows_impl<PixelAccum>(
214  src_view, kernel, dst_view, option, detail::correlator_n<PixelAccum>(kernel.size()));
215 }
216 
225 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
226 BOOST_FORCEINLINE
227 void correlate_cols(
228  SrcView const& src_view,
229  Kernel const& kernel,
230  DstView const& dst_view,
231  boundary_option option = boundary_option::extend_zero)
232 {
233  correlate_rows<PixelAccum>(
234  transposed_view(src_view), kernel, transposed_view(dst_view), option);
235 }
236 
244 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
245 BOOST_FORCEINLINE
246 void convolve_rows(
247  SrcView const& src_view,
248  Kernel const& kernel,
249  DstView const& dst_view,
250  boundary_option option = boundary_option::extend_zero)
251 {
252  correlate_rows<PixelAccum>(src_view, reverse_kernel(kernel), dst_view, option);
253 }
254 
263 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
264 BOOST_FORCEINLINE
265 void convolve_cols(
266  SrcView const& src_view,
267  Kernel const& kernel,
268  DstView const& dst_view,
269  boundary_option option = boundary_option::extend_zero)
270 {
271  convolve_rows<PixelAccum>(
272  transposed_view(src_view), kernel, transposed_view(dst_view), option);
273 }
274 
282 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
283 BOOST_FORCEINLINE
284 void correlate_rows_fixed(
285  SrcView const& src_view,
286  Kernel const& kernel,
287  DstView const& dst_view,
288  boundary_option option = boundary_option::extend_zero)
289 {
290  using correlator = detail::correlator_k<Kernel::static_size, PixelAccum>;
291  detail::correlate_rows_impl<PixelAccum>(src_view, kernel, dst_view, option, correlator{});
292 }
293 
302 template <typename PixelAccum,typename SrcView,typename Kernel,typename DstView>
303 BOOST_FORCEINLINE
304 void correlate_cols_fixed(
305  SrcView const& src_view,
306  Kernel const& kernel,
307  DstView const& dst_view,
308  boundary_option option = boundary_option::extend_zero)
309 {
310  correlate_rows_fixed<PixelAccum>(
311  transposed_view(src_view), kernel, transposed_view(dst_view), option);
312 }
313 
321 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
322 BOOST_FORCEINLINE
323 void convolve_rows_fixed(
324  SrcView const& src_view,
325  Kernel const& kernel,
326  DstView const& dst_view,
327  boundary_option option = boundary_option::extend_zero)
328 {
329  correlate_rows_fixed<PixelAccum>(src_view, reverse_kernel(kernel), dst_view, option);
330 }
331 
340 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
341 BOOST_FORCEINLINE
342 void convolve_cols_fixed(
343  SrcView const& src_view,
344  Kernel const& kernel,
345  DstView const& dst_view,
346  boundary_option option = boundary_option::extend_zero)
347 {
348  convolve_rows_fixed<PixelAccum>(
349  transposed_view(src_view), kernel, transposed_view(dst_view), option);
350 }
351 
352 namespace detail
353 {
354 
363 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
364 BOOST_FORCEINLINE
365 void convolve_1d(
366  SrcView const& src_view,
367  Kernel const& kernel,
368  DstView const& dst_view,
369  boundary_option option = boundary_option::extend_zero)
370 {
371  convolve_rows<PixelAccum>(src_view, kernel, dst_view, option);
372  convolve_cols<PixelAccum>(dst_view, kernel, dst_view, option);
373 }
374 
375 template <typename SrcView, typename DstView, typename Kernel>
376 void convolve_2d_impl(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel)
377 {
378  int flip_ker_row, flip_ker_col, row_boundary, col_boundary;
379  float aux_total;
380  for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row)
381  {
382  for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col)
383  {
384  aux_total = 0.0f;
385  for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row)
386  {
387  flip_ker_row = kernel.size() - 1 - kernel_row; // row index of flipped kernel
388 
389  for (std::size_t kernel_col = 0; kernel_col < kernel.size(); ++kernel_col)
390  {
391  flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel
392 
393  // index of input signal, used for checking boundary
394  row_boundary = view_row + (kernel.center_y() - flip_ker_row);
395  col_boundary = view_col + (kernel.center_x() - flip_ker_col);
396 
397  // ignore input samples which are out of bound
398  if (row_boundary >= 0 && row_boundary < src_view.height() &&
399  col_boundary >= 0 && col_boundary < src_view.width())
400  {
401  aux_total +=
402  src_view(col_boundary, row_boundary)[0] *
403  kernel.at(flip_ker_col, flip_ker_row);
404  }
405  }
406  }
407  dst_view(view_col, view_row) = aux_total;
408  }
409  }
410 }
411 
419 template <typename SrcView, typename DstView, typename Kernel>
420 void convolve_2d(SrcView const& src_view, Kernel const& kernel, DstView const& dst_view)
421 {
422  BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions());
423  BOOST_ASSERT(kernel.size() != 0);
424 
425  gil_function_requires<ImageViewConcept<SrcView>>();
426  gil_function_requires<MutableImageViewConcept<DstView>>();
427  static_assert(color_spaces_are_compatible
428  <
429  typename color_space_type<SrcView>::type,
430  typename color_space_type<DstView>::type
431  >::value, "Source and destination views must have pixels with the same color space");
432 
433  for (std::size_t i = 0; i < src_view.num_channels(); i++)
434  {
435  detail::convolve_2d_impl(
436  nth_channel_view(src_view, i),
437  nth_channel_view(dst_view, i),
438  kernel
439  );
440  }
441 }
442 
443 }}} // namespace boost::gil::detail
444 
445 #endif
Provides functionality for performing 1D correlation between the kernel and a buffer storing row pixe...
Definition: convolve.hpp:160
BOOST_FORCEINLINE void fill_pixels(View const &view, Value const &value)
std::fill for image views
Definition: algorithm.hpp:420
defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE)
Definition: algorithm.hpp:36
Provides functionality for performing 1D correlation between the kernel and a buffer storing row pixe...
Definition: convolve.hpp:183
Returns an integral constant type specifying the number of elements in a color base.
Definition: color_base_algorithm.hpp:42