配色: 字号:
Android高性能高斯模糊方案
2016-12-20 | 阅:  转:  |  分享 
  
Android高性能高斯模糊方案



简述:

做直播类app的时候点击进入直播间接通的过程中首先显示一张模糊的毛玻璃效果的图片,那么此时就要考虑使用高斯模糊的时候了。Android中提供了RenderScript来操作图片,但是这个的使用版本要求是在API17以上,所以我们还可以考虑使用第三方可FastBlur。

使用RenderScript方案:

Renderscript是android平台上进行高性能计算的框架。Renderscript主要面向并行计算,虽然它对计算密集型工作也是有益的。Renderscript在运行时将在设备上可用的处理器间平衡负载,比如多核CPU,GPU或者DSP,它让你专注于算法而不是平衡负载。RenderScript对图像处理,计算摄影学,计算机视觉方面的应用非常有用。

为了提高模糊的效果以及减少模糊过程中性能的消耗,首先对待模糊的图片进行压缩。废话少说,上代码:

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

/

类描述:利用RenderScript进行图片的高斯模糊(该方案兼容最低版本API17)

Createdbylizhenyaon16/5/28.

/

publicclassRenderScriptBlur{

privatestaticfinalfloatBITMAP_SCALE=0.4f;

privatestaticfinalintBLUR_RADIUS=7;



publicstaticBitmapblur(Contextcontext,Bitmapbitmap){

returnblur(context,bitmap,BITMAP_SCALE,BLUR_RADIUS);

}



publicstaticBitmapblur(Contextcontext,Bitmapbitmap,floatbitmap_scale){

returnblur(context,bitmap,bitmap_scale,BLUR_RADIUS);

}



publicstaticBitmapblur(Contextcontext,Bitmapbitmap,intblur_radius){

returnblur(context,bitmap,BITMAP_SCALE,blur_radius);

}



@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)

publicstaticBitmapblur(Contextcontext,Bitmapbitmap,floatbitmap_scale,intblur_radius){

//先对图片进行压缩然后再blur

BitmapinputBitmap=Bitmap.createScaledBitmap(bitmap,Math.round(bitmap.getWidth()bitmap_scale),

Math.round(bitmap.getHeight()bitmap_scale),false);

//创建空的Bitmap用于输出

BitmapoutputBitmap=Bitmap.createBitmap(inputBitmap);

//①、初始化Renderscript

RenderScriptrs=RenderScript.create(context);

//②、CreateanIntrinsicBlurScriptusingtheRenderscript

ScriptIntrinsicBlurtheIntrinsic=ScriptIntrinsicBlur.create(rs,Element.U8_4(rs));

//③、native层分配内存空间

AllocationtmpIn=Allocation.createFromBitmap(rs,inputBitmap);

AllocationtmpOut=Allocation.createFromBitmap(rs,outputBitmap);

//④、设置blur的半径然后进行blur

theIntrinsic.setRadius(blur_radius);

theIntrinsic.setInput(tmpIn);

theIntrinsic.forEach(tmpOut);

//⑤、拷贝blur后的数据到java缓冲区中

tmpOut.copyTo(outputBitmap);

//⑥、销毁Renderscript

rs.destroy();

bitmap.recycle();



returnoutputBitmap;

}





}

实际效果如何呢?下面我把多次测试打印出来的日志贴出来:

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

07-2817:16:32.39420734-20734/?E/TAg:耗时时间:30ms

07-2817:16:33.45020734-20734/?E/TAg:耗时时间:13ms

07-2817:16:34.20720734-20734/?E/TAg:耗时时间:7ms

07-2817:16:35.04520734-20734/?E/TAg:耗时时间:9ms

07-2817:16:35.90920734-20734/?E/TAg:耗时时间:6ms

07-2817:16:36.80620734-20734/?E/TAg:耗时时间:9ms

07-2817:16:39.31220734-20734/?E/TAg:耗时时间:7ms

07-2817:16:44.04520734-20734/?E/TAg:耗时时间:8ms

