关于Renderscript的理解
我的簡書同步發布: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文件:
- 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.r、in.g、in.b分別取出rgb顏色。我猜想uchar4是個結構體類型,本來想去官網查看一下,找了很久沒找到。找到的童鞋麻煩告訴我一下,我可以重新編輯這篇文章。但是就算沒找到,我們也可以理解的通,其實,如果從本質上來說,它并不復雜,r表示第一個字節,g表示第二個字節,b表示第三個字節。甚至我們可以可以猜得到,還有in.a表示透明度,然后我測試了一下,發現真的編譯通過。另外,從RenderScript Basics Tutorial這篇文章可以知道,還可以通過x、y、z、w分別取出對應的第1、2、3、4個字節。也就是說,in.x與in.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會檢查,如果不想同會拋異常。這里提到了Element,Elemtent是指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里。最后調用Allocation的copyTo函數把計算的結果轉入到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
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的关于Renderscript的理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SearchRecentsuggesti
- 下一篇: 关于音频焦点的运用