Blur Bitmap Cepat Untuk Android SDK

185

Saat ini dalam aplikasi Android yang saya kembangkan, saya mengulang-ulang piksel gambar untuk mengaburkannya. Ini membutuhkan waktu sekitar 30 detik pada gambar 640x480.

Saat menelusuri aplikasi di Android Market, saya menemukan satu yang menyertakan fitur blur dan blur mereka sangat cepat (seperti 5 detik) sehingga mereka harus menggunakan metode blur yang berbeda.

Adakah yang tahu cara yang lebih cepat selain mengulang melalui piksel?

Greg
sumber
2
Sayangnya gambar akan selalu berbeda sehingga saya tidak akan dapat membuat versi buram sebelumnya. Ditambah lagi, saya tidak akan tahu intensitas kabur sebelumnya.
Greg
Bisakah Anda memposting kode Anda, mungkin itu adalah algoritma / kode yang tidak efisien, 30 detik untuk melalui gambar 640x480 lambat, saya pikir 5 detik lambat tapi sekali lagi tergantung pada prosesor.
vickirk

Jawaban:

78

Ini adalah bidikan dalam gelap, tetapi Anda dapat mencoba mengecilkan gambar dan kemudian memperbesarnya lagi. Ini bisa dilakukan dengan Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Pastikan dan atur parameter filter ke true. Ini akan berjalan dalam kode asli sehingga mungkin lebih cepat.

Luke
sumber
5
Setelah beberapa pengujian dan pengaburan yang saya lakukan ini sebenarnya bekerja cukup baik untuk saya dan cepat. Terima kasih!
Greg
4
Jika berhasil baik. Sayang sekali kami tidak pernah sampai ke dasar mengapa itu tidak efisien.
vickirk
Anda mungkin ingin mencoba createScaledBitmap dan membiarkan gambar dengan ukuran yang sama. Ini kabur bagi saya :-(
Casebash
1
Berikut ini adalah diskusi tentang makna argumen "filter": stackoverflow.com/questions/2895065
user1364368
4
Ini bukan cara yang tepat untuk mendapatkannya karena 2 alasan: 1) ia membutuhkan memori dari gambar penuh walaupun Anda mungkin hanya menggunakan yang downscaled 2) Anda perlu memuat gambar penuh yang lebih lambat - gunakan memuat dengan inSampleSize dan BitmapFactory.decodeResource (), yang merupakan solusi yang jauh lebih unggul untuk ini.
Patrick Favre
303

Untuk Googlers masa depan, berikut adalah algoritma yang saya porting dari Quasimondo. Ini semacam campuran antara blur kotak dan blaus gaussian, sangat cantik dan cukup cepat juga.

Pembaruan untuk orang-orang yang mengalami masalah ArrayIndexOutOfBoundsException: @anthonycr di komentar memberikan informasi ini:

Saya menemukan bahwa dengan mengganti Math.abs dengan StrictMath.abs atau implementasi abs lainnya, crash tidak terjadi.

