Flou de bitmap rapide pour le SDK Android

Actuellement, dans une application Android que je développe, je passe en revue les pixels d’une image pour la rendre floue. Cela prend environ 30 secondes sur une image 640×480.

Lors de la navigation dans les applications de l’Android Market, j’en ai rencontré une qui comporte une fonction de flou et leur flou est très rapide (environ 5 secondes), elles doivent donc utiliser une méthode de flou différente.

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

Ceci est une prise de vue 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 s’exécutera en code natif pour être plus rapide.

Pour les futurs Googlers, voici un algorithme que j’ai porté de Quasimondo. C’est un peu un mélange entre un flou de boîte et un flou gaussien, c’est très joli et assez rapide aussi:

 /** * Stack Blur v1.0 from * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html * Java Author: Mario Klingemann  * http://incubator.quasimondo.com * * created Feburary 29, 2004 * Android port : Yahel Bouaziz  * 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  */ 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); } 

Android Blur Guide 2016

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

Après avoir beaucoup expérimenté, je peux maintenant vous donner des recommandations solides qui vous faciliteront la vie avec Android en utilisant Android Framework.

Chargement et utilisation de bitmap réduite (pour des images très floues)

Ne jamais utiliser la taille d’un bitmap. Plus l’image est grande, plus le flou est important et plus le rayon de flou doit être élevé et plus le rayon de flou est élevé, plus l’algorithme est lent.

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

Cela va charger le bitmap avec inSampleSize 8, donc seulement 1/64 de l’image d’origine. Testez ce que inSampleSize répond à vos besoins, mais conservez-le 2, 4 (2,4,8, …) pour éviter toute dégradation de la qualité due à la mise à l’échelle. Voir Google doc pour plus

Un autre avantage très important est que le chargement bitmap sera très rapide. Lors de mes premiers tests de flou, je pensais que le chargement d’images était le plus long pendant tout le processus de flou. Donc, pour charger une image 1920×1080 à partir du disque, mon Nexus 5 avait besoin de 500 ms alors que le flou ne prenait que 250 ms environ.

Utiliser Renderscript

Renderscript fournit ScriptInsortingnsicBlur qui est un filtre flou gaussien. Il a une bonne qualité visuelle et est le plus rapide que vous obtenez avec Android. Google prétend être “généralement 2 à 3 fois plus rapide qu’une implémentation C multithread et souvent 10 fois plus rapide qu’une implémentation Java” . Renderscript est vraiment sophistiqué (en utilisant un périphérique de traitement fastes (GPU, ISP, etc.), etc.) et il existe également la bibliothèque de support v8 qui le rend compatible jusqu’à la version 2.2 . Eh bien au moins en théorie, à travers mes propres tests et rapports provenant d’autres développeurs, il semble impossible d’utiliser un rendu aveugle, car la fragmentation du matériel / pilote semble causer des problèmes avec certains périphériques, même problèmes avec le 4.1 Nexus S) alors soyez prudent et testez sur beaucoup d’appareils. Voici un exemple simple qui vous permettra de 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 ScriptInsortingnsicBlur script = ScriptInsortingnsicBlur.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, spécifiquement recommandé par Google “car ils incluent les dernières améliorations” , il vous suffit d’append 2 lignes à votre script de génération et d’utiliser android.support.v8.renderscript avec les outils de construction actuels ( syntaxe mise à jour). pour android gradle plugin 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 pic Le temps d’exécution moyen par flou sur différentes tailles de pic

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_FAST est ScriptInsortingnsicBlur (et presque toujours le plus rapide), d’autres commençant par RS_ sont principalement des implémentations de convolution avec des kernelx simples. Les détails des algorithmes peuvent être trouvés ici . Ce n’est pas purement flou, mais une bonne partie est la récupération de place qui est mesurée. Cela peut être vu ici ( ScriptInsortingnsicBlur sur l’image 100×100 avec environ 500 tours)

