Làm mờ Bitmap nhanh cho SDK Android


185

Hiện tại trong một ứng dụng Android mà tôi đang phát triển, tôi đang lặp qua các pixel của hình ảnh để làm mờ nó. Quá trình này mất khoảng 30 giây trên hình ảnh 640x480.

Trong khi duyệt các ứng dụng trong Android Market, tôi đã bắt gặp một ứng dụng có tính năng làm mờ và độ mờ của chúng rất nhanh (như 5 giây) vì vậy chúng phải sử dụng một phương pháp làm mờ khác.

Bất cứ ai cũng biết một cách nhanh hơn ngoài việc lặp qua các pixel?


2
Thật không may, hình ảnh sẽ luôn khác nhau vì vậy tôi sẽ không thể tạo ra một phiên bản mờ trước thời hạn. Thêm vào đó tôi cũng sẽ không biết cường độ mờ trước thời hạn.
Greg

Bạn có thể đăng mã của mình không, có thể đó là thuật toán / mã không hiệu quả, 30 giây để đi qua hình ảnh 640x480 là chậm, tôi đã nghĩ 5 giây chậm nhưng sau đó lại phụ thuộc vào bộ xử lý.
vickirk

Câu trả lời:


78

Đây là một bức ảnh trong bóng tối, nhưng bạn có thể thử thu nhỏ hình ảnh và sau đó phóng to nó một lần nữa. Điều này có thể được thực hiện với Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). Đảm bảo và đặt tham số bộ lọc thành đúng. Nó sẽ chạy trong mã gốc để có thể nhanh hơn.


5
Sau một số thử nghiệm và làm mờ rằng tôi đang làm điều này thực sự hoạt động đủ tốt cho tôi và nó nhanh chóng. Cảm ơn!
Greg

4
Nếu nó hoạt động tốt. Thật xấu hổ vì chúng tôi không bao giờ hiểu được lý do tại sao nó không hiệu quả.
vickirk

Bạn có thể muốn thử createdScaledBitmap và để hình ảnh có cùng kích thước. Nó làm mờ nó cho tôi :-(
Casebash

1
Dưới đây là một cuộc thảo luận về ý nghĩa của "bộ lọc": stackoverflow.com/questions/2895065
user1364368

4
Đây không phải là cách chính xác để có được vì 2 lý do: 1) nó cần bộ nhớ của hình ảnh đầy đủ, có lẽ bạn chỉ sử dụng một mức giảm 2), bạn cần tải toàn bộ hình ảnh sẽ chậm hơn - sử dụng tải với inSampleSize và BitmapFactory.decodeResource (), đây là giải pháp vượt trội hơn nhiều so với điều này.
Patrick Favre

300

Đối với nhân viên Google trong tương lai, đây là một thuật toán mà tôi đã chuyển từ Quasimondo. Đó là một sự pha trộn giữa mờ hộp và mờ gaussian, nó cũng rất đẹp và cũng khá nhanh.

Cập nhật cho những người gặp phải vấn đề ArrayIndexOutOfBoundException : @anthonycr trong các bình luận cung cấp thông tin này:

Tôi thấy rằng bằng cách thay thế Math.abs bằng StrictMath.abs hoặc một số triển khai abs khác, sự cố không xảy ra.

/**
 * 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 <mario@quasimondo.com>
 */

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

4
Cảm ơn Yahel. Bạn đã giải quyết vấn đề của tôi. Cảm ơn một lần nữa.
Yog Guru

1
những gì tôi nên vượt qua như bán kính?
krisDrOid

16
Đối với bán kính lớn hơn 1 đôi khi bạn nhận được ArrayIndexOutOfBoundException. Tôi sẽ cố gắng xác định vấn đề.
MikeL

7
@MichaelLiberman Tôi cũng gặp phải vấn đề tương tự. Tìm hiểu tại sao chưa? "g [yi] = dv [gsum];" -> lỗi: java.lang.ArrayIndexOutOfBoundException: length = 112896; chỉ số = 114021
xem2851

