Schnelle Bitmap-Unschärfe für Android SDK

185

Derzeit durchlaufe ich in einer Android-Anwendung, die ich entwickle, die Pixel eines Bildes, um es zu verwischen. Dies dauert bei einem 640 x 480-Bild etwa 30 Sekunden.

Beim Durchsuchen von Apps im Android Market bin ich auf eine gestoßen, die eine Unschärfefunktion enthält und deren Unschärfe sehr schnell ist (z. B. 5 Sekunden), sodass sie eine andere Methode zum Verwischen verwenden müssen.

Kennt jemand einen schnelleren Weg als das Durchlaufen der Pixel?

Greg
quelle
2
Leider werden die Bilder immer unterschiedlich sein, so dass ich nicht im Voraus eine unscharfe Version erstellen kann. Außerdem werde ich die Unschärfeintensität auch nicht im Voraus kennen.
Greg
Könnten Sie Ihren Code posten, ist es vielleicht der Algorithmus / Code, der ineffizient ist, 30 Sekunden, um ein 640x480-Bild zu durchlaufen, sind langsam, ich hätte gedacht, 5 Sekunden wären langsam, aber dann hängt es wieder vom Prozessor ab.
Vickirk

Antworten:

78

Dies ist eine Aufnahme im Dunkeln, aber Sie können versuchen, das Bild zu verkleinern und dann erneut zu vergrößern. Dies kann mit gemacht werden Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Stellen Sie sicher und setzen Sie den Filterparameter auf true. Es wird in nativem Code ausgeführt, sodass es möglicherweise schneller ist.

Luke
quelle
5
Nach einigen Tests und der Unschärfe, die ich mache, funktioniert das tatsächlich gut genug für mich und es ist schnell. Vielen Dank!
Greg
4
Wenn es gut funktioniert. Es ist eine Schande, dass wir nie auf den Grund gegangen sind, warum es ineffizient war.
Vickirk
Möglicherweise möchten Sie createScaledBitmap ausprobieren und das Bild in der gleichen Größe belassen. Es verwischt es für mich :-(
Casebash
1
Hier ist eine Diskussion über die Bedeutung des Arguments "Filter": stackoverflow.com/questions/2895065
user1364368
4
Dies ist aus zwei Gründen nicht genau der richtige Weg: 1) Es benötigt den Speicher des Vollbilds, obwohl Sie wahrscheinlich nur ein verkleinertes Bild verwenden. 2) Sie müssen das Vollbild laden, das langsamer ist. Verwenden Sie das Laden mit inSampleSize und BitmapFactory.decodeResource (), eine weitaus bessere Lösung.
Patrick Favre
300

Für zukünftige Googler ist hier ein Algorithmus, den ich von Quasimondo portiert habe. Es ist eine Art Mischung zwischen einer Box-Unschärfe und einer Gaußschen Unschärfe, es ist sehr hübsch und auch ziemlich schnell.

Das Update für Personen, bei denen das ArrayIndexOutOfBoundsException-Problem auftritt: @anthonycr in den Kommentaren enthält folgende Informationen:

Ich habe festgestellt, dass durch das Ersetzen von Math.abs durch StrictMath.abs oder eine andere abs-Implementierung der Absturz nicht auftritt.

