Desenfoque rápido de mapa de bits para Android SDK

185

Actualmente en una aplicación de Android que estoy desarrollando, estoy recorriendo los píxeles de una imagen para desenfocarla. Esto toma alrededor de 30 segundos en una imagen de 640x480.

Mientras navegaba por las aplicaciones en Android Market, me encontré con una que incluye una función de desenfoque y su desenfoque es muy rápido (como 5 segundos), por lo que deben estar usando un método diferente de desenfoque.

¿Alguien conoce una forma más rápida que no sea recorrer los píxeles?

Greg
fuente
2
Lamentablemente, las imágenes siempre serán diferentes, por lo que no podré crear una versión borrosa antes de tiempo. Además, tampoco sabré la intensidad del desenfoque con anticipación.
Greg
Podría publicar su código, tal vez sea el algoritmo / código el que sea ineficiente, 30 segundos para pasar por una imagen de 640x480 es lento, hubiera pensado que 5 segundos fueron lentos pero depende de nuevo del procesador.
vickirk

Respuestas:

78

Esta es una toma en la oscuridad, pero puede intentar reducir la imagen y luego agrandarla nuevamente. Esto se puede hacer con Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Asegúrese de establecer el parámetro de filtro en verdadero. Se ejecutará en código nativo, por lo que podría ser más rápido.

Luke
fuente
55
Después de algunas pruebas y el desenfoque que estoy haciendo, esto funciona bastante bien para mí y es rápido. ¡Gracias!
Greg
44
Si funciona bien Es una pena que nunca hayamos llegado al fondo de por qué era ineficiente.
vickirk
Es posible que desee probar createScaledBitmap y dejar la imagen del mismo tamaño. Me está borrando :-(
Casebash
1
Aquí hay una discusión sobre el significado del argumento "filtro": stackoverflow.com/questions/2895065
user1364368
44
Esta no es exactamente la forma de obtenerla debido a 2 razones: 1) necesita la memoria de la imagen completa, aunque probablemente solo esté usando una escala reducida 2) necesita cargar la imagen completa que es más lenta: use la carga con inSampleSize y BitmapFactory.decodeResource (), que es una solución muy superior a esto.
Patrick Favre
303

Para futuros Googlers, aquí hay un algoritmo que porté desde Quasimondo. Es una especie de mezcla entre un desenfoque de caja y un desenfoque gaussiano, también es muy bonito y bastante rápido.

Actualización para las personas que se encuentran con el problema ArrayIndexOutOfBoundsException : @anthonycr en los comentarios proporciona esta información:

Descubrí que al reemplazar Math.abs con StrictMath.abs o alguna otra implementación de abdominales, el bloqueo no ocurre.

