美文网首页
android 之高斯模糊

android 之高斯模糊

作者: 山林小猎人_ | 来源:发表于2018-04-03 17:28 被阅读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 

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

    相关文章

      网友评论

          本文标题:android 之高斯模糊

          本文链接:https://www.haomeiwen.com/subject/xnuicftx.html