07-2817:16:44.87720734-20734/?E/TAg:耗时时间:8ms

07-2817:16:45.55520734-20734/?E/TAg:耗时时间:13ms

07-2817:16:46.20220734-20734/?E/TAg:耗时时间:8ms

07-2817:16:46.90120734-20734/?E/TAg:耗时时间:9ms

07-2817:16:47.95220734-20734/?E/TAg:耗时时间:7ms

上面的日志可以一眼看得出平均时间是小于16ms的,时间性能杠杠的。但是这个方案只能适用于API17以上的版本,对于低版本怎么办呢?上网搜了一圈,看到一个第三方的框架FastBlur。

使用FastBlur兼容低版本:

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

/

Createdbypaveldon3/6/14.

/

publicclassStackBlur{



publicstaticBitmapdoBlur(BitmapsentBitmap,intradius,booleancanReuseInBitmap){



//StackBlurv1.0from

//http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html

//

//JavaAuthor:MarioKlingemann

//http://incubator.quasimondo.com

//createdFeburary29,2004

//Androidport:YahelBouaziz

//http://www.kayenko.com

//portedapril5th,2012



//ThisisacompromisebetweenGaussianBlurandBoxblur

//ItcreatesmuchbetterlookingblursthanBoxBlur,butis

//7xfasterthanmyGaussianBlurimplementation.

//

//IcalleditStackBlurbecausethisdescribesbesthowthis

//filterworksinternally:itcreatesakindofmovingstack

//ofcolorswhilstscanningthroughtheimage.Therebyit

//justhastoaddonenewblockofcolortotherightside

//ofthestackandremovetheleftmostcolor.Theremaining

//colorsonthetopmostlayerofthestackareeitheraddedon

//orreducedbyone,dependingoniftheyareontherightor

//ontheleftsideofthestack.

//

//Ifyouareusingthisalgorithminyourcodepleaseadd

//thefollowingline:

//

//StackBlurAlgorithmbyMarioKlingemann



Bitmapbitmap;

if(canReuseInBitmap){

bitmap=sentBitmap;

}else{

bitmap=sentBitmap.copy(sentBitmap.getConfig(),true);

}



if(radius<1){

return(null);

}



intw=bitmap.getWidth();

inth=bitmap.getHeight();



int[]pix=newint[wh];

bitmap.getPixels(pix,0,w,0,0,w,h);



intwm=w-1;

inthm=h-1;

intwh=wh;

intdiv=radius+radius+1;



intr[]=newint[wh];

intg[]=newint[wh];

intb[]=newint[wh];

intrsum,gsum,bsum,x,y,i,p,yp,yi,yw;

intvmin[]=newint[Math.max(w,h)];



intdivsum=(div+1)>>1;

divsum=divsum;

intdv[]=newint[256divsum];

for(i=0;i<256divsum;i++){

dv[i]=(i/divsum);

}



yw=yi=0;



int[][]stack=newint[div][3];

intstackpointer;

intstackstart;

int[]sir;

intrbs;

intr1=radius+1;

introutsum,goutsum,boutsum;

intrinsum,ginsum,binsum;



for(y=0;y
rinsum=ginsum=binsum=routsum=goutsum=boutsum=rsum=gsum=bsum=0;

for(i=-radius;i<=radius;i++){

p=pix[yi+Math.min(wm,Math.max(i,0))];

sir=stack[i+radius];

sir[0]=(p&0xff0000)>>16;

sir[1]=(p&0x00ff00)>>8;

sir[2]=(p&0x0000ff);

rbs=r1-Math.abs(i);

rsum+=sir[0]rbs;

gsum+=sir[1]rbs;

bsum+=sir[2]rbs;

if(i>0){

rinsum+=sir[0];

ginsum+=sir[1];

binsum+=sir[2];

}else{

routsum+=sir[0];

goutsum+=sir[1];

boutsum+=sir[2];

}

}

stackpointer=radius;



for(x=0;x


r[yi]=dv[rsum];

g[yi]=dv[gsum];

b[yi]=dv[bsum];



rsum-=routsum;

gsum-=goutsum;

bsum-=boutsum;



stackstart=stackpointer-radius+div;

sir=stack[stackstart%div];



routsum-=sir[0];

goutsum-=sir[1];

boutsum-=sir[2];



if(y==0){

vmin[x]=Math.min(x+radius+1,wm);

}

p=pix[yw+vmin[x]];



sir[0]=(p&0xff0000)>>16;

sir[1]=(p&0x00ff00)>>8;

sir[2]=(p&0x0000ff);



rinsum+=sir[0];

ginsum+=sir[1];

binsum+=sir[2];



rsum+=rinsum;

gsum+=ginsum;

bsum+=binsum;



stackpointer=(stackpointer+1)%div;

sir=stack[(stackpointer)%div];



routsum+=sir[0];

goutsum+=sir[1];

boutsum+=sir[2];



rinsum-=sir[0];

ginsum-=sir[1];

binsum-=sir[2];



yi++;

}

yw+=w;

}

for(x=0;x
rinsum=ginsum=binsum=routsum=goutsum=boutsum=rsum=gsum=bsum=0;

yp=-radiusw;

for(i=-radius;i<=radius;i++){

yi=Math.max(0,yp)+x;



sir=stack[i+radius];



sir[0]=r[yi];

sir[1]=g[yi];

sir[2]=b[yi];



rbs=r1-Math.abs(i);



rsum+=r[yi]rbs;

gsum+=g[yi]rbs;

bsum+=b[yi]rbs;



if(i>0){

rinsum+=sir[0];

ginsum+=sir[1];

binsum+=sir[2];

}else{

routsum+=sir[0];

goutsum+=sir[1];

boutsum+=sir[2];

}



if(i
yp+=w;

}

}

yi=x;

stackpointer=radius;

for(y=0;y
//Preservealphachannel:(0xff000000&pix[yi])

pix[yi]=(0xff000000&pix[yi])|(dv[rsum]<<16)|(dv[gsum]<<8)|dv[bsum];



rsum-=routsum;

gsum-=goutsum;

bsum-=boutsum;



stackstart=stackpointer-radius+div;

sir=stack[stackstart%div];



routsum-=sir[0];

goutsum-=sir[1];

boutsum-=sir[2];



if(x==0){

vmin[y]=Math.min(y+r1,hm)w;

}

p=x+vmin[y];



sir[0]=r[p];

sir[1]=g[p];

sir[2]=b[p];



rinsum+=sir[0];

ginsum+=sir[1];

binsum+=sir[2];



rsum+=rinsum;

gsum+=ginsum;

bsum+=binsum;



stackpointer=(stackpointer+1)%div;

sir=stack[stackpointer];



routsum+=sir[0];

goutsum+=sir[1];

boutsum+=sir[2];



rinsum-=sir[0];

ginsum-=sir[1];

binsum-=sir[2];



yi+=w;

}

}



bitmap.setPixels(pix,0,w,0,0,w,h);



return(bitmap);

}

}

