我的简书同步发布:RenderScript 让你的Android计算速度快的飞上天!

在上一篇文章Android自动手绘,圆你儿时画家梦! 中结尾提到,我将介绍提升轮廓提取速度相关内容,今天一起学习Android中的RenderScript。看完本文,你将学会如何使用并行计算技术,提高你的app中计算模块速度,尤其是提升图像处理中的复杂计算。

RenderScript介绍

根据Android官方网站的介绍:RenderScript是Android平台上用于运行计算密集任务的框架。RenderScript主要是面向数据并行计算,当然了,RenderScript中使用串行计算效率也很好。RenderScript是充分利用GPU,CPU的计算能力,由于不同的硬件对应的并行执行不同,RenderScript会编译2次,首先是我们的PC编译器编译到apk中,然后在apk安装的时候,再编译一次。这样的好处是,可以充分利用不同的硬件,我们编写的代码无需关心具体的硬件的不同,都能写出高性能的代码。 
RenderScript相关文档并不多,导致很难去学好RenderScript。但是其实用起来并不复杂,结合SDK中的两个例子和官方文档,基本可以入门了。

在使用RenderScript之前,请在eclipse的project.properties加上:

renderscript.target=18
renderscript.support.mode=true
  • 1
  • 2
  • 1
  • 2

Hello RenderScript

概念说太多没啥用,先来一段简单代码。需求很简单,我们需要将一张图片中的每个像素的颜色取反色,即分别将255减去当前像素点的R、G、B,得到的新的RGB作为当前像素点的新颜色。如果不用RenderScript,实现起来也非常简单,通过两个for循环,遍历每个像素点,然后替换像素就好,如下:


