Fast Bitmap Blur For Android SDK

150,298

Solution 1

This is a shot in the dark, but you might try shrinking the image and then enlarging it again. This can be done with Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Make sure and set the filter parameter to true. It'll run in native code so it might be faster.

Solution 2

For future Googlers, here is an algorithm that I ported from Quasimondo. It's kind of a mix between a box blur and a gaussian blur, it's very pretty and quite fast too.

Update for people encountering the ArrayIndexOutOfBoundsException problem : @anthonycr in the comments provides this information :

I found that by replacing Math.abs with StrictMath.abs or some other abs implementation, the crash does not occur.

/**
 * Stack Blur v1.0 from
 * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
 * Java Author: Mario Klingemann <mario at quasimondo.com>
 * http://incubator.quasimondo.com
 *
 * created Feburary 29, 2004
 * Android port : Yahel Bouaziz <yahel at kayenko.com>
 * http://www.kayenko.com
 * ported april 5th, 2012
 *
 * This is a compromise between Gaussian Blur and Box blur
 * It creates much better looking blurs than Box Blur, but is
 * 7x faster than my Gaussian Blur implementation.
 *
 * I called it Stack Blur because this describes best how this
 * filter works internally: it creates a kind of moving stack
 * of colors whilst scanning through the image. Thereby it
 * just has to add one new block of color to the right side
 * of the stack and remove the leftmost color. The remaining
 * colors on the topmost layer of the stack are either added on
 * or reduced by one, depending on if they are on the right or
 * on the left side of the stack.
 *  
 * If you are using this algorithm in your code please add
 * the following line:
 * Stack Blur Algorithm by Mario Klingemann <[email protected]>
 */

public Bitmap fastblur(Bitmap sentBitmap, float scale, int radius) {

    int width = Math.round(sentBitmap.getWidth() * scale);
    int height = Math.round(sentBitmap.getHeight() * scale);
    sentBitmap = Bitmap.createScaledBitmap(sentBitmap, width, height, false);

    Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);

    if (radius < 1) {
        return (null);
    }

    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    int[] pix = new int[w * h];
    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.getPixels(pix, 0, w, 0, 0, w, h);

    int wm = w - 1;
    int hm = h - 1;
    int wh = w * h;
    int div = radius + radius + 1;

    int r[] = new int[wh];
    int g[] = new int[wh];
    int b[] = new int[wh];
    int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
    int vmin[] = new int[Math.max(w, h)];

    int divsum = (div + 1) >> 1;
    divsum *= divsum;
    int dv[] = new int[256 * divsum];
    for (i = 0; i < 256 * divsum; i++) {
        dv[i] = (i / divsum);
    }

    yw = yi = 0;

    int[][] stack = new int[div][3];
    int stackpointer;
    int stackstart;
    int[] sir;
    int rbs;
    int r1 = radius + 1;
    int routsum, goutsum, boutsum;
    int rinsum, ginsum, binsum;

    for (y = 0; y < h; y++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        for (i = -radius; i <= radius; i++) {
            p = pix[yi + Math.min(wm, Math.max(i, 0))];
            sir = stack[i + radius];
            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            rbs = r1 - Math.abs(i);
            rsum += sir[0] * rbs;
            gsum += sir[1] * rbs;
            bsum += sir[2] * rbs;
            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }
        }
        stackpointer = radius;

        for (x = 0; x < w; x++) {

            r[yi] = dv[rsum];
            g[yi] = dv[gsum];
            b[yi] = dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (y == 0) {
                vmin[x] = Math.min(x + radius + 1, wm);
            }
            p = pix[yw + vmin[x]];

            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[(stackpointer) % div];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];

            yi++;
        }
        yw += w;
    }
    for (x = 0; x < w; x++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        yp = -radius * w;
        for (i = -radius; i <= radius; i++) {
            yi = Math.max(0, yp) + x;

            sir = stack[i + radius];

            sir[0] = r[yi];
            sir[1] = g[yi];
            sir[2] = b[yi];

            rbs = r1 - Math.abs(i);

            rsum += r[yi] * rbs;
            gsum += g[yi] * rbs;
            bsum += b[yi] * rbs;

            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }

            if (i < hm) {
                yp += w;
            }
        }
        yi = x;
        stackpointer = radius;
        for (y = 0; y < h; y++) {
            // Preserve alpha channel: ( 0xff000000 & pix[yi] )
            pix[yi] = ( 0xff000000 & pix[yi] ) | ( dv[rsum] << 16 ) | ( dv[gsum] << 8 ) | dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (x == 0) {
                vmin[y] = Math.min(y + r1, hm) * w;
            }
            p = x + vmin[y];

            sir[0] = r[p];
            sir[1] = g[p];
            sir[2] = b[p];

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[stackpointer];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];

            yi += w;
        }
    }

    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.setPixels(pix, 0, w, 0, 0, w, h);

    return (bitmap);
}

