Fast Bitmap Blur для Android SDK

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

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

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


person Greg    schedule 14.01.2010    source источник
comment
К сожалению, изображения всегда будут разными, поэтому я не смогу заранее создать размытую версию. Плюс я не узнаю заранее интенсивность размытия.   -  person Greg    schedule 15.01.2010
comment
Не могли бы вы опубликовать свой код, возможно, это алгоритм / код, который неэффективен, 30 секунд для просмотра изображения 640x480 - это медленно, я бы подумал, что 5 секунд медленно, но опять же, это зависит от процессора.   -  person vickirk    schedule 15.01.2010


Ответы (19)


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

person Luke    schedule 15.01.2010
comment
После некоторого тестирования и размытия, которое я делаю, это работает для меня достаточно хорошо и быстро. Спасибо! - person Greg; 15.01.2010
comment
Если все работает хорошо. Жаль, что мы так и не разобрались, почему это было неэффективно. - person vickirk; 19.01.2010
comment
Вы можете попробовать createScaledBitmap и оставить изображение того же размера. У меня это размывает :-( - person Casebash; 14.05.2010
comment
Вот обсуждение значения фильтра аргументов: stackoverflow.com/questions/2895065 - person user1364368; 26.12.2013
comment
Это не совсем способ получить по двум причинам: 1) ему нужна память полного изображения, хотя вы, вероятно, используете только уменьшенное изображение 2) вам нужно загрузить полное изображение, которое медленнее - используйте загрузку с inSampleSize и BitmapFactory.decodeResource (), что намного превосходит это решение. - person Patrick Favre; 24.05.2014

