Быстрое размытие растровых изображений для Android SDK

В настоящее время в Android-приложении, которое я разрабатываю, я просматриваю пиксели изображения, чтобы размыть его. Это занимает около 30 секунд на изображении 640×480.

Во время просмотра приложений на Android Market я столкнулся с тем, что включает функцию размытия, и их размытие происходит очень быстро (например, 5 секунд), поэтому они должны использовать другой способ размытия.

Кто-нибудь знает более быстрый способ, кроме прокрутки пикселей?

Solutions Collecting From Web of "Быстрое размытие растровых изображений для Android SDK"

Это выстрел в темноте, но вы можете попытаться сжать изображение, а затем снова увеличить его. Это можно сделать с помощью Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) . Убедитесь, что параметр фильтра соответствует true. Он будет работать в собственном коде, чтобы он мог быть быстрее.

Для будущих Googlers, вот алгоритм, который я перенес из 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 Blur Guide 2016

С помощью приложения Showcase / Benchmark и источника на Github . Также проверьте структуру Blur, над которой я сейчас работаю: Dali .

После много экспериментов я могу теперь с уверенностью дать вам некоторые солидные рекомендации, которые упростят вашу жизнь в Android при использовании 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 который является фильтром размытия Gaussian. Он обладает хорошим визуальным качеством и является самым быстрым, что вы реально получаете на Android. Google утверждает, что он «обычно на 2-3 раза быстрее, чем многопоточная реализация C, и часто на 10x + быстрее, чем реализация Java» . Renderscript действительно сложный (с использованием устройства обработки fastes (GPU, ISP и т. Д.) И т. Д.), А также имеется библиотека поддержки v8, благодаря чему она совместима с 2.2 . По крайней мере теоретически, по моим собственным тестам и отчетам от других разработчиков, кажется, что невозможно использовать renderscript вслепую, так как фрагментация аппаратного / драйвера, похоже, вызывает проблемы с некоторыми устройствами, даже с более высоким уровнем 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); 

При использовании поддержки V8 с Gradle, которая специально рекомендована Google, «потому что они включают в себя последние улучшения» , вам нужно всего лишь добавить 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.

Вы можете проверить себя, тестовое приложение находится в игровом магазине: BlurBenchmark

Reuse Bitmap, где это возможно (если prio: производительность> объем памяти)

Если вам нужно несколько размытий для живого размытия или аналогичного, и ваша память позволяет ему не загружать растровое изображение из чертежей несколько раз, но сохраняйте его «кэшированным» в переменной-члене. В этом случае всегда старайтесь использовать одни и те же переменные, чтобы свести сбор мусора до минимума.

Также проверьте новую опцию «inBitmap» при загрузке из файла или выталкиваемой, которая будет повторно использовать растровую память и экономит время сбора мусора.

Для смешивания от резкого до размытия

Простой и наивный метод состоит только в том, чтобы использовать 2 изображения, один размытый, и альфа исчезают. Но если вам нужен более сложный вид, который плавно исчезает от резкого до размытия, тогда посмотрите сообщение Романа Нурика о том, как это сделать, как в его приложении Muzei .

В основном он объясняет, что он предварительно размывает некоторые кадры с разным размытием и использует их в качестве ключевых кадров в анимации, которая выглядит очень гладко

Диаграмма, где Нурик преуспевает в своем подходе

EDIT (апрель 2014): Это страница вопросов / ответов, которая по-прежнему получает много хитов. Я знаю, что я всегда получаю вознаграждение за этот пост. Но если вы читаете это, вам нужно понять, что ответы, опубликованные здесь (как мои, так и принятый ответ) устарели. Если вы хотите реализовать эффективное размытие сегодня , вы должны использовать RenderScript вместо NDK или Java. RenderScript работает на Android 2.2+ (используя библиотеку поддержки Android ), поэтому нет причин не использовать его.

Старый ответ следует, но будьте осторожны, поскольку он устарел.


Для будущих googlers2, вот алгоритм, который я портировал из порта Яхеля алгоритма Квазимондо, но используя NDK. Разумеется, это основано на ответе Яхеля. Но на этом работает собственный C-код, поэтому он быстрее. Намного быстрее. Например, в 40 раз быстрее.

Я считаю, что использование NDK заключается в том, как все манипуляции с изображениями должны выполняться на Android … сначала это раздражает, чтобы реализовать (читайте отличный учебник по использованию JNI и NDK здесь ), но гораздо лучше, и почти в режиме реального времени для много вещей.

Для справки, используя функцию Java Yahel, понадобилось 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; 

Проблема в том, что если вы это сделаете, вы больше не сможете читать .red , .green и .blue пикселя, вам нужно правильно прочитать байт, duh. Когда мне это было нужно, я сделал это:

 r = (pixels[x].byte0 & 0xF800) >> 8; g = (pixels[x].byte0 & 0x07E0) >> 3; b = (pixels[x].byte0 & 0x001F) << 3; 

Но, вероятно, есть несколько менее глупый способ сделать это. Боюсь, я не очень похожа на низкоуровневый C-кодер.

Теперь вы можете использовать ScriptIntrinsicBlur из библиотеки RenderScript для быстрого размытия. Вот как получить доступ к API RenderScript. Следующий класс, который я сделал для размытия видов и растровых изображений:

 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; } } 

Это отлично работает для меня: как эффективно размывать изображения с помощью RenderScript от Android

 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; } } 

Используйте Render Script, как указано здесь http://blog.neteril.org/blog/2013/08/12/blurring-images-on-android/

Спасибо @Yahel за код. Проводка того же метода с поддержкой размывания альфа-канала, поскольку мне потребовалось некоторое время, чтобы он работал правильно, чтобы он мог сэкономить чье-то время:

 /** * 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); } 

Для будущих Googlers, которые выбирают подход NDK – я нахожу надежный упомянутый алгоритм 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.

https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=71347

from Mario Viviani blog one can use this solution from 17 Android Version:

https://plus.google.com/+MarioViviani/posts/fhuzYkji9zz

или

https://gist.github.com/Mariuxtheone/903c35b4927c0df18cf8

Here is a realtime blurring overlay using RenderScript, which seems to be fast enough.

https://github.com/mmin18/RealtimeBlurView

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.