Flou Bitmap rapide pour Android SDK

185

Actuellement, dans une application Android que je développe, je parcourt les pixels d'une image pour la brouiller. Cela prend environ 30 secondes sur une image 640x480.

En parcourant des applications sur l'Android Market, j'en suis tombé sur une qui inclut une fonction de flou et leur flou est très rapide (comme 5 secondes), ils doivent donc utiliser une méthode différente de flou.

Quelqu'un connaît-il un moyen plus rapide autre que de parcourir les pixels?

Greg
la source
2
Malheureusement, les images seront toujours différentes, je ne pourrai donc pas créer une version floue à l'avance. De plus, je ne connais pas non plus l'intensité du flou à l'avance.
Greg
Pourriez-vous poster votre code, c'est peut-être l'algorithme / code qui est inefficace, 30 secondes pour parcourir une image 640x480 est lente, j'aurais pensé que 5 secondes était lent, mais cela dépend encore une fois du processeur.
vickirk

Réponses:

78

Il s'agit d'une photo dans le noir, mais vous pouvez essayer de réduire l'image, puis de l'agrandir à nouveau. Cela peut être fait avec Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Assurez-vous et définissez le paramètre de filtre sur true. Il fonctionnera en code natif, donc ce sera peut-être plus rapide.

Luke
la source
5
Après quelques tests et le flou que je fais, cela fonctionne assez bien pour moi et c'est rapide. Merci!
Greg
4
Si ça marche bien. C'est dommage que nous n'ayons jamais compris pourquoi c'était inefficace.
vickirk
Vous voudrez peut-être essayer createScaledBitmap et laisser l'image de la même taille. Cela me brouille :-(
Casebash
1
Voici une discussion sur la signification de l'argument "filtre": stackoverflow.com/questions/2895065
user1364368
4
Ce n'est pas exactement la façon de procéder pour 2 raisons: 1) il a besoin de la mémoire de l'image complète bien que vous n'utilisiez probablement qu'une image réduite 2) vous devez charger l'image complète qui est plus lente - utilisez le chargement avec inSampleSize et BitmapFactory.decodeResource (), qui est une solution bien supérieure à cela.
Patrick Favre
303

Pour les futurs Googleurs, voici un algorithme que j'ai porté depuis Quasimondo. C'est une sorte de mélange entre un flou de boîte et un flou gaussien, c'est très joli et assez rapide aussi.

Mise à jour pour les personnes rencontrant le problème ArrayIndexOutOfBoundsException: @anthonycr dans les commentaires fournit ces informations:

J'ai trouvé qu'en remplaçant Math.abs par StrictMath.abs ou une autre implémentation abs, le crash ne se produit pas.

/**
 * Stack Blur v1.0 from
 * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
 * Java Author: Mario Klingemann <mario at quasimondo.com>
 * http://incubator.quasimondo.com
 *
 * created Feburary 29, 2004
 * Android port : Yahel Bouaziz <yahel at kayenko.com>
 * http://www.kayenko.com
 * ported april 5th, 2012
 *
 * This is a compromise between Gaussian Blur and Box blur
 * It creates much better looking blurs than Box Blur, but is
 * 7x faster than my Gaussian Blur implementation.
 *
 * I called it Stack Blur because this describes best how this
 * filter works internally: it creates a kind of moving stack
 * of colors whilst scanning through the image. Thereby it
 * just has to add one new block of color to the right side
 * of the stack and remove the leftmost color. The remaining
 * colors on the topmost layer of the stack are either added on
 * or reduced by one, depending on if they are on the right or
 * on the left side of the stack.
 *  
 * If you are using this algorithm in your code please add
 * the following line:
 * Stack Blur Algorithm by Mario Klingemann <[email protected]>
 */