Для будущих гуглеров вот алгоритм, который я перенес из 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 <[email protected]>
 */

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);
}
person Yahel    schedule 05.04.2012
comment
Спасибо Яхель. Вы решили мою проблему. Еще раз спасибо. - person Yog Guru; 17.06.2013
comment
На это уходит вдвое меньше времени, чем на размытие с помощью ConvolutionMatrix. - person Sileria; 12.09.2013
comment
что я должен передать как радиус? - person krisDrOid; 18.02.2014
comment
Давненько я не публиковал это, но насколько я помню, чем больше радиус, тем более размытым окончательное изображение. Алгоритм смешивает пиксели по кругу шириной радиуса. Окончательный цвет изменяемого пикселя - это средний цвет всех пикселей в радиусном круге. - person Yahel; 03.03.2014
comment
Для радиуса больше 1 иногда возникает исключение ArrayIndexOutOfBoundsException. Постараюсь выявить проблему. - person MikeL; 09.03.2014
comment
@MichaelLiberman Я тоже столкнулся с той же проблемой. Еще не выяснил, почему? g [yi] = dv [gsum]; - ›ошибка: java.lang.ArrayIndexOutOfBoundsException: length = 112896; index = 114021 - person see2851; 28.09.2014
comment
@MichaelLiberman Вы когда-нибудь выявляли проблему ArrayIndexOutOfBoundsException? У нас та же проблема. - person Sileria; 10.03.2015
comment
@Mobistry Извините, не помню, как починил. Если я найду старый код, я опубликую решение здесь. - person MikeL; 10.03.2015
comment
@ OlcayErtaş: Это должно быть одно большое растровое изображение, которое вы используете :) - person Yahel; 29.04.2015
comment
Обратите внимание, что это не работает, если ваше приложение не полноэкранное, потому что верхняя панель инструментов не принимается во внимание. Как только вы установите фон, вы увидите, что синие эффекты отключены. - person portfoliobuilder; 06.08.2015
comment
Оптимизируйте функцию, сначала масштабируя растровое изображение: int width = Math.round (sentBitmap.getWidth () * scale); int height = Math.round (sentBitmap.getHeight () * масштаб); sentBitmap = Bitmap.createScaledBitmap (sentBitmap, ширина, высота, ложь); .Пример значения шкалы: 0,2f. Необходимый радиус также меньше, поэтому это намного быстрее. - person radu122; 10.08.2015
comment
Спасибо. Я без проблем портировал на Xamarin. - person Ian Newson; 20.10.2015
comment
Важно знать: sentBitmap.getConfig () может возвращать значение null, поэтому у вас должен быть запасной вариант. Например, создайте конфигурацию по умолчанию. Кроме того, можно ли использовать тот же алгоритм в Renderscript, чтобы сделать его быстрее? - person android developer; 31.12.2015
comment
Какие рекомендуемые параметры для масштаба и радиуса? - person Janneman96; 09.09.2016
comment
@ Janneman96: Все зависит от того, какого эффекта вы хотите достичь. Чем выше масштаб и радиус, тем более размытым становится изображение. Также не то, что чем выше радиус, тем больше процессора потребляет алгоритм. - person Yahel; 12.09.2016
comment
@Yahel 21 В итоге я использовал (0.2f, 10) - person Janneman96; 13.09.2016
comment
@NigamPatro, К сожалению, я этого не сделал, так как это был побочный проект, над которым я больше не работаю. Есть ли причина, по которой вы не хотите использовать RenderScript сейчас? - person MikeL; 08.05.2017
comment
@MikeL Renderscript работает не на всех устройствах. Некоторые устройства не работают. До сих пор тоже не работал, но узнал от других разработчиков. - person Nigam Patro; 09.05.2017
comment
@ OlcayErtaş и все остальные получают ошибку OOM: добавьте android: largeHeap = true в ваше ‹application› в AndroidManifest.xml. Я тоже испытывал это на разных устройствах. Теперь это исправлено! - person the_dude_abides; 02.08.2017
comment
Обходной путь - это просто проверка длины массива. if (rsum ›= dv.length) {Log.e (TAG, вне границы rsum: + rsum); } еще {r [yi] = dv [rsum]; } if (gsum ›= dv.length) {Log.e (TAG, вне границы gsum: + gsum); } else {g [yi] = dv [gsum]; } if (bsum ›= dv.length) {Log.e (TAG, вне границы bsum: + bsum); } еще {b [yi] = dv [bsum]; } - person KoreanDude; 10.11.2017
comment
Забавно то, что когда я запустил приложение в режиме отладки, размытие работало нормально. Заглянул в него и обнаружил, что bitmap.getPixels () возвращает другой массив int (другой размер, другой порядок данных), чем при запуске в обычном режиме. Поиграв с ним некоторое время, я просто добавил проверку длины и двинулся дальше. Теперь все работает. - person KoreanDude; 10.11.2017
comment
Кто-нибудь знает, почему здесь используются массивы int? Мы определенно можем использовать меньше памяти, если вместо этого будем использовать байты со знаком или, по крайней мере, сокращенно для массивов r, g и b, может быть, и для других. - person A. Kazarovets; 04.04.2019
comment
Заглянул в известное исключение ArrayIndexOutOfBoundsException, и после некоторого анализа я считаю, что это вызвано неправильной оптимизацией виртуальной машины Dalvik. В цикле for непосредственно перед неправильным разыменованием либо вычисление переменной rbs, либо вычисление переменных gsum, rsum или bsum выполняются неправильно. Я обнаружил, что при замене Math.abs на StrictMath.abs или какой-либо другой реализации abs сбой не происходит. Поскольку StrictMath.abs сам делегирует Math.abs, похоже, что это плохая оптимизация. - person anthonycr; 07.05.2019
comment
Спасибо! Оптимизирован для меньшего потребления памяти и отсутствия копирования: gist.github.com/Miha-x64/3fb489d13d/Miha-x64/3fb489d13d/ а> - person Miha_x64; 20.10.2020

Руководство Android Blur 2016

с приложением Showcase / Benchmark и Источник на Github. Также ознакомьтесь с фреймворком Blur, над которым я сейчас работаю: Дали.

После долгих экспериментов я теперь могу смело дать вам несколько надежных рекомендаций, которые облегчат вашу жизнь в 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 x 1080 с диска, моему Nexus 5 потребовалось 500 мс, в то время как размытие заняло всего 250 мс или около того.

Использовать Renderscript

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

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

Шипы gc.

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

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

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

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

Для перехода от резкого к размытому

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

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

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

