- What are convolutions?
- What are kernels?
- Different kernels and their effects
- Separable convolutions
- A suboptimal implementation
- Resources you can visit
- Instructions
- Results
A convolution is a fundamental mathematical operation commonly used in various
image processing tasks.
Convolutions provide a way of 'multiplying' two arrays numbers together,
typically of different sizes. While convolutions are also used to process 1D
signals such as sound data, we will focus on the application of convolutions to
2D signals, i.e. images.
They form the basis for implementing image processing operations where the
output pixel values are some linear combination of the corresponding input
pixel value and those that happen to surround it.
Mathematically, if
and if
In this notation,
Note: In our use case, where we convolve an image having a finite number of discrete rows and columns, we just need to use the discrete form of the convolution operation.
The convolution operation is better shown using an animation:
Kernels, also known as filters, are arrays of numbers (can be n dimensional i.e. 1D, 2D, 3D ...), which we often refer to as matrices, with which we convolve input images to bring out certain desired effects. Some such effects are blurring, sharpening, outlining, embossing, edge detection, et cetera.
So, here
Now, we can directly apply the kernels on the input image using convolution.
Here are some examples of specific kernels and their effects on some example input images:
Example:
Matrix:
Blur operations like this have the effect of averaging nearby pixels.
This simple blur corresponds to a simple average, whereas others (like the
Gaussian blur we will see later) are a weighted average.
A common theme with blurs is that their entries sum to 1, such that the total
luminance of the image is preserved.
Example:
Matrix:
This also works as a high pass filter.
Example:
Matrices (horizontal and vertical):
Example:
Matrix:
To compute the value of one output pixel after a 2D convolution using a
The Gaussian kernel is a well-known example of a separable kernel:
It is worth noting that the convolution operation is associative, so either of the simpler convolutions can be applied first.
We have spun up a suboptimal implementation of the convolution operation for
demonstration purposes.
To build and run it, execute the following commands from the root of this
repository.
cd 4_cv_basics/4_convolutions_filtering # Enter the project directory
make SRC=main.cpp link=src/convolution.cpp # Use make to build the project
./Convolution_Filtering # Execute the built project
The demo code is purposefully kept in an unoptimized state, your task is to
understand it and then improve it based on your understanding.
Feel free to make any changes you deem useful.
Some points to be considered while thinking about optimizations are:
- Will it work for kernels having different sizes?
- Can it handle images having different number of channels?
- Can you improve space complexity of above implementation?
Above points are given only for the purpose of giving a rough idea about possible optimizations and are not necessarily sufficient.
- Move into the cv_basics directory from the root pixels directory by running following command :
cd 4_cv_basics
- Move into the Convolutions directory from the cv_basics directory by running following command :
cd 4_convolutions_filtering
- For building the file:-
- If you want to build naive convolution then run
make SRC=naive.cpp link=convolution.cpp
- If you want to build convolutionUsingOpenCV then run
make SRC=convolutionUsingOpenCV.cpp link=convolution.cpp
- If you want to build SeparableConvolution then run
make SRC=separableConvolutions.cpp link=convolution.cpp
- Finally execute the binary file created by running the following command :
./convolution_filtering
- Move into the cv_basics directory from the root pixels directory by running following command :
cd 4_cv_basics
- Move into the Convolutions directory directory from the cv_basics directory by running following command :
cd 4_convolutions_filtering
- For building the file:-
- If you want to build naive convolution then run
make SRC=./benchmarks/naive.cpp link=./src/convolution.cpp
- If you want to build convolutionUsingOpenCV then run
make SRC=./benchmarks/convolutionUsingOpenCV.cpp link=./src/convolution.cpp
- If you want to build SeparableConvolution then run
make SRC=./benchmarks/separableConvolutions.cpp link=./src/convolution.cpp
- Finally execute the binary file created by running the following command :
./convolution_filtering
Naive Convolution:-
Convolutions Using OpenCV:-
Separable Convolutions Using OpenCV:-