2008年4月30日星期三

关于高斯模糊和不完备Java实现

前两天发图片马赛克时青姐问的,今天没事研究了一下:
1)什么是高斯模糊:
高斯模糊,是依靠正态分布函数的图像模糊算法,我们知道,正太分布函数曲线是中间高,两边低的对称图形,周边像素与某像素的距离为自变量,用这个函数的取值当作系数,分别乘以周边像素的ARGB值,然后把结果相加,可以得到一个新的ARGB,这个值就是模糊后图像的对应像素的ARGB值。
由于正太分布的曲线是中间高两边低,所以所得的像素与原来的像素相似度最大,也就基本上保持了原来图形的基本形状,并且其边缘也被模糊了
上面的话是我自己打的,可能有些表述不清,具体的可以到Wikipedia上搜索一下,那里的表述是很详细的。
2)高斯模糊的具体实现:
首先要生成高斯系数矩阵:

public static double[] gMatrix(double s, int r) {
double[] ret = new double[r];
double a = 1 / (2d * s * s);
double b = Math.sqrt(a * (1 / Math.PI));
double c = Math.exp(a);
for (int i = 0; i < r; i++) {
ret[i] = b * Math.pow(c, -i * i * 1d);
//ret[i+r-1] = Math.pow(Math.sqrt(Math.PI*2D*s*s), -1)*Math.exp((-i*i)/(2*s*s));
}
return ret;
}

然后是针对每像素操作的函数:

public static byte[] getARGB(int j) {
byte b = (byte) (j & 0x000000ff);
byte g = (byte) ((j & 0x0000ff00) >> 8);
byte r = (byte) ((j & 0x00ff0000) >> 16);
byte a = (byte) ((j & 0xff000000) >> 24);
return new byte[]{a, r, g, b};
}

public static int getInt(byte[] bytes) {
//System.out.println(Arrays.toString(d));
return ((bytes[0] << i =" 1;" i =" 1;" a =" src[i]" b =" p[i]" x =" (a" d="%d" x="%x]\n" x =" x"> 0xFF ? 0xFF : x;
src[i] = (byte) (x & 0xff);
}
}

下面是针对一个图像行(横线或纵向)的操作

public static int[] smoothLine(int[] data, double[] mtx) {
int[] ret = data.clone();
for (int i = 0; i < d =" getARGB(data[i]);" k =" 1;" nd =" getARGB(data[i">= 0) {
byte[] nd = getARGB(data[i - k]);
procByte(nd, mtx[k]);
// System.out.println("" + (i - k) + "," + Arrays.toString(nd));
addByte(d, nd);
// System.out.println("plus:" + Arrays.toString(d));
}
}

ret[i] = getInt(d);
//System.out.printf("final:%s 0x%08X\n", Arrays.toString(d), ret[i]);
}
return ret;
}

最后是针对整个图像的操作:

public static BufferedImage smooth(BufferedImage i, double s, int r) {
BufferedImage ret = new BufferedImage(i.getWidth(),

i.getHeight(),

i.getType());
double[] mtx = gMritex(s, r);
for (int j = 0; j < i.getHeight(); j++) {
int[] data = i.getRGB(0, j, i.getWidth() - 1, 1,

null, 0, i.getWidth());
//System.out.println(Arrays.toString(data));
data = smoothLine(data, mtx);
//System.out.println(Arrays.toString(data));
ret.setRGB(0, j, i.getWidth() - 1, 1,

data, 0, i.getWidth());
}
// for (int j = 0; j < i.getWidth(); j++) {
// int[] data = ret.getRGB(j, 0, 1, i.getHeight()-1, null, 0, i.getHeight());
// //System.out.println(Arrays.toString(data));
// data = smoothLine(data, mtx);
// //System.out.println(Arrays.toString(data));
// ret.setRGB(j, 0, 1, i.getHeight()-1, data, 0, i.getHeight());
// }
return ret;
}

这里需要说明几点:本来高斯模糊是矩阵运算的,但是可以化简为线性运算,也就是横竖各处理一次(至于原因参看Wikipedia)但是我发现使用 JavaAPI对竖向像素列操作十分费时,所以就把它暂时注释掉了。但是这样模糊的结果就不是完全高斯模糊的结果。我想应该吧横向处理过的图像做90度翻转,然后再横相处理一次。不过这个想法还没顾得上实施。有时间再写。
另外一个问题是:处理过的图像最右边会有一道黑线,不知道是为什么,希望有高人能够说明一下。

刚才发现好多正态分布都写成了正太分布……感觉好邪恶的样子=_=|||
最后是完整代码,里面还有Wikipedia的高斯模糊的词条页,省的伟大的GFW和谐掉我们的网络。

0 人次吐槽:

发表评论