person Patrick Favre    schedule 16.04.2014
comment
Прежде всего, спасибо за вашу тяжелую работу! Но у меня вопрос: потому что он использует режим USAGE_SHARED, который повторно использует память. Где вы нашли константу USAGE_SHARED? Я нигде не мог найти. - person Some Noob Student; 15.06.2014
comment
Я нашел, USAGE_SHARED доступен только в support.v8.renderscript - person Some Noob Student; 15.06.2014
comment
Чувак, это заслуживает ТАКОГО ОБЪЯВЛЕНИЯ, что, если бы я мог, я бы проголосовал тысячу раз. : D - person WitaloBenicio; 15.06.2014
comment
@SomeNoobStudent, это неправда, USAGE_SHARED был добавлен в API lvl 18, поэтому ваша цель, вероятно, ниже developer.android.com/reference/android/renderscript/ - person Patrick Favre; 15.06.2014
comment
@ for3st: Понятно, еще раз спасибо. Вы сравнивали давление памяти / кучи между разными методами? Я слышал, что scriptintrinsicblur может занимать много места в куче ... - person Some Noob Student; 16.06.2014
comment
@SomeNoobStudent нет, подсказка от json sams от команды google renderscript, я не проверял. Я ДЕЙСТВИТЕЛЬНО не уверен, что выделенная память renderscript находится в куче, потому что она компилируется в C afaik, который не будет ограничен / с использованием памяти кучи. - person Patrick Favre; 16.06.2014
comment
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 не готов к производственному использованию. - person Theo; 12.08.2014
comment
Кроме того, обычное быстрое размытие по Гауссу не работает на Nexus 5 (Android 4.4.4) с IllegalStateException при использовании кода или тестового приложения Play Store. - person Theo; 12.08.2014
comment
Лучший ответ, который я читал на stackoverflow до сих пор! Отличная работа, мужик :) - person MrMaffen; 03.09.2014
comment
для более новых версий Gradle используйте renderscriptSupportModeEnabled true, иначе он не будет построен! Искал вечно! - person seb; 17.11.2014
comment
спасибо @seb; изменение синтаксиса относится к плагину android gradle v0.14 +; Я обновил ответ - person Patrick Favre; 17.11.2014
comment
Когда я попробовал это решение, вместо получения размытого растрового изображения я получил растровое изображение цвета радуги. Кто-нибудь еще испытывает эту проблему? Если да, то как вы это исправили? - person HaloMediaz; 26.07.2015
comment
Просто начал получать то же, что и @HaloMediaz - person JMRboosties; 07.08.2015
comment
Это быстрее, чем использование на Java, но, судя по тому, что я заметил, результат относительно плохого качества по сравнению с решением fastBlur в другом сообщении (здесь: stackoverflow.com/a/10028267/878126). возможно ли преобразовать их решение в RS? - person android developer; 31.12.2015
comment
Идея сжатия изображения до низкого качества и последующего размытия сработала :) - person karthik kolanji; 12.02.2016

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

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


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

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

Для справки, при использовании Java-функции Yahel потребовалось 10 секунд, чтобы размыть мое изображение размером 480x532 пикселей с радиусом размытия 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, либо создайте преобразованную копию перед передачей параметра (фу), либо измените функцию, чтобы использовать новый тип 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.

person zeh    schedule 14.08.2012
comment
НО для этого требуется много памяти. Чтобы уменьшить потребление памяти, измените тип r[wh], g[wh] и b[wh] на uint8_t. - person Dmitry Zaytsev; 31.08.2012
comment
не могли бы вы показать мне, как выглядит ваш файл Android.mk на pastebin.com - person CQM; 29.01.2013
comment
@CQM: Конечно: pastebin.com/8VMTZy85 Однако учтите, что использование RenderScript, вероятно, более простое, современное и потенциально более быстрое решение этой проблемы (доступно только на Android 4 +, хотя). - person zeh; 29.01.2013
comment
@zeh, вчера вечером реализовал. Это достаточно быстро, но какие значения я должен передать для Radius, я произвольно ввел 5, но я не понимаю последствий (время или эстетика размытия), должна ли это быть динамическая переменная, основанная на разрешении? - person CQM; 29.01.2013
comment
@CQM: Это должно быть то, чем вы хотите его размыть. Чем выше число, тем более размытым оно становится. Я также использую произвольное значение 14, но это потому, что у меня есть статические исходные изображения, которые всегда имеют одинаковый размер. В зависимости от того, что вам нужно и как вы это представляете, вам может потребоваться принять во внимание разрешение устройства или размер изображения. Я не измерял влияние времени различных значений радиуса. Поскольку это размытие рамки, я предполагаю, что есть некоторое ухудшение эффекта, если радиус слишком велик, но я не проанализировал алгоритм достаточно, чтобы быть уверенным. - person zeh; 29.01.2013
comment
@zeh, подскажите, пожалуйста, как заменить output [yi] .red = dv [rsum]; вывод [yi] .green = dv [gsum]; вывод [yi] .blue = dv [bsum]; для изображений RGB_565? - person Zakharov Roman; 15.08.2013
comment
@zeh у вас есть пример скрипта рендеринга? - person Chris.Jenkins; 02.09.2013
comment
@ Chris.Jenkins: Я не знаю, я просто знаю, что это должно быть сделано на основе документации. - person zeh; 04.09.2013
comment
Рабочий пример RenderScript размытия по Гауссу в Android SDK 17+ см. Здесь: stackoverflow.com/questions/14988990/android-fast-bitmap-blur - person Martin Marconcini; 25.09.2013
comment
RenderScript также доступен как часть библиотеки поддержки для Android 2.2+, поэтому нет причин не использовать его повсюду больше: android-developers.blogspot.com/2013/09/ - person zeh; 27.09.2013
comment
это быстро, но по какой-то причине я не мог заставить его работать на doInBackground AsyncTask. Когда я запускаю его на doInBackground, он всегда терпит неудачу - person xiaowoo; 17.12.2013
comment
Есть ли какой-нибудь пример проекта, в котором используется RenderScript с поддержкой библиотеки? - person seb; 15.03.2014

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

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

}
person Niks    schedule 11.08.2014
comment
Самый простой способ размыть изображение (y) - person VAdaihiep; 21.11.2015

