[java] 안드로이드 SDK를위한 빠른 비트 맵 블러

현재 개발중인 Android 응용 프로그램에서 이미지의 픽셀을 반복하여 이미지를 흐리게 처리합니다. 640×480 이미지에서 약 30 초가 걸립니다.

안드로이드 마켓에서 앱을 탐색하는 동안 블러 기능이 포함 된 앱을 발견했으며 블러가 매우 빠르기 때문에 (5 초 정도) 다른 블러 링 방법을 사용해야합니다.

픽셀을 반복하는 것보다 더 빠른 방법을 아는 사람이 있습니까?



답변

어두운 곳에서 촬영 한 이미지이지만 이미지를 축소 한 다음 다시 확대 할 수 있습니다. 이 작업을 수행 할 수 있습니다 Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter). 필터 매개 변수를 확인하고 true로 설정하십시오. 네이티브 코드로 실행되므로 더 빠를 수 있습니다.


답변

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


답변

Android Blur Guide 2016

Github에서 Showcase / Benchmark 앱소스 사용 .
또한 현재 작업중 인 Blur 프레임 워크를 확인하십시오 : Dali .

많은 실험을 한 후에 Android Framework를 사용할 때 Android에서 더 쉽게 생활 할 수 있도록 확실한 권장 사항을 제공 할 수 있습니다.

축소 된 비트 맵로드 및 사용 (매우 흐릿한 이미지의 경우)

전체 크기의 비트 맵을 사용하지 마십시오. 이미지가 클수록 흐림이 더 많이 필요하고 흐림 반경이 커야하며 일반적으로 알고리즘이 오래 걸릴수록 흐림 반경이 높아집니다.

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 문서를 참조하십시오

또 다른 큰 장점은 비트 맵 로딩이 정말 빠르다는 것입니다. 초기 블러 테스트에서 전체 블러 프로세스 중 가장 긴 시간이 이미지로드라고 생각했습니다. 디스크에서 1920×1080 이미지를로드하려면 Nexus 5에 500ms가 필요했지만 흐림 효과는 250ms 정도 밖에 걸리지 않았습니다.

렌더 스크립트 사용

렌더 스크립트는 ScriptIntrinsicBlur가우시안 블러 필터를 제공합니다. 시각적 품질이 좋으며 Android에서 현실적으로 가장 빠릅니다. 구글은 “일반적으로 멀티 스레드 C 구현보다 2-3 배 빠르며 자바 구현보다 10 배 이상 빠르다”고 주장했다 . 렌더 스크립트는 매우 정교하며 (가장 빠른 처리 장치 (GPU, ISP 등) 사용) 2.2까지 호환되는 v8 지원 라이브러리 도 있습니다. 글쎄, 이론적으로, 다른 개발자의 내 테스트와 보고서를 통해 하드웨어 / 드라이버 조각화가 더 높은 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);

Gradle과 함께 v8 지원을 사용할 때 (특히 최신 개선 사항이 포함되어 있기 때문에) Google에서 권장하는 경우 빌드 스크립트에 2 줄만 추가android.support.v8.renderscript 하고 현재 빌드 도구와 함께 사용해야 합니다 ( Android Gradle 플러그인 v14 + 구문 업데이트 )

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

Nexus 5의 간단한 벤치 마크-다른 Java 및 Renderscript 구현과 다른 RenderScript 비교 :

다른 사진 크기에서 블러 당 평균 런타임
다른 사진 크기에서 블러 당 평균 런타임

흐리게 할 수있는 초당 메가 픽셀
흐리게 할 수있는 초당 메가 픽셀

각 값은 평균 250 라운드입니다. RS_GAUSS_FAST이다 ScriptIntrinsicBlur(거의 항상 가장 빠른)로 시작하는 다른 사람은 RS_간단한 커널과 말다 구현이 대부분이다. 알고리즘에 대한 자세한 내용은 여기를 참조하십시오 . 좋은 부분은 측정 된 가비지 수집이므로 순전히 흐릿하지 않습니다. 이것은 여기에서 볼 수 있습니다 ( ScriptIntrinsicBlur약 500 라운드의 100×100 이미지)

여기에 이미지 설명을 입력하십시오

