Швидке розмиття растрових зображень для SDK для Android


185

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

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

Хтось знає більш швидкий спосіб, крім прокручування пікселів?


2
На жаль, зображення завжди будуть різними, тому я не зможу створити розмиту версію достроково. Плюс, я не знаю інтенсивності розмиття достроково.
Грег

Чи можете ви опублікувати свій код, можливо, алгоритм / код є неефективним, 30 секунд пройти через зображення 640x480 повільно, я б подумав, що 5 секунд повільно, але потім знову залежить від процесора.
vickirk

Відповіді:


78

Це знімок у темряві, але ви можете спробувати зменшити зображення, а потім знову збільшити його. Це можна зробити за допомогою Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Переконайтесь, що встановіть для параметра фільтра значення true. Він запуститься в кодовому коді, тому може бути швидшим.


5
Після деякого тестування та розмиття, що я роблю це, фактично працює для мене досить добре, і це швидко. Дякую!
Грег

4
Якщо це працює добре. Прикро, що ми ніколи не дійшли до низу, чому це було неефективно.
vickirk

Ви можете спробувати createScaledBitmap і залишити зображення однакового розміру. Це розмиває це для мене :-(
Casebash

1
Ось обговорення про значення аргументу «фільтра»: stackoverflow.com/questions/2895065
user1364368

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

303

Для майбутніх Googlers, ось алгоритм, який я переніс із 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; індекс = 114021
див2851

2
Потрапив у відомий ArrayIndexOutOfBoundsException, і після деякого аналізу, я вважаю, що це викликано неправильною оптимізацією Dalvik VM. У forциклі безпосередньо перед поганим разименованія, або обчислення rbsзмінного або розрахунок gsum, rsumабо bsumзмінних не робиться правильно. Я виявив , що шляхом заміни Math.absз StrictMath.absабо який - або іншої absреалізацією, аварії не відбуваються. Оскільки StrictMath.absсам делегує Math.abs, схоже, це має бути погана оптимізація.
anthonycr

255

Android Blur Guide 2016

з вітриною / додатком 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);

Це завантажить растрову карту з inSampleSize8, тобто лише 1/64 вихідного зображення. Перевірте, що inSampleSizeвідповідає вашим потребам, але зберігайте його 2 ^ n (2,4,8, ...), щоб уникнути погіршення якості через масштабування. Докладнішу інформацію див. У документі Google

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

Використовуйте Renderscript

Renderscript передбачає, ScriptIntrinsicBlurщо є фільтром розмитості Гаусса. Він має гарну візуальну якість і є лише найшвидшим, який ви реально отримуєте на Android. Google стверджує, що "зазвичай на 2-3 рази швидше, ніж багатопотокова реалізація C, і часто на 10 разів + швидша, ніж реалізація Java" . Рендерскрипт справді складний (використовуючи найшвидший пристрій обробки (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 plugin v14 + )

android {
    ...
    defaultConfig {
        ...
        renderscriptTargetApi 19
        renderscriptSupportModeEnabled true
    }
}

Простий орієнтир на Nexus 5 - порівняння RenderScript з різними іншими реалізаціями Java та Renderscript:

Середній час виконання на розмиття для різних розмірів зображення Середній час виконання на розмиття для різних розмірів зображення

Мегапікселі в секунду, які можуть бути розмитими Мегапікселі в секунду, які можуть бути розмитими

Кожне значення становить середнє значення в 250 раундів. RS_GAUSS_FASTце ScriptIntrinsicBlur(і майже завжди найшвидший), інші, що починаються з RS_, здебільшого включають реалізацію з простими ядрами. Деталі алгоритмів можна знайти тут . Це не є просто розмитим, оскільки хороша частина - це сміття, яке вимірюється. Це можна побачити тут ( ScriptIntrinsicBlurна зображенні розміром 100x100 з приблизно 500 раундами)

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

Шипи - gc.

Ви можете самі переконатися, що додаток-орієнтир є у майстерні: BlurBenchmark

Повторно використовує растрові карти (якщо це можливо: продуктивність> слід пам'яті)

Якщо вам потрібно кілька розмиттів для розмиття в реальному часі або подібне, і ваша пам'ять дозволяє йому не завантажувати растрову карту з чернетки кілька разів, а тримати її "кешованою" у змінній члена. У цьому випадку завжди намагайтеся використовувати одні й ті ж змінні, щоб звести сміття до мінімуму.

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

Для змішування від різкого до розмитого

Простий і наївний метод - просто використовувати 2 ImageViews, один розмитий, і альфа-їх зникає. Але якщо ви хочете більш досконалого вигляду, який плавно затухає від різкого до розмитого , то ознайомтеся з публікацією Романа Нурика про те, як це зробити, як у його додатку Muzei .

В основному він пояснює, що попередньо розмиває деякі кадри з різними розширеннями розмиття і використовує їх як ключові кадри в анімації, яка виглядає дійсно гладкою.

Діаграма, де Нурік пояснює свій підхід


1
Перш за все, дякую за вашу наполегливу працю! Але у мене виникло питання: "тому що він використовує режим USAGE_SHARED, який повторно використовує пам'ять". Де ви знайшли постійну USAGE_SHARED? Я не міг його знайти ніде.
Якийсь студент Noob

2
Я знайшов це, USAGE_SHARED доступний лише у support.v8.renderscript
Some Noob Student

2
Швидке розмиття рендеріазу не вдається з помилками розподілу пам’яті 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або не будуйте! Я шукав навіки!
seb


53

EDIT (квітень 2014 р.): Ця сторінка з питаннями та відповідями все ще отримує багато звернень. Я знаю, що я завжди отримую гроші за цю посаду. Але якщо ви читаєте це, вам потрібно усвідомити відповіді, розміщені тут (і моя, і прийнята відповідь) застаріли. Якщо ви хочете реалізувати ефективне розмиття сьогодні , вам слід використовувати RenderScript замість NDK або Java. RenderScript працює на Android 2.2+ (використовує бібліотеку підтримки Android) ), тому немає жодної причини не використовувати його.

Стара відповідь випливає, але будьте обережні, оскільки вона застаріла.


Для майбутніх² Googlers - ось алгоритм, який я переніс із порту Яхеля за алгоритмом Квазімондо, але використовуючи NDK. Це, звичайно, засновано на відповіді Яхеля. Але це працює нативний C код, тому він швидше. Набагато швидше. Мовляв, у 40 разів швидше.

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

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

ви можете показати мені, як виглядає ваш файл Android.mkpastebin.com
CQM

1
Для робочого прикладу RenderScript Gaussian Blur на Android SDK 17+ дивіться тут: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini

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

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

}

Найпростіший спосіб розмиття зображення (y)
VAdaihiep

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
Donal Rafferty

10

Для мене це спрацювало чудово: як ефективно розмити зображення за допомогою RenderScript Android

public class BlurBuilder {
    private static final float BITMAP_SCALE = 0.4f;
    private static final float BLUR_RADIUS = 7.5f;

    @SuppressLint("NewApi")
    public static Bitmap blur(Context context, Bitmap image) {
        int width = Math.round(image.getWidth() * BITMAP_SCALE);
        int height = Math.round(image.getHeight() * BITMAP_SCALE);

        Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height,
            false);
        Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);

        RenderScript rs = RenderScript.create(context);
        ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs,
            Element.U8_4(rs));
        Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
        Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
        theIntrinsic.setRadius(BLUR_RADIUS);
        theIntrinsic.setInput(tmpIn);
        theIntrinsic.forEach(tmpOut);
        tmpOut.copyTo(outputBitmap);

        return outputBitmap;
    }
}

Це було б ще краще / швидше, якби ви кешували створений вами об'єкт 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

Для майбутніх службовців Google, які обирають підхід 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, як згадувалося вище, в різних відповідях. Ми обмежилися використанням версії v8 RenderScript, що доставило нам багато проблем.

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

Це рішення далеко не ідеальне, але створює розумний ефект розмивання, виходячи з того, що воно малює високопрозору версію того ж зображення поверх ледь прозорої "гострої" версії. Альфа залежить від відстані до початку.

Ви можете налаштувати деякі "магічні числа" під свої потреби. Я просто хотів поділитися цим "рішенням" для всіх, хто має проблеми з підтримкою версії V8 RenderScript.


У мене також виникають проблеми з RenderScript на деяких старих пристроях Armeabi, але ваше рішення занадто багато часу.
AsafK

0

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

https://code.google.com/p/android/isissue/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.