这个方案的时间性能惨不忍睹呀,远远大于16ms,

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

07-2818:33:14.75111928-11928/?E/TAg:耗时时间:483ms

07-2818:33:17.60211928-11928/?E/TAg:耗时时间:431ms

07-2818:33:19.17611928-11928/?E/TAg:耗时时间:369ms

07-2818:33:20.45211928-11928/?E/TAg:耗时时间:405ms

07-2818:33:21.59911928-11928/?E/TAg:耗时时间:412ms

07-2818:33:22.79111928-11928/?E/TAg:耗时时间:368ms

07-2818:33:23.97011928-11928/?E/TAg:耗时时间:314ms

07-2818:33:25.05911928-11928/?E/TAg:耗时时间:335ms

07-2818:33:25.88611928-11928/?E/TAg:耗时时间:384ms

07-2818:33:26.80611928-11928/?E/TAg:耗时时间:404ms

07-2818:33:27.42311928-11928/?E/TAg:耗时时间:473ms

07-2818:33:27.95911928-11928/?E/TAg:耗时时间:373ms

封装为工具类:

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

importandroid.annotation.TargetApi;

importandroid.content.Context;

importandroid.graphics.Bitmap;

importandroid.os.Build;

importandroid.renderscript.Allocation;

importandroid.renderscript.Element;