2
Chạy vào ArrayIndexOutOfBoundException đã biết, và sau một số phân tích, tôi tin rằng nó được gây ra bởi một tối ưu hóa không chính xác bởi máy ảo Dalvik. Trong forvòng lặp ngay trước dereference xấu, hoặc là tính toán của rbsbiến hoặc các tính toán của gsum, rsumhoặc bsumcác biến không được thực hiện đúng. Tôi thấy rằng bằng cách thay thế Math.absbằng StrictMath.abshoặc một số absthực hiện khác , sự cố không xảy ra. Vì StrictMath.absbản thân nó được ủy quyền Math.abs, có vẻ như nó phải là một sự tối ưu hóa tồi.
anthonycr

255

Hướng dẫn làm mờ Android 2016

với Ứng dụng Showcase / Điểm chuẩnNguồn trên Github . Ngoài ra, hãy kiểm tra khung Blur tôi hiện đang làm việc: Dali .

Sau khi thử nghiệm nhiều, giờ đây tôi có thể cung cấp cho bạn một số đề xuất chắc chắn sẽ giúp cuộc sống của bạn dễ dàng hơn trong Android khi sử dụng Android Framework.

Tải và sử dụng Bitmap thu nhỏ (đối với hình ảnh rất mờ)

Không bao giờ sử dụng kích thước đầy đủ của Bitmap. Hình ảnh càng lớn thì càng cần phải làm mờ và bán kính mờ càng cao và thông thường, bán kính mờ càng cao thì thuật toán càng mất nhiều thời gian.

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

Điều này sẽ tải bitmap với inSampleSize8, vì vậy chỉ bằng 1/64 ảnh gốc. Kiểm tra những gì inSampleSizephù hợp với nhu cầu của bạn, nhưng hãy giữ nó 2 ^ n (2,4,8, ...) để tránh làm giảm chất lượng do chia tỷ lệ. Xem tài liệu Google để biết thêm

Một lợi thế thực sự lớn khác là tải bitmap sẽ rất nhanh. Trong thử nghiệm làm mờ đầu tiên của tôi, tôi đã nhận ra rằng thời gian dài nhất trong toàn bộ quá trình làm mờ là tải hình ảnh. Vì vậy, để tải hình ảnh 1920x1080 từ đĩa, Nexus 5 của tôi cần 500ms trong khi quá trình làm mờ chỉ mất thêm 250 ms hoặc hơn.

Sử dụng Rendercript

Rendercript cung cấp ScriptIntrinsicBlurbộ lọc mờ Gaussian. Nó có chất lượng hình ảnh tốt và chỉ là nhanh nhất bạn thực sự có được trên Android. Google tuyên bố là "thường nhanh hơn 2-3 lần so với triển khai C đa luồng và thường nhanh hơn gấp 10 lần so với triển khai Java" . Rendercript thực sự tinh vi (sử dụng thiết bị xử lý nhanh nhất (GPU, ISP, v.v.), v.v.) và cũng có thư viện hỗ trợ v8 để nó tương thích với 2.2. Ít nhất là về mặt lý thuyết, thông qua các thử nghiệm và báo cáo của riêng tôi từ các nhà phát triển khác, dường như không thể sử dụng Rô-bốt một cách mù quáng, vì sự phân mảnh phần cứng / trình điều khiển dường như gây ra sự cố với một số thiết bị, ngay cả với sdk lvl cao hơn (ví dụ: tôi đã có sự cố với Nexus S 4.1), vì vậy hãy cẩn thận và thử nghiệm trên nhiều thiết bị. Đây là một ví dụ đơn giản giúp bạn bắt đầu:

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

Khi sử dụng hỗ trợ v8 với Gradle, được Google khuyến nghị cụ thể "vì chúng bao gồm các cải tiến mới nhất" , bạn chỉ cần thêm 2 dòng vào tập lệnh xây dựng của mình và sử dụng android.support.v8.renderscriptvới các công cụ xây dựng hiện tại ( cú pháp cập nhật cho plugin Gradle android v14 + )

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

