• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2005-2007 Adobe Systems Incorporated
3 // Copyright 2019 Miral Shah <miralshah2211@gmail.com>
4 //
5 // Distributed under the Boost Software License, Version 1.0
6 // See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt
8 //
9 #ifndef BOOST_GIL_EXTENSION_NUMERIC_CONVOLVE_HPP
10 #define BOOST_GIL_EXTENSION_NUMERIC_CONVOLVE_HPP
11 
12 #include <boost/gil/extension/numeric/algorithm.hpp>
13 #include <boost/gil/extension/numeric/kernel.hpp>
14 #include <boost/gil/extension/numeric/pixel_numeric_operations.hpp>
15 
16 #include <boost/gil/algorithm.hpp>
17 #include <boost/gil/image_view_factory.hpp>
18 #include <boost/gil/metafunctions.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 
34 /// \brief Compute the cross-correlation of 1D kernel with the rows of an image
35 /// \tparam PixelAccum - TODO
36 /// \tparam SrcView Models ImageViewConcept
37 /// \tparam Kernel - TODO
38 /// \tparam DstView Models MutableImageViewConcept
39 /// \tparam Correlator - TODO
40 /// \param src_view
41 /// \param kernel - TODO
42 /// \param dst_view Destination where new computed values of pixels are assigned to
43 /// \param option - TODO
44 /// \param correlator - TODO
45 template
46 <
47     typename PixelAccum,
48     typename SrcView,
49     typename Kernel,
50     typename DstView,
51     typename Correlator
52 >
correlate_rows_impl(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option,Correlator correlator)53 void correlate_rows_impl(
54     SrcView const& src_view,
55     Kernel const& kernel,
56     DstView const& dst_view,
57     boundary_option option,
58     Correlator correlator)
59 {
60     BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions());
61     BOOST_ASSERT(kernel.size() != 0);
62 
63     if(kernel.size() == 1)
64     {
65         // Reduces to a multiplication
66         view_multiplies_scalar<PixelAccum>(src_view, *kernel.begin(), dst_view);
67         return;
68     }
69 
70     using src_pixel_ref_t = typename pixel_proxy<typename SrcView::value_type>::type;
71     using dst_pixel_ref_t = typename pixel_proxy<typename DstView::value_type>::type;
72     using x_coord_t = typename SrcView::x_coord_t;
73     using y_coord_t = typename SrcView::y_coord_t;
74 
75     x_coord_t const width = src_view.width();
76     y_coord_t const height = src_view.height();
77     if (width == 0)
78         return;
79 
80     PixelAccum acc_zero;
81     pixel_zeros_t<PixelAccum>()(acc_zero);
82     if (option == boundary_option::output_ignore || option == boundary_option::output_zero)
83     {
84         typename DstView::value_type dst_zero;
85         pixel_assigns_t<PixelAccum, dst_pixel_ref_t>()(acc_zero, dst_zero);
86         if (width < static_cast<x_coord_t>(kernel.size()))
87         {
88             if (option == boundary_option::output_zero)
89                 fill_pixels(dst_view, dst_zero);
90         }
91         else
92         {
93             std::vector<PixelAccum> buffer(width);
94             for (y_coord_t y = 0; y < height; ++y)
95             {
96                 assign_pixels(src_view.row_begin(y), src_view.row_end(y), &buffer.front());
97                 typename DstView::x_iterator it_dst = dst_view.row_begin(y);
98                 if (option == boundary_option::output_zero)
99                     std::fill_n(it_dst, kernel.left_size(), dst_zero);
100                 it_dst += kernel.left_size();
101                 correlator(&buffer.front(), &buffer.front() + width + 1 - kernel.size(),
102                            kernel.begin(), it_dst);
103                 it_dst += width + 1 - kernel.size();
104                 if (option == boundary_option::output_zero)
105                     std::fill_n(it_dst, kernel.right_size(), dst_zero);
106             }
107         }
108     }
109     else
110     {
111         std::vector<PixelAccum> buffer(width + kernel.size() - 1);
112         for (y_coord_t y = 0; y < height; ++y)
113         {
114             PixelAccum *it_buffer = &buffer.front();
115             if (option == boundary_option::extend_padded)
116             {
117                 assign_pixels(
118                     src_view.row_begin(y) - kernel.left_size(),
119                     src_view.row_end(y) + kernel.right_size(),
120                     it_buffer);
121             }
122             else if (option == boundary_option::extend_zero)
123             {
124                 std::fill_n(it_buffer, kernel.left_size(), acc_zero);
125                 it_buffer += kernel.left_size();
126                 assign_pixels(src_view.row_begin(y), src_view.row_end(y), it_buffer);
127                 it_buffer += width;
128                 std::fill_n(it_buffer, kernel.right_size(), acc_zero);
129             }
130             else if (option == boundary_option::extend_constant)
131             {
132                 PixelAccum filler;
133                 pixel_assigns_t<src_pixel_ref_t, PixelAccum>()(*src_view.row_begin(y), filler);
134                 std::fill_n(it_buffer, kernel.left_size(), filler);
135                 it_buffer += kernel.left_size();
136                 assign_pixels(src_view.row_begin(y), src_view.row_end(y), it_buffer);
137                 it_buffer += width;
138                 pixel_assigns_t<src_pixel_ref_t, PixelAccum>()(src_view.row_end(y)[-1], filler);
139                 std::fill_n(it_buffer, kernel.right_size(), filler);
140             }
141 
142             correlator(
143                 &buffer.front(), &buffer.front() + width,
144                 kernel.begin(),
145                 dst_view.row_begin(y));
146         }
147     }
148 }
149 
150 template <typename PixelAccum>
151 class correlator_n
152 {
153 public:
correlator_n(std::size_t size)154     correlator_n(std::size_t size) : size_(size) {}
155 
156     template <typename SrcIterator, typename KernelIterator, typename DstIterator>
operator ()(SrcIterator src_begin,SrcIterator src_end,KernelIterator kernel_begin,DstIterator dst_begin)157     void operator()(
158         SrcIterator src_begin,
159         SrcIterator src_end,
160         KernelIterator kernel_begin,
161         DstIterator dst_begin)
162     {
163         correlate_pixels_n<PixelAccum>(src_begin, src_end, kernel_begin, size_, dst_begin);
164     }
165 
166 private:
167     std::size_t size_{0};
168 };
169 
170 template <std::size_t Size, typename PixelAccum>
171 struct correlator_k
172 {
173     template <typename SrcIterator, typename KernelIterator, typename DstIterator>
operator ()boost::gil::detail::correlator_k174     void operator()(
175         SrcIterator src_begin,
176         SrcIterator src_end,
177         KernelIterator kernel_begin,
178         DstIterator dst_begin)
179     {
180         correlate_pixels_k<Size, PixelAccum>(src_begin, src_end, kernel_begin, dst_begin);
181     }
182 };
183 
184 } // namespace detail
185 
186 /// \ingroup ImageAlgorithms
187 /// \brief Correlate 1D variable-size kernel along the rows of image
188 /// \tparam PixelAccum TODO
189 /// \tparam SrcView Models ImageViewConcept
190 /// \tparam Kernel TODO
191 /// \tparam DstView Models MutableImageViewConcept
192 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
193 BOOST_FORCEINLINE
correlate_rows(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option=boundary_option::extend_zero)194 void correlate_rows(
195     SrcView const& src_view,
196     Kernel const& kernel,
197     DstView const& dst_view,
198     boundary_option option = boundary_option::extend_zero)
199 {
200     detail::correlate_rows_impl<PixelAccum>(
201         src_view, kernel, dst_view, option, detail::correlator_n<PixelAccum>(kernel.size()));
202 }
203 
204 /// \ingroup ImageAlgorithms
205 /// \brief Correlate 1D variable-size kernel along the columns of image
206 /// \tparam PixelAccum TODO
207 /// \tparam SrcView Models ImageViewConcept
208 /// \tparam Kernel TODO
209 /// \tparam DstView Models MutableImageViewConcept
210 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
211 BOOST_FORCEINLINE
correlate_cols(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option=boundary_option::extend_zero)212 void correlate_cols(
213     SrcView const& src_view,
214     Kernel const& kernel,
215     DstView const& dst_view,
216     boundary_option option = boundary_option::extend_zero)
217 {
218     correlate_rows<PixelAccum>(
219         transposed_view(src_view), kernel, transposed_view(dst_view), option);
220 }
221 
222 /// \ingroup ImageAlgorithms
223 /// \brief Convolve 1D variable-size kernel along the rows of image
224 /// \tparam PixelAccum TODO
225 /// \tparam SrcView Models ImageViewConcept
226 /// \tparam Kernel TODO
227 /// \tparam DstView Models MutableImageViewConcept
228 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
229 BOOST_FORCEINLINE
convolve_rows(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option=boundary_option::extend_zero)230 void convolve_rows(
231     SrcView const& src_view,
232     Kernel const& kernel,
233     DstView const& dst_view,
234     boundary_option option = boundary_option::extend_zero)
235 {
236     correlate_rows<PixelAccum>(src_view, reverse_kernel(kernel), dst_view, option);
237 }
238 
239 /// \ingroup ImageAlgorithms
240 /// \brief Convolve 1D variable-size kernel along the columns of image
241 /// \tparam PixelAccum TODO
242 /// \tparam SrcView Models ImageViewConcept
243 /// \tparam Kernel TODO
244 /// \tparam DstView Models MutableImageViewConcept
245 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
246 BOOST_FORCEINLINE
convolve_cols(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option=boundary_option::extend_zero)247 void convolve_cols(
248     SrcView const& src_view,
249     Kernel const& kernel,
250     DstView const& dst_view,
251     boundary_option option = boundary_option::extend_zero)
252 {
253     convolve_rows<PixelAccum>(
254         transposed_view(src_view), kernel, transposed_view(dst_view), option);
255 }
256 
257 /// \ingroup ImageAlgorithms
258 /// \brief Correlate 1D fixed-size kernel along the rows of image
259 /// \tparam PixelAccum TODO
260 /// \tparam SrcView Models ImageViewConcept
261 /// \tparam Kernel TODO
262 /// \tparam DstView Models MutableImageViewConcept
263 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
264 BOOST_FORCEINLINE
correlate_rows_fixed(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option=boundary_option::extend_zero)265 void correlate_rows_fixed(
266     SrcView const& src_view,
267     Kernel const& kernel,
268     DstView const& dst_view,
269     boundary_option option = boundary_option::extend_zero)
270 {
271     using correlator = detail::correlator_k<Kernel::static_size, PixelAccum>;
272     detail::correlate_rows_impl<PixelAccum>(src_view, kernel, dst_view, option, correlator{});
273 }
274 
275 /// \ingroup ImageAlgorithms
276 /// \brief Correlate 1D fixed-size kernel along the columns of image
277 /// \tparam PixelAccum TODO
278 /// \tparam SrcView Models ImageViewConcept
279 /// \tparam Kernel TODO
280 /// \tparam DstView Models MutableImageViewConcept
281 template <typename PixelAccum,typename SrcView,typename Kernel,typename DstView>
282 BOOST_FORCEINLINE
correlate_cols_fixed(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option=boundary_option::extend_zero)283 void correlate_cols_fixed(
284     SrcView const& src_view,
285     Kernel const& kernel,
286     DstView const& dst_view,
287     boundary_option option = boundary_option::extend_zero)
288 {
289     correlate_rows_fixed<PixelAccum>(
290         transposed_view(src_view), kernel, transposed_view(dst_view), option);
291 }
292 
293 /// \ingroup ImageAlgorithms
294 /// \brief Convolve 1D fixed-size kernel along the rows of image
295 /// \tparam PixelAccum TODO
296 /// \tparam SrcView Models ImageViewConcept
297 /// \tparam Kernel TODO
298 /// \tparam DstView Models MutableImageViewConcept
299 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
300 BOOST_FORCEINLINE
convolve_rows_fixed(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option=boundary_option::extend_zero)301 void convolve_rows_fixed(
302     SrcView const& src_view,
303     Kernel const& kernel,
304     DstView const& dst_view,
305     boundary_option option = boundary_option::extend_zero)
306 {
307     correlate_rows_fixed<PixelAccum>(src_view, reverse_kernel(kernel), dst_view, option);
308 }
309 
310 /// \ingroup ImageAlgorithms
311 /// \brief Convolve 1D fixed-size kernel along the columns of image
312 /// \tparam PixelAccum TODO
313 /// \tparam SrcView Models ImageViewConcept
314 /// \tparam Kernel TODO
315 /// \tparam DstView Models MutableImageViewConcept
316 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
317 BOOST_FORCEINLINE
convolve_cols_fixed(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option=boundary_option::extend_zero)318 void convolve_cols_fixed(
319     SrcView const& src_view,
320     Kernel const& kernel,
321     DstView const& dst_view,
322     boundary_option option = boundary_option::extend_zero)
323 {
324     convolve_rows_fixed<PixelAccum>(
325         transposed_view(src_view), kernel, transposed_view(dst_view), option);
326 }
327 
328 namespace detail
329 {
330 
331 /// \ingroup ImageAlgorithms
332 /// \brief Convolve 1D variable-size kernel along both rows and columns of image
333 /// \tparam PixelAccum TODO
334 /// \tparam SrcView Models ImageViewConcept
335 /// \tparam Kernel TODO
336 /// \tparam DstView Models MutableImageViewConcept
337 template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
338 BOOST_FORCEINLINE
convolve_1d(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view,boundary_option option=boundary_option::extend_zero)339 void convolve_1d(
340     SrcView const& src_view,
341     Kernel const& kernel,
342     DstView const& dst_view,
343     boundary_option option = boundary_option::extend_zero)
344 {
345     convolve_rows<PixelAccum>(src_view, kernel, dst_view, option);
346     convolve_cols<PixelAccum>(dst_view, kernel, dst_view, option);
347 }
348 
349 template <typename SrcView, typename DstView, typename Kernel>
convolve_2d_impl(SrcView const & src_view,DstView const & dst_view,Kernel const & kernel)350 void convolve_2d_impl(SrcView const& src_view, DstView const& dst_view, Kernel const& kernel)
351 {
352     int flip_ker_row, flip_ker_col, row_boundary, col_boundary;
353     float aux_total;
354     for (std::ptrdiff_t view_row = 0; view_row < src_view.height(); ++view_row)
355     {
356         for (std::ptrdiff_t view_col = 0; view_col < src_view.width(); ++view_col)
357         {
358             aux_total = 0.0f;
359             for (std::size_t kernel_row = 0; kernel_row < kernel.size(); ++kernel_row)
360             {
361                 flip_ker_row = kernel.size() - 1 - kernel_row;      // row index of flipped kernel
362 
363                 for (std::size_t kernel_col = 0; kernel_col < kernel.size(); ++kernel_col)
364                 {
365                     flip_ker_col = kernel.size() - 1 - kernel_col; // column index of flipped kernel
366 
367                     // index of input signal, used for checking boundary
368                     row_boundary = view_row + (kernel.center_y() - flip_ker_row);
369                     col_boundary = view_col + (kernel.center_x() - flip_ker_col);
370 
371                     // ignore input samples which are out of bound
372                     if (row_boundary >= 0 && row_boundary < src_view.height() &&
373                         col_boundary >= 0 && col_boundary < src_view.width())
374                     {
375                         aux_total +=
376                             src_view(col_boundary, row_boundary) *
377                             kernel.at(flip_ker_row, flip_ker_col);
378                     }
379                 }
380             }
381             dst_view(view_col, view_row) = aux_total;
382         }
383     }
384 }
385 
386 /// \ingroup ImageAlgorithms
387 /// \brief convolve_2d can only use convolve_option_extend_zero as convolve_boundary_option
388 ///  this is the default option and cannot be changed for now
389 ///  (In future there are plans to improve the algorithm and allow user to use other options as well)
390 /// \tparam SrcView Models ImageViewConcept
391 /// \tparam Kernel TODO
392 /// \tparam DstView Models MutableImageViewConcept
393 template <typename SrcView, typename DstView, typename Kernel>
convolve_2d(SrcView const & src_view,Kernel const & kernel,DstView const & dst_view)394 void convolve_2d(SrcView const& src_view, Kernel const& kernel, DstView const& dst_view)
395 {
396     BOOST_ASSERT(src_view.dimensions() == dst_view.dimensions());
397     BOOST_ASSERT(kernel.size() != 0);
398 
399     gil_function_requires<ImageViewConcept<SrcView>>();
400     gil_function_requires<MutableImageViewConcept<DstView>>();
401     static_assert(color_spaces_are_compatible
402     <
403         typename color_space_type<SrcView>::type,
404         typename color_space_type<DstView>::type
405     >::value, "Source and destination views must have pixels with the same color space");
406 
407     for (std::size_t i = 0; i < src_view.num_channels(); i++)
408     {
409         detail::convolve_2d_impl(
410             nth_channel_view(src_view, i),
411             nth_channel_view(dst_view, i),
412             kernel
413         );
414     }
415 }
416 
417 }}} // namespace boost::gil::detail
418 
419 #endif
420