A parallel 2-dimensional convolution implementation in C++17 for image blurring based on MPI, openMP and hybrid approaches.
Blurring is a very common operation in many fields, ranging from image vision to medical science to astrophysics. Although the blurring concept is commonly associated to a post- processing step in digital photography, mathematically blurring is well defined as a cross correlation between the input signal
In this repository we implemented parallel blurring for 2D onechannel images (2-dimensional matrices), i.e. the cross correlation operator is re-defined as:
where
The serial implementation of the algorithm is straightforward, and the core component of the code is a simple four nested loop (see serial_conv.hpp
):
The procedure employed to build such a structure is the following:
.... // other stuff
for (row = 0; row < input_rows; ++row)
{
for (col = 0; col < input_cols; ++col)
{
tmp = 0.0; // temporary variable to accumulate the sum
for (stride_row = 0; stride_row < kernel_size_row; ++stride_row)
{
for (stride_col = 0; stride_col < kernel_size_col; ++stride_col)
{
x = col - dx + stride_col;
y = row - dy + stride_row;
if (x >= 0 && x < input_cols && y >= 0 && y < input_rows)
{
index_input = y * input_cols + x;
index_kernel = stride_row * kernel_size_col + stride_col;
tmp += input[index_input] * kernel[index_kernel];
}
}
}
output[row * input_cols + col] = tmp; //output matrix
}
}
.... // other stuff
Note: PGM
format is used for images. Matrix are saved in a std::vector
templated with unsigned short int
if two byte are needed for representing the image (maximum value > 255
), otherwise unsigned char
(one byte representation) is enough. The kernel representation is templated (default float
), it is easily changable by changing the flag prec
in the make
command, check Compile.
The strategy employed for the parallelization with MPI consists in dividing in stripes the input matrix
The strategy implemented divides the workload by the use of a #pragma parallel for
and using openmp threads.
Curretly different kernels are available:
- Mean kernel:
Mean
- Weight kernel:
Weight
- Gaussian kernel:
Gaussian
In the mean kernel the weights of each entries are
In the weight the weights assigned to each pixel are no equal as the mean kernel. A typical example is the common centrally-weighted Kernel in which the
center entry of the filter holds a significant fraction focus
) has to be passed at execution time (error raising otherwise)
In the gaussian kernel the weights of each entries are assigned by using a Gaussian function (in 2D in this case):
where
Clone the repository and navigate to the folder src
. A Makefile
is
available along with the following recipes:
Recipe | Result |
---|---|
verbose |
Show the textual representation of the program in execution. |
nfile |
Does not save the blurred image output as PGM image. |
time |
Show the time it took to perform the convolution (one run only). |
debug |
The binary produced will show debug messages. |
leaks |
Find memory leaks in the source code. The executable does not produce any output. |
There are also some optional parameters which are used to select additional features:
Parameter | Values | Explanation | Default |
---|---|---|---|
src |
mpi /omp /hybrid /none |
Framework used for the parallelization (none produced a serial version of the code). |
none |
prec |
double |
Precision used to represents the values in memory for the kernel matrix. | float |
For instance, the following produces the executable blur_mpi.x
which prints
the time needed to convolve an image using MPI, and represents data as
float
values in memory:
make time src=mpi prec=float
Run the executable blur_omp.x
(or blur_mpi.x
) generated in
Compile with:
# OpenMP
OMP_NUM_THREADS=... ./blur_openmp.x [kernel type] [kernel size] {extra parameters} [image.pgm] {output.pgm}
or
# MPI
mpirun -np ... ./blur_mpi.x [kernel type] [kernel size] {extra parameters} [image.pgm] {output.pgm}
or
# Hybrid (MPI + OpenMP)
OMP_NUM_THREADS=... mpirun -np ... ./blur_hybrid.x [kernel type] [kernel size] {extra parameters} [image.pgm] {output.pgm}
where:
kernel type
: type of kernels, available onesMean
,Weight
,Gaussian
kernel size
: size of the kernel, only odd kernelsextra parameters
(optional): extra parameters for kernel, more on Kernelimage.pgm
: input image to convolveoutput.pgm
(optional): output where to write the image, defaultblurred.pgm
if interested, there is a test picture in test
folder. Also a program that checks the percentage of not correct pixel in the blurred image is reported (useful to test that serial and parallel implementation agree).
Note: We assume that your are compiling using g++ for serial and OpenMP implementation, while mpic++ for MPI and hybrid. Tests on MPI and hybrid are done using openmpi-4.1.1 and gnu-9.3.0