java एंड्रॉइड एसडीके के लिए फास्ट बिटमैप ब्लर




android image-processing (15)

कोड के लिए धन्यवाद @Yahhel। अल्फा चैनल धुंधला समर्थन के साथ एक ही विधि पोस्ट करना क्योंकि मुझे इसे सही तरीके से काम करने में कुछ समय लगा, ताकि यह किसी के समय को बचा सके:

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

वर्तमान में एक एंड्रॉइड एप्लिकेशन में जो मैं विकसित कर रहा हूं, मैं इसे धुंधला करने के लिए किसी छवि के पिक्सल के माध्यम से लूपिंग कर रहा हूं। इसमें 640x480 छवि पर लगभग 30 सेकंड लगते हैं।

एंड्रॉइड मार्केट में ऐप ब्राउज़ करने के दौरान मैं एक ब्लर फीचर भी शामिल करता था और उनका धुंध बहुत तेज़ होता है (5 सेकेंड की तरह) ताकि वे धुंधली होने की एक अलग विधि का उपयोग कर रहे हों।

किसी को भी पिक्सेल के माध्यम से लूपिंग के अलावा एक तेज तरीका पता है?


यह कोड मेरे लिए सही काम है

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

}

Nicolas POMEPUY advice. I think this link will be helpful: Blur effect for Android design

Sample project at 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;
}

यह अंधेरे में एक शॉट है, लेकिन आप छवि को कम करने की कोशिश कर सकते हैं और फिर इसे फिर से बढ़ा सकते हैं। यह Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) साथ किया जा सकता है। सुनिश्चित करें और फ़िल्टर पैरामीटर को सही पर सेट करें। यह देशी कोड में चलाएगा ताकि यह तेज़ हो सके।


यह मेरे लिए ठीक काम करता है: एंड्रॉइड के रेंडरस्क्रिप्ट के साथ छवियों को कुशलता से कैसे धुंधला करें

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

We tried to implement RenderScript blur like mentioned above in different answers. We were limited to use the v8 RenderScript version and that caused us a lot of trouble.

  • Samsung S3 crashed randomly whenever we tried to use the renderscript
  • Other devices (across different APIs) randomly showed different color issues

I want to share our dirty Java-only version which is slow and should be done on a separate thread and, if possible, before usage and therefore persisted.

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

This solution is far from perfect but creates a reasonable blur effect based on the fact, that it draws highly transparent version of the same image on top of a barely transparent "sharp" version. The alpha depends on the distance to the origin.

You can adjust some "magic numbers" to your needs. I just wanted to share that "solution" for everybody who has issues with the v8 support version of RenderScript.


संपादित करें (अप्रैल 2014): यह एक प्रश्न / उत्तर पृष्ठ है जो अभी भी बहुत सी हिट लगता है। मुझे पता है कि मैं हमेशा इस पोस्ट के लिए अपवर्तित हो रहा हूं। लेकिन अगर आप इसे पढ़ रहे हैं, तो आपको यहां पोस्ट किए गए उत्तरों (मेरा और स्वीकार्य उत्तर दोनों) का एहसास होना चाहिए यदि आप आज कुशल धुंध को लागू करना चाहते हैं , तो आपको एनडीके या जावा के बजाय रेंडरस्क्रिप्ट का उपयोग करना चाहिए । रेंडरस्क्रिप्ट एंड्रॉइड 2.2+ ( एंड्रॉइड सपोर्ट लाइब्रेरी का उपयोग करके) पर चलता है, इसलिए इसका उपयोग करने का कोई कारण नहीं है।

पुराना जवाब निम्नानुसार है, लेकिन सावधान रहें क्योंकि यह पुराना है।

भविष्य के लिए Googlers, यहां एक एल्गोरिदम है जिसे मैंने याहेल के क्सीसिमोन्डो के एल्गोरिदम के बंदरगाह से पोर्ट किया था, लेकिन एनडीके का उपयोग किया था। यह निश्चित रूप से याहेल के जवाब पर आधारित है। लेकिन यह देशी सी कोड चला रहा है, इसलिए यह तेज़ है। काफी तेज। 40 गुना तेज की तरह।

मुझे लगता है कि एनडीके का उपयोग करना यह है कि एंड्रॉइड पर सभी छवि कुशलता कैसे की जानी चाहिए ... पहले इसे लागू करने के लिए कुछ हद तक परेशान करना है ( here जेएनआई और एनडीके का उपयोग करने पर एक महान ट्यूटोरियल पढ़ें), लेकिन बहुत बेहतर, और वास्तविक समय के लिए वास्तविक समय बहुत सारी चीज़ें।

संदर्भ के लिए, याहेल के जावा फ़ंक्शन का उपयोग करके, 10 की धुंध त्रिज्या के साथ मेरी 480x532 पिक्सल छवि को धुंधला करने में 10 सेकंड लग गए। लेकिन मूल सी संस्करण का उपयोग करके 250 मिमी लग गए। और मुझे पूरा यकीन है कि इसे अभी भी और अनुकूलित किया जा सकता है ... मैंने जावा कोड का एक गूंगा रूपांतरण किया है, शायद कुछ चीजें हैं जिन्हें छोटा किया जा सकता है, पूरी चीज को दोबारा हासिल करने में ज्यादा समय नहीं बिताना चाहता था।

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

