RenderScript 讓你的Android計(jì)算速度快的飛上天!

我的CSDN博客同步發(fā)布:RenderScript 讓你的Android計(jì)算速度快的飛上天榜配!

在上一篇文章Android自動(dòng)手繪,圓你兒時(shí)畫(huà)家夢(mèng)吕晌! 中結(jié)尾提到蛋褥,我將介紹提升輪廓提取速度相關(guān)內(nèi)容,今天一起學(xué)習(xí)Android中的RenderScript睛驳±有模看完本文,你將學(xué)會(huì)如何使用并行計(jì)算技術(shù)乏沸,提高你的app中計(jì)算模塊速度淫茵,尤其是提升圖像處理中的復(fù)雜計(jì)算。

RenderScript介紹

根據(jù)Android官方網(wǎng)站的介紹:RenderScript是Android平臺(tái)上用于運(yùn)行計(jì)算密集任務(wù)的框架蹬跃。RenderScript主要是面向數(shù)據(jù)并行計(jì)算匙瘪,當(dāng)然了,RenderScript中使用串行計(jì)算效率也很好蝶缀。RenderScript是充分利用GPU丹喻,CPU的計(jì)算能力,由于不同的硬件對(duì)應(yīng)的并行執(zhí)行不同翁都,RenderScript會(huì)編譯2次碍论,首先是我們的PC編譯器編譯到apk中,然后在apk安裝的時(shí)候柄慰,再編譯一次鳍悠。這樣的好處是,可以充分利用不同的硬件坐搔,我們編寫(xiě)的代碼無(wú)需關(guān)心具體的硬件的不同藏研,都能寫(xiě)出高性能的代碼。
RenderScript相關(guān)文檔并不多概行,導(dǎo)致很難去學(xué)好RenderScript蠢挡。但是其實(shí)用起來(lái)并不復(fù)雜,結(jié)合SDK中的兩個(gè)例子和官方文檔占锯,基本可以入門(mén)了袒哥。

在使用RenderScript之前,請(qǐng)?jiān)趀clipse的project.properties加上:

renderscript.target=18
renderscript.support.mode=true

Hello RenderScript

概念說(shuō)太多沒(méi)啥用消略,先來(lái)一段簡(jiǎn)單代碼堡称。需求很簡(jiǎn)單,我們需要將一張圖片中的每個(gè)像素的顏色取反色艺演,即分別將255減去當(dāng)前像素點(diǎn)的R却紧、G桐臊、B,得到的新的RGB作為當(dāng)前像素點(diǎn)的新顏色晓殊。如果不用RenderScript断凶,實(shí)現(xiàn)起來(lái)也非常簡(jiǎn)單,通過(guò)兩個(gè)for循環(huán)巫俺,遍歷每個(gè)像素點(diǎn)认烁,然后替換像素就好,如下:


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);
     }
}

這是使用普通java代碼實(shí)現(xiàn)介汹,如果對(duì)一張較大的圖執(zhí)行這段代碼却嗡,其耗時(shí)可想而知!再去看看RenderScript是如何實(shí)現(xiàn)相同的功能的:
首先嘹承,在代碼目錄下(即包目錄下)創(chuàng)建rs文件窗价,取名可以任意,我們新建一個(gè)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;

}

