在过去的三天里,我一直在搜索一种内置的、硬件加速的方法,用android模糊位图。我偶然发现了一些解决方法,比如缩小位图并再次放大,但这种方法产生的结果质量很低,不适合我的图像识别要求。我还读到,使用着色器或JNI实现卷积是一个很好的方法,但我不敢相信安卓框架中没有内置的解决方案来实现这一非常常见的目的。目前,我已经用Java完成了一个自己编写的卷积实现,但速度慢得令人尴尬。我的问题是:
- 安卓框架中是否真的没有内置解决方案
- 如果没有:在实现和维护仍然合理复杂的情况下,加速卷积的最有效方法是什么?我们应该使用JNI、着色器还是完全不同的东西
我终于找到了一个合适的解决方案:
- RenderScript允许实现繁重的计算,这些计算对执行设备上可用的所有核心都是透明的。我得出的结论是,就性能和实现复杂性的合理平衡而言,这是一种比JNI或着色器更好的方法
- 从API 17级开始,API提供了ScriptIntrinsicBlur类。这正是我一直在寻找的,即一个高水平的、硬件加速的高斯模糊实现
- ScriptIntrinsicBlur现在是android支持库(v8)的一部分,支持Froyo及以上版本(API>8)。android开发人员关于支持RenderScript库的博客文章提供了一些关于如何使用它的基本提示
然而,关于ScriptIntrinsicBlur
类的文档非常罕见,我已经花了更多的时间来弄清楚正确的调用参数。对于模糊名为photo
的普通ARGB_8888
类型位图,它们是:
final RenderScript rs = RenderScript.create( myAndroidContext );
final Allocation input = Allocation.createFromBitmap( rs, photo, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT );
final Allocation output = Allocation.createTyped( rs, input.getType() );
final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create( rs, Element.U8_4( rs ) );
script.setRadius( myBlurRadius /* e.g. 3.f */ );
script.setInput( input );
script.forEach( output );
output.copyTo( photo );
可能最苛刻的要求是实时模糊,这意味着随着视图的变化,您会实时模糊。在这种情况下,模糊不应该超过10毫秒左右(在16ms/60fps上有一些游戏空间)才能看起来平滑。通过正确的设置,即使在不那么高端的设备上(galaxy s3甚至更慢)也可以实现这种效果。
以下是如何按重要性递减来提高性能:
-
使用缩小的图像:这大大减少了模糊的像素。此外,当你想要一个真正的模糊图像时,它也适用于你。图像加载和内存消耗也大大降低。
-
使用Renderscript ScriptIntrinsicBlur-截至2014年,Android中可能没有更好/更快的解决方案。我经常看到的一个错误是,Renderscript上下文没有被重用,而是在每次使用模糊算法时创建的。请注意,
RenderScript.create(this);
在Nexus 5上大约需要20ms,所以您需要避免这种情况。 -
重用位图:不要创建不必要的实例,始终使用相同的实例。当您需要非常快速的模糊时,垃圾收集起着重要的作用(收集一些位图需要10-20毫秒)。也只裁剪和模糊你需要的东西。
-
对于实时模糊,可能是因为上下文切换,不可能在另一个线程中模糊(即使使用线程池),只有主线程足够快,可以及时更新视图,我看到线程滞后100-300ms
关于更多提示,请参阅我的其他帖子https://stackoverflow.com/a/23119957/774398
顺便说一句。我在这个应用程序中做了一个简单的实时模糊:github,Playstore