android 之高斯模糊

很多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 


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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容