Tip: Blur Images Efficiently with RenderScript in Android

    There are a lot of cool effects that we can do in Android to static images, and I cover some of these in my conference presentation for 2014 entitled Graphical Magic. One technique that I cover in the presentation is how to blur images and the example code uses RenderScript to perform the blur because there is no simple API built into Android that we can use.
    Available from API 17, ScriptIntrinsicBlur - is a Intrinsic Gausian blur filter, pplies a gaussian blur of the specified radius to all elements of an allocation, blurring image work become easier. Today, in this post, I will present a sample project show how to create blur bitmap with these 2 Object, I also provide a SeekBar to change the blurred amount. See this DEMO VIDEO first for ouput:

    Note: The following example code is developed for API17 and higher. I have decided against using the v8 support library for RenderScript.
    Firstly, make a simple layout for project's main activity contains a SeekBar and an ImageView like this:
    Note: like develope doc say, radius of Blur (a float value) supported from 0 to 25.0. So, please set max value is 25 for SeekBar.
    The following code snippets can be used create a bitmap blur effect in Android using RenderScript API:
private Bitmap createBlurBitmap(Bitmap src, float r) {
        if (r <= 0) {
            r = 0.1f;
        } else if (r > 25) {
            r = 25.0f;
        }

        Bitmap bitmap = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
        RenderScript renderScript = RenderScript.create(this);

        Allocation blurInput = Allocation.createFromBitmap(renderScript, src);
        Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        blur.setInput(blurInput);
        blur.setRadius(r);
        blur.forEach(blurOutput);

        blurOutput.copyTo(bitmap);
        renderScript.destroy();

        return bitmap;
    }
     To increase/decrease the opacity by SeekBar, provide a OnSeekBarChangeListener method and override nested follow methods:
private OnSeekBarChangeListener onSeekBarChanged() {
        return new OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                textRadius.setText("Blur radius: " + progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                float radius = (float) MainActivity.this.seekBar.getProgress();
                imageView.setImageBitmap(createBlurBitmap(bitmap, radius));
            }
        };
    }
    Recommended set Blur Bitmap for ImageView in onStopTrackingTouch() to avoid lagging in weak memory/chip devices.
    Set handling event for SeekBar, locate views, we now have full code for main activity:
package info.devexchanges.blurimage;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView textRadius;
    private ImageView imageView;
    private SeekBar seekBar;

    private Bitmap bitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textRadius = (TextView) findViewById(R.id.txt_radius);
        imageView = (ImageView) findViewById(R.id.image);
        seekBar = (SeekBar) findViewById(R.id.blur_radius);

        //decode a drawable resource to Bitmap
        bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ao_dai);
        imageView.setImageBitmap(bitmap);

        textRadius.setText("Blur radius: 0");

        //handling seek bar event
        seekBar.setOnSeekBarChangeListener(onSeekBarChanged());

    }

    private OnSeekBarChangeListener onSeekBarChanged() {
        return new OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                textRadius.setText("Blur radius: " + progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                float radius = (float) MainActivity.this.seekBar.getProgress();
                imageView.setImageBitmap(createBlurBitmap(bitmap, radius));
            }
        };
    }

    private Bitmap createBlurBitmap(Bitmap src, float r) {
        if (r <= 0) {
            r = 0.1f;
        } else if (r > 25) {
            r = 25.0f;
        }

        Bitmap bitmap = Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888);
        RenderScript renderScript = RenderScript.create(this);

        Allocation blurInput = Allocation.createFromBitmap(renderScript, src);
        Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap);

        ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
        blur.setInput(blurInput);
        blur.setRadius(r);
        blur.forEach(blurOutput);

        blurOutput.copyTo(bitmap);
        renderScript.destroy();

        return bitmap;
    }
}
    After running app, we have this output:
    Because of this ScriptIntrinsicBlur only added from API 17, so your project must define in app/build.gradle file about min-sdk:
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "info.devexchanges.blurimage"
        minSdkVersion 17
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
}
    You may also use support v8 library to use this method, more details, see this post in Android Developers blog, I haven't mentioned here. Full code of this project now available on Github, you can clicked below button to view. Thank for reading, subcribe my blog to see newest tutorials!


Share


Previous post
« Prev Post
Next post
Next Post »