public Bitmap fastblur(Bitmap sentBitmap, float scale, int radius) {

    int width = Math.round(sentBitmap.getWidth() * scale);
    int height = Math.round(sentBitmap.getHeight() * scale);
    sentBitmap = Bitmap.createScaledBitmap(sentBitmap, width, height, false);

    Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);

    if (radius < 1) {
        return (null);
    }

    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    int[] pix = new int[w * h];
    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.getPixels(pix, 0, w, 0, 0, w, h);

    int wm = w - 1;
    int hm = h - 1;
    int wh = w * h;
    int div = radius + radius + 1;

    int r[] = new int[wh];
    int g[] = new int[wh];
    int b[] = new int[wh];
    int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
    int vmin[] = new int[Math.max(w, h)];

    int divsum = (div + 1) >> 1;
    divsum *= divsum;
    int dv[] = new int[256 * divsum];
    for (i = 0; i < 256 * divsum; i++) {
        dv[i] = (i / divsum);
    }

    yw = yi = 0;

    int[][] stack = new int[div][3];
    int stackpointer;
    int stackstart;
    int[] sir;
    int rbs;
    int r1 = radius + 1;
    int routsum, goutsum, boutsum;
    int rinsum, ginsum, binsum;

    for (y = 0; y < h; y++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        for (i = -radius; i <= radius; i++) {
            p = pix[yi + Math.min(wm, Math.max(i, 0))];
            sir = stack[i + radius];
            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            rbs = r1 - Math.abs(i);
            rsum += sir[0] * rbs;
            gsum += sir[1] * rbs;
            bsum += sir[2] * rbs;
            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }
        }
        stackpointer = radius;

        for (x = 0; x < w; x++) {

            r[yi] = dv[rsum];
            g[yi] = dv[gsum];
            b[yi] = dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (y == 0) {
                vmin[x] = Math.min(x + radius + 1, wm);
            }
            p = pix[yw + vmin[x]];

            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[(stackpointer) % div];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];

            yi++;
        }
        yw += w;
    }
    for (x = 0; x < w; x++) {
        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
        yp = -radius * w;
        for (i = -radius; i <= radius; i++) {
            yi = Math.max(0, yp) + x;

            sir = stack[i + radius];

            sir[0] = r[yi];
            sir[1] = g[yi];
            sir[2] = b[yi];

            rbs = r1 - Math.abs(i);

            rsum += r[yi] * rbs;
            gsum += g[yi] * rbs;
            bsum += b[yi] * rbs;

            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
            }

            if (i < hm) {
                yp += w;
            }
        }
        yi = x;
        stackpointer = radius;
        for (y = 0; y < h; y++) {
            // Preserve alpha channel: ( 0xff000000 & pix[yi] )
            pix[yi] = ( 0xff000000 & pix[yi] ) | ( dv[rsum] << 16 ) | ( dv[gsum] << 8 ) | dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];

            if (x == 0) {
                vmin[y] = Math.min(y + r1, hm) * w;
            }
            p = x + vmin[y];

            sir[0] = r[p];
            sir[1] = g[p];
            sir[2] = b[p];

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[stackpointer];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];

            yi += w;
        }
    }

    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.setPixels(pix, 0, w, 0, 0, w, h);

    return (bitmap);
}
Yahel
la source
4
Merci Yahel. Vous avez résolu mon problème. Merci encore.
Yog Guru
1
que dois-je passer comme rayon?
krisDrOid
16
Pour un rayon supérieur à 1, vous obtenez parfois ArrayIndexOutOfBoundsException. J'essaierai d'identifier le problème.
MikeL
7
@MichaelLiberman J'ai également rencontré le même problème. Vous avez trouvé pourquoi? "g [yi] = dv [gsum];" -> erreur: java.lang.ArrayIndexOutOfBoundsException: length = 112896; index = 114021
voir2851
2
Ran dans la ArrayIndexOutOfBoundsException connue, et après quelques analyses, je pense que cela est causé par une optimisation incorrecte par la VM Dalvik. Dans la forboucle immédiatement avant la mauvaise déréférencement, soit le calcul de la rbsvariable ou le calcul des gsum, rsumou les bsumvariables ne sont pas faites à droite. J'ai trouvé qu'en remplaçant Math.abspar StrictMath.absou une autre absimplémentation, le crash ne se produit pas. Puisqu'il StrictMath.absdélègue à Math.abs, il semble que ce soit une mauvaise optimisation.
anthonycr
255