entrer la description de l'image ici

Les pointes sont gc.

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

Réutilise Bitmap dans la mesure du possible (si prio: performance> 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 tirables plusieurs fois, mais conservez-le “en cache” dans une variable membre. Dans ce cas, essayez toujours d’utiliser les mêmes variables, afin de minimiser la collecte de déchets.

Consultez également la nouvelle option “inBitmap” lors du chargement à partir d’un fichier ou d’un fichier pouvant être dessiné, qui réutilisera la mémoire bitmap et enregistre le temps de récupération de la mémoire.

Pour mélanger de net à flou

La méthode simple et naïve consiste simplement à utiliser 2 vues d’images, l’une floue et l’alpha. Mais si vous voulez un look plus sophistiqué, qui disparaît en douceur du flou, alors consultez l’article de Roman Nurik sur la façon de le faire dans son application Muzei .

Fondamentalement, il explique qu’il brouille certaines images avec différentes extensions de flou et les utilise comme images clés dans une animation qui a l’air très lisse.

Diagramme où Nurik exagère son approche

EDIT (avril 2014): Il s’agit d’une page de questions / réponses qui a toujours beaucoup de succès. Je sais que je reçois toujours des abonnements pour cet article. Mais si vous lisez ceci, vous devez vous rendre compte que les réponses affichées ici (à la fois la mienne et la réponse acceptée) sont obsolètes. Si vous souhaitez implémenter le flou efficace aujourd’hui , vous devez utiliser RenderScript au lieu du NDK ou de Java. RenderScript fonctionne sur Android 2.2+ (à l’aide de la bibliothèque de support Android ), il n’y a donc aucune raison de ne pas l’utiliser.

L’ancienne réponse suit, mais méfiez-vous car elle est obsolète.


Pour les futurs Googlers, voici un algorithme que j’ai porté du 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 utilise du code C natif, donc c’est plus rapide. Plus vite. Comme 40 fois plus vite.

Je trouve que l’utilisation du NDK est la façon dont toute manipulation d’image doit être faite sur Android … c’est un peu ennuyeux à mettre en œuvre au départ (lisez un excellent tutoriel sur l’utilisation de JNI et du NDK ici ), mais bien meilleur beaucoup de choses.

Pour référence, en utilisant la fonction Java de Yahel, il a fallu 10 secondes pour rendre floue mon image de 480×532 pixels avec un rayon de flou de 10. Mais cela a pris 250ms en utilisant la version C native. Et je suis 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, ne voulant pas passer trop de temps à refactoriser le tout.

 #include  #include  #include  #include  #include  #include  #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 (yuck) ou modifiez la fonction pour utiliser un nouveau type rgb565 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 , .green et .blue du pixel, vous devez lire l’octet correctement, duh. Quand j’en avais besoin auparavant, je l’ai fait:

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

Mais il y a probablement une façon moins stupide de le faire. Je crains que je ne sois pas vraiment un codeur C de bas niveau.

Ce code est un travail parfait 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); ScriptInsortingnsicBlur blur = ScriptInsortingnsicBlur.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; } } 

Vous pouvez maintenant utiliser ScriptInsortingnsicBlur à partir de la bibliothèque RenderScript pour flouter rapidement. Voici comment accéder à l’API RenderScript. Ce qui suit est une classe que j’ai faite pour rendre floues 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); ScriptInsortingnsicBlur theInsortingnsic = ScriptInsortingnsicBlur.create(rs, Element.U8_4(rs)); Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); theInsortingnsic.setRadius(BLUR_RADIUS); theInsortingnsic.setInput(tmpIn); theInsortingnsic.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; } } 

Cela a bien fonctionné pour moi: comment flouter efficacement les images 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); ScriptInsortingnsicBlur theInsortingnsic = ScriptInsortingnsicBlur.create(rs, Element.U8_4(rs)); Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); theInsortingnsic.setRadius(BLUR_RADIUS); theInsortingnsic.setInput(tmpIn); theInsortingnsic.forEach(tmpOut); tmpOut.copyTo(outputBitmap); return outputBitmap; } } 

