Fast Bitmap Blur สำหรับ Android SDK


185

ขณะนี้อยู่ในแอปพลิเคชัน Android ที่ฉันกำลังพัฒนาฉันวนลูปผ่านพิกเซลของภาพเพื่อทำให้เบลอ ขั้นตอนนี้ใช้เวลาประมาณ 30 วินาทีสำหรับภาพ 640x480

ในขณะที่เรียกดูแอพใน Android Market ฉันเจอสิ่งที่มีคุณสมบัติเบลอและความพร่ามัวนั้นรวดเร็วมาก (เช่น 5 วินาที) ดังนั้นพวกเขาจึงต้องใช้วิธีการเบลอที่แตกต่างกัน

ใครรู้วิธีที่เร็วกว่าวนลูปผ่านพิกเซล?


2
น่าเสียดายที่ภาพจะแตกต่างกันเสมอดังนั้นฉันจะไม่สามารถสร้างเวอร์ชันเบลอล่วงหน้า รวมทั้งฉันจะไม่ทราบความพร่ามัวก่อนเวลาเช่นกัน
เกร็ก

คุณสามารถโพสต์รหัสของคุณได้ไหมมันอาจเป็นอัลกอริธึม / รหัสที่ไม่มีประสิทธิภาพ 30 วินาทีในการดูภาพ 640x480 ช้าฉันคิดว่า 5 วินาทีช้าไปแล้ว แต่ขึ้นอยู่กับโปรเซสเซอร์อีกครั้ง
vickirk

คำตอบ:


78

นี่คือภาพในที่มืด แต่คุณอาจลองลดขนาดรูปภาพแล้วขยายภาพอีกครั้ง Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)ซึ่งสามารถทำได้ด้วย ตรวจสอบและตั้งค่าพารามิเตอร์ตัวกรองเป็นจริง มันจะทำงานในรหัสเนทีฟดังนั้นจึงอาจเร็วกว่า


5
หลังจากการทดสอบและการเบลอที่ฉันกำลังทำอยู่จริงนี้ก็ใช้ได้ดีพอสำหรับฉันและมันก็เร็ว ขอบคุณ!
Greg

4
ถ้ามันใช้งานได้ดี มันเป็นความอัปยศที่เราไม่เคยไปถึงจุดต่ำสุดว่าทำไมมันถึงไม่มีประสิทธิภาพ
vickirk

คุณอาจต้องการลอง createScaledBitmap และปล่อยให้ภาพมีขนาดเท่ากัน มันทำให้มันเบลอสำหรับฉัน :-(
Casebash

1
นี่คือการอภิปรายเกี่ยวกับความหมายของอาร์กิวเมนต์ "ตัวกรอง": stackoverflow.com/questions/2895065
1364368

4
นี่ไม่ใช่วิธีที่จะได้มาเนื่องจากเหตุผล 2 ประการ: 1) มันต้องการหน่วยความจำของภาพเต็มรูปแบบที่คุณอาจใช้เพียงลดระดับภาพ 2) คุณต้องโหลดภาพเต็มซึ่งช้ากว่า - ใช้การโหลดด้วย inSampleSize และ BitmapFactory.decodeResource () ซึ่งเป็นโซลูชั่นที่เหนือกว่าสำหรับเรื่องนี้
Patrick Favre

303

สำหรับชาว Google ในอนาคตนี่คืออัลกอริทึมที่ฉันส่งต่อจาก Quasimondo มันเป็นการผสมผสานระหว่างกล่องเบลอกับเบลอเกาส์มันสวยมากและเร็วเกินไป

อัปเดตสำหรับผู้ที่พบปัญหา ArrayIndexOutOfBoundsException: @anthonycr ในความคิดเห็นแสดงข้อมูลนี้:

ฉันพบว่าการแทนที่ Math.abs ด้วย StrictMath.abs หรือการใช้งาน abs อื่น ๆ ความผิดพลาดจะไม่เกิดขึ้น