Guide Android Blur 2016

avec Showcase / Benchmark App et Source sur Github . Consultez également le framework Blur sur lequel je travaille actuellement: Dali .

Après avoir beaucoup expérimenté, je peux maintenant vous donner en toute sécurité des recommandations solides qui vous faciliteront la vie sous Android lorsque vous utilisez le Framework Android.

Charger et utiliser une image bitmap réduite (pour les images très floues)

N'utilisez jamais la taille d'un Bitmap. Plus l'image est grande, plus elle doit être floue et plus le rayon de flou doit être élevé et, généralement, plus le rayon de flou est élevé, plus l'algorithme prend du temps.

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

Cela chargera le bitmap avec inSampleSize8, donc seulement 1/64 de l'image d'origine. Testez ce qui inSampleSizecorrespond à vos besoins, mais gardez-le 2 ^ n (2,4,8, ...) pour éviter de dégrader la qualité en raison de la mise à l'échelle. Voir la documentation Google pour en savoir plus

Un autre très gros avantage est que le chargement des bitmaps sera très rapide. Lors de mes premiers tests de flou, j'ai pensé que le temps le plus long de tout le processus de flou était le chargement de l'image. Donc, pour charger une image 1920 x 1080 à partir du disque, mon Nexus 5 avait besoin de 500 ms, tandis que le flou ne prenait que 250 ms environ.

Utiliser Renderscript