Теперь вы можете использовать ScriptIntrinsicBlur из библиотеки RenderScript для быстрого размытия. Вот как получить доступ к 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;
    }
}
person b_yng    schedule 10.01.2014
comment
Контекст Renderscript не должен создаваться в методе размытия, он должен управляться статически или передаваться методу. (если вы против производительности) - person Patrick Favre; 22.04.2014
comment
Вы можете привести пример такого использования? Когда я пытаюсь использовать его, я получаю следующую ошибку: java.lang.IllegalArgumentException: ширина и высота должны быть ›0 - person Donal Rafferty; 18.05.2014

У меня это сработало: Как эффективно размывать изображения с помощью 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;
    }
}
person Artificioo    schedule 03.09.2015
comment
Это было бы еще лучше / быстрее, если бы вы кэшировали созданный вами объект RenderScript. Создание нового экземпляра каждый раз, когда вы хотите размыть изображение, просто добавляет ненужные накладные расходы (создание / уничтожение Java-объекта). - person Stephen Hines; 08.09.2015

Спасибо @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 <[email protected]>
 */

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);
}
person vir us    schedule 07.10.2015
comment
все еще с 'index out of range' на устройствах ›hdpi в качестве исходного источника - person Gabriel Ferreira; 05.07.2016

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

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;
        }
person Huseyin    schedule 19.09.2016

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

person Rohit    schedule 06.12.2013

Это для всех, кому нужно увеличить радиус 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;
}
    }
person Vansuita Jr.    schedule 17.10.2014
comment
нет, если вы уменьшите изображение, чтобы увеличить его позже, вы получите блочное изображение вместо размытого изображения :( - person zeus; 05.02.2018

Для будущих гуглеров, которые выбирают подход 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-кратное увеличение скорости.

person Victor Laskin    schedule 27.09.2013

Николя ПОМЕПУЙ совет. Думаю, эта ссылка будет полезна: Эффект размытия для дизайна 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;
}
person Yura Shinkarev    schedule 16.01.2014
comment
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - person Amal Murali; 09.05.2014
comment
Амаль Мурали, вы правы. А теперь измени мой пост. Хорошо, что вы не только голосуете против, но и комментируете. - person Yura Shinkarev; 10.05.2014

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

  • Samsung S3 вылетал случайным образом всякий раз, когда мы пытались использовать скрипт рендеринга
  • Другие устройства (через разные 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.

person WarrenFaith    schedule 03.06.2014
comment
У меня также есть проблема с RenderScript на некоторых старых устройствах armeabi, но ваше решение слишком потребляет память. - person AsafK; 13.11.2015

Тем, у кого все еще есть проблемы с библиотекой поддержки 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

person mradzinski    schedule 30.09.2014

из блога Mario Viviani можно использовать это решение с 17 версии Android:

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

or

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

person Sagar Shah    schedule 24.12.2014

Вот наложение размытия в реальном времени с использованием RenderScript, которое кажется достаточно быстрым.

https://github.com/mmin18/RealtimeBlurView

person mmin    schedule 25.09.2016

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

person wings    schedule 07.02.2017

На 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()
    }
}
person Leo Droidcoder    schedule 09.05.2019