importandroid.renderscript.RenderScript;

importandroid.renderscript.ScriptIntrinsicBlur;



/

@authorlizhenya



@time16/5/28



@类描述:

/

publicclassGauseBulrHelper{

privatestaticfinalfloatBITMAP_SCALE=0.4f;

privatestaticfinalintBLUR_RADIUS=7;



/

@方法描述:模糊图片

@authorlizhenya

@paramcontext

上下文

@parambitmap

待模糊的图片

@return模糊后的图片

/

publicstaticBitmapblur(Contextcontext,Bitmapbitmap){

returnblur(context,bitmap,BLUR_RADIUS,BITMAP_SCALE,true);

}



/

@方法描述:图片进行模糊

@authorlizhenya

@paramcontext

上下文

@parambitmap

待模糊的图片

@paramcanReuseInBitmap

原始Bitmap是否还会使用(如果原始图片还会再次使用则参数设为true,否则会报出java.lang.

IllegalStateException异常,一般情况下设为true防止程序出错)

@return模糊后的图片

/

publicstaticBitmapblur(Contextcontext,Bitmapbitmap,

booleancanReuseInBitmap){

returnblur(context,bitmap,BLUR_RADIUS,BITMAP_SCALE,

canReuseInBitmap);

}



/



@方法描述:

@authorlizhenya

@paramcontext

上下文

@parambitmap

待模糊的图片

@paramblur_radius

模糊半径(1~25的正整数)

@paramcanReuseInBitmap

原始Bitmap是否还会使用(如果原始图片还会再次使用则参数设为true,否则会报出java.lang.

IllegalStateException异常,一般情况下设为true防止程序出错)

@return模糊后的图片

/

publicstaticBitmapblur(Contextcontext,Bitmapbitmap,intblur_radius,

booleancanReuseInBitmap){

returnblur(context,bitmap,blur_radius,BITMAP_SCALE,

canReuseInBitmap);

}



/



@方法描述:

@authorlizhenya

@paramcontext

上下文

@parambitmap

待模糊的图片

@parambitmapScale

图片的压缩因子

@paramcanReuseInBitmap

原始Bitmap是否还会使用(如果原始图片还会再次使用则参数设为true,否则会报出java.lang.

IllegalStateException异常,一般情况下设为true防止程序出错)

@return模糊后的图片

/

publicstaticBitmapblur(Contextcontext,Bitmapbitmap,

floatbitmapScale,booleancanReuseInBitmap){

returnblur(context,bitmap,BLUR_RADIUS,bitmapScale,canReuseInBitmap);

}



publicstaticBitmapblur(Contextcontext,Bitmapbitmap,intblur_radius,

floatbitmapScale,booleancanReuwww.tt951.comseInBitmap){

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.JELLY_BEAN_MR1){

returnRSBlur(context,bitmap,bitmapScale,blur_radius);

}else{

returndoBlur(bitmap,blur_radius,canReuseInBitmap);

}

}



@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)