Điểm chuẩn đơn giản trên Nexus 5 - so sánh RenderScript với các triển khai java và Rendercript khác nhau:

Thời gian chạy trung bình cho mỗi lần mờ trên các kích cỡ pic khác nhau Thời gian chạy trung bình cho mỗi lần mờ trên các kích cỡ pic khác nhau

Megapixel mỗi giây có thể bị mờ Megapixel mỗi giây có thể bị mờ

Mỗi giá trị là avg của 250 vòng. RS_GAUSS_FASTScriptIntrinsicBlur(và gần như luôn luôn là nhanh nhất), những cái khác bắt đầu với RS_hầu hết là các triển khai thực hiện với các nhân đơn giản. Các chi tiết của các thuật toán có thể được tìm thấy ở đây . Điều này không hoàn toàn làm mờ, vì một phần tốt là bộ sưu tập rác được đo. Điều này có thể được nhìn thấy ở đây ( ScriptIntrinsicBlurtrên hình ảnh 100x100 với khoảng 500 vòng)

nhập mô tả hình ảnh ở đây

Các gai là gc.

Bạn có thể tự kiểm tra, ứng dụng điểm chuẩn có trong playstore: BlurBenchmark

Sử dụng lại Bitmap bất cứ khi nào có thể (nếu tiên quyết: hiệu suất> dấu chân bộ nhớ)

Nếu bạn cần làm mờ nhiều lần để làm mờ trực tiếp hoặc tương tự và bộ nhớ của bạn cho phép nó không tải bitmap từ các bản vẽ nhiều lần, nhưng hãy giữ nó "lưu trong bộ nhớ cache" trong một biến thành viên. Trong trường hợp này, luôn cố gắng sử dụng các biến giống nhau, để giữ cho việc thu gom rác ở mức tối thiểu.

Ngoài ra kiểm tra tùy chọn mới inBitmapkhi tải từ tệp hoặc có thể vẽ được, nó sẽ sử dụng lại bộ nhớ bitmap và tiết kiệm thời gian thu gom rác.

Để pha trộn từ sắc nét đến mờ

Phương pháp đơn giản và ngây thơ chỉ là sử dụng 2 ImageViews, một làm mờ và làm mờ dần chúng. Nhưng nếu bạn muốn có một cái nhìn tinh vi hơn, mờ dần từ sắc nét đến mờ, thì hãy xem bài đăng của Roman Nurik về cách làm như thế nào trong ứng dụng Muzei của anh ấy .

Về cơ bản, anh giải thích rằng anh làm mờ trước một số khung hình với các mức độ mờ khác nhau và sử dụng chúng làm khung hình chính trong một hình ảnh động trông rất mượt mà.

Sơ đồ nơi Nurik thể hiện cách tiếp cận của mình


1
Trước hết, cảm ơn bạn đã làm việc chăm chỉ! Nhưng tôi có một câu hỏi: "bởi vì nó sử dụng chế độ USAGE_SHARED để sử dụng lại bộ nhớ". Bạn đã tìm thấy USAGE_SHARED không đổi ở đâu? Tôi không thể tìm thấy nó ở bất cứ đâu.
Một số sinh viên Noob

2
Tôi đã tìm thấy nó, USAGE_SHARED chỉ có sẵn trong support.v8.renderscript
Một số sinh viên Noob

2
Làm mờ nhanh Gaussian làm mờ lỗi với lỗi cấp phát bộ nhớ C trên các thiết bị cấp thấp. Đã thử nghiệm trên ZTE Z992 (Android 4.1.1) và Kyocera Rise (Android 4.0.4) bằng ứng dụng Play Store được cung cấp. Cũng đã có một báo cáo thất bại về Samsung Galaxy S3 mini. Do lỗi xảy ra trong lớp C, chúng không thể bị mắc kẹt như các ngoại lệ trong Java, có nghĩa là sự cố ứng dụng là không thể tránh khỏi. Có vẻ như RenderScript có thể chưa sẵn sàng để sử dụng sản xuất.
Theo

