PDA

View Full Version : Convolution filter



toutarrive
20th October 2009, 18:43
Hello,

I need to implement an edge dectection filter on an image.

i've managed to put all the Qrgb colors of the image in a table (matrice_pixel[height] [width] ).

My filter is kernel[3][3] .

But i can't get it work properly when i mutiply the two tables.

Any idea?


QRgb **matrice_pixel = new QRgb*[height];
for (int i = 0; i <height; i++){
matrice_pixel[i] = new QRgb[width];
}

/// Getting the colors
for ( int i=0; i<height; i++ ) {
uchar *p = source.scanLine(i);
for (int j = 0; j< width;j ++) {
int blue = int(*p++);
int green = int(*p++);
int red = int(*p++);
int alpha = int(*p++);
QRgb rgbValue = qRgba(red,green,blue, alpha);
matrice_pixel[i][j] = rgbValue;
}

for (int x = 1; x<width ;++x)
{
for (int y= 1; y< height; ++y)
{
int red = 0, green = 0, blue = 0;
//multiply every value of the filter with corresponding image pixel
for(int kernelX = 0; kernelX < kernelWidth; kernelX++)
{
for(int kernelY = 0; kernelY < kernelHeight; kernelY++){

red += qRed(matrice_pixel[x][ y] ) * kernel[kernelX][kernelY];
green += qGreen(matrice_pixel[x][y]) * kernel[kernelX][kernelY];
blue += qBlue(matrice_pixel[x] [ y]) * kernel[kernelX][kernelY];
}
//truncate values smaller than zero and larger than 255
red = qMin(qMax(int(factor * red + bias), 0), 255);
green= qMin(qMax(int(factor * green + bias), 0), 255);
blue= qMin(qMax(int(factor * blue + bias), 0), 255);
QRgb rgbValue = qRgb(red,green,blue);
img.setPixel(x,y, rgbValue);

}
}
}

john_god
20th October 2009, 20:35
But i can't get it work properly when i mutiply the two tables.

I am assuming that it compiles fine without errors, so its a logical error. You are the autor so you have to have the patiente and the trouble to debug your aplication, because you are the one who knows what your program its supose to do. Debugging isnt fun but that the way it goes. :cool:

wysota
20th October 2009, 23:06
According to me your algorithm is simply incorrect. To apply a convolution filter you need to consider not only the current pixel (x,y) but also its neighbours.

Convolution for a single pixel should look more or less like this:

QRgb convolute(const QList<int> &kernel, const QImage &image, int x, int y){
// assuming kernel is 3x3
int total = 0;
int red = qRed(image.pixel(x,y));
int green = qGreen(image.pixel(x,y));
int blue = qBlue(image.pixel(x,y));
for(int r = -1 ; r<=1; ++r){
for(int c = -1; c<=1; ++c){
int kerVal = kernel.at((1+r)*3+(1+c));
total+=kerVal;
red += qRed(image.pixel(x+c, y+r));
green += qGreen(image.pixel(x+c, y+r));
blue += qBlue(image.pixel(x+c, y+r));
}
}
return QRgb(qBound(0, red/total, 255), qBound(0, green/total, 255), qBound(0, blue/total, 255));
}

Then you have to use the above method (provided it's correct) to get values for all pixels of the image (watch out for borders as the above function doesn't handle them) and then apply them on the destination image (you can't modify the original image "in place").

Edit: Here is something that actually seems to work.


#include <QtGui>

QRgb convolute(const QList<int> &kernel, const QImage &image, int x, int y){
int kernelsize = sqrt(kernel.size());
qreal total = 0;
qreal red = 0;
qreal green = 0;
qreal blue = 0;
for(int r = -kernelsize/2 ; r<=kernelsize/2; ++r){
for(int c = -kernelsize/2; c<=kernelsize/2; ++c){
int kerVal = kernel.at((kernelsize/2+r)*kernelsize+(kernelsize/2+c));
total+=kerVal;
red += qRed(image.pixel(x+c, y+r))*kerVal;
green += qGreen(image.pixel(x+c, y+r))*kerVal;
blue += qBlue(image.pixel(x+c, y+r))*kerVal;
}
}
if(total==0)
return qRgb(qBound(0, qRound(red), 255), qBound(0, qRound(green), 255), qBound(0, qRound(blue), 255));
return qRgb(qBound(0, qRound(red/total), 255), qBound(0, qRound(green/total), 255), qBound(0, qRound(blue/total), 255));
}

int main(int argc, char **argv){
QApplication app(argc, argv);
QImage img(argv[1]);
QImage dest = img;
QList<int> sharpen, blur, laplace;


sharpen << 0 << 0 << 0 << 0 << 0
<< 0 << 0 << -1 << 0 << 0
<< 0 << -1 << 5 << -1 << 0
<< 0 << 0 << -1 << 0 << 0
<< 0 << 0 << 0 << 0 << 0;

blur << 0 << 0 << 1 << 0 << 0
<< 0 << 1 << 3 << 1 << 0
<< 1 << 3 << 7 << 3 << 1
<< 0 << 1 << 3 << 1 << 0
<< 0 << 0 << 1 << 0 << 0;

laplace << -1 << -1 << -1 << -1 << -1
<< -1 << -1 << -1 << -1 << -1
<< -1 << -1 << 24 << -1 << -1
<< -1 << -1 << -1 << -1 << -1
<< -1 << -1 << -1 << -1 << -1;

for(int r=2;r<img.height()-2;r++){
for(int c=2;c<img.width()-2;c++){
dest.setPixel(c, r, convolute(sharpen, img, c, r));
}
}
QLabel label;
label.setPixmap(QPixmap::fromImage(dest));
label.show();
QLabel lab2;
lab2.setPixmap(QPixmap::fromImage(img));
lab2.show();
return app.exec();
}

toutarrive
21st October 2009, 08:03
Hello wysota,

Thanks for your help, i'll try it out right now. And let you know about the process.

toutarrive
21st October 2009, 13:47
It works fine.

The processing speed for 3x3 filters is not so bad but can become an issue for heavy image and 5x5 filter or greater.

I was wandering if it was possible to limit the convolute(...) calls ?

I found this article : http://www.codeproject.com/KB/GDI-plus/csharpfilters.aspx

What do you think of it?

Regards.

wysota
21st October 2009, 13:55
If you want speed then I would do convolutions in GPUs, either through shaders or CUDA/OpenCL.

You have to process every pixel so there is not much to limit.