/**
 * 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
fuente
44
Gracias Yahel Resolviste mi problema. Gracias de nuevo.
Yog Guru
1
¿Qué debo pasar como radio?
krisDrOid
16
Para radios mayores que 1, a veces se obtiene ArrayIndexOutOfBoundsException. Intentaré identificar el problema.
MikeL
77
@MichaelLiberman También encontré el mismo problema. ¿Ya descubriste por qué? "g [yi] = dv [gsum];" -> error: java.lang.ArrayIndexOutOfBoundsException: longitud = 112896; índice = 114021
2851
2
Me encontré con la conocida ArrayIndexOutOfBoundsException, y después de algunos análisis, creo que es causada por una optimización incorrecta por parte de la máquina virtual Dalvik. En el forbucle inmediatamente antes de la mala eliminar la referencia, ya sea el cálculo de la rbsvariable o el cálculo de los gsum, rsumo bsumvariables no se está haciendo bien. Descubrí que al reemplazar Math.abscon StrictMath.abso alguna otra absimplementación, el bloqueo no ocurre. Como en StrictMath.abssí mismo delega Math.abs, parece que debe ser una mala optimización.
anthonycr
255

Android Blur Guide 2016

con Showcase / Benchmark App y Source en Github . Consulte también el marco de Blur en el que estoy trabajando actualmente: Dali .

Después de experimentar mucho, ahora puedo darte algunas recomendaciones sólidas que te harán la vida más fácil en Android cuando uses Android Framework.

Cargue y use un mapa de bits reducido (para imágenes muy borrosas)

Nunca use un tamaño completo de un mapa de bits. Cuanto más grande es la imagen, más necesita ser borrosa y también más alto debe ser el radio de desenfoque y, por lo general, cuanto mayor sea el radio de desenfoque, más tiempo tomará el algoritmo.

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

Esto cargará el mapa de bits con inSampleSize8, por lo que solo 1/64 de la imagen original. Pruebe lo que se inSampleSizeadapte a sus necesidades, pero manténgalo 2 ^ n (2,4,8, ...) para evitar degradar la calidad debido al escalado. Consulte el documento de Google para obtener más información.

Otra gran ventaja es que la carga de mapas de bits será realmente rápida. En mis primeras pruebas de desenfoque, pensé que el tiempo más largo durante todo el proceso de desenfoque fue la carga de la imagen. Entonces, para cargar una imagen de 1920x1080 desde el disco, mi Nexus 5 necesitó 500 ms, mientras que el desenfoque solo tomó otros 250 ms más o menos.

Use Renderscript

Renderscript proporciona ScriptIntrinsicBlurcuál es un filtro de desenfoque gaussiano. Tiene buena calidad visual y es el más rápido que obtienes de forma realista en Android. Google afirma ser "típicamente 2-3 veces más rápido que una implementación C multiproceso y, a menudo, 10 veces más rápido que una implementación Java" . Renderscript es realmente sofisticado (utilizando el dispositivo de procesamiento más rápido (GPU, ISP, etc.), etc.) y también existe la biblioteca de soporte v8 para que sea compatible hasta 2.2. Bueno, al menos en teoría, a través de mis propias pruebas e informes de otros desarrolladores, parece que no es posible usar Renderscript a ciegas, ya que la fragmentación del hardware / controlador parece causar problemas con algunos dispositivos, incluso con un nivel de SDK más alto (por ejemplo, tuve problemas con el 4.1 Nexus S), así que tenga cuidado y pruebe en muchos dispositivos. Aquí hay un ejemplo simple que lo ayudará a comenzar:

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

Cuando se utiliza el soporte v8 con Gradle, que Google recomienda específicamente "porque incluyen las últimas mejoras" , solo necesita agregar 2 líneas a su script de compilación y usarlo android.support.v8.renderscriptcon las herramientas de compilación actuales ( sintaxis actualizada para el complemento Android Gradle v14 + )

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

Punto de referencia simple en un Nexus 5: comparación de RenderScript con otras implementaciones de Java y Renderscript diferentes:

El tiempo de ejecución promedio por desenfoque en diferentes tamaños de imagen El tiempo de ejecución promedio por desenfoque en diferentes tamaños de imagen

Megapíxeles por segundo que pueden ser borrosos Megapíxeles por segundo que pueden ser borrosos

Cada valor es el promedio de 250 rondas. RS_GAUSS_FASTes ScriptIntrinsicBlur(y casi siempre el más rápido), otros que comienzan RS_son en su mayoría implementaciones complicadas con núcleos simples. Los detalles de los algoritmos se pueden encontrar aquí . Esto no es puramente borroso, ya que una buena parte es la recolección de basura que se mide. Esto se puede ver aquí ( ScriptIntrinsicBluren una imagen de 100x100 con aproximadamente 500 rondas)

ingrese la descripción de la imagen aquí

Los picos son gc.

Puede comprobarlo usted mismo, la aplicación de referencia está en la Play Store: BlurBenchmark

Reutiliza Bitmap siempre que sea posible (si prio: rendimiento> huella de memoria)

Si necesita múltiples desenfoques para un desenfoque en vivo o similar y su memoria lo permite, no cargue el mapa de bits de los dibujos varias veces, sino que lo mantenga "en caché" en una variable miembro. En este caso, siempre trate de usar las mismas variables, para mantener la recolección de basura al mínimo.

También revise la nueva inBitmapopción al cargar desde un archivo o dibujable que reutilizará la memoria de mapa de bits y ahorrará tiempo de recolección de basura.

Para mezclar de nítido a borroso

El método simple e ingenuo es usar 2 ImageViews, uno borroso y alfa desvanecerse. Pero si desea un aspecto más sofisticado que se desvanezca suavemente de nítido a borroso, consulte la publicación de Roman Nurik sobre cómo hacerlo en su aplicación Muzei .

Básicamente, explica que difumina previamente algunos fotogramas con diferentes extensiones de desenfoque y los usa como fotogramas clave en una animación que se ve realmente suave.

Diagrama donde Nurik explica su enfoque

Patrick Favre
fuente
1
En primer lugar, ¡gracias por su arduo trabajo! Pero tengo una pregunta: "porque usa el modo USAGE_SHARED que reutiliza la memoria". ¿Dónde encontraste la constante USAGE_SHARED? No pude encontrarlo en ningún lado.
Algún estudiante de Noob
2
Lo encontré, USAGE_SHARED solo está disponible en support.v8.renderscript
Some Noob Student
2
El desenfoque gaussiano rápido de Renderscript falla con errores de asignación de memoria C en dispositivos de gama baja. Probado en ZTE Z992 (Android 4.1.1) y Kyocera Rise (Android 4.0.4) utilizando la aplicación Play Store proporcionada. También tenía un informe de falla en el Samsung Galaxy S3 mini. Dado que se producen errores en la capa C, no pueden quedar atrapados como excepciones en Java, lo que significa que un bloqueo de la aplicación es inevitable. Parece que RenderScript puede no estar listo para su uso en producción.
Theo
44
para versiones más recientes de gradle, ¡use renderscriptSupportModeEnabled trueo no se construirá! ¡Busqué por siempre!
seb
3
Cuando probé esta solución, en lugar de obtener un mapa de bits borroso, obtuve un mapa de bits de color arcoíris . ¿Alguien más experimentó este problema? ¿Si es así, cómo lo arreglaste?
HaloMediaz
53

EDITAR (abril de 2014): Esta es una página de preguntas / respuestas que todavía recibe muchos éxitos, parece. Sé que siempre recibo votos positivos para esta publicación. Pero si está leyendo esto, debe darse cuenta de que las respuestas publicadas aquí (tanto la mía como la respuesta aceptada) están desactualizadas. Si desea implementar un desenfoque eficiente hoy , debe usar RenderScript en lugar de NDK o Java. RenderScript se ejecuta en Android 2.2+ (usando la Biblioteca de soporte de Android ), por lo que no hay razón para no usarlo.

La vieja respuesta sigue, pero ten cuidado ya que está desactualizada.


Para los futuros Googlers, aquí hay un algoritmo que porté desde el puerto de Yahel del algoritmo de Quasimondo, pero usando el NDK. Se basa en la respuesta de Yahel, por supuesto. Pero esto está ejecutando código C nativo, por lo que es más rápido. Mucho mas rápido. Como, 40 veces más rápido.

Me parece que usar el NDK es cómo se debe hacer toda la manipulación de imágenes en Android ... es algo molesto implementarlo al principio (lea un excelente tutorial sobre el uso de JNI y el NDK aquí ), pero mucho mejor, y casi en tiempo real para un muchas cosas.

Como referencia, utilizando la función Java de Yahel, tardé 10 segundos en desenfocar mi imagen de 480x532 píxeles con un radio de desenfoque de 10. Pero tomó 250 ms usando la versión nativa de C. Y estoy bastante seguro de que aún puede optimizarse aún más ... Acabo de hacer una conversión tonta del código de Java, probablemente hay algunas manipulaciones que pueden acortarse, no quería pasar demasiado tiempo refactorizando todo el asunto.

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

Luego úselo de esta manera (considerando una clase llamada com.insert.your.package.ClassName y una función nativa llamada functionToBlur, como dice el código anterior):

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

¡Espera un mapa de bits RGB_8888!

Para usar un mapa de bits RGB_565, cree una copia convertida antes de pasar el parámetro (yuck) o cambie la función para usar un nuevo rgb565tipo en lugar de rgba:

typedef struct {
    uint16_t byte0;
} rgb565;

El problema es que si haces eso no puedes leer .red , .greeny .bluedel píxel más, necesitas leer el byte correctamente, duh. Cuando lo necesitaba antes, hice esto:

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

Pero probablemente haya una forma menos tonta de hacerlo. Me temo que no soy un codificador C de bajo nivel.

zeh
fuente
Gracias, me ha ayudado mucho :)
Dmitry Zaytsev
18
PERO requiere mucha memoria. Para reducir el consumo de memoria, cambie el tipo de r[wh], g[wh]y b[wh]a uint8_t.
Dmitry Zaytsev
¿me puede mostrar lo que su apariencia de archivos Android.mk gusta, sobrepastebin.com
CQM
1
Para ver un ejemplo de RenderScript en funcionamiento de Gaussian Blur en Android SDK 17+, consulte aquí: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini
2
RenderScript también está disponible como parte de la biblioteca de soporte para Android 2.2+, por lo que ya no hay razón para no usarlo en todas partes: android-developers.blogspot.com/2013/09/…
zeh
14

Este código es perfecto para mi

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
fuente
Forma más sencilla de desenfoque de la imagen (y)
VAdaihiep
12

Ahora puede usar ScriptIntrinsicBlur de la biblioteca RenderScript para desenfocar rápidamente. Aquí se explica cómo acceder a la API de RenderScript. La siguiente es una clase que hice para difuminar vistas y mapas de bits:

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
fuente
2
El contexto Renderscript no debe crearse en el método de desenfoque, sino administrarse de forma estática o administrarse al método. (si te importa el rendimiento)
Patrick Favre
1
¿Puedes dar un ejemplo de esto es el uso? Cuando trato de usarlo aparece el siguiente error: java.lang.IllegalArgumentException: ancho y alto deben ser> 0
Donal Rafferty
10

Esto funcionó bien para mí: cómo desenfocar imágenes de manera eficiente con RenderScript de 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
fuente
Esto sería aún mejor / más rápido si almacena en caché el objeto RenderScript que creó. Crear una instancia nueva cada vez que desee desenfocar una imagen solo agrega una sobrecarga innecesaria (creación / destrucción de objetos Java).
Stephen Hines
4

Esto es para todas las personas que necesitan aumentar el radio de ScriptIntrinsicBlurpara obtener un desenfoque gaussiano más difícil.

En lugar de poner el radio más de 25, puede reducir la imagen y obtener el mismo resultado. Escribí una clase llamadaGaussianBlur . A continuación puede ver cómo usar, y la implementación de toda la clase.

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

Clase:

 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.
fuente
no, si reduce la imagen para ampliarla más tarde, recuperará una imagen en bloque en lugar de una imagen borrosa :(
loki
4

Gracias @Yahel por el código. Publicar el mismo método con soporte de desenfoque de canal alfa ya que me tomó un tiempo hacer que funcione correctamente para que pueda ahorrarle tiempo a alguien:

/**
 * 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
fuente
todavía con 'índice fuera de rango' en dispositivos> hdpi como fuente original
Gabriel Ferreira
4

Usé esto antes ...

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
fuente
2

Para los futuros Googlers que eligen el enfoque NDK, encuentro un algoritmo confiable de stackblur mencionado. Encontré la implementación de C ++ que no se basa en SSE aquí: http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32 que contiene algunas optimizaciones utilizando tablas estáticas como:

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

Modifiqué el algoritmo stackblur para sistemas de múltiples núcleos. Puede encontrarlo aquí http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ A medida que más y más dispositivos tienen 4 núcleos, las optimizaciones dan Beneficio de velocidad 4x.

Victor Laskin
fuente
1

Nicolas POMEPUY consejos. Creo que este enlace será útil: efecto de desenfoque para el diseño de Android

Proyecto de muestra en 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
fuente
Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia.
Amal Murali
1
Amal Murali, tienes razón. Ahora cambia mi publicación. Bien, que no solo desestimes, sino que también comentes.
Yura Shinkarev
1

Intentamos implementar RenderScript blur como se mencionó anteriormente en diferentes respuestas. Estábamos limitados a usar la versión v8 RenderScript y eso nos causó muchos problemas.

  • Samsung S3 se bloquea al azar cada vez que intentamos usar el renderscript
  • Otros dispositivos (en diferentes API) mostraron aleatoriamente diferentes problemas de color

Quiero compartir nuestra versión sucia de Java, que es lenta y debe hacerse en un hilo separado y, si es posible, antes del uso y, por lo tanto, persistió.

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

Esta solución está lejos de ser perfecta, pero crea un efecto de desenfoque razonable basado en el hecho de que dibuja una versión altamente transparente de la misma imagen sobre una versión "nítida" apenas transparente. El alfa depende de la distancia al origen.

Puede ajustar algunos "números mágicos" a sus necesidades. Solo quería compartir esa "solución" para todos los que tienen problemas con la versión de soporte v8 de RenderScript.

WarrenFe
fuente
También tengo un problema con RenderScript en algunos dispositivos antiguos de armeabi, pero su solución consume demasiada memoria.
AsafK
0

Para aquellos que todavía tienen problemas con la biblioteca de soporte de Renderscript en chipsets x86, eche un vistazo a esta publicación del creador de la biblioteca. Parece que la solución que preparó no llegó de alguna manera a Build Tools v20.0.0, por lo que proporciona los archivos para solucionarlo manualmente y una breve explicación de cómo hacerlo.

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
fuente
0

Descubrí que disminuir un poco el contraste, el brillo y la saturación hace que las imágenes borrosas sean más bonitas, así que combiné varios métodos de desbordamiento de pila e hice esta clase de desenfoque que se ocupa de imágenes borrosas, cambiando el brillo, la saturación, el contraste y el tamaño de las imágenes borrosas. También puede convertir imágenes de dibujables a mapas de bits y viceversa.

Ankit Gaur
fuente
0

En el i / o 2019 se presentó la siguiente solución:

/**
 * 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
fuente