Java Android SDK的快速位图模糊

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2067955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 03:12:32  来源:igfitidea点击:

Fast Bitmap Blur For Android SDK

javaandroidimage-processingrenderscript

提问by Greg

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.

目前在我正在开发的 Android 应用程序中,我正在遍历图像的像素以对其进行模糊处理。在 640x480 图像上这大约需要 30 秒。

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.

在 Android Market 中浏览应用程序时,我遇到了一个包含模糊功能的应用程序,它们的模糊速度非常快(如 5 秒),因此它们必须使用不同的模糊方法。

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

除了循环遍历像素之外,有人知道更快的方法吗?

采纳答案by Luke

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.

这是在黑暗中拍摄,但您可以尝试缩小图像,然后再次放大。这可以通过Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). 确保并将过滤器参数设置为 true。它将在本机代码中运行,因此可能会更快。

回答by Yahel

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.

对于未来的 Google 员工,这里有一个我从 Quasimondo 移植过来的算法。这是一种盒子模糊和高斯模糊之间的混合,它非常漂亮而且速度也很快。

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

遇到 ArrayIndexOutOfBoundsException 问题的人的更新:评论中的@anthonycr 提供了以下信息:

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

我发现通过用 StrictMath.abs 或其他一些 abs 实现替换 Math.abs ,不会发生崩溃。

