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


185

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

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

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


2
불행히도 이미지는 항상 다르므로 미리 흐리게 버전을 만들 수 없습니다. 또한 흐림 강도를 미리 알 수 없습니다.
Greg

코드를 게시 할 수 있습니까? 아마도 알고리즘 / 코드가 비효율적이며 640x480 이미지를 통과하는 데 30 초가 느립니다 .5 초가 느리다고 생각했지만 다시 프로세서에 달려 있습니다.
vickirk

답변:


78

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


5
몇 가지 테스트와 내가 실제로하고있는 흐리게 처리하면 나에게 충분히 잘 작동하며 빠릅니다. 감사!
Greg

4
잘 작동하면. 그것이 비효율적 인 이유에 대해 우리가 결코 알지 못하는 것은 부끄러운 일입니다.
vickirk

createScaledBitmap을 시도하고 이미지의 크기를 동일하게 유지하려고 할 수 있습니다. 나를 위해 그것을 흐리게하고있다 :-(
Casebash

1
"filter"인수의 의미에 대한 설명은 다음과 같습니다. stackoverflow.com/questions/2895065
user1364368

4
이것은 두 가지 이유로 인해 얻는 방법이 아닙니다 .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 2016

1
반경으로 무엇을 통과해야합니까?
krisDrOid

16
반경이 1보다 큰 경우 때때로 ArrayIndexOutOfBoundsException이 발생합니다. 나는 문제를 식별하려고 노력할 것이다.
MikeL

7
@MichaelLiberman 나는 또한 같은 문제가 발생했습니다. 아직 왜 그 이유를 알았습니까? "g [yi] = dv [gsum];" -> 오류 : java.lang.ArrayIndexOutOfBoundsException : length = 112896; 인덱스 = 114021
see2851

2
알려진 ArrayIndexOutOfBoundsException에 부딪 히고 일부 분석 후 Dalvik VM의 잘못된 최적화로 인한 것이라고 생각합니다. 에서 for루프 즉시 나쁜 참조 해제되기 전에 계산 어느 rbs변수 또는 계산 gsum, rsum또는 bsum변수 오른쪽 완료되는 것은 아니다. 나는 대체하여 발견 Math.abs으로 StrictMath.abs또는 다른 abs구현, 충돌이 발생하지 않습니다. StrictMath.abs자체가에 위임 했기 때문에 Math.abs최적화가 좋지 않은 것 같습니다.
anthonycr

255

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

또 다른 큰 장점은 비트 맵 로딩이 정말 빠르다는 것입니다. 초기 블러 테스트에서 전체 블러 프로세스 중 가장 긴 시간이 이미지로드라고 생각했습니다. 디스크에서 1920x1080 이미지를로드하려면 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 라운드의 100x100 이미지)

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

스파이크는 gc입니다.

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

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

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

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

샤프에서 블러로 블렌딩

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

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

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


1
여러분의 노고에 감사드립니다! 그러나 "메모리를 재사용하는 USAGE_SHARED 모드를 사용하기 때문에"라는 질문이 있습니다. 상수 USAGE_SHARED를 어디에서 찾았습니까? 어디서나 찾을 수 없었습니다.
일부 멍청한 학생

2
USAGE_SHARED는 support.v8.renderscript에서만 사용할 수 있음을 발견했습니다
일부 멍청한 학생

2
로우 엔드 장치에서 C 메모리 할당 오류로 렌더링 스크립트 빠른 가우시안 블러가 실패합니다. 제공된 Play Store 앱을 사용하여 ZTE Z992 (Android 4.1.1) 및 Kyocera Rise (Android 4.0.4)에서 테스트되었습니다. 또한 Samsung Galaxy S3 mini에 대한 실패 보고서가있었습니다. C 계층에서 오류가 발생하므로 Java에서 예외로 트랩 될 수 없으므로 앱 충돌을 피할 수 없습니다. RenderScript가 프로덕션 용으로 준비되지 않은 것 같습니다.
Theo

4
최신 gradle 버전의 경우 사용 renderscriptSupportModeEnabled true하거나 빌드하지 않습니다! 나는 영원히 검색했다!
seb

3
흐린 비트 맵을 얻는 대신이 솔루션을 시도했을 때 무지개 색의 비트 맵을 얻었습니다 . 다른 사람이이 문제를 경험합니까? 그렇다면 어떻게 고쳤습니까?
HaloMediaz

53

