Android SDK 的快速位图模糊
目前,在我正在开发的 Android 应用程序中,我正在循环遍历图像的像素以使其模糊。对于 640x480 图像,这大约需要 30 秒。
在浏览 Android Market 中的应用程序时,我遇到了一个包含模糊功能的应用程序,并且它们的模糊速度非常快(例如 5 秒),因此它们一定使用了不同的模糊方法。
除了循环像素之外,有人知道更快的方法吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
对于未来的 Google 员工,这是我从 Quasimondo 移植的算法。这是一种盒子模糊和高斯模糊的混合,它非常漂亮而且速度也很快。
针对遇到 ArrayIndexOutOfBoundsException 问题的人的更新:评论中的 @anthonycr 提供了以下信息:
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.
Update for people encountering the ArrayIndexOutOfBoundsException problem : @anthonycr in the comments provides this information :
Android 模糊指南 2016
带有 Showcase/Benchmark 应用 和 GitHub 上的源代码。
另请查看我目前正在开发的 Blur 框架:Dali。
经过大量实验后,我现在可以安全地为您提供一些可靠的建议,这些建议将使您在使用 Android 框架时在 Android 中的生活更加轻松。
加载并使用缩小的位图(对于非常模糊的图像)
切勿使用完整大小的位图。图像越大,越需要模糊,模糊半径也需要越高,通常,模糊半径越大,算法花费的时间就越长。
这将加载
inSampleSize
8 的位图,因此只有原始图像的 1/64。测试inSampleSize
适合您的需求,但保持 2^n (2,4,8,...) 以避免因缩放而降低质量。 查看 Google 文档了解更多信息另一个巨大优势位图加载速度会非常快。在我早期的模糊测试中,我发现整个模糊过程中最长的时间是图像加载。因此,要从磁盘加载 1920x1080 图像,我的 Nexus 5 需要 500 毫秒,而模糊处理只需要另外 250 毫秒左右。
使用 Renderscript
Renderscript 提供了
ScriptIntrinsicBlur
这是高斯模糊滤镜。它具有良好的视觉质量,并且是 Android 上实际运行速度最快的。 Google 声称“通常比多线程 C 快 2-3 倍实现,并且通常比 Java 实现快 10 倍以上”。 Renderscript 非常复杂(使用最快的处理设备(GPU、ISP 等)等),并且还有 v8 支持库,使其兼容至 2.2。至少在理论上,通过我自己的测试和其他开发人员的报告,似乎不可能盲目使用 Renderscript,因为硬件/驱动程序碎片似乎会导致某些设备出现问题,即使具有更高的 SDK 级别(例如,我有4.1 Nexus S 存在问题),因此要小心并在很多设备上进行测试。下面是一个可以帮助您入门的简单示例:当使用 Gradle 的 v8 支持时,Google 特别推荐 "因为它们包含最新的改进",您只需需要在构建脚本中添加 2 行 并使用
android.support.v8.renderscript
与当前构建工具(更新了 android Gradle 插件 v14+ 的语法) a Nexus 5 - 将 RenderScript 与其他不同的 java 和 Renderscript 实现进行比较:
不同图片尺寸上每次模糊的平均运行时间
每秒可模糊的百万像素
每个值都是 250 轮的平均值。 RS_GAUSS_FAST 是 ScriptIntrinsicBlur(几乎总是最快的),其他以 RS_ 开头的大多是具有简单内核的卷积实现。 可以在此处找到算法的详细信息。这并不是纯粹的模糊,因为很大一部分是被测量的垃圾收集。这可以在这里看到(
ScriptIntrinsicBlur
在 100x100 图像上,大约 500 轮)尖峰是 gc。
您可以自己检查一下,基准应用程序位于 Playstore 中: BlurBenchmark
尽可能重用位图(如果优先级:性能 > 内存占用)
如果您需要多次模糊以实现实时模糊或类似效果,并且您的内存允许,请不要多次从可绘制对象中加载位图,而是保留它“缓存”在成员变量中。在这种情况下,请始终尝试使用相同的变量,以将垃圾收集保持在最低限度。
另请查看新的
inBitmap
选项当从文件或可绘制对象加载时,这将重用位图内存并节省垃圾收集时间。对于从锐利到模糊的混合,
简单而天真的方法就是使用 2 个 ImageView,其中一个是模糊的,然后进行 alpha 淡入淡出。但如果您想要更精致的外观,从清晰平滑过渡到模糊,请查看 Roman Nurik 的发布有关如何在他的 Muzei 应用程序中执行此操作的帖子。
基本上,他解释说,他预先模糊了一些具有不同模糊程度的帧,并将它们用作看起来非常平滑的动画中的关键帧。
Android Blur Guide 2016
with Showcase/Benchmark App and Source on GitHub.
Also check out the Blur framework I'm currently working on: 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.
Load and Use a down scaled Bitmap (for very blurry images)
Never use 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.
This will load the bitmap with
inSampleSize
8, so only 1/64 of the original image. Test whatinSampleSize
suits your needs, but keep it 2^n (2,4,8,...) to avoid degrading quality due to scaling. See Google doc for moreAnother huge 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.
Use Renderscript
Renderscript provides
ScriptIntrinsicBlur
which 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 level (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: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 script and use
android.support.v8.renderscript
with current build tools (updated syntax for android Gradle plugin v14+)Simple benchmark on a Nexus 5 - comparing RenderScript with different other java and Renderscript implementations:
The average runtime per blur on different pic sizes
Megapixels per sec that can be blurred
Each value is the avg of 250 rounds.
RS_GAUSS_FAST
isScriptIntrinsicBlur
(and nearly always the fastest), others that start withRS_
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 (ScriptIntrinsicBlur
on a 100x100 image with about 500 rounds)The spikes are gc.
You can check for yourself, the benchmark app is in the Playstore: BlurBenchmark
Reuses Bitmap wherever possible (if priority: 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
inBitmap
option when loading from a file or drawable which will reuse the bitmap memory and save garbage collection time.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.Basically he explains that he pre-blurs some frames with different blur extents and uses them as key frames in an animation that looks really smooth.
这是在黑暗中拍摄的照片,但您可以尝试缩小图像然后再次放大。这可以通过 Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) 来完成。确保并将过滤器参数设置为 true。它将以本机代码运行,因此可能会更快。
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.编辑(2014 年 4 月):这是一个问题/答案页面,看起来仍然有很多点击。我知道我的这篇文章总是会得到点赞。但如果您正在阅读本文,您需要意识到此处发布的答案(包括我的答案和已接受的答案)已经过时。如果您想立即实现高效模糊 >, 您应该使用 RenderScript 而不是 NDK 或 Java。 RenderScript 在 Android 2.2+ 上运行(使用 Android支持库),所以没有理由不使用它。
旧的答案如下,但要小心,因为它已经过时了。
对于未来² Google 员工,这里有一个算法,是我从 Yahel 的 Quasimondo 算法移植而来的,但使用的是 NDK。当然,这是基于 Yahel 的回答。但这是运行本机 C 代码,因此速度更快。快得多。比如说,快了 40 倍。
我发现使用 NDK 是在 Android 上完成所有图像操作的方式...一开始实现起来有点烦人(阅读关于使用 JNI 和 NDK 的精彩教程此处),但好多了,而且很多事情都接近实时。
作为参考,使用 Yahel 的 Java 函数,模糊半径为 10 的 480x532 像素图像花了 10 秒。但使用本机 C 版本花了 250 毫秒。而且我很确定它仍然可以进一步优化...我只是对java代码做了一个愚蠢的转换,可能有一些操作可以缩短,不想花太多时间重构整个事情。
然后像这样使用它(考虑一个名为 com.insert.your.package.ClassName 的类和一个名为 functionToBlur 的本机函数,如上面的代码所述):
它需要一个 RGB_8888 位图!
要使用 RGB_565 位图,请在传递参数之前创建转换后的副本(恶心),或者更改函数以使用新的
rgb565
类型而不是rgba
:问题是如果你这样做,你就无法再读取像素的
.red
、.green
和.blue
,你需要读取字节正确地,呃。当我以前需要它时,我这样做了:但是可能有一些不那么愚蠢的方法可以做到这一点。恐怕我不是一个低级的 C 程序员。
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 RenderScript instead 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.
The old answer follows, but beware as it's outdated.
For future² 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.
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.
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.
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):
It expects a RGB_8888 bitmap!
To use a RGB_565 bitmap, either create a converted copy before passing the parameter (yuck), or change the function to use a new
rgb565
type instead ofrgba
:The problem is that if you do that you can't read
.red
,.green
and.blue
of the pixel anymore, you need to read the byte properly, duh. When I needed that before, I did this:But there's probably some less dumb way of doing it. I'm not much of a low-level C coder, I'm afraid.
这段代码对我来说非常完美
This code is work perfect for me
现在,您可以使用 RenderScript 库中的 ScriptIntrinsicBlur 来快速模糊。 此处介绍如何访问 RenderScript API。以下是我制作的用于模糊视图和位图的类:
You can now use ScriptIntrinsicBlur from the RenderScript library to blur quickly. Here is how to access the RenderScript API. The following is a class I made to blur Views and Bitmaps:
这对我来说效果很好:How to Blur Images Efficiently with Android's RenderScript
This worked fine for me: How to Blur Images Efficiently with Android's RenderScript
我以前用过这个..
I used this before..
感谢@Yahel 提供的代码。
发布具有 alpha 通道模糊 支持的相同方法,因为我花了一些时间才使其正常工作,因此它可能会节省某人的时间:
Thanks @Yahel for the code.
Posting the same method with alpha channel blurring support as it took me some time to make it work correctly so it may save someone's time:
使用此处提到的渲染脚本 http://blog。 neteril.org/blog/2013/08/12/blurring-images-on-android/
Use Render Script as mentioned here http://blog.neteril.org/blog/2013/08/12/blurring-images-on-android/
这适用于所有需要增加 ScriptIntrinsicBlur 半径以获得更硬的高斯模糊的人。
您可以缩小图像并获得相同的结果,而不是将半径设置为超过 25。我编写了一个名为
GaussianBlur
的类。下面你可以看到如何使用,以及整个类的实现。用途:
类别:
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:
Class:
对于未来选择 NDK 方法的 Google 员工 - 我发现提到的可靠的 stackblur 算法。我在这里发现了不依赖 SSE 的 C++ 实现 - http://www .antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32 其中包含一些使用静态表的优化,例如:
我对多核系统的 stackblur 算法进行了修改 - 可以在这里找到http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for- .cpp/
随着越来越多的设备拥有 4 核 - 优化可带来 4 倍的速度优势。
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:
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.
尼古拉斯·波梅普的建议。我认为这个链接会有帮助: Android 设计的模糊效果
示例项目位于 github
Nicolas POMEPUY advice. I think this link will be helpful: Blur effect for Android design
Sample project at github
我们尝试在不同的答案中实现如上所述的 RenderScript 模糊。我们被限制使用 v8 RenderScript 版本,这给我们带来了很多麻烦。
我想分享我们肮脏的纯 Java 版本,该版本速度很慢,应该在单独的线程上完成,如果可能的话,应该在之前完成使用并因此持续存在。
该解决方案远非完美,但基于以下事实创建了合理的模糊效果:它在几乎不透明的“锐利”版本之上绘制同一图像的高度透明版本。 alpha 取决于到原点的距离。
您可以根据需要调整一些“幻数”。
我只是想与所有对 RenderScript v8 支持版本有问题的人分享这个“解决方案”。
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.
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.
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.
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.
在 i/o 2019 上,提出了以下解决方案:
On the i/o 2019 the following solution was presented:
截至 2024 年,RenderScript 不再是在 Android 中实现模糊的最佳方法。
Toolkit 即将取代 RenderScript。我能够在
~9-12 毫秒
内实现640x360
位图的模糊效果。以下是使用工具包实现位图模糊的步骤。
1- 添加依赖:
2- 然后调用Toolkit 中的blur 函数。请注意,您还可以执行许多其他操作,例如混合和卷积。
这里
sourceBitmap
是要模糊的位图,25
是半径。您可以尝试 1 到 25 之间的半径以获得所需的结果。As of 2024, RenderScript is no longer the best approach to achieve blurring in Android.
The Toolkit is around to replace the RenderScript. I was able to achieve blur on a bitmap of
640x360
in~9-12 milliseconds
.Here are the steps to follow to achieve the blur on a bitmap using the Toolkit.
1- Add the dependency:
2- And then call the blur function in the Toolkit. Note there are many other operations like blend and convolve which you can perform.
Here
sourceBitmap
is the bitmap to blur and25
is the radius. You can try out a radius from 1 to 25 to get the required results.对于那些仍然对 x86 芯片组上的 Renderscript 支持库有问题的人,请查看该库创建者的这篇文章。看起来他准备的修复程序并没有以某种方式进入构建工具 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
For those still having issues with Renderscript support library on x86 chipsets, please have a look at this post by the creator of the library. It looks like the fix he prepared didn't make it somehow to the Build Tools v20.0.0, so he provides the files to fix it manually and a brief explanation of how to do it.
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
来自 Mario Viviani 博客,可以在 17 Android 版本中使用此解决方案:
https://plus.google。 com/+MarioViviani/posts/fhuzYkji9zz
或
https://gist.github.com/Mariuxtheone/ 903c35b4927c0df18cf8
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
这是使用 RenderScript 的实时模糊叠加,看起来速度足够快。
https://github.com/mmin18/RealtimeBlurView
Here is a realtime blurring overlay using RenderScript, which seems to be fast enough.
https://github.com/mmin18/RealtimeBlurView
我发现稍微降低对比度、亮度和饱和度会让模糊图像变得更漂亮,所以我结合了堆栈溢出中的各种方法并制作了这个Blur Class 处理模糊图像,改变模糊图像的亮度、饱和度、对比度和大小。它还可以将图像从可绘制图像转换为位图,反之亦然。
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.