스파이크는 gc입니다.

벤치 마크 앱은 Playstore에 있습니다. BlurBenchmark

가능한 경우 비트 맵을 재사용합니다 (prio 인 경우 : 성능> 메모리 풋 프린트)

라이브 블러 또는 이와 유사한 경우 여러 블러가 필요한 경우 메모리에서 드로어 블에서 비트 맵을 여러 번로드하지 않고 멤버 변수에 “캐시”된 상태로 유지하십시오. 이 경우 가비지 수집을 최소화하기 위해 항상 동일한 변수를 사용하십시오.

또한 파일 또는 드로어 블에서 inBitmap로드 할 때 비트 맵 메모리를 재사용하고 가비지 수집 시간을 절약 할 수 있는 새 옵션을 확인하십시오 .

샤프에서 블러로 블렌딩

간단하고 순진한 방법은 2 ImageViews, 하나를 흐리게 사용 하고 알파를 희미하게하는 것입니다. 그러나 날카로운 것에서 흐릿한 것으로 부드럽게 희미 해지는보다 정교한 모양을 원한다면 Roman Nurik의 게시물 을 확인하여 Muzei 앱과 같은 방법을 확인하십시오 .

기본적으로 그는 블러 범위가 다른 일부 프레임을 미리 흐리게 처리하여 애니메이션에서 매끄럽게 보이는 키 프레임으로 사용한다고 설명합니다.

Nurik이 접근 방식을 설명하는 다이어그램


답변

편집 (2014 년 4 월) : 이 질문 / 답변 페이지는 여전히 많은 인기를 얻는 것 같습니다. 이 게시물에 대해 항상 투표를하고 있다는 것을 알고 있습니다. 그러나이 글을 읽고 있다면 여기에 게시 된 답변 (나의 답변과 수락 된 답변 모두)이 오래되었다는 것을 알아야합니다. 당신이 효율적으로 흐림 효과 구현하려면 오늘 , 당신은 RenderScript 사용해야하는 대신 NDK 또는 Java를. RenderScript는 Android 2.2 이상 ( Android 지원 라이브러리 사용 )에서 실행되므로 사용하지 않을 이유가 없습니다.

이전 답변은 따르지만 구식이므로주의하십시오.


향후 2 Google 직원을 위해 Yahel의 Quasimondo 알고리즘 포트에서 이식했지만 NDK를 사용하는 알고리즘이 있습니다. 물론 Yahel의 답변을 기반으로합니다. 그러나 이것은 네이티브 C 코드를 실행하므로 더 빠릅니다. 훨씬 더 빨리. 40 배 더 빠릅니다.

NDK를 사용하는 것이 Android에서 모든 이미지 조작을 수행하는 방법이라는 것을 알았습니다 … 처음에는 구현하는 것이 다소 성가시다 (JNI와 NDK 사용에 대한 훌륭한 자습서를 읽으 십시오 ). 많은 것.

참고로 Yahel의 Java 기능을 사용하면 흐림 반경이 10 인 480×532 픽셀 이미지를 흐리게하는 데 10 초가 걸렸습니다. 그러나 네이티브 C 버전을 사용하면 250ms가 걸렸습니다. 그리고 나는 그것이 여전히 더 최적화 될 수 있다고 확신합니다 … 자바로 자바 코드를 바보로 변환했습니다. 어쩌면 단축 될 수있는 조작이있을 수 있으며 전체를 리팩토링하는 데 너무 많은 시간을 소비하고 싶지 않았습니다.

#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더 이상 픽셀의, 당신은 제대로 뜨아를 바이트를 읽을 필요가있다. 내가 전에 그것을 필요로 할 때, 나는 이것을했다 :

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

그러나 아마도 덜 바보 같은 방법이있을 것입니다. 나는 저수준 C 코더가별로 두렵지 않다.


답변

이 코드는 나에게 완벽하게 작동합니다.

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

}


답변

이제 RenderScript 라이브러리에서 ScriptIntrinsicBlur 를 사용 하여 빠르게 흐리게 처리 할 수 ​​있습니다. 다음 은 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;
    }
}


답변

이것은 나를 위해 잘 작동했습니다 : 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;
    }
}