当前位置:   article > 正文

Android Bitmap高斯模糊

Android Bitmap高斯模糊

加载和使用缩小的位图(对于非常模糊的图像)

永远不要使用完整大小的位图。图像越大,需要模糊的越多,模糊半径也需要越高,通常,模糊半径越高,算法所需的时间就越长。

缩小位图的两种方式

1.位图options缩小

  1. BitmapFactory.Options options = new BitmapFactory.Options();
  2. options.inSampleSize = 8;
  3. Bitmap blurTemplate = BitmapFactory.decodeResource(getResources(), bitmap, options);

加载8 位图,因此只有原始图像的 1/64。inSampleSize可以根据需要修改,但需要满足条件 2^n (2,4,8,...) ,以避免因缩放而降低质量。

2.位图Scale缩小

  1. Bitmap bitmap = ...;
  2. float BITMAP_SCALE = 0.4f;
  3. int width = Math.round(bitmap .getWidth() * BITMAP_SCALE);
  4. int height = Math.round(bitmap .getHeight() * BITMAP_SCALE);
  5. // 将缩小后的图片做为预渲染的图片
  6. Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap , width, height, false);

使用渲染脚本模糊

Renderscript 提供
了一个高斯模糊滤镜ScriptIntrinsicBlur。它具有良好的视觉质量,使得位图在 Android上获得的更快速度。Google 声称“通常比多线程 C 实现快 2-3 倍,通常比 Java 实现快 10 倍以上”
。Renderscript 非常复杂(使用最快的处理设备(GPU、ISP 等)等),在理论上,通过测试和其他开发人员的报告,不可盲目地使用 Renderscript,因为硬件/驱动程序碎片似乎会导致某些设备出现问题。

  1. // 图片缩放比例(即模糊度)
  2. private static final float BITMAP_SCALE = 0.4f;
  3. /**
  4. * @param context 上下文对象
  5. * @param srcBitmap 需要模糊的图片
  6. * @return 模糊处理后的Bitmap
  7. */
  8. public static Bitmap blurBitmap(Context context, Bitmap srcBitmap, float blurRadius) {
  9. // 计算图片缩小后的长宽
  10. int width = Math.round(srcBitmap.getWidth() * BITMAP_SCALE);
  11. int height = Math.round(srcBitmap.getHeight() * BITMAP_SCALE);
  12. // 将缩小后的图片做为预渲染的图片
  13. Bitmap inputBitmap = Bitmap.createScaledBitmap(srcBitmap, width, height, false);
  14. // 创建一张渲染后的输出图片
  15. Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
  16. // 创建RenderScript内核对象
  17. RenderScript rs = RenderScript.create(context);
  18. // 创建一个模糊效果的RenderScript的工具对象
  19. ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
  20. // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间
  21. // 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去
  22. Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
  23. Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
  24. // 设置渲染的模糊程度, 25f是最大模糊度
  25. blurScript.setRadius(blurRadius);
  26. // 设置blurScript对象的输入内存
  27. blurScript.setInput(tmpIn);
  28. // 将输出数据保存到输出内存中
  29. blurScript.forEach(tmpOut);
  30. // 将数据填充到Allocation中
  31. tmpOut.copyTo(outputBitmap);
  32. rs.destroy();
  33. return outputBitmap;
  34. }
  1. android {
  2. ...
  3. defaultConfig {
  4. ...
  5. renderscriptTargetApi 19
  6. renderscriptSupportModeEnabled true
  7. }
  8. }


尽可能重用位图(如果优先:性能 > 内存占用)

如果需要多个模糊来进行实时模糊或类似操作,并且内存允许它不会多次从可绘制对象中加载位图,而是将其“缓存”在成员变量中。在这种情况下,请始终尝试使用相同的变量,以将垃圾收集降至最低。

  使用RGB点模糊

