Fast Bitmap Blur для Android SDK


185

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

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

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


2
К сожалению, изображения всегда будут разными, поэтому я не смогу заранее создать размытую версию. Кроме того, я не буду заранее знать интенсивность размытия.
Грег

Не могли бы вы опубликовать свой код, возможно, это неэффективный алгоритм / код, 30 секунд на просмотр изображения 640x480 идет медленно, я бы подумал, что 5 секунд - это медленно, но опять же зависит от процессора.
Викирк

Ответы:


78

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


5
После некоторого тестирования и размытия, которое я делаю, этот факт работает достаточно хорошо для меня, и это быстро. Спасибо!
Грег

4
Если это работает хорошо. Обидно, что мы так и не дошли до того, почему это было неэффективно.
Викирк

Возможно, вы захотите попробовать createScaledBitmap и оставить изображение того же размера. Это
размыто

1
Вот обсуждение значения аргумента «фильтр»: stackoverflow.com/questions/2895065
user1364368

4
Это не совсем то, что нужно по двум причинам: 1) ему нужна память полного изображения, хотя вы, вероятно, используете только уменьшенное изображение 2) вам нужно загрузить полное изображение медленнее - используйте загрузку с помощью inSampleSize и BitmapFactory.decodeResource (), который является гораздо лучшим решением для этого.
Патрик Фавр

303

Для будущих гуглеров, вот алгоритм, который я перенес из Quasimondo. Это своего рода смесь между размытием рамки и размытием по Гауссу, она очень красивая и довольно быстрая.

Обновление для людей, сталкивающихся с проблемой ArrayIndexOutOfBoundsException: @anthonycr в комментариях предоставляет следующую информацию:

Я обнаружил, что при замене Math.abs на StrictMath.abs или какой-либо другой реализации abs сбой не происходит.

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

4
Спасибо, Яхель. Вы решили мою проблему. Спасибо еще раз.
Йог Гуру

1
что я должен пройти как радиус?
krisDrOid

16
Для радиуса больше 1 иногда вы получаете ArrayIndexOutOfBoundsException. Я постараюсь определить проблему.
MikeL

7
@MichaelLiberman Я также столкнулся с той же проблемой. Выяснил, почему еще? "g [yi] = dv [gsum];" -> ошибка: java.lang.ArrayIndexOutOfBoundsException: length = 112896; index = 114021
2851

2
Столкнулся с известным ArrayIndexOutOfBoundsException, и после некоторого анализа, я полагаю, это вызвано неправильной оптимизацией VM Dalvik. В forцикле непосредственно перед плохим разыменованием, либо вычисление rbsпеременной, либо вычисление gsum, rsumили bsumпеременные выполняются неправильно. Я обнаружил , что путем замены Math.absс StrictMath.absили какой - либо другой absреализацией, аварии не происходят. Поскольку StrictMath.absсам делегат Math.abs, кажется, что это должна быть плохая оптимизация.
Anthonycr

255

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

Это загрузит растровое изображение с inSampleSize8, так что только 1/64 исходного изображения. Проверьте, что inSampleSizeсоответствует вашим потребностям, но оставьте 2 ^ n (2,4,8, ...), чтобы избежать ухудшения качества из-за масштабирования. Смотрите Google Doc для более

Еще одно большое преимущество заключается в том, что загрузка растровых изображений будет очень быстрой. В моем раннем тесте размытия я понял, что самое длительное время в течение всего процесса размытия была загрузка изображения. Таким образом, чтобы загрузить изображение 1920x1080 с диска, моему Nexus 5 понадобилось 500 мс, а размытие заняло еще 250 мс или около того.

Используйте Renderscript