publicstaticBitmapRSBlur(Contextcontext,Bitmapbitmap,

floatbitmap_scale,intblur_radius){

//先对图片进行压缩然后再blur

BitmapinputBitmap=Bitmap.createScaledBitmap(bitmap,

Math.round(bitmap.getWidth()bitmap_scale),

Math.round(bitmap.getHeight()bitmap_scale),false);

//创建空的Bitmap用于输出

BitmapoutputBitmap=Bitmap.createBitmap(inputBitmap);

//①、初始化Renderscript

RenderScriptrs=RenderScript.create(context);

//②、CreateanIntrinsicBlurScriptusingtheRenderscript

ScriptIntrinsicBlurtheIntrinsic=ScriptIntrinsicBlur.create(rs,

Element.U8_4(rs));

//③、native层分配内存空间

AllocationtmpIn=Allocation.createFromBitmap(rs,inputBitmap);

AllocationtmpOut=Allocation.createFromBitmap(rs,outputBitmap);

//④、设置blur的半径然后进行blur

theIntrinsic.setRadius(blur_radius);

theIntrinsic.setInput(tmpIn);

theIntrinsic.forwww.baiyuewang.netEach(tmpOut);

//⑤、拷贝blur后的数据到java缓冲区中

tmpOut.copyTo(outputBitmap);

//⑥、销毁Renderscript

rs.destroy();

bitmap.recycle();



returnoutputBitmap;

}