java代码(比脚本模糊慢,但是开原程度高)

  1. public static Bitmap fastBlur(Bitmap sentBitmap, float scale, int radius) {
  2. int width = Math.round(sentBitmap.getWidth() * scale);
  3. int height = Math.round(sentBitmap.getHeight() * scale);
  4. sentBitmap = Bitmap.createScaledBitmap(sentBitmap, width, height, false);
  5. Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
  6. if (radius < 1) {
  7. return null;
  8. }
  9. int w = bitmap.getWidth();
  10. int h = bitmap.getHeight();
  11. int[] pix = new int[w * h];
  12. bitmap.getPixels(pix, 0, w, 0, 0, w, h);
  13. int wm = w - 1;
  14. int hm = h - 1;
  15. int wh = w * h;
  16. int div = radius + radius + 1;
  17. int[] r = new int[wh];
  18. int[] g = new int[wh];
  19. int[] b = new int[wh];
  20. int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
  21. int[] vmin = new int[Math.max(w, h)];
  22. int divsum = (div + 1) >> 1;
  23. divsum *= divsum;
  24. int[] dv = new int[256 * divsum];
  25. for (i = 0; i < 256 * divsum; i++) {
  26. dv[i] = (i / divsum);
  27. }
  28. yw = yi = 0;
  29. int[][] stack = new int[div][3];
  30. int stackpointer;
  31. int stackstart;
  32. int[] sir;
  33. int rbs;
  34. int r1 = radius + 1;
  35. int routsum, goutsum, boutsum;
  36. int rinsum, ginsum, binsum;
  37. for (y = 0; y < h; y++) {
  38. rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
  39. for (i = -radius; i <= radius; i++) {
  40. p = pix[yi + Math.min(wm, Math.max(i, 0))];
  41. sir = stack[i + radius];
  42. sir[0] = (p & 0xff0000) >> 16;
  43. sir[1] = (p & 0x00ff00) >> 8;
  44. sir[2] = (p & 0x0000ff);
  45. rbs = r1 - Math.abs(i);
  46. rsum += sir[0] * rbs;
  47. gsum += sir[1] * rbs;
  48. bsum += sir[2] * rbs;
  49. if (i > 0) {
  50. rinsum += sir[0];
  51. ginsum += sir[1];
  52. binsum += sir[2];
  53. } else {
  54. routsum += sir[0];
  55. goutsum += sir[1];
  56. boutsum += sir[2];
  57. }
  58. }
  59. stackpointer = radius;
  60. for (x = 0; x < w; x++) {
  61. r[yi] = dv[rsum];
  62. g[yi] = dv[gsum];
  63. b[yi] = dv[bsum];
  64. rsum -= routsum;
  65. gsum -= goutsum;
  66. bsum -= boutsum;
  67. stackstart = stackpointer - radius + div;
  68. sir = stack[stackstart % div];
  69. routsum -= sir[0];
  70. goutsum -= sir[1];
  71. boutsum -= sir[2];
  72. if (y == 0) {
  73. vmin[x] = Math.min(x + radius + 1, wm);
  74. }
  75. p = pix[yw + vmin[x]];
  76. sir[0] = (p & 0xff0000) >> 16;
  77. sir[1] = (p & 0x00ff00) >> 8;
  78. sir[2] = (p & 0x0000ff);
  79. rinsum += sir[0];
  80. ginsum += sir[1];
  81. binsum += sir[2];
  82. rsum += rinsum;
  83. gsum += ginsum;
  84. bsum += binsum;
  85. stackpointer = (stackpointer + 1) % div;
  86. sir = stack[(stackpointer) % div];
  87. routsum += sir[0];
  88. goutsum += sir[1];
  89. boutsum += sir[2];
  90. rinsum -= sir[0];
  91. ginsum -= sir[1];
  92. binsum -= sir[2];
  93. yi++;
  94. }
  95. yw += w;
  96. }
  97. for (x = 0; x < w; x++) {
  98. rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
  99. yp = -radius * w;
  100. for (i = -radius; i <= radius; i++) {
  101. yi = Math.max(0, yp) + x;
  102. sir = stack[i + radius];
  103. sir[0] = r[yi];
  104. sir[1] = g[yi];
  105. sir[2] = b[yi];
  106. rbs = r1 - Math.abs(i);
  107. rsum += r[yi] * rbs;
  108. gsum += g[yi] * rbs;
  109. bsum += b[yi] * rbs;
  110. if (i > 0) {
  111. rinsum += sir[0];
  112. ginsum += sir[1];
  113. binsum += sir[2];
  114. } else {
  115. routsum += sir[0];
  116. goutsum += sir[1];
  117. boutsum += sir[2];
  118. }
  119. if (i < hm) {
  120. yp += w;
  121. }
  122. }
  123. yi = x;
  124. stackpointer = radius;
  125. for (y = 0; y < h; y++) {
  126. pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
  127. rsum -= routsum;
  128. gsum -= goutsum;
  129. bsum -= boutsum;
  130. stackstart = stackpointer - radius + div;
  131. sir = stack[stackstart % div];
  132. routsum -= sir[0];
  133. goutsum -= sir[1];
  134. boutsum -= sir[2];
  135. if (x == 0) {
  136. vmin[y] = Math.min(y + r1, hm) * w;
  137. }
  138. p = x + vmin[y];
  139. sir[0] = r[p];
  140. sir[1] = g[p];
  141. sir[2] = b[p];
  142. rinsum += sir[0];
  143. ginsum += sir[1];
  144. binsum += sir[2];
  145. rsum += rinsum;
  146. gsum += ginsum;
  147. bsum += binsum;
  148. stackpointer = (stackpointer + 1) % div;
  149. sir = stack[stackpointer];
  150. routsum += sir[0];
  151. goutsum += sir[1];
  152. boutsum += sir[2];
  153. rinsum -= sir[0];
  154. ginsum -= sir[1];
  155. binsum -= sir[2];
  156. yi += w;
  157. }
  158. }
  159. bitmap.setPixels(pix, 0, w, 0, 0, w, h);
  160. return bitmap;
  161. }

