【问题标题】:Fast Bitmap Blur For Android SDKAndroid SDK 的快速位图模糊
【发布时间】:2011-01-05 07:02:58
【问题描述】:

目前在我正在开发的 Android 应用程序中,我正在循环通过图像的像素来模糊它。这在 640x480 图像上大约需要 30 秒。

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

有谁知道除了循环像素之外更快的方法吗?

【问题讨论】:

  • 不幸的是,图像总是不同的,所以我无法提前创建模糊版本。另外,我也不会提前知道模糊强度。
  • 您能否发布您的代码,可能是算法/代码效率低下,30 秒通过 640x480 图像很慢,我原以为 5 秒很慢,但又一次取决于处理器。

标签: java android image-processing renderscript


【解决方案1】:

Android 模糊指南 2016

with Showcase/Benchmark AppSource on Github还可以查看我目前正在开发的 Blur 框架:Dali

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

加载和使用缩小的位图(用于非常模糊的图像)

切勿使用完整大小的位图。图像越大,需要模糊的越多,模糊半径也需要越高,通常,模糊半径越大,算法所需的时间就越长。

final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap blurTemplate = BitmapFactory.decodeResource(getResources(), R.drawable.myImage, options);

这将加载带有inSampleSize 8 的位图,因此只有原始图像的 1/64。测试 inSampleSize 适合您的需求,但保持 2^n (2,4,8,...) 以避免因缩放而降低质量。 See Google doc for more

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

使用渲染脚本

Renderscript 提供ScriptIntrinsicBlur,这是一个高斯模糊滤镜。它具有良好的视觉质量,是您在 Android 上实际获得的最快速度。谷歌声称是"typically 2-3x faster than a multithreaded C implementation and often 10x+ faster than a Java implementation"。 Renderscript 非常复杂(使用最快的处理设备(GPU、ISP 等)等),还有v8 support library for it making it compatible down to 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);

当使用谷歌"because they include the latest improvements"特别推荐的Gradle v8支持时,你只能need to add 2 lines to your build scriptandroid.support.v8.renderscript与当前的构建工具一起使用(updated syntax for android Gradle plugin v14+)

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

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

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

每秒可模糊的百万像素

每个值是 250 轮的平均值。 RS_GAUSS_FASTScriptIntrinsicBlur(几乎总是最快的),其他以 RS_ 开头的主要是使用简单内核的卷积实现。 The details of the algorithms can be found here。这并不是纯粹的模糊,因为很大一部分是被测量的垃圾收集。这可以在这里看到(ScriptIntrinsicBlur 在 100x100 的图像上,大约 500 轮)

尖峰是 gc。

你可以自己查,基准应用在playstore里:BlurBenchmark

尽可能重用位图(如果优先:性能 > 内存占用)

如果您需要多个模糊来实现实时模糊或类似功能,并且您的内存允许它不会多次从可绘制对象中加载位图,而是将其“缓存”在成员变量中。在这种情况下,请始终尝试使用相同的变量,以尽量减少垃圾收集。

还可以从文件或可绘制对象中检查新的inBitmap option when loading,这将重用位图内存并节省垃圾收集时间。

用于从锐利到模糊的混合

简单天真的方法就是用2个ImageViews,一个模糊,然后alpha淡化它们。但是,如果您想要一个更复杂的外观,从锐利平滑地消失到模糊,请查看Roman Nurik's post about how to do it like in his Muzei app

基本上,他解释说他预先模糊了一些具有不同模糊程度的帧,并将它们用作看起来非常流畅的动画中的关键帧。

【讨论】:

  • 首先感谢您的辛勤工作!但我有一个问题:“因为它使用重用内存的 USAGE_SHARED 模式”。你在哪里找到常量 USAGE_SHARED?我在任何地方都找不到。
  • 我找到了,USAGE_SHARED 仅在 support.v8.renderscript 中可用
  • Renderscript 快速高斯模糊在低端设备上因 C 内存分配错误而失败。使用提供的 Play Store 应用在 ZTE Z992 (Android 4.1.1) 和 Kyocera Rise (Android 4.0.4) 上进行测试。也有关于三星 Galaxy S3 mini 的故障报告。由于错误发生在 C 层,因此它们不能在 Java 中被捕获为异常,这意味着应用程序崩溃是不可避免的。看起来 RenderScript 可能还没有准备好用于生产。
  • 对于较新的 gradle 版本,请使用 renderscriptSupportModeEnabled true 否则它将无法构建!我一直在寻找!
  • When I tried this solution, rather than getting a blurred bitmap, I got a rainbow colored bitmap。其他人遇到这个问题吗?如果是这样,您是如何解决的?
