高斯模糊與圖像卷積濾波一些知識點(diǎn)

最新剛好遇到個需求是要求做高斯模糊的,雖然現(xiàn)有已經(jīng)有一些框架可以提供調(diào)用,但關(guān)鍵還是要理解原理才行,思考的過程才是最重要的,高斯模糊的原理則與圖像卷積濾波有些關(guān)系。

目錄大綱

1.圖像卷積濾波與高斯模糊
2.高斯模糊實(shí)現(xiàn)與優(yōu)化
3.RenderScript的介紹與使用

一.圖像卷積濾波與高斯模糊

1.1 圖像卷積濾波

對于濾波來說,它可以說是圖像處理最基本的方法,可以產(chǎn)生很多不同的效果。以下圖來說



圖中矩陣分別為二維原圖像素矩陣,二維的圖像濾波矩陣(也叫做卷積核,下面講到濾波器和卷積核都是同個概念),以及最后濾波后的新像素圖。對于原圖像的每一個像素點(diǎn),計(jì)算它的領(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)負(fù)數(shù)或者大于255的數(shù)值。對這種情況,我們將他們直接截?cái)嗟?和255之間即可。對于負(fù)數(shù),也可以取絕對值。

1.2 卷積核一些用法

既然知道濾波器可以用來對原圖進(jìn)行操作,那么,有沒有一些比較具體的例子。文中卷積核相關(guān)圖片來源于網(wǎng)絡(luò)

1.2.1 空卷積核

可以看到,這個濾波器啥也沒有做,得到的圖像和原圖是一樣的。因?yàn)橹挥兄行狞c(diǎn)的值是1。鄰域點(diǎn)的權(quán)值都是0,對濾波后的取值沒有任何影響。

1.2.2 圖像銳化濾波器

圖像的銳化和邊緣檢測很像,首先找到邊緣,然后把邊緣加到原來的圖像上面,這樣就強(qiáng)化了圖像的邊緣,使圖像看起來更加銳利了。這兩者操作統(tǒng)一起來就是銳化濾波器了,也就是在邊緣檢測濾波器的基礎(chǔ)上,再在中心的位置加1,這樣濾波后的圖像就會和原始的圖像具有同樣的亮度了,但是會更加銳利。



我們把核加大,就可以得到更加精細(xì)的銳化效果


1.2.3 浮雕

浮雕濾波器可以給圖像一種3D陰影的效果。只要將中心一邊的像素減去另一邊的像素就可以了。這時候,像素值有可能是負(fù)數(shù),我們將負(fù)數(shù)當(dāng)成陰影,將正數(shù)當(dāng)成光,然后我們對結(jié)果圖像加上128的偏移。這時候,圖像大部分就變成灰色了。
下面是45度的浮雕濾波器

我們只要加大濾波器,就可以得到更加夸張的效果了

1.2.4 均值模糊

我們可以將當(dāng)前像素和它的四鄰域的像素一起取平均,然后再除以5,或者直接在濾波器的5個地方取0.2的值即可,如下圖:

可以看到,這個模糊還是比較溫柔的,我們可以把濾波器變大,這樣就會變得粗暴了:注意要將和再除以13.

可以看到均值模糊也可以做到讓圖片模糊,但是它的模糊不是很平滑,不平滑主要在于距離中心點(diǎn)很遠(yuǎn)的點(diǎn)與距離中心點(diǎn)很近的所帶的權(quán)重值相同,產(chǎn)生的模糊效果一樣
而想要做到平滑,讓權(quán)重值跟隨中心點(diǎn)位置距離不同而不同,則可以利用正態(tài)分布(中間大,兩端?。┻@個特點(diǎn)來實(shí)現(xiàn)。

1.3 高斯模糊

有了前面的知識,我們知道如果要想實(shí)現(xiàn)高斯模糊的特點(diǎn),則需要通過構(gòu)建對應(yīng)的權(quán)重矩陣來進(jìn)行濾波。

1.3.1 正態(tài)分布
正態(tài)分布

正態(tài)分布中,越接近中心點(diǎn),取值越大,越遠(yuǎn)離中心,取值越小。
計(jì)算平均值的時候,我們只需要將"中心點(diǎn)"作為原點(diǎn),其他點(diǎn)按照其在正態(tài)曲線上的位置,分配權(quán)重,就可以得到一個加權(quán)平均值。正態(tài)分布顯然是一種可取的權(quán)重分配模式。

1.3.2 高斯函數(shù)

如何反映出正態(tài)分布?則需要使用高函數(shù)來實(shí)現(xiàn)。
上面的正態(tài)分布是一維的,而對于圖像都是二維的,所以我們需要二維的正態(tài)分布。



正態(tài)分布的密度函數(shù)叫做"高斯函數(shù)"(Gaussian function)。它的一維形式是:

image.png

其中,μ是x的均值,σ是x的方差。因?yàn)橛?jì)算平均值的時候,中心點(diǎn)就是原點(diǎn),所以μ等于0。