Renderscript fournit ScriptIntrinsicBlurun filtre de flou gaussien. Il a une bonne qualité visuelle et est juste le plus rapide que vous obtenez de manière réaliste sur Android. Google prétend être "généralement 2-3 fois plus rapide qu'une implémentation C multithread et souvent 10x + plus rapide qu'une implémentation Java" . Renderscript est vraiment sophistiqué (utilisant le périphérique de traitement le plus rapide (GPU, FAI, etc.), etc.) et il existe également la bibliothèque de support v8 qui le rend compatible jusqu'à 2,2 . Eh bien au moins en théorie, grâce à mes propres tests et rapports d'autres développeurs, il semble qu'il n'est pas possible d'utiliser Renderscript à l'aveuglette, car la fragmentation du matériel / pilote semble causer des problèmes avec certains périphériques, même avec un sdk lvl plus élevé (par exemple, j'avais problèmes avec le Nexus S 4.1) alors soyez prudent et testez sur de nombreux appareils. Voici un exemple simple qui vous aidera à démarrer:

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

Lorsque vous utilisez le support v8 avec Gradle, qui est spécifiquement recommandé par Google "car ils incluent les dernières améliorations" , il vous suffit d'ajouter 2 lignes à votre script de build et de l'utiliser android.support.v8.renderscriptavec les outils de build actuels ( syntaxe mise à jour pour le plugin Android Gradle v14 + )

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

Benchmark simple sur un Nexus 5 - comparaison de RenderScript avec différentes autres implémentations java et Renderscript:

Le temps d'exécution moyen par flou sur différentes tailles de photo Le temps d'exécution moyen par flou sur différentes tailles de photo

Mégapixels par seconde qui peuvent être flous Mégapixels par seconde qui peuvent être flous

Chaque valeur est la moyenne de 250 tours. RS_GAUSS_FASTest ScriptIntrinsicBlur(et presque toujours le plus rapide), les autres qui commencent par RS_sont pour la plupart des implémentations convolves avec des noyaux simples. Les détails des algorithmes peuvent être trouvés ici . Ce n'est pas purement flou, car une bonne partie est le ramassage des ordures qui est mesuré. Cela peut être vu ici ( ScriptIntrinsicBlursur une image 100x100 avec environ 500 tours)

entrez la description de l'image ici

Les pointes sont gc.

Vous pouvez vérifier par vous-même, l'application de référence est dans le Playstore: BlurBenchmark

Réutilise Bitmap dans la mesure du possible (si prio: performances> empreinte mémoire)

Si vous avez besoin de plusieurs flous pour un flou en direct ou similaire et que votre mémoire le permet, ne chargez pas le bitmap à partir de drawables plusieurs fois, mais gardez-le "mis en cache" dans une variable membre. Dans ce cas, essayez toujours d'utiliser les mêmes variables, pour réduire au minimum la récupération de place.

Découvrez également la nouvelle inBitmapoption lors du chargement partir d'un fichier ou d'un dessin qui réutilisera la mémoire bitmap et économisera le temps de récupération de place.

Pour mélanger du net au flou

La méthode simple et naïve consiste simplement à utiliser 2 ImageViews, un flou et un fondu alpha. Mais si vous voulez un look plus sophistiqué qui passe en douceur de net à flou, consultez l'article de Roman Nurik sur la façon de le faire comme dans son application Muzei. .

Fondamentalement, il explique qu'il pré-floute certaines images avec différentes étendues de flou et les utilise comme images clés dans une animation qui semble vraiment fluide.

Schéma où Nurik explique son approche

Patrick Favre
la source
1
Tout d'abord, merci pour votre travail acharné! Mais j'ai une question: "car il utilise le mode USAGE_SHARED qui réutilise la mémoire". Où avez-vous trouvé la constante USAGE_SHARED? Je ne l'ai trouvé nulle part.
Some Noob Student
2
Je l'ai trouvé, USAGE_SHARED n'est disponible que dans support.v8.renderscript
Some Noob Student
2
Le flou gaussien rapide de Renderscript échoue avec des erreurs d'allocation de mémoire C sur les appareils bas de gamme. Testé sur ZTE Z992 (Android 4.1.1) et Kyocera Rise (Android 4.0.4) à l'aide de l'application Play Store fournie. A également eu un rapport d'échec sur le Samsung Galaxy S3 mini. Étant donné que les erreurs se produisent dans la couche C, elles ne peuvent pas être interceptées en tant qu'exceptions en Java, ce qui signifie qu'un blocage d'application est inévitable. Il semble que RenderScript ne soit pas prêt pour une utilisation en production.
Theo
4
pour les nouvelles versions de gradle, utilisez renderscriptSupportModeEnabled trueou il ne sera pas construit! J'ai cherché pour toujours!
seb
3
Lorsque j'ai essayé cette solution, plutôt que d'obtenir un bitmap flou, j'ai obtenu un bitmap de couleur arc-en-ciel . Quelqu'un d'autre rencontre ce problème? Si oui, comment l'avez-vous résolu?
HaloMediaz
53

EDIT (avril 2014): Il s'agit d'une page de questions / réponses qui reçoit encore beaucoup de hits, semble-t-il. Je sais que je reçois toujours des votes pour ce message. Mais si vous lisez ceci, vous devez vous rendre compte que les réponses publiées ici (la mienne et la réponse acceptée) sont obsolètes. Si vous souhaitez implémenter un flou efficace aujourd'hui , vous devez utiliser RenderScript au lieu du NDK ou de Java. RenderScript fonctionne sur Android 2.2+ (en utilisant la bibliothèque de support Android ), il n'y a donc aucune raison de ne pas l'utiliser.

La vieille réponse suit, mais méfiez-vous car elle est obsolète.


Pour les futurs Googleurs, voici un algorithme que j'ai porté depuis le port de Yahel de l'algorithme de Quasimondo, mais en utilisant le NDK. C'est basé sur la réponse de Yahel, bien sûr. Mais cela exécute du code C natif, donc c'est plus rapide. Plus vite. Genre, 40 fois plus rapide.

Je trouve que l'utilisation du NDK est la façon dont toute la manipulation d'image doit être effectuée sur Android ... c'est un peu ennuyeux à mettre en œuvre au début (lisez un excellent tutoriel sur l'utilisation de JNI et du NDK ici ), mais bien mieux, et presque en temps réel pour un beaucoup de choses.

Pour référence, en utilisant la fonction Java de Yahel, il a fallu 10 secondes pour brouiller mon image de 480x532 pixels avec un rayon de flou de 10. Mais cela a pris 250ms en utilisant la version native C. Et je suis à peu près sûr que cela peut encore être optimisé ... Je viens de faire une conversion stupide du code java, il y a probablement des manipulations qui peuvent être raccourcies, je ne voulais pas passer trop de temps à refactoriser le tout.

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