JNI代码(性能没有测试)

  1. #include <jni.h>
  2. #include <string.h>
  3. #include <math.h>
  4. #include <stdio.h>
  5. #include <android/log.h>
  6. #include <android/bitmap.h>
  7. #define LOG_TAG "libbitmaputils"
  8. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
  9. #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
  10. typedef struct {
  11. uint8_t red;
  12. uint8_t green;
  13. uint8_t blue;
  14. uint8_t alpha;
  15. } rgba;
  16. JNIEXPORT void JNICALL Java_com_insert_your_package_ClassName_functionToBlur(JNIEnv* env, jobject obj, jobject bitmapIn, jobject bitmapOut, jint radius) {
  17. LOGI("Blurring bitmap...");
  18. // Properties
  19. AndroidBitmapInfo infoIn;
  20. void* pixelsIn;
  21. AndroidBitmapInfo infoOut;
  22. void* pixelsOut;
  23. int ret;
  24. // Get image info
  25. if ((ret = AndroidBitmap_getInfo(env, bitmapIn, &infoIn)) < 0 || (ret = AndroidBitmap_getInfo(env, bitmapOut, &infoOut)) < 0) {
  26. LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
  27. return;
  28. }
  29. // Check image
  30. if (infoIn.format != ANDROID_BITMAP_FORMAT_RGBA_8888 || infoOut.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
  31. LOGE("Bitmap format is not RGBA_8888!");
  32. LOGE("==> %d %d", infoIn.format, infoOut.format);
  33. return;
  34. }
  35. // Lock all images
  36. if ((ret = AndroidBitmap_lockPixels(env, bitmapIn, &pixelsIn)) < 0 || (ret = AndroidBitmap_lockPixels(env, bitmapOut, &pixelsOut)) < 0) {
  37. LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
  38. }
  39. int h = infoIn.height;
  40. int w = infoIn.width;
  41. LOGI("Image size is: %i %i", w, h);
  42. rgba* input = (rgba*) pixelsIn;
  43. rgba* output = (rgba*) pixelsOut;
  44. int wm = w - 1;
  45. int hm = h - 1;
  46. int wh = w * h;
  47. int whMax = max(w, h);
  48. int div = radius + radius + 1;
  49. int r[wh];
  50. int g[wh];
  51. int b[wh];
  52. int rsum, gsum, bsum, x, y, i, yp, yi, yw;
  53. rgba p;
  54. int vmin[whMax];
  55. int divsum = (div + 1) >> 1;
  56. divsum *= divsum;
  57. int dv[256 * divsum];
  58. for (i = 0; i < 256 * divsum; i++) {
  59. dv[i] = (i / divsum);
  60. }
  61. yw = yi = 0;
  62. int stack[div][3];
  63. int stackpointer;
  64. int stackstart;
  65. int rbs;
  66. int ir;
  67. int ip;
  68. int r1 = radius + 1;
  69. int routsum, goutsum, boutsum;
  70. int rinsum, ginsum, binsum;
  71. for (y = 0; y < h; y++) {
  72. rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
  73. for (i = -radius; i <= radius; i++) {
  74. p = input[yi + min(wm, max(i, 0))];
  75. ir = i + radius; // same as sir
  76. stack[ir][0] = p.red;
  77. stack[ir][1] = p.green;
  78. stack[ir][2] = p.blue;
  79. rbs = r1 - abs(i);
  80. rsum += stack[ir][0] * rbs;
  81. gsum += stack[ir][1] * rbs;
  82. bsum += stack[ir][2] * rbs;
  83. if (i > 0) {
  84. rinsum += stack[ir][0];
  85. ginsum += stack[ir][1];
  86. binsum += stack[ir][2];
  87. } else {
  88. routsum += stack[ir][0];
  89. goutsum += stack[ir][1];
  90. boutsum += stack[ir][2];
  91. }
  92. }
  93. stackpointer = radius;
  94. for (x = 0; x < w; x++) {
  95. r[yi] = dv[rsum];
  96. g[yi] = dv[gsum];
  97. b[yi] = dv[bsum];
  98. rsum -= routsum;
  99. gsum -= goutsum;
  100. bsum -= boutsum;
  101. stackstart = stackpointer - radius + div;
  102. ir = stackstart % div; // same as sir
  103. routsum -= stack[ir][0];
  104. goutsum -= stack[ir][1];
  105. boutsum -= stack[ir][2];
  106. if (y == 0) {
  107. vmin[x] = min(x + radius + 1, wm);
  108. }
  109. p = input[yw + vmin[x]];
  110. stack[ir][0] = p.red;
  111. stack[ir][1] = p.green;
  112. stack[ir][2] = p.blue;
  113. rinsum += stack[ir][0];
  114. ginsum += stack[ir][1];
  115. binsum += stack[ir][2];
  116. rsum += rinsum;
  117. gsum += ginsum;
  118. bsum += binsum;
  119. stackpointer = (stackpointer + 1) % div;
  120. ir = (stackpointer) % div; // same as sir
  121. routsum += stack[ir][0];
  122. goutsum += stack[ir][1];
  123. boutsum += stack[ir][2];
  124. rinsum -= stack[ir][0];
  125. ginsum -= stack[ir][1];
  126. binsum -= stack[ir][2];
  127. yi++;
  128. }
  129. yw += w;
  130. }
  131. for (x = 0; x < w; x++) {
  132. rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
  133. yp = -radius * w;
  134. for (i = -radius; i <= radius; i++) {
  135. yi = max(0, yp) + x;
  136. ir = i + radius; // same as sir
  137. stack[ir][0] = r[yi];
  138. stack[ir][1] = g[yi];
  139. stack[ir][2] = b[yi];
  140. rbs = r1 - abs(i);
  141. rsum += r[yi] * rbs;
  142. gsum += g[yi] * rbs;
  143. bsum += b[yi] * rbs;
  144. if (i > 0) {
  145. rinsum += stack[ir][0];
  146. ginsum += stack[ir][1];
  147. binsum += stack[ir][2];
  148. } else {
  149. routsum += stack[ir][0];
  150. goutsum += stack[ir][1];
  151. boutsum += stack[ir][2];
  152. }
  153. if (i < hm) {
  154. yp += w;
  155. }
  156. }
  157. yi = x;
  158. stackpointer = radius;
  159. for (y = 0; y < h; y++) {
  160. output[yi].red = dv[rsum];
  161. output[yi].green = dv[gsum];
  162. output[yi].blue = dv[bsum];
  163. rsum -= routsum;
  164. gsum -= goutsum;
  165. bsum -= boutsum;
  166. stackstart = stackpointer - radius + div;
  167. ir = stackstart % div; // same as sir
  168. routsum -= stack[ir][0];
  169. goutsum -= stack[ir][1];
  170. boutsum -= stack[ir][2];
  171. if (x == 0) vmin[y] = min(y + r1, hm) * w;
  172. ip = x + vmin[y];
  173. stack[ir][0] = r[ip];
  174. stack[ir][1] = g[ip];
  175. stack[ir][2] = b[ip];
  176. rinsum += stack[ir][0];
  177. ginsum += stack[ir][1];
  178. binsum += stack[ir][2];
  179. rsum += rinsum;
  180. gsum += ginsum;
  181. bsum += binsum;
  182. stackpointer = (stackpointer + 1) % div;
  183. ir = stackpointer; // same as sir
  184. routsum += stack[ir][0];
  185. goutsum += stack[ir][1];
  186. boutsum += stack[ir][2];
  187. rinsum -= stack[ir][0];
  188. ginsum -= stack[ir][1];
  189. binsum -= stack[ir][2];
  190. yi += w;
  191. }
  192. }
  193. // Unlocks everything
  194. AndroidBitmap_unlockPixels(env, bitmapIn);
  195. AndroidBitmap_unlockPixels(env, bitmapOut);
  196. LOGI ("Bitmap blurred.");
  197. }
  198. int min(int a, int b) {
  199. return a > b ? b : a;
  200. }
  201. int max(int a, int b) {
  202. return a > b ? a : b;
  203. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/318071
推荐阅读
相关标签
  

闽ICP备14008679号