image.png

根據(jù)一維高斯函數(shù),可以推導(dǎo)得到二維高斯函數(shù):

image.png

有了這個函數(shù) ,就可以計(jì)算每個點(diǎn)的權(quán)重了。

1.3.3 獲取權(quán)重矩陣

假定中心點(diǎn)的坐標(biāo)是(0,0),那么距離它最近的8個點(diǎn)的坐標(biāo)如下:


更遠(yuǎn)的點(diǎn)以此類推。
為了計(jì)算權(quán)重矩陣,需要設(shè)定σ的值。假定σ=1.5,則模糊半徑為1的權(quán)重矩陣如下:

這9個點(diǎn)的權(quán)重總和等于0.4787147,如果只計(jì)算這9個點(diǎn)的加權(quán)平均,還必須讓它們的權(quán)重之和等于1,因此上面9個值還要分別除以0.4787147,得到最終的權(quán)重矩陣。

除以總值這個過程也叫做”歸一問題“
目的是讓濾鏡的權(quán)重總值等于1。否則的話,使用總值大于1的濾鏡會讓圖像偏亮,小于1的濾鏡會讓圖像偏暗。

1.3.4 計(jì)算模糊值

有了權(quán)重矩陣,就可以計(jì)算高斯模糊的值了。
假設(shè)現(xiàn)有9個像素點(diǎn),灰度值(0-255)如下:


每個點(diǎn)乘以自己的權(quán)重值:

得到

將這9個值加起來,就是中心點(diǎn)的高斯模糊的值。
對所有點(diǎn)重復(fù)這個過程,就得到了高斯模糊后的圖像。對于彩色圖片來說,則需要對RGB三個通道分別做高斯模糊。

1.3.5 邊界值問題

既然是根據(jù)權(quán)重矩陣來進(jìn)行處理的

image.png

如果一個點(diǎn)處于邊界,周邊沒有足夠的點(diǎn),怎么辦?

  • ① 對稱處理,就是把已有的點(diǎn)拷貝到另一面的對應(yīng)位置,模擬出完整的矩陣。
  • ② 賦0,想象圖像是無限長的圖像的一部分,除了我們給定值的部分,其他部分的像素值都是0
  • ③ 賦邊界值,想象圖像是無限制長,但是默認(rèn)賦值的不是0而是對應(yīng)邊界點(diǎn)的值

二. 高斯模糊實(shí)現(xiàn)與優(yōu)化

