Android ScriptIntrinsicBlur doesn't blur the whole image and generates a transparent Edge
Asked Answered
O

4

13

I have some problems with the RenderScript ScriptIntrinsic Blur - on some devices it doesn't blur the whole image. I downscale the input image and make sure that the width is a multiple of 4 (because it's suggested by Roman Nurik: https://plus.google.com/+RomanNurik/posts/TLkVQC3M6jW)

@SuppressLint("NewApi")
private Bitmap blurRenderScript(Bitmap smallBitmap) {

    Bitmap output = Bitmap.createBitmap(smallBitmap.getWidth(), smallBitmap.getHeight(), smallBitmap.getConfig());

    RenderScript rs = RenderScript.create(getContext());
    ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
    Allocation inAlloc = Allocation.createFromBitmap(rs, smallBitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE);
    Allocation outAlloc = Allocation.createFromBitmap(rs, output);
    script.setRadius(BLUR_RADIUS);
    script.setInput(inAlloc);
    script.forEach(outAlloc);
    outAlloc.copyTo(output);

    rs.destroy();

    MutableBitmap.delete(smallBitmap);

    return output;
}

It is working on a Nexus 4:

Nexus 4 before blurringNexus 4 after blurring

But on a Galaxy S4 the right side has a transparent edge:

Galaxy S4 before blurringGalaxy S4 after blurring

I hope you can see what I mean - if you open the picture in gimp or so you can see it better. It doesn't depend on the picture size. I Also tried it with bigger and smaller images, and the result was always the same. It also happens on a Nexus 7 2012 for example. Also, the transparent artifacts are sometimes on the bottem or left edge. Thanks in advance for your help!

Nexus 4: 4.4.2/Build Number KOT49H Galaxy S4: 4.2.2/Build Number JDQ39.I9505XXUDMGG

Olivaolivaceous answered 13/3, 2014 at 9:29 Comment(5)
Ever figure out a workaround for this?Tindal
Not with renderscript.. I implemented something similar to https://mcmap.net/q/127844/-fast-bitmap-blur-for-android-sdk - but this is of course slower than renderscriptOlivaolivaceous
Yeah, I contemplated using that implementation but ended up on a halfway answer I provided below. Worked well enough for my situation. Too bad, spent a long time trying to figure this out.Tindal
You should read this article, they talk about removing the artifacts : developers.500px.com/2015/03/17/…Trussell
Could it have been that your code to ensure the width was a multiple of 4 was faulty? That solution worked for me.Kieserite
N
3

I'm not sure why this might work, but for me it did..

Try instead of

    Bitmap output = Bitmap.createBitmap(smallBitmap.getWidth(), smallBitmap.getHeight(), smallBitmap.getConfig());

pass in Bitmap.Config.ARGB_8888 like so

    Bitmap output = Bitmap.createBitmap(smallBitmap.getWidth(), smallBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Nihility answered 21/3, 2014 at 16:29 Comment(0)
S
2

I am assuming you are using android.renderscript instead of the support lib android.v8.renderscript. (or a old support lib?)

If that is true, then it is a known bug that can only be fixed by a system upgrade, which may not be feasible to the users.

One way to workaround this is to use RenderScript Support lib: you can find the detailed instructions here:http://developer.android.com/guide/topics/renderscript/compute.html#access-rs-apis

Basically you just need to change the import:

import android.support.v8.renderscript.*;

And config your build.gradle:

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 19

        renderscriptTargetApi 18
        renderscriptSupportModeEnabled true

    }
}

There are several advantages of using support lib:

  1. Support devices back to API 8.
  2. Can be updated with Build-Tools to incorporate bugs fixes and workaround.

On the downside, to achieve this, it needs to bundle several shared libraries which will make your app beefier.

Schaefer answered 13/5, 2016 at 18:23 Comment(0)
T
1

I still had trouble getting anything to work to take away this transparency, so I ended on just trimming the image by 25 pixels after I had blurred it. Definitely not ideal, but it worked well enough for my application of ScriptIntrinsicBlur:

output = Bitmap.createBitmap(nbm, 25, 25, bm.getWidth() - 25, bm.getHeight() - 25);
Tindal answered 7/3, 2015 at 2:41 Comment(0)
E
-3

H! This is for all people who need to increase the radius of ScriptIntrinsicBlur to obtain a harder gaussian blur.

Instead of to put the radius more than 25, you can scale down the image and get the same result. I wrote a class called GaussianBlur. Bellow you can see how to use, and the whole class implementation.

Usage:

    GaussianBlur gaussian = new GaussianBlur(context);
    gaussian.setMaxImageSize(60);
    gaussian.setRadius(25); //max

    Bitmap output = gaussian.render(<your bitmap>,true);
    Drawable d = new BitmapDrawable(getResources(),output);

Class:

public class GaussianBlur {
private final int DEFAULT_RADIUS = 25;
private final float DEFAULT_MAX_IMAGE_SIZE = 400;

private Context context;
private int radius;
private float maxImageSize;

public GaussianBlur(Context context) {
    this.context = context;
    setRadius(DEFAULT_RADIUS);
    setMaxImageSize(DEFAULT_MAX_IMAGE_SIZE);
}

public Bitmap render(Bitmap bitmap, boolean scaleDown) {
    RenderScript rs = RenderScript.create(context);

    if (scaleDown) {
        bitmap = scaleDown(bitmap);
    }

    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);

    Allocation inAlloc = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE);
    Allocation outAlloc = Allocation.createFromBitmap(rs, output);

    ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, inAlloc.getElement()); // Element.U8_4(rs));
    script.setRadius(getRadius());
    script.setInput(inAlloc);
    script.forEach(outAlloc);
    outAlloc.copyTo(output);

    rs.destroy();

    return output;
}

public Bitmap scaleDown(Bitmap input) {
    float ratio = Math.min((float) getMaxImageSize() / input.getWidth(), (float) getMaxImageSize() / input.getHeight());
    int width = Math.round((float) ratio * input.getWidth());
    int height = Math.round((float) ratio * input.getHeight());

    return Bitmap.createScaledBitmap(input, width, height, true);
}

public int getRadius() {
    return radius;
}

public void setRadius(int radius) {
    this.radius = radius;
}

public float getMaxImageSize() {
    return maxImageSize;
}

public void setMaxImageSize(float maxImageSize) {
    this.maxImageSize = maxImageSize;
}

}

;)

Entero answered 17/10, 2014 at 13:52 Comment(2)
What does the "Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE)" do in the code ?Kcal
Uh, this answer completely irrelevant to the question at hand.Kieserite

© 2022 - 2024 — McMap. All rights reserved.