Renderscript предоставляет ScriptIntrinsicBlurфильтр размытия по Гауссу. Он имеет хорошее визуальное качество и является самым быстрым на Android. Google утверждает, что «как правило, в 2-3 раза быстрее, чем многопоточная реализация C, и часто в 10 раз быстрее, чем реализация Java» . Renderscript действительно сложен (использует самое быстрое устройство обработки (GPU, ISP и т. Д.) И т. Д.), И есть библиотека поддержки v8, что делает его совместимым до 2.2, Ну, по крайней мере, теоретически, благодаря моим собственным тестам и отчетам от других разработчиков кажется, что невозможно использовать Renderscript вслепую, поскольку фрагментация аппаратного обеспечения / драйвера, кажется, вызывает проблемы с некоторыми устройствами, даже с более высоким уровнем SDK (например, у меня было проблемы с 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на изображении 100x100 с 500 раундами)

введите описание изображения здесь

Шипы являются gc.

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

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

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

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

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

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

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

Схема, где Нурик объясняет свой подход


1
Прежде всего, спасибо за ваш тяжелый труд! Но у меня возник вопрос: «потому что он использует режим USAGE_SHARED, который повторно использует память». Где вы нашли константу USAGE_SHARED? Я не мог найти это нигде.
Какой-то нубский студент

2
Я нашел это, USAGE_SHARED доступен только в support.v8.renderscript
Некоторые Noob Student

2
Renderscript fast Gaussian Blur не удается с ошибками распределения памяти C на младших устройствах. Протестировано на ZTE Z992 (Android 4.1.1) и Kyocera Rise (Android 4.0.4) с использованием прилагаемого приложения Play Store. Также был отчет о сбое на Samsung Galaxy S3 mini. Так как ошибки возникают на уровне C, они не могут быть перехвачены как исключения в Java, а это означает, что сбой приложения неизбежен. Похоже, RenderScript может быть не готов к использованию.
Тео

4
для более новых версий Gradle, используйте renderscriptSupportModeEnabled trueили он не будет собираться ! Я искал вечно!
СЕБ


53

РЕДАКТИРОВАТЬ (апрель 2014 г.): Это страница вопросов / ответов, которая все еще получает много просмотров, как кажется. Я знаю, что я всегда получаю голоса за этот пост. Но если вы читаете это, вы должны понимать, что ответы, опубликованные здесь (и мой, и принятый ответ), устарели. Если вы хотите реализовать эффективное размытие сегодня , вы должны использовать RenderScript вместо NDK или Java. RenderScript работает на Android 2.2+ (с использованием библиотеки поддержки Android ), поэтому нет причин не использовать его.

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


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

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

Для справки: с помощью Java-функции Yahel потребовалось 10 секунд, чтобы смазать мое изображение размером 480x532 пикселей с радиусом размытия 10. Но при использовании родной версии C потребовалось 250 мс. И я почти уверен, что он еще может быть оптимизирован ... Я просто сделал глупое преобразование кода 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пикселя больше, вам нужно правильно прочитать байт, да. Когда мне это было нужно раньше, я делал это:

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

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


Спасибо, это мне очень помогло :)
Дмитрий Зайцев

18
НО это требует много памяти. Для уменьшения памяти типа изменения потребления r[wh], g[wh]и b[wh]в uint8_t.
Дмитрий Зайцев

pastebin.com
Можете

1
Рабочий пример RenderScript для Gaussian Blur на Android SDK 17+ можно посмотреть здесь: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Мартин Маркончини,

2
RenderScript также доступен как часть библиотеки поддержки для Android 2.2+, поэтому нет причин больше не использовать его везде: android-developers.blogspot.com/2013/09/…
zeh

14

Этот код идеально подходит для меня

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

}


12

Теперь вы можете использовать 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;
    }
}

2
Контекст Renderscript не должен создаваться в методе размытия, а должен управляться статически или передаваться методу. (если вы не возражаете против производительности)
Патрик Фавр

1
Можете ли вы привести пример этого использования? Когда я пытаюсь использовать его, я получаю следующую ошибку: java.lang.IllegalArgumentException: ширина и высота должны быть> 0
Донал Рафферти

10

Для меня это работало нормально: как эффективно размыть изображения с помощью 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;
    }
}