Ensuite, utilisez-le comme ceci (en considérant une classe appelée com.insert.your.package.ClassName et une fonction native appelée functionToBlur, comme l'indique le code ci-dessus):

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

Il attend un bitmap RGB_8888!

Pour utiliser un bitmap RGB_565, créez une copie convertie avant de passer le paramètre (beurk), ou modifiez la fonction pour utiliser un nouveau rgb565type au lieu de rgba:

typedef struct {
    uint16_t byte0;
} rgb565;

Le problème est que si vous faites cela, vous ne pouvez plus lire .red, .greenet .bluedu pixel plus, vous devez lire l'octet correctement, duh. Quand j'en avais besoin avant, j'ai fait ceci:

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

Mais il y a probablement une manière moins stupide de le faire. Je ne suis pas vraiment un codeur C de bas niveau, j'en ai peur.

zeh
la source
Merci, ça m'a beaucoup aidé :)
Dmitry Zaytsev
18
MAIS cela demande beaucoup de mémoire. Pour réduire la consommation de mémoire, modifiez le type de r[wh], g[wh]et b[wh]à uint8_t.
Dmitry Zaytsev
pouvez-vous me montrer à quoi ressemble votre fichier Android.mk, surpastebin.com
CQM
1
Pour un exemple fonctionnel de RenderScript d'un flou gaussien sur Android SDK 17+, regardez ici: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini
2
RenderScript est également disponible dans le cadre de la bibliothèque de support pour Android 2.2+, il n'y a donc aucune raison de ne plus l'utiliser partout: android-developers.blogspot.com/2013/09/…
zeh
14

Ce code fonctionne parfaitement pour moi

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

}
Niks
la source
Le moyen le plus simple de brouiller l'image (y)
VAdaihiep
12

Vous pouvez maintenant utiliser ScriptIntrinsicBlur de la bibliothèque RenderScript pour flouter rapidement. Voici comment accéder à l'API RenderScript. Voici une classe que j'ai créée pour brouiller les vues et les bitmaps:

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;
    }
}
b_yng
la source
2
Le contexte Renderscript ne doit pas être créé dans la méthode blur, mais géré de manière statique ou donné à la méthode. (si la performance vous dérange)
Patrick Favre
1
Pouvez-vous donner un exemple de cette utilisation? Lorsque j'essaie de l'utiliser, j'obtiens l'erreur suivante: java.lang.IllegalArgumentException: la largeur et la hauteur doivent être> 0
Donal Rafferty
10

Cela a bien fonctionné pour moi: Comment flouter les images efficacement avec RenderScript d'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;
    }
}
Artificioo
la source
Ce serait encore mieux / plus rapide si vous mettiez en cache l'objet RenderScript que vous avez créé. En instancier une nouvelle chaque fois que vous souhaitez brouiller une image, cela ne fait qu'ajouter une surcharge inutile (création / destruction d'objets Java).
Stephen Hines
4

Ceci est pour toutes les personnes qui ont besoin d'augmenter le rayon de ScriptIntrinsicBlur pour obtenir un flou gaussien plus dur.

Au lieu de mettre le rayon à plus de 25, vous pouvez réduire l'image et obtenir le même résultat. J'ai écrit une classe appeléeGaussianBlur . Ci-dessous, vous pouvez voir comment utiliser et toute l'implémentation de la classe.

Usage:

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;
}
    }
Vansuita Jr.
la source
non si vous réduisez l'image pour l'agrandir plus tard, vous récupérerez une image en blocs au lieu d'une image estompée :(
loki
4

Merci @Yahel pour le code. Publier la même méthode avec la prise en charge du flou du canal alpha car il m'a fallu un certain temps pour la faire fonctionner correctement, ce qui peut faire gagner du temps à quelqu'un:

