Skip to content

Commit ed6391b

Browse files
committed
Add functional documentation and fix some warnings
1 parent 06fd700 commit ed6391b

File tree

3 files changed

+142
-23
lines changed

3 files changed

+142
-23
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
Convolution forms a pillar in a large number of image processing applications. It is a mathematical
2+
operation which is applied in a same manner over the entire considered region in an image.
3+
Accessing all pixels in a similar manner creates the room for optimization in this algorithm.
4+
We have thus targeted our efforts towards improving the spatial access of pixel elements during
5+
2D convolution and correlation.
6+
7+
There are many ways of performing 2D convolution, the naive one includes irregular access of source
8+
image pixels. Due to this, a large number of cache misses occur which further in turn degrade the
9+
performance. We have tried to improve this access pattern and hence reduce the number of overall
10+
cache misses.
11+
12+
What are we doing differently?
13+
14+
Our strategy involves storing relevant 2D image data in a 1D buffer container. This container will
15+
then be used for accessing required patches of pixels during convolution. These patches will be
16+
accessed by a sliding window algorithm so that their dot product with 1D aligned kernel can be stored
17+
in destination image.
18+
19+
But how much data should be stored inside the buffer?
20+
21+
We store only that much data which will be required by kernel during its one single horizontal slide.
22+
In other words, the size of buffer container will be kernel_size * img_width + boundary_offset.
23+
Here boundary_offset is the addition in buffer size which will take place due to boundary
24+
extrapolation for corner pixels.
25+
26+
What to do after forming the buffer container?
27+
28+
Once our data is present inside the buffer container, a sliding window algorithm is applied for
29+
calculating the dot product between kernel and image patch contained inside sliding window.
30+
31+
Boundary extrapolation is taken care by changing elements of buffer container suitably.
32+
33+
How do we take care of variable anchor point positions?
34+
35+
While filling the buffer container, we will take care of this by starting/ending the filling process
36+
at suitable points in source image.
37+
38+
This will automatically take care of the problem and will achieve desired effect due to the followed
39+
pattern of pixel assignment.

include/boost/gil/extension/numeric/algorithm.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ auto correlate_pixels_k(
218218
return dst_begin;
219219
}
220220

