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
当然还有其他图片库的形式 合适自己用就好