Android SDK的快速位图模糊
目前在我正在开发的一个Android应用程序中,我循环播放一个图像的像素来模糊它。 这在640×480图像上需要大约30秒。
在Android Market中浏览应用程序时,我遇到了一个包含模糊功能的应用程序,其模糊速度非常快(例如5秒),因此它们必须使用不同的模糊方法。
任何人都知道一个更快的方式,而不是循环像素?
这是在黑暗中的一个镜头,但你可以尝试缩小图像,然后再扩大。 这可以通过Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)
。 确保并将filter参数设置为true。 它将运行在本地代码,所以它可能会更快。
对于未来的Google员工,这是一个从Quasimondo移植过来的算法。 这是一个框模糊和高斯模糊之间的混合,这是非常漂亮,也很快:
/** * Stack Blur v1.0 from * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html * Java Author: Mario Klingemann <mario at quasimondo.com> * http://incubator.quasimondo.com * * created Feburary 29, 2004 * Android port : Yahel Bouaziz <yahel at kayenko.com> * 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 <mario@quasimondo.com> */ public Bitmap fastblur(Bitmap sentBitmap, float scale, int radius) { int width = Math.round(sentBitmap.getWidth() * scale); int height = Math.round(sentBitmap.getHeight() * scale); sentBitmap = Bitmap.createScaledBitmap(sentBitmap, width, height, false); Bitmap 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]; Log.e("pix", w + " " + h + " " + pix.length); 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; } } Log.e("pix", w + " " + h + " " + pix.length); bitmap.setPixels(pix, 0, w, 0, 0, w, h); return (bitmap); }
Android模糊指南2016
与 Github上的 Showcase / Benchmark App和Source 。 还请看看我目前正在使用的Blur框架: Dali 。
经过大量的实验后,我现在可以安全地给你一些可靠的建议,在使用Android Framework的时候可以让你的生活变得更轻松。
加载和使用缩小的位图(对于非常模糊的图像)
切勿使用全尺寸的位图。 图像越大,模糊越多,模糊半径越大,模糊半径越高,算法越慢。
final BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 8; Bitmap blurTemplate = BitmapFactory.decodeResource(getResources(), R.drawable.myImage, options);
这将加载位图inSampleSize 8,所以只有原始图像的1/64。 测试inSampleSize适合您的需求,但保持2 ^ n(2,4,8,…)以避免因缩放而导致质量下降。 有关详情,请参阅Google文档
另一个非常大的优势是位图加载速度非常快。 在我早期的模糊测试中,我发现整个模糊过程中最长的时间是图像加载。 所以要从磁盘加载1920×1080的图像,我的Nexus 5需要500毫秒,而模糊只需要另外250毫秒左右。
使用Renderscript
Renderscript提供了一个高斯模糊滤镜ScriptIntrinsicBlur
。 它具有良好的视觉质量,只是你在Android上实际获得的最快速度。 Google宣称“通常比多线程C实现速度快2-3倍,通常比Java实现快10倍以上” 。 Renderscript是非常复杂的(使用fastes处理设备(GPU,ISP等)等),也有v8的支持库,使其兼容到2.2 。 至少在理论上,通过我自己的测试和来自其他开发者的报告,似乎不可能盲目地使用渲染,因为硬件/驱动程序碎片似乎会导致某些设备出现问题,即使更高的sdk lvl(例如,我麻烦与4.1 Nexus S)所以要小心,并在很多设备上测试。 这是一个简单的例子,可以让你开始
//define this only once if blurring multiple times RenderScript rs = RenderScript.create(context); (...) //this will blur the bitmapOriginal with a radius of 8 and save it in bitmapOriginal final Allocation input = Allocation.createFromBitmap(rs, bitmapOriginal); //use this constructor for best performance, because it uses USAGE_SHARED mode which reuses memory final Allocation output = Allocation.createTyped(rs, input.getType()); final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); script.setRadius(8f); script.setInput(input); script.forEach(output); output.copyTo(bitmapOriginal);
当使用Gradle特别推荐的Gradle时, “因为它们包含最新的改进” ,所以你只需要添加2行到你的构建脚本,并使用android.support.v8.renderscript
和当前的构建工具( 更新语法为Android gradle插件v14 + )
android { ... defaultConfig { ... renderscriptTargetApi 19 renderscriptSupportModeEnabled true } }
Nexus 5上的简单基准测试 – 将RenderScript与其他不同的Java和Renderscript实现进行比较:
不同图片大小的平均运行时间
可以模糊的百万像素每秒
每个值是250轮的平均值。 RS_GAUSS_FAST是ScriptIntrinsicBlur
(几乎总是最快的),其他以RS_开始的大多是使用简单内核的卷积实现。 算法的细节可以在这里找到 。 这不是纯粹的模糊,而是一个很好的部分是测量的垃圾收集。 这可以在这里看到( ScriptIntrinsicBlur
在100×100图像上,约有500轮)
尖峰是gc。
你可以检查自己,基准的应用程序是在Playstore: BlurBenchmark
尽可能重用位图(如果prio:性能>内存占用)
如果你需要多个模糊模糊或类似的模糊,你的内存允许它不能从可绘制多次加载位图,但保持“缓存”成员变量。 在这种情况下,总是尝试使用相同的变量,以尽量减少垃圾收集。
当从一个文件或drawable中加载时 ,检查出新的“inBitmap”选项,这将重新使用位图内存并节省垃圾收集时间。
从锐利到模糊的混合
简单朴实的方法就是使用2个图像浏览,一个模糊和alpha淡入淡出。 但是,如果你想要一个更复杂的外观,平滑淡化,然后检查罗曼努里克的帖子,如何在他的Muzei应用程序如何做到这一点 。
基本上他解释说,他预先模糊了不同模糊的一些帧,并将其用作看起来非常平滑的动画中的关键帧
编辑(2014年4月):这是一个问题/答案页面,似乎仍然得到很多点击。 我知道我总是得到这个职位upvotes。 但是,如果你正在阅读这个,你需要认识到这里发布的答案(包括我和被接受的答案)都是过时的。 如果你想今天实现高效的模糊, 你应该使用RenderScript而不是NDK或者Java。 RenderScript在Android 2.2+(使用Android支持库 )上运行,所以没有理由不使用它。
旧的答案如下,但要小心,因为它已经过时了。
对于未来的Google员工,这是一个算法,我从Yahel的Quasimondo算法的端口移植,但使用NDK。 这当然是基于Yahel的回答。 但是,这是运行本机C代码,所以它更快。 快多了。 喜欢,快40倍。
我发现使用NDK是所有图像处理应该如何在Android上完成的…起初实现起来有些恼人(在这里阅读关于使用JNI和NDK的一个很好的教程),但是更好,接近实时很多东西。
作为参考,使用Yahel的Java函数,花了10秒钟,模糊了我的480×532像素的图像,模糊半径为10.但花了250毫秒使用本机C版本。 我敢肯定,它还可以进一步优化…我只是做了一个愚蠢的Java代码转换,可能有一些操纵可以缩短,不想花太多时间重构整个事情。
#include <jni.h> #include <string.h> #include <math.h> #include <stdio.h> #include <android/log.h> #include <android/bitmap.h> #define LOG_TAG "libbitmaputils" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) typedef struct { uint8_t red; uint8_t green; uint8_t blue; uint8_t alpha; } rgba; JNIEXPORT void JNICALL Java_com_insert_your_package_ClassName_functionToBlur(JNIEnv* env, jobject obj, jobject bitmapIn, jobject bitmapOut, jint radius) { LOGI("Blurring bitmap..."); // Properties AndroidBitmapInfo infoIn; void* pixelsIn; AndroidBitmapInfo infoOut; void* pixelsOut; int ret; // Get image info if ((ret = AndroidBitmap_getInfo(env, bitmapIn, &infoIn)) < 0 || (ret = AndroidBitmap_getInfo(env, bitmapOut, &infoOut)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return; } // Check image if (infoIn.format != ANDROID_BITMAP_FORMAT_RGBA_8888 || infoOut.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { LOGE("Bitmap format is not RGBA_8888!"); LOGE("==> %d %d", infoIn.format, infoOut.format); return; } // Lock all images if ((ret = AndroidBitmap_lockPixels(env, bitmapIn, &pixelsIn)) < 0 || (ret = AndroidBitmap_lockPixels(env, bitmapOut, &pixelsOut)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); } int h = infoIn.height; int w = infoIn.width; LOGI("Image size is: %i %i", w, h); rgba* input = (rgba*) pixelsIn; rgba* output = (rgba*) pixelsOut; int wm = w - 1; int hm = h - 1; int wh = w * h; int whMax = max(w, h); int div = radius + radius + 1; int r[wh]; int g[wh]; int b[wh]; int rsum, gsum, bsum, x, y, i, yp, yi, yw; rgba p; int vmin[whMax]; int divsum = (div + 1) >> 1; divsum *= divsum; int dv[256 * divsum]; for (i = 0; i < 256 * divsum; i++) { dv[i] = (i / divsum); } yw = yi = 0; int stack[div][3]; int stackpointer; int stackstart; int rbs; int ir; int ip; 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 = input[yi + min(wm, max(i, 0))]; ir = i + radius; // same as sir stack[ir][0] = p.red; stack[ir][1] = p.green; stack[ir][2] = p.blue; rbs = r1 - abs(i); rsum += stack[ir][0] * rbs; gsum += stack[ir][1] * rbs; bsum += stack[ir][2] * rbs; if (i > 0) { rinsum += stack[ir][0]; ginsum += stack[ir][1]; binsum += stack[ir][2]; } else { routsum += stack[ir][0]; goutsum += stack[ir][1]; boutsum += stack[ir][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; ir = stackstart % div; // same as sir routsum -= stack[ir][0]; goutsum -= stack[ir][1]; boutsum -= stack[ir][2]; if (y == 0) { vmin[x] = min(x + radius + 1, wm); } p = input[yw + vmin[x]]; stack[ir][0] = p.red; stack[ir][1] = p.green; stack[ir][2] = p.blue; rinsum += stack[ir][0]; ginsum += stack[ir][1]; binsum += stack[ir][2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; ir = (stackpointer) % div; // same as sir routsum += stack[ir][0]; goutsum += stack[ir][1]; boutsum += stack[ir][2]; rinsum -= stack[ir][0]; ginsum -= stack[ir][1]; binsum -= stack[ir][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; ir = i + radius; // same as sir stack[ir][0] = r[yi]; stack[ir][1] = g[yi]; stack[ir][2] = b[yi]; rbs = r1 - abs(i); rsum += r[yi] * rbs; gsum += g[yi] * rbs; bsum += b[yi] * rbs; if (i > 0) { rinsum += stack[ir][0]; ginsum += stack[ir][1]; binsum += stack[ir][2]; } else { routsum += stack[ir][0]; goutsum += stack[ir][1]; boutsum += stack[ir][2]; } if (i < hm) { yp += w; } } yi = x; stackpointer = radius; for (y = 0; y < h; y++) { output[yi].red = dv[rsum]; output[yi].green = dv[gsum]; output[yi].blue = dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; stackstart = stackpointer - radius + div; ir = stackstart % div; // same as sir routsum -= stack[ir][0]; goutsum -= stack[ir][1]; boutsum -= stack[ir][2]; if (x == 0) vmin[y] = min(y + r1, hm) * w; ip = x + vmin[y]; stack[ir][0] = r[ip]; stack[ir][1] = g[ip]; stack[ir][2] = b[ip]; rinsum += stack[ir][0]; ginsum += stack[ir][1]; binsum += stack[ir][2]; rsum += rinsum; gsum += ginsum; bsum += binsum; stackpointer = (stackpointer + 1) % div; ir = stackpointer; // same as sir routsum += stack[ir][0]; goutsum += stack[ir][1]; boutsum += stack[ir][2]; rinsum -= stack[ir][0]; ginsum -= stack[ir][1]; binsum -= stack[ir][2]; yi += w; } } // Unlocks everything AndroidBitmap_unlockPixels(env, bitmapIn); AndroidBitmap_unlockPixels(env, bitmapOut); LOGI ("Bitmap blurred."); } int min(int a, int b) { return a > b ? b : a; } int max(int a, int b) { return a > b ? a : b; }
然后像这样使用它(考虑一个名为com.insert.your.package.ClassName的类和一个名为functionToBlur的本地函数,如上面的代码所示):
// Create a copy Bitmap bitmapOut = bitmapIn.copy(Bitmap.Config.ARGB_8888, true); // Blur the copy functionToBlur(bitmapIn, bitmapOut, __radius);
它期望一个RGB_8888位图!
要使用RGB_565位图,请在传递参数(yuck)之前创建转换副本,或更改函数以使用新的rgb565
类型而不是rgba
:
typedef struct { uint16_t byte0; } rgb565;
问题是,如果你这样做,你不能阅读像素的.blue
, .green
和.blue
了,你需要正确地读取字节,杜。 当我需要之前,我做了这个:
r = (pixels[x].byte0 & 0xF800) >> 8; g = (pixels[x].byte0 & 0x07E0) >> 3; b = (pixels[x].byte0 & 0x001F) << 3;
但是可能有一些不那么愚蠢的做法。 恐怕我不是一个低级的C编码器。
您现在可以使用RenderScript库中的ScriptIntrinsicBlur快速模糊。 以下是如何访问RenderScript API。 以下是我为模糊视图和位图所做的一个类:
public class BlurBuilder { private static final float BITMAP_SCALE = 0.4f; private static final float BLUR_RADIUS = 7.5f; public static Bitmap blur(View v) { return blur(v.getContext(), getScreenshot(v)); } public static Bitmap blur(Context ctx, Bitmap image) { int width = Math.round(image.getWidth() * BITMAP_SCALE); int height = Math.round(image.getHeight() * BITMAP_SCALE); Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false); Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); RenderScript rs = RenderScript.create(ctx); ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); theIntrinsic.setRadius(BLUR_RADIUS); theIntrinsic.setInput(tmpIn); theIntrinsic.forEach(tmpOut); tmpOut.copyTo(outputBitmap); return outputBitmap; } private static Bitmap getScreenshot(View v) { Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b); v.draw(c); return b; } }
这段代码对我来说是完美的
Bitmap tempbg = BitmapFactory.decodeResource(getResources(),R.drawable.b1); //Load a background. Bitmap final_Bitmap = BlurImage(tempbg); @SuppressLint("NewApi") Bitmap BlurImage (Bitmap input) { try { RenderScript rsScript = RenderScript.create(getApplicationContext()); Allocation alloc = Allocation.createFromBitmap(rsScript, input); ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rsScript, Element.U8_4(rsScript)); blur.setRadius(21); blur.setInput(alloc); Bitmap result = Bitmap.createBitmap(input.getWidth(), input.getHeight(), Bitmap.Config.ARGB_8888); Allocation outAlloc = Allocation.createFromBitmap(rsScript, result); blur.forEach(outAlloc); outAlloc.copyTo(result); rsScript.destroy(); return result; } catch (Exception e) { // TODO: handle exception return input; } }
这对我来说工作得很好: 如何使用Android的RenderScript高效地模糊图像
public class BlurBuilder { private static final float BITMAP_SCALE = 0.4f; private static final float BLUR_RADIUS = 7.5f; @SuppressLint("NewApi") public static Bitmap blur(Context context, Bitmap image) { int width = Math.round(image.getWidth() * BITMAP_SCALE); int height = Math.round(image.getHeight() * BITMAP_SCALE); Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false); Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); RenderScript rs = RenderScript.create(context); ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); theIntrinsic.setRadius(BLUR_RADIUS); theIntrinsic.setInput(tmpIn); theIntrinsic.forEach(tmpOut); tmpOut.copyTo(outputBitmap); return outputBitmap; } }
感谢@Yahel的代码。 使用alpha通道模糊支持发送相同的方法,因为它花费了一些时间使其正常工作,以便节省时间:
/** * Stack Blur v1.0 from * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html * Java Author: Mario Klingemann <mario at quasimondo.com> * http://incubator.quasimondo.com * <p/> * created Feburary 29, 2004 * Android port : Yahel Bouaziz <yahel at kayenko.com> * http://www.kayenko.com * ported april 5th, 2012 * <p/> * 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. * <p/> * 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. * <p/> * If you are using this algorithm in your code please add * the following line: * Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com> */ public static Bitmap fastblur(Bitmap sentBitmap, float scale, int radius) { int width = Math.round(sentBitmap.getWidth() * scale); int height = Math.round(sentBitmap.getHeight() * scale); sentBitmap = Bitmap.createScaledBitmap(sentBitmap, width, height, false); Bitmap 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]; Log.e("pix", w + " " + h + " " + pix.length); 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 a[] = new int[wh]; int rsum, gsum, bsum, asum, 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][4]; int stackpointer; int stackstart; int[] sir; int rbs; int r1 = radius + 1; int routsum, goutsum, boutsum, aoutsum; int rinsum, ginsum, binsum, ainsum; for (y = 0; y < h; y++) { rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 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); sir[3] = 0xff & (p >> 24); rbs = r1 - Math.abs(i); rsum += sir[0] * rbs; gsum += sir[1] * rbs; bsum += sir[2] * rbs; asum += sir[3] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; ainsum += sir[3]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; aoutsum += sir[3]; } } stackpointer = radius; for (x = 0; x < w; x++) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; a[yi] = dv[asum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; asum -= aoutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; aoutsum -= sir[3]; 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); sir[3] = 0xff & (p >> 24); rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; ainsum += sir[3]; rsum += rinsum; gsum += ginsum; bsum += binsum; asum += ainsum; stackpointer = (stackpointer + 1) % div; sir = stack[(stackpointer) % div]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; aoutsum += sir[3]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; ainsum -= sir[3]; yi++; } yw += w; } for (x = 0; x < w; x++) { rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 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]; sir[3] = a[yi]; rbs = r1 - Math.abs(i); rsum += r[yi] * rbs; gsum += g[yi] * rbs; bsum += b[yi] * rbs; asum += a[yi] * rbs; if (i > 0) { rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; ainsum += sir[3]; } else { routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; aoutsum += sir[3]; } if (i < hm) { yp += w; } } yi = x; stackpointer = radius; for (y = 0; y < h; y++) { pix[yi] = (dv[asum] << 24) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; rsum -= routsum; gsum -= goutsum; bsum -= boutsum; asum -= aoutsum; stackstart = stackpointer - radius + div; sir = stack[stackstart % div]; routsum -= sir[0]; goutsum -= sir[1]; boutsum -= sir[2]; aoutsum -= sir[3]; 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]; sir[3] = a[p]; rinsum += sir[0]; ginsum += sir[1]; binsum += sir[2]; ainsum += sir[3]; rsum += rinsum; gsum += ginsum; bsum += binsum; asum += ainsum; stackpointer = (stackpointer + 1) % div; sir = stack[stackpointer]; routsum += sir[0]; goutsum += sir[1]; boutsum += sir[2]; aoutsum += sir[3]; rinsum -= sir[0]; ginsum -= sir[1]; binsum -= sir[2]; ainsum -= sir[3]; yi += w; } } Log.e("pix", w + " " + h + " " + pix.length); bitmap.setPixels(pix, 0, w, 0, 0, w, h); return (bitmap); }
对于选择NDK方法的未来Google员工 – 我发现可靠的提到的stackblur算法。 我发现C ++的实现不依赖SSE在这里 – http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32其中包含一些优化使用静态表,如:;
static unsigned short const stackblur_mul[255] = { 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512, 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512, 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456, 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512, 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328, 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456, 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335, 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512, 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405, 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328, 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271, 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456, 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388, 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335, 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292, 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259 }; static unsigned char const stackblur_shr[255] = { 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 };
I made modification of stackblur algorithm for multi-core systems – it can be found here http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ As more and more devices have 4 cores – optimizations give 4x speed benefit.
This is for all people who need to increase the radius of ScriptIntrinsicBlur
to obtain a harder gaussian blur.
Instead of to put the radius more than 25, you can scale down the image and get the same result. I wrote a class called GaussianBlur
. Below you can see how to use, and the whole class implementation.
用法:
GaussianBlur gaussian = new GaussianBlur(context); gaussian.setMaxImageSize(60); gaussian.setRadius(25); //max Bitmap output = gaussian.render(<your bitmap>,true); Drawable d = new BitmapDrawable(getResources(),output);
类:
public class GaussianBlur { private final int DEFAULT_RADIUS = 25; private final float DEFAULT_MAX_IMAGE_SIZE = 400; private Context context; private int radius; private float maxImageSize; public GaussianBlur(Context context) { this.context = context; setRadius(DEFAULT_RADIUS); setMaxImageSize(DEFAULT_MAX_IMAGE_SIZE); } public Bitmap render(Bitmap bitmap, boolean scaleDown) { RenderScript rs = RenderScript.create(context); if (scaleDown) { bitmap = scaleDown(bitmap); } Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); Allocation inAlloc = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE); Allocation outAlloc = Allocation.createFromBitmap(rs, output); ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, inAlloc.getElement()); // Element.U8_4(rs)); script.setRadius(getRadius()); script.setInput(inAlloc); script.forEach(outAlloc); outAlloc.copyTo(output); rs.destroy(); return output; } public Bitmap scaleDown(Bitmap input) { float ratio = Math.min((float) getMaxImageSize() / input.getWidth(), (float) getMaxImageSize() / input.getHeight()); int width = Math.round((float) ratio * input.getWidth()); int height = Math.round((float) ratio * input.getHeight()); return Bitmap.createScaledBitmap(input, width, height, true); } public int getRadius() { return radius; } public void setRadius(int radius) { this.radius = radius; } public float getMaxImageSize() { return maxImageSize; } public void setMaxImageSize(float maxImageSize) { this.maxImageSize = maxImageSize; } }
Nicolas POMEPUY advice. I think this link will be helpful: Blur effect for Android design
Sample project at github
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) private static Bitmap fastblur16(Bitmap source, int radius, Context ctx) { Bitmap bitmap = source.copy(source.getConfig(), true); RenderScript rs = RenderScript.create(ctx); Allocation input = Allocation.createFromBitmap(rs, source, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); Allocation output = Allocation.createTyped(rs, input.getType()); ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); script.setRadius(radius); script.setInput(input); script.forEach(output); output.copyTo(bitmap); return bitmap; }
We tried to implement RenderScript blur like mentioned above in different answers. We were limited to use the v8 RenderScript version and that caused us a lot of trouble.
- Samsung S3 crashed randomly whenever we tried to use the renderscript
- Other devices (across different APIs) randomly showed different color issues
I want to share our dirty Java-only version which is slow and should be done on a separate thread and, if possible, before usage and therefore persisted.
private final Paint mPaint = new Paint(); public Bitmap blur(final String pathToBitmap) { final BitmapFactory.Options options = new BitmapFactory.Options(); final Bitmap normalOne = BitmapFactory.decodeFile(pathToBitmap, options); final Bitmap resultBitmap = Bitmap.createBitmap(options.outWidth, options.outHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(resultBitmap); mPaint.setAlpha(180); canvas.drawBitmap(normalOne, 0, 0, mPaint); int blurRadius = 12; for (int row = -blurRadius; row < blurRadius; row += 2) { for (int col = -blurRadius; col < blurRadius; col += 2) { if (col * col + row * row <= blurRadius * blurRadius) { mPaint.setAlpha((blurRadius * blurRadius) / ((col * col + row * row) + 1) * 2); canvas.drawBitmap(normalOne, row, col, mPaint); } } } normalOne.recycle(); return resultBitmap; }
This solution is far from perfect but creates a reasonable blur effect based on the fact, that it draws highly transparent version of the same image on top of a barely transparent "sharp" version. The alpha depends on the distance to the origin.
You can adjust some "magic numbers" to your needs. I just wanted to share that "solution" for everybody who has issues with the v8 support version of RenderScript.
I used this before..
public static Bitmap myblur(Bitmap image, Context context) { final float BITMAP_SCALE = 0.4f; final float BLUR_RADIUS = 7.5f; int width = Math.round(image.getWidth() * BITMAP_SCALE); int height = Math.round(image.getHeight() * BITMAP_SCALE); Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false); Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); RenderScript rs = RenderScript.create(context); ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); theIntrinsic.setRadius(BLUR_RADIUS); theIntrinsic.setInput(tmpIn); theIntrinsic.forEach(tmpOut); tmpOut.copyTo(outputBitmap); return outputBitmap; }
For those still having issues with Renderscript support library on x86 chipsets, please have a look at this post by the creator of the library. It looks like the fix he prepared didn't make it somehow to the Build Tools v20.0.0, so he provides the files to fix it manually and a brief explanation of how to do it.
from Mario Viviani blog one can use this solution from 17 Android Version:
https://plus.google.com/+MarioViviani/posts/fhuzYkji9zz
要么
Here is a realtime blurring overlay using RenderScript, which seems to be fast enough.
I found that decreasing contrast, brightness and saturation a little makes blurred images more pretty so I combined various methods from stack overflow and made this Blur Class which deals with blurring images, changing brightness, saturation, contrast and size of the blurred images. It can also convert images from drawable to bitmap and vice-versa.