图像模糊处理RenderScript

来源:互联网 发布:算法爱好者 编辑:程序博客网 时间:2024/05/16 15:58

在Android中,我们可以实现很多很酷的处理图片的效果。在2014年某次会议的讲演《图像的魔力》中,我介绍了其中的一部分。其中的一项技术是如何模糊图像,示例代码是使用RenderScript实现的,因为在Android中没有内置的可使用的简单的API。在这个系列中,我们将着眼于RenderScript模糊技术和JAVA实现模糊功能。我们还将进行一些基准测试,以了解每种方案的运行情况,并探讨获取最佳性能的可行方法。

让我们先从实现一个简单的可以运行的例子开始,使用RenderScript!对于没有使用过RenderScript的开发者来说,这是一个让人心生恐惧的设想,因为RenderScript真的是很难,是不是?是的,的确是。不过它也有一些事情真的很简单,而模糊图像就是其中之一。

对于不熟悉RenderScript的人来说,他是在API11中引入的,并且有一个compat库,提供RenderScript给API8及以后的版本。它本质上是一个面向图形的本地计算框架。RenderScript引擎在运行期会选择最合适的处理器(CPU或者GPU核心,多核处理器间可以分解原子操作)来执行请求的操作。本地语法基于C99,与OpenCL, CUDA, and GLSL的API相似。

如果这听起来很可怕,请稍等一下,因为我们正要简化整个过程。因为它是一个框架,允许我们创建自定义的内核来实现过滤与处理,它内置了不少可以使用的内核,其中的一个允许我们模糊图像。

后面的实例代码基于API17及更高版本开发,我已经决定不使用compat库,因为在本文写作的时候,Android Studio还不支持。此外,我们使用的模糊核心在API17后才引入,所以有最小SDK版本为17的需求。

让我们深入到一个简单的例子,这里有一个非常简单的RelativeLayout,包含了一个ImageView,上层还有个TextView:

<?xmlversion="1.0"encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:gravity="center"    android:orientation="vertical">         <ImageView        android:id="@+id/image"        android:src="@drawable/broadstairs"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:scaleType="matrix"        android:layout_centerInParent="true"/>     <TextView        android:id="@+id/text"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/hello"        android:layout_centerHorizontal="true"        android:textColor="<a href="http://www.jobbole.com/members/android/"rel="nofollow">@android</a>:color/white"        android:layout_marginTop="300dp"        android:textStyle="bold"        android:textSize="48sp"/></RelativeLayout>

part1-168x300

我们想要做的效果是模糊ImageView内位于TextView显示区域的图像,有效的模糊ImageView后的区域。我们最终使用的技术是拿到位于TextView区域的图像副本进行模糊,然后再将模糊后的副本设定为TextView的背景。

我刻意的设计了这种布局,使图像显示实际大小,并从显示屏的左上方开始。这样让随后的位置计算简单些,而且这里讨论的是模糊技术而不是图像定位的数学算法。尝试设定ImageView的属性android:scaleType=”center”,就会发现定位出现错乱。

此处有一个方法,它有3个参数,一个位图(已在ImageView里获得),一个视图(这是我们的TextView,但是该技术适用于任意的视图类型,所以我们将在方法中进行支持),以及一个半径用来控制模糊的程度。

private void blur(Bitmap bkg, View view, floatradius) {    Bitmap overlay = Bitmap.createBitmap(        view.getMeasuredWidth(),        view.getMeasuredHeight(),        Bitmap.Config.ARGB_8888);     Canvas canvas =new Canvas(overlay);    canvas.drawBitmap(bkg, -view.getLeft(),        -view.getTop(),null);       RenderScript rs = RenderScript.create(this);     Allocation overlayAlloc = Allocation.createFromBitmap(        rs, overlay);     ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(        rs, overlayAlloc.getElement());       blur.setInput(overlayAlloc);     blur.setRadius(radius);     blur.forEach(overlayAlloc);     overlayAlloc.copyTo(overlay);     view.setBackground(newBitmapDrawable(        getResources(), overlay));       rs.destroy(); }

我们要做的第一件事是创建一个新的位图对象,用来保存想要模糊的图像区域的副本。它的大小将会是我们视图的尺寸(2-5行)。

现在我们将空的位图包装到画布中,我们可以在上面绘图(7行)。

接下来的步骤我称为”缓存剪切”,我们拷贝位图中的处于视图显示区域内的那部分图像(9-10行)。

接下来我们必须做的事情是构建一个RenderScript对象上下文,在其中我们执行模糊操作(12行)。

RenderScript是一个本地环境,它在自己的内存空间内运行,所以我们不能简单的传递位图引用,我们需要在JAVA和RenderScript内存区域进行序列化。这使用了一个分配实例来完成,这是在RenderScript内存区域中创建和引用对象的方式,为我们的位图创建一个分配会将位图的内容拷贝的分配区域中(14-15行)。

现在我们创建一个 ScriptIntrinsicBlur实例,它创建合适的RenderScript模糊脚本,并且是脚本对象的Java接口,它让我们能够控制它。(17-18行)。

脚本的输入定义了我们需要执行模糊的位图源(20行)。

现在我们设置半径来控制模糊强度(22行)。

forEach方法执行模糊操作,参数代表了结果的输出位置。我们将它写回源分配地址以减少创建的对象数量(24行)。

现在模糊完成了,但是我们必须把模糊后的图像拷贝回JAVA内存区域(26行)。

最后将模糊后的位图包装到BitmapDrawable内,并设置为视图的背景(28-29行)。

我们已经使用完RenderScript上下文,清理掉。这样做也将自动删除我们的分配(31行)。

这就是我们完整的基本模糊逻辑,但目前还不清楚什么地方和什么时候我们需要调用此方法。不幸的是关于这个问题不能简单的进行回答,所以我们将在接下来的文章中讨论。

平时我喜欢每篇文章都发布相关工作代码,但是在本例中,我们还没达到有一个可运行的完整的端到端的示例的程度。我保证在接下来的文章中进行调整。

0 0
原创粉丝点击