Utilisez Render Script comme mentionné ici http://blog.neteril.org/blog/2013/08/12/blurring-images-on-android/

Merci @Yahel pour le code. Publier la même méthode avec le support flou du canal alpha car cela m’a pris du temps pour le faire fonctionner correctement afin de gagner du temps:

 /** * Stack Blur v1.0 from * http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html * Java Author: Mario Klingemann  * http://incubator.quasimondo.com * 

* created Feburary 29, 2004 * Android port : Yahel Bouaziz * 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 */ 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); }

Je l’ai utilisé 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); ScriptInsortingnsicBlur theInsortingnsic = ScriptInsortingnsicBlur.create(rs, Element.U8_4(rs)); Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); theInsortingnsic.setRadius(BLUR_RADIUS); theInsortingnsic.setInput(tmpIn); theInsortingnsic.forEach(tmpOut); tmpOut.copyTo(outputBitmap); return outputBitmap; } 

Pour les futurs Googlers qui choisissent l’approche NDK, je trouve l’algorithme stackblur mentionné. I found C++ implementation which does not rely on SSE here – http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32 which contains some optimizations using static tables like:

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

I made modification of stackblur algorithm for multi-core systems – it can be found here http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ As more and more devices have 4 cores – optimizations give 4x speed benefit.

This is for all people who need to increase the radius of ScriptInsortingnsicBlur to obtain a harder gaussian blur.

Instead of to put the radius more than 25, you can scale down the image and get the same result. I wrote a class called GaussianBlur . Below you can see how to use, and the whole class implementation.

Usage:

 GaussianBlur gaussian = new GaussianBlur(context); gaussian.setMaxImageSize(60); gaussian.setRadius(25); //max Bitmap output = gaussian.render(,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); ScriptInsortingnsicBlur script = ScriptInsortingnsicBlur.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; } } 

Nicolas POMEPUY advice. I think this link will be helpful: Blur effect for Android design

Sample project at 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()); ScriptInsortingnsicBlur script = ScriptInsortingnsicBlur.create(rs, Element.U8_4(rs)); script.setRadius(radius); script.setInput(input); script.forEach(output); output.copyTo(bitmap); return bitmap; } 

We sortinged to implement RenderScript blur like mentioned above in different answers. We were limited to use the v8 RenderScript version and that caused us a lot of trouble.

  • Samsung S3 crashed randomly whenever we sortinged to use the renderscript
  • Other devices (across different APIs) randomly showed different color issues

I want to share our dirty Java-only version which is slow and should be done on a separate thread and, if possible, before usage and therefore persisted.

 private final Paint mPaint = new Paint(); public Bitmap blur(final Ssortingng 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; } 

This solution is far from perfect but creates a reasonable blur effect based on the fact, that it draws highly transparent version of the same image on top of a barely transparent "sharp" version. The alpha depends on the distance to the origin.

You can adjust some "magic numbers" to your needs. I just wanted to share that "solution" for everybody who has issues with the v8 support version of RenderScript.

For those still having issues with Renderscript support library on x86 chipsets, please have a look at this post by the creator of the library. It looks like the fix he prepared didn’t make it somehow to the Build Tools v20.0.0, so he provides the files to fix it manually and a brief explanation of how to do it.

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

from Mario Viviani blog one can use this solution from 17 Android Version:

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

ou

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

Here is a realtime blurring overlay using RenderScript, which seems to be fast enough.

https://github.com/mmin18/RealtimeBlurView

I found that decreasing contrast, brightness and saturation a little makes blurred images more pretty so I combined various methods from stack overflow and made this Blur Class which deals with blurring images, changing brightness, saturation, contrast and size of the blurred images. It can also convert images from drawable to bitmap and vice-versa.