【解决方案2】:

在 i/o 2019 上提出了以下解决方案:

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

【讨论】:

    【解决方案3】:

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

    遇到 ArrayIndexOutOfBoundsException 问题的人的更新: cmets 中的@anthonycr 提供了此信息:

    我发现通过将 Math.abs 替换为 StrictMath.abs 或其他一些 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 <mario@quasimondo.com>
     */
    
    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);
    }
    

    【讨论】:

    • 谢谢亚赫尔。你解决了我的问题。再次感谢。
    • 我应该通过什么作为半径?
    • 对于大于 1 的半径,有时你会得到 ArrayIndexOutOfBoundsException。我会尝试找出问题所在。
    • @MichaelLiberman 我也遇到了同样的问题。找出原因了吗? “g[yi] = dv[gsum];” -->错误:java.lang.ArrayIndexOutOfBoundsException:长度=112896;索引=114021
    • 遇到了已知的ArrayIndexOutOfBoundsException,经过一番分析,相信是Dalvik VM优化不正确造成的。在错误取消引用之前的for 循环中,rbs 变量的计算或gsumrsumbsum 变量的计算没有正确完成。我发现通过将Math.abs 替换为StrictMath.abs 或其他一些abs 实现,不会发生崩溃。由于StrictMath.abs 本身委托给Math.abs,看来这一定是一个糟糕的优化。
    【解决方案4】:

    我发现稍微降低对比度、亮度和饱和度会使模糊的图像更漂亮,所以我结合了堆栈溢出的各种方法并制作了这个Blur Class 处理模糊图像、改变亮度、饱和度,模糊图像的对比度和大小。它还可以将图像从可绘制图像转换为位图,反之亦然。

    【讨论】:

      【解决方案5】:

      这是一个使用 RenderScript 的实时模糊叠加,看起来速度足够快。

      https://github.com/mmin18/RealtimeBlurView

      【讨论】:

        【解决方案6】:

        我以前用过这个..

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

        【讨论】:

          【解决方案7】:

          感谢@Yahel 提供代码。 发布具有 alpha 通道模糊 支持的相同方法,因为我花了一些时间使其正常工作,因此它可以节省某人的时间:

          /**
           * Stack Blur v1.0 from
           * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
           * Java Author: Mario Klingemann <mario at quasimondo.com>
           * http://incubator.quasimondo.com
           * <p/>
           * created Feburary 29, 2004
           * Android port : Yahel Bouaziz <yahel at kayenko.com>
           * http://www.kayenko.com
           * ported april 5th, 2012
           * <p/>
           * 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.
           * <p/>
           * 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.
           * <p/>
           * If you are using this algorithm in your code please add
           * the following line:
           * Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
           */
          
          public static 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 a[] = new int[wh];
              int rsum, gsum, bsum, asum, 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][4];
              int stackpointer;
              int stackstart;
              int[] sir;
              int rbs;
              int r1 = radius + 1;
              int routsum, goutsum, boutsum, aoutsum;
              int rinsum, ginsum, binsum, ainsum;
          
              for (y = 0; y < h; y++) {
                  rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 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);
                      sir[3] = 0xff & (p >> 24);
          
                      rbs = r1 - Math.abs(i);
                      rsum += sir[0] * rbs;
                      gsum += sir[1] * rbs;
                      bsum += sir[2] * rbs;
                      asum += sir[3] * rbs;
                      if (i > 0) {
                          rinsum += sir[0];
                          ginsum += sir[1];
                          binsum += sir[2];
                          ainsum += sir[3];
                      } else {
                          routsum += sir[0];
                          goutsum += sir[1];
                          boutsum += sir[2];
                          aoutsum += sir[3];
                      }
                  }
                  stackpointer = radius;
          
                  for (x = 0; x < w; x++) {
          
                      r[yi] = dv[rsum];
                      g[yi] = dv[gsum];
                      b[yi] = dv[bsum];
                      a[yi] = dv[asum];
          
                      rsum -= routsum;
                      gsum -= goutsum;
                      bsum -= boutsum;
                      asum -= aoutsum;
          
                      stackstart = stackpointer - radius + div;
                      sir = stack[stackstart % div];
          
                      routsum -= sir[0];
                      goutsum -= sir[1];
                      boutsum -= sir[2];
                      aoutsum -= sir[3];
          
                      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);
                      sir[3] = 0xff & (p >> 24);
          
                      rinsum += sir[0];
                      ginsum += sir[1];
                      binsum += sir[2];
                      ainsum += sir[3];
          
                      rsum += rinsum;
                      gsum += ginsum;
                      bsum += binsum;
                      asum += ainsum;
          
                      stackpointer = (stackpointer + 1) % div;
                      sir = stack[(stackpointer) % div];
          
                      routsum += sir[0];
                      goutsum += sir[1];
                      boutsum += sir[2];
                      aoutsum += sir[3];
          
                      rinsum -= sir[0];
                      ginsum -= sir[1];
                      binsum -= sir[2];
                      ainsum -= sir[3];
          
                      yi++;
                  }
                  yw += w;
              }
              for (x = 0; x < w; x++) {
                  rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 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];
                      sir[3] = a[yi];
          
                      rbs = r1 - Math.abs(i);
          
                      rsum += r[yi] * rbs;
                      gsum += g[yi] * rbs;
                      bsum += b[yi] * rbs;
                      asum += a[yi] * rbs;
          
                      if (i > 0) {
                          rinsum += sir[0];
                          ginsum += sir[1];
                          binsum += sir[2];
                          ainsum += sir[3];
                      } else {
                          routsum += sir[0];
                          goutsum += sir[1];
                          boutsum += sir[2];
                          aoutsum += sir[3];
                      }
          
                      if (i < hm) {
                          yp += w;
                      }
                  }
                  yi = x;
                  stackpointer = radius;
                  for (y = 0; y < h; y++) {
                      pix[yi] = (dv[asum] << 24) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
          
                      rsum -= routsum;
                      gsum -= goutsum;
                      bsum -= boutsum;
                      asum -= aoutsum;
          
                      stackstart = stackpointer - radius + div;
                      sir = stack[stackstart % div];
          
                      routsum -= sir[0];
                      goutsum -= sir[1];
                      boutsum -= sir[2];
                      aoutsum -= sir[3];
          
                      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];
                      sir[3] = a[p];
          
                      rinsum += sir[0];
                      ginsum += sir[1];
                      binsum += sir[2];
                      ainsum += sir[3];
          
                      rsum += rinsum;
                      gsum += ginsum;
                      bsum += binsum;
                      asum += ainsum;
          
                      stackpointer = (stackpointer + 1) % div;
                      sir = stack[stackpointer];
          
                      routsum += sir[0];
                      goutsum += sir[1];
                      boutsum += sir[2];
                      aoutsum += sir[3];
          
                      rinsum -= sir[0];
                      ginsum -= sir[1];
                      binsum -= sir[2];
                      ainsum -= sir[3];
          
                      yi += w;
                  }
              }
          
              Log.e("pix", w + " " + h + " " + pix.length);
              bitmap.setPixels(pix, 0, w, 0, 0, w, h);
          
              return (bitmap);
          }
          

          【讨论】:

          • 设备中的“索引超出范围”>hdpi 作为原始来源
          【解决方案8】:

          这对我来说很好用:How to Blur Images Efficiently with Android's RenderScript

          public class BlurBuilder {
              private static final float BITMAP_SCALE = 0.4f;
              private static final float BLUR_RADIUS = 7.5f;
          
              @SuppressLint("NewApi")
              public static Bitmap blur(Context context, 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(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;
              }
          }
          

          【讨论】:

          • 如果您缓存了您创建的 RenderScript 对象,这会更好/更快。每次想要模糊图像时都实例化一个新的,只会增加不必要的开销(Java 对象创建/销毁)。
          • b_yng解决方案的c副本。
          【解决方案9】:

          来自 Mario Viviani 的博客,可以从 17 Android 版本开始使用此解决方案:

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

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

          【讨论】:

            【解决方案10】:

            这适用于所有需要增加ScriptIntrinsicBlur 的半径以获得更硬的高斯模糊的人。

            您可以缩小图像并获得相同的结果,而不是将半径设置为超过 25。我写了一个名为GaussianBlur 的类。下面你可以看到如何使用,以及整个类的实现。

            用法:

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

            类:

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

            【讨论】:

            • 不,如果您缩小图像以稍后放大,您将检索到块状图像而不是模糊图像:(
            【解决方案11】:

            对于那些仍然对 x86 芯片组上的 Renderscript 支持库有问题的人,请查看库创建者的这篇文章。看起来他准备的修复并没有以某种方式进入 Build Tools v20.0.0,因此他提供了手动修复它的文件并简要说明了如何进行修复。

            https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=71347

            【讨论】:

              【解决方案12】:

              这段代码非常适合我

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

              【讨论】:

              • 模糊图像的最简单方法(y)
              • 谢谢!不要忘记在 build.gradle 中添加 RenderScript。这个方法需要Bitmap,所以首先从一个视图中复制位图,然后调用BlurImage(bitmap)
              【解决方案13】:

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

              • 每当我们尝试使用渲染脚本时,Samsung S3 都会随机崩溃
              • 其他设备(跨不同 API)随机显示不同的颜色问题

              我想分享我们肮脏的纯 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;
              }
              

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

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

              【讨论】:

              • 我在一些旧的 armeabi 设备上也遇到了 RenderScript 问题,但您的解决方案太耗内存了。
              【解决方案14】:

              Nicolas POMEPUY 的建议。我认为这个链接会有所帮助:Blur effect for Android design

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

              【讨论】:

              • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
              • Amal Murali,你是对的。现在换我的帖子。很好,您不仅可以投反对票,还可以发表评论。
              【解决方案15】:

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

              下面是旧答案,但要小心,因为它已经过时了。


              对于未来² Google 员工,这是我从 Yahel 的 Quasimondo 算法端口移植而来的算法,但使用的是 NDK。当然,这是基于 Yahel 的回答。但这是运行本机 C 代码,因此速度更快。快多了。比如,快 40 倍。

              我发现使用 NDK 是应该在 Android 上完成所有图像处理的方式......起初实现有点烦人(阅读关于使用 JNI 和 NDK here 的精彩教程),但要好得多,并且很多事情都是近乎实时的。

              作为参考,使用 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;
              }
              

              然后像这样使用它(考虑一个名为 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);
              

              它需要一个 RGB_8888 位图!

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

              typedef struct {
                  uint16_t byte0;
              } rgb565;
              

              问题是,如果您这样做,您将无法再读取像素的.red.green.blue,您需要正确读取字节,呵呵。当我以前需要它时,我这样做了:

              r = (pixels[x].byte0 & 0xF800) >> 8;
              g = (pixels[x].byte0 & 0x07E0) >> 3;
              b = (pixels[x].byte0 & 0x001F) << 3;
              

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

              【讨论】:

              • 但是它需要大量内存。为了减少内存消耗,将r[wh]g[wh]b[wh]的类型更改为uint8_t
              • 你能告诉我你的 Android.mk 文件是什么样的吗,pastebin.com
              • @CQM: 当然:pastebin.com/8VMTZy85 不过考虑一下,使用RenderScript 可能是解决此问题的更简单、更现代且可能更快的解决方案(但仅适用于 Android 4+) .
              • 对于 Android SDK 17+ 上的高斯模糊的工作 RenderScript 示例,请看这里:stackoverflow.com/questions/14988990/android-fast-bitmap-blur
              • RenderScript 也作为 Android 2.2+ 支持库的一部分提供,因此没有理由不再在任何地方使用它:android-developers.blogspot.com/2013/09/…
              【解决方案16】:

              您现在可以使用 RenderScript 库中的 ScriptIntrinsicBlur 快速模糊。 Here 是如何访问 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;
                  }
              }
              

              【讨论】:

              • Renderscript 上下文不应在 blur 方法中创建,而应静态管理或赋予该方法。 (如果你介意表现)
              • 你能举个例子说明这是使用吗?当我尝试使用它时,出现以下错误: java.lang.IllegalArgumentException: width and height must be > 0
              • @DonalRafferty,这是因为尚未测量 onCreate 中的观看次数。我们应该在短暂的延迟后执行此操作。
              【解决方案17】:

              【讨论】:

                【解决方案18】:

                对于选择 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
                }; 
                

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

                【讨论】:

                  【解决方案19】:

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

                  【讨论】:

                  • 经过一些测试和模糊,我正在做这个实际工作对我来说已经足够好了,而且速度很快。谢谢!
                  • 如果它运作良好。很遗憾,我们从来没有弄清楚它为什么效率低下。
                  • 您可能想尝试 createScaledBitmap 并让图像保持相同大小。它对我来说模糊了:-(
                  • 这里讨论一下参数“filter”的含义:stackoverflow.com/questions/2895065
                  • 这并不完全是因为两个原因:1)它需要完整图像的内存,尽管您可能只使用缩小的图像 2)您需要加载完整图像至速度较慢 - 使用 inSampleSize 和 BitmapFactory.decodeResource() 加载,这是解决此问题的好方法。
                  猜你喜欢
                  • 2013-02-05
                  • 1970-01-01
                  • 2015-01-18
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-11-09
                  相关资源
                  最近更新 更多