/**
 * Stack Blur v1.0 from
 * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
 * Java Author: Mario Klingemann <mario at quasimondo.com>
 * http://incubator.quasimondo.com
 * <p/>
 * created Feburary 29, 2004
 * Android port : Yahel Bouaziz <yahel at kayenko.com>
 * http://www.kayenko.com
 * ported april 5th, 2012
 * <p/>
 * This is a compromise between Gaussian Blur and Box blur
 * It creates much better looking blurs than Box Blur, but is
 * 7x faster than my Gaussian Blur implementation.
 * <p/>
 * I called it Stack Blur because this describes best how this
 * filter works internally: it creates a kind of moving stack
 * of colors whilst scanning through the image. Thereby it
 * just has to add one new block of color to the right side
 * of the stack and remove the leftmost color. The remaining
 * colors on the topmost layer of the stack are either added on
 * or reduced by one, depending on if they are on the right or
 * on the left side of the stack.
 * <p/>
 * If you are using this algorithm in your code please add
 * the following line:
 * Stack Blur Algorithm by Mario Klingemann <[email protected]>
 */

public static Bitmap fastblur(Bitmap sentBitmap, float scale, int radius) {

    int width = Math.round(sentBitmap.getWidth() * scale);
    int height = Math.round(sentBitmap.getHeight() * scale);
    sentBitmap = Bitmap.createScaledBitmap(sentBitmap, width, height, false);

    Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);

    if (radius < 1) {
        return (null);
    }

    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    int[] pix = new int[w * h];
    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.getPixels(pix, 0, w, 0, 0, w, h);

    int wm = w - 1;
    int hm = h - 1;
    int wh = w * h;
    int div = radius + radius + 1;

    int r[] = new int[wh];
    int g[] = new int[wh];
    int b[] = new int[wh];
    int a[] = new int[wh];
    int rsum, gsum, bsum, asum, x, y, i, p, yp, yi, yw;
    int vmin[] = new int[Math.max(w, h)];

    int divsum = (div + 1) >> 1;
    divsum *= divsum;
    int dv[] = new int[256 * divsum];
    for (i = 0; i < 256 * divsum; i++) {
        dv[i] = (i / divsum);
    }

    yw = yi = 0;

    int[][] stack = new int[div][4];
    int stackpointer;
    int stackstart;
    int[] sir;
    int rbs;
    int r1 = radius + 1;
    int routsum, goutsum, boutsum, aoutsum;
    int rinsum, ginsum, binsum, ainsum;

    for (y = 0; y < h; y++) {
        rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0;
        for (i = -radius; i <= radius; i++) {
            p = pix[yi + Math.min(wm, Math.max(i, 0))];
            sir = stack[i + radius];
            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            sir[3] = 0xff & (p >> 24);

            rbs = r1 - Math.abs(i);
            rsum += sir[0] * rbs;
            gsum += sir[1] * rbs;
            bsum += sir[2] * rbs;
            asum += sir[3] * rbs;
            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
                ainsum += sir[3];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
                aoutsum += sir[3];
            }
        }
        stackpointer = radius;

        for (x = 0; x < w; x++) {

            r[yi] = dv[rsum];
            g[yi] = dv[gsum];
            b[yi] = dv[bsum];
            a[yi] = dv[asum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;
            asum -= aoutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];
            aoutsum -= sir[3];

            if (y == 0) {
                vmin[x] = Math.min(x + radius + 1, wm);
            }
            p = pix[yw + vmin[x]];

            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            sir[3] = 0xff & (p >> 24);

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];
            ainsum += sir[3];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;
            asum += ainsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[(stackpointer) % div];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];
            aoutsum += sir[3];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];
            ainsum -= sir[3];

            yi++;
        }
        yw += w;
    }
    for (x = 0; x < w; x++) {
        rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0;
        yp = -radius * w;
        for (i = -radius; i <= radius; i++) {
            yi = Math.max(0, yp) + x;

            sir = stack[i + radius];

            sir[0] = r[yi];
            sir[1] = g[yi];
            sir[2] = b[yi];
            sir[3] = a[yi];

            rbs = r1 - Math.abs(i);

            rsum += r[yi] * rbs;
            gsum += g[yi] * rbs;
            bsum += b[yi] * rbs;
            asum += a[yi] * rbs;

            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
                ainsum += sir[3];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
                aoutsum += sir[3];
            }

            if (i < hm) {
                yp += w;
            }
        }
        yi = x;
        stackpointer = radius;
        for (y = 0; y < h; y++) {
            pix[yi] = (dv[asum] << 24) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;
            asum -= aoutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];
            aoutsum -= sir[3];

            if (x == 0) {
                vmin[y] = Math.min(y + r1, hm) * w;
            }
            p = x + vmin[y];


            sir[0] = r[p];
            sir[1] = g[p];
            sir[2] = b[p];
            sir[3] = a[p];

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];
            ainsum += sir[3];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;
            asum += ainsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[stackpointer];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];
            aoutsum += sir[3];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];
            ainsum -= sir[3];

            yi += w;
        }
    }

    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.setPixels(pix, 0, w, 0, 0, w, h);

    return (bitmap);
}
virus
la source
toujours avec `` index hors de portée '' dans les appareils> hdpi comme source d'origine
Gabriel Ferreira
4