Это было бы еще лучше / быстрее, если бы вы кэшировали созданный вами объект RenderScript. Создание нового при каждом размытии изображения просто добавляет ненужные издержки (создание / уничтожение объекта Java).
Стивен Хайнс,


4

Это для всех людей, которым нужно увеличить радиус, ScriptIntrinsicBlurчтобы получить более жесткое размытие по Гауссу.

Вместо того, чтобы поставить радиус больше 25, вы можете уменьшить изображение и получить тот же результат. Я написал класс под названием GaussianBlur. Ниже вы можете увидеть, как использовать и весь класс реализации.

Использование:

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

нет, если вы уменьшите изображение, чтобы увеличить его позже, вы получите блочное изображение вместо размытого изображения :(
loki

4

Спасибо @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);
}

все еще с «индексом вне диапазона» в устройствах> hdpi в качестве исходного источника
Габриэль Феррейра

4

Я использовал это раньше ..

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

2

Для будущих 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
}; 

Я сделал модификацию алгоритма stackblur для многоядерных систем - его можно найти здесь http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ Поскольку все больше и больше устройств имеют 4 ядра, оптимизации дают 4х скоростное преимущество.


1

Николя ПОМЕПУЙ совет. Я думаю, что эта ссылка будет полезна: эффект размытия для дизайна Android

Пример проекта на 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;
}

Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится.
Амаль Мурали

1
Амаль Мурали, ты прав. Теперь поменяй мой пост. Хорошо, что вы не только понизили голос, но и прокомментировали.
Юра Шинкарев

1

Мы попытались реализовать размытие RenderScript, как указано выше в разных ответах. Мы были ограничены в использовании версии RenderScript v8, и это доставило нам много хлопот.

  • Samsung S3 зависал случайно, когда мы пытались использовать Renderscript
  • Другие устройства (через разные API) случайно показали разные цветовые проблемы

Я хочу поделиться нашей грязной версией только для Java, которая медленная и должна быть сделана в отдельном потоке и, если возможно, перед использованием и, следовательно, сохранена.

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

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

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


У меня также есть проблема с RenderScript на некоторых старых устройствах armeabi, но ваше решение занимает слишком много памяти.
AsafK

0

Для тех, у кого все еще есть проблемы с библиотекой поддержки Renderscript на чипсетах x86, пожалуйста, посмотрите на этот пост создателя библиотеки. Похоже, что подготовленное им исправление каким-то образом не помогло Build Tools v20.0.0, поэтому он предоставляет файлы для исправления вручную и краткое объяснение того, как это сделать.

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




0

Я обнаружил, что уменьшение контраста, яркости и насыщенности немного делает размытые изображения более красивыми, поэтому я объединил различные методы от переполнения стека и создал класс размытия, который работает с размытыми изображениями, изменением яркости, насыщенности, контрастности и размера размытых изображений. Он также может конвертировать изображения из растрового в растровое и наоборот.


0

На I / O 2019 было представлено следующее решение:

/**
 * Blurs the given Bitmap image
 * @param bitmap Image to blur
 * @param applicationContext Application context
 * @return Blurred bitmap image
 */
@WorkerThread
fun blurBitmap(bitmap: Bitmap, applicationContext: Context): Bitmap {
    lateinit var rsContext: RenderScript
    try {

        // Create the output bitmap
        val output = Bitmap.createBitmap(
                bitmap.width, bitmap.height, bitmap.config)

        // Blur the image
        rsContext = RenderScript.create(applicationContext, RenderScript.ContextType.DEBUG)
        val inAlloc = Allocation.createFromBitmap(rsContext, bitmap)
        val outAlloc = Allocation.createTyped(rsContext, inAlloc.type)
        val theIntrinsic = ScriptIntrinsicBlur.create(rsContext, Element.U8_4(rsContext))
        theIntrinsic.apply {
            setRadius(10f)
            theIntrinsic.setInput(inAlloc)
            theIntrinsic.forEach(outAlloc)
        }
        outAlloc.copyTo(output)

        return output
    } finally {
        rsContext.finish()
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.