###  | #  |

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 seperable 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>
159 class correlator_n
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>
182 struct correlator_k
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>
206BOOST_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>
226BOOST_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>
245BOOST_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>
264BOOST_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>
283BOOST_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>
303BOOST_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>
322BOOST_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>
341BOOST_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>
364BOOST_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
boost::gil::detail::correlator_n
Provides functionality for performing 1D correlation between the kernel and a buffer storing row pixe...
Definition: convolve.hpp:160
boost::gil::fill_pixels
BOOST_FORCEINLINE void fill_pixels(View const &view, Value const &value)
std::fill for image views
Definition: algorithm.hpp:420
boost
defined(BOOST_NO_CXX17_HDR_MEMORY_RESOURCE)
Definition: algorithm.hpp:36
boost::gil::detail::correlator_k
Provides functionality for performing 1D correlation between the kernel and a buffer storing row pixe...
Definition: convolve.hpp:183
boost::gil::size
Returns an integral constant type specifying the number of elements in a color base.
Definition: color_base_algorithm.hpp:42