看不懂叹卷?不要急撼港!我們一行一行解釋。仔細(xì)看會(huì)發(fā)現(xiàn)其實(shí)大部分跟C語(yǔ)言很像骤竹,首先#pragma是給編譯器看的,#pragma version(1)是指版本號(hào)帝牡,目前只能選擇1,沒(méi)有更高的版本了瘤载。#pragma rs java_package_name(com.hc.renderscript)是告訴編譯器否灾,包的名稱(chēng)卖擅。因?yàn)槊總€(gè)rs文件都會(huì)自動(dòng)生成對(duì)應(yīng)的Java代碼鸣奔,比如,我們新建的hello.rs文件惩阶,會(huì)自動(dòng)生成ScriptC_hello類(lèi)挎狸,因此,我們需要在rs聲明包的名稱(chēng)断楷。接下來(lái)比較重要的關(guān)鍵字__attribute__((kernel))锨匆,它跟函數(shù)放在一起,用于聲明這個(gè)函數(shù)是個(gè)RenderScript核心函數(shù)冬筒,而不是一個(gè)可調(diào)用的函數(shù)恐锣。什么意思呢?其實(shí)可以這樣理解舞痰,就是這個(gè)函數(shù)不是個(gè)普通函數(shù)土榴,是用于并行計(jì)算的函數(shù)。我們不能顯式調(diào)用响牛,它是RenderScript內(nèi)部調(diào)用的函數(shù)玷禽。這時(shí)你可能會(huì)想赫段,既然我們不能顯式調(diào)用,那該怎么調(diào)用呢矢赁?別急糯笙,接下來(lái)為你揭曉。

我們繼續(xù)看到invert函數(shù)撩银,這個(gè)函數(shù)有個(gè)uchar4類(lèi)型给涕,不用想,肯定表示占用4個(gè)字節(jié)额获,每個(gè)字節(jié)表示的取值范圍0~255稠炬。但是接下來(lái)的事情就很奇怪了,uchar4 in中直接可以用in.r咪啡、in.g首启、in.b分別取出rgb顏色。我猜想uchar4是個(gè)結(jié)構(gòu)體類(lèi)型撤摸,本來(lái)想去官網(wǎng)查看一下毅桃,找了很久沒(méi)找到。找到的童鞋麻煩告訴我一下准夷,我可以重新編輯這篇文章钥飞。但是就算沒(méi)找到,我們也可以理解的通衫嵌,其實(shí)读宙,如果從本質(zhì)上來(lái)說(shuō),它并不復(fù)雜楔绞,r表示第一個(gè)字節(jié)结闸,g表示第二個(gè)字節(jié),b表示第三個(gè)字節(jié)酒朵。甚至我們可以可以猜得到桦锄,還有in.a表示透明度,然后我測(cè)試了一下蔫耽,發(fā)現(xiàn)真的編譯通過(guò)结耀。另外,從RenderScript Basics Tutorial這篇文章可以知道匙铡,還可以通過(guò)x图甜、yz鳖眼、w分別取出對(duì)應(yīng)的第1黑毅、2、3具帮、4個(gè)字節(jié)博肋。也就是說(shuō)低斋,in.xin.r都是一個(gè)意思.好了這里不再繼續(xù)糾結(jié)uchar4.

RenderScript的核心我們編寫(xiě)完成了,從上面rs文件的invert函數(shù)我們知道匪凡,這個(gè)函數(shù)只對(duì)具體一個(gè)像素點(diǎn)操作膊畴,可是我們的圖片有width*height個(gè)像素點(diǎn),我們需要這些像素點(diǎn)并行執(zhí)行inver函數(shù)才能得到我們想要的結(jié)果病游。

我們?cè)倏纯碕ava代碼如何調(diào)用唇跨,使之并行計(jì)算。

    @Override
    protected 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();
    }

運(yùn)行出來(lái)的結(jié)果:

反色效果