4
đối với các phiên bản lớp mới hơn, sử dụng renderscriptSupportModeEnabled truehoặc nó sẽ không được xây dựng! Tôi đã tìm kiếm mãi mãi!
seb

3
Khi tôi thử giải pháp này, thay vì nhận được một bitmap mờ, tôi đã nhận được một bitmap màu cầu vồng . Bất cứ ai khác gặp vấn đề này? Nếu là vậy, bạn đã sửa nó như thế nào?
HaloMediaz

53

EDIT (Tháng 4 năm 2014): Đây là một trang câu hỏi / câu trả lời vẫn nhận được rất nhiều lượt truy cập. Tôi biết tôi luôn nhận được sự ủng hộ cho bài viết này. Nhưng nếu bạn đang đọc bài này, bạn cần nhận ra câu trả lời được đăng ở đây (cả câu trả lời của tôi và câu trả lời được chấp nhận) đã hết hạn. Nếu bạn muốn thực hiện làm mờ hiệu quả ngay hôm nay , bạn nên sử dụng RenderScript thay vì NDK hoặc Java. RenderScript chạy trên Android 2.2+ (sử dụng Thư viện hỗ trợ Android ), vì vậy không có lý do gì để không sử dụng nó.

Câu trả lời cũ theo sau, nhưng hãy cẩn thận vì nó đã lỗi thời.


Đối với các nhân viên Google trong tương lai, đây là một thuật toán mà tôi đã chuyển từ thuật toán Quasimondo của Yahel, nhưng sử dụng NDK. Tất nhiên, nó dựa trên câu trả lời của Yahel. Nhưng đây là chạy mã C gốc, vì vậy nó nhanh hơn. Nhanh hơn nhiều. Giống như, nhanh hơn 40 lần.

Tôi thấy rằng sử dụng NDK là cách tất cả các thao tác hình ảnh nên được thực hiện trên Android ... ban đầu hơi khó thực hiện (đọc một hướng dẫn tuyệt vời về cách sử dụng JNI và NDK tại đây ), nhưng tốt hơn nhiều và gần thời gian thực cho nhiều thứ.

Để tham khảo, sử dụng chức năng Java của Yahel, phải mất 10 giây để làm mờ hình ảnh 480x532 pixel của tôi với bán kính mờ là 10. Nhưng phải mất 250ms khi sử dụng phiên bản C gốc. Và tôi khá chắc chắn rằng nó vẫn có thể được tối ưu hóa hơn nữa ... Tôi vừa thực hiện một chuyển đổi ngu ngốc của mã java, có lẽ có một số thao tác có thể rút ngắn, không muốn mất quá nhiều thời gian để cấu trúc lại toàn bộ.

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

Sau đó sử dụng nó như thế này (xem xét một lớp có tên com.insert.your.package.ClassName và một hàm riêng có tên functionToBlur, như mã ở trên)

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

Nó mong đợi một bitmap RGB_8888!

Để sử dụng bitmap RGB_565, hãy tạo một bản sao được chuyển đổi trước khi truyền tham số (yuck) hoặc thay đổi chức năng để sử dụng rgb565loại mới thay vì rgba:

typedef struct {
    uint16_t byte0;
} rgb565;

Vấn đề là nếu bạn làm điều đó bạn không thể đọc được .red, .green.bluecác điểm ảnh nữa, bạn cần phải đọc các byte đúng cách, duh. Khi tôi cần điều đó trước đây, tôi đã làm điều này:

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

Nhưng có lẽ có một số cách ít ngu ngốc hơn để làm điều đó. Tôi không phải là một lập trình viên C cấp thấp, tôi sợ.


Cảm ơn, nó đã giúp tôi rất nhiều :)
Dmitry Zaytsev

18
NHƯNG nó đòi hỏi rất nhiều bộ nhớ. Để giảm bộ nhớ loại thay đổi tiêu thụ r[wh], g[wh]b[wh]để uint8_t.
Dmitry Zaytsev

bạn có thể chỉ cho tôi tệp Android.mk của bạn trông như thế nào không, trênpastebin.com
CQM