Solution 3

Android Blur Guide 2016

with Showcase/Benchmark App and Source on Github. Also check out the Blur framework I'm currently working on: Dali.

After experimenting a lot I can now safely give you some solid recommendations that will make your life easier in Android when using the Android Framework.

Load and Use a downscaled Bitmap (for very blurry images)

Never use a the full size of a Bitmap. The bigger the image the more needs to be blurred and also the higher the blur radius needs to be and usually, the higher the blur radius the longer the algorithm takes.

final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap blurTemplate = BitmapFactory.decodeResource(getResources(), R.drawable.myImage, options);

This will load the bitmap with inSampleSize 8, so only 1/64 of the original image. Test what inSampleSize suits your needs, but keep it 2^n (2,4,8,...) to avoid degrading quality due to scaling. See Google doc for more

Another really big advantage is that bitmap loading will be really fast. In my early blur testing I figured that the longest time during the whole blur process was the image loading. So to load a 1920x1080 image from disk my Nexus 5 needed 500ms while the blurring only took another 250 ms or so.

Use Renderscript

Renderscript provides ScriptIntrinsicBlur which is a Gaussian blur filter. It has good visual quality and is just the fastest you realistically get on Android. Google claims to be "typically 2-3x faster than a multithreaded C implementation and often 10x+ faster than a Java implementation". Renderscript is really sophisticated (using the fastest processing device (GPU, ISP, etc.), etc.) and there is also the v8 support library for it making it compatible down to 2.2. Well at least in theory, through my own tests and reports from other devs it seems that it is not possible to use Renderscript blindly, since the hardware/driver fragmentation seems to cause problems with some devices, even with higher sdk lvl (e.g. I had troubles with the 4.1 Nexus S) so be careful and test on a lot of devices. Here's a simple example that will get you started:

//define this only once if blurring multiple times
RenderScript rs = RenderScript.create(context);

(...)
//this will blur the bitmapOriginal with a radius of 8 and save it in bitmapOriginal
final Allocation input = Allocation.createFromBitmap(rs, bitmapOriginal); //use this constructor for best performance, because it uses USAGE_SHARED mode which reuses memory
final Allocation output = Allocation.createTyped(rs, input.getType());
final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setRadius(8f);
script.setInput(input);
script.forEach(output);
output.copyTo(bitmapOriginal);

When using the v8 support with Gradle, which is specifically recommended by Google "because they include the latest improvements", you only need to add 2 lines to your build script and use android.support.v8.renderscript with current build tools (updated syntax for android Gradle plugin v14+)

android {
    ...
    defaultConfig {
        ...
        renderscriptTargetApi 19
        renderscriptSupportModeEnabled true
    }
}

Simple benchmark on a Nexus 5 - comparing RenderScript with different other java and Renderscript implementations:

The average runtime per blur on different pic sizes The average runtime per blur on different pic sizes

Megapixels per sec that can be blurred Megapixels per sec that can be blurred

Each value is the avg of 250 rounds. RS_GAUSS_FAST is ScriptIntrinsicBlur (and nearly always the fastest), others that start with RS_ are mostly convolve implementations with simple kernels. The details of the algorithms can be found here. This is not purely blurring, since a good portion is garbage collection that is measured. This can be seen in this here (ScriptIntrinsicBlur on a 100x100 image with about 500 rounds)

enter image description here

The spikes are gc.

You can check for yourself, the benchmark app is in the playstore: BlurBenchmark

Reuses Bitmap wherever possible (if prio: performance > memory footprint)

If you need multiple blurs for a live blur or similar and your memory allows it do not load the bitmap from drawables multiple times, but keep it "cached" in a member variable. In this case always try to use the same variables, to keep garbage collecting to a minimum.

Also check out the new inBitmap option when loading from a file or drawable which will reuse the bitmap memory and save garbage collection time.

For blending from sharp to blurry

The simple and naive method is just to use 2 ImageViews, one blurred, and alpha fade them. But if you want a more sophisticated look that smoothly fades from sharp to blurry, then check out Roman Nurik's post about how to do it like in his Muzei app.

Basically he explains that he pre-blurs some frames with different blur extents and uses them as keyframes in an animation that looks really smooth.

Diagram where Nurik exaplains his approach

Solution 4

EDIT (April 2014): This is a question/answer page that still gets a lot of hits it seems. I know I'm always getting upvotes for this post. But if you're reading this, you need to realize the answers posted here (both mine and the accepted answer) are out of date. If you want to implement efficient blur today, you should use RenderScript instead of the NDK or Java. RenderScript runs on Android 2.2+ (using the Android Support Library), so there's no reason not to use it.

The old answer follows, but beware as it's outdated.


For future² Googlers, here is an algorithm that I ported from Yahel's port of Quasimondo's algorithm, but using the NDK. It's based on Yahel's answer, of course. But this is running native C code, so it's faster. Much faster. Like, 40 times faster.

I find that using the NDK is how all image manipulation should be done on Android... it's somewhat annoying to implement at first (read a great tutorial on using JNI and the NDK here), but much better, and near real time for a lot of things.

For reference, using Yahel's Java function, it took 10 seconds to blur my 480x532 pixels image with a blur radius of 10. But it took 250ms using the native C version. And I'm pretty sure it can still be further optimized... I just did a dumb conversion of the java code, there's probably some manipulations that can be shortened, didn't want to spend too much time refactoring the whole thing.

#include <jni.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <android/log.h>
#include <android/bitmap.h>

#define LOG_TAG "libbitmaputils"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

typedef struct {
    uint8_t red;
    uint8_t green;
    uint8_t blue;
    uint8_t alpha;
} rgba;

JNIEXPORT void JNICALL Java_com_insert_your_package_ClassName_functionToBlur(JNIEnv* env, jobject obj, jobject bitmapIn, jobject bitmapOut, jint radius) {
    LOGI("Blurring bitmap...");

    // Properties
    AndroidBitmapInfo   infoIn;
    void*               pixelsIn;
    AndroidBitmapInfo   infoOut;
    void*               pixelsOut;

    int ret;

    // Get image info
    if ((ret = AndroidBitmap_getInfo(env, bitmapIn, &infoIn)) < 0 || (ret = AndroidBitmap_getInfo(env, bitmapOut, &infoOut)) < 0) {
        LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
        return;
    }

    // Check image
    if (infoIn.format != ANDROID_BITMAP_FORMAT_RGBA_8888 || infoOut.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
        LOGE("Bitmap format is not RGBA_8888!");
        LOGE("==> %d %d", infoIn.format, infoOut.format);
        return;
    }

    // Lock all images
    if ((ret = AndroidBitmap_lockPixels(env, bitmapIn, &pixelsIn)) < 0 || (ret = AndroidBitmap_lockPixels(env, bitmapOut, &pixelsOut)) < 0) {
        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
    }

    int h = infoIn.height;
    int w = infoIn.width;

    LOGI("Image size is: %i %i", w, h);

    rgba* input = (rgba*) pixelsIn;
    rgba* output = (rgba*) pixelsOut;

    int wm = w - 1;
    int hm = h - 1;
    int wh = w * h;
    int whMax = max(w, h);
    int div = radius + radius + 1;

    int r[wh];
    int g[wh];
    int b[wh];
    int rsum, gsum, bsum, x, y, i, yp, yi, yw;
    rgba p;
    int vmin[whMax];

    int divsum = (div + 1) >> 1;
    divsum *= divsum;
    int dv[256 * divsum];
    for (i = 0; i < 256 * divsum; i++) {
        dv[i] = (i / divsum);
    }

    yw = yi = 0;

    int stack[div][3];
    int stackpointer;
    int stackstart;
    int rbs;
    int ir;
    int ip;
    int r1 = radius + 1;
    int routsum, goutsum, boutsum;
    int rinsum, ginsum, binsum;

    for (y = 0; y < h; y++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        for (i = -radius; i <= radius; i++) {
            p = input[yi + min(wm, max(i, 0))];

            ir = i + radius; // same as sir

            stack[ir][0] = p.red;
            stack[ir][1] = p.green;
            stack[ir][2] = p.blue;
            rbs = r1 - abs(i);
            rsum += stack[ir][0] * rbs;
            gsum += stack[ir][1] * rbs;
            bsum += stack[ir][2] * rbs;
            if (i > 0) {
                rinsum += stack[ir][0];
                ginsum += stack[ir][1];
                binsum += stack[ir][2];
            } else {
                routsum += stack[ir][0];
                goutsum += stack[ir][1];
                boutsum += stack[ir][2];
            }
        }
        stackpointer = radius;

        for (x = 0; x < w; x++) {

            r[yi] = dv[rsum];
            g[yi] = dv[gsum];
            b[yi] = dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            ir = stackstart % div; // same as sir

            routsum -= stack[ir][0];
            goutsum -= stack[ir][1];
            boutsum -= stack[ir][2];

            if (y == 0) {
                vmin[x] = min(x + radius + 1, wm);
            }
            p = input[yw + vmin[x]];

            stack[ir][0] = p.red;
            stack[ir][1] = p.green;
            stack[ir][2] = p.blue;

            rinsum += stack[ir][0];
            ginsum += stack[ir][1];
            binsum += stack[ir][2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            ir = (stackpointer) % div; // same as sir

            routsum += stack[ir][0];
            goutsum += stack[ir][1];
            boutsum += stack[ir][2];

            rinsum -= stack[ir][0];
            ginsum -= stack[ir][1];
            binsum -= stack[ir][2];

            yi++;
        }
        yw += w;
    }
    for (x = 0; x < w; x++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        yp = -radius * w;
        for (i = -radius; i <= radius; i++) {
            yi = max(0, yp) + x;

            ir = i + radius; // same as sir

            stack[ir][0] = r[yi];
            stack[ir][1] = g[yi];
            stack[ir][2] = b[yi];

            rbs = r1 - abs(i);

            rsum += r[yi] * rbs;
            gsum += g[yi] * rbs;
            bsum += b[yi] * rbs;

            if (i > 0) {
                rinsum += stack[ir][0];
                ginsum += stack[ir][1];
                binsum += stack[ir][2];
            } else {
                routsum += stack[ir][0];
                goutsum += stack[ir][1];
                boutsum += stack[ir][2];
            }

            if (i < hm) {
                yp += w;
            }
        }
        yi = x;
        stackpointer = radius;
        for (y = 0; y < h; y++) {
            output[yi].red = dv[rsum];
            output[yi].green = dv[gsum];
            output[yi].blue = dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            ir = stackstart % div; // same as sir

            routsum -= stack[ir][0];
            goutsum -= stack[ir][1];
            boutsum -= stack[ir][2];

            if (x == 0) vmin[y] = min(y + r1, hm) * w;
            ip = x + vmin[y];

            stack[ir][0] = r[ip];
            stack[ir][1] = g[ip];
            stack[ir][2] = b[ip];

            rinsum += stack[ir][0];
            ginsum += stack[ir][1];
            binsum += stack[ir][2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            ir = stackpointer; // same as sir

            routsum += stack[ir][0];
            goutsum += stack[ir][1];
            boutsum += stack[ir][2];

            rinsum -= stack[ir][0];
            ginsum -= stack[ir][1];
            binsum -= stack[ir][2];

            yi += w;
        }
    }

    // Unlocks everything
    AndroidBitmap_unlockPixels(env, bitmapIn);
    AndroidBitmap_unlockPixels(env, bitmapOut);

    LOGI ("Bitmap blurred.");
}

int min(int a, int b) {
    return a > b ? b : a;
}

int max(int a, int b) {
    return a > b ? a : b;
}

Then use it like this (considering a class called com.insert.your.package.ClassName and a native function called functionToBlur, as the code above states):

// Create a copy
Bitmap bitmapOut = bitmapIn.copy(Bitmap.Config.ARGB_8888, true);
// Blur the copy
functionToBlur(bitmapIn, bitmapOut, __radius);

It expects a RGB_8888 bitmap!

To use a RGB_565 bitmap, either create a converted copy before passing the parameter (yuck), or change the function to use a new rgb565 type instead of rgba:

typedef struct {
    uint16_t byte0;
} rgb565;

The problem is that if you do that you can't read .red, .green and .blue of the pixel anymore, you need to read the byte properly, duh. When I needed that before, I did this:

r = (pixels[x].byte0 & 0xF800) >> 8;
g = (pixels[x].byte0 & 0x07E0) >> 3;
b = (pixels[x].byte0 & 0x001F) << 3;

But there's probably some less dumb way of doing it. I'm not much of a low-level C coder, I'm afraid.

Solution 5

This code is work perfect for me

Bitmap tempbg = BitmapFactory.decodeResource(getResources(),R.drawable.b1); //Load a background.
Bitmap final_Bitmap = BlurImage(tempbg);


@SuppressLint("NewApi")
Bitmap BlurImage (Bitmap input)
{
    try
    {
    RenderScript  rsScript = RenderScript.create(getApplicationContext());
    Allocation alloc = Allocation.createFromBitmap(rsScript, input);

    ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rsScript,   Element.U8_4(rsScript));
    blur.setRadius(21);
    blur.setInput(alloc);

    Bitmap result = Bitmap.createBitmap(input.getWidth(), input.getHeight(), Bitmap.Config.ARGB_8888);
    Allocation outAlloc = Allocation.createFromBitmap(rsScript, result);

    blur.forEach(outAlloc);
    outAlloc.copyTo(result);

    rsScript.destroy();
    return result;
    }
    catch (Exception e) {
        // TODO: handle exception
        return input;
    }

}
Share:
150,298

Related videos on Youtube

Greg
Author by

Greg

Updated on February 10, 2022

Comments

  • Greg
    Greg over 2 years

    Currently in an Android application that I'm developing I'm looping through the pixels of an image to blur it. This takes about 30 seconds on a 640x480 image.

    While browsing apps in the Android Market I came across one that includes a blur feature and their blur is very fast (like 5 seconds) so they must be using a different method of blurring.

    Anyone know a faster way other than looping through the pixels?

    • Greg
      Greg over 14 years
      Unfortunately the images will always be different so I won't be able create a blurred version ahead of time. Plus I won't know the blur intensity ahead of time either.
    • vickirk
      vickirk over 14 years
      Could you post your code, it maybe it is the algorithm/code that is inefficient, 30 secs to go through an 640x480 image is slow, I'd have thought 5 secs was slow to but then again depends on the processor.
  • Greg
    Greg over 14 years
    After some testing and the blurring that I'm doing this actual works well enough for me and it's fast. Thanks!
  • vickirk
    vickirk over 14 years
    If it works good. It's a shame we never got to the bottom of why it was inefficient.
  • Casebash
    Casebash about 14 years
    You might want to try createScaledBitmap and leaving the image the same size. It is blurring it for me :-(
  • Dmitry Zaytsev
    Dmitry Zaytsev almost 12 years
    BUT it requires a lot of memory. To reduce memory consumption change type of r[wh], g[wh] and b[wh] to uint8_t.
  • CQM
    CQM over 11 years
    can you show me what your Android.mk file looks like, on pastebin.com
  • zeh
    zeh over 11 years
    @CQM: Sure: pastebin.com/8VMTZy85 Consider, though, that using RenderScript is probably an easier, more modern, and potentially faster solution to this problem (only available on Android 4+, though).
  • CQM
    CQM over 11 years
    @zeh, I implemented this last evening. It is fast enough, but what values should I pass in for Radius, I put in "5" arbitrarily, but I don't understand the consequences (time, vs aesthetic of blur), should this be a dynamic variable based on resolution ?
  • zeh
    zeh over 11 years
    @CQM: It should be whatever you want to blur it with. The higher the number, the more blurred it is. I'm also using an arbitrary value of 14, but that's because I have static source images that always have the same size. Depending on what you need and how you present it, you may need to take the device resolution or image size into account. I haven't measured the time impact of different values for radius. Because this is a box blur, I'm assuming there's some degradation of the effect if the radius is too high, but I haven't analyzed the algorithm enough to be sure.
  • Yog Guru
    Yog Guru about 11 years
    Thanks Yahel. You Solved my problem. Thanks Again.
  • Zakharov Roman
    Zakharov Roman almost 11 years
    @zeh, can you tell please how to replace output[yi].red = dv[rsum]; output[yi].green = dv[gsum]; output[yi].blue = dv[bsum]; for RGB_565 images?
  • Chris.Jenkins
    Chris.Jenkins almost 11 years
    @zeh do you have a renderscript example?
  • zeh
    zeh almost 11 years
    @Chris.Jenkins: I don't, I just know it's supposed to do that based on the documentation.
  • Sileria
    Sileria almost 11 years
    This take half the time than blurring using ConvolutionMatrix.
  • Martin Marconcini
    Martin Marconcini almost 11 years
    For a working RenderScript example of a Gaussian Blur on Android SDK 17+ look here: stackoverflow.com/questions/14988990/android-fast-bitmap-blu‌​r
  • zeh
    zeh over 10 years
    RenderScript is also available as part of the support lib for Android 2.2+, so there's no reason not to use it everywhere anymore: android-developers.blogspot.com/2013/09/…
  • xiaowoo
    xiaowoo over 10 years
    this is fast but for some reason, i couldn't get it to run on a AsyncTask's doInBackground. When I run it on doInBackground, it would always fail
  • user1364368
    user1364368 over 10 years
    Here is a discussion on the meaning of the argument "filter": stackoverflow.com/questions/2895065
  • krisDrOid
    krisDrOid over 10 years
    what should i pass as radius?
  • Yahel
    Yahel over 10 years
    Long time since I posted this, but from what I recall, the bigger the radius, the blurrier the final image. The algorithm mixes the pixels in a circle "radius" wide. The final color of the pixel being modified is the average of all the pixel in the "radius" circle.
  • MikeL
    MikeL over 10 years
    For radius larger than 1 sometimes you get ArrayIndexOutOfBoundsException. I will try to identify the problem.
  • seb
    seb over 10 years
    Is there some example project, which utilizes RenderScript with the support lib?
  • Patrick
    Patrick about 10 years
    The Renderscript context should not be created in the blur method, but managed statically or given to the method. (if you mind performance)
  • Amal Murali
    Amal Murali about 10 years
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes.
  • Yura Shinkarev
    Yura Shinkarev about 10 years
    Amal Murali, you are right. Now change my post. Good, that you not only downvote, but also comment.
  • Donal Rafferty
    Donal Rafferty about 10 years
    Can you give an example of this is use? When I try to use it I get the following error: java.lang.IllegalArgumentException: width and height must be > 0
  • Patrick
    Patrick about 10 years
    This is not exactly the way to got because of 2 reasons: 1) it needs the memory of the full image altough you are probably only using a downscaled one 2) you need to load the full image wich is slower - use loading with inSampleSize and BitmapFactory.decodeResource(), which is far superior solution to this.
  • Some Noob Student
    Some Noob Student about 10 years
    First of all, thanks for your hard work! But I got a question: "because it uses USAGE_SHARED mode which reuses memory". Where did you find the constant USAGE_SHARED? I couldn't find it anywhere.
  • Some Noob Student
    Some Noob Student about 10 years
    I found it, USAGE_SHARED is only available in support.v8.renderscript
  • WitaloBenicio
    WitaloBenicio about 10 years
    Man, this deserve to be SO UPVOTED that if i could, i would upvote a thousand times. :D
  • Patrick
    Patrick about 10 years
    @SomeNoobStudent that's not true, USAGE_SHARED was added in API lvl 18, so your target is probably lower developer.android.com/reference/android/renderscript/…
  • Some Noob Student
    Some Noob Student about 10 years
    @for3st: I see, thank you again. Have you compared the memory/heap pressure between different methods? I hear scriptintrinsicblur can take up a lot of heap space...
  • Patrick
    Patrick about 10 years
    @SomeNoobStudent no, the tip is from json sams from the google renderscript team, I did not verify. Im not REALLY sure if renderscript memory allocations are on the heap, because it gets compiled to C afaik which would not be restricted/using heap memory.
  • Theo
    Theo almost 10 years
    Renderscript fast Gaussian blur fails with C memory allocation errors on low end devices. Tested on ZTE Z992 (Android 4.1.1) and Kyocera Rise (Android 4.0.4) using the provided Play Store app. Also had a failure report on Samsung Galaxy S3 mini. Since errors occur in the C layer, they cannot be trapped as exceptions in Java, meaning an app crash is unavoidable. Looks like RenderScript may not ready for production use.
  • Theo
    Theo almost 10 years
    Also, regular Gaussian fast blur fails on Nexus 5 (Android 4.4.4) with IllegalStateException when using the code or the Play Store test app.
  • MrMaffen
    MrMaffen almost 10 years
    Best answer I've read on stackoverflow so far! Awesome job man :)
  • see2851
    see2851 over 9 years
    @MichaelLiberman I also encountered the same problem.Found out why yet? "g[yi] = dv[gsum];" -->error :java.lang.ArrayIndexOutOfBoundsException: length=112896; index=114021
  • seb
    seb over 9 years
    for newer gradle versions, use renderscriptSupportModeEnabled true or it will not build! I searched for ever!
  • Patrick
    Patrick over 9 years
    thx @seb ; the syntax change is for android gradle plugin v0.14+; Ive updated the answer
  • Sileria
    Sileria over 9 years
    @MichaelLiberman Did you ever identify the ArrayIndexOutOfBoundsException problem? We are having the same problem.
  • MikeL
    MikeL over 9 years
    @Mobistry Sorry, I do not remember how I fixed it. If I will find the old code I will post the solution here.
  • Yahel
    Yahel about 9 years
    @OlcayErtaş : That must be one big bitmap you are using :)
  • HaloMediaz
    HaloMediaz almost 9 years
    When I tried this solution, rather than getting a blurred bitmap, I got a rainbow colored bitmap. Anyone else experience this problem? If so how did you fix it?
  • portfoliobuilder
    portfoliobuilder almost 9 years
    Note that this does not work if your app is not fullscreen, because the top toolbar is not taken into consideration. Once you set as a background you will see that the blue effects are off.
  • JMRboosties
    JMRboosties almost 9 years
    Just started getting the same thing as @HaloMediaz
  • radu122
    radu122 almost 9 years
    Optimize the function by scaling the bitmap first: int width = Math.round(sentBitmap.getWidth() * scale); int height = Math.round(sentBitmap.getHeight() * scale); sentBitmap = Bitmap.createScaledBitmap(sentBitmap, width, height, false); .Example of scale value: 0.2f. Radius needed is also smaller, so it is much much faster.
  • Stephen Hines
    Stephen Hines almost 9 years
    This would be even better/faster if you cached the RenderScript object that you created. Instantiating a new one every time you want to blur an image just adds unnecessary overhead (Java object creation/destruction).
  • Ian Newson
    Ian Newson over 8 years
    Thanks. I ported to Xamarin with no trouble.
  • AsafK
    AsafK over 8 years
    Im also having an issue with RenderScript on some old armeabi devices, but your solution is too memory consuming.
  • VAdaihiep
    VAdaihiep over 8 years
    Simplest way to blurring image (y)
  • android developer
    android developer over 8 years
    important to know: sentBitmap.getConfig() might return null, so you should have a fallback for it. For example, create the default config. Also, is it possible to have the exact same algorithm on Renderscript, to make it faster?
  • android developer
    android developer over 8 years
    This is faster than using on Java, but according to what I've noticed, it has relatively bad quality result, compared to the "fastBlur" solution of the other post (here: stackoverflow.com/a/10028267/878126 ) . is it possible to convert their solution to RS?
  • karthik kolanji
    karthik kolanji over 8 years
    The idea of compressing the image to low quality and then blurring did the trick :)
  • Gabriel Ferreira
    Gabriel Ferreira almost 8 years
    still with 'index out of range' in devices >hdpi as the original source
  • Janneman96
    Janneman96 almost 8 years
    What are recommended parameters for the scale and radius?
  • Yahel
    Yahel almost 8 years
    @Janneman96 : It depends what effect you want to achieve. The higher the scale and the radius, the blurrier it gets. Also not that the higher the radius, the more cpu consuming the algorithm gets.
  • Janneman96
    Janneman96 almost 8 years
    @Yahel 21 I ended up using (0.2f, 10)
  • MikeL
    MikeL about 7 years
    @NigamPatro, Regretfully I didn't, as it was a side project that I am no longer working on. Is there a reason you don't want to use RenderScript now?
  • Nigam Patro
    Nigam Patro about 7 years
    @MikeL Renderscript is not working fine in all devices. Some devices its crashing. Till now I also not worked, but got to know from some other developers.
  • the_dude_abides
    the_dude_abides almost 7 years
    @OlcayErtaş and anyone else getting OOM error: add android:largeHeap="true" to your <application> in AndroidManifest.xml. I was experiencing this on various devices too. Now it's fixed!
  • KoreanDude
    KoreanDude over 6 years
    Workaround is just checking the array length. if (rsum >= dv.length) { Log.e(TAG, "out of bound rsum: " + rsum); } else { r[yi] = dv[rsum]; } if (gsum >= dv.length) { Log.e(TAG, "out of bound gsum: " + gsum); } else { g[yi] = dv[gsum]; } if (bsum >= dv.length) { Log.e(TAG, "out of bound bsum: " + bsum); } else { b[yi] = dv[bsum]; }
  • KoreanDude
    KoreanDude over 6 years
    Funny thing is when I launched the app in debug mode, blur worked fine. Looked into it and found that bitmap.getPixels() returned different int array (different size, different data order) than when launched in normal mode. After playing with it for a while, I just added a length check and moved on. It all works now.
  • zeus
    zeus over 6 years
    no if you scale down the image to scale up later you will retrieve a blocky image instead of a blured image :(
  • A. Kazarovets
    A. Kazarovets about 5 years
    Does anybody know why are int arrays used here? We can definetely use less memory if we instead use signed bytes or at least short for r, g and b arrays, maybe for others as well.
  • anthonycr
    anthonycr about 5 years
    Ran into the known ArrayIndexOutOfBoundsException, and after some analysis, I believe it is caused by an incorrect optimization by the Dalvik VM. In the for loop immediately before the bad dereference, either the calculation of the rbs variable or the calculation of the gsum, rsum, or bsum variables are not being done right. I found that by replacing Math.abs with StrictMath.abs or some other abs implementation, the crash does not occur. Since StrictMath.abs itself delegates to Math.abs, it seems like it must be a bad optimization.
  • Miha_x64
    Miha_x64 over 3 years
    Thanks! Optimized for less memory consumption and no copying: gist.github.com/Miha-x64/3fb489d13dbf69e1611a8fb688b57d3d
  • CoolMind
    CoolMind over 2 years
    It works for ImageView, but not for complex layouts with TextView (text won't be blurred). I will turn to RenderScript version.
  • CoolMind
    CoolMind over 2 years
    Thanks! Don't forget to add RenderScript in build.gradle. This method requires Bitmap, so first copy bitmap from a view, then call BlurImage(bitmap).
  • CoolMind
    CoolMind over 2 years
    @DonalRafferty, this is because in onCreate views have not been measured yet. We should do this after a short delay.
  • CoolMind
    CoolMind over 2 years
    It is c copy of b_yng solution.