Android高性能高斯模糊方案
2016-07-28 23:32
676 查看
简述:
做直播类app的时候点击进入直播间接通的过程中首先显示一张模糊的毛玻璃效果的图片,那么此时就要考虑使用高斯模糊的时候了。Android中提供了RenderScript来操作图片,但是这个的使用版本要求是在API17以上,所以我们还可以考虑使用第三方可FastBlur。使用RenderScript方案:
Renderscript是android平台上进行高性能计算的框架。Renderscript主要面向并行计算,虽然它对计算密集型工作也是有益的。Renderscript在运行时将在设备上可用的处理器间平衡负载,比如多核CPU,GPU或者DSP,它让你专注于算法而不是平衡负载。RenderScript对图像处理,计算摄影学,计算机视觉方面的应用非常有用。为了提高模糊的效果以及减少模糊过程中性能的消耗,首先对待模糊的图片进行压缩。废话少说,上代码:
/** * 类描述:利用RenderScript进行图片的高斯模糊(该方案兼容最低版本API17) * Created by lizhenya on 16/5/28. */ public class RenderScriptBlur { private static final float BITMAP_SCALE = 0.4f; private static final int BLUR_RADIUS = 7; public static Bitmap blur(Context context, Bitmap bitmap) { return blur(context, bitmap, BITMAP_SCALE, BLUR_RADIUS); } public static Bitmap blur(Context context, Bitmap bitmap, float bitmap_scale) { return blur(context, bitmap, bitmap_scale, BLUR_RADIUS); } public static Bitmap blur(Context context, Bitmap bitmap, int blur_radius) { return blur(context, bitmap, BITMAP_SCALE, blur_radius); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static Bitmap blur(Context context, Bitmap bitmap, float bitmap_scale, int blur_radius) { //先对图片进行压缩然后再blur Bitmap inputBitmap = Bitmap.createScaledBitmap(bitmap, Math.round(bitmap.getWidth() * bitmap_scale), Math.round(bitmap.getHeight() * bitmap_scale), false); //创建空的Bitmap用于输出 Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); //①、初始化Renderscript RenderScript rs = RenderScript.create(context); //②、Create an Intrinsic Blur Script using the Renderscript ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); //③、native层分配内存空间 Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); Allocation tmpOut = 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(); return outputBitmap; } }实际效果如何呢?下面我把多次测试打印出来的日志贴出来:
07-28 17:16:32.394 20734-20734/? E/TAg: 耗时时间:30ms 07-28 17:16:33.450 20734-20734/? E/TAg: 耗时时间:13ms 07-28 17:16:34.207 20734-20734/? E/TAg: 耗时时间:7ms 07-28 17:16:35.045 20734-20734/? E/TAg: 耗时时间:9ms 07-28 17:16:35.909 20734-20734/? E/TAg: 耗时时间:6ms 07-28 17:16:36.806 20734-20734/? E/TAg: 耗时时间:9ms 07-28 17:16:39.312 20734-20734/? E/TAg: 耗时时间:7ms 07-28 17:16:44.045 20734-20734/? E/TAg: 耗时时间:8ms 07-28 17:16:44.877 20734-20734/? E/TAg: 耗时时间:8ms 07-28 17:16:45.555 20734-20734/? E/TAg: 耗时时间:13ms 07-28 17:16:46.202 20734-20734/? E/TAg: 耗时时间:8ms 07-28 17:16:46.901 20734-20734/? E/TAg: 耗时时间:9ms 07-28 17:16:47.952 20734-20734/? E/TAg: 耗时时间:7ms上面的日志可以一眼看得出平均时间是小于16ms的,时间性能杠杠的。但是这个方案只能适用于API17以上的版本,对于低版本怎么办呢?上网搜了一圈,看到一个第三方的框架FastBlur。
使用FastBlur兼容低版本:
/** * Created by paveld on 3/6/14. */ public class StackBlur { public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { // Stack Blur v1.0 from // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html // // Java Author: Mario Klingemann // http://incubator.quasimondo.com // created Feburary 29, 2004 // Android port : Yahel Bouaziz // http://www.kayenko.com // ported april 5th, 2012 // This is a compromise between Gaussian Blur and Box blur // It creates much better looking blurs than Box Blur, but is // 7x faster than my Gaussian Blur implementation. // // I called it Stack Blur because this describes best how this // filter works internally: it creates a kind of moving stack // of colors whilst scanning through the image. Thereby it // just has to add one new block of color to the right side // of the stack and remove the leftmost color. The remaining // colors on the topmost layer of the stack are either added on // or reduced by one, depending on if they are on the right or // on the left side of the stack. // // If you are using this algorithm in your code please add // the following line: // // Stack Blur Algorithm by Mario Klingemann 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); } }这个方案的时间性能惨不忍睹呀,远远大于16ms,
07-28 18:33:14.751 11928-11928/? E/TAg: 耗时时间:483ms 07-28 18:33:17.602 11928-11928/? E/TAg: 耗时时间:431ms 07-28 18:33:19.176 11928-11928/? E/TAg: 耗时时间:369ms 07-28 18:33:20.452 11928-11928/? E/TAg: 耗时时间:405ms 07-28 18:33:21.599 11928-11928/? E/TAg: 耗时时间:412ms 07-28 18:33:22.791 11928-11928/? E/TAg: 耗时时间:368ms 07-28 18:33:23.970 11928-11928/? E/TAg: 耗时时间:314ms 07-28 18:33:25.059 11928-11928/? E/TAg: 耗时时间:335ms 07-28 18:33:25.886 11928-11928/? E/TAg: 耗时时间:384ms 07-28 18:33:26.806 11928-11928/? E/TAg: 耗时时间:404ms 07-28 18:33:27.423 11928-11928/? E/TAg: 耗时时间:473ms 07-28 18:33:27.959 11928-11928/? E/TAg: 耗时时间:373ms
封装为工具类:
import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.os.Build; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RenderScript; import android.renderscript.ScriptIntrinsicBlur; /** * @author lizhenya * * @time 16/5/28 * * @类描述: */ public class GauseBulrHelper { private static final float BITMAP_SCALE = 0.4f; private static final int BLUR_RADIUS = 7; /** * @方法描述:模糊图片 * @author lizhenya * @param context * 上下文 * @param bitmap * 待模糊的图片 * @return 模糊后的图片 */ public static Bitmap blur(Context context, Bitmap bitmap) { return blur(context, bitmap, BLUR_RADIUS, BITMAP_SCALE, true); } /** * @方法描述:图片进行模糊 * @author lizhenya * @param context * 上下文 * @param bitmap * 待模糊的图片 * @param canReuseInBitmap * 原始Bitmap是否还会使用(如果原始图片还会再次使用则参数设为true,否则会报出java.lang. * IllegalStateException异常,一般情况下设为true防止程序出错) * @return 模糊后的图片 */ public static Bitmap blur(Context context, Bitmap bitmap, boolean canReuseInBitmap) { return blur(context, bitmap, BLUR_RADIUS, BITMAP_SCALE, canReuseInBitmap); } /** * * @方法描述: * @author lizhenya * @param context * 上下文 * @param bitmap * 待模糊的图片 * @param blur_radius * 模糊半径(1~25的正整数) * @param canReuseInBitmap * 原始Bitmap是否还会使用(如果原始图片还会再次使用则参数设为true,否则会报出java.lang. * IllegalStateException异常,一般情况下设为true防止程序出错) * @return 模糊后的图片 */ public static Bitmap blur(Context context, Bitmap bitmap, int blur_radius, boolean canReuseInBitmap) { return blur(context, bitmap, blur_radius, BITMAP_SCALE, canReuseInBitmap); } /** * * @方法描述: * @author lizhenya * @param context * 上下文 * @param bitmap * 待模糊的图片 * @param bitmapScale * 图片的压缩因子 * @param canReuseInBitmap * 原始Bitmap是否还会使用(如果原始图片还会再次使用则参数设为true,否则会报出java.lang. * IllegalStateException异常,一般情况下设为true防止程序出错) * @return 模糊后的图片 */ public static Bitmap blur(Context context, Bitmap bitmap, float bitmapScale, boolean canReuseInBitmap) { return blur(context, bitmap, BLUR_RADIUS, bitmapScale, canReuseInBitmap); } public static Bitmap blur(Context context, Bitmap bitmap, int blur_radius, float bitmapScale, boolean canReuseInBitmap) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { return RSBlur(context, bitmap, bitmapScale, blur_radius); } else { return doBlur(bitmap, blur_radius, canReuseInBitmap); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static Bitmap RSBlur(Context context, Bitmap bitmap, float bitmap_scale, int blur_radius) { // 先对图片进行压缩然后再blur Bitmap inputBitmap = Bitmap.createScaledBitmap(bitmap, Math.round(bitmap.getWidth() * bitmap_scale), Math.round(bitmap.getHeight() * bitmap_scale), false); // 创建空的Bitmap用于输出 Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); // ①、初始化Renderscript RenderScript rs = RenderScript.create(context); // ②、Create an Intrinsic Blur Script using the Renderscript ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // ③、native层分配内存空间 Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); Allocation tmpOut = 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(); return outputBitmap; } public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { // Stack Blur v1.0 from // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html // // Java Author: Mario Klingemann // http://incubator.quasimondo.com // created Feburary 29, 2004 // Android port : Yahel Bouaziz // http://www.kayenko.com // ported april 5th, 2012 // This is a compromise between Gaussian Blur and Box blur // It creates much better looking blurs than Box Blur, but is // 7x faster than my Gaussian Blur implementation. // // I called it Stack Blur because this describes best how this // filter works internally: it creates a kind of moving stack // of colors whilst scanning through the image. Thereby it // just has to add one new block of color to the right side // of the stack and remove the leftmost color. The remaining // colors on the topmost layer of the stack are either added on // or reduced by one, depending on if they are on the right or // on the left side of the stack. // // If you are using this algorithm in your code please add // the following line: // // Stack Blur Algorithm by Mario Klingemann 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 blur = GauseBulrHelper.blur(getApplicationContext(), bitmap); Bitmap blur2 = GauseBulrHelper.blur(getApplicationContext(), bitmap, true); Bitmap blur3 = GauseBulrHelper.blur(getApplicationContext(), bitmap, 7, true); Bitmap blur4 = GauseBulrHelper.blur(getApplicationContext(), bitmap, (float) 0.4, true); Bitmap blur5 = GauseBulrHelper.blur(getApplicationContext(), bitmap, 7, (float) 0.4, true); imageView.setImageBitmap(bitmap);
这个结果也能满足基本的需求,但是对于性能有待进一步的优化。本次方案设计的Demo链接:Android高斯模糊
相关文章推荐
- fragment的使用以及fragment大家族解析
- Android菜单optionMenu(选择菜单)的实现
- 打造专属的Chromium for Android
- [Android]Android 开发环境下载地址 -- 百度网盘 adt-bundle android-studio sdk adt 下载
- invalidate()不起作用
- Android Gradle插件之最新TransfomAPI解析
- Android开发艺术探索--singleTask的启动模式--笔记
- android deep Link
- android通过更改hosts免优酷广告方法
- Android 教你亲手打造酷炫的弹幕效果
- Android中如何实现一键退出
- Android应用安装过程分析(二)
- Android优化(一)_Java代码优化
- Android 异步任务的封装
- Android 动画
- Android 界面适配
- Android Canvas 绘图
- android studio xposed教程(含jar下载地址)
- Android中常用的对话框的创建
- android Intent的相关Flag说明