# Calculating a convolution of an Image with C++: Image Processing

In convolution, the calculation performed at a pixel is a weighted sum of grey levels from a neighbourhood surrounding a pixel. Grey levels taken from the neighbourhood are weighted by coefficients that come from a matrix or convolution kernel. The kernel’s dimensions define the size of the neighbourhood in which calculation take place. The most common dimension is *3×3*. I am using this size of matrix in this article. During convolution, we take each kernel coefficient in turn and multiply it by a value from the neighbourhood of the image lying under the kernel. We apply the kernel to the image in such a way that the value at the top-left corner of the kernel is multiplied by the value at bottom-right corner of the neighbourhood. This can be expressed by following mathematical expression for kernel of size *mxn*.

Where *h* denotes the convolution kernel, *f* is the input pixel, *g* is the output pixel. Here *n2 = n/2* and *m2 = m/2*. For *3×3* dimension of kernel, the above expression reduces to

Lets see an example. Suppose we have convolution matrix

and part of the image pixel is

Now applying above expression, the value of a central pixel becomes

*g(x, y)* = -1 x 82 + 0 x 78 + 1 x 88 + -1 x 65 + 0 x 56 + 1 x 76 + -1 x 60 + 0 x 53 + 1 x 72 = 29

Algorithm for single pixel convolution can be written as ( for *3×3*)

Create a Kernel of size *3×3* and fill the kernel with coefficients

sum = 0

**for** *k *= -1 to 1 **do**

**for** *j* = -1 to 1 **do**

sum = sum + *h(j +1, k + 1)*f(x – j, y – k)*

**end for**

**end for**

*g(x, y)* = sum

The above discussion is mainly focused on single pixel operation. But the image doesn’t have only single pixel. To do the convolution operation on whole image, we must perform convolution on all pixels as follows

**for** *y* = 0 to *image_height* **do**

**for** *x =* 0 to *image_width* **do**

perform single pixel convolution

** end for**

**end for**

But this algorithm has one limitation that it is impossible to calculate the convolution at borders. When we try to calculate convolution of pixel lying in image border, part of the kernel lies outside the image. To handle this limitation there are various approaches, some are given below

**No processing at **border

This is simple technique in which pixels at border are simply neglected. This can be done using following algorithm

**for** **all** pixel coordinates *x* and *y*, **do**

*g(x, y)* = 0

**end for**

**for** *y* = 1 to *image_height* – 1

**for** *x* = 1 to *image_width* – 1

perform single pixel convolution

**end for**

**end for**

**Reflected Indexing**

In this method, the pixel lying outside the image i.e. (*x – j, y – k*) are reflected back into the image by using following algorithm

**if** *x < 0* **then**

* x = -x – 1*

**else** **if** *x* >= *image_width* **then**

*x* =* 2*image_width – x* – 1

**end if**

**Circular Indexing**

In this method, coordinates that exceed the bounds of the image wrap around to the opposite side using following algorithm

**if** x < 0 **then**

*x = x + image_width*

**else if** x >= *image_width ***then**

*x = x – image_width*

**end if**

**C++ implementation : Source Code**

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | #include<iostream> #include<opencv2/imgproc/imgproc.hpp> #include<opencv2/highgui/highgui.hpp> using namespace std; using namespace cv; int reflect(int M, int x) { if(x < 0) { return -x - 1; } if(x >= M) { return 2*M - x - 1; } return x; } int circular(int M, int x) { if (x<0) return x+M; if(x >= M) return x-M; return x; } void noBorderProcessing(Mat src, Mat dst, float Kernel[][3]) { float sum; for(int y = 1; y < src.rows - 1; y++){ for(int x = 1; x < src.cols - 1; x++){ sum = 0.0; for(int k = -1; k <= 1;k++){ for(int j = -1; j <=1; j++){ sum = sum + Kernel[j+1][k+1]*src.at<uchar>(y - j, x - k); } } dst.at<uchar>(y,x) = sum; } } } void refletedIndexing(Mat src, Mat dst, float Kernel[][3]) { float sum, x1, y1; for(int y = 0; y < src.rows; y++){ for(int x = 0; x < src.cols; x++){ sum = 0.0; for(int k = -1;k <= 1; k++){ for(int j = -1;j <= 1; j++ ){ x1 = reflect(src.cols, x - j); y1 = reflect(src.rows, y - k); sum = sum + Kernel[j+1][k+1]*src.at<uchar>(y1,x1); } } dst.at<uchar>(y,x) = sum; } } } void circularIndexing(Mat src, Mat dst, float Kernel[][3]) { float sum, x1, y1; for(int y = 0; y < src.rows; y++){ for(int x = 0; x < src.cols; x++){ sum = 0.0; for(int k = -1;k <= 1; k++){ for(int j = -1;j <= 1; j++ ){ x1 = circular(src.cols, x - j); y1 = circular(src.rows, y - k); sum = sum + Kernel[j+1][k+1]*src.at<uchar>(y1,x1); } } dst.at<uchar>(y,x) = sum; } } } int main() { Mat src, dst; /// Load an image src = imread("salt.jpg", CV_LOAD_IMAGE_GRAYSCALE); if( !src.data ) { return -1; } float Kernel[3][3] = { {1/9.0, 1/9.0, 1/9.0}, {1/9.0, 1/9.0, 1/9.0}, {1/9.0, 1/9.0, 1/9.0} }; dst = src.clone(); for(int y = 0; y < src.rows; y++) for(int x = 0; x < src.cols; x++) dst.at<uchar>(y,x) = 0.0; circularIndexing(src, dst, Kernel); namedWindow("final"); imshow("final", dst); namedWindow("initial"); imshow("initial", src); waitKey(); return 0; } |

I am not able to understand why are you using "dst.at(y,x) = 0.0;" when you have already created a cloned image. Help. Thanks.

65456