Sfocatura bitmap veloce per Android SDK


185

Attualmente in un'applicazione Android che sto sviluppando sto scorrendo i pixel di un'immagine per sfocarla. Questa operazione richiede circa 30 secondi su un'immagine 640x480.

Durante la navigazione delle app in Android Market ne ho trovata una che include una funzione di sfocatura e la loro sfocatura è molto veloce (come 5 secondi), quindi devono utilizzare un metodo diverso di sfocatura.

Qualcuno conosce un modo più veloce diverso dal passare in rassegna i pixel?


2
Sfortunatamente le immagini saranno sempre diverse, quindi non sarò in grado di creare una versione sfocata in anticipo. Inoltre, non saprò nemmeno in anticipo l'intensità della sfocatura.
Greg,

Potresti pubblicare il tuo codice, forse è l'algoritmo / codice che è inefficiente, 30 secondi per passare attraverso un'immagine 640x480 è lento, avrei pensato che 5 secondi fosse lento ma poi dipende ancora dal processore.
Vickirk,

Risposte:


78

Questo è uno scatto al buio, ma potresti provare a rimpicciolire l'immagine e poi a ingrandirla di nuovo. Questo può essere fatto con Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Assicurarsi e impostare il parametro del filtro su true. Verrà eseguito in codice nativo quindi potrebbe essere più veloce.


5
Dopo alcuni test e la sfocatura che sto facendo questo funziona davvero abbastanza bene per me ed è veloce. Grazie!
Greg,

4
Se funziona bene. È un peccato che non siamo mai riusciti a capire perché fosse inefficiente.
Vickirk,

Potresti provare a creareScaledBitmap e a lasciare l'immagine della stessa dimensione. Per me è sfocato :-(
Casebash

1
Ecco una discussione sul significato dell'argomento "filtro": stackoverflow.com/questions/2895065
user1364368

4
Questo non è esattamente il modo per ottenere a causa di 2 motivi: 1) ha bisogno della memoria dell'immagine completa anche se probabilmente ne stai usando solo una ridimensionata 2) devi caricare l'immagine intera che è più lenta - usa il caricamento con inSampleSize e BitmapFactory.decodeResource (), che è una soluzione di gran lunga superiore a questo.
Patrick Favre,

303

Per i futuri googler, ecco un algoritmo che ho portato da Quasimondo. È una specie di mix tra una sfocatura a scatola e una sfocatura gaussiana, è molto carina e anche abbastanza veloce.

Aggiornamento per le persone che riscontrano il problema ArrayIndexOutOfBoundsException : @anthonycr nei commenti fornisce queste informazioni:

Ho scoperto che sostituendo Math.abs con StrictMath.abs o altre implementazioni di abs, il crash non si verifica.

