android 之高斯模糊

2018-04-03  本文已影响0人  山林小猎人_

很多app上都有这种效果,下面的效果图,抱着玩的心态 来搞搞实现

实现1 JAVA

核心

public static BitmapdoBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) {

Bitmap bitmap;

    if (canReuseInBitmap) {

bitmap = sentBitmap;

    }else {

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

    }

if (radius <1) {

return (null);

    }

int w = bitmap.getWidth();

    int h = bitmap.getHeight();

    int[] pix =new int[w * h];

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

    int wm = w -1;

    int hm = h -1;

    int wh = w * h;

    int div = radius + radius +1;

    int r[] =new int[wh];

    int g[] =new int[wh];

    int b[] =new int[wh];

    int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;

    int vmin[] =new int[Math.max(w, h)];

    int divsum = (div +1) >>1;

    divsum *= divsum;

    int dv[] =new int[256 * divsum];

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

dv[i] = (i / divsum);

    }

yw = yi =0;

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

    int stackpointer;

    int stackstart;

    int[] sir;

    int rbs;

    int r1 = radius +1;

    int routsum, goutsum, boutsum;

    int rinsum, ginsum, binsum;

    for (y =0; y < h; 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 < w; 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 < w; x++) {

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

        yp = -radius * w;

        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 < hm) {

yp += w;

            }

}

yi = x;

        stackpointer = radius;

        for (y =0; y < h; y++) {

// Preserve alpha channel: ( 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);

}

把bitmap  跟模糊度给传值去处理 重新生成新的图位就OK  但这种方法并不是很推荐 因为java的执行时间耗时过长

方法2 OpenCv

OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类 调用的实现也是NDK的形式

OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION,this,mOpenCVCallBacl);//环境配置好 执行回调函数

private BaseLoaderCallbackmOpenCVCallBacl=new BaseLoaderCallback(this) {

@Override

    public void onManagerConnected(int status) {

super.onManagerConnected(status);

        switch (status)

{

case LoaderCallbackInterface.SUCCESS:

src =new Mat(mBitmap.getHeight(), mBitmap.getWidth(), CvType.CV_8UC4);//以位图加载图像 并将其转换为Mat以供处理的代码

                Utils.bitmapToMat(mBitmap, src);

                Imgproc.GaussianBlur(src,src,new Size(3,3),10);

                Bitmap change = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888);

                Utils.matToBitmap(src, change);

                ivopencv.setImageBitmap(change);

break;

            default:

super.onManagerConnected(status);

break;

        }

}

};

执行效率很快 但是依赖OpenCV管理器

方法3 RenderScript

RenderScript 局限在

模糊半径(radius)越大,性能要求越高,模糊半径不能超过25,所以并不能得到模糊度非常高的图片。

ScriptIntrinsicBlur在API 17时才被引入,如果需要在Android 4.2以下的设备上实现,就需要引入RenderScript Support Library,当然,安装包体积会相应的增大。

主要实现

private Bitmapblur(Bitmap bitmap,float radius) {

Bitmap output = Bitmap.createBitmap(bitmap); // 创建输出图片

    RenderScript rs = RenderScript.create(this); // 构建一个RenderScript对象

    ScriptIntrinsicBlur gaussianBlue = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 创建高斯模糊脚本

    Allocation allIn = Allocation.createFromBitmap(rs, bitmap); // 创建用于输入的脚本类型

    Allocation allOut = Allocation.createFromBitmap(rs, output); // 创建用于输出的脚本类型

    gaussianBlue.setRadius(radius); // 设置模糊半径,范围0f

    gaussianBlue.setInput(allIn); // 设置输入脚本类型

    gaussianBlue.forEach(allOut); // 执行高斯模糊算法,并将结果填入输出脚本类型中

    allOut.copyTo(output); // 将输出内存编码为Bitmap,图片大小必须注意

    rs.destroy(); // 关闭RenderScript对象,API>=23则使用rs.releaseAllContexts()

    return output;

}