221+
/// \brief 2D cross-correlation with a variable-size kernel
221222
template
222223
<
223224
typename PixelAccum,
@@ -246,7 +247,7 @@ auto correlate_pixels_n_2d(
246247

247248
PixelAccum accum_zero;
248249
pixel_zeros_t<PixelAccum>()(accum_zero);
249-
std::ptrdiff_t index = 0;
250+
long unsigned int index = 0;
250251
std::ptrdiff_t const kernel_size = kernel_dimension * kernel_dimension;
251252

252253
// try eliminating "index" variable.
@@ -268,6 +269,7 @@ auto correlate_pixels_n_2d(
268269
return dst_begin;
269270
}
270271

272+
/// \brief 2D cross-correlation with a fix-size kernel
271273
template
272274
<
273275
std::size_t kernel_dimension,
@@ -296,7 +298,7 @@ auto correlate_pixels_k_2d(
296298

297299
PixelAccum accum_zero;
298300
pixel_zeros_t<PixelAccum>()(accum_zero);
299-
std::ptrdiff_t index = 0;
301+
long unsigned int index = 0;
300302
std::ptrdiff_t const kernel_size = kernel_dimension * kernel_dimension;
301303

302304
while (index < src_size - kernel_size + 1)

include/boost/gil/extension/numeric/convolve.hpp

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@ void convolve_1d(
372372
convolve_cols<PixelAccum>(dst_view, kernel, dst_view, option);
373373
}
374374

375+
/// \brief Provides functionality for performing correlation between the kernel and buffer.
376+
/// Kernel size is to be provided through constructor for all instances.
377+
/// This correlator is specifically used in 2D correlation.
375378
template <typename PixelAccum>
376379
class correlator_n_2d
377380
{
@@ -393,6 +396,9 @@ class correlator_n_2d
393396
std::size_t _kernel_dimension{0};
394397
};
395398

399+
/// \brief Provides functionality for performing correlation between the kernel and buffer.
400+
/// Kernel size is a template parameter and must be compulsorily specified while using.
401+
/// This correlator is specifically used in 2D correlation.
396402
template <std::size_t kernel_dimension, typename PixelAccum>
397403
struct correlator_k_2d
398404
{
@@ -408,17 +414,33 @@ struct correlator_k_2d
408414
}
409415
};
410416

417+
/// \brief Computes the cross-correlation of a 2D kernel with an image.
418+
/// \tparam PixelAccum - Specifies tha data type which will be used for creating buffer container
419+
/// utilized for holding source image pixels after applying appropriate boundary manipulations.
420+
/// \tparam SrcView - Specifies the type of gil view of source image which is to be correlated
421+
/// with the kernel.
422+
/// \tparam Kernel - Specifies the type of 2D kernel which will be correlated with source image.
423+
/// \tparam DstView - Specifies the type of gil view which will store the result of
424+
/// correlation between source image and kernel.
425+
/// \tparam Correlator - Specifies the type of correlator which should be used for performing
426+
/// correlation.
427+
/// \param src_view - Gil view of source image used in correlation.
428+
/// \param kernel - 2D kernel which will be correlated with source image.
429+
/// \param dst_view - Gil view which will store the result of correlation between "src_view"
430+
/// and "kernel".
431+
/// \param option - Specifies the manner in which boundary pixels of "dst_view" should be computed.
432+
/// \param correlator - Correlator which will be used for performing correlation.
411433
template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView, typename Correlator>
412434
void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
413435
boundary_option option, Correlator correlator)
414436
{
415-
std::size_t const upper_extrapolation_size = kernel.upper_size();
416-
std::size_t const lower_extrapolation_size = kernel.lower_size();
417-
std::size_t const left_extrapolation_size = kernel.left_size();
418-
std::size_t const right_extrapolation_size = kernel.right_size();
437+
long unsigned int const upper_extrapolation_size = kernel.upper_size();
438+
long unsigned int const lower_extrapolation_size = kernel.lower_size();
439+
long unsigned int const left_extrapolation_size = kernel.left_size();
440+
long unsigned int const right_extrapolation_size = kernel.right_size();
419441

420442
bool explicit_fill = 1;
421-
std::ptrdiff_t col = 0, row = 0;
443+
long unsigned int col = 0, row = 0;
422444
PixelAccum zero_pixel;
423445
pixel_zeros_t<PixelAccum>()(zero_pixel);
424446

@@ -492,11 +514,11 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
492514
std::fill_n(buffer.begin() + buffer.size() - kernel.size() * right_extrapolation_size,
493515
kernel.size() * right_extrapolation_size, zero_pixel);
494516

495-
for (std::ptrdiff_t index = kernel.size() * left_extrapolation_size;
517+
for (long unsigned int index = kernel.size() * left_extrapolation_size;
496518
index < buffer.size() - kernel.size() * right_extrapolation_size;
497519
index += kernel.size())
498520
{
499-
for (std::ptrdiff_t inner_index = 0; inner_index < upper_extrapolation_size;
521+
for (long unsigned int inner_index = 0; inner_index < upper_extrapolation_size;
500522
++inner_index)
501523
{
502524
buffer[index + inner_index] = zero_pixel;
@@ -515,7 +537,7 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
515537
std::fill_n(intermediate_buffer.begin(), upper_extrapolation_size,
516538
intermediate_buffer[upper_extrapolation_size]);
517539

518-
for (std::ptrdiff_t inner_index = 0; inner_index < kernel.size() * left_extrapolation_size;
540+
for (long unsigned int inner_index = 0; inner_index < kernel.size() * left_extrapolation_size;
519541
inner_index += kernel.size())
520542
{
521543
std::copy(intermediate_buffer.begin(), intermediate_buffer.end(),
@@ -529,18 +551,18 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
529551
std::fill_n(intermediate_buffer.begin(), upper_extrapolation_size,
530552
intermediate_buffer[upper_extrapolation_size]);
531553

532-
for (std::ptrdiff_t inner_index = buffer.size() - kernel.size() * right_extrapolation_size;
554+
for (long unsigned int inner_index = buffer.size() - kernel.size() * right_extrapolation_size;
533555
inner_index < buffer.size(); inner_index += kernel.size())
534556
{
535557
std::copy(intermediate_buffer.begin(), intermediate_buffer.end(),
536558
buffer.begin() + inner_index);
537559
}
538560

539-
for (std::ptrdiff_t index = kernel.size() * left_extrapolation_size;
561+
for (long unsigned int index = kernel.size() * left_extrapolation_size;
540562
index < buffer.size() - kernel.size() * right_extrapolation_size;
541563
index += kernel.size())
542564
{
543-
for (std::ptrdiff_t inner_index = 0; inner_index < upper_extrapolation_size;
565+
for (long unsigned int inner_index = 0; inner_index < upper_extrapolation_size;
544566
++inner_index)
545567
{
546568
// check indices throughout the algorithm.
@@ -552,7 +574,7 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
552574
else if (option == boundary_option::extend_reflection)
553575
{
554576
explicit_fill = 0;
555-
std::ptrdiff_t row_bound =
577+
long unsigned int row_bound =
556578
kernel.size() - upper_extrapolation_size > upper_extrapolation_size ?
557579
kernel.size() - upper_extrapolation_size : upper_extrapolation_size;
558580

@@ -661,7 +683,7 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
661683
{
662684
if (row)
663685
{
664-
for (std::ptrdiff_t temp_col = 0; temp_col < left_extrapolation_size; ++temp_col)
686+
for (long unsigned int temp_col = 0; temp_col < left_extrapolation_size; ++temp_col)
665687
{
666688
std::ptrdiff_t left_bound = temp_col * kernel.size();
667689
std::rotate(buffer.begin() + left_bound, buffer.begin() + left_bound + 1,
@@ -692,7 +714,7 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
692714
}
693715
}
694716

695-
for (std::ptrdiff_t temp_col = 0; temp_col < right_extrapolation_size; ++temp_col)
717+
for (long unsigned int temp_col = 0; temp_col < right_extrapolation_size; ++temp_col)
696718
{
697719
std::ptrdiff_t left_bound = (left_extrapolation_size + src_view.width() +
698720
temp_col) * kernel.size();
@@ -722,7 +744,7 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
722744
}
723745
}
724746

725-
for (std::ptrdiff_t temp_col = 0; temp_col < src_view.width(); ++temp_col)
747+
for (long int temp_col = 0; temp_col < src_view.width(); ++temp_col)
726748
{
727749
std::ptrdiff_t left_bound = (left_extrapolation_size + temp_col) * kernel.size();
728750
std::rotate(buffer.begin() + left_bound, buffer.begin() + left_bound + 1,
@@ -737,7 +759,7 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
737759

738760
for (row = src_view.height() - lower_extrapolation_size; row < src_view.height(); ++row)
739761
{
740-
for (std::ptrdiff_t temp_col = 0; temp_col < left_extrapolation_size; ++temp_col)
762+
for (long unsigned int temp_col = 0; temp_col < left_extrapolation_size; ++temp_col)
741763
{
742764
std::ptrdiff_t left_bound = temp_col * kernel.size();
743765
std::rotate(buffer.begin() + left_bound, buffer.begin() + left_bound + 1,
@@ -765,7 +787,7 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
765787
}
766788
}
767789

768-
for (std::ptrdiff_t temp_col = 0; temp_col < right_extrapolation_size; ++temp_col)
790+
for (long unsigned int temp_col = 0; temp_col < right_extrapolation_size; ++temp_col)
769791
{
770792
std::ptrdiff_t left_bound = (left_extrapolation_size + src_view.width() + temp_col) *
771793
kernel.size();
@@ -796,7 +818,7 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
796818
}
797819
}
798820

799-
for (std::ptrdiff_t temp_col = 0; temp_col < src_view.width(); ++temp_col)
821+
for (long int temp_col = 0; temp_col < src_view.width(); ++temp_col)
800822
{
801823
std::ptrdiff_t left_bound = (left_extrapolation_size + temp_col) * kernel.size();
802824
std::rotate(buffer.begin() + left_bound, buffer.begin() + left_bound + 1,
@@ -828,18 +850,29 @@ void correlate_2d_impl(SrcView src_view, Kernel kernel, DstView dst_view,
828850
}
829851
}
830852

853+
/// \ingroup ImageAlgorithms
854+
/// \brief Detects whether a 2D kernel is spatial separable or not and separates it if the kernel
855+
/// is spatially separable.
856+
/// \tparam Kernel Specifies the type of 2D kernel which will be considered for separation.
857+
/// \tparam Container_1d Specifies the type of 1D container which will store the separated components
858+
/// of input kernel if it is separable.
859+
/// \param kernel - Kernel which is to be considered for separation.
860+
/// \param sep_ker_vertical - Container which will store separated vertical component of 2D kernel
861+
/// if kernel is spatially separable.
862+
/// \param sep_ker_horizontal - Container which will store separated horizontal component of 2D
863+
/// kernel if kernel is spatially separable.
831864
template <typename Kernel, typename Container_1d>
832865
bool separate(Kernel const kernel, Container_1d& sep_ker_vertical, Container_1d& sep_ker_horizontal)
833866
{
834867
bool is_rank_1 = 1;
835868
sep_ker_vertical[0] = 1;
836-
for (std::ptrdiff_t row = 1; row < kernel.size(); ++row)
869+
for (std::size_t row = 1; row < kernel.size(); ++row)
837870
{
838871
float mul_factor = 0;
839872
if (kernel.at(0, 0))
840873
mul_factor = kernel.at(0, row) / kernel.at(0, 0);
841874
sep_ker_vertical[row] = mul_factor;
842-
for (std::ptrdiff_t col = 0; col < kernel.size(); ++col)
875+
for (std::size_t col = 0; col < kernel.size(); ++col)
843876
{
844877
auto transformed_elem = mul_factor * kernel.at(col, 0);
845878
if (transformed_elem != kernel.at(col, row))
@@ -853,14 +886,26 @@ bool separate(Kernel const kernel, Container_1d& sep_ker_vertical, Container_1d&
853886
}
854887
if (is_rank_1)
855888
{
856-
for (std::ptrdiff_t col = 0; col < kernel.size(); ++col)
889+
for (std::size_t col = 0; col < kernel.size(); ++col)
857890
sep_ker_horizontal[col] = kernel.at(col, 0);
858891
}
859892
return is_rank_1;
860893
}
861894

862895
} // namespace detail
863896

897+
/// \ingroup ImageAlgorithms
898+
/// \brief Correlate 2D variable-size kernel along an image.
899+
/// \tparam PixelAccum Specifies tha data type which will be used while creating buffer container
900+
/// which is utilized for holding source image pixels after applying appropriate boundary
901+
/// manipulations.
902+
/// \tparam SrcView Models ImageViewConcept.
903+
/// \tparam Kernel Specifies the type of 2D kernel which will be correlated with source image.
904+
/// \tparam DstView Models MutableImageViewConcept.
905+
/// \param src_view - Gil view of source image.
906+
/// \param kernel - Variable size 2D kernel which will be correlated with src_view.
907+
/// \param dst_view - Gil view of destination image.
908+
/// \param option - Specifies the manner in which boundary pixels of dst_view will be computed.
864909
template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
865910
void correlate_2d(SrcView src_view, Kernel kernel, DstView dst_view,
866911
boundary_option option = boundary_option::extend_zero)
@@ -898,6 +943,17 @@ void correlate_2d(SrcView src_view, Kernel kernel, DstView dst_view,
898943
kernel.size()));
899944
}
900945

946+
/// \ingroup ImageAlgorithms
947+
/// \brief Correlate 2D fixed-size kernel along an image.
948+
/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container
949+
/// utilized for holding source image pixels after applying appropriate boundary manipulations.
950+
/// \tparam SrcView Models ImageViewConcept.
951+
/// \tparam Kernel Specifies the type of 2D kernel which will be correlated with source image.
952+
/// \tparam DstView Models MutableImageViewConcept.
953+
/// \param src_view - Gil view of source image.
954+
/// \param kernel - Fixed size 2D kernel which will be correlated with src_view.
955+
/// \param dst_view - Gil view of destination image.
956+
/// \param option - Specifies the manner in which boundary pixels of dst_view will be computed.
901957
template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
902958
void correlate_2d_fixed(SrcView src_view, Kernel kernel, DstView dst_view,
903959
boundary_option option = boundary_option::extend_zero)
@@ -933,6 +989,17 @@ void correlate_2d_fixed(SrcView src_view, Kernel kernel, DstView dst_view,
933989
detail::correlate_2d_impl<PixelAccum>(src_view, kernel, dst_view, option, correlator{});
934990
}
935991

992+
/// \ingroup ImageAlgorithms
993+
/// \brief Convolves 2D variable-size kernel along an image.
994+
/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container
995+
/// utilized for holding source image pixels after applying appropriate boundary manipulations.
996+
/// \tparam SrcView Models ImageViewConcept.
997+
/// \tparam Kernel Specifies the type of 2D kernel which will be convoluted with source image.
998+
/// \tparam DstView Models MutableImageViewConcept.
999+
/// \param src_view - Gil view of source image.
1000+
/// \param kernel - Variable size 2D kernel which will be convolved with src_view.
1001+
/// \param dst_view - Gil view of destination image.
1002+
/// \param option - Specifies the manner in which boundary pixels of dst_view will be computed.
9361003
template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
9371004
void convolve_2d(SrcView src_view, Kernel kernel, DstView dst_view,
9381005
boundary_option option = boundary_option::extend_zero)
@@ -952,6 +1019,17 @@ void convolve_2d(SrcView src_view, Kernel kernel, DstView dst_view,
9521019
// check reverse kernel.
9531020
}
9541021

1022+
/// \ingroup ImageAlgorithms
1023+
/// \brief Convolve 2D fixed-size kernel along an image
1024+
/// \tparam PixelAccum Specifies tha data type which will be used for creating buffer container
1025+
/// utilized for holding source image pixels after applying appropriate boundary manipulations.
1026+
/// \tparam SrcView Models ImageViewConcept.
1027+
/// \tparam Kernel Specifies the type of 2D kernel which will be convolved with source image.
1028+
/// \tparam DstView Models MutableImageViewConcept.
1029+
/// \param src_view - Gil view of source image.
1030+
/// \param kernel - Fixed size 2D kernel which will be convolved with src_view.
1031+
/// \param dst_view - Gil view of destination image.
1032+
/// \param option - Specifies the manner in which boundary pixels of dst_view will be computed.
9551033
template <typename PixelAccum, typename SrcView, typename Kernel, typename DstView>
9561034
void convolve_2d_fixed(SrcView src_view, Kernel kernel, DstView dst_view,
9571035
boundary_option option = boundary_option::extend_zero)

0 commit comments

Comments
 (0)