我們繼續(xù)解釋Java代碼:先看到第13行衬衬,創(chuàng)建的是一個(gè)RenderScript對(duì)象买猖。接下來(lái)是將我們編寫(xiě)的rs文件對(duì)應(yīng)的自動(dòng)生成的Java類(lèi)(即ScriptC_hello類(lèi))初始化。到目前為止滋尉,這些都很好理解玉控。緊接著是創(chuàng)建了兩個(gè)Allocation對(duì)象,這個(gè)對(duì)象是干嘛用的呢狮惜?從名稱(chēng)可以看出高诺,它是用于分配內(nèi)存的,createFromBitmap根據(jù)Bitmap分配內(nèi)存碾篡。為什么需要?jiǎng)?chuàng)建2個(gè)Allocation對(duì)象呢虱而?這主要是在執(zhí)行rs文件里面的并行函數(shù)時(shí)一個(gè)Allocation類(lèi)型 aIn用于參數(shù)傳入,一個(gè)Allocation類(lèi)型 aOut用于計(jì)算結(jié)果輸出开泽。這兩個(gè)Allocation的Element類(lèi)型必須相同牡拇,在函數(shù)調(diào)用時(shí)RenderScript會(huì)檢查,如果不想同會(huì)拋異常穆律。這里提到了Element惠呼,Elemtent是指Allocation里的一項(xiàng)。比如我們要處理的是Bitmap众旗,則Element表示的類(lèi)型是像素罢杉。做并行計(jì)算時(shí)趟畏,aIn對(duì)應(yīng)的一個(gè)元素(Element)的計(jì)算結(jié)果會(huì)放入aOut對(duì)應(yīng)的位置上贡歧。定位到代碼:mScript.forEach_invert(aIn, aOut);我們的rs文件里面并沒(méi)有寫(xiě)forEach_invert函數(shù),但是卻在ScriptC_hello 類(lèi)里面生成了這個(gè)函數(shù)赋秀。請(qǐng)注意利朵,我們編寫(xiě)了invert函數(shù),正因?yàn)槲覀兊膇nvert函數(shù)加了__attribute__((kernel))關(guān)鍵字猎莲,所以绍弟,會(huì)生成forEach_invert函數(shù),這個(gè)函數(shù)傳入的參數(shù)aIn和aOut我們都清楚了著洼,RenderScript會(huì)自動(dòng)將aIn里的每個(gè)元素(Element)并行的去執(zhí)行invert函數(shù).得到的結(jié)果放入aOut里樟遣。最后調(diào)用AllocationcopyTo函數(shù)把計(jì)算的結(jié)果轉(zhuǎn)入到Bitmap中而叼。

另外,值得注意的是豹悬,__attribute__((kernel))修飾的函數(shù)葵陵,其形參該怎么寫(xiě),為啥我們這里是uchar4而不是uchar3或者是uint32之類(lèi)的呢瞻佛?我們?cè)撛趺创_定好這個(gè)參數(shù)呢脱篙?其實(shí),這主要是跟我們的需求有關(guān)伤柄,你可以根據(jù)需求改動(dòng)绊困。比如我們的aIn里的元素是像素,而一個(gè)像素有RGBA占4個(gè)字節(jié)适刀,因此我們寫(xiě)成uchar4作為形參秤朗。還有就是,后面還可以加形參uint32 x,uint32 y,uint32 z笔喉。這些是可選項(xiàng)川梅,可以加也可以不加,不影響函數(shù)的調(diào)用,但是必須是uint32類(lèi)型然遏。

還有個(gè)可選函數(shù)init()贫途,在rs文件里的這個(gè)函數(shù)會(huì)指初始化時(shí)調(diào)用,并且只會(huì)調(diào)用一次待侵。

有時(shí)候我們希望返回的結(jié)果不止一個(gè)對(duì)象該怎么辦丢早?我們可以選擇使用全局變量,在rs文件中聲明全局變量秧倾,在rs文件的函數(shù)中把數(shù)據(jù)寫(xiě)入到rs文件的全局變量中怨酝。再?gòu)腏ava代碼中讀取rs的全局變量即可!那么在Java代碼中該怎么讀取和設(shè)置rs中的全局變量呢那先?答案是农猬,rs文件對(duì)應(yīng)生成的Java類(lèi)會(huì)自動(dòng)生成全局變量的get和set方法。比如售淡,在hello.rs文件中定義了全局變量int myVar.自動(dòng)生成的ScriptC_hello類(lèi)中會(huì)自動(dòng)生成函數(shù):set_myVar(int v)get_myVar().這樣就可以訪問(wèn)rs文件中的全局變量了斤葱。

最后