int width = mInBitmap.getWidth();
int height = mInBitmap.getHeight();
for (int x = 0; x < width; x++) {for (int y = 0; y < height; y++) {int color = mInBitmap.getPixel(x, y);int r=  255-(Color.red(color) ;int g=  255-(Color.green(color) ;int b=  255-(Color.blue(color) ;int c = Color.rgb(gray, gray, gray);mOutBitmap.setPixel(x, y, c);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这是使用普通Java代码实现,如果对一张较大的图执行这段代码,其耗时可想而知!再去看看RenderScript是如何实现相同的功能的: 
首先,在代码目录下(即包目录下)创建rs文件,取名可以任意,我们新建一个hello.rs文件:

#pragma version(1)
#pragma rs java_package_name(com.hc.renderscript)
uchar4 __attribute__((kernel)) invert(uchar4 in)
{uchar4 out = in;out.r =255- in.r;out.g = 255-in.g;out.b = 255-in.b;return out;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

看不懂?不要急!我们一行一行解释。仔细看会发现其实大部分跟C语言很像,首先#pragma是给编译器看的,#pragma version(1)是指版本号,目前只能选择1,没有更高的版本了。#pragma rs java_package_name(com.hc.renderscript)是告诉编译器,包的名称。因为每个rs文件都会自动生成对应的Java代码,比如,我们新建的hello.rs文件,会自动生成ScriptC_hello类,因此,我们需要在rs声明包的名称。接下来比较重要的关键字__attribute__((kernel)),它跟函数放在一起,用于声明这个函数是个RenderScript核心函数,而不是一个可调用的函数。什么意思呢?其实可以这样理解,就是这个函数不是个普通函数,是用于并行计算的函数。我们不能显式调用,它是RenderScript内部调用的函数。这时你可能会想,既然我们不能显式调用,那该怎么调用呢?别急,接下来为你揭晓。

我们继续看到invert函数,这个函数有个uchar4类型,不用想,肯定表示占用4个字节,每个字节表示的取值范围0~255。但是接下来的事情就很奇怪了,uchar4 in中直接可以用in.rin.gin.b分别取出rgb颜色。我猜想uchar4是个结构体类型,本来想去官网查看一下,找了很久没找到。找到的童鞋麻烦告诉我一下,我可以重新编辑这篇文章。但是就算没找到,我们也可以理解的通,其实,如果从本质上来说,它并不复杂,r表示第一个字节,g表示第二个字节,b表示第三个字节。甚至我们可以可以猜得到,还有in.a表示透明度,然后我测试了一下,发现真的编译通过。另外,从RenderScript Basics Tutorial这篇文章可以知道,还可以通过xyzw分别取出对应的第1、2、3、4个字节。也就是说,in.xin.r都是一个意思.好了这里不再继续纠结uchar4.

RenderScript的核心我们编写完成了,从上面rs文件的invert函数我们知道,这个函数只对具体一个像素点操作,可是我们的图片有width*height个像素点,我们需要这些像素点并行执行inver函数才能得到我们想要的结果。

我们再看看Java代码如何调用,使之并行计算。

    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mSrcImageView = (ImageView) findViewById(R.id.src);mDstImageView = (ImageView) findViewById(R.id.dst);mInBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);mOutBitmap = Bitmap.createBitmap(mInBitmap.getWidth(),mInBitmap.getHeight(), mInBitmap.getConfig());mSrcImageView.setImageBitmap(mInBitmap);RenderScript rs = RenderScript.create(this);mScript = new ScriptC_hello(rs);aIn = Allocation.createFromBitmap(rs, mInBitmap);aOut = Allocation.createFromBitmap(rs, mInBitmap);mScript.forEach_invert(aIn, aOut);aOut.copyTo(mOutBitmap);mDstImageView.setImageBitmap(mOutBitmap);rs.destroy();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

运行出来的结果: 
 
我们继续解释Java代码:先看到第13行,创建的是一个RenderScript对象。接下来是将我们编写的rs文件对应的自动生成的Java类(即ScriptC_hello类)初始化。到目前为止,这些都很好理解。紧接着是创建了两个Allocation对象,这个对象是干嘛用的呢?从名称可以看出,它是用于分配内存的,createFromBitmap根据Bitmap分配内存。为什么需要创建2个Allocation对象呢?这主要是在执行rs文件里面的并行函数时一个Allocation类型 aIn用于参数传入,一个Allocation类型 aOut用于计算结果输出。这两个Allocation的Element类型必须相同,在函数调用时RenderScript会检查,如果不想同会抛异常。这里提到了ElementElemtent是指Allocation里的一项。比如我们要处理的是Bitmap,则Element表示的类型是像素。做并行计算时,aIn对应的一个元素(Element)的计算结果会放入aOut对应的位置上。定位到代码:mScript.forEach_invert(aIn, aOut);我们的rs文件里面并没有写forEach_invert函数,但是却在ScriptC_hello 类里面生成了这个函数。请注意,我们编写了invert函数,正因为我们的invert函数加了__attribute__((kernel))关键字,所以,会生成forEach_invert函数,这个函数传入的参数aIn和aOut我们都清楚了,RenderScript会自动将aIn里的每个元素(Element)并行的去执行invert函数.得到的结果放入aOut里。最后调用AllocationcopyTo函数把计算的结果转入到Bitmap中。

另外,值得注意的是,__attribute__((kernel))修饰的函数,其形参该怎么写,为啥我们这里是uchar4而不是uchar3或者是uint32之类的呢?我们该怎么确定好这个参数呢?其实,这主要是跟我们的需求有关,你可以根据需求改动。比如我们的aIn里的元素是像素,而一个像素有RGBA占4个字节,因此我们写成uchar4作为形参。还有就是,后面还可以加形参uint32 x,uint32 y,uint32 z。这些是可选项,可以加也可以不加,不影响函数的调用,但是必须是uint32类型。

还有个可选函数init(),在rs文件里的这个函数会指初始化时调用,并且只会调用一次。

有时候我们希望返回的结果不止一个对象该怎么办?我们可以选择使用全局变量,在rs文件中声明全局变量,在rs文件的函数中把数据写入到rs文件的全局变量中。再从Java代码中读取rs的全局变量即可!那么在Java代码中该怎么读取和设置rs中的全局变量呢?答案是,rs文件对应生成的Java类会自动生成全局变量的get和set方法。比如,在hello.rs文件中定义了全局变量int myVar.自动生成的ScriptC_hello类中会自动生成函数:set_myVar(int v)get_myVar().这样就可以访问rs文件中的全局变量了。

最后

回到最开始说的,提升上一篇文章的轮廓提取速度。如果没有看过上一篇文章的请跳过,或者是前去: Android自动手绘,圆你儿时画家梦! 查看。我们去看看Sobel算法,主要分为2步,首先将彩色图转为灰度图,在CommenUtils类的toGrayscale函数中。然后再是调用Sobelsuanf ,在SobelUtils类的Sobel函数。先看看toGrayscale函数:这个函数是直接调用系统的函数,我们不去管。在Sobel函数中,有两个地方使用了两个for循环,显然可以通过RenderScript进行并行计算,提升速度。篇幅原因,具体的实现这里就不提了。

源码地址:RenderScript

关于Renderscript的理解相关推荐

  1. 理解和使用systrace

    理解和使用systrace. 一.介绍systrace systrace是Android4.1版本之后推出的,对系统Performance分析的工具. systrace的功能包括跟踪系统的I/O操作. ...

  2. RenderScript 让你的Android计算速度快的飞上天!

    我的简书同步发布:RenderScript 让你的Android计算速度快的飞上天! 在上一篇文章Android自动手绘,圆你儿时画家梦! 中结尾提到,我将介绍提升轮廓提取速度相关内容,今天一起学习A ...

  3. Android 开发艺术探索 看不懂对着书敲慢慢理解,设计模式之禅总结,平时记录的笔记,3w多次字防止丢失,留存。

    知识点1: 1.子线程为什么不允许访问ui因为android中的ui控件不是线程安全的. 2.为什么不给Ui加上锁的机制,第一点 会让ui访问的逻辑变得复杂,其次降低ui访问的效率. 3.List转化 ...

  4. 通用解题法——回溯算法(理解+练习)

    积累算法经验,积累解题方法--回溯算法,你必须要掌握的解题方法! 什么是回溯算法呢? 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就&quo ...

  5. stream流对象的理解及使用

    我的理解:用stream流式处理数据,将数据用一个一个方法去 . (点,即调用) 得到新的数据结果,可以一步达成. 有多种方式生成 Stream Source: 从 Collection 和数组 Co ...

  6. Linux shell 学习笔记(11)— 理解输入和输出(标准输入、输出、错误以及临时重定向和永久重定向)

    1. 理解输入和输出 1.1 标准文件描述符 Linux 系统将每个对象当作文件处理.这包括输入和输出进程.Linux 用文件描述符(file descriptor)来标识每个文件对象.文件描述符是一 ...

  7. java局部变量全局变量,实例变量的理解

    java局部变量全局变量,实例变量的理解 局部变量 可以理解为写在方法中的变量. public class Variable {//类变量static String name = "小明&q ...

  8. 智能文档理解:通用文档预训练模型

    预训练模型到底是什么,它是如何被应用在产品里,未来又有哪些机会和挑战? 预训练模型把迁移学习很好地用起来了,让我们感到眼前一亮.这和小孩子读书一样,一开始语文.数学.化学都学,读书.网上游戏等,在脑子 ...

  9. 熵,交叉熵,散度理解较为清晰

    20210511 https://blog.csdn.net/qq_35455503/article/details/105714287 交叉熵和散度 自己给自己编码肯定是最小的 其他的编码都会比这个 ...

最新文章

  1. eoiioe IE 和 firefox js 兼容问题
  2. poj1273(最大网络流问题模版)
  3. 图像滤波与OpenCV中的图像平滑处理
  4. word文档无法连接服务器,sql数据库无法连接服务器解决办法绝对有效
  5. Core Animation放大缩小;CAKeyframeAnimation
  6. idea插件GsonFormat的使用
  7. 循环冗余校验码CRC,求解步骤
  8. 【数据库】数据库单表对比
  9. php怎么计算图片的大小,php 根据比例计算图片缩放尺寸函数的用法
  10. 【瑞星系统】促销更新【4】
  11. Web安全测试实战之测试HTTP方法
  12. cubemx stm32 配置两个串口_STM32CubeMX系列教程5:串行通信(USART)
  13. python查看字符编码值_Python 字符编码
  14. AD之前的电压跟随器可以不用吗?
  15. wsimport 用法详解
  16. IDEA添加快捷输入
  17. 云计算就业方向有哪些 未来的发展前景怎么样
  18. 转发小程序php,微信小程序 转发功能的实现
  19. word文字上下间距怎么调_word上下文字间距 word字体上下间距怎么调整
  20. 网页唤起QQ在线聊天

热门文章

  1. [Python图像识别] 四十九.图像生成之什么是生成对抗网络GAN?基础原理和代码普及
  2. 【Python数据挖掘课程】六.Numpy、Pandas和Matplotlib包基础知识
  3. 763. Partition Labels 划分字母区间
  4. 2013\National _C_C++_A\2.骰子迷题
  5. python实现中文字符繁体和简体中文转换
  6. Stark 组件:快速开发神器 —— 页面显示
  7. 信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1099:第n小的质数
  8. 深度学习——02、深度学习入门 1-7
  9. TMS320F28335的SCI通信-FIFO中断通信实验
  10. 【Linux】一步一步学Linux——dpkg命令(269)