android高斯模糊
2016-06-02 11:05
459 查看
高斯模糊算法介绍
高斯模糊就是将指定像素变换为其与周边像素加权平均后的值,权重就是高斯分布函数计算出来的值。算法介绍一、通过自身的写算法
public static void gaussBlur(int[] data, int width, int height, int radius, float sigma) { float pa = (float) (1 / (Math.sqrt(2 * Math.PI) * sigma)); float pb = -1.0f / (2 * sigma * sigma); // generate the Gauss Matrix float[] gaussMatrix = new float[radius * 2 + 1]; float gaussSum = 0f; for (int i = 0, x = -radius; x <= radius; ++x, ++i) { float g = (float) (pa * Math.exp(pb * x * x)); gaussMatrix[i] = g; gaussSum += g; } for (int i = 0, length = gaussMatrix.length; i < length; ++i) { gaussMatrix[i] /= gaussSum; } // x direction for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { float r = 0, g = 0, b = 0; gaussSum = 0; for (int j = -radius; j <= radius; ++j) { int k = x + j; if (k >= 0 && k < width) { int index = y * width + k; int color = data[index]; int cr = (color & 0x00ff0000) >> 16; int cg = (color & 0x0000ff00) >> 8; int cb = (color & 0x000000ff); r += cr * gaussMatrix[j + radius]; g += cg * gaussMatrix[j + radius]; b += cb * gaussMatrix[j + radius]; gaussSum += gaussMatrix[j + radius]; } } int index = y * width + x; int cr = (int) (r / gaussSum); int cg = (int) (g / gaussSum); int cb = (int) (b / gaussSum); data[index] = cr << 16 | cg << 8 | cb | 0xff000000; } } // y direction for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) { float r = 0, g = 0, b = 0; gaussSum = 0; for (int j = -radius; j <= radius; ++j) { int k = y + j; if (k >= 0 && k < height) { int index = k * width + x; int color = data[index]; int cr = (color & 0x00ff0000) >> 16; int cg = (color & 0x0000ff00) >> 8; int cb = (color & 0x000000ff); r += cr * gaussMatrix[j + radius]; g += cg * gaussMatrix[j + radius]; b += cb * gaussMatrix[j + radius]; gaussSum += gaussMatrix[j + radius]; } } int index = y * width + x; int cr = (int) (r / gaussSum); int cg = (int) (g / gaussSum); int cb = (int) (b / gaussSum); data[index] = cr << 16 | cg << 8 | cb | 0xff000000; } } }
`
实际测试会发现这种计算方式是很耗时间的,而且模糊半径越大,从原理也可以看到计算量是平方增长的,所以计算时间也越长。
二、通过Android自带的图像处理工具类
public static Bitmap blurBitmap(Bitmap bitmap, float radius, Context context) { RenderScript rs = RenderScript.create(context); Allocation allocation = Allocation.createFromBitmap(rs, bitmap); Type t = allocation.getType(); Allocation blurredAllocation = Allocation.createTyped(rs, t, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); //(maximum 25.0) blurScript.setRadius(radius); blurScript.setInput(allocation); blurScript.forEach(blurredAllocation); blurredAllocation.copyTo(bitmap); allocation.destroy(); blurredAllocation.destroy(); blurScript.destroy(); t.destroy(); rs.destroy(); return bitmap; }
RenderScript是Android在API 11之后加入的,用于高效的图片处理,包括模糊、混合、矩阵卷积计算等,RenderScript的使用介绍链接。使用这种方式明显处理起来更快很多,但是使用RenderScript做高斯模糊,模糊半径只能小于25
三、采用另一种特殊的算法,比第一种要快,但是比RenderScript还是要慢
public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { // 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> Bitmap bitmap; if (canReuseInBitmap) { bitmap = sentBitmap; } else { 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]; 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; 4000 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; } } bitmap.setPixels(pix, 0, w, 0, 0, w, h); return (bitmap); }
`
随着手势的滑动,图片越来越模糊
背景会随着手指上滑模糊程度加深,实际使用中发现怎么都达不到那样流畅的效果,因为手势刷新的速度很快,每一帧都去重新模糊计算一遍,还是会有延迟,造成页面卡顿。后来在一次偶然的开发中发现其实不需要每一帧都重新去模糊一遍,而是将图片最大程度模糊一次,之后和原图叠加,通过改变叠加的模糊图片的alpha值来达到不同程度的模糊效。
mBlurImage.setOnTouchListener(new OnTouchListener() { private float mLastY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastY = event.getY(); break; case MotionEvent.ACTION_MOVE: float y = event.getY(); float alphaDelt = (y - mLastY) / 1000; float alpha = mBlurImage.getAlpha() + alphaDelt; if (alpha > 1.0) { alpha = 1.0f; } else if (alpha < 0.0) { alpha = 0.0f; } mTextView.setText(String.valueOf(alpha)); mBlurImage.setAlpha(alpha); break; case MotionEvent.ACTION_UP: break; } return true; } });
还有一种挺有意思的模糊实现:
ClassView盖一层模糊层
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories