Fast Bitmap Blur For Android SDK
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
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)
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.
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;
}
}
Related videos on Youtube
Greg
Updated on February 10, 2022Comments
-
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 over 14 yearsUnfortunately 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 over 14 yearsCould 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 over 14 yearsAfter some testing and the blurring that I'm doing this actual works well enough for me and it's fast. Thanks!
-
vickirk over 14 yearsIf it works good. It's a shame we never got to the bottom of why it was inefficient.
-
Casebash about 14 yearsYou might want to try createScaledBitmap and leaving the image the same size. It is blurring it for me :-(
-
Dmitry Zaytsev almost 12 yearsBUT it requires a lot of memory. To reduce memory consumption change type of
r[wh]
,g[wh]
andb[wh]
touint8_t
. -
CQM over 11 yearscan you show me what your Android.mk file looks like, on
pastebin.com
-
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 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 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 about 11 yearsThanks Yahel. You Solved my problem. Thanks Again.
-
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 almost 11 years@zeh do you have a renderscript example?
-
zeh almost 11 years@Chris.Jenkins: I don't, I just know it's supposed to do that based on the documentation.
-
Sileria almost 11 yearsThis take half the time than blurring using ConvolutionMatrix.
-
Martin Marconcini almost 11 yearsFor a working RenderScript example of a Gaussian Blur on Android SDK 17+ look here: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
-
zeh over 10 yearsRenderScript 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 over 10 yearsthis 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 over 10 yearsHere is a discussion on the meaning of the argument "filter": stackoverflow.com/questions/2895065
-
krisDrOid over 10 yearswhat should i pass as radius?
-
Yahel over 10 yearsLong 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 over 10 yearsFor radius larger than 1 sometimes you get ArrayIndexOutOfBoundsException. I will try to identify the problem.
-
seb over 10 yearsIs there some example project, which utilizes RenderScript with the support lib?
-
Patrick about 10 yearsThe Renderscript context should not be created in the blur method, but managed statically or given to the method. (if you mind performance)
-
Amal Murali about 10 yearsWhile 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 about 10 yearsAmal Murali, you are right. Now change my post. Good, that you not only downvote, but also comment.
-
Donal Rafferty about 10 yearsCan 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 about 10 yearsThis 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 about 10 yearsFirst 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 about 10 yearsI found it, USAGE_SHARED is only available in support.v8.renderscript
-
WitaloBenicio about 10 yearsMan, this deserve to be SO UPVOTED that if i could, i would upvote a thousand times. :D
-
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 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 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 almost 10 yearsRenderscript 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 almost 10 yearsAlso, 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 almost 10 yearsBest answer I've read on stackoverflow so far! Awesome job man :)
-
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 over 9 yearsfor newer gradle versions, use
renderscriptSupportModeEnabled true
or it will not build! I searched for ever! -
Patrick over 9 yearsthx @seb ; the syntax change is for android gradle plugin v0.14+; Ive updated the answer
-
Sileria over 9 years@MichaelLiberman Did you ever identify the ArrayIndexOutOfBoundsException problem? We are having the same problem.
-
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 about 9 years@OlcayErtaş : That must be one big bitmap you are using :)
-
HaloMediaz almost 9 yearsWhen 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 almost 9 yearsNote 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 almost 9 yearsJust started getting the same thing as @HaloMediaz
-
radu122 almost 9 yearsOptimize 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 almost 9 yearsThis 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 over 8 yearsThanks. I ported to Xamarin with no trouble.
-
AsafK over 8 yearsIm also having an issue with RenderScript on some old armeabi devices, but your solution is too memory consuming.
-
VAdaihiep over 8 yearsSimplest way to blurring image (y)
-
android developer over 8 yearsimportant 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 over 8 yearsThis 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 over 8 yearsThe idea of compressing the image to low quality and then blurring did the trick :)
-
Gabriel Ferreira almost 8 yearsstill with 'index out of range' in devices >hdpi as the original source
-
Janneman96 almost 8 yearsWhat are recommended parameters for the scale and radius?
-
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 almost 8 years@Yahel 21 I ended up using (0.2f, 10)
-
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 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 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 over 6 yearsWorkaround 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 over 6 yearsFunny 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 over 6 yearsno if you scale down the image to scale up later you will retrieve a blocky image instead of a blured image :(
-
A. Kazarovets about 5 yearsDoes 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 about 5 yearsRan 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 therbs
variable or the calculation of thegsum
,rsum
, orbsum
variables are not being done right. I found that by replacingMath.abs
withStrictMath.abs
or some otherabs
implementation, the crash does not occur. SinceStrictMath.abs
itself delegates toMath.abs
, it seems like it must be a bad optimization. -
Miha_x64 over 3 yearsThanks! Optimized for less memory consumption and no copying: gist.github.com/Miha-x64/3fb489d13dbf69e1611a8fb688b57d3d
-
CoolMind over 2 yearsIt works for
ImageView
, but not for complex layouts withTextView
(text won't be blurred). I will turn toRenderScript
version. -
CoolMind over 2 yearsThanks! Don't forget to add RenderScript in build.gradle. This method requires
Bitmap
, so first copy bitmap from a view, then callBlurImage(bitmap)
. -
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 over 2 yearsIt is c copy of b_yng solution.