J'ai utilisé ça avant ..

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;
        }
Huseyin
la source
2

Pour les futurs Googleurs qui choisissent l'approche NDK - je trouve un algorithme de stackblur fiable. J'ai trouvé ici une implémentation C ++ qui ne repose pas sur SSE - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32 qui contient quelques optimisations utilisant des tables statiques comme:

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

J'ai modifié l'algorithme stackblur pour les systèmes multi-core - il peut être trouvé ici http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ Comme de plus en plus d'appareils ont 4 cœurs - les optimisations donnent Avantage de vitesse 4x.

Victor Laskin
la source
1

Conseils de Nicolas POMEPUY. Je pense que ce lien sera utile: effet de flou pour la conception Android

Exemple de projet sur 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;
}
Yura Shinkarev
la source
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien pour référence. Les réponses aux liens uniquement peuvent devenir invalides si la page liée change.
Amal Murali
1
Amal Murali, vous avez raison. Maintenant, changez mon message. Bien, non seulement vous votez contre, mais vous commentez également.
Yura Shinkarev
1

Nous avons essayé d'implémenter le flou RenderScript comme mentionné ci-dessus dans différentes réponses. Nous étions limités à utiliser la version v8 RenderScript et cela nous a causé beaucoup de problèmes.

  • Samsung S3 s'est écrasé au hasard chaque fois que nous avons essayé d'utiliser le rendu
  • D'autres appareils (sur différentes API) ont montré au hasard différents problèmes de couleur

Je veux partager notre version sale de Java uniquement qui est lente et devrait être effectuée sur un thread séparé et, si possible, avant utilisation et donc persistante.

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

Cette solution est loin d'être parfaite mais crée un effet de flou raisonnable basé sur le fait qu'elle dessine une version hautement transparente de la même image sur une version "nette" à peine transparente. L'alpha dépend de la distance à l'origine.

Vous pouvez ajuster certains «nombres magiques» à vos besoins. Je voulais juste partager cette "solution" pour tous ceux qui ont des problèmes avec la version de support v8 de RenderScript.

WarrenFaith
la source
J'ai également un problème avec RenderScript sur certains anciens appareils armeabi, mais votre solution consomme trop de mémoire.
AsafK
0

Pour ceux qui ont encore des problèmes avec la bibliothèque de support Renderscript sur les chipsets x86, veuillez consulter cet article du créateur de la bibliothèque. Il semble que le correctif qu'il a préparé n'a pas réussi à atteindre les outils de construction v20.0.0, il fournit donc les fichiers pour le corriger manuellement et une brève explication sur la façon de le faire.

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

mradzinski
la source
0

J'ai trouvé qu'une diminution du contraste, de la luminosité et de la saturation rendait les images floues plus jolies.J'ai donc combiné diverses méthodes de débordement de pile et créé cette classe de flou qui traite des images floues, de la modification de la luminosité, de la saturation, du contraste et de la taille des images floues. Il peut également convertir des images de dessinables en bitmap et vice-versa.

Ankit Gaur
la source
0

Lors des entrées / sorties 2019, la solution suivante a été présentée:

/**
 * 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()
    }
}
Leo Droidcoder
la source