2
votes

I'm trying to implement a simple Gaussian blur in android and it works pretty slow :( here's the relevant code:

double rSum = 0;
double gSum = 0;
double bSum = 0;
double weightSum = 0;

for(int y = 0; y < originalImage.height ; y++){
    for(int x = 0; x < originalImage.width ; x++){
        int newPixel;

        rSum = 0;
        gSum = 0;
        bSum = 0;
        weightSum = 1;

        for(int row = y-FRAME_OFFSET ; row <= y+FRAME_OFFSET ; row++){
            for(int col = x-FRAME_OFFSET ; col <= x+FRAME_OFFSET ; col++){
                if(originalImage.inBounds(col, row)){
                    double weight = weights[(x-col)*(x-col) + (y-row)*(y-row)];
                    weightSum += weight;

                    int pixel = originalImage.at(col, row);

                    int red =  (pixel >> 16) & 0xFF ;
                    int green = (pixel >> 8) & 0xFF ;
                    int blue = pixel & 0xFF ;

                    rSum += red * weight;
                    gSum += green * weight;
                    bSum += blue * weight;  

                }
            }
        }

        rSum /= weightSum;
        gSum /= weightSum;
        bSum /= weightSum;

        newPixel = Color.rgb((int)rSum, (int)gSum, (int)bSum);                  

        maskedImage.set(x, y, newPixel);
    }
}

If i use this algorithm with frame FRAME_OFFSET (radius) of 15 it takes about 3 minutes(!) on a 512x512 image, and it get worst as i increase the offset. My guess is that it's a caching problem as when i calculate the new pixel i'm accessing pixels in different rows that's probably not in the cache.

Any help/improvements will be appreciated.

Please note that i need to implement this algorithm by myself and not use an existing implementation.

Thanks.

2
Have you tried using traceview to find the bottleneck?Kevin Coppock
"i need to implement this algorithm by myself and not use an existing implementation". Are we helping with homework?Krylez
You are applying a filter of size k to an image of size NxM by computing for every output pixel the k by k weighted sum of the input. I.e., for every output pixel you are reading the k by k area around it. So your algorithm takes order(kkNM) time. A gaussian filter can be computed in just two passes through the input data (one horizontal and one vertical). Not enough space here to describe it, but read about how to implement a convolution kernel. Once you get the computation correct, your algorithm will take order(2*NM) time.Yojimbo
Check out this link, especially the comments about a separable filter: jhlabs.com/ip/blurring.htmlYojimbo
Here is another usefull link: lotsacode.wordpress.com/2010/12/08/…jnovacho

2 Answers

1
votes

The 2D Gaussian blur kernel is linearly separable, meaning that it can be expressed as the outer (column-times-row) product of two 1D kernels, one for the image rows and one for the columns. So any straightforward implementation that uses this property should be O(kMN) for an MN image and a kk kernel, and be implemented as a two pass algorithm, with each pass performing a 1D convolution, first along the image rows, then along the image columns.

In some cases the 1D step can be speeded up by taking advantage of certain properties of the Gaussian kernel itself, that allow the computation of the weight coefficients to be done incrementally using integer operations only. This is the fastest implementation that I am aware of - and it can be easily ported to Java on Android: Incremental Computation of the Gaussian.

0
votes

Use this lib: https://github.com/jrvansuita/GaussianBlur

Implement like this:

//Asynchronous with scaleDown and changing radius
GaussianBlur.with(context).size(300).radius(10).put(R.mipmap.your_image, imageView);