图像处理之积分图像应用一(半径无关的快速模糊算法)
一:基本原理概述
传统的图像空间域卷积模糊算法,当窗口大小改变时卷积模糊时间也会变化,而且随着窗口尺寸越大计算量也越大,算法运行时间约越长。在很多时候无法满足实时性要求。而基于积分图像可以实现对窗口区域和大小的快速计算,把传统卷积模糊计算受窗口大小影响消除,把卷积模糊变成一个与窗口大小半径无关的常量时间完成的操作。关于如何从图像本身得到积分图像的算法请看上一篇文章《图像处理之积分图像算法》
二:详细解释
以5x5的窗口大小为例,假设图像I、积分图像II、处理之后模糊图像BI、则传统空间域卷积实现的图像均值模糊对每个像素点公式表示如下:
基于积分图像计算每个像素点模糊公式表示如下:
上述基于传统的均值模糊计算得到模糊之后的结果要计算24次加法和一次除法共计25次计算,而基于积分图像则只需要一次加法两次减法和一次除法共计四次计算,而且基于传统卷积均值模糊计算当窗口大小越大计算次数也越多,而基于积分图像则计算次数保持常量不变,是一个半径无关的均值模糊算法。
三:代码实现
积分图像算法实现参见:http://blog.csdn.net/jia20003/article/details/52710751
传统模式的卷积模糊代码如下:
-
package com.gloomyfish.ii.demo;
-
-
import java.awt.image.BufferedImage;
-
-
public
class Convolution2DFilter extends AbstractImageOptionFilter {
-
// 窗口半径大小
-
private
int xr;
-
private
int yr;
-
-
public Convolution2DFilter() {
-
xr =
1;
-
yr =
1;
-
}
-
-
public int getXr() {
-
return xr;
-
}
-
-
public void setXr(int xr) {
-
this.xr = xr;
-
}
-
-
public int getYr() {
-
return yr;
-
}
-
-
public void setYr(int yr) {
-
this.yr = yr;
-
}
-
-
@Override
-
public BufferedImage process(BufferedImage image) {
-
long time = System.currentTimeMillis();
-
int width = image.getWidth();
-
int height = image.getHeight();
-
-
int[] pixels =
new
int[width * height];
-
int[] outPixels =
new
int[width * height];
-
getRGB(image,
0,
0, width, height, pixels);
-
int size = (xr *
2 +
1) * (yr *
2 +
1);
-
int r =
0, g =
0, b =
0;
-
-
for (
int row = yr; row < height - yr; row++) {
-
for (
int col = xr; col < width - xr; col++) {
-
int sr =
0, sg =
0, sb =
0;
-
// 鍗风Н鎿嶄綔/妯℃澘璁$畻
-
for (
int i = -yr; i <= yr; i++) {
-
int roffset = row + i;
-
for (
int j = -xr; j <= xr; j++) {
-
int coffset = col + j;
-
sr += ((pixels[roffset * width + coffset] >>
16) &
0xff);
-
sg += (pixels[roffset * width + coffset] >>
8) &
0xff;
-
sb += (pixels[roffset * width + coffset] &
0xff);
-
}
-
}
-
-
r = sr / size;
-
g = sg / size;
-
b = sb / size;
-
outPixels[row * width + col] = (
0xff <<
24) | (r <<
16) | (g <<
8) | b;
-
}
-
}
-
System.out.println(
"Convolution2DFilter ->> time duration : " + (System.currentTimeMillis() - time));
-
BufferedImage dest =
new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-
setRGB(dest,
0,
0, width, height, outPixels);
-
return dest;
-
}
-
-
}
基于积分图像的快速模糊代码如下:
-
package com.gloomyfish.ii.demo;
-
-
import java.awt.image.BufferedImage;
-
-
public
class FastBlurFilter extends AbstractImageOptionFilter {
-
// 窗口半径大小
-
private
int xr;
-
private
int yr;
-
-
public FastBlurFilter() {
-
xr =
1;
-
yr =
1;
-
}
-
-
public int getXr() {
-
return xr;
-
}
-
-
public void setXr(int xr) {
-
this.xr = xr;
-
}
-
-
public int getYr() {
-
return yr;
-
}
-
-
public void setYr(int yr) {
-
this.yr = yr;
-
}
-
-
@Override
-
public BufferedImage process(BufferedImage image) {
-
long time = System.currentTimeMillis();
-
int width = image.getWidth();
-
int height = image.getHeight();
-
// get image data
-
int[] pixels =
new
int[width * height];
-
int[] outPixels =
new
int[width * height];
-
getRGB(image,
0,
0, width, height, pixels);
-
int size = (xr *
2 +
1) * (yr *
2 +
1);
-
int r =
0, g =
0, b =
0;
-
-
// per-calculate integral image
-
byte[] R =
new
byte[width*height];
-
byte[] G =
new
byte[width*height];
-
byte[] B =
new
byte[width*height];
-
getRGB(width, height, pixels, R, G, B);
-
IntIntegralImage rii =
new IntIntegralImage();
-
rii.setImage(R);
-
rii.process(width, height);
-
IntIntegralImage gii =
new IntIntegralImage();
-
gii.setImage(G);
-
gii.process(width, height);
-
IntIntegralImage bii =
new IntIntegralImage();
-
bii.setImage(B);
-
bii.process(width, height);
-
-
for (
int row = yr; row < height - yr; row++) {
-
for (
int col = xr; col < width - xr; col++) {
-
int sr = rii.getBlockSum(col, row, (yr *
2 +
1), (xr *
2 +
1));
-
int sg = gii.getBlockSum(col, row, (yr *
2 +
1), (xr *
2 +
1));
-
int sb = bii.getBlockSum(col, row, (yr *
2 +
1), (xr *
2 +
1));
-
r = sr / size;
-
g = sg / size;
-
b = sb / size;
-
outPixels[row * width + col] = (
0xff <<
24) | (r <<
16) | (g <<
8) | b;
-
}
-
}
-
System.out.println(
"FastBlurFilter ->> time duration : " + (System.currentTimeMillis() - time));
-
BufferedImage dest =
new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
-
setRGB(dest,
0,
0, width, height, outPixels);
-
return dest;
-
}
-
-
/** Returns the red, green and blue planes as 3 byte arrays. */
-
public void getRGB(int width, int height, int[] pixels, byte[] R, byte[] G, byte[] B) {
-
int c, r, g, b;
-
for (
int i=
0; i < width*height; i++) {
-
c = pixels[i];
-
r = (c&
0xff0000)>>
16;
-
g = (c&
0xff00)>>
8;
-
b = c&
0xff;
-
R[i] = (
byte)r;
-
G[i] = (
byte)g;
-
B[i] = (
byte)b;
-
}
-
}
-
-
}
四:效率之比
分别把窗口半径调整到1、3、10、20的情况下,对同一张图像做模糊处理,CPU耗时直方图如下:
可见在其它条件不改变的情况下,窗口半径越大,两者之间执行时间差距越大。各位国庆节快乐!