高斯模糊是用得最广泛的图像模糊算法,它的原理很简单,对每个点计算它周围其他点的平均色值,设置到该点上,就是模糊后的图。取周围其他点的范围称为模糊半径,模糊半径越大也就越模糊。高斯模糊算法网上很多,可以参考这个:
http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
然而最近项目需要图片在竖直方向上模糊,网上找了一下没有发现相应算法,于是自己写了一个。原理跟高斯模糊相似,由于只有一个方向,比高斯模糊少了一个维度,因此简单很多:
- 把图像Bitmap每一列的像素的ARGB(透明度和红绿蓝)值取出来
- 透明度不变,红绿蓝值分别根据模糊半径,跟竖直方向上相邻的点算平均值
- 将计算后的ARGB值合成新的像素,创建新的Bitmap
public static Bitmap verticalBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) {
Bitmap bitmap;
if (canReuseInBitmap) {
bitmap = sentBitmap;
} else {
bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
}
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int hm = h - 1;
if (radius < 1) return bitmap;
if(radius > hm / 2) radius = hm / 2;
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int[] a = new int[h];
int[] r = new int[h];
int[] g = new int[h];
int[] b = new int[h];
for(int i = 0; i < w; i++) {
a[0] = pix[i] & 0xff000000;
r[0] = pix[i] >> 16 & 0x000000ff;
g[0] = pix[i] >> 8 & 0x000000ff;
b[0] = pix[i] & 0x000000ff;
for(int j = 1; j < h; j++) {
int p = pix[i + j * w];
a[j] = (p & 0xff000000);
r[j] = (p >> 16 & 0x000000ff) + r[j - 1];
g[j] = (p >> 8 & 0x000000ff) + g[j - 1];
b[j] = (p & 0x000000ff) + b[j - 1];
}
for(int j = 0; j < h; j++) {
int low = j - radius, high = j + radius;
if(low < 0) low = 0;
if(high > hm) high = hm;
int len = high - low;
int rj = ((r[high] - r[low]) / len) << 16;
int gj = ((g[high] - g[low]) / len) << 8;
int bj = (b[high] - b[low]) / len;
pix[i + j * w] = a[j] | rj | gj | bj;
}
}
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
return bitmap;
}
由于是在Java层计算的,为了提高效率,有一些技巧:
用
bitmap.getPixels()
方法将bitmap转换成一个一维数组int[]
, 直接操作数组是计算效率最高的
尽量使用位运算, 像素的ARGB值恰好占一个int的四个八位, 取出和合成它们用
&
和|
就行
重复计算一个数组中相邻几项的和只需要累加一次. 比如有数组A, 要计算从A[i]到A[j]的和, 先创建数组B, B[0] = A[0], B[1] = B[0] + A[1], B[i] = B[i - 1] + A[i]..., 这样累加一次后,从A[i]到A[j]的和就等于B[j] - B[i - 1].
这样模糊一张500*500的图片大概需要十几ms. 效果如下:
模糊半径小 模糊半径大
网友评论