这是一个问答页面,似乎仍有很多点击量。我知道我一直在为这个职位争取选票。但如果你在读这篇文章,你需要意识到在这里张贴的答案(我的和接受的答案)都过时了。如果你想实现高效的模糊今天, 您应该使用Renderscript而不是NDK或Java。Renderscript运行在Android2.2+上(使用Android支持库),所以没有理由不使用它。
旧的答案随之而来,但要小心,因为它已经过时了。
对于未来的谷歌用户,这是我从Yahel的Quasimondo算法端口移植的算法,但使用的是NDK。当然,这是基于雅赫尔的回答。但这是运行本机C代码,所以它更快。快多了。大概快40倍。
我发现使用NDK是所有图像处理应该在Android上进行的方式.一开始实现起来有点烦人(请阅读关于使用JNI和NDK的一个很好的教程这里),但要好得多,而且在很多事情上几乎是实时的。
作为参考,使用Yahel的Java函数,用了10秒的时间模糊了我的480x532像素图像,模糊半径为10,但使用本机C版本需要250 ms。我很确定它还能被进一步优化.。我只是对java代码做了一个简单的转换,可能有一些操作可以缩短,不想花费太多的时间来重构整个过程。#include #include #include #include #include #include #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))
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))
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
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
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
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
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
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0; 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.sert.your.Package.ClassName的类和一个名为FunctionToBlur的本机函数,如上面的代码所述):// Create a copyBitmap bitmapOut = bitmapIn.copy(Bitmap.Config.ARGB_8888, true);// Blur the copyfunctionToBlur(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) <
但可能有一些不那么愚蠢的方法。恐怕我不是什么低级的C级编码器。