/**
 * 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
ขอบคุณ Yahel คุณแก้ปัญหาของฉัน ขอบคุณอีกครั้ง.
Yog Guru

1
ฉันควรผ่านอะไรในรัศมี
krisDrOid

16
สำหรับรัศมีที่มากกว่า 1 ในบางครั้งคุณอาจได้รับ ArrayIndexOutOfBoundsException ฉันจะพยายามระบุปัญหา
MikeL

7
@MichaelLiberman ฉันยังพบปัญหาเดียวกันพบว่าทำไมยัง "g [yi] = dv [gsum];" -> ข้อผิดพลาด: java.lang.ArrayIndexOutOfBoundsException: length = 112896; index = 114021
see2851

2
วิ่งเข้าไปใน ArrayIndexOutOfBoundsException ที่เป็นที่รู้จักและหลังจากการวิเคราะห์บางอย่างฉันเชื่อว่ามันเกิดจากการปรับแต่งที่ไม่ถูกต้องโดย Dalvik VM ในforวงทันทีก่อน dereference ไม่ดีทั้งการคำนวณของrbsตัวแปรหรือการคำนวณของgsum, rsumหรือbsumตัวแปรจะไม่ได้สิทธิทำ ฉันพบว่าการแทนที่Math.absด้วยStrictMath.absหรือabsการใช้งานอื่น ๆความผิดพลาดจะไม่เกิดขึ้น เนื่องจากStrictMath.absผู้ได้รับมอบหมายเองMath.absดูเหมือนว่าจะต้องเป็นการเพิ่มประสิทธิภาพที่ไม่ดี
anthonycr

255

Android Blur Guide 2016

มีตู้โชว์ / เกณฑ์มาตรฐาน Appและแหล่งที่มาบน Github ยังตรวจสอบกรอบเบลอฉันกำลังทำงานเมื่อ: ต้าหลี่

หลังจากทดลองมากฉันสามารถให้คำแนะนำที่มั่นคงที่จะทำให้ชีวิตของคุณง่ายขึ้นใน Android เมื่อใช้ Android Framework

โหลดและใช้บิตแมป downscaled (สำหรับภาพที่พร่ามัวมาก)

อย่าใช้บิตแมปขนาดเต็ม ยิ่งภาพยิ่งใหญ่ต้องเบลอมากเท่าไหร่ก็จะยิ่งจำเป็นต้องมีรัศมีภาพเบลอมากขึ้นเท่านั้นและยิ่งรัศมีเบลอมากเท่าไหร่

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

สิ่งนี้จะโหลดบิตแมปด้วยinSampleSize8 ดังนั้นจะมีเพียง 1/64 ของภาพต้นฉบับ ทดสอบสิ่งที่inSampleSizeเหมาะสมกับความต้องการของคุณ แต่เก็บไว้ 2 ^ n (2,4,8, ... ) เพื่อหลีกเลี่ยงการลดคุณภาพเนื่องจากการปรับขนาด ดู Google เอกสารเพิ่มเติม

ข้อดีอีกข้อที่ใหญ่มากคือการโหลดบิตแมปจะเร็วมาก ๆ ในการทดสอบการเบลอในช่วงต้นฉันคิดว่าเวลาที่ยาวที่สุดในระหว่างกระบวนการเบลอทั้งหมดคือการโหลดภาพ ดังนั้นในการโหลดภาพขนาด 1920x1080 จากดิสก์ Nexus 5 ของฉันต้องใช้ 500 มิลลิวินาทีในขณะที่การเบลอนั้นใช้เวลาเพียง 250 มิลลิวินาทีหรือมากกว่านั้น

ใช้ Renderscript

Renderscript ให้ScriptIntrinsicBlurซึ่งเป็นตัวกรองเบลอ Gaussian มันมีคุณภาพของภาพที่ดีและเร็วที่สุดเท่าที่คุณจะทำได้บน Android Google อ้างว่าเป็น"มักจะ 2-3x เร็วกว่าการใช้งานแบบมัลติเธรด C และมักจะ 10x + เร็วกว่าการใช้ Java" Renderscript นั้นซับซ้อนมาก (ใช้อุปกรณ์ประมวลผลที่เร็วที่สุด (GPU, ISP, ฯลฯ ) ฯลฯ ) และยังมีไลบรารีสนับสนุน v8 สำหรับทำให้สามารถทำงานร่วมกันได้ถึง 2.2. อย่างน้อยก็ในทางทฤษฎีผ่านการทดสอบของฉันเองและรายงานจาก devs อื่น ๆ ดูเหมือนว่ามันเป็นไปไม่ได้ที่จะใช้ Renderscript สุ่มสี่สุ่มห้าเนื่องจากการกระจายตัวของฮาร์ดแวร์ / ไดรเวอร์ดูเหมือนจะทำให้เกิดปัญหากับอุปกรณ์บางอย่างแม้จะมี sdk lvl ที่สูงขึ้น ปัญหากับ 4.1 Nexus S) ดังนั้นควรระมัดระวังและทดสอบกับอุปกรณ์จำนวนมาก นี่คือตัวอย่างง่ายๆที่จะช่วยให้คุณเริ่มต้น:

//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 กับ Gradle ซึ่งแนะนำโดย Google โดยเฉพาะ"เพราะมีการปรับปรุงล่าสุด"คุณจะต้องเพิ่ม 2 บรรทัดในสคริปต์การสร้างของคุณและใช้android.support.v8.renderscriptกับเครื่องมือสร้างปัจจุบัน ( ไวยากรณ์ที่อัปเดตสำหรับ android Gradle plugin v14 + )

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

เกณฑ์มาตรฐานอย่างง่ายบน Nexus 5 - การเปรียบเทียบ RenderScript กับการใช้งาน Java และ Renderscript อื่น ๆ :

รันไทม์เฉลี่ยต่อเบลอในขนาดรูปที่แตกต่างกัน รันไทม์เฉลี่ยต่อเบลอในขนาดรูปที่แตกต่างกัน

ล้านพิกเซลต่อวินาทีที่สามารถเบลอได้ ล้านพิกเซลต่อวินาทีที่สามารถเบลอได้

แต่ละค่าคือค่าเฉลี่ยของ 250 รอบ RS_GAUSS_FASTคือScriptIntrinsicBlur(และเกือบจะเร็วที่สุดเสมอ) ส่วนอื่น ๆ ที่เริ่มต้นด้วยRS_ส่วนใหญ่จะทำให้เกิดความมั่นใจในการใช้งานกับเมล็ดอย่างง่าย รายละเอียดของขั้นตอนวิธีที่สามารถพบได้ที่นี่ นี่ไม่ใช่การเบลออย่างแท้จริงเนื่องจากส่วนที่ดีคือการรวบรวมขยะที่วัด สิ่งนี้สามารถเห็นได้ที่นี่ ( ScriptIntrinsicBlurในภาพขนาด 100x100 ที่มีประมาณ 500 รอบ)

ป้อนคำอธิบายรูปภาพที่นี่

เดือยแหลมเป็น gc

คุณสามารถตรวจสอบด้วยตัวคุณเองแอพอ้างอิงมาตรฐานอยู่ใน playstore: BlurBenchmark

ใช้ Bitmap ซ้ำทุกที่ที่ทำได้

หากคุณต้องการพร่าเลือนหลายครั้งสำหรับการเบลอสดหรือที่คล้ายกันและหน่วยความจำของคุณอนุญาตให้ไม่โหลดบิตแมปจาก drawable หลาย ๆ ครั้ง แต่ให้ "แคช" ไว้ในตัวแปรสมาชิก ในกรณีนี้พยายามใช้ตัวแปรเดียวกันเสมอเพื่อเก็บขยะให้น้อยที่สุด

ตรวจสอบinBitmapตัวเลือกใหม่เมื่อโหลดจากไฟล์หรือ drawable ซึ่งจะนำหน่วยความจำบิตแมปไปใช้ซ้ำและประหยัดเวลาในการรวบรวมขยะ

สำหรับการผสมจากคมชัดไปจนถึงพร่ามัว

วิธีที่ง่ายและไร้เดียงสาเพียงแค่ใช้ 2 ImageViewsวิธีหนึ่งเบลอและอัลฟาจะจางหายไป แต่ถ้าคุณต้องการดูทันสมัยมากขึ้นที่ราบรื่นจางหายไปจากคมเบลอแล้วตรวจสอบการโพสต์โรมัน Nurik เกี่ยวกับวิธีที่จะทำเช่นเดียวกับในแอปพลิเค

โดยทั่วไปเขาอธิบายว่าเขาเตรียมพร่ามัวเฟรมบางภาพที่มีขอบเขตพร่ามัวแตกต่างกันและใช้เป็นเฟรมหลักในภาพเคลื่อนไหวที่ดูราบรื่นจริงๆ

แผนผังที่ Nurik exaplains วิธีการของเขา


1
ก่อนอื่นขอขอบคุณสำหรับการทำงานอย่างหนักของคุณ! แต่ฉันมีคำถาม: "เพราะใช้โหมด USAGE_SHARED ซึ่งนำหน่วยความจำกลับมาใช้" คุณพบค่าคงที่ USAGE_SHARED ที่ไหน ฉันไม่พบที่ใดก็ได้
นักเรียน Noob คนที่

2
ฉันพบว่า USAGE_SHARED มีเฉพาะใน support.v8.renderscript
นักศึกษา Noob บางคน

2
Rurscript fast Gaussian blur ล้มเหลวด้วยข้อผิดพลาดในการจัดสรรหน่วยความจำ C บนอุปกรณ์ระดับล่าง ทดสอบกับ ZTE Z992 (Android 4.1.1) และ Kyocera Rise (Android 4.0.4) โดยใช้แอพ Play Store ที่มีให้ นอกจากนี้ยังมีรายงานความล้มเหลวของ Samsung Galaxy S3 mini เนื่องจากข้อผิดพลาดเกิดขึ้นในเลเยอร์ C พวกเขาไม่สามารถถูกดักจับเป็นข้อยกเว้นใน Java ซึ่งหมายความว่าแอพผิดพลาดไม่สามารถหลีกเลี่ยงได้ ดูเหมือนว่า RenderScript อาจไม่พร้อมสำหรับการใช้งานจริง
Theo

4
สำหรับรุ่น gradle ที่ใหม่กว่าใช้renderscriptSupportModeEnabled trueหรือจะไม่สร้าง! ฉันค้นหาตลอดกาล!
seb

3
เมื่อฉันพยายามแก้ปัญหานี้แทนที่จะได้รับบิตแมปเบลอผมได้บิตแมปสีรุ้ง ใครบ้างที่ประสบปัญหานี้ ถ้าเป็นเช่นนั้นคุณแก้ไขได้อย่างไร?
HaloMediaz

53

แก้ไข (เมษายน 2014):นี่คือหน้าคำถาม / คำตอบที่ยังคงได้รับความนิยมจำนวนมากดูเหมือนว่า ฉันรู้ว่าฉันได้รับ upvotes เสมอสำหรับโพสต์นี้ แต่ถ้าคุณอ่านข้อความนี้คุณต้องตระหนักถึงคำตอบที่โพสต์ไว้ที่นี่ (ทั้งของฉันและคำตอบที่ยอมรับได้) ล้าสมัย หากคุณต้องการที่จะใช้ภาพเบลอที่มีประสิทธิภาพวันนี้ , คุณควรใช้ RenderScriptแทน NDK หรือ Java RenderScript ทำงานบน Android 2.2 ขึ้นไป (ใช้ห้องสมุดสนับสนุน Android ) ดังนั้นจึงไม่มีเหตุผลที่จะไม่ใช้มัน

คำตอบเก่านั้นตามมา แต่ระวังเมื่อมันล้าสมัย


สำหรับอนาคต²ชาว Google นี่คืออัลกอริทึมที่ฉันพอร์ตจากอัลกอริทึมของ Quasimondo ของ Yahel แต่ใช้ NDK แน่นอนขึ้นอยู่กับคำตอบของ Yahel แน่นอน แต่นี่คือการเรียกใช้รหัส C ดั้งเดิมดังนั้นจึงเร็วขึ้น เร็วขึ้นมาก ชอบเร็วขึ้น 40 เท่า

ฉันพบว่าการใช้ NDK เป็นวิธีการจัดการภาพทั้งหมดที่ควรทำบน Android ... มันค่อนข้างน่ารำคาญที่จะใช้ในตอนแรก (อ่านบทช่วยสอนที่ดีเกี่ยวกับการใช้ JNI และ NDK ที่นี่ ) แต่ดีกว่ามากและใกล้เวลาจริงสำหรับ สิ่งต่างๆมากมาย.

สำหรับการอ้างอิงโดยใช้ฟังก์ชั่นจาวาของ Yahel นั้นใช้เวลา 10 วินาทีในการเบลอภาพ 480x532 พิกเซลของฉันด้วยรัศมีเบลอ 10 แต่ใช้เวลา 250 มิลลิวินาทีโดยใช้เวอร์ชั่น C ดั้งเดิม และฉันค่อนข้างแน่ใจว่ามันยังสามารถเพิ่มประสิทธิภาพได้อีก ... ฉันเพิ่งแปลงรหัสจาวาเป็นใบ้อาจมีการดัดแปลงบางอย่างที่สั้นลงไม่ต้องการใช้เวลามากเกินไปในการฟื้นฟูสิ่งใหม่ทั้งหมด

#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 และฟังก์ชันเนทิฟที่เรียกว่า functionToBlur เป็นรหัสที่อยู่เหนือสถานะ):

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

คาดว่าบิตแมป RGB_8888!

หากต้องการใช้บิตแมป RGB_565 ให้สร้างสำเนาที่แปลงแล้วก่อนส่งพารามิเตอร์ (yuck) หรือเปลี่ยนฟังก์ชั่นเพื่อใช้rgb565ประเภทใหม่แทนrgba:

typedef struct {
    uint16_t byte0;
} rgb565;

ปัญหาคือว่าถ้าคุณทำสิ่งที่คุณไม่สามารถอ่าน.red, .greenและ.blueของพิกเซลอีกต่อไปคุณจะต้องอ่านไบต์ถูกต้อง duh เมื่อฉันต้องการสิ่งนั้นมาก่อนฉันทำสิ่งนี้:

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

แต่อาจจะมีวิธีที่ทำให้โง่น้อยลง ฉันไม่ได้เป็น coder ระดับต่ำมากฉันกลัว


ขอขอบคุณมันช่วยฉันได้มาก :)
Dmitry Zaytsev

18
แต่มันต้องใช้จำนวนมากของหน่วยความจำ เพื่อลดหน่วยความจำประเภทการเปลี่ยนแปลงการบริโภคr[wh], g[wh]และการb[wh] uint8_t
Dmitry Zaytsev

คุณสามารถแสดงให้ฉันเห็นว่าไฟล์ Android.mk ของคุณเป็นอย่างไร, บนpastebin.com
CQM

1
สำหรับตัวอย่าง RenderScript ที่ใช้งานได้ของ Gaussian Blur บน Android SDK 17+ ดูที่นี่: stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini

2
RenderScript ยังมีให้เป็นส่วนหนึ่งของ lib การสนับสนุนสำหรับ Android 2.2 ขึ้นไปดังนั้นจึงไม่มีเหตุผลที่จะไม่ใช้มันทุกที่อีกต่อไป: android-developers.blogspot.com/2013/09/39
zeh

14

รหัสนี้เหมาะสำหรับฉัน

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

}

วิธีที่ง่ายที่สุดในการทำให้ภาพเบลอ (y)
VAdaihiep

12

ตอนนี้คุณสามารถใช้ScriptIntrinsicBlurจากไลบรารี RenderScript เพื่อเบลออย่างรวดเร็ว นี่คือวิธีการเข้าถึง RenderScript API ต่อไปนี้เป็นคลาสที่ฉันทำเพื่อทำให้เบลอมุมมองและบิตแมป:

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
ไม่ควรสร้างบริบท Renderscript ในวิธีการเบลอ แต่จัดการแบบสแตติกหรือกำหนดให้กับเมธอด (ถ้าคุณคำนึงถึงประสิทธิภาพ)
Patrick Favre

1
คุณสามารถยกตัวอย่างการใช้งานนี้ได้หรือไม่? เมื่อฉันพยายามใช้ฉันได้รับข้อผิดพลาดต่อไปนี้: java.lang.IllegalArgumentException: ความกว้างและความสูงต้องเป็น> 0
Donal Rafferty

10

สิ่งนี้ใช้ได้ผลดีสำหรับฉัน: วิธีทำให้ภาพเบลออย่างมีประสิทธิภาพด้วย Android ของ RenderScript

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

สิ่งนี้จะดียิ่งขึ้น / เร็วขึ้นหากคุณแคชวัตถุ RenderScript ที่คุณสร้างขึ้น สร้างใหม่ทันทีทุกครั้งที่คุณต้องการทำให้ภาพเบลอเพียงแค่เพิ่มค่าใช้จ่ายที่ไม่จำเป็น (การสร้าง / ทำลายวัตถุ Java)
Stephen Hines


4

นี่คือสำหรับทุกคนที่ต้องการเพิ่มรัศมีของ ScriptIntrinsicBlurการรับ gaussian เบลอยากขึ้น

แทนที่จะใส่รัศมีมากกว่า 25 คุณสามารถย่อขนาดรูปภาพและได้ผลลัพธ์เดียวกัน ฉันเขียนชั้นเรียนที่เรียกว่าGaussianBlurผมเขียนระดับที่เรียกว่าด้านล่างคุณจะเห็นวิธีการใช้งานและการใช้งานทั้งชั้นเรียน

การใช้งาน:

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

ประเภท:

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

ไม่ถ้าคุณลดขนาดภาพเพื่อขยายภาพในภายหลังคุณจะดึงภาพบล็อกแทนภาพที่มีการเบลอ :(
loki

4

ขอบคุณ @Yahel สำหรับรหัส การโพสต์วิธีเดียวกันด้วยการสนับสนุนการเบลอช่องอัลฟาเพราะใช้เวลาพอสมควรที่จะทำให้มันทำงานได้อย่างถูกต้องดังนั้นมันอาจช่วยประหยัดเวลาของใครบางคน:

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

ยังคงมี 'ดัชนีอยู่นอกช่วง' ในอุปกรณ์> hdpi เป็นแหล่งที่มาดั้งเดิม
Gabriel Ferreira

4

ฉันเคยใช้มันมาก่อน ..

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

สำหรับชาว Google ในอนาคตที่เลือกวิธี NDK - ฉันพบอัลกอริทึม stackblur ที่กล่าวถึงอย่างน่าเชื่อถือ ฉันพบการใช้งาน C ++ ซึ่งไม่ขึ้นอยู่กับ SSE ที่นี่ - http://www.antigrain.com/__code/include/agg_blur.h.html#stack_blur_rgba32ซึ่งประกอบด้วยการเพิ่มประสิทธิภาพบางอย่างโดยใช้ตารางคงที่เช่น:

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

ฉันทำการปรับเปลี่ยนอัลกอริทึม stackblur สำหรับระบบมัลติคอร์ - มันสามารถพบได้ที่นี่http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ที่นี่ มีอุปกรณ์มากขึ้นเรื่อย ๆ ประโยชน์ความเร็ว 4x


1

คำแนะนำของ Nicolas POMEPUY ฉันคิดว่าลิงก์นี้จะมีประโยชน์: เอฟเฟกต์เบลอสำหรับการออกแบบ Android

ตัวอย่างโครงการที่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;
}

แม้ว่าลิงก์นี้อาจตอบคำถามได้ดีกว่าหากรวมส่วนสำคัญของคำตอบไว้ที่นี่และให้ลิงก์สำหรับการอ้างอิง คำตอบสำหรับลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง
Amal Murali

1
Amal Murali คุณพูดถูก ตอนนี้เปลี่ยนโพสต์ของฉัน ดีที่คุณไม่เพียง แต่ลงคะแนน แต่ยังแสดงความคิดเห็น
Yura Shinkarev

1

เราพยายามใช้เบลอของ RenderScript อย่างที่กล่าวไว้ข้างต้นในคำตอบที่ต่างกัน เราถูก จำกัด ให้ใช้รุ่น v8 RenderScript และนั่นทำให้เรามีปัญหามากมาย

  • Samsung S3 เกิดปัญหาแบบสุ่มทุกครั้งที่เราพยายามใช้ renderscript
  • อุปกรณ์อื่น ๆ (ข้าม API ที่แตกต่างกัน) สุ่มพบปัญหาสีที่แตกต่างกัน

ฉันต้องการแบ่งปันรุ่น Java-only ที่สกปรกของเราซึ่งช้าและควรทำในเธรดแยกต่างหากและถ้าเป็นไปได้ก่อนการใช้งานและยังคงมีอยู่

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

วิธีการแก้ปัญหานี้ยังห่างไกลจากความสมบูรณ์แบบ แต่สร้างเอฟเฟ็กต์เบลอที่เหมาะสมตามความเป็นจริงซึ่งทำให้ได้ภาพที่มีความโปร่งใสสูงของภาพเดียวกันบน "ชาร์ป" ที่โปร่งใส อัลฟาขึ้นอยู่กับระยะทางไปยังจุดกำเนิด

คุณสามารถปรับ "หมายเลขเวทย์มนตร์" ได้ตามต้องการ ฉันแค่อยากจะแบ่งปัน "การแก้ปัญหา" สำหรับทุกคนที่มีปัญหากับ RenderScript รุ่นที่รองรับ v8


ฉันยังมีปัญหากับ RenderScript ในอุปกรณ์ armeabi เก่าบางรุ่น แต่โซลูชันของคุณใช้หน่วยความจำมากเกินไป
AsafK

0

สำหรับผู้ที่ยังคงมีปัญหากับไลบรารีการสนับสนุนของ Renderscript บนชิปเซ็ต x86 โปรดดูที่โพสต์นี้โดยผู้สร้างของห้องสมุด ดูเหมือนว่าการแก้ไขที่เขาเตรียมไว้ไม่ได้ทำให้ Build Tools v20.0.0 เป็นอย่างใดดังนั้นเขาจึงจัดเตรียมไฟล์เพื่อแก้ไขด้วยตนเองและคำอธิบายโดยย่อเกี่ยวกับวิธีการทำ

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




0

ฉันพบว่าการลดความคมชัดความสว่างและความอิ่มตัวเพียงเล็กน้อยทำให้ภาพเบลอสวยขึ้นดังนั้นฉันจึงรวมวิธีการต่าง ๆ จากการล้นสแต็กและทำให้Blur Class นี้เกี่ยวข้องกับภาพเบลอการเปลี่ยนความสว่างความอิ่มตัวความคมชัดและขนาดของภาพเบลอ นอกจากนี้ยังสามารถแปลงรูปภาพจาก drawable เป็น bitmap และในทางกลับกัน


0

ใน i / o 2019 มีการนำเสนอโซลูชันต่อไปนี้:

/**
 * 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()
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.