/**
 * 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
sumber
4
Terima kasih Yahel. Anda Memecahkan masalah saya. Terima kasih lagi.
Yog Guru
1
apa yang harus saya lewati sebagai radius?
krisDrOid
16
Untuk radius lebih besar dari 1 kadang-kadang Anda mendapatkan ArrayIndexOutOfBoundsException. Saya akan mencoba mengidentifikasi masalahnya.
MikeL
7
@MichaelLiberman Saya juga mengalami masalah yang sama. Menemukan mengapa belum? "g [yi] = dv [gsum];" -> error: java.lang.ArrayIndexOutOfBoundsException: length = 112896; index = 114021
see2851
2
Berlari ke ArrayIndexOutOfBoundsException yang dikenal, dan setelah beberapa analisis, saya percaya itu disebabkan oleh optimasi yang salah oleh VM Dalvik. Dalam forlingkaran segera sebelum dereference buruk, baik perhitungan rbsvariabel atau perhitungan gsum, rsumatau bsumvariabel tidak menjadi dilakukan tepat. Saya menemukan bahwa dengan mengganti Math.absdengan StrictMath.absatau beberapa absimplementasi lainnya , crash tidak terjadi. Sejak StrictMath.absdidelegasikan kepada dirinya sendiri Math.abs, sepertinya itu pasti optimasi yang buruk.
anthonycr
255

Android Blur Guide 2016

dengan Aplikasi Showcase / Benchmark dan Sumber di Github . Lihat juga kerangka Blur yang sedang saya kerjakan: Dali .

Setelah banyak bereksperimen, saya sekarang dapat dengan aman memberi Anda beberapa rekomendasi kuat yang akan membuat hidup Anda lebih mudah di Android saat menggunakan Kerangka Android.

Muat dan Gunakan Bitmap yang diturunkan (untuk gambar yang sangat buram)

Jangan pernah gunakan ukuran penuh Bitmap. Semakin besar gambar semakin banyak yang perlu dikaburkan dan juga semakin tinggi radius blur yang perlu dan biasanya, semakin tinggi radius blur semakin lama algoritma dibutuhkan.

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

Ini akan memuat bitmap dengan inSampleSize8, jadi hanya 1/64 dari gambar asli. Uji apa yang inSampleSizesesuai dengan kebutuhan Anda, tetapi simpanlah 2 ^ n (2,4,8, ...) untuk menghindari penurunan kualitas karena penskalaan. Lihat Google doc untuk lebih lanjut

Keuntungan lain yang sangat besar adalah bahwa pemuatan bitmap akan sangat cepat. Dalam pengujian blur awal saya, saya menemukan bahwa waktu paling lama selama seluruh proses blur adalah pemuatan gambar. Jadi untuk memuat gambar 1920x1080 dari disk, Nexus 5 saya membutuhkan 500ms sedangkan pengaburan hanya membutuhkan sekitar 250 ms.

Gunakan Renderscript

Renderscript menyediakan ScriptIntrinsicBluryang merupakan filter blur Gaussian. Ini memiliki kualitas visual yang baik dan hanya tercepat yang Anda dapatkan secara realistis di Android. Google mengklaim "biasanya 2-3x lebih cepat dari implementasi C multithreaded dan sering 10x + lebih cepat daripada implementasi Java" . Renderscript sangat canggih (menggunakan perangkat pemrosesan tercepat (GPU, ISP, dll.), Dll.) Dan ada juga perpustakaan dukungan v8 yang membuatnya kompatibel hingga 2.2.. Yah setidaknya dalam teori, melalui tes dan laporan saya sendiri dari pengembang lain tampaknya tidak mungkin untuk menggunakan Renderscript secara membabi buta, karena fragmentasi perangkat keras / driver tampaknya menyebabkan masalah dengan beberapa perangkat, bahkan dengan lvl sdk yang lebih tinggi (misalnya saya punya masalah dengan 4.1 Nexus S) jadi hati-hati dan uji pada banyak perangkat. Berikut ini contoh sederhana yang akan membantu Anda memulai:

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

Saat menggunakan dukungan v8 dengan Gradle, yang secara khusus direkomendasikan oleh Google "karena mereka menyertakan peningkatan terbaru" , Anda hanya perlu menambahkan 2 baris ke skrip build Anda dan gunakan android.support.v8.renderscriptdengan tools build saat ini ( sintaks yang diperbarui untuk plugin android Gradle v14 + )

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

Tolok ukur sederhana pada Nexus 5 - membandingkan RenderScript dengan implementasi java dan Renderscript lainnya:

Rata-rata runtime per blur pada berbagai ukuran gambar Rata-rata runtime per blur pada berbagai ukuran gambar

Megapiksel per detik yang dapat dikaburkan Megapiksel per detik yang dapat dikaburkan

Setiap nilai adalah rata-rata 250 putaran. RS_GAUSS_FASTadalah ScriptIntrinsicBlur(dan hampir selalu yang tercepat), yang lain yang memulai dengan RS_sebagian besar melibatkan implementasi dengan kernel sederhana. Detail algoritma dapat ditemukan di sini . Ini tidak sepenuhnya kabur, karena sebagian yang baik adalah pengumpulan sampah yang diukur. Ini dapat dilihat di sini ( ScriptIntrinsicBlurpada gambar 100x100 dengan sekitar 500 putaran)

masukkan deskripsi gambar di sini

Paku itu gc.

Anda dapat memeriksa sendiri, aplikasi benchmark ada di playstore: BlurBenchmark

Gunakan kembali Bitmap sedapat mungkin (jika prio: kinerja> jejak memori)

Jika Anda memerlukan banyak pengaburan untuk pengaburan langsung atau sejenisnya dan memori Anda memungkinkannya tidak memuat bitmap dari drawable beberapa kali, tetapi simpanlah "di-cache" dalam variabel anggota. Dalam hal ini selalu mencoba menggunakan variabel yang sama, untuk menjaga pengumpulan sampah seminimal mungkin.

Periksa juga inBitmapopsi baru saat memuat dari file atau drawable yang akan menggunakan kembali memori bitmap dan menghemat waktu pengumpulan sampah.

Untuk pencampuran dari tajam ke buram

Metode sederhana dan naif hanya menggunakan 2 ImageViews, satu kabur, dan alpha memudar mereka. Tetapi jika Anda ingin tampilan yang lebih canggih yang halus memudar dari tajam ke buram, maka periksa pos Roman Nurik tentang bagaimana melakukannya seperti di aplikasi Muzei-nya .

Pada dasarnya dia menjelaskan bahwa dia mengaburkan beberapa frame dengan luasan kabur yang berbeda dan menggunakannya sebagai keyframe dalam animasi yang terlihat sangat halus.

Diagram tempat Nurik memberi contoh pendekatannya

Patrick Favre
sumber
1
Pertama-tama, terima kasih atas kerja keras Anda! Tetapi saya mendapat pertanyaan: "karena menggunakan mode USAGE_SHARED yang menggunakan kembali memori". Di mana Anda menemukan USAGE_SHARED yang konstan? Saya tidak dapat menemukannya di mana pun.
Some Noob Student
2
Saya menemukannya, USAGE_SHARED hanya tersedia di support.v8.renderscript
Some Noob Student
2
Renderscript fast Gaussian blur gagal dengan kesalahan alokasi memori C pada perangkat low-end. Diuji pada ZTE Z992 (Android 4.1.1) dan Kyocera Rise (Android 4.0.4) menggunakan aplikasi Play Store yang disediakan. Juga memiliki laporan kegagalan pada Samsung Galaxy S3 mini. Karena kesalahan terjadi pada lapisan C, mereka tidak dapat terjebak sebagai pengecualian di Jawa, yang berarti crash aplikasi tidak dapat dihindari. Sepertinya RenderScript mungkin tidak siap untuk penggunaan produksi.
Theo
4
untuk versi gradle yang lebih baru, gunakan renderscriptSupportModeEnabled trueatau tidak akan dibangun! Saya mencari selamanya!
seb
3
Ketika saya mencoba solusi ini, daripada mendapatkan bitmap yang kabur, saya mendapat bitmap berwarna pelangi . Adakah yang mengalami masalah ini? Jika demikian, bagaimana Anda memperbaikinya?
HaloMediaz
53

EDIT (April 2014): Ini adalah halaman tanya jawab yang sepertinya masih mendapatkan banyak hit. Saya tahu saya selalu mendapatkan upvotes untuk posting ini. Tetapi jika Anda membaca ini, Anda perlu menyadari bahwa jawaban yang diposting di sini (baik milik saya maupun jawaban yang diterima) sudah usang. Jika Anda ingin menerapkan blur yang efisien hari ini , Anda harus menggunakan RenderScript, bukan NDK atau Java. RenderScript berjalan di Android 2.2+ (menggunakan Perpustakaan Dukungan Android ), jadi tidak ada alasan untuk tidak menggunakannya.

Jawaban lama berikut, tetapi waspadalah karena sudah ketinggalan zaman.


Untuk Googlers² di masa depan, berikut adalah algoritma yang saya porting dari port Yahel tentang algoritma Quasimondo, tetapi menggunakan NDK. Itu berdasarkan jawaban Yahel, tentu saja. Tapi ini menjalankan kode C asli, jadi lebih cepat. Lebih cepat. Seperti, 40 kali lebih cepat.

Saya menemukan bahwa menggunakan NDK adalah bagaimana semua manipulasi gambar harus dilakukan pada Android ... agak menjengkelkan untuk diterapkan pada awalnya (baca tutorial hebat tentang menggunakan JNI dan NDK di sini ), tetapi jauh lebih baik, dan mendekati waktu nyata untuk sebuah banyak hal.

Sebagai referensi, menggunakan fungsi Java Yahel, butuh 10 detik untuk mengaburkan gambar 480x532 piksel saya dengan radius 10 blur. Tetapi butuh 250ms menggunakan versi C asli. Dan saya cukup yakin itu masih dapat dioptimalkan lebih lanjut ... Saya baru saja melakukan konversi bodoh dari kode java, mungkin ada beberapa manipulasi yang dapat dipersingkat, tidak ingin menghabiskan terlalu banyak waktu refactoring semuanya.

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

Kemudian gunakan seperti ini (mempertimbangkan kelas yang disebut com.insert.your.package.ClassName dan fungsi asli yang disebut functionToBlur, seperti yang dinyatakan oleh kode di atas):

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

Itu mengharapkan bitmap RGB_8888!

Untuk menggunakan bitmap RGB_565, buat salinan yang dikonversi sebelum melewati parameter (yuck), atau ubah fungsi untuk menggunakan rgb565tipe baru alih-alih rgba:

typedef struct {
    uint16_t byte0;
} rgb565;

Masalahnya adalah bahwa jika Anda melakukannya Anda tidak dapat membaca .red, .greendan .bluedari pixel lagi, Anda perlu membaca byte dengan benar, ya. Ketika saya membutuhkan itu sebelumnya, saya melakukan ini:

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

Tapi mungkin ada cara yang kurang bodoh untuk melakukannya. Saya tidak terlalu suka kode C tingkat rendah, saya khawatir.

zeh
sumber
Terima kasih, ini banyak membantu saya :)
Dmitry Zaytsev
18
TAPI itu membutuhkan banyak memori. Untuk mengurangi konsumsi memori, ubah jenis r[wh], g[wh]dan b[wh]ke uint8_t.
Dmitry Zaytsev
dapatkah Anda menunjukkan kepada saya seperti apa file Android.mk Anda, padapastebin.com
CQM
1
Untuk contoh RenderScript yang berfungsi dari Gaussian Blur pada Android SDK 17+ lihat di sini: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini
2
RenderScript juga tersedia sebagai bagian dari lib dukungan untuk Android 2.2+, jadi tidak ada alasan untuk tidak menggunakannya lagi di mana-mana: android-developers.blogspot.com/2013/09/…
zeh
14

Kode ini sangat cocok untuk saya

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
sumber
Cara termudah untuk mengaburkan gambar (y)
VAdaihiep
12

Anda sekarang dapat menggunakan ScriptIntrinsicBlur dari pustaka RenderScript untuk kabur dengan cepat. Berikut ini cara mengakses RenderScript API. Berikut ini adalah kelas yang saya buat untuk mengaburkan Tampilan dan Bitmap:

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
sumber
2
Konteks Renderscript tidak boleh dibuat dalam metode blur, tetapi dikelola secara statis atau diberikan ke metode. (jika Anda keberatan kinerja)
Patrick Favre
1
Bisakah Anda memberi contoh penggunaan ini? Ketika saya mencoba menggunakannya saya mendapatkan kesalahan berikut: java.lang.IllegalArgumentException: lebar dan tinggi harus> 0
Donal Rafferty
10

Ini bekerja dengan baik untuk saya: Cara Memburamkan Gambar secara Efisien dengan RenderScript 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
sumber
Ini akan menjadi lebih baik / lebih cepat jika Anda menembolok objek RenderScript yang Anda buat. Instantiating yang baru setiap kali Anda ingin mengaburkan gambar hanya menambahkan overhead yang tidak perlu (Java object creation / destruction).
Stephen Hines
4

Ini untuk semua orang yang perlu meningkatkan jari-jari ScriptIntrinsicBluruntuk mendapatkan gaussian blur yang lebih sulit.

Alih-alih menempatkan radius lebih dari 25, Anda dapat memperkecil gambar dan mendapatkan hasil yang sama. Saya menulis sebuah kelas bernama GaussianBlur. Di bawah ini Anda dapat melihat cara menggunakan, dan implementasi seluruh kelas.

Pemakaian:

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

Kelas:

 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.
sumber
tidak jika Anda menurunkan skala gambar untuk meningkatkan skala nanti, Anda akan mengambil gambar yang ganjil alih-alih gambar yang kabur :(
loki
4

Terima kasih @Yahel untuk kodenya. Memposting metode yang sama dengan dukungan saluran alpha kabur karena butuh beberapa waktu untuk membuatnya berfungsi dengan benar sehingga dapat menghemat waktu seseorang:

/**
 * 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
sumber
masih dengan 'indeks di luar jangkauan' di perangkat> hdpi sebagai sumber aslinya
Gabriel Ferreira
4

Saya menggunakan ini sebelumnya ..

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

Untuk Googler masa depan yang memilih pendekatan NDK - saya menemukan algoritma stackblur yang andal. Saya menemukan implementasi C ++ yang tidak bergantung pada SSE di sini - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32 yang berisi beberapa optimasi menggunakan tabel statis seperti:

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

Saya membuat modifikasi algoritma stackblur untuk sistem multi-core - dapat ditemukan di sini http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ Karena semakin banyak perangkat memiliki 4 core - optimasi memberikan Manfaat kecepatan 4x.

Victor Laskin
sumber
1

Saran Nicolas POMEPUY. Saya pikir tautan ini akan sangat membantu: Efek blur untuk desain Android

Contoh proyek di 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
sumber
Meskipun tautan ini dapat menjawab pertanyaan, lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini dan memberikan tautan untuk referensi. Jawaban hanya tautan dapat menjadi tidak valid jika halaman tertaut berubah.
Amal Murali
1
Amal Murali, kamu benar. Sekarang ubah posting saya. Bagus, Anda tidak hanya downvote, tetapi juga komentar.
Yura Shinkarev
1

Kami mencoba menerapkan RenderScript blur seperti yang disebutkan di atas dalam jawaban yang berbeda. Kami terbatas untuk menggunakan versi v8 RenderScript dan itu menyebabkan kami banyak masalah.

  • Samsung S3 mogok secara acak setiap kali kami mencoba menggunakan renderscript
  • Perangkat lain (lintas API berbeda) secara acak menunjukkan masalah warna yang berbeda

Saya ingin berbagi versi kotor Java-only yang lambat dan harus dilakukan pada utas terpisah dan, jika mungkin, sebelum digunakan dan karenanya tetap ada.

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

Solusi ini jauh dari sempurna tetapi menciptakan efek blur yang masuk akal berdasarkan fakta, bahwa ia menggambar versi yang sangat transparan dari gambar yang sama di atas versi "tajam" yang nyaris tidak transparan. Alfa tergantung pada jarak ke titik asal.

Anda dapat menyesuaikan beberapa "angka ajaib" dengan kebutuhan Anda. Saya hanya ingin membagikan "solusi" itu untuk semua orang yang memiliki masalah dengan versi dukungan v8 dari RenderScript.

Warren Faith
sumber
Saya juga mengalami masalah dengan RenderScript pada beberapa perangkat armeabi lama, tetapi solusi Anda terlalu memakan memori.
AsafK
0

Bagi mereka yang masih memiliki masalah dengan pustaka dukungan Renderscript pada chipset x86, silakan lihat posting ini oleh pembuat perpustakaan. Sepertinya perbaikan yang disiapkannya tidak membuatnya entah bagaimana ke Build Tools v20.0.0, jadi dia menyediakan file untuk memperbaikinya secara manual dan penjelasan singkat tentang bagaimana melakukannya.

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

Saya menemukan bahwa mengurangi kontras, kecerahan dan saturasi sedikit membuat gambar buram lebih cantik, jadi saya menggabungkan berbagai metode dari stack overflow dan membuat Kelas Blur ini yang berhubungan dengan gambar buram, mengubah kecerahan, saturasi, kontras, dan ukuran gambar buram. Itu juga dapat mengkonversi gambar dari drawable ke bitmap dan sebaliknya.

Ankit Gaur
sumber
0

Pada i / o 2019 solusi berikut disajikan:

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