Desfoque rápido de bitmap para Android SDK

185

Atualmente em um aplicativo Android que estou desenvolvendo, estou percorrendo os pixels de uma imagem para desfocá-la. Isso leva cerca de 30 segundos em uma imagem de 640x480.

Ao navegar pelos aplicativos no Android Market, deparei-me com um que inclui um recurso de desfoque e seu desfoque é muito rápido (como 5 segundos), portanto eles devem estar usando um método diferente de desfoque.

Alguém conhece uma maneira mais rápida que não seja repetir os pixels?

Greg
fonte
2
Infelizmente, as imagens sempre serão diferentes, então não poderei criar uma versão borrada antes do tempo. Além disso, também não saberei a intensidade do desfoque com antecedência.
Greg
Você poderia postar seu código, talvez seja o algoritmo / código que é ineficiente, 30 segundos para passar por uma imagem de 640x480 é lento, eu pensaria que 5 segundos eram lentos, mas depende novamente do processador.
vickirk

Respostas:

78

Esta é uma foto no escuro, mas você pode tentar diminuir a imagem e ampliá-la novamente. Isso pode ser feito com Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Certifique-se e defina o parâmetro filter como true. Ele será executado no código nativo para que seja mais rápido.

Lucas
fonte
5
Depois de alguns testes e o desfoque que estou fazendo, isso funciona bem o suficiente para mim e é rápido. Obrigado!
Greg
4
Se funcionar bem. É uma pena que nunca tenhamos chegado ao fundo do porquê de ser ineficiente.
vickirk
Convém tentar createScaledBitmap e deixar a imagem do mesmo tamanho. Está borrando para mim :-(
Casebash 14/05
1
Aqui está uma discussão sobre o significado do argumento "filtro": stackoverflow.com/questions/2895065
user1364368
4
Este não é exatamente o caminho a ser alcançado devido a 2 razões: 1) ele precisa da memória da imagem completa, embora você provavelmente esteja usando apenas uma imagem reduzida 2) você precisa carregar a imagem completa mais lenta - use o carregamento com inSampleSize e BitmapFactory.decodeResource (), que é uma solução muito superior a isso.
Patrick Favre
303

Para futuros Googlers, aqui está um algoritmo que eu portado do Quasimondo. É uma espécie de mistura entre um desfoque de caixa e um desfoque gaussiano, é muito bonito e muito rápido também.

Atualização para pessoas que encontram o problema ArrayIndexOutOfBoundsException: @anthonycr nos comentários fornece essas informações:

Eu descobri que, substituindo Math.abs por StrictMath.abs ou alguma outra implementação abs, a falha não ocorre.