/**
 * 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
quelle
4
Danke Yahel. Du hast mein Problem gelöst. Danke noch einmal.
Yog Guru
1
Was soll ich als Radius passieren?
krisDrOid
16
Bei einem Radius größer als 1 erhalten Sie manchmal die ArrayIndexOutOfBoundsException. Ich werde versuchen, das Problem zu identifizieren.
MikeL
7
@ MichaelLiberman Ich bin auch auf das gleiche Problem gestoßen. Warum schon herausgefunden? g [yi] = dv [gsum]; -> Fehler: java.lang.ArrayIndexOutOfBoundsException: length = 112896; index = 114021
see2851
2
Ist auf die bekannte ArrayIndexOutOfBoundsException gestoßen, und nach einigen Analysen glaube ich, dass sie durch eine falsche Optimierung durch die Dalvik-VM verursacht wird. In der forSchleife unmittelbar vor dem schlechten dereferenzieren, entweder die Berechnung der rbsvariablen oder die Berechnung der gsum, rsumoder bsumVariablen wird nicht richtig gemacht. Ich habe festgestellt, dass durch Ersetzen Math.absdurch StrictMath.absoder eine andere absImplementierung der Absturz nicht auftritt. Da sich StrictMath.absselbst delegiert Math.abs, scheint es eine schlechte Optimierung zu sein.
Anthonycr
255

Android Blur Guide 2016

mit Showcase / Benchmark App und Source auf Github . Schauen Sie sich auch das Blur-Framework an, an dem ich gerade arbeite: Dali .

Nachdem ich viel experimentiert habe, kann ich Ihnen jetzt sicher einige solide Empfehlungen geben, die Ihnen das Leben in Android erleichtern, wenn Sie das Android Framework verwenden.

Laden und Verwenden einer verkleinerten Bitmap (für sehr verschwommene Bilder)

Verwenden Sie niemals eine Bitmap in voller Größe. Je größer das Bild ist, desto mehr muss verwischt werden und desto höher muss der Unschärferadius sein. Je länger der Unschärferadius ist, desto länger dauert der Algorithmus.

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

Dadurch wird die Bitmap mit inSampleSize8 geladen , also nur 1/64 des Originalbilds. Testen Sie, was inSampleSizeIhren Anforderungen entspricht, behalten Sie es jedoch bei 2 ^ n (2,4,8, ...), um eine Qualitätsminderung aufgrund von Skalierung zu vermeiden. Weitere Informationen finden Sie in Google Doc

Ein weiterer großer Vorteil ist, dass das Laden von Bitmaps sehr schnell erfolgt. Bei meinen frühen Unschärfetests stellte ich fest, dass die längste Zeit während des gesamten Unschärfeprozesses das Laden von Bildern war. Um ein 1920x1080-Image von der Festplatte zu laden, benötigte mein Nexus 5 500 ms, während die Unschärfe nur noch etwa 250 ms dauerte.

Verwenden Sie Renderscript

Renderscript bietet ScriptIntrinsicBlureinen Gaußschen Unschärfefilter. Es hat eine gute visuelle Qualität und ist nur die schnellste, die Sie realistisch auf Android bekommen. Google behauptet, "normalerweise 2-3x schneller als eine Multithread-C-Implementierung und oft 10x + schneller als eine Java-Implementierung zu sein" . Renderscript ist sehr ausgefeilt (unter Verwendung des schnellsten Verarbeitungsgeräts (GPU, ISP usw.) usw.) und es gibt auch die v8-Unterstützungsbibliothek, die es bis auf 2.2 kompatibel macht. Nun, zumindest theoretisch scheint es durch meine eigenen Tests und Berichte von anderen Entwicklern nicht möglich zu sein, Renderscript blind zu verwenden, da die Hardware- / Treiberfragmentierung bei einigen Geräten Probleme zu verursachen scheint, selbst bei höheren SDK-Levels (z. B. bei mir) Probleme mit dem 4.1 Nexus S) Seien Sie also vorsichtig und testen Sie auf vielen Geräten. Hier ist ein einfaches Beispiel, mit dem Sie beginnen können:

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

Wenn Sie die v8-Unterstützung mit Gradle verwenden, die von Google ausdrücklich empfohlen wird, "weil sie die neuesten Verbesserungen enthält" , müssen Sie Ihrem Build-Skript nur zwei Zeilen hinzufügen und android.support.v8.renderscriptmit den aktuellen Build-Tools verwenden ( aktualisierte Syntax für das Android Gradle-Plugin v14 + ).

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

Einfacher Benchmark auf einem Nexus 5 - Vergleich von RenderScript mit verschiedenen anderen Java- und Renderscript-Implementierungen:

Die durchschnittliche Laufzeit pro Unschärfe bei verschiedenen Bildgrößen Die durchschnittliche Laufzeit pro Unschärfe bei verschiedenen Bildgrößen

Megapixel pro Sekunde, die unscharf werden können Megapixel pro Sekunde, die unscharf werden können

Jeder Wert ist der Durchschnitt von 250 Runden. RS_GAUSS_FASTist ScriptIntrinsicBlur(und fast immer die schnellste), andere, die damit beginnen, RS_sind meistens Convolve-Implementierungen mit einfachen Kerneln. Die Details der Algorithmen finden Sie hier . Dies ist keine reine Unschärfe, da ein guter Teil die gemessene Speicherbereinigung ist. Dies ist hier zu sehen ( ScriptIntrinsicBlurauf einem 100x100-Bild mit ca. 500 Runden)

Geben Sie hier die Bildbeschreibung ein

Die Spikes sind gc.

Sie können selbst überprüfen, ob die Benchmark-App im Playstore verfügbar ist: BlurBenchmark

Verwendet Bitmap wo immer möglich (wenn prio: Leistung> Speicherbedarf)

Wenn Sie mehrere Unschärfen für eine Live-Unschärfe oder ähnliches benötigen und Ihr Speicher dies zulässt, wird die Bitmap nicht mehrmals aus Drawables geladen, sondern in einer Elementvariablen "zwischengespeichert". Versuchen Sie in diesem Fall immer, dieselben Variablen zu verwenden, um die Speicherbereinigung auf ein Minimum zu beschränken.

Überprüfen Sie auch die neue inBitmapOption beim Laden aus einer Datei oder einem Drawable, die den Bitmap-Speicher wiederverwendet und Zeit für die Speicherbereinigung spart.

Zum Mischen von scharf bis verschwommen

Die einfache und naive Methode besteht darin, nur 2 ImageViews, eine unscharfe und Alpha-Fade zu verwenden. Wenn Sie jedoch einen raffinierteren Look wünschen, der sanft von scharf zu verschwommen übergeht, lesen Sie den Beitrag von Roman Nurik in seiner Muzei-App .

Grundsätzlich erklärt er, dass er einige Frames mit unterschiedlichen Unschärfebereichen vorab verwischt und sie als Keyframes in einer Animation verwendet, die wirklich flüssig aussieht.

Diagramm, in dem Nurik seinen Ansatz erklärt

Patrick Favre
quelle
1
Zunächst einmal vielen Dank für Ihre harte Arbeit! Aber ich habe eine Frage: "Weil es den USAGE_SHARED-Modus verwendet, der den Speicher wiederverwendet". Wo haben Sie die Konstante USAGE_SHARED gefunden? Ich konnte es nirgendwo finden.
Einige Noob Student
2
Ich fand es, USAGE_SHARED ist nur in support.v8.renderscript verfügbar
Some Noob Student
2
Renderscript schnelle Gaußsche Unschärfe schlägt mit C-Speicherzuordnungsfehlern auf Low-End-Geräten fehl. Getestet auf ZTE Z992 (Android 4.1.1) und Kyocera Rise (Android 4.0.4) mit der mitgelieferten Play Store App. Hatte auch einen Fehlerbericht auf Samsung Galaxy S3 mini. Da Fehler in der C-Schicht auftreten, können sie in Java nicht als Ausnahmen abgefangen werden, sodass ein App-Absturz unvermeidbar ist. RenderScript ist möglicherweise nicht für die Produktion bereit.
Theo
4
Für neuere Gradle-Versionen verwenden renderscriptSupportModeEnabled trueoder es wird nicht gebaut! Ich habe immer gesucht!
17.
3
Als ich diese Lösung ausprobierte, bekam ich keine regenbogenfarbene Bitmap, anstatt eine unscharfe Bitmap zu erhalten . Hat noch jemand dieses Problem? Wenn ja, wie haben Sie das Problem behoben?
HaloMediaz
53

EDIT (April 2014): Dies ist eine Frage- / Antwortseite, die anscheinend immer noch viele Treffer erhält. Ich weiß, dass ich für diesen Beitrag immer positive Stimmen bekomme. Wenn Sie dies lesen, müssen Sie jedoch feststellen, dass die hier veröffentlichten Antworten (sowohl meine als auch die akzeptierte Antwort) veraltet sind. Wenn Sie heute eine effiziente Unschärfe implementieren möchten , sollten Sie RenderScript anstelle von NDK oder Java verwenden. RenderScript läuft unter Android 2.2+ (unter Verwendung der Android Support Library ), daher gibt es keinen Grund, es nicht zu verwenden.

Die alte Antwort folgt, aber Vorsicht, sie ist veraltet.


Für zukünftige²-Googler ist hier ein Algorithmus, den ich von Yahels Port von Quasimondos Algorithmus portiert habe, aber mit dem NDK. Es basiert natürlich auf Yahels Antwort. Da hier jedoch nativer C-Code ausgeführt wird, ist er schneller. Viel schneller. Wie 40 mal schneller.

Ich finde , dass das NDK verwendet , ist wie alle Bildmanipulation sollte auf Android getan werden ... es etwas ärgerlich ist zunächst zu implementieren (ein großes Tutorial lesen JNI über die Verwendung und die NDK hier ), aber viel besser, und nahezu in Echtzeit für eine viele Dinge.

Als Referenz dauerte es mit der Java-Funktion von Yahel 10 Sekunden, um mein Bild mit 480 x 532 Pixel und einem Unschärferadius von 10 zu verwischen. Mit der nativen C-Version dauerte es jedoch 250 ms. Und ich bin mir ziemlich sicher, dass es noch weiter optimiert werden kann ... Ich habe gerade eine dumme Konvertierung des Java-Codes durchgeführt, es gibt wahrscheinlich einige Manipulationen, die verkürzt werden können, ich wollte nicht zu viel Zeit damit verbringen, das Ganze umzugestalten.

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

Verwenden Sie es dann folgendermaßen (unter Berücksichtigung einer Klasse namens com.insert.your.package.ClassName und einer nativen Funktion namens functionToBlur, wie im obigen Code angegeben):

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

Es wird eine RGB_8888-Bitmap erwartet!

Um eine RGB_565-Bitmap zu verwenden, erstellen Sie entweder eine konvertierte Kopie, bevor Sie den Parameter übergeben (yuck), oder ändern Sie die Funktion, um einen neuen rgb565Typ anstelle von rgba:

typedef struct {
    uint16_t byte0;
} rgb565;

Das Problem ist, dass Sie, wenn Sie dies tun, nicht mehr lesen .redkönnen .greenund .bluevon dem Pixel das Byte richtig lesen müssen, duh. Als ich das vorher brauchte, tat ich Folgendes:

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

Aber es gibt wahrscheinlich einen weniger dummen Weg, dies zu tun. Ich fürchte, ich bin kein großer C-Codierer.

zeh
quelle
Danke, es hat mir sehr geholfen :)
Dmitry Zaytsev
18
ABER es erfordert viel Speicher. Um den Speicherverbrauch ändert Art von zu verringern r[wh], g[wh]und b[wh]zu uint8_t.
Dmitry Zaytsev
Können Sie mir zeigen, wie Ihre Android.mk-Datei aussieht, ampastebin.com
CQM
1
Ein funktionierendes RenderScript-Beispiel für eine Gaußsche Unschärfe unter Android SDK 17+ finden Sie hier: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini
2
RenderScript ist auch als Teil der Support-Bibliothek für Android 2.2+ verfügbar, daher gibt es keinen Grund, es nicht mehr überall zu verwenden: android-developers.blogspot.com/2013/09/…
zeh
14

Dieser Code ist perfekt für mich

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
quelle
Einfachster Weg, um Bild (y) zu verwischen
VAdaihiep
12

Sie können jetzt ScriptIntrinsicBlur aus der RenderScript-Bibliothek verwenden, um schnell zu verwischen. Hier erfahren Sie, wie Sie auf die RenderScript-API zugreifen. Das Folgende ist eine Klasse, die ich erstellt habe, um Ansichten und Bitmaps zu verwischen:

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
quelle
2
Der Renderscript-Kontext sollte nicht in der Unschärfemethode erstellt, sondern statisch verwaltet oder der Methode übergeben werden. (wenn Sie Leistung
Patrick Favre
1
Können Sie ein Beispiel dafür geben, ist Verwendung? Wenn ich versuche, es zu verwenden, erhalte ich die folgende Fehlermeldung: java.lang.IllegalArgumentException: Breite und Höhe müssen> 0 sein
Donal Rafferty
10

Für mich hat das gut funktioniert: So verwischen Sie Bilder effizient mit RenderScript von 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
quelle
Dies wäre sogar noch besser / schneller, wenn Sie das von Ihnen erstellte RenderScript-Objekt zwischengespeichert hätten. Das Instanziieren eines neuen Bilds jedes Mal, wenn Sie ein Bild verwischen möchten, verursacht nur unnötigen Overhead (Erstellung / Zerstörung von Java-Objekten).
Stephen Hines
4

Dies ist für alle Personen, die den Radius von vergrößern müssen ScriptIntrinsicBlur , um eine härtere Gaußsche Unschärfe zu erzielen.

Anstatt den Radius auf mehr als 25 zu setzen, können Sie das Bild verkleinern und das gleiche Ergebnis erzielen. Ich habe eine Klasse namens geschriebenGaussianBlur . Unten sehen Sie die Verwendung und die gesamte Klassenimplementierung.

Verwendung:

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

Klasse:

 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.
quelle
Nein, wenn Sie das Bild verkleinern, um es später zu vergrößern, erhalten Sie ein blockartiges Bild anstelle eines unscharfen Bildes :(
loki
4

Danke @Yahel für den Code. Das Posten der gleichen Methode mit Unterstützung für das Verwischen von Alphakanälen, für die ich einige Zeit gebraucht habe, damit sie ordnungsgemäß funktioniert, sodass möglicherweise Zeit gespart wird:

/**
 * 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
quelle
immer noch mit 'Index außerhalb des Bereichs' in Geräten> HDPI als Originalquelle
Gabriel Ferreira
4

Ich habe das schon mal benutzt ..

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

Für zukünftige Googler, die sich für den NDK-Ansatz entscheiden, finde ich den verlässlich genannten Stackblur-Algorithmus. Ich habe hier eine C ++ - Implementierung gefunden, die nicht auf SSE basiert - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32, die einige Optimierungen unter Verwendung statischer Tabellen enthält, wie:

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

Ich habe den Stackblur-Algorithmus für Multi-Core-Systeme modifiziert - hier finden Sie http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ Da immer mehr Geräte 4 Kerne haben - Optimierungen ergeben 4x Geschwindigkeitsvorteil.

Victor Laskin
quelle
1

Nicolas POMEPUY Rat. Ich denke, dieser Link wird hilfreich sein: Unschärfeeffekt für Android-Design

Beispielprojekt bei 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
quelle
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Amal Murali
1
Amal Murali, du hast recht. Jetzt ändere meinen Beitrag. Gut, dass Sie nicht nur abstimmen, sondern auch kommentieren.
Yura Shinkarev
1

Wir haben versucht, RenderScript-Unschärfe wie oben erwähnt in verschiedenen Antworten zu implementieren. Wir konnten nur die v8-RenderScript-Version verwenden, was uns große Probleme bereitete.

  • Samsung S3 stürzte zufällig ab, wenn wir versuchten, das Renderscript zu verwenden
  • Andere Geräte (über verschiedene APIs hinweg) zeigten zufällig unterschiedliche Farbprobleme

Ich möchte unsere schmutzige Nur-Java-Version teilen, die langsam ist und in einem separaten Thread und, wenn möglich, vor der Verwendung ausgeführt werden sollte und daher fortbesteht.

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

Diese Lösung ist alles andere als perfekt, erzeugt jedoch einen vernünftigen Unschärfeeffekt, der auf der Tatsache beruht, dass eine hochtransparente Version desselben Bildes auf eine kaum transparente "scharfe" Version gezeichnet wird. Das Alpha hängt von der Entfernung zum Ursprung ab.

Sie können einige "magische Zahlen" an Ihre Bedürfnisse anpassen. Ich wollte diese "Lösung" nur für alle freigeben, die Probleme mit der v8-Supportversion von RenderScript haben.

WarrenFaith
quelle
Ich habe auch ein Problem mit RenderScript auf einigen alten Armeabi-Geräten, aber Ihre Lösung ist zu speicherintensiv.
AsafK
0

Wenn Sie weiterhin Probleme mit der Renderscript-Unterstützungsbibliothek auf x86-Chipsätzen haben, lesen Sie bitte diesen Beitrag des Erstellers der Bibliothek. Es sieht so aus, als hätte das von ihm vorbereitete Update es nicht irgendwie in die Build Tools v20.0.0 geschafft, daher stellt er die Dateien zur Verfügung, um es manuell zu reparieren, und eine kurze Erklärung, wie es geht.

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

Ich fand heraus, dass ein wenig abnehmender Kontrast, Helligkeit und Sättigung unscharfe Bilder hübscher machen. Deshalb kombinierte ich verschiedene Methoden aus dem Stapelüberlauf und erstellte diese Unschärfeklasse, die sich mit unscharfen Bildern, sich ändernder Helligkeit, Sättigung, Kontrast und Größe der unscharfen Bilder befasst. Es kann auch Bilder von zeichnbar in Bitmap konvertieren und umgekehrt.

Ankit Gaur
quelle
0

Am i / o 2019 wurde folgende Lösung vorgestellt:

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