1
Để biết ví dụ về RenderScript hoạt động của Gaussian Blur trên Android SDK 17+, hãy xem tại đây: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini

2
RenderScript cũng có sẵn như là một phần của lib hỗ trợ dành cho Android 2.2 +, vì vậy không có lý do không sử dụng nó ở khắp mọi nơi nữa: android-developers.blogspot.com/2013/09/...
Zeh

14

Mã này là công việc hoàn hảo cho tôi

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

}

Cách đơn giản nhất để làm mờ hình ảnh (y)
VAdaihiep

12

Bây giờ bạn có thể sử dụng ScriptIntrinsicBlur từ thư viện RenderScript để làm mờ nhanh chóng. Đây là cách truy cập API RenderScript. Sau đây là lớp tôi đã thực hiện để làm mờ Lượt xem và 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;
    }
}

2
Không nên tạo bối cảnh Rendercript trong phương thức làm mờ, nhưng được quản lý tĩnh hoặc được cung cấp cho phương thức. (nếu bạn quan tâm đến hiệu suất)
Patrick Favre

1
Bạn có thể cho một ví dụ về điều này là sử dụng? Khi tôi cố gắng sử dụng, tôi gặp lỗi sau: java.lang.IllegalArgumentException: chiều rộng và chiều cao phải> 0
Donal Rafferty

10

Điều này hoạt động tốt với tôi: Cách làm mờ hình ảnh hiệu quả với RenderScript của 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;
    }
}

Điều này sẽ còn tốt hơn / nhanh hơn nếu bạn lưu trữ đối tượng RenderScript mà bạn đã tạo. Khởi tạo một cái mới mỗi khi bạn muốn làm mờ hình ảnh chỉ cần thêm chi phí không cần thiết (tạo / hủy đối tượng Java).
Stephen Hines


4

Điều này là dành cho tất cả những người cần tăng bán kính ScriptIntrinsicBlurđể có được độ mờ gaussian khó hơn.

Thay vì đặt bán kính hơn 25, bạn có thể thu nhỏ hình ảnh và nhận được kết quả tương tự. Tôi đã viết một lớp gọi làGaussianBlur . Dưới đây bạn có thể xem cách sử dụng và toàn bộ lớp thực hiện.

Sử dụng:

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

Lớp học:

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

