Fast Bitmap Blur For Android SDK

后端 未结 19 1480
情书的邮戳
情书的邮戳 2020-11-22 08:53

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.

W

相关标签:
19条回答
  • 2020-11-22 09:34

    For future Googlers who choose NDK approach - i find reliable mentioned stackblur algorithm. I found C++ implementation which does not rely on SSE here - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32 which contains some optimizations using static tables like:

    static unsigned short const stackblur_mul[255] =
    {
        512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
        454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
        482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
        437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
        497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
        320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
        446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
        329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
        505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
        399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
        324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
        268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
        451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
        385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
        332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
        289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
    };
    
    static unsigned char const stackblur_shr[255] =
    {
        9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
        17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
        19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
        20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
        21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
        21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
        22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
        22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
        23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
        23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
        23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
        23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
        24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
        24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
        24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
        24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
    }; 
    

    I made modification of stackblur algorithm for multi-core systems - it can be found here http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ As more and more devices have 4 cores - optimizations give 4x speed benefit.

    0 讨论(0)
  • 2020-11-22 09:36

    from Mario Viviani blog one can use this solution from 17 Android Version:

    https://plus.google.com/+MarioViviani/posts/fhuzYkji9zz

    or

    https://gist.github.com/Mariuxtheone/903c35b4927c0df18cf8

    0 讨论(0)
  • 2020-11-22 09:36

    I found that decreasing contrast, brightness and saturation a little makes blurred images more pretty so I combined various methods from stack overflow and made this Blur Class which deals with blurring images, changing brightness, saturation, contrast and size of the blurred images. It can also convert images from drawable to bitmap and vice-versa.

    0 讨论(0)
  • 2020-11-22 09:38

    On the i/o 2019 the following solution was presented:

    /**
     * Blurs the given Bitmap image
     * @param bitmap Image to blur
     * @param applicationContext Application context
     * @return Blurred bitmap image
     */
    @WorkerThread
    fun blurBitmap(bitmap: Bitmap, applicationContext: Context): Bitmap {
        lateinit var rsContext: RenderScript
        try {
    
            // Create the output bitmap
            val output = Bitmap.createBitmap(
                    bitmap.width, bitmap.height, bitmap.config)
    
            // Blur the image
            rsContext = RenderScript.create(applicationContext, RenderScript.ContextType.DEBUG)
            val inAlloc = Allocation.createFromBitmap(rsContext, bitmap)
            val outAlloc = Allocation.createTyped(rsContext, inAlloc.type)
            val theIntrinsic = ScriptIntrinsicBlur.create(rsContext, Element.U8_4(rsContext))
            theIntrinsic.apply {
                setRadius(10f)
                theIntrinsic.setInput(inAlloc)
                theIntrinsic.forEach(outAlloc)
            }
            outAlloc.copyTo(output)
    
            return output
        } finally {
            rsContext.finish()
        }
    }
    
    0 讨论(0)
  • 2020-11-22 09:39

    I used this before..

    public static Bitmap myblur(Bitmap image, Context context) {
                final float BITMAP_SCALE = 0.4f;
                final float BLUR_RADIUS = 7.5f;
                int width = Math.round(image.getWidth() * BITMAP_SCALE);
                int height = Math.round(image.getHeight() * BITMAP_SCALE);
                Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
                Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
                RenderScript rs = RenderScript.create(context);
                ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
                Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
                Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
                theIntrinsic.setRadius(BLUR_RADIUS);
                theIntrinsic.setInput(tmpIn);
                theIntrinsic.forEach(tmpOut);
                tmpOut.copyTo(outputBitmap);
                return outputBitmap;
            }
    
    0 讨论(0)
  • 2020-11-22 09:41

    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. Below 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;
    }
        }
    
    0 讨论(0)
提交回复
热议问题