फिर इसे इस तरह उपयोग करें (com.insert.your.package.ClassName नामक एक क्लास पर विचार करें और फ़ंक्शन टॉब्लर नामक मूल फ़ंक्शन, ऊपर दिए गए कोड के रूप में):

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

यह एक आरजीबी_8888 बिटमैप की उम्मीद है!

RGB_565 बिटमैप का उपयोग करने के लिए, या तो पैरामीटर (यक) पास करने से पहले एक परिवर्तित प्रतिलिपि बनाएं, या rgba बजाय एक नया rgb565 प्रकार का उपयोग करने के लिए फ़ंक्शन को बदलें:

typedef struct {
    uint16_t byte0;
} rgb565;

समस्या यह है कि यदि आप ऐसा करते हैं तो आप पिक्सेल के .blue , .green और .blue को पढ़ नहीं सकते हैं, आपको बाइट को ठीक से पढ़ना होगा, duh। जब मुझे पहले इसकी ज़रूरत थी, मैंने यह किया:

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

लेकिन शायद ऐसा करने के कुछ कम गूंगा तरीका है। मैं कम स्तर वाला सी कोडर नहीं हूं, मुझे डर है।




अब आप जल्दी से धुंधला करने के लिए रेंडरस्क्रिप्ट लाइब्रेरी से ScriptIntrinsicBlur उपयोग कर सकते हैं। यहां रेंडरस्क्रिप्ट एपीआई तक पहुंचने का तरीका बताया गया है। निम्नलिखित एक वर्ग है जिसे मैंने धुंधला और बिटमैप्स को धुंधला करने के लिए बनाया है:

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

एंड्रॉइड ब्लर गाइड 2016

गिथब पर शोकेस / बेंचमार्क ऐप और स्रोत के साथब्लर फ्रेमवर्क भी देखें जो मैं वर्तमान में काम कर रहा हूं: Dali

बहुत प्रयोग करने के बाद मैं अब आपको कुछ ठोस सिफारिशें सुरक्षित कर सकता हूं जो एंड्रॉइड फ्रेमवर्क का उपयोग करते समय एंड्रॉइड में आपके जीवन को आसान बना देगा।

लोड और डाउनस्केल्ड बिटमैप का उपयोग करें (बहुत धुंधली छवियों के लिए)

बिटमैप के पूर्ण आकार का कभी भी उपयोग न करें। जितनी अधिक छवि को धुंधला करने की आवश्यकता होती है और धुंध त्रिज्या जितनी अधिक होनी चाहिए और आम तौर पर ब्लर त्रिज्या जितना धीमा एल्गोरिदम होता है।

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

यह बिटमैप को नमूना आकार 8 के साथ लोड करेगा, इसलिए मूल छवि का केवल 1/64 होगा। परीक्षण करें कि नमूना आकार आपकी आवश्यकताओं के अनुरूप है, लेकिन स्केलिंग के कारण खराब गुणवत्ता से बचने के लिए इसे 2 ^ एन (2,4,8, ...) रखें। अधिक के लिए Google दस्तावेज़ देखें

एक और वास्तव में बड़ा फायदा यह है कि बिटमैप लोडिंग वास्तव में तेज़ होगा। मेरे शुरुआती धुंध परीक्षणों में मैंने पाया कि पूरे धुंध प्रक्रिया के दौरान सबसे लंबा समय छवि लोडिंग था। तो डिस्क से 1920x1080 छवि को लोड करने के लिए मेरे नेक्सस 5 को 500ms की आवश्यकता थी जबकि धुंधला केवल 250 एमएस या उससे अधिक ले लिया।

रेंडरस्क्रिप्ट का प्रयोग करें

ScriptIntrinsicBlur प्रदान करता है जो एक गॉसियन ब्लर फ़िल्टर है। इसमें अच्छी दृश्य गुणवत्ता है और यह वास्तव में सबसे तेज़ है जो आप वास्तव में एंड्रॉइड पर प्राप्त करते हैं। Google "मल्टीथ्रेड सी कार्यान्वयन से आमतौर पर 2-3x तेज और जावा कार्यान्वयन से 10x + तेज" होने का दावा करता है। रेंडरस्क्रिप्ट वास्तव में परिष्कृत है (फास्ट प्रोसेसिंग डिवाइस (जीपीयू, आईएसपी, आदि) आदि का उपयोग करके) और इसके लिए v8 समर्थन लाइब्रेरी भी है जो इसे 2.2 तक संगत बनाती है । कम से कम सिद्धांत में, मेरे स्वयं के परीक्षणों और अन्य देवताओं की रिपोर्ट के माध्यम से ऐसा लगता है कि रेन्डरस्क्रिप्ट का अंधाधुंध उपयोग करना संभव नहीं है, क्योंकि हार्डवेयर / ड्राइवर विखंडन कुछ उपकरणों के साथ समस्याएं पैदा करता है, यहां तक ​​कि उच्च sdk lvl (उदाहरण के लिए मेरे पास था) 4.1 नेक्सस एस के साथ परेशानी) तो सावधान रहें और बहुत से उपकरणों पर परीक्षण करें। यहां एक साधारण उदाहरण है जो आपको शुरू करेगा

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