回到最開(kāi)始說(shuō)的,提升上一篇文章的輪廓提取速度揖闸。如果沒(méi)有看過(guò)上一篇文章的請(qǐng)?zhí)^(guò)揍堕,或者是前去: Android自動(dòng)手繪,圓你兒時(shí)畫(huà)家夢(mèng)汤纸! 查看衩茸。我們?nèi)タ纯碨obel算法,主要分為2步贮泞,首先將彩色圖轉(zhuǎn)為灰度圖楞慈,在CommenUtils類(lèi)的toGrayscale函數(shù)中幔烛。然后再是調(diào)用Sobelsuanf ,在SobelUtils類(lèi)的Sobel函數(shù)。先看看toGrayscale函數(shù):這個(gè)函數(shù)是直接調(diào)用系統(tǒng)的函數(shù)囊蓝,我們不去管说贝。在Sobel函數(shù)中,有兩個(gè)地方使用了兩個(gè)for循環(huán)慎颗,顯然可以通過(guò)RenderScript進(jìn)行并行計(jì)算乡恕,提升速度。篇幅原因俯萎,具體的實(shí)現(xiàn)這里就不提了傲宜。

源碼地址:RenderScript

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市夫啊,隨后出現(xiàn)的幾起案子函卒,更是在濱河造成了極大的恐慌,老刑警劉巖撇眯,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件报嵌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡熊榛,警方通過(guò)查閱死者的電腦和手機(jī)锚国,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)玄坦,“玉大人血筑,你說(shuō)我怎么就攤上這事〖彘梗” “怎么了豺总?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)择懂。 經(jīng)常有香客問(wèn)我喻喳,道長(zhǎng),這世上最難降的妖魔是什么困曙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任表伦,我火速辦了婚禮,結(jié)果婚禮上赂弓,老公的妹妹穿的比我還像新娘绑榴。我一直安慰自己,他們只是感情好盈魁,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著窃诉,像睡著了一般杨耙。 火紅的嫁衣襯著肌膚如雪赤套。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天珊膜,我揣著相機(jī)與錄音容握,去河邊找鬼。 笑死车柠,一個(gè)胖子當(dāng)著我的面吹牛剔氏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竹祷,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼谈跛,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了塑陵?” 一聲冷哼從身側(cè)響起感憾,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎令花,沒(méi)想到半個(gè)月后阻桅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡兼都,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年嫂沉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扮碧。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡输瓜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芬萍,到底是詐尸還是另有隱情尤揣,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布柬祠,位于F島的核電站北戏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏漫蛔。R本人自食惡果不足惜嗜愈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望莽龟。 院中可真熱鬧蠕嫁,春花似錦、人聲如沸毯盈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至赘阀,卻和暖如春益缠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芦倒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓胰伍,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親酸休。 傳聞我的和親對(duì)象是個(gè)殘疾皇子骂租,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 一、入口 https://developer.android.google.cn -> API指南 -> 庫(kù) ->...
    撿之閱讀 9,255評(píng)論 4 8
  • [譯]原文鏈接 RenderScript是運(yùn)行在Android系統(tǒng)上的一個(gè)高性能密集計(jì)算框架雨席。RenderScri...
    stefanli閱讀 8,424評(píng)論 0 13
  • 最新剛好遇到個(gè)需求是要求做高斯模糊的菩咨,雖然現(xiàn)有已經(jīng)有一些框架可以提供調(diào)用,但關(guān)鍵還是要理解原理才行陡厘,思考的過(guò)程才是...
    Hohohong閱讀 13,542評(píng)論 1 37
  • 其實(shí)有關(guān) android 下實(shí)現(xiàn)圖片模糊的文章有很多抽米,大多都是使用 renderscript 內(nèi)置的 Script...
    Dajavu閱讀 9,757評(píng)論 12 39
  • 如果你問(wèn)我,幸福是什么糙置?我想大概就是8月份的樣子吧云茸。 這個(gè)月我讀了11本書(shū),這個(gè)月我反思了自己過(guò)去8年的經(jīng)歷谤饭,這個(gè)...
    聽(tīng)風(fēng)看樹(shù)望天空閱讀 288評(píng)論 0 1