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和谐掉我们的网络。

2008年4月5日星期六

你可能不知道的:突破Java单例模式

这个东西是以前写代码注入时发现的。这两天实在无聊,所以写出来,仅供参考。MS没什么使用价值。。。。权当娱乐
我们知道,在Java中有一种单例模式,是在一个虚拟机里,某个对象只有一个实例。但是,真的只能有一个实例么?
考虑下面代码:


import java.io.InputStream;
public interface IOnlyOne{
void printOne();
}
public class OnlyOne implements IOnlyOne{
private static final OnlyOne myOne = new OnlyOne();
public void printOne(){
System.out.println(myOne);
}
public static void main(String[] args) throws Exception{
myOne.printOne();
OnlyOneLoader loader = new OnlyOneLoader();
IOnlyOne o = loader.createNewOne();
o.printOne();
}
}
public class OnlyOneLoader extends ClassLoader{
public IOnlyOne createNewOne() throws Exception{
InputStream is = getClass().getResourceAsStream("OnlyOne.class");
byte[] b = new byte[is.available()];
is.read(b);
Class clz = defineClass(null,b, 0, b.length);
Object o = clz.newInstance();
return (IOnlyOne) o;
}
}

我们看到createNewOne()首先定义了一个类OnlyOne,然后实例化了一个对象。那么根据代码来看,这个对象的printOne方法又会访问那个myOne的静态成员。看起来两次打印的结果应该是相同的,但是事实上,结果并不相同。
那么,这是为什么呢?是不是定义类过程中又对那个myOne对象重新赋值了呢?我们可以在o.printOne();后面再加上一句myOne.printOne();
可以看到,打印的和第一次的结果是一样的。这就说明了并不是重新赋值,而是有两个myOne的变量存在。
事实上,Java中,一个类是可以存在多个定义的,这些类的类名可以相同,但是确可以有各自的成员存储区,他们的代码也可以不同。在同一个 ClassLoader中,不允许出现重名类,但是Java却允许多个不同的ClassLoader同时存在。需要注意的是:即使类名和代码都相同,但是不同ClassLoader说产生的类是不兼容的,这就是为什么上述代码中要引入一个接口的原因了。你可以把那个接口去掉,看看会发生什么有趣的现象~~
那么,这个和单例模式有什么关系呢?因为单例的对象实例是存储在静态成员变量中的,所以在不同的ClassLoader下,这个变量的值可能是不同的。

2008年4月4日星期五

没事干,PS两张证书来玩

最近看Gundam Seed,无聊之时就PS两张认证出来。猜猜原来的认证是什么证书的?