ग्रैडल के साथ v8 समर्थन का उपयोग करते समय, जिसे विशेष रूप से Google द्वारा अनुशंसित किया जाता है "क्योंकि उनमें नवीनतम सुधार शामिल हैं" , आपको केवल अपनी बिल्ड स्क्रिप्ट में 2 लाइनें जोड़ने की आवश्यकता है और वर्तमान निर्माण टूल के साथ android.support.v8.renderscript उपयोग करें ( अद्यतन वाक्यविन्यास एंड्रॉइड ग्रेडल प्लगइन v14 + के लिए )

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

नेक्सस 5 पर सरल बेंचमार्क - विभिन्न अन्य जावा और रेंडरस्क्रिप्ट कार्यान्वयन के साथ रेंडरस्क्रिप्ट की तुलना करना:

विभिन्न तस्वीर आकारों पर औसत रनटाइम प्रति धुंध

प्रति सेकंड मेगापिक्सेल जो धुंधला हो सकता है

प्रत्येक मान 250 राउंड का औसत है। RS_GAUSS_FAST ScriptIntrinsicBlur (और लगभग हमेशा सबसे तेज़) है, ScriptIntrinsicBlur साथ शुरू होने वाले अन्य लोग सरल कर्नेल के साथ अधिकतर कार्यान्वयन को नियंत्रित करते हैं। एल्गोरिदम का विवरण यहां पाया जा सकता है । यह पूरी तरह धुंधला नहीं है, लेकिन एक अच्छा हिस्सा कचरा संग्रह है जिसे मापा जाता है। इसे यहां देखा जा सकता है (लगभग 500 राउंड के साथ 100x100 छवि पर ScriptIntrinsicBlur )

स्पाइक्स जीसी हैं।

आप स्वयं को देख सकते हैं, बेंचमार्क ऐप प्लेस्टोर में है : ब्लरबेन्मार्क

जहां भी संभव हो बिटमैप का पुन: उपयोग करता है (यदि प्राइयो: प्रदर्शन> मेमोरी पदचिह्न)

यदि आपको लाइव ब्लर या इसी तरह के लिए एकाधिक ब्लर्स की आवश्यकता है और आपकी मेमोरी इसे कई बार ड्रैबल्स से बिटमैप लोड नहीं करती है, लेकिन इसे सदस्य चर में "कैश" रखें। इस मामले में कचरा इकट्ठा करने के लिए हमेशा एक ही चर का उपयोग करने का प्रयास करें।

फ़ाइल या ड्रॉइंग से लोड करते समय नया "इन बिटमैप" विकल्प भी देखें, जो बिटमैप मेमोरी का पुन: उपयोग करेगा और कचरा संग्रहण समय बचाएगा

तेज से धुंध से मिश्रण के लिए

सरल और निष्पक्ष विधि सिर्फ 2 छविदृश्यों का उपयोग करने के लिए है, एक धुंधला और अल्फा उन्हें फीका करता है। लेकिन यदि आप एक अधिक परिष्कृत रूप चाहते हैं जो आसानी से तेज से धुंध से फीका हो, तो रोमन नूरिक के पोस्ट को उसके मुज़ी ऐप में कैसा करना है, इस बारे में जांचें।

असल में वह बताते हैं कि वह अलग-अलग धुंध के साथ कुछ फ्रेम को पूर्व-धुंधला करता है और उन्हें एनीमेशन में कीफ्रेम के रूप में उपयोग करता है जो वास्तव में चिकनी दिखता है


For future Googlers who choose NDK approach - i find reliable mentioned stackblur algorithm. I found C++ implementation which does not rely on SSE here - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32 which contains some optimizations using static tables like:

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

I made modification of stackblur algorithm for multi-core systems - it can be found here http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ As more and more devices have 4 cores - optimizations give 4x speed benefit.


भविष्य के Googlers के लिए, यहां एक एल्गोरिदम है जिसे मैंने Quasimondo से पोर्ट किया था। यह एक बॉक्स धुंध और एक गाऊशियन धुंध के बीच एक मिश्रण है, यह बहुत सुंदर और काफी तेज़ है:

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

मैंने पाया कि कम कंट्रास्ट, चमक और संतृप्ति कम धुंधली छवियों को और अधिक सुंदर बनाता है इसलिए मैंने स्टैक ओवरफ्लो से विभिन्न विधियों को जोड़ा और इस धुंध वर्ग को धुंधला करने वाली छवियों, चमक, संतृप्ति, विपरीतता और धुंधली छवियों के आकार को बदलने से संबंधित किया। यह छवियों को खींचने योग्य से बिटमैप और इसके विपरीत में भी परिवर्तित कर सकता है।


मैंने पहले इसका इस्तेमाल किया ..

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




renderscript