最新剛好遇到個需求是要求做高斯模糊的软舌,雖然現(xiàn)有已經(jīng)有一些框架可以提供調(diào)用殉疼,但關(guān)鍵還是要理解原理才行梯浪,思考的過程才是最重要的境肾,高斯模糊的原理則與圖像卷積濾波有些關(guān)系山林。
目錄大綱
1.圖像卷積濾波與高斯模糊
2.高斯模糊實現(xiàn)與優(yōu)化
3.RenderScript的介紹與使用
一.圖像卷積濾波與高斯模糊
1.1 圖像卷積濾波
對于濾波來說谈跛,它可以說是圖像處理最基本的方法泞遗,可以產(chǎn)生很多不同的效果。以下圖來說
圖中矩陣分別為二維原圖像素矩陣歼捏,二維的圖像濾波矩陣(也叫做卷積核,下面講到濾波器和卷積核都是同個概念),以及最后濾波后的新像素圖荠藤。對于原圖像的每一個像素點,計算它的領(lǐng)域像素和濾波器矩陣的對應(yīng)元素的成績获高,然后加起來哈肖,作為當(dāng)前中心像素位置的值,這樣就完成了濾波的過程了念秧。
可以看到淤井,一個原圖像通過一定的卷積核處理后就可以變換為另一個圖像了。而對于濾波器來說,也是有一定的規(guī)則要求的币狠。
- ① 濾波器的大小應(yīng)該是奇數(shù)游两,這樣它才有一個中心,例如3x3漩绵,5x5或者7x7贱案。有中心了,也有了半徑的稱呼止吐,例如5x5大小的核的半徑就是2宝踪。
- ② 濾波器矩陣所有的元素之和應(yīng)該要等于1,這是為了保證濾波前后圖像的亮度保持不變碍扔。當(dāng)然了瘩燥,這不是硬性要求了。
- ③ 如果濾波器矩陣所有元素之和大于1不同,那么濾波后的圖像就會比原圖像更亮厉膀,反之,如果小于1二拐,那么得到的圖像就會變暗站蝠。如果和為0,圖像不會變黑卓鹿,但也會非常暗菱魔。
- ④ 對于濾波后的結(jié)構(gòu),可能會出現(xiàn)負數(shù)或者大于255的數(shù)值吟孙。對這種情況澜倦,我們將他們直接截斷到0和255之間即可。對于負數(shù)杰妓,也可以取絕對值藻治。
1.2 卷積核一些用法
既然知道濾波器可以用來對原圖進行操作,那么巷挥,有沒有一些比較具體的例子桩卵。文中卷積核相關(guān)圖片來源于網(wǎng)絡(luò)
1.2.1 空卷積核
可以看到,這個濾波器啥也沒有做倍宾,得到的圖像和原圖是一樣的雏节。因為只有中心點的值是1。鄰域點的權(quán)值都是0高职,對濾波后的取值沒有任何影響钩乍。
1.2.2 圖像銳化濾波器
圖像的銳化和邊緣檢測很像,首先找到邊緣怔锌,然后把邊緣加到原來的圖像上面寥粹,這樣就強化了圖像的邊緣变过,使圖像看起來更加銳利了。這兩者操作統(tǒng)一起來就是銳化濾波器了涝涤,也就是在邊緣檢測濾波器的基礎(chǔ)上媚狰,再在中心的位置加1,這樣濾波后的圖像就會和原始的圖像具有同樣的亮度了阔拳,但是會更加銳利哈雏。
我們把核加大,就可以得到更加精細的銳化效果
1.2.3 浮雕
浮雕濾波器可以給圖像一種3D陰影的效果衫生。只要將中心一邊的像素減去另一邊的像素就可以了裳瘪。這時候,像素值有可能是負數(shù)罪针,我們將負數(shù)當(dāng)成陰影彭羹,將正數(shù)當(dāng)成光,然后我們對結(jié)果圖像加上128的偏移泪酱。這時候派殷,圖像大部分就變成灰色了。
下面是45度的浮雕濾波器
我們只要加大濾波器墓阀,就可以得到更加夸張的效果了
1.2.4 均值模糊
我們可以將當(dāng)前像素和它的四鄰域的像素一起取平均毡惜,然后再除以5,或者直接在濾波器的5個地方取0.2的值即可斯撮,如下圖:
可以看到经伙,這個模糊還是比較溫柔的,我們可以把濾波器變大勿锅,這樣就會變得粗暴了:注意要將和再除以13.
可以看到均值模糊也可以做到讓圖片模糊帕膜,但是它的模糊不是很平滑,不平滑主要在于距離中心點很遠的點與距離中心點很近的所帶的權(quán)重值相同溢十,產(chǎn)生的模糊效果一樣
而想要做到平滑垮刹,讓權(quán)重值跟隨中心點位置距離不同而不同,則可以利用正態(tài)分布(中間大张弛,兩端谢牡洹)這個特點來實現(xiàn)。
1.3 高斯模糊
有了前面的知識吞鸭,我們知道如果要想實現(xiàn)高斯模糊的特點寺董,則需要通過構(gòu)建對應(yīng)的權(quán)重矩陣來進行濾波。
1.3.1 正態(tài)分布
正態(tài)分布中瞒大,越接近中心點螃征,取值越大搪桂,越遠離中心透敌,取值越小盯滚。
計算平均值的時候,我們只需要將"中心點"作為原點酗电,其他點按照其在正態(tài)曲線上的位置魄藕,分配權(quán)重,就可以得到一個加權(quán)平均值撵术。正態(tài)分布顯然是一種可取的權(quán)重分配模式背率。
1.3.2 高斯函數(shù)
如何反映出正態(tài)分布?則需要使用高函數(shù)來實現(xiàn)嫩与。
上面的正態(tài)分布是一維的寝姿,而對于圖像都是二維的,所以我們需要二維的正態(tài)分布划滋。
正態(tài)分布的密度函數(shù)叫做"高斯函數(shù)"(Gaussian function)饵筑。它的一維形式是:
其中,μ是x的均值处坪,σ是x的方差根资。因為計算平均值的時候,中心點就是原點同窘,所以μ等于0玄帕。
根據(jù)一維高斯函數(shù),可以推導(dǎo)得到二維高斯函數(shù):
有了這個函數(shù) 想邦,就可以計算每個點的權(quán)重了裤纹。
1.3.3 獲取權(quán)重矩陣
假定中心點的坐標(biāo)是(0,0),那么距離它最近的8個點的坐標(biāo)如下:
更遠的點以此類推丧没。
為了計算權(quán)重矩陣服傍,需要設(shè)定σ的值。假定σ=1.5骂铁,則模糊半徑為1的權(quán)重矩陣如下:
這9個點的權(quán)重總和等于0.4787147吹零,如果只計算這9個點的加權(quán)平均,還必須讓它們的權(quán)重之和等于1拉庵,因此上面9個值還要分別除以0.4787147灿椅,得到最終的權(quán)重矩陣。
除以總值這個過程也叫做”歸一問題“
目的是讓濾鏡的權(quán)重總值等于1钞支。否則的話茫蛹,使用總值大于1的濾鏡會讓圖像偏亮,小于1的濾鏡會讓圖像偏暗烁挟。
1.3.4 計算模糊值
有了權(quán)重矩陣婴洼,就可以計算高斯模糊的值了。
假設(shè)現(xiàn)有9個像素點撼嗓,灰度值(0-255)如下:
每個點乘以自己的權(quán)重值:
得到
將這9個值加起來柬采,就是中心點的高斯模糊的值欢唾。
對所有點重復(fù)這個過程,就得到了高斯模糊后的圖像粉捻。對于彩色圖片來說礁遣,則需要對RGB三個通道分別做高斯模糊。
1.3.5 邊界值問題
既然是根據(jù)權(quán)重矩陣來進行處理的
如果一個點處于邊界肩刃,周邊沒有足夠的點祟霍,怎么辦?
- ① 對稱處理盈包,就是把已有的點拷貝到另一面的對應(yīng)位置沸呐,模擬出完整的矩陣。
- ② 賦0呢燥,想象圖像是無限長的圖像的一部分垂谢,除了我們給定值的部分,其他部分的像素值都是0
- ③ 賦邊界值疮茄,想象圖像是無限制長滥朱,但是默認(rèn)賦值的不是0而是對應(yīng)邊界點的值
二. 高斯模糊實現(xiàn)與優(yōu)化
理解原理之后則可以做進一步的實現(xiàn),從Android上來說力试。
2.1 構(gòu)建權(quán)重矩陣
public static double[][] getMatrix(int radius){
//根據(jù)radius創(chuàng)建權(quán)重矩陣.
int size = 2 * radius + 1;
double[][] matrix = new double[size][size];
double sigama = (double) radius / 3;
double sigamaDouble = 2 * sigama * sigama;
double sigamaPi = Math.PI * sigamaDouble;
int row = 0;
double sum = 0;
for(int i = -radius ; i <= radius ; i++){
int line = 0;
for(int j = -radius ; j <= radius ; j++){
double x = i * i;
double y = j * j;
matrix[row][line] = Math.exp(-(x + y)/sigamaDouble)/sigamaPi;
sum += matrix[row][line];
line++;
}
row++;
}
//歸一
for(int i = 0 ; i < size ; i++){
for(int j = 0 ; j < size ; j++){
matrix[i][j] /= sum;
}
}
return matrix;
}
對于第5行sigama的計算徙邻,參考正態(tài)分布曲線圖,可以知道 3σ 距離以外的點畸裳,權(quán)重已經(jīng)微不足道了缰犁。反推即可知道當(dāng)模糊半徑為r時,取σ為 r/3 是一個比較合適的取值怖糊。
2.2 計算
//獲取權(quán)重矩陣
double[][] matrix = getMatrix(radius);
int width = scaleBitmap.getWidth();
int height = scaleBitmap.getHeight();
int[] currentPixels = new int[width * height];
scaleBitmap.getPixels(currentPixels, 0, width, 0, 0, width, height)
for(int i = 0 ; i < width ; i++){
for(int j = 0; j < height ; j++){
int red = 0;
int green = 0;
int blue = 0;
int x = i - radius;
int y = j - radius;
//先不處理邊界值
if(x >0 && y > 0 && (i+radius < width && j+radius < height)) {
for (int tempI = -radius; tempI <= radius; tempI++) {
for (int tempJ = -radius; tempJ <= radius; tempJ++) {
int color = currentPixels[(j + tempJ) * width + i + tempI];
red += (int) (Color.red(color) * matrix[tempI + radius][tempJ + radius]);
green += (int) (Color.green(color) * matrix[tempI + radius][tempJ + radius]);
blue += (int) (Color.blue(color) * matrix[tempI + radius][tempJ + radius]);
}
}
int color = currentPixels[j * width + i];
currentPixels[j * width + i] = Color.rgb(red, green, blue);
}
}
}
}
這里currentPixels則是圖像的像素矩陣帅容。獲取到的則是圖片的像素的color,而通過Color對應(yīng)的方法則可以轉(zhuǎn)成對應(yīng)的RGB形式伍伤,
也可以直接
red = (p & 0xff0000) >> 16;
green = (p & 0x00ff00) >> 8;
blue = (p & 0x0000ff);
當(dāng)你跑程序后并徘,你會等到懷疑世界。手機一些低端手機來說扰魂,手機性能是硬傷麦乞。而且上面的算法是最最最菜的算法,跑起來差不多要幾十秒甚至幾分鐘劝评。
2.3 優(yōu)化
優(yōu)化上可以分為很多種姐直,一種是從圖片像素上的優(yōu)化,一種是算法的優(yōu)化蒋畜,另一種是調(diào)用層的優(yōu)化声畏,從java層改為jni層實現(xiàn)
2.3.1 圖片像素上的優(yōu)化
前面可以看到算法的主要循環(huán)在于width,height姻成,radius插龄,那么可以從降低像素點愿棋,也就是壓縮圖片上入手。既然模糊后的圖片相比于原圖來說是不清晰的辫狼,那么我也可以先對圖片做壓縮初斑,然后再高斯辛润,最終再放大膨处,得到的結(jié)果也與原圖直接模糊結(jié)果一樣,都是不清晰的砂竖。當(dāng)然如果對于清晰度來說的話真椿,可以通過模糊半徑radius來做調(diào)整。壓縮太大就比較模糊乎澄,可以通過減小radius突硝,相反,壓縮太小則通過增加radius即可置济。
Matrix matrix = new Matrix();
matrix.setScale(0.1f , 0.1f);
Bitmap scaleBitmap = Bitmap.createBitmap(srcBitmap , 0 , 0 , srcBitmap.getWidth() , srcBitmap.getHeight() , matrix , true);
2.3.2 算法上的優(yōu)化
前面可以看到解恰,當(dāng)前算法的復(fù)雜度則是 O(width×height×(2×radius)2),radius為模糊半徑浙于。
前面講到的處理方式都是建立在二維的情況下進行的护盈。高斯模糊也可以在二維圖像上對兩個獨立的一維空間分別進行計算,這叫作線性可分羞酗。這也就是說腐宋,使用二維矩陣變換得到的效果也可以通過在水平方向進行一維高斯矩陣變換加上豎直方向的一維高斯矩陣變換得到。
回到前面一維高斯的計算公式
跟前面一樣檀轨,不過這里的權(quán)重矩陣變?yōu)橐痪S的
//根據(jù)radius創(chuàng)建權(quán)重矩陣.
int size = 2 * radius + 1;
double[] matrix = new double[size];
double sigama = (double) radius / 3;
double sigamaDouble = 2 * sigama * sigama;
double sqlPi = Math.sqrt(2 * Math.PI);
double sigamaPi = sigama * sqlPi;
int row = 0;
double sum = 0;
for(int i = -radius ; i <= radius ; i++){
double x = i * i;
matrix[row] = Math.exp(-x/sigamaDouble)/sigamaPi;
sum += matrix[row];
row++;
}
//歸一處理目的是讓權(quán)重總值等于1胸竞。
//否則的話,使用總值大于1的濾鏡會讓圖像偏亮参萄,小于1的濾鏡會讓圖像偏暗卫枝。
for(int i = 0 ; i < size ; i++){
matrix[i] /= sum;
}
分別對橫縱方向進行處理
double[] matrix = getOneMatrix(radius);
int width = scaleBitmap.getWidth();
int height = scaleBitmap.getHeight();
int[] currentPixels = new int[width * height];
int red[] = new int[width * height];
int green[] = new int[width * height];
int blue[] = new int[width * height];
scaleBitmap.getPixels(currentPixels, 0, width, 0, 0, width, height);
for(int j = 0 ; j < height ; j++){
for(int i = 0 ; i < width ; i++){
int n = 0;
int x = i - radius;
int y = j - radius;
//先過濾邊界值
if(x >=0 && y >= 0 && (i+radius < width && j+radius < height)) {
for (int temp = -radius; temp <= radius; temp++) {
int point = temp + i;
int colorPoint = j * width + point;
int color = currentPixels[colorPoint];
red[colorPoint] += Color.red(color) * matrix[n];
green[colorPoint] += Color.green(color) * matrix[n];
blue[colorPoint] += Color.blue(color) * matrix[n];
n++;
}
}
}
}
for(int i = 0 ; i < width ; i++){
for(int j = 0 ; j < height ; j++){
int n = 0;
int r = 0 , b = 0 , g = 0;
int x = i - radius;
int y = j - radius;
//先過濾邊界值
if(x >=0 && y >= 0 && (i+radius < width && j+radius < height)) {
for (int temp = -radius; temp <= radius; temp++) {
int currentPoint = (j + temp) * width + i;
Log.e(TAG, "temp = " + temp + " i = " + i + " j : " + j + " currentPoint = " + currentPoint
);
r += red[currentPoint] * matrix[n];
g += green[currentPoint] * matrix[n];
b += blue[currentPoint] * matrix[n];
n++;
}
currentPixels[j*width + i] = Color.rgb(r, g, b);
}
}
}
此時的時間復(fù)雜度則為 O(width×height×2×radius×2)對比前面來說則少了radius倍。
當(dāng)然這里算法的優(yōu)化只是其中之一讹挎,網(wǎng)上有很多優(yōu)化后的高斯模糊剃盾,比如android-stackblur,FastBlur等,FastBlur則是參考Javascript來做個實現(xiàn)的淤袜,不過FastBlur的實現(xiàn)使用了很多額外的內(nèi)存(它會復(fù)制整個位圖到一個緩充區(qū)中)痒谴,因此它適用于小位圖,對于大圖來說則比較容易造成OOM
2.3.3 轉(zhuǎn)換到JNI層
對于Java與JNI來說铡羡,同樣的代碼在JNI層調(diào)用所耗的時間要比Java的調(diào)用要少的多积蔚,特別是在一些圖像算法,或者游戲邏輯的時候烦周。
JNI層面來說能夠帶來性能提升尽爆,它可以突破VM的內(nèi)存限制怎顾,由自己來管理內(nèi)存,而Java的內(nèi)存管理全部由虛擬機來管理的漱贱。
三.RenderScript
官網(wǎng)介紹:RenderScript是Android平臺上用于運行計算密集任務(wù)的框架槐雾。RenderScript主要是面向數(shù)據(jù)并行計算,當(dāng)然了幅狮,RenderScript中使用串行計算效率也很好募强。RenderScript是充分利用手機GPU,CPU的計算能力崇摄,讓開發(fā)者專注于算法而不在于調(diào)度擎值。我們編寫的代碼無需關(guān)心具體的硬件的不同,都能寫出高性能的代碼逐抑。
RenderScript是基于C99語言的鸠儿,我們需要通過寫一個RenderScript腳本來控制。
結(jié)合官網(wǎng)上來做個入門厕氨。
3.1 編寫rs內(nèi)核腳本
在項目的代碼目錄下(即src根目錄下/src/)創(chuàng)建rs文件进每,表示是腳本文件。這里新建一個image.rs命斧,輸入
#pragma version(1)
#pragma rs java_package_name(com.rdc.zzh.stackblurtest)
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;
}
- 對于第一行來說田晚,#pragma version(1)是指版本號,表示當(dāng)前腳本所使用的版本冯丙,不過這里只能是1才是有效的肉瓦,#pragma是標(biāo)記給編譯器看的。
- 同樣第二行胃惜,可以看出這里是告訴編譯器當(dāng)前應(yīng)用的包名泞莉。因為每個rs文件都會自動生成對應(yīng)的Java代碼,比如船殉,我們新建的hello.rs文件鲫趁,會自動生成ScriptC_hello類,因此利虫,我們需要在rs聲明包的名稱挨厚。
- 這里使用到了
uchar4 __attribute__((kernel)) invert(uchar4 in)
可以知道這是一個調(diào)用方法。
首先對于RenderScript來說它有兩種計算內(nèi)核形式糠惫,分別是 映射(mapping)內(nèi)核 與 減少(reduction)內(nèi)核疫剃。 - 參數(shù)類型,uchar4表示的是一個4字節(jié)的類型硼讽,uchar4 in中可以直接用in.r巢价,in.g,in.b,in.a 取出對應(yīng)像素點的色值信息壤躲,每個值各占一個字節(jié)城菊,取值范圍則是在(0`255)也比較說得通。上面函數(shù)則表示對一個像素點做處理碉克。
3.1.1 映射內(nèi)核mapping kernel
映射內(nèi)核:它是一個對相同維度的Allocations集合進行操作的并行函數(shù)凌唬。通常是將一個輸入Allocations分配集轉(zhuǎn)成另一個輸出Allocations分配集。比如
uchar4 RS_KERNEL invert(uchar4 in, uint32_t x, uint32_t y) {
uchar4 out = in;
out.r = 255 - in.r;
out.g = 255 - in.g;
out.b = 255 - in.b;
return out;
}
這里將輸入in(傳遞的是Allocations)漏麦,輸出則是另一個out客税。
在多數(shù)情況下,這與C語言的標(biāo)準(zhǔn)函數(shù)語法一樣唁奢,在這里 RS_KERNEL 它則是一個宏定義常量霎挟,表示的是一個映射內(nèi)核而不是一個可調(diào)用函數(shù)窝剖。它的定義為
#define RS_KERNEL __attribute__((kernel))
由上面例子可知道我們用的是映射內(nèi)核的形式麻掸,會有一個Allocations做為輸入和輸出。
此外赐纱,一個映射內(nèi)核可有一個或者多個輸入Allocations脊奋,有一個或者兩個Allocation輸出
3.1.2 減少內(nèi)核reduction kernel
減少內(nèi)核則是在相同維數(shù)的對輸入Allocations進行操作的函數(shù)族。它主要用于“降維”一個輸入的Allocation集合成一個單獨的值疙描。
下面則是一個減少內(nèi)核將輸入元素累加起來的例子
#pragma rs reduce(addint) accumulator(addintAccum)
static void addintAccum(int *accum, int val) {
*accum += val;
}
在這里例子中诚隙,#pragma rs reduce
則是用于定義內(nèi)核的名字(這里表示的是addint內(nèi)核),而后面的addintAccum則表示它是一個accumulator方法起胰,所有這樣的方法都應(yīng)該是static的久又。而一個減少內(nèi)核通常需要一個accumulator方法,它的返回值必須是void效五。
此外地消,一個減少內(nèi)核擁有一個或者多個輸入Allocation,但是沒有輸出Allocation畏妖。詳細內(nèi)容還得參考官方API文檔脉执。
3.2 Java代碼調(diào)用
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_image(rs);
Allocation aIn = Allocation.createFromBitmap(rs, mInBitmap);
Allocation aOut = Allocation.createFromBitmap(rs, mInBitmap);
mScript.forEach_invert(aIn, aOut);
aOut.copyTo(mOutBitmap);
mDstImageView.setImageBitmap(mOutBitmap);
rs.destroy();
首先這里先創(chuàng)建一個RenderScript對象,接著則是將編寫的rs文件對應(yīng)的自動生成的java類ScriptC_image初始化戒劫,接著則是創(chuàng)建兩個Allocation半夷,從名字可以看出是用來分配內(nèi)存,作為映射內(nèi)核的輸入和輸出迅细,createFromBitmap則是根據(jù)Bitmap分配內(nèi)存巫橄,把Bitmap的像素值傳遞到Allocation里面。
這兩個Allocation的Element類型必須相同茵典,在函數(shù)調(diào)用時RenderScript會檢查湘换,如果不想同會拋異常。這里提到了Element,Elemtent是指Allocation里的一項枚尼。比如我們要處理的是Bitmap贴浙,則Element表示的類型是像素。
接著調(diào)用到forEach_invert署恍,后面的invert則是我們在rs里面編寫的方法名字崎溃,這里則是映射到了ScriptC_image里面了,RenderScript會自動將aIn里的每個元素(Element)并行的去執(zhí)行invert函數(shù).得到的結(jié)果放入aOut里盯质。最后調(diào)用Allocation的copyTo函數(shù)把計算的結(jié)果轉(zhuǎn)入到Bitmap中袁串。
當(dāng)然這里所列舉的都是比較簡單的例子,很多關(guān)于圖像間的操作都可以編寫對應(yīng)的rs文件來進行處理呼巷,RenderScript所涉及到的知識點比較多囱修,詳細的還得多參考官網(wǎng)API。
3.3 RenderScript高斯模糊
前面可以看到王悍,實現(xiàn)的關(guān)鍵在于編寫對應(yīng)的rs文件生成響應(yīng)的Script類破镰,以此來進行調(diào)用。官方也已經(jīng)給出了對應(yīng)高斯模糊的實現(xiàn)類ScriptIntrinsicBlur压储,使用時的調(diào)用則為
//先對圖片進行壓縮然后再blur
Bitmap inputBitmap = Bitmap.createScaledBitmap(bitmap, Math.round(bitmap.getWidth() * bitmap_scale),
Math.round(bitmap.getHeight() * bitmap_scale), false);
//創(chuàng)建空的Bitmap用于輸出
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
//①鲜漩、初始化Renderscript
RenderScript rs = RenderScript.create(context);
//②、Create an Intrinsic Blur Script using the Renderscript
ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
//③集惋、native層分配內(nèi)存空間
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
//④孕似、設(shè)置blur的半徑然后進行blur
theIntrinsic.setRadius(blur_radius);
theIntrinsic.setInput(tmpIn);
theIntrinsic.forEach(tmpOut);
//⑤、拷貝blur后的數(shù)據(jù)到j(luò)ava緩沖區(qū)中
tmpOut.copyTo(outputBitmap);
//⑥刮刑、銷毀Renderscript
rs.destroy();
bitmap.recycle();
不過這里的限制條件則是API要大于17才可以調(diào)用喉祭,也可以自己導(dǎo)入一些v8兼容包。
3.4 對比
對于大圖來說雷绢,F(xiàn)astBlur的實現(xiàn)效果是不如RenderScript好的泛烙,甚至?xí)l(fā)生OOM問題。
而對于RenderScript來說习寸,無論大圖小圖耗時則都是1050mm左右胶惰,而FastBlur在小圖的情況下可以達到1050mm,甚至比RenderScript要好
四.參考鏈接
圖片資料來源