현재 개발중인 Android 응용 프로그램에서 이미지의 픽셀을 반복하여 이미지를 흐리게 처리합니다. 640x480 이미지에서 약 30 초가 걸립니다.
안드로이드 마켓에서 앱을 탐색하는 동안 블러 기능이 포함 된 앱을 발견했으며 블러가 매우 빠르기 때문에 (5 초 정도) 다른 블러 링 방법을 사용해야합니다.
픽셀을 반복하는 것보다 더 빠른 방법을 아는 사람이 있습니까?
현재 개발중인 Android 응용 프로그램에서 이미지의 픽셀을 반복하여 이미지를 흐리게 처리합니다. 640x480 이미지에서 약 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);
}
for
루프 즉시 나쁜 참조 해제되기 전에 계산 어느 rbs
변수 또는 계산 gsum
, rsum
또는 bsum
변수 오른쪽 완료되는 것은 아니다. 나는 대체하여 발견 Math.abs
으로 StrictMath.abs
또는 다른 abs
구현, 충돌이 발생하지 않습니다. StrictMath.abs
자체가에 위임 했기 때문에 Math.abs
최적화가 좋지 않은 것 같습니다.
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);
비트 맵이 inSampleSize
8로 로드 되므로 원본 이미지의 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
라이브 블러 또는 이와 유사한 경우 여러 블러가 필요한 경우 메모리에서 드로어 블에서 비트 맵을 여러 번로드하지 않고 멤버 변수에 "캐시"된 상태로 유지하십시오. 이 경우 가비지 수집을 최소화하기 위해 항상 동일한 변수를 사용하십시오.
또한 파일 또는 드로어 블에서 inBitmap
로드 할 때 비트 맵 메모리를 재사용하고 가비지 수집 시간을 절약 할 수 있는 새 옵션을 확인하십시오 .
간단하고 순진한 방법은 2 ImageViews
, 하나를 흐리게 사용 하고 알파를 희미하게하는 것입니다. 그러나 날카로운 것에서 흐릿한 것으로 부드럽게 희미 해지는보다 정교한 모양을 원한다면 Roman Nurik의 게시물 을 확인하여 Muzei 앱과 같은 방법을 확인하십시오 .
기본적으로 그는 블러 범위가 다른 일부 프레임을 미리 흐리게 처리하여 애니메이션에서 매끄럽게 보이는 키 프레임으로 사용한다고 설명합니다.
renderscriptSupportModeEnabled true
하거나 빌드하지 않습니다! 나는 영원히 검색했다!
편집 (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 코더가별로 두렵지 않다.
r[wh]
, g[wh]
그리고 b[wh]
로를 uint8_t
.
pastebin.com
이 코드는 나에게 완벽하게 작동합니다.
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;
}
}
여기에 언급 된대로 렌더 스크립트를 사용하십시오 http://blog.neteril.org/blog/2013/08/12/blurring-images-on-android/
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;
}
}
코드에 감사드립니다. 알파 채널 블러 링 지원 과 동일한 방법을 게시하면 제대로 작동하는 데 시간이 걸리므로 다른 사람의 시간을 절약 할 수 있습니다.
/**
* 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);
}
나는 이것을 전에 사용했다 ..
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;
}
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 배속 혜택.
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;
}
위에서 언급 한 것처럼 RenderScript 흐림 효과를 다른 답변으로 구현하려고했습니다. 우리는 v8 RenderScript 버전을 사용하도록 제한되었고 많은 문제를 일으켰습니다.
더러운 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에 문제가있는 모든 사람에게 "솔루션"을 공유하고 싶었습니다.
x86 칩셋의 렌더 스크립트 지원 라이브러리에 여전히 문제가있는 사람은 라이브러리 작성자가이 게시물을 살펴보십시오. 그가 준비한 수정 사항이 Build Tools v20.0.0을 수정하지 않은 것으로 보이므로 수동으로 수정하기위한 파일과이를 수행하는 방법에 대한 간단한 설명을 제공합니다.
Mario Viviani 블로그에서 17 Android 버전 의이 솔루션을 사용할 수 있습니다.
https://plus.google.com/+MarioViviani/posts/fhuzYkji9zz
또는
다음은 RenderScript를 사용한 실시간 블러 링 오버레이입니다.
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()
}
}