편집 (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 인 480x532 픽셀 이미지를 흐리게하는 데 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 코더가별로 두렵지 않다.


고마워, 그것은 나에게 많은 도움이되었습니다 :)
Dmitry Zaytsev

18
그러나 많은 메모리 가 필요 합니다 . 의 메모리 소비 변경 유형 줄이기 위해 r[wh], g[wh]그리고 b[wh]로를 uint8_t.
Dmitry Zaytsev

당신은 당신의 Android.mk 파일 외모에, 좋아 저를 보여줄 수pastebin.com
CQM

1
Android SDK 17+에서 작동하는 가우시안 블러의 RenderScript 예제는 여기를 참조하십시오 : stackoverflow.com/questions/14988990/android-fast-bitmap-blur
Martin Marconcini

2
렌더 스크립트는 안드로이드 2.2 이상 지원 라이브러리의 일부로도 제공되므로 더 이상 어디서나 사용하지 않을 이유는 없습니다 : android-developers.blogspot.com/2013/09/…
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

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

2
렌더 스크립트 컨텍스트는 blur 메소드에서 작성해서는 안되며 정적으로 관리되거나 메소드에 제공되어야합니다. (퍼포먼스가 마음에 들면)
Patrick Favre

1
이 사용 예를 줄 수 있습니까? 사용하려고하면 다음 오류가 발생합니다. java.lang.IllegalArgumentException : 너비와 높이가 0보다
커야

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더 가우시안 블러를 얻기 위해 반경을 늘려야하는 모든 사람들을위한 것 입니다.

반경을 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

코드에 감사드립니다. 알파 채널 블러 링 지원 과 동일한 방법을 게시하면 제대로 작동하는 데 시간이 걸리므로 다른 사람의 시간을 절약 할 수 있습니다.

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

NDK 접근법을 선택하는 미래 Google 직원에게는 신뢰할 수있는 스택 블러 알고리즘이 있습니다. 여기 SSE에 의존하지 않는 C ++ 구현을 발견 - 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
}; 

멀티 코어 시스템의 스택 블러 알고리즘을 수정했습니다. http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/ 더 많은 장치에 4 개의 코어가 있으므로 최적화는 4 배속 혜택.


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

이 링크가 질문에 대답 할 수 있지만 여기에 답변의 필수 부분을 포함시키고 참조 할 수있는 링크를 제공하는 것이 좋습니다. 링크 된 페이지가 변경되면 링크 전용 답변이 유효하지 않을 수 있습니다.
아말 무랄리

1
아말 무랄리, 당신 말이 맞아요 이제 내 게시물을 변경하십시오. 좋은 의견, 당신은 downvote뿐만 아니라 의견도 있습니다.
Yura Shinkarev

1

위에서 언급 한 것처럼 RenderScript 흐림 효과를 다른 답변으로 구현하려고했습니다. 우리는 v8 RenderScript 버전을 사용하도록 제한되었고 많은 문제를 일으켰습니다.

  • renderscript를 사용하려고 할 때마다 Samsung S3가 무작위로 추락했습니다.
  • 다른 API (다른 API)에서 다른 색상 문제가 무작위로 표시됨

더러운 Java 전용 버전을 공유하고 싶습니다. 속도가 느리고 별도의 스레드에서 수행해야하며 가능한 경우 사용하기 전에 유지되어야합니다.

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

이 솔루션은 완벽하지는 않지만 거의 투명한 "날카로운"버전 위에 동일한 이미지의 투명도가 높은 버전을 그립니다. 알파는 원점까지의 거리에 따라 다릅니다.

필요에 따라 일부 "매직 숫자"를 조정할 수 있습니다. v8 지원 버전의 RenderScript에 문제가있는 모든 사람에게 "솔루션"을 공유하고 싶었습니다.


일부 구형 armeabi 장치에서 RenderScript에 문제가 있지만 솔루션이 너무 많은 메모리를 소비합니다.
AsafK

0

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

콘트라스트, 밝기 및 채도 를 약간 낮추면 흐린 이미지가 더 예쁘게 보이므로 스택 오버플로의 다양한 방법을 결합하고 이미지 흐림, 밝기, 채도, 대비 및 흐린 이미지의 크기를 처리하는 이 블러 클래스 를 만들었습니다 . 또한 이미지를 드로어 블에서 비트 맵으로 변환하거나 그 반대로 변환 할 수도 있습니다.


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.