/**
 * 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
fonte
4
Obrigado Yahel. Você resolveu meu problema. Obrigado novamente.
precisa
1
o que devo passar como raio?
KrisDrOid
16
Para um raio maior que 1, às vezes você obtém ArrayIndexOutOfBoundsException. Vou tentar identificar o problema.
quer
7
@MichaelLiberman Eu também encontrei o mesmo problema. "g [yi] = dv [gsum];" -> erro: java.lang.ArrayIndexOutOfBoundsException: length = 112896; index = 114021
ver2851 28/09
2
Encontrei o ArrayIndexOutOfBoundsException conhecido e, após algumas análises, acredito que ele é causado por uma otimização incorreta pela Dalvik VM. No forcircuito imediatamente antes do mau dereference, quer o cálculo da rbsvariável ou o cálculo dos gsum, rsumou bsumvariáveis não estão sendo bem feito. Eu descobri que, substituindo Math.abspor StrictMath.absou alguma outra absimplementação, a falha não ocorre. Como StrictMath.absele próprio delega Math.abs, parece que deve ser uma otimização ruim.
Anthonycr
255

Android Blur Guide 2016

com aplicativo Showcase / Benchmark e Origem no Github . Verifique também a estrutura do Blur em que estou trabalhando atualmente: Dali .

Depois de experimentar muito, agora posso dar com segurança algumas recomendações sólidas que facilitarão sua vida no Android ao usar o Android Framework.

Carregar e usar um bitmap reduzido (para imagens muito embaçadas)

Nunca use o tamanho completo de um bitmap. Quanto maior a imagem, mais precisa ser desfocada e também maior o raio de desfoque e, geralmente, quanto maior o raio de desfoque, mais tempo o algoritmo demora.

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

Isso carregará o bitmap com inSampleSize 8, portanto, apenas 1/64 da imagem original. Teste o que se inSampleSizeadequa às suas necessidades, mas mantenha 2 ^ n (2,4,8, ...) para evitar a degradação da qualidade devido ao dimensionamento. Consulte o documento do Google para obter mais

Outra grande vantagem é que o carregamento de bitmap será muito rápido. Nos meus primeiros testes de desfoque, percebi que o maior tempo durante todo o processo de desfoque era o carregamento da imagem. Portanto, para carregar uma imagem de 1920x1080 do disco, meu Nexus 5 precisava de 500ms, enquanto o desfoque levava apenas mais 250 ms.

Usar Renderscript

O Renderscript fornece ScriptIntrinsicBlurum filtro de desfoque gaussiano. Tem boa qualidade visual e é apenas o mais rápido que você consegue realisticamente no Android. O Google afirma ser "tipicamente 2-3 vezes mais rápido que uma implementação C com vários threads e, muitas vezes, 10 vezes mais rápido que uma implementação Java" . O Renderscript é realmente sofisticado (usando o dispositivo de processamento mais rápido (GPU, ISP, etc.), etc.) e também existe a biblioteca de suporte da v8, tornando-o compatível até 2.2 . Bem, pelo menos em teoria, através dos meus próprios testes e relatórios de outros desenvolvedores, parece que não é possível usar o Renderscript às cegas, pois a fragmentação de hardware / driver parece causar problemas em alguns dispositivos, mesmo com sdk lvl mais alto (por exemplo, problemas com o 4.1 Nexus S), tenha cuidado e teste em vários dispositivos. Aqui está um exemplo simples para você começar:

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

Ao usar o suporte da v8 com o Gradle, recomendado especificamente pelo Google "porque incluem as melhorias mais recentes" , você só precisa adicionar duas linhas ao seu script de construção e usá -lasandroid.support.v8.renderscript com as ferramentas de construção atuais ( sintaxe atualizada para o plugin Gradle para Android v14 + )

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

Referência simples em um Nexus 5 - comparando o RenderScript com outras implementações java e Renderscript:

O tempo médio de execução por desfoque em diferentes tamanhos de foto O tempo médio de execução por desfoque em diferentes tamanhos de foto

Megapixels por segundo que podem ser desfocados Megapixels por segundo que podem ser desfocados

Cada valor é a média de 250 rodadas. RS_GAUSS_FASTé ScriptIntrinsicBlur(e quase sempre o mais rápido), outros que iniciam RS_geralmente envolvem implementações com kernels simples. Os detalhes dos algoritmos podem ser encontrados aqui . Isso não é totalmente desfocado, pois uma boa parte é a coleta de lixo que é medida. Isso pode ser visto aqui ( ScriptIntrinsicBlurem uma imagem 100x100 com cerca de 500 rodadas)

insira a descrição da imagem aqui

Os picos são gc.

Você pode verificar por si mesmo, o aplicativo de benchmark está na playstore: BlurBenchmark

Reutiliza o bitmap sempre que possível (se for prio: performance> memory footprint)

Se você precisar de vários borrões para um desfoque ao vivo ou semelhante e sua memória permitir, ele não carrega o bitmap de drawables várias vezes, mas o mantém "em cache" em uma variável de membro. Nesse caso, tente sempre usar as mesmas variáveis, para manter a coleta de lixo no mínimo.

Verifique também a nova inBitmapopção ao carregar de um arquivo ou desenho que reutilize a memória de bitmap e economize tempo de coleta de lixo.

Para misturar de nítido a desfocado

O método simples e ingênuo é apenas usar 2 ImageViews, um desfocado, e o alfa desbotá-los. Mas se você quer uma aparência mais sofisticada que desbota suavemente de nítida a desfocada, confira o post de Roman Nurik sobre como fazê-lo como em seu aplicativo Muzei .

Basicamente, ele explica que pré-desfoca alguns quadros com diferentes extensões de desfoque e os usa como quadros-chave em uma animação que parece realmente suave.

Diagrama em que Nurik explica sua abordagem

Patrick Favre
fonte
1
Primeiro de tudo, obrigado pelo seu trabalho duro! Mas eu tenho uma pergunta: "porque ele usa o modo USAGE_SHARED que reutiliza a memória". Onde você encontrou a constante USAGE_SHARED? Não encontrei em lugar algum.
Algum estudante Noob
2
Eu encontrei, USAGE_SHARED só está disponível no support.v8.renderscript
Some Noob Student
2
O desfoque gaussiano rápido na renderização falha com erros de alocação de memória C em dispositivos de gama baixa. Testado no ZTE Z992 (Android 4.1.1) e Kyocera Rise (Android 4.0.4) usando o aplicativo Play Store fornecido. Também houve um relatório de falha no Samsung Galaxy S3 mini. Como os erros ocorrem na camada C, eles não podem ser capturados como exceções em Java, o que significa que uma falha no aplicativo é inevitável. Parece que o RenderScript pode não estar pronto para uso em produção.
Theo
4
para versões mais recentes do gradle, use renderscriptSupportModeEnabled trueou ele não será construído! Eu procurei para sempre!
seb
3
Quando tentei esta solução, em vez de obter um bitmap desfocado, recebi um bitmap colorido do arco-íris . Alguém mais teve esse problema? Se sim, como você corrigiu isso?
HaloMediaz 25/07
53

EDIT (abril de 2014): esta é uma página de perguntas / respostas que ainda recebe muitos acessos ao que parece. Eu sei que estou sempre recebendo votos para este post. Mas se você estiver lendo isso, precisará entender que as respostas postadas aqui (as minhas e as aceitas) estão desatualizadas. Se você deseja implementar um desfoque eficiente hoje , use o RenderScript em vez do NDK ou Java. O RenderScript é executado no Android 2.2+ (usando a Biblioteca de suporte do Android ), portanto, não há motivo para não usá-lo.

A resposta antiga segue, mas tenha cuidado, pois está desatualizada.


Para futuros Googlers², aqui está um algoritmo que eu portado do porto de Yahel do algoritmo de Quasimondo, mas usando o NDK. É baseado na resposta de Yahel, é claro. Mas isso está executando o código C nativo, por isso é mais rápido. Muito mais rapido. Tipo, 40 vezes mais rápido.

Acho que usar o NDK é como toda manipulação de imagem deve ser feita no Android ... é um pouco chato de implementar no início (leia um ótimo tutorial sobre como usar o JNI e o NDK aqui ), mas muito melhor e quase em tempo real para um muitas coisas.

Para referência, usando a função Java de Yahel, levou 10 segundos para desfocar minha imagem de 480x532 pixels com um raio de desfoque de 10. Mas levou 250ms usando a versão C nativa. E tenho certeza de que ainda pode ser otimizado ainda mais ... Acabei de fazer uma conversão idiota do código java, provavelmente há algumas manipulações que podem ser reduzidas, que não queriam gastar muito tempo refatorando a coisa toda.

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

Em seguida, use-o assim (considerando uma classe chamada com.insert.your.package.ClassName e uma função nativa chamada functionToBlur, conforme o código acima afirma):

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

Ele espera um bitmap RGB_8888!

Para usar um bitmap RGB_565, crie uma cópia convertida antes de passar o parâmetro (eca) ou altere a função para usar um novo rgb565tipo em vez de rgba:

typedef struct {
    uint16_t byte0;
} rgb565;

O problema é que se você fizer isso você não pode ler .red, .greene .bluedo pixel mais, você precisa ler o byte corretamente, duh. Quando eu precisava disso antes, fiz o seguinte:

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

Mas provavelmente há uma maneira menos idiota de fazer isso. Receio que não sou muito de codificador C de baixo nível.

zeh
fonte
Obrigado, isso me ajudou muito :)
Dmitry Zaytsev
18
MAS requer muita memória. Para reduzir a memória do tipo mudança consumo de r[wh], g[wh]e b[wh]para uint8_t.
Dmitry Zaytsev
você pode me mostrar o que sua aparência de arquivo Android.mk gosta, empastebin.com
CQM
1
Para um exemplo Renderscript de trabalho de um Gaussian Blur no SDK Android 17+ olhada aqui: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini
2
Renderscript também está disponível como parte do lib suporte para Android 2.2 +, então não há nenhuma razão para não usá-lo em todos os lugares mais: android-developers.blogspot.com/2013/09/...
zeh
14

Este código é um trabalho perfeito para mim

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
fonte
Maneira mais simples de desfocar a imagem (y)
VAdaihiep 21/11/2015
12

Agora você pode usar ScriptIntrinsicBlur da biblioteca Renderscript para desfocar rapidamente. Aqui está como acessar a API do RenderScript. A seguir, é uma classe que fiz para desfocar Views e 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
fonte
2
O contexto Renderscript não deve ser criado no método blur, mas gerenciado estaticamente ou fornecido ao método. (se você mente performance)
Patrick Favre
1
Você pode dar um exemplo disso é usar? Quando eu tento usá-lo eu recebo o seguinte erro: java.lang.IllegalArgumentException: largura e altura deve ser> 0
Donal Rafferty
10

Isso funcionou bem para mim: Como desfocar imagens com eficiência com o RenderScript do 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
fonte
Isso seria ainda melhor / mais rápido se você armazenasse em cache o objeto RenderScript que você criou. A instanciação de uma nova sempre que você deseja desfocar uma imagem adiciona uma sobrecarga desnecessária (criação / destruição de objetos Java).
Stephen Hines
4

Isso é para todas as pessoas que precisam aumentar o raio de ScriptIntrinsicBlur para obter um desfoque gaussiano mais difícil.

Em vez de colocar o raio acima de 25, você pode reduzir a imagem e obter o mesmo resultado. Eu escrevi uma classe chamadaGaussianBlur . Abaixo, você pode ver como usar e toda a implementação da classe.

Uso:

GaussianBlur gaussian = new GaussianBlur(context);
gaussian.setMaxImageSize(60);
gaussian.setRadius(25); //max

Bitmap output = gaussian.render(<your bitmap>,true);
Drawable d = new BitmapDrawable(getResources(),output);

Classe:

 public class GaussianBlur {
    private final int DEFAULT_RADIUS = 25;
    private final float DEFAULT_MAX_IMAGE_SIZE = 400;

    private Context context;
    private int radius;
    private float maxImageSize;

    public GaussianBlur(Context context) {
    this.context = context;
    setRadius(DEFAULT_RADIUS);
    setMaxImageSize(DEFAULT_MAX_IMAGE_SIZE);
    } 

    public Bitmap render(Bitmap bitmap, boolean scaleDown) {
    RenderScript rs = RenderScript.create(context);

    if (scaleDown) {
        bitmap = scaleDown(bitmap);
    }

    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);

    Allocation inAlloc = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE);
    Allocation outAlloc = Allocation.createFromBitmap(rs, output);

    ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, inAlloc.getElement()); // Element.U8_4(rs));
    script.setRadius(getRadius());
    script.setInput(inAlloc);
    script.forEach(outAlloc);
    outAlloc.copyTo(output);

    rs.destroy();

    return output;
}

public Bitmap scaleDown(Bitmap input) {
    float ratio = Math.min((float) getMaxImageSize() / input.getWidth(), (float) getMaxImageSize() / input.getHeight());
    int width = Math.round((float) ratio * input.getWidth());
    int height = Math.round((float) ratio * input.getHeight());

    return Bitmap.createScaledBitmap(input, width, height, true);
}

public int getRadius() {
    return radius;
}

public void setRadius(int radius) {
    this.radius = radius;
}

public float getMaxImageSize() {
    return maxImageSize;
}

public void setMaxImageSize(float maxImageSize) {
    this.maxImageSize = maxImageSize;
}
    }
Vansuita Jr.
fonte
não, se você diminuir a escala da imagem para aumentar mais tarde, recuperará uma imagem em bloco em vez de uma imagem manchada :(
loki
4

Obrigado @Yahel pelo código. Postando o mesmo método com suporte a desfoque de canal alfa , demorei algum tempo para fazê-lo funcionar corretamente, para que ele economizasse o tempo de alguém:

/**
 * 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);
}
vírus
fonte
ainda com o 'índice de fora de alcance' em dispositivos> hdpi como a fonte original
Gabriel Ferreira
4

Eu usei isso 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
fonte
2

Para futuros Googlers que escolherem a abordagem NDK - eu acho o algoritmo stackblur mencionado confiável. Encontrei a implementação C ++ que não depende do SSE aqui - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32, que contém algumas otimizações usando tabelas 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
}; 

Eu fiz a modificação do algoritmo stackblur para sistemas com vários núcleos - ele pode ser encontrado aqui http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ Como cada vez mais dispositivos têm 4 núcleos, as otimizações permitem Benefício de velocidade 4x.

Victor Laskin
fonte
1

Nicolas POMEPUY conselho. Acho que este link será útil: Efeito de desfoque para o design do Android

Exemplo de projeto no 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
fonte
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada.
Amal Murali
1
Amal Murali, você está certo. Agora mude minha postagem. Bom, você não apenas fez voto negativo, mas também comentou.
Yura Shinkarev
1

Tentamos implementar o desfoque do RenderScript como mencionado acima em respostas diferentes. Estávamos limitados a usar a versão v8 do RenderScript e isso nos causou muitos problemas.

  • O Samsung S3 travava aleatoriamente sempre que tentávamos usar o renderscript
  • Outros dispositivos (em diferentes APIs) mostraram aleatoriamente problemas de cores diferentes

Quero compartilhar nossa versão suja apenas de Java, que é lenta e deve ser feita em um encadeamento separado e, se possível, antes do uso e, portanto, persistiu.

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 solução está longe de ser perfeita, mas cria um efeito de desfoque razoável com base no fato de que ela desenha uma versão altamente transparente da mesma imagem em cima de uma versão "nítida" quase transparente. O alfa depende da distância da origem.

Você pode ajustar alguns "números mágicos" às suas necessidades. Eu só queria compartilhar essa "solução" para todos que têm problemas com a versão de suporte de versão v8 do RenderScript.

WarrenFaith
fonte
Também estou tendo um problema com o RenderScript em alguns dispositivos antigos, mas sua solução consome muito memória.
AsafK
0

Descobri que diminuir o contraste, o brilho e a saturação torna um pouco as imagens borradas mais bonitas, então eu combinei vários métodos de estouro de pilha e criei esta Classe de desfoque que lida com imagens borradas, alteração de brilho, saturação, contraste e tamanho das imagens borradas. Também pode converter imagens de drawable em bitmap e vice-versa.

Ankit Gaur
fonte
0

No i / o 2019, a seguinte solução foi apresentada:

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