không, nếu bạn thu nhỏ hình ảnh để tăng tỷ lệ sau, bạn sẽ truy xuất hình ảnh khối thay vì hình ảnh bị mờ :(
loki

4

Cảm ơn @Yahel cho mã. Đăng cùng một phương pháp với hỗ trợ làm mờ kênh alpha vì tôi phải mất một thời gian để làm cho nó hoạt động chính xác để có thể tiết kiệm thời gian của ai đó:

/**
 * 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 <mario@quasimondo.com>
 */

public static Bitmap fastblur(Bitmap sentBitmap, float scale, int radius) {

    int width = Math.round(sentBitmap.getWidth() * scale);
    int height = Math.round(sentBitmap.getHeight() * scale);
    sentBitmap = Bitmap.createScaledBitmap(sentBitmap, width, height, false);

    Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);

    if (radius < 1) {
        return (null);
    }

    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    int[] pix = new int[w * h];
    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.getPixels(pix, 0, w, 0, 0, w, h);

    int wm = w - 1;
    int hm = h - 1;
    int wh = w * h;
    int div = radius + radius + 1;

    int r[] = new int[wh];
    int g[] = new int[wh];
    int b[] = new int[wh];
    int a[] = new int[wh];
    int rsum, gsum, bsum, asum, x, y, i, p, yp, yi, yw;
    int vmin[] = new int[Math.max(w, h)];

    int divsum = (div + 1) >> 1;
    divsum *= divsum;
    int dv[] = new int[256 * divsum];
    for (i = 0; i < 256 * divsum; i++) {
        dv[i] = (i / divsum);
    }

    yw = yi = 0;

    int[][] stack = new int[div][4];
    int stackpointer;
    int stackstart;
    int[] sir;
    int rbs;
    int r1 = radius + 1;
    int routsum, goutsum, boutsum, aoutsum;
    int rinsum, ginsum, binsum, ainsum;

    for (y = 0; y < h; y++) {
        rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0;
        for (i = -radius; i <= radius; i++) {
            p = pix[yi + Math.min(wm, Math.max(i, 0))];
            sir = stack[i + radius];
            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            sir[3] = 0xff & (p >> 24);

            rbs = r1 - Math.abs(i);
            rsum += sir[0] * rbs;
            gsum += sir[1] * rbs;
            bsum += sir[2] * rbs;
            asum += sir[3] * rbs;
            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
                ainsum += sir[3];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
                aoutsum += sir[3];
            }
        }
        stackpointer = radius;

        for (x = 0; x < w; x++) {

            r[yi] = dv[rsum];
            g[yi] = dv[gsum];
            b[yi] = dv[bsum];
            a[yi] = dv[asum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;
            asum -= aoutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];
            aoutsum -= sir[3];

            if (y == 0) {
                vmin[x] = Math.min(x + radius + 1, wm);
            }
            p = pix[yw + vmin[x]];

            sir[0] = (p & 0xff0000) >> 16;
            sir[1] = (p & 0x00ff00) >> 8;
            sir[2] = (p & 0x0000ff);
            sir[3] = 0xff & (p >> 24);

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];
            ainsum += sir[3];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;
            asum += ainsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[(stackpointer) % div];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];
            aoutsum += sir[3];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];
            ainsum -= sir[3];

            yi++;
        }
        yw += w;
    }
    for (x = 0; x < w; x++) {
        rinsum = ginsum = binsum = ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0;
        yp = -radius * w;
        for (i = -radius; i <= radius; i++) {
            yi = Math.max(0, yp) + x;

            sir = stack[i + radius];

            sir[0] = r[yi];
            sir[1] = g[yi];
            sir[2] = b[yi];
            sir[3] = a[yi];

            rbs = r1 - Math.abs(i);

            rsum += r[yi] * rbs;
            gsum += g[yi] * rbs;
            bsum += b[yi] * rbs;
            asum += a[yi] * rbs;

            if (i > 0) {
                rinsum += sir[0];
                ginsum += sir[1];
                binsum += sir[2];
                ainsum += sir[3];
            } else {
                routsum += sir[0];
                goutsum += sir[1];
                boutsum += sir[2];
                aoutsum += sir[3];
            }

            if (i < hm) {
                yp += w;
            }
        }
        yi = x;
        stackpointer = radius;
        for (y = 0; y < h; y++) {
            pix[yi] = (dv[asum] << 24) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];

            rsum -= routsum;
            gsum -= goutsum;
            bsum -= boutsum;
            asum -= aoutsum;

            stackstart = stackpointer - radius + div;
            sir = stack[stackstart % div];

            routsum -= sir[0];
            goutsum -= sir[1];
            boutsum -= sir[2];
            aoutsum -= sir[3];

            if (x == 0) {
                vmin[y] = Math.min(y + r1, hm) * w;
            }
            p = x + vmin[y];


            sir[0] = r[p];
            sir[1] = g[p];
            sir[2] = b[p];
            sir[3] = a[p];

            rinsum += sir[0];
            ginsum += sir[1];
            binsum += sir[2];
            ainsum += sir[3];

            rsum += rinsum;
            gsum += ginsum;
            bsum += binsum;
            asum += ainsum;

            stackpointer = (stackpointer + 1) % div;
            sir = stack[stackpointer];

            routsum += sir[0];
            goutsum += sir[1];
            boutsum += sir[2];
            aoutsum += sir[3];

            rinsum -= sir[0];
            ginsum -= sir[1];
            binsum -= sir[2];
            ainsum -= sir[3];

            yi += w;
        }
    }

    Log.e("pix", w + " " + h + " " + pix.length);
    bitmap.setPixels(pix, 0, w, 0, 0, w, h);

    return (bitmap);
}

vẫn với 'chỉ số nằm ngoài phạm vi' trong các thiết bị> hdpi làm nguồn ban đầu
Gabriel Ferreira

