安卓:快速位图模糊?

33

我已经搜索了过去三天,寻找在Android中使用内置的硬件加速方式来模糊位图。我遇到了一些解决方法,比如缩小位图并再次放大,但是这种方法会产生低质量的结果,无法满足我的图像识别需求。我还阅读了实现着色器或JNI卷积的好方法,但我不能相信Android框架中没有针对这个非常普遍的目的的内置解决方案。目前,我用Java编写了一个自己的卷积实现,但速度非常慢。我的问题是:

  • Android框架中真的没有内置的解决方案吗?
  • 如果没有:加速卷积的最有效方法是什么,其实现和维护的复杂性仍然合理?我们应该使用JNI、着色器还是完全不同的东西?
2个回答

74

我终于找到了一个合适的解决方案:

  • RenderScript 可以实现重型计算,并且能够透明地扩展到执行设备上的所有可用内核。我得出结论,就性能和实现复杂度的合理平衡而言,这比JNI或着色器更好。
  • 自API Level 17以来,API中提供了ScriptIntrinsicBlur 类。 这正是我一直在寻找的,也就是一个高级别的,硬件加速的高斯模糊实现。
  • ScriptIntrinsicBlur 现在是android support库(v8)的一部分,支持Froyo及以上(API>8)。关于支持RenderScript库的android开发者博客文章介绍了如何使用它的一些基本提示。

然而,对于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 );

2
@thV0ID:很好的回答。有没有办法使其向后兼容,可以在ICS中工作? - Bhavesh Patadiya
@BhaveshPatadiya:你可以使用RenderScript实现快速卷积并使用高斯核。有一个关于使用RenderScript实现卷积的例子可以在这里找到:https://dev59.com/wWLVa4cB1Zd3GeqPxIZf#10257994 - theV0ID
3
RS现在是兼容性库的一部分,renderscript-v8.jar文件。 - Gabor
1
渲染脚本的支持在此处解释:http://developer.android.com/tools/support-library/features.html - Martin Marconcini
1
这个可以运行,但在Galaxy S 3上模糊一个半径为25的137KB图像需要大约2秒钟。:( - Bitcoin Cash - ADA enthusiast
显示剩余2条评论

13

最具挑战性的需求可能是实时模糊,即当视图发生更改时实时模糊。在这种情况下,为了呈现流畅效果,模糊不应该超过10毫秒左右(留有16ms / 60fps的一些余地)。通过正确的设置,即使在不那么高端的设备上(Galaxy S3甚至更慢),也可以实现此效果。

以下是按重要性降序列出的提高性能的方法:

  1. 使用缩小后的图像:这样可以大幅减少要模糊的像素。当您需要真正模糊的图像时,它也可以为您工作。此外,图像加载和内存消耗也会大幅降低。

  2. 使用Renderscript ScriptIntrinsicBlur - 截至2014年,在Android中可能没有更好/更快的解决方案。我经常看到的一个错误是Renderscript上下文没有被重复使用,而是每次使用模糊算法时都会创建一个新的上下文。请注意, RenderScript.create(this); 在Nexus 5上需要约20毫秒,因此您需要避免这种情况。

  3. 重新使用Bitmaps:不要创建不必要的实例,始终使用相同的实例。当您需要真正快速的模糊效果时,垃圾收集起着重要作用(将花费10-20毫秒收集某些位图)。此外,请仅裁剪和模糊所需内容。

  4. 对于实时模糊,由于上下文切换的原因,不可能在另一个线程中进行模糊处理(即使使用线程池),只有主线程足够快才能及时更新视图,使用线程会导致100-300ms的延迟。

如需更多提示,请参见我的另一篇文章:https://dev59.com/5nI95IYBdhLWcg3w-DH0#23119957

顺便说一下,我在这个应用程序中做了一个简单的实时模糊:GitHubPlay Store


关于第二点,我并不认为它有良好的模糊质量(请参见此处:https://dev59.com/85Xfa4cB1Zd3GeqPkMqI)。此外,你真的建议Renderscript对象在整个应用程序生命周期中保持存在,并且永远不会调用“destroy()”吗? - android developer
嗨,我认为你已经在帖子中找到了质量差的错误(我本来想说我无法重现你的糟糕输出)。至于第二点:当然,如果你完成模糊处理,不要破坏上下文。只需记住创建上下文是昂贵的。 - Patrick
质量不好 - 我不确定原因(因为我使用了其他样本和教程代码),但我使用了“createTyped”,现在它可以工作了。如果您能在那里写一下我遇到这些问题的原因,那就太好了。关于“销毁上下文”,我是指RenderScript.create。如果我在应用程序的许多地方模糊图像,从来不销毁它应该没问题,对吧?此外,对于其参数使用哪个上下文是否重要? - android developer
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Patrick
好的,谢谢。但是,如果我将一个Activity作为参数传递给它,即使Activity被销毁后Renderscript对象实例仍然存在,那么这是否会导致内存泄漏? - android developer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接