/**
 * 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);
}

回答by zeh

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 RenderScriptinstead 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.

编辑(2014 年 4 月):这是一个问答页面,似乎仍然获得了很多点击。我知道我总是为这篇文章点赞。但是,如果您正在阅读本文,则需要意识到此处发布的答案(我的和已接受的答案)已过时。如果你今天想实现高效的模糊,你应该使用 RenderScript而不是 NDK 或 Java。RenderScript 在 Android 2.2+ 上运行(使用Android 支持库),所以没有理由不使用它。

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

旧答案如下,但要注意它已经过时了。



For future2 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.

对于future2 Googlers,这是我从Yahel's port of Quasimondo's algorithm 移植的算法,但使用的是NDK。当然,这是基于 Yahel 的回答。但这是运行本机 C 代码,因此速度更快。快多了。比如,快了 40 倍。

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.

我发现使用 NDK 是在 Android 上完成所有图像处理的方式......一开始实现有点烦人(阅读有关使用 JNI 和 NDK 的精彩教程在这里),但更好,并且接近实时很多东西。

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.

作为参考,使用 Yahel 的 Java 函数,模糊半径为 10 的 480x532 像素图像需要 10 秒。但使用本机 C 版本需要 250 毫秒。而且我很确定它仍然可以进一步优化......我只是对java代码做了一个愚蠢的转换,可能有一些可以缩短的操作,不想花太多时间重构整个事情。

#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):

然后像这样使用它(考虑一个名为 com.insert.your.package.ClassName 的类和一个名为 functionToBlur 的本机函数,如上面的代码所述):

// 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!

它需要一个 RGB_8888 位图!

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

要使用 RGB_565 位图,请在传递参数之前创建转换后的副本 (yuck),或者更改函数以使用新rgb565类型而不是rgba

typedef struct {
    uint16_t byte0;
} rgb565;

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

问题是,如果你这样做,你可以不读.red.green并且.blue像素的了,你需要正确地读取字节,咄。当我之前需要它时,我这样做了:

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.

但可能有一些不那么愚蠢的方法。恐怕我不是一个低级的 C 编码员。

回答by Victor Laskin

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_rgba32which contains some optimizations using static tables like:

对于选择 NDK 方法的未来 Google 员工 - 我找到了可靠的提到的 stackblur 算法。我在这里找到了不依赖于 SSE 的 C++ 实现 - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32包含一些使用静态表的优化,例如:

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.

我修改了多核系统的 stackblur 算法 - 可以在这里找到http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/随着越来越多的设备有 4 个内核 - 优化给出4 倍速度优势。

回答by b_yng

You can now use ScriptIntrinsicBlurfrom the RenderScript library to blur quickly. Hereis how to access the RenderScript API. The following is a class I made to blur Views and Bitmaps:

您现在可以使用RenderScript 库中的ScriptIntrinsicBlur快速模糊。以下是访问 RenderScript API 的方法。以下是我用来模糊视图和位图的类:

public class BlurBuilder {
    private static final float BITMAP_SCALE = 0.4f;
    private static final float BLUR_RADIUS = 7.5f;

    public static Bitmap blur(View v) {
        return blur(v.getContext(), getScreenshot(v));
    }

    public static Bitmap blur(Context ctx, Bitmap image) {
        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(ctx);
        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;
    }

    private static Bitmap getScreenshot(View v) {
        Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        v.draw(c);
        return b;
    }
}

回答by Yura Shinkarev

Nicolas POMEPUY advice. I think this link will be helpful: Blur effect for Android design

Nicolas POMEPUY 的建议。我认为此链接会有所帮助:Android 设计的模糊效果

Sample project at github

github上的示例项目

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static Bitmap fastblur16(Bitmap source, int radius, Context ctx) {    
    Bitmap bitmap = source.copy(source.getConfig(), true);    
    RenderScript rs = RenderScript.create(ctx);
    Allocation input = Allocation.createFromBitmap(rs, source, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    Allocation output = Allocation.createTyped(rs, input.getType());
    ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
    script.setRadius(radius);
    script.setInput(input);
    script.forEach(output);
    output.copyTo(bitmap);
    return bitmap;
}

回答by Patrick Favre

Android Blur Guide 2016

Android 模糊指南 2016

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

带有 Showcase/Benchmark 应用程序Github 上的源代码另请查看我目前正在研究的 Blur 框架: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.

经过大量试验后,我现在可以安全地为您提供一些可靠的建议,这些建议将使您在使用 Android 框架时在 Android 中的生活更轻松。

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 inSampleSize8, so only 1/64 of the original image. Test what inSampleSizesuits your needs, but keep it 2^n (2,4,8,...) to avoid degrading quality due to scaling. See Google doc for more

这将加载inSampleSize8 个位图,因此只有原始图像的 1/64。测试什么inSampleSize适合您的需求,但保持 2^n (2,4,8,...) 以避免由于缩放而降低质量。请参阅 Google 文档了解更多信息

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.

另一个非常大的优势是位图加载将非常快。在我早期的模糊测试中,我认为整个模糊过程中最长的时间是图像加载。因此,要从磁盘加载 1920x1080 图像,我的 Nexus 5 需要 500 毫秒,而模糊只需要 250 毫秒左右。

Use Renderscript

使用渲染脚本

Renderscript provides ScriptIntrinsicBlurwhich 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:

Renderscript 提供了ScriptIntrinsicBlur哪个是高斯模糊过滤器。它具有良好的视觉质量,并且是您在 Android 上实际获得的最快速度。Google 声称“通常比多线程 C 实现快 2-3 倍,通常比 Java 实现快 10 倍以上”。Renderscript 非常复杂(使用最快的处理设备(GPU、ISP 等)等)并且还有v8 支持库,使其兼容低至 2.2. 至少在理论上,通过我自己的测试和其他开发人员的报告,似乎不可能盲目地使用 Renderscript,因为硬件/驱动程序碎片似乎会导致某些设备出现问题,即使使用更高的 sdk lvl(例如我有4.1 Nexus S 的问题)所以要小心并在很多设备上进行测试。这是一个简单的示例,可以帮助您入门:

//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 scriptand use android.support.v8.renderscriptwith current build tools (updated syntax for android Gradle plugin v14+)

当使用谷歌特别推荐的 v8 支持 Gradle 时,“因为它们包含最新的改进”,您只需要在构建脚本中添加 2 行并使用android.support.v8.renderscript当前构建工具(android Gradle 插件 v14+ 的更新语法

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

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

Nexus 5 上的简单基准测试 - 将 RenderScript 与其他不同的 java 和 Renderscript 实现进行比较:

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

不同图片尺寸下每个模糊的平均运行时间不同图片尺寸下每个模糊的平均运行时间

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

每秒可模糊的百万像素每秒可模糊的百万像素

Each value is the avg of 250 rounds. RS_GAUSS_FASTis 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 (ScriptIntrinsicBluron a 100x100 image with about 500 rounds)

每个值是 250 轮的平均值。RS_GAUSS_FASTScriptIntrinsicBlur(并且几乎总是最快的),其他的开始RS_主要是用简单的内核卷积实现。可以在此处找到算法的详细信息。这不是纯粹的模糊,因为很大一部分是被测量的垃圾收集。这可以在这里看到(ScriptIntrinsicBlur在大约 500 轮的 100x100 图像上)

enter image description here

在此处输入图片说明

The spikes are gc.

尖峰是 gc。

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

您可以自己检查,基准应用程序在 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 inBitmapoption when loadingfrom a file or drawable which will reuse the bitmap memory and save garbage collection time.

从文件或可绘制对象inBitmap加载时还要检查新选项,这将重用位图内存并节省垃圾收集时间。

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.

简单而天真的方法就是使用 2 ImageViews,一个模糊,然后 alpha 淡化它们。但是如果你想要一个更复杂的外观,从清晰到模糊平滑地淡化,然后查看Roman Nurik 的帖子,了解如何在他的 Muzei 应用程序中做到这一点

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

Nurik 解释他的方法的图表

回答by WarrenFaith

We tried to implement RenderScript blur like mentioned above in different answers. We were limited to use the v8 RenderScript version and that caused us a lot of trouble.

我们尝试在不同的答案中实现上面提到的 RenderScript 模糊。我们只能使用 v8 RenderScript 版本,这给我们带来了很多麻烦。

  • Samsung S3 crashed randomly whenever we tried to use the renderscript
  • Other devices (across different APIs) randomly showed different color issues
  • 每当我们尝试使用渲染脚本时,三星 S3 都会随机崩溃
  • 其他设备(跨不同 API)随机显示不同的颜色问题

I want to share our dirty Java-only version which is slow and should be done on a separate thread and, if possible, before usage and therefore persisted.

我想分享我们肮脏的纯 Java 版本,它很慢,应该在单独的线程上完成,如果可能,在使用之前完成,因此会持续存在。

private final Paint mPaint = new Paint();

public Bitmap blur(final String pathToBitmap) {
    final BitmapFactory.Options options = new BitmapFactory.Options();
    final Bitmap normalOne = BitmapFactory.decodeFile(pathToBitmap, options);
    final Bitmap resultBitmap = Bitmap.createBitmap(options.outWidth, options.outHeight, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(resultBitmap);
    mPaint.setAlpha(180);
    canvas.drawBitmap(normalOne, 0, 0, mPaint);
    int blurRadius = 12;
    for (int row = -blurRadius; row < blurRadius; row += 2) {
        for (int col = -blurRadius; col < blurRadius; col += 2) {
            if (col * col + row * row <= blurRadius * blurRadius) {
                mPaint.setAlpha((blurRadius * blurRadius) / ((col * col + row * row) + 1) * 2);
                canvas.drawBitmap(normalOne, row, col, mPaint);
            }
        }
    }
    normalOne.recycle();
    return resultBitmap;
}

This solution is far from perfect but creates a reasonable blur effect based on the fact, that it draws highly transparent version of the same image on top of a barely transparent "sharp" version. The alpha depends on the distance to the origin.

该解决方案远非完美,但基于以下事实创建了合理的模糊效果,即它在几乎不透明的“锐利”版本之上绘制了同一图像的高度透明版本。alpha 取决于到原点的距离。

You can adjust some "magic numbers" to your needs. I just wanted to share that "solution" for everybody who has issues with the v8 support version of RenderScript.

您可以根据需要调整一些“幻数”。我只是想为所有对 RenderScript 的 v8 支持版本有问题的人分享这个“解决方案”。

回答by Niks

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;
    }

}