理解原理之后則可以做進(jìn)一步的實(shí)現(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的計(jì)算,參考正態(tài)分布曲線圖,可以知道 3σ 距離以外的點(diǎn),權(quán)重已經(jīng)微不足道了。反推即可知道當(dāng)模糊半徑為r時,取σ為 r/3 是一個比較合適的取值。

2.2 計(jì)算

        //獲取權(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)你跑程序后,你會等到懷疑世界。手機(jī)一些低端手機(jī)來說,手機(jī)性能是硬傷。而且上面的算法是最最最菜的算法,跑起來差不多要幾十秒甚至幾分鐘。

2.3 優(yōu)化

優(yōu)化上可以分為很多種,一種是從圖片像素上的優(yōu)化,一種是算法的優(yōu)化,另一種是調(diào)用層的優(yōu)化,從java層改為jni層實(shí)現(xiàn)

2.3.1 圖片像素上的優(yōu)化

前面可以看到算法的主要循環(huán)在于width,height,radius,那么可以從降低像素點(diǎn),也就是壓縮圖片上入手。既然模糊后的圖片相比于原圖來說是不清晰的,那么我也可以先對圖片做壓縮,然后再高斯,最終再放大,得到的結(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為模糊半徑。
前面講到的處理方式都是建立在二維的情況下進(jìn)行的。高斯模糊也可以在二維圖像上對兩個獨(dú)立的一維空間分別進(jìn)行計(jì)算,這叫作線性可分。這也就是說,使用二維矩陣變換得到的效果也可以通過在水平方向進(jìn)行一維高斯矩陣變換加上豎直方向的一維高斯矩陣變換得到。

回到前面一維高斯的計(jì)算公式

一維高斯

跟前面一樣,不過這里的權(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;
        }

分別對橫縱方向進(jìn)行處理

  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來做個實(shí)現(xiàn)的,不過FastBlur的實(shí)現(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)存管理全部由虛擬機(jī)來管理的。

三.RenderScript

官網(wǎng)介紹:RenderScript是Android平臺上用于運(yùn)行計(jì)算密集任務(wù)的框架。RenderScript主要是面向數(shù)據(jù)并行計(jì)算,當(dāng)然了,RenderScript中使用串行計(jì)算效率也很好。RenderScript是充分利用手機(jī)GPU,CPU的計(jì)算能力,讓開發(fā)者專注于算法而不在于調(diào)度。我們編寫的代碼無需關(guān)心具體的硬件的不同,都能寫出高性能的代碼。

RenderScript是基于C99語言的,我們需要通過寫一個RenderScript腳本來控制。
結(jié)合官網(wǎng)上來做個入門。

3.1 編寫rs內(nèi)核腳本

在項(xiàng)目的代碼目錄下(即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)用的包名。因?yàn)槊總€rs文件都會自動生成對應(yīng)的Java代碼,比如,我們新建的hello.rs文件,會自動生成ScriptC_hello類,因此,我們需要在rs聲明包的名稱。
  • 這里使用到了 uchar4 __attribute__((kernel)) invert(uchar4 in) 可以知道這是一個調(diào)用方法。
    首先對于RenderScript來說它有兩種計(jì)算內(nèi)核形式,分別是 映射(mapping)內(nèi)核減少(reduction)內(nèi)核。
  • 參數(shù)類型,uchar4表示的是一個4字節(jié)的類型,uchar4 in中可以直接用in.r,in.g,in.b,in.a 取出對應(yīng)像素點(diǎn)的色值信息,每個值各占一個字節(jié),取值范圍則是在(0`255)也比較說得通。上面函數(shù)則表示對一個像素點(diǎn)做處理。
3.1.1 映射內(nèi)核mapping kernel

映射內(nèi)核:它是一個對相同維度的Allocations集合進(jìn)行操作的并行函數(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進(jìn)行操作的函數(shù)族。它主要用于“降維”一個輸入的Allocation集合成一個單獨(dú)的值。

下面則是一個減少內(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。詳細(xì)內(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里的一項(xiàng)。比如我們要處理的是Bitmap,則Element表示的類型是像素。

接著調(diào)用到forEach_invert,后面的invert則是我們在rs里面編寫的方法名字,這里則是映射到了ScriptC_image里面了,RenderScript會自動將aIn里的每個元素(Element)并行的去執(zhí)行invert函數(shù).得到的結(jié)果放入aOut里。最后調(diào)用Allocation的copyTo函數(shù)把計(jì)算的結(jié)果轉(zhuǎn)入到Bitmap中。

當(dāng)然這里所列舉的都是比較簡單的例子,很多關(guān)于圖像間的操作都可以編寫對應(yīng)的rs文件來進(jìn)行處理,RenderScript所涉及到的知識點(diǎn)比較多,詳細(xì)的還得多參考官網(wǎng)API。

3.3 RenderScript高斯模糊

前面可以看到,實(shí)現(xiàn)的關(guān)鍵在于編寫對應(yīng)的rs文件生成響應(yīng)的Script類,以此來進(jìn)行調(diào)用。官方也已經(jīng)給出了對應(yīng)高斯模糊的實(shí)現(xiàn)類ScriptIntrinsicBlur,使用時的調(diào)用則為

//先對圖片進(jìn)行壓縮然后再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的半徑然后進(jìn)行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的實(shí)現(xiàn)效果是不如RenderScript好的,甚至?xí)l(fā)生OOM問題。
而對于RenderScript來說,無論大圖小圖耗時則都是1050mm左右,而FastBlur在小圖的情況下可以達(dá)到1050mm,甚至比RenderScript要好

四.參考鏈接

圖片資料來源

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 不同圖像灰度不同,邊界處一般會有明顯的邊緣,利用此特征可以分割圖像。需要說明的是:邊緣和物體間的邊界并不等同,邊緣...
    大川無敵閱讀 14,143評論 0 29
  • 參考資料: 圖像卷積與濾波的一些知識點(diǎn) 圖像處理基本概念——卷積,濾波,平滑 1.卷積的基本概念 首先,我們有一個...
    keloli閱讀 10,362評論 0 26
  • 圖像濾波,即在盡量保留圖像細(xì)節(jié)特征的條件下對目標(biāo)圖像的噪聲進(jìn)行抑制,是圖像預(yù)處理中不可缺少的操作,其處理效果的好壞...
    木夜溯閱讀 3,224評論 0 10
  • 高斯模糊 高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP...
    fengzhizi715閱讀 6,082評論 3 22
  • 字體 ubuntu自帶的字體挺漂亮的,但是個人覺得有點(diǎn)輕佻。所以采用文泉譯微米黑字體替代。 具體配置如下:
    iamxcc閱讀 251評論 0 0

友情鏈接更多精彩內(nèi)容