Introduction
In the article we will look at algorithm for Fast 2D Convolution.
Separable Symmetric/Anti-Symmetric Convolution
- This article presents a convolution algorithm involving a separable symmetric/anti symmetric kernel.
- Such kernels are common in image processing like blurring,edge detection etc
- This optimization helps in speeding up many routines .
- Another requirement being addressed is we may have multiple kernels that need to be applied to the same image in application such as computing the basis representation of rectangular path of the image.
- since we are traversing the image,we can perform computation of all these kernels simultaneously instead of independent traversals.
- It is to be noted that this optimization is specific to symmetric/antisymmetric separable filters.
- We will be assuming that all the kernel are of the same size as well
- Since kernels are separable we will perform vertical convolution followed by horizontal convolution.
- One of the considerations is to minimize the row accesses .
- Another consideration is to perform a single pass over the entire source image
- Let (n*2+1) be the kernel size.
- The convolution filter is initialized by accepting all the row and column filter coefficients as well as the information if the filter is symmetric or not
- A class called SeperableSConvolution implemented this algorithm
01 class SeperableSConvolution
02 {
03 public:
04 SeperableSConvolution();
05 vector<mat> rowk;
06 vector<mat> colk;
07 vector<bool> symmetric;
08 int N;
09
10 void setKernel(Mat row,Mat col,bool symm);
11
12 void clearKernel();
13
14
15 16 void apply(Mat &src,Mat &dst);
17 };
Vertical Convolution
01 for(int y=0;y<s.height;y++)
02 {
03
04 float *srow0 = (float*)(src.data + src.step*y),*srow1=0;
05 float *drow = (float*)(dst.data + dst.step*y);
06
07
08
09
10
11 for( x = 0; x < s.width; x++ )
12 {
13 for(int l=0;l<ch;l++)
14 {
15 row[x*ch+l] = srow0[x]*rowk[l].at<float>(n);
16 }
17
18 }
19
20
21
22
23 for(int k=1;k<=n;k++)
24 {
25
26
27 srow0 = (float*)(src.data + src.step*std::max(y-k,0));
28 srow1 = (float*)(src.data + src.step*std::min(y+k,s.height-1));
29 for(int x=0;x<s.width;x++)
30 {
31
32
33 for(int l=0;l<ch;l++)
34 {
35 float p=srow0[x]+(1*(symmetric[l]?1:-1))*srow1[x];
36 row[x*ch+l]+=rowk[l].at<float>(k)*(p);
37 }
38 }
39 }
Horizontal Convolution
- Once vertical convolution is done we proceed to perform horizontal convolution
- Since in horizontal convolution there is only a single row access ,it is relatively simple process.
- The output image is a multi channel image,containing number of channels as desired number of input kernels being applied to the source image.
01 for(x=0;x<s.width;x++)
02 {
03
04
05
06 for(int l=0;l<ch;l++)
07 {
08 res[l]=row[x*ch+l]*colk[l].at<float>(n);
09 }
10
11
12 for(int k=1;k<=n;k++)
13 {
14
15 for(int l=0;l<ch;l++)
16 {
17 float p=(row[(x+k)]+(1*symmetric[l]?1:-1)
18 *row[(x-k)])*colk[l].at<float>(k);
19 res[l]=res[l]+p;
20 }
21 }
22
23
24 for(int l=0;l<ch;l++)
25 {
26 drow[x*ch+l]=res[l];
27 }
28 }
Code
- The class SeperableSConvolution defines a class for performing separable symmetric/ant-symmetric convolution/ Code is available in repository https://github.com/pi19404/OpenVision/ at Improper/convolution.hpp and ImgProc/convolution.cpp files.