方法4  Blurry图片模糊库

Blurry.with(this).radius(10)//模糊半径

        .async()//异步

        .sampling(2)//对原图像进行抽样

        .from(mBitmap)//高斯模糊

        .into(ivBlurry);

方法5 gilde 库+transformations

LruBitmapPool pool =new LruBitmapPool((int) (Runtime.getRuntime().maxMemory()) /8);

Glide.with(this).load(R.drawable.yr).bitmapTransform(new BlurTransformation(this, pool)).into(ivgilde);

//由于开发包冲突的情况 笔者没试成功

方法6 native

参考java的方法修改过来的

static { //声明加载本地so库

System.loadLibrary("native-lib");

}

public native int gaussBlur(Object bitmap, int radius);//声明本地方法

然后c++本地的方法声明

void GaussBlur(int* pix,int w,int h,int radius);

实现

int MAX(int a,int b) {

return a > b ? a : b;

}

int MIN(int a,int b) {

return a < b ? a : b;

}

int ABS(int a) {

return a >0 ? a : -a;

}

void GaussBlur(int *pix,int w,int h,int radius) {

int wm = w -1;

int hm = h -1;

int wh = w * h;

int div = radius + radius +1;

int *r = (int *) malloc(wh *sizeof(int));

int *g = (int *) malloc(wh *sizeof(int));

int *b = (int *) malloc(wh *sizeof(int));

int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;

int *vmin = (int *) malloc(MAX(w, h) *sizeof(int));

int divsum = (div +1) >>1;

divsum *= divsum;

int *dv = (int *) malloc(256 * divsum *sizeof(int));

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

dv[i] = (i / divsum);

}

yw = yi =0;

int(*stack)[3] = (int (*)[3]) malloc(div *3 *sizeof(int));

int stackpointer;

int stackstart;

int *sir;

int rbs;

int r1 = radius +1;

int routsum, goutsum, boutsum;

int rinsum, ginsum, binsum;

for (y =0; y < h; y++) {

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

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

p = pix[yi + (MIN(wm, MAX(i,0)))];

sir = stack[i + radius];

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

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

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

rbs = r1 - 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 < w; 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] = 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 < w; x++) {

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

yp = -radius * w;

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

yi = MAX(0, yp) + x;

sir = stack[i + radius];

sir[0] = r[yi];

sir[1] = g[yi];

sir[2] = b[yi];

rbs = r1 - 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 < hm) {

yp += w;

}

}

yi = x;

stackpointer = radius;

for (y =0; y < h; y++) {

// Preserve alpha channel: ( 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] = 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;

}

}

free(r);

free(g);

free(b);

free(vmin);

free(dv);

free(stack);

};

JNI调用

JNIEXPORT jint JNICALL Java_demo_example_com_opencv_Activity_GaussianBlurActivity_gaussBlur(

JNIEnv *env, jclass type, jobject bitmap,jint radius) {

AndroidBitmapInfo bmpinfo;

if (AndroidBitmap_getInfo(env, bitmap, &bmpinfo) <0) {

return -1;

}

int *bitmapData = NULL;

if (AndroidBitmap_lockPixels(env, bitmap, (void **) &bitmapData)) {

return -1;

}

GaussBlur(bitmapData, bmpinfo.width, bmpinfo.height, radius);

AndroidBitmap_unlockPixels(env, bitmap);

return 1;

}

 ndk  拷贝数据,获取bitmap里的图像数据,传递给native层;

当然在别的书中看到可以把图片的本地地址 传到native 然后调用java的方法 去生成图位 然后处理数据

还有bitmap 已经序列化了 可以尝试用intent 传到native 

 最后附上这几种方法的执行时间与效果图

左一 java 右一Ndk 左二OpenCv 右二RenderScript 右三Blurry 

当然还有其他图片库的形式   合适自己用就好

上一篇下一篇

猜你喜欢

热点阅读