4

Tôi đã sử dụng điều này trước đây ..

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

2

Đối với các nhân viên Google trong tương lai chọn cách tiếp cận NDK - tôi tìm thấy thuật toán stackblur được đề cập đáng tin cậy. Tôi đã tìm thấy triển khai C ++ không dựa vào SSE ở đây - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32 có chứa một số tối ưu hóa bằng cách sử dụng các bảng tĩnh như:

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

Tôi đã thực hiện sửa đổi thuật toán stackblur cho các hệ thống đa lõi - có thể tìm thấy ở đây http://vitiy.info/stackblur-alerskym-multi-threaded-blur-for-cpp/ Khi ngày càng nhiều thiết bị có 4 lõi - tối ưu hóa Lợi ích gấp 4 lần tốc độ.


1

Nicolas POMEPUY tư vấn. Tôi nghĩ liên kết này sẽ hữu ích: Hiệu ứng làm mờ cho thiết kế Android

Dự án mẫu tại 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;
}

Mặc dù liên kết này có thể trả lời câu hỏi, tốt hơn là bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
Amal Murali

1
Amal Murali, bạn đã đúng. Bây giờ thay đổi bài viết của tôi. Tốt, rằng bạn không chỉ downvote, mà còn bình luận.
Yura Shinkarev

1

Chúng tôi đã cố gắng thực hiện BlurScript như đã đề cập ở trên trong các câu trả lời khác nhau. Chúng tôi đã giới hạn sử dụng phiên bản RenderScript v8 và điều đó gây ra cho chúng tôi rất nhiều rắc rối.

  • Samsung S3 bị sập ngẫu nhiên bất cứ khi nào chúng tôi cố gắng sử dụng bản kết xuất
  • Các thiết bị khác (trên các API khác nhau) ngẫu nhiên hiển thị các vấn đề màu khác nhau

Tôi muốn chia sẻ phiên bản chỉ dành cho Java bẩn của chúng tôi, phiên bản chậm và nên được thực hiện trên một luồng riêng biệt và, nếu có thể, trước khi sử dụng và do đó vẫn tồn tại.

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

Giải pháp này không hoàn hảo nhưng tạo ra hiệu ứng làm mờ hợp lý dựa trên thực tế, nó vẽ phiên bản có độ trong suốt cao của cùng một hình ảnh trên đầu phiên bản "sắc nét" trong suốt. Các alpha phụ thuộc vào khoảng cách đến nguồn gốc.

Bạn có thể điều chỉnh một số "số ma thuật" theo nhu cầu của mình. Tôi chỉ muốn chia sẻ "giải pháp" đó cho mọi người gặp vấn đề với phiên bản hỗ trợ v8 của RenderScript.


Tôi cũng gặp sự cố với RenderScript trên một số thiết bị armeabi cũ, nhưng giải pháp của bạn quá tốn bộ nhớ.
AsafK

0

Đối với những người vẫn gặp sự cố với thư viện hỗ trợ Rendercript trên chipset x86, vui lòng xem bài đăng này của người tạo thư viện. Có vẻ như bản sửa lỗi mà anh ấy đã chuẩn bị không bằng cách nào đó với Build Tools v20.0.0, vì vậy anh ấy cung cấp các tệp để sửa nó bằng tay và giải thích ngắn gọn về cách thực hiện.

https://code.google.com.vn/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=71347




0

Tôi thấy rằng việc giảm độ tương phản, độ sáng và độ bão hòa giảm đi một chút làm cho hình ảnh bị mờ đẹp hơn nên tôi đã kết hợp nhiều phương pháp khác nhau từ tràn ngăn xếp và tạo ra Lớp Blur này xử lý hình ảnh mờ, thay đổi độ sáng, độ bão hòa, độ tương phản và kích thước của hình ảnh bị mờ. Nó cũng có thể chuyển đổi hình ảnh từ drawable sang bitmap và ngược lại.


0

Vào ngày i / o 2019, giải pháp sau đây đã được trình bày:

/**
 * 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()
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.