publicstaticBitmapdoBlur(BitmapsentBitmap,intradius,

booleancanReuseInBitmap){



//StackBlurv1.0from

//http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html

//

//JavaAuthor:MarioKlingemann

//http://incubator.quasimondo.com

//createdFeburary29,2004

//Androidport:YahelBouaziz

//http://www.kayenko.com

//portedapril5th,2012



//ThisisacompromisebetweenGaussianBlurandBoxblur

//ItcreatesmuchbetterlookingblursthanBoxBlur,butis

//7xfasterthanmyGaussianBlurimplementation.

//

//IcalleditStackBlurbecausethisdescribesbesthowthis

//filterworksinternally:itcreatesakindofmovingstack

//ofcolorswhilstscanningthroughtheimage.Therebyit

//justhastoaddonenewblockofcolortotherightside

//ofthestackandremovetheleftmostcolor.Theremaining

//colorsonthetopmostlayerofthestackareeitheraddedon

//orreducedbyone,dependingoniftheyareontherightor

//ontheleftsideofthestack.

//

//Ifyouareusingthisalgorithminyourcodepleaseadd

//thefollowingline:

//

//StackBlurAlgorithmbyMarioKlingemann



Bitmapbitmap;

if(canReuseInBitmap){

bitmap=sentBitmap;

}else{

bitmap=sentBitmap.copy(sentBitmap.getConfig(),true);

}



if(radius<1){

return(null);

}



intw=bitmap.getWidth();

inth=bitmap.getHeight();



int[]pix=newint[wh];

bitmap.getPixels(pix,0,w,0,0,w,h);



intwm=w-1;

inthm=h-1;

intwh=wh;

intdiv=radius+radius+1;



intr[]=newint[wh];

intg[]=newint[wh];

intb[]=newint[wh];

intrsum,gsum,bsum,x,y,i,p,yp,yi,yw;

intvmin[]=newint[Math.max(w,h)];



intdivsum=(div+1)>>1;

divsum=divsum;

intdv[]=newint[256divsum];

for(i=0;i<256divsum;i++){

dv[i]=(i/divsum);

}



yw=yi=0;



int[][]stack=newint[div][3];

intstackpointer;

intstackstart;

int[]sir;

intrbs;

intr1=radius+1;

introutsum,goutsum,boutsum;

intrinsum,ginsum,binsum;



for(y=0;y
rinsum=ginsum=binsum=routsum=goutsum=boutsum=rsum=gsum=bsum=0;

for(i=-radius;i<=radius;i++){

p=pix[yi+Math.min(wm,Math.max(i,0))];

sir=stack[i+radius];

sir[0]=(p&0xff0000)>>16;

sir[1]=(p&0x00ff00)>>8;

sir[2]=(p&0x0000ff);

rbs=r1-Math.abs(i);

rsum+=sir[0]rbs;

gsum+=sir[1]rbs;

bsum+=sir[2]rbs;

if(i>0){

rinsum+=sir[0];

ginsum+=sir[1];

binsum+=sir[2];

}else{

routsum+=sir[0];

goutsum+=sir[1];

boutsum+=sir[2];

}

}

stackpointer=radius;



for(x=0;x


r[yi]=dv[rsum];

g[yi]=dv[gsum];

b[yi]=dv[bsum];



rsum-=routsum;

gsum-=goutsum;

bsum-=boutsum;



stackstart=stackpointer-radius+div;

sir=stack[stackstart%div];



routsum-=sir[0];

goutsum-=sir[1];

boutsum-=sir[2];



if(y==0){

vmin[x]=Math.min(x+radius+1,wm);

}

p=pix[yw+vmin[x]];



sir[0]=(p&0xff0000)>>16;

sir[1]=(p&0x00ff00)>>8;

sir[2]=(p&0x0000ff);



rinsum+=sir[0];

ginsum+=sir[1];

binsum+=sir[2];



rsum+=rinsum;

gsum+=ginsum;

bsum+=binsum;



stackpointer=(stackpointer+1)%div;

sir=stack[(stackpointer)%div];



routsum+=sir[0];

goutsum+=sir[1];

boutsum+=sir[2];



rinsum-=sir[0];

ginsum-=sir[1];

binsum-=sir[2];



yi++;

}

yw+=w;

}

for(x=0;x
rinsum=ginsum=binsum=routsum=goutsum=boutsum=rsum=gsum=bsum=0;

yp=-radiusw;

for(i=-radius;i<=radius;i++){

yi=Math.max(0,yp)+x;



sir=stack[i+radius];



sir[0]=r[yi];

sir[1]=g[yi];

sir[2]=b[yi];



rbs=r1-Math.abs(i);



rsum+=r[yi]rbs;

gsum+=g[yi]rbs;

bsum+=b[yi]rbs;



if(i>0){

rinsum+=sir[0];

ginsum+=sir[1];

binsum+=sir[2];

}else{

routsum+=sir[0];

goutsum+=sir[1];

boutsum+=sir[2];

}



if(i
yp+=w;

}

}

yi=x;

stackpointer=radius;

for(y=0;y
//Preservealphachannel:(0xff000000&pix[yi])

pix[yi]=(0xff000000&pix[yi])|(dv[rsum]<<16)

|(dv[gsum]<<8)|dv[bsum];



rsum-=routsum;

gsum-=goutsum;

bsum-=boutsum;



stackstart=stackpointer-radius+div;

sir=stack[stackstart%div];



routsum-=sir[0];

goutsum-=sir[1];

boutsum-=sir[2];



if(x==0){

vmin[y]=Math.min(y+r1,hm)w;

}

p=x+vmin[y];



sir[0]=r[p];

sir[1]=g[p];

sir[2]=b[p];



rinsum+=sir[0];

ginsum+=sir[1];

binsum+=sir[2];



rsum+=rinsum;

gsum+=ginsum;

bsum+=binsum;



stackpointer=(stackpointer+1)%div;

sir=stack[stackpointer];



routsum+=sir[0];

goutsum+=sir[1];

boutsum+=sir[2];



rinsum-=sir[0];

ginsum-=sir[1];

binsum-=sir[2];



yi+=w;

}

}



bitmap.setPixels(pix,0,w,0,0,w,h);



return(bitmap);

}

}



工具类的使用:

[html]viewplaincopy在CODE上查看代码片派生到我的代码片

//四种方式任选其一

Bitmapblur=GauseBulrHelper.blur(getApplicationContext(),bitmap);

Bitmapblur2=GauseBulrHelper.blur(getApplicationContext(),bitmap,

true);

Bitmapblur3=GauseBulrHelper.blur(getApplicationContext(),bitmap,7,

true);

Bitmapblur4=GauseBulrHelper.blur(getApplicationContext(),bitmap,

(float)0.4,true);

Bitmapblur5=GauseBulrHelper.blur(getApplicationContext(),bitmap,7,

(float)0.4,true);



imageView.setImageBitmap(bitmap);



献花(0)
+1
(本文系thedust79首藏)