如何使用
Floyd–Steinberg dithering将24位PNG转换为3位PNG?应该使用java.awt.image.BufferedImage来获取和设置RGB值.
在维基百科上,给出了如何将16位转换为8位图像的示例:
find_closest_palette_color(oldpixel) = (oldpixel + 128) / 256
基于此,有没有想到如何适应上面的例子来实现目标?
解决方法
使用
image.getRGB(x,y)
和
image.setRGB(x,y,color)
,并从
wikipedia article使用
pseudocode.请注意,维基上的代码不会说如何“减”,“添加”和“乘”颜色. (下面的T3类处理“颜色”操作.)
下面的代码将会产生这个截图:
class Test { private static BufferedImage floydSteinbergDithering(BufferedImage img) { C3[] palette = new C3[] { new C3( 0,0),new C3( 0,255),255,new C3(255,255) }; int w = img.getWidth(); int h = img.getHeight(); C3[][] d = new C3[h][w]; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) d[y][x] = new C3(img.getRGB(x,y)); for (int y = 0; y < img.getHeight(); y++) { for (int x = 0; x < img.getWidth(); x++) { C3 oldColor = d[y][x]; C3 newColor = findClosestPaletteColor(oldColor,palette); img.setRGB(x,newColor.toColor().getRGB()); C3 err = oldColor.sub(newColor); if (x+1 < w) d[y ][x+1] = d[y ][x+1].add(err.mul(7./16)); if (x-1>=0 && y+1<h) d[y+1][x-1] = d[y+1][x-1].add(err.mul(3./16)); if (y+1 < h) d[y+1][x ] = d[y+1][x ].add(err.mul(5./16)); if (x+1<w && y+1<h) d[y+1][x+1] = d[y+1][x+1].add(err.mul(1./16)); } } return img; } private static C3 findClosestPaletteColor(C3 c,C3[] palette) { C3 closest = palette[0]; for (C3 n : palette) if (n.diff(c) < closest.diff(c)) closest = n; return closest; } public static void main(String[] args) throws IOException { final BufferedImage normal = ImageIO.read(new URL("http://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png")).getSubimage(100,100,300,300); final BufferedImage dietered = floydSteinbergDithering(ImageIO.read(new URL("http://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png"))).getSubimage(100,300); JFrame frame = new JFrame("Test"); frame.setLayout(new GridLayout(1,2)); frame.add(new JComponent() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(normal,this); } }); frame.add(new JComponent() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(dietered,this); } }); frame.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400,400); frame.setVisible(true); } static class C3 { int r,g,b; public C3(int c) { Color color = new Color(c); this.r = color.getRed(); this.g = color.getGreen(); this.b = color.getBlue(); } public C3(int r,int g,int b) { this.r = r; this.g = g; this.b = b; } public C3 add(C3 o) { return new C3(r + o.r,g + o.g,b + o.b); } public C3 sub(C3 o) { return new C3(r - o.r,g - o.g,b - o.b); } public C3 mul(double d) { return new C3((int) (d * r),(int) (d * g),(int) (d * b)); } public int diff(C3 o) { return Math.abs(r - o.r) + Math.abs(g - o.g) + Math.abs(b - o.b); } public int toRGB() { return toColor().getRGB(); } public Color toColor() { return new Color(clamp(r),clamp(g),clamp(b)); } public int clamp(int c) { return Math.max(0,Math.min(255,c)); } } }