Android 中比较“高效”的图像模糊处理算法

 

本文假设读者拥有基础的图像处理概念。

这是今天刚刚完成的一个方法,之前不停地在网上找有关 Android 的图像模糊处理代码。
期间找到了倒影、缩放等参考代码,却无一帖子对模糊处理有过提及。
最多也就是提到使用 BlurMaskFilter 来进行模糊处理。
为了这个害人的帖子,我整整浪费了一下午时间,最后发现它只能用于对 Paint 的边缘进行处理。
而我们要处理的是整幅图像,所以这完完全全是一个骗人的说法。

由于先前在 VB.NET 上处理过图像,也写过相关代码,当时 VB.NET 代码分两种形式:
1、死算:即将每个像素点的颜色值与周围 8 点计算平均值,每计算一次 SetPixel() 一次,这样做非常慢;
2、读取整幅图像的内存数据,也就是已三原色为单位了,数据量(颜色数组)比上一种方法大一倍。
这样的做法可以将所有颜色计算好以后再重新写入内存,肯定比 SetPixel() 更高效。
不过由于也并非通过底层函数对内存操作,因此虽然有提速,效果和 C++ 相比却是小巫见大巫了。

下面说正事,其实 JAVA 和 .NET 很类似的(据说 .NET 的架构师就是 SUN 过去的呢)。
当然也无法直接对内存操作,当然也无法像 .NET 这样间接对内存操作,很抓狂啊!
要知道,我是死也不会用第一种方法死算的!
那么怎么办?我仍然希望从第二种方法入手,虽然慢了一些,但还是能忍受的。
好在 Android 提供了 setPixels() 方法,看清楚,Pixel 后面有 s 的!
这个方法其实是 setPixel() 的“批量”版本,也就是说允许我们一下子填充多个连续的像素。
既然这样,我们离模拟第二点还差一半,那就是取得包含三原色的数组了,看代码吧。

/* 设置图片模糊 */

public static Bitmap SetBlur(Bitmap bmpSource, int Blur)  //源位图,模糊强度
{
 int pixels[] = new int[bmpSource.getWidth() * bmpSource.getHeight()];  //颜色数组,一个像素对应一个元素
 int pixelsRawSource[] = new int[bmpSource.getWidth() * bmpSource.getHeight() * 3];  //三原色数组,作为元数据,在每一层模糊强度的时候不可更改
 int pixelsRawNew[] = new int[bmpSource.getWidth() * bmpSource.getHeight() * 3];  //三原色数组,接受计算过的三原色值
 bmpSource.getPixels(pixels, 0, bmpSource.getWidth(), 0, 0, bmpSource.getWidth(), bmpSource.getHeight());  //获取像素点

 //模糊强度,每循环一次强度增加一次
 for (int k = 1; k <= Blur; k++)
 {
  //从图片中获取每个像素三原色的值
  for (int i = 0; i < pixels.length; i++)
  {
   pixelsRawSource[i * 3 + 0] = Color.red(pixels[i]);
   pixelsRawSource[i * 3 + 1] = Color.green(pixels[i]);
   pixelsRawSource[i * 3 + 2] = Color.blue(pixels[i]);
  }

  //取每个点上下左右点的平均值作自己的值
  int CurrentPixel = bmpSource.getWidth() * 3 + 3; // 当前处理的像素点,从点(2,2)开始
  for (int i = 0; i < bmpSource.getHeight() - 3; i++) // 高度循环
  {
   for (int j = 0; j < bmpSource.getWidth() * 3; j++) // 宽度循环
   {
    CurrentPixel += 1;
    // 取上下左右,取平均值
    int sumColor = 0; // 颜色和
    sumColor = pixelsRawSource[CurrentPixel - bmpSource.getWidth() * 3]; // 上一点
    sumColor = sumColor + pixelsRawSource[CurrentPixel - 3]; // 左一点
    sumColor = sumColor + pixelsRawSource[CurrentPixel + 3]; // 右一点
    sumColor = sumColor + pixelsRawSource[CurrentPixel + bmpSource.getWidth() * 3]; // 下一点
    pixelsRawNew[CurrentPixel] = Math.round(sumColor / 4); // 设置像素点
   }
  }

  //将新三原色组合成像素颜色
  for (int i = 0; i < pixels.length; i++)
  {
   pixels[i] = Color.rgb(pixelsRawNew[i * 3 + 0], pixelsRawNew[i * 3 + 1], pixelsRawNew[i * 3 + 2]);
  }
 }

 //应用到图像
 Bitmap bmpReturn = Bitmap.createBitmap(bmpSource.getWidth(), bmpSource.getHeight(), Config.ARGB_8888);
 bmpReturn.setPixels(pixels, 0, bmpSource.getWidth(), 0, 0, bmpSource.getWidth(), bmpSource.getHeight());  //必须新建位图然后填充,不能直接填充源图像,否则内存报错

 return bmpReturn;
}

 

代码其实很简单,总体而言就是分为三步:
1、取得该位图所有的字节对应的数据;
2、计算每个像素的模糊平均值;
3、利用颜色数组重新绘图。

这就是跟 VB.NET 不同的地方了,我们在 VB.NET 中是改写内存,而这里是申请新的内存区域。
此外,还多了一个大循环,将上述三步进行多次循环,每次循环前将上一次计算好的平均颜色数组作为新的源数据,这样就能获取更模糊的效果了。

要注意的是,此方法对设备性能要求较高,在 1 GHz 处理器上(三星 Galaxy S i9000)模糊强度为 8 时处理 800 × 400 的图像耗时约 20 秒。
HTC Magic 512 MHz 处理器中约 40 秒,可以说基本保持在一个固定比例中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值