/**
 * 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
Grazie Yahel. Hai risolto il mio problema. Grazie ancora.
Yog Guru,

1
cosa devo passare come raggio?
krisDrOid,

16
Per un raggio maggiore di 1 a volte si ottiene ArrayIndexOutOfBoundsException. Proverò a identificare il problema.
MikeL

7
@MichaelLiberman Ho anche riscontrato lo stesso problema. Capire perché ancora? "g [yi] = dv [gsum];" -> errore: java.lang.ArrayIndexOutOfBoundsException: lunghezza = 112896; indice = 114021
vedi 2851

2
Sono arrivato al noto ArrayIndexOutOfBoundsException e, dopo alcune analisi, credo che sia causato da un'errata ottimizzazione da parte della VM Dalvik. Nel forciclo immediatamente prima del male dereferenziare, sia il calcolo della rbsvariabile o il calcolo dei gsum, rsumo bsumvariabili non vengono right Fatto. Ho scoperto che sostituendo Math.abscon StrictMath.abso qualche altra absimplementazione, non si verifica l'arresto anomalo. Poiché StrictMath.abssi è delegato Math.abs, sembra che debba essere una cattiva ottimizzazione.
anthonycr

255

Blur Guide Android 2016

con app Showcase / Benchmark e Source su Github . Dai un'occhiata anche al framework Blur su cui sto attualmente lavorando: Dali .

Dopo aver sperimentato molto, ora posso tranquillamente darti alcuni solidi consigli che ti semplificheranno la vita in Android quando utilizzi Android Framework.

Carica e usa una bitmap ridotta (per immagini molto sfocate)

Non utilizzare mai le dimensioni complete di una bitmap. Più grande è l'immagine, più deve essere sfocata e più alto deve essere il raggio di sfocatura e di solito, maggiore è il raggio di sfocatura più tempo impiega l'algoritmo.

final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap blurTemplate = BitmapFactory.decodeResource(getResources(), R.drawable.myImage, options);

Questo caricherà la bitmap con inSampleSize8, quindi solo 1/64 dell'immagine originale. Prova ciò che inSampleSizesoddisfa le tue esigenze, ma mantienilo 2 ^ n (2,4,8, ...) per evitare un degrado della qualità dovuto al ridimensionamento. Vedi Google doc per ulteriori informazioni

Un altro grande vantaggio è che il caricamento della bitmap sarà molto veloce. Nei miei primi test di sfocatura ho pensato che il tempo più lungo durante l'intero processo di sfocatura fosse il caricamento dell'immagine. Quindi per caricare un'immagine 1920x1080 dal disco il mio Nexus 5 aveva bisogno di 500 ms mentre la sfocatura richiedeva solo altri 250 ms circa.

Usa Renderscript

Renderscript fornisce ScriptIntrinsicBlurquale è un filtro di sfocatura gaussiana. Ha una buona qualità visiva ed è solo il più veloce che realisticamente ottieni su Android. Google afferma di essere "in genere 2-3 volte più veloce di un'implementazione C multithread e spesso 10 volte più veloce di un'implementazione Java" . Renderscript è davvero sofisticato (usando il dispositivo di elaborazione più veloce (GPU, ISP, ecc.), Ecc.) E c'è anche la libreria di supporto v8 per renderlo compatibile fino a 2.2 . Beh, almeno in teoria, attraverso i miei test e le relazioni di altri sviluppatori sembra che non sia possibile usare Renderscript alla cieca, poiché la frammentazione hardware / driver sembra causare problemi con alcuni dispositivi, anche con livelli di SDK superiori (ad es. problemi con il 4.1 Nexus S) quindi fai attenzione e prova su molti dispositivi. Ecco un semplice esempio che ti farà iniziare:

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

Quando si utilizza il supporto v8 con Gradle, che è specificamente raccomandato da Google "perché includono gli ultimi miglioramenti" , è sufficiente aggiungere 2 righe allo script di compilazione e utilizzare android.support.v8.renderscriptcon gli strumenti di compilazione correnti ( sintassi aggiornata per il plug-in Android Gradle v14 + )

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

Semplice benchmark su Nexus 5: confronto di RenderScript con diverse altre implementazioni java e Renderscript:

Durata media per sfocatura con dimensioni delle immagini diverse Durata media per sfocatura con dimensioni delle immagini diverse

Megapixel al secondo che possono essere sfocati Megapixel al secondo che possono essere sfocati

Ogni valore è la media di 250 round. RS_GAUSS_FASTè ScriptIntrinsicBlur(e quasi sempre il più veloce), gli altri che iniziano con RS_sono per lo più implementazioni convolute con kernel semplici. I dettagli degli algoritmi sono disponibili qui . Questo non è puramente sfocato, poiché una buona parte è la raccolta dei rifiuti che viene misurata. Questo può essere visto in questo qui ( ScriptIntrinsicBlursu un'immagine 100x100 con circa 500 colpi)

inserisci qui la descrizione dell'immagine

I picchi sono gc.

Puoi verificare tu stesso, l'app di riferimento è nel playstore: BlurBenchmark

Riutilizza Bitmap ove possibile (se prio: performance> footprint di memoria)

Se hai bisogno di più sfocature per una sfocatura dal vivo o simile e la tua memoria consente di non caricare più la bitmap dai disegni estraibili, ma di tenerla "memorizzata nella cache" in una variabile membro. In questo caso, cerca sempre di utilizzare le stesse variabili, per ridurre al minimo la raccolta dei rifiuti.

Controlla anche la nuova inBitmapopzione durante il caricamento da un file o da un disegno che riutilizza la memoria bitmap e risparmia tempo di raccolta dei rifiuti.

Per sfumare da nitido a sfocato

Il metodo semplice e ingenuo è solo quello di utilizzare 2 ImageViews, uno sfocato e alfa dissolvenza. Ma se vuoi un look più sofisticato che passa gradualmente da nitido a sfocato, dai un'occhiata al post di Roman Nurik su come farlo come nella sua app Muzei .

Fondamentalmente, spiega che pre-sfoca alcuni fotogrammi con diverse estensioni di sfocatura e li usa come fotogrammi chiave in un'animazione che sembra davvero fluida.

Diagramma in cui Nurik esalta il suo approccio


1
Prima di tutto, grazie per il tuo duro lavoro! Ma ho una domanda: "perché usa la modalità USAGE_SHARED che riutilizza la memoria". Dove hai trovato la costante USAGE_SHARED? Non sono riuscito a trovarlo da nessuna parte.
Alcuni studenti Noob il

2
L'ho trovato, USAGE_SHARED è disponibile solo in support.v8.renderscript
Some Noob Student

2
La sfocatura gaussiana veloce con renderscript fallisce con errori di allocazione della memoria C su dispositivi di fascia bassa. Testato su ZTE Z992 (Android 4.1.1) e Kyocera Rise (Android 4.0.4) utilizzando l'app Play Store fornita. Aveva anche un rapporto di errore su Samsung Galaxy S3 mini. Poiché gli errori si verificano nel livello C, non possono essere intrappolati come eccezioni in Java, il che significa che è inevitabile un arresto anomalo dell'app. Sembra che RenderScript potrebbe non essere pronto per l'uso in produzione.
Theo,

4
per le versioni più recenti di grado, utilizzare renderscriptSupportModeEnabled trueo non costruirà! Ho cercato per sempre!
SEB


53

EDIT (aprile 2014): questa è una pagina di domande / risposte che sembra avere ancora molti successi. So che ricevo sempre voti per questo post. Ma se stai leggendo questo, devi capire che le risposte pubblicate qui (sia la mia che la risposta accettata) non sono aggiornate. Se vuoi implementare una sfocatura efficiente oggi , dovresti usare RenderScript invece di NDK o Java. RenderScript funziona su Android 2.2+ (utilizzando la libreria di supporto Android ), quindi non c'è motivo di non usarlo.

Segue la vecchia risposta, ma attenzione poiché è obsoleta.


Per i futuri googler², ecco un algoritmo che ho portato dalla porta di Yahel all'algoritmo di Quasimondo, ma usando l'NDK. Si basa sulla risposta di Yahel, ovviamente. Ma questo esegue codice C nativo, quindi è più veloce. Più veloce. Come, 40 volte più veloce.

Trovo che l'utilizzo dell'NDK sia il modo in cui tutta la manipolazione delle immagini dovrebbe essere eseguita su Android ... è piuttosto fastidioso da implementare all'inizio (leggi un ottimo tutorial sull'uso di JNI e NDK qui ), ma molto meglio e quasi in tempo reale per un un sacco di cose.

Per riferimento, usando la funzione Java di Yahel, ci sono voluti 10 secondi per sfocare la mia immagine di 480x532 pixel con un raggio di sfocatura di 10. Ma ci sono voluti 250ms usando la versione C nativa. E sono abbastanza sicuro che possa ancora essere ulteriormente ottimizzato ... Ho appena fatto una stupida conversione del codice java, probabilmente ci sono alcune manipolazioni che possono essere abbreviate, non volevo passare troppo tempo a riformulare il tutto.

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

Quindi usalo in questo modo (considerando una classe chiamata com.insert.your.package.ClassName e una funzione nativa chiamata functionToBlur, come afferma il codice sopra):

// Create a copy
Bitmap bitmapOut = bitmapIn.copy(Bitmap.Config.ARGB_8888, true);
// Blur the copy
functionToBlur(bitmapIn, bitmapOut, __radius);

Si aspetta una bitmap RGB_8888!

Per utilizzare una bitmap RGB_565, creare una copia convertita prima di passare il parametro (yuck) o modificare la funzione per utilizzare un nuovo rgb565tipo anziché rgba:

typedef struct {
    uint16_t byte0;
} rgb565;

Il problema è che se lo fai non puoi più leggere .red, .greene .bluedel pixel, devi leggere correttamente il byte, duh. Quando ne avevo bisogno prima, ho fatto questo:

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

Ma c'è probabilmente un modo meno stupido di farlo. Non sono un programmatore C di basso livello, temo.


Grazie, mi ha aiutato molto :)
Dmitry Zaytsev il

18
MA richiede molta memoria. Per ridurre il consumo di memoria a cambiamento di tipo r[wh], g[wh]e b[wh]di uint8_t.
Dmitry Zaytsev

puoi mostrarmi come appare il tuo file Android.mk supastebin.com
CQM

1
Per un esempio di RenderScript funzionante di una sfocatura gaussiana su Android SDK 17+, guarda qui: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini,

2
RenderScript è anche disponibile come parte della libreria di supporto per Android 2.2+, quindi non c'è motivo di non usarlo più ovunque: android-developers.blogspot.com/2013/09/…
zeh

14

Questo codice è perfetto per me

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

}

Modo più semplice per sfocare l'immagine (y)
VAdaihiep

12

Ora puoi usare ScriptIntrinsicBlur dalla libreria RenderScript per sfocare rapidamente. Ecco come accedere all'API RenderScript. Quella che segue è una classe che ho creato per sfocare viste e bitmap:

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
Il contesto Renderscript non deve essere creato nel metodo di sfocatura, ma gestito staticamente o assegnato al metodo. (se ti dispiace esibizione)
Patrick Favre,

1
Puoi fare un esempio di questo è l'uso? Quando provo ad usarlo ottengo il seguente errore: java.lang.IllegalArgumentException: larghezza e altezza devono essere> 0
Donal Rafferty,

10

Questo ha funzionato bene per me: come sfocare le immagini in modo efficiente con 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;
    }
}

Ciò sarebbe ancora migliore / più veloce se si memorizza nella cache l'oggetto RenderScript che è stato creato. L'istanza di una nuova ogni volta che si desidera sfocare un'immagine aggiunge semplicemente un sovraccarico non necessario (creazione / distruzione di oggetti Java).
Stephen Hines,


4

Questo è per tutte le persone che hanno bisogno di aumentare il raggio di ScriptIntrinsicBlur ottenere una sfocatura gaussiana più dura.

Invece di mettere il raggio più di 25, puoi ridimensionare l'immagine e ottenere lo stesso risultato. Ho scritto una classe chiamataGaussianBlur . Di seguito puoi vedere come utilizzare e l'implementazione dell'intera classe.

Uso:

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

Classe:

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

no se riduci l'immagine per ridimensionarla in seguito, recupererai un'immagine a blocchi invece di un'immagine sfocata :(
loki,

4

Grazie @Yahel per il codice. Pubblicare lo stesso metodo con il supporto della sfocatura del canale alfa perché mi ci è voluto un po 'di tempo per farlo funzionare correttamente in modo da risparmiare tempo a qualcuno:

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

ancora con "indice fuori portata" nei dispositivi> hdpi come sorgente originale
Gabriel Ferreira,

4

L'ho usato prima ..

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

Per i futuri googler che scelgono l'approccio NDK, trovo l'algoritmo stackblur citato in modo affidabile. Ho trovato l'implementazione C ++ che non si basa su SSE qui - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32 che contiene alcune ottimizzazioni usando tabelle statiche come:

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

Ho apportato modifiche all'algoritmo stackblur per sistemi multi-core - può essere trovato qui http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ Dato che sempre più dispositivi hanno 4 core - le ottimizzazioni danno Vantaggio di 4x velocità.


1

Consiglio Nicolas POMEPUY. Penso che questo link sarà utile: effetto sfocatura per il design Android

Progetto di esempio su 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;
}

Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
Amal Murali

1
Amal Murali, hai ragione. Ora cambia il mio post. Bene, non solo il downvote, ma anche un commento.
Yura Shinkarev,

1

Abbiamo cercato di implementare la sfocatura di RenderScript come menzionato sopra in diverse risposte. Eravamo limitati a utilizzare la versione RenderScript v8 e questo ci causava molti problemi.

  • Samsung S3 si è schiantato in modo casuale ogni volta che abbiamo provato a utilizzare il rendering
  • Altri dispositivi (attraverso diverse API) hanno mostrato casualmente diversi problemi di colore

Voglio condividere la nostra versione solo Java sporca che è lenta e dovrebbe essere fatta su un thread separato e, se possibile, prima dell'uso e quindi persistente.

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

Questa soluzione è tutt'altro che perfetta, ma crea un ragionevole effetto di sfocatura basato sul fatto, che disegna una versione altamente trasparente della stessa immagine sopra una versione "nitida" appena trasparente. L'alfa dipende dalla distanza dall'origine.

Puoi adattare alcuni "numeri magici" alle tue esigenze. Volevo solo condividere quella "soluzione" per tutti coloro che hanno problemi con la versione di supporto v8 di RenderScript.


Ho anche un problema con RenderScript su alcuni vecchi dispositivi armeabi, ma la tua soluzione richiede troppo memoria.
AsafK,

0

Per coloro che hanno ancora problemi con la libreria di supporto di Renderscript su chipset x86, dai un'occhiata a questo post del creatore della libreria. Sembra che la correzione che ha preparato non sia arrivata in qualche modo a Build Tools v20.0.0, quindi fornisce i file per correggerlo manualmente e una breve spiegazione di come farlo.

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




0

Ho scoperto che la riduzione del contrasto, della luminosità e della saturazione rende un po 'più belle le immagini sfocate, quindi ho combinato vari metodi dallo sfioro dello stack e ho creato questa classe di sfocatura che si occupa di immagini sfocate, modifica di luminosità, saturazione, contrasto e dimensioni delle immagini sfocate. Può anche convertire immagini da disegnabili a bitmap e viceversa.


0

Sull'I / O 2019 è stata presentata la seguente soluzione:

/**
 * 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()
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.