What is a convolution?
The mathematical formulation of a convolution is as follow:$$(f*g)(t)=\int_{-\infty}^{\infty}f(x)g(t-x)dx$$
But, what this formula means? what it does?, let see an example. Let define f(x) as:
$$ f(x)=\left\{\begin{matrix} 1, & |x| \leqslant 1 \\ 0, & otherwise \end{matrix}\right.; $$
And let define g(x) as:
$$ f(x)=\left\{\begin{matrix} 1 - |x|, & |x| \leqslant 1 \\ 0, & otherwise \end{matrix}\right.; $$
The convolution of f(x) and g(x) will define a new function in which the values of (f * g)(t) will be the area enclosed by f(x) and g(x) in the instant t.
Since an image can be treated as a function of two variables that returns the color of a pixel relative to its coordinates, we can extend the mathematical definition of the convolution to the following formula:
$$(f*g)(u, v)=\int_{-\infty}^{\infty}\int_{-\infty}^{\infty}f(x, y)g(u - x, v - y)dxdy$$
How convolution formula is applied to an image?
An image can be treated as a discrete function, so we will use the discrete form of the last formula:
$$ pixel(x,y)=\sum_{j=0}^{kh}\sum_{i=0}^{kw}inImage\left(x+i-\frac{kw-1}{2},y+j-\frac{kh-1}{2}\right)kernel(i,j) $$
inImage is the image in which we will apply the convolution over every pixel, kernel is a kw * kh array values, the convolution is applied to a group of pixels in which the center of the kernel y aligned to the coordinates of the pixel that is begin processed. Graphically:
The pseudo-algorithm for convolution is:
for (y = 0; y < height; y++)
for (x = 0; x < width; x++) {
pixel = 0;
// Apply convolution
for (j = 0; j < kh; j++)
for (i = 0; i < kw; i++)
pixel += inImage(x + i - (kw - 1) / 2, y + j - (kh - 1) / 2) * kernel(i, j);
outImage(x, y, pixel);
}
The source code
At this point there is not much to explain, just apply the algorithm above. This is the source code for the project file in Qt Creator:
QT += core gui TARGET = convolution CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp
And this is the source code for main.cpp:
#include <QCoreApplication>
#include <QImage>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Q_UNUSED(a)
QImage inImage("lena.png");
inImage = inImage.convertToFormat(QImage::Format_RGB32);
QImage outImage(inImage.size(), inImage.format());
int kw = 3;
int kh = 3;
qreal kernel[] = {0, 0, 0,
0, 1, 0,
0, 0, 0};
int offsetX = (kw - 1) / 2;
int offsetY = (kh - 1) / 2;
for (int y = 0; y < inImage.height(); y++) {
QRgb *outLine = (QRgb *) outImage.scanLine(y);
for (int x = 0; x < inImage.width(); x++) {
qreal pixelR = 0;
qreal pixelG = 0;
qreal pixelB = 0;
// Apply convolution to each channel.
for (int j = 0; j < kh; j++) {
if (y + j < offsetY
|| y + j - offsetY >= inImage.height())
continue;
const QRgb *inLine = (QRgb *) inImage.constScanLine(y + j - offsetY);
for (int i = 0; i < kw; i++) {
if (x + i < offsetX
|| x + i - offsetX >= inImage.width())
continue;
qreal k = kernel[i + j * kw];
QRgb pixel = inLine[x + i - offsetX];
pixelR += k * qRed(pixel);
pixelG += k * qGreen(pixel);
pixelB += k * qBlue(pixel);
}
}
quint8 r = qBound(0., pixelR, 255.);
quint8 g = qBound(0., pixelG, 255.);
quint8 b = qBound(0., pixelB, 255.);
outLine[x] = qRgb(r, g, b);
}
}
outImage.save("out.png");
return EXIT_SUCCESS;
}
Some examples
You can try giving different size and values to kernel and see what results you get. Here are some examples:
// Identity kernel (original image)
int kw = 3;
int kh = 3;
qreal kernel[] = {0, 0, 0,
0, 1, 0,
0, 0, 0};
// Edge detection
int kw = 3;
int kh = 3;
qreal kernel[] = {-1, -1, -1,
-1, 8, -1,
-1, -1, -1};
// Sharppening
int kw = 3;
int kh = 3;
qreal kernel[] = {-1, -1, -1,
-1, 9, -1,
-1, -1, -1};
// Brightness
int kw = 3;
int kh = 3;
qreal kernel[] = {0, 0, 0,
0, 2, 0,
0, 0, 0};
// Blur
int kw = 7;
int kh = 7;
qreal kernel[] = {1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49,
1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49,
1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49,
1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49,
1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49,
1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49,
1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49, 1. / 49};
// Gaussian Blur (Denoise)
int kw = 5;
int kh = 5;
qreal kernel[] = {1 / 273., 4 / 273., 7 / 273., 4 / 273., 1 / 273.,
4 / 273., 16 / 273., 26 / 273., 16 / 273., 4 / 273.,
7 / 273., 26 / 273., 41 / 273., 26 / 273., 7 / 273.,
4 / 273., 16 / 273., 26 / 273., 16 / 273., 4 / 273.,
1 / 273., 4 / 273., 7 / 273., 4 / 273., 1 / 273.};







Good information . Thanks alot
ReplyDelete