如何使用 Java 实现 1 位抖动?
How to implement 1-Bit Dithering using Java?
最近,我们的老师给了我们一个任务,使用 Java 将彩色图像转换为 1 位图像。经过一些实验后,我得到了以下结果:
BufferedImage image = ...
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int clr = image.getRGB(x, y);
int r = (clr & 0x00ff0000) >> 16;
int g = (clr & 0x0000ff00) >> 8;
int b = clr & 0x000000ff;
double mono = 0.2126*r + 0.7152*g + 0.0722*b;
int c = mono < 128 ? 1 : 0;
//Adding to image buffer
buffer.add(c);
}
}
好吧,它可以工作,但不幸的是丢失了很多细节。这是一个比较:
原文:
输出:
我想要的:(总部:https://i.stack.imgur.com/vlEAE.png)
我正在考虑将抖动添加到我的转换器中,但我还没有找到可行的方法,更不用说任何伪代码了。
谁能帮帮我?
编辑:
所以我创建了一个DitheringUtils
-class:
import java.awt.Color;
import java.awt.image.BufferedImage;
public class DitheringUtils {
public static BufferedImage dithering(BufferedImage image) {
Color3i[] palette = new Color3i[] {
new Color3i(0, 0, 0),
new Color3i(255, 255, 255)
};
int width = image.getWidth();
int height = image.getHeight();
Color3i[][] buffer = new Color3i[height][width];
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
buffer[y][x] = new Color3i(image.getRGB(x, y));
}
}
for(int y=0; y<image.getHeight();y++) {
for(int x=0; x<image.getWidth();x++) {
Color3i old = buffer[y][x];
Color3i nem = findClosestPaletteColor(old, palette);
image.setRGB(x, y, nem.toColor().getRGB());
Color3i error = old.sub(nem);
if (x+1 < width) buffer[y ][x+1] = buffer[y ][x+1].add(error.mul(7./16));
if (x-1>=0 && y+1<height) buffer[y+1][x-1] = buffer[y+1][x-1].add(error.mul(3./16));
if (y+1 < height) buffer[y+1][x ] = buffer[y+1][x ].add(error.mul(5./16));
if (x+1<width && y+1<height) buffer[y+1][x+1] = buffer[y+1][x+1].add(error.mul(1./16));
}
}
return image;
}
private static Color3i findClosestPaletteColor(Color3i match, Color3i[] palette) {
Color3i closest = palette[0];
for(Color3i color : palette) {
if(color.diff(match) < closest.diff(match)) {
closest = color;
}
}
return closest;
}
}
class Color3i {
private int r, g, b;
public Color3i(int c) {
Color color = new Color(c);
this.r = color.getRed();
this.g = color.getGreen();
this.b = color.getBlue();
}
public Color3i(int r, int g, int b) {
this.r = r;
this.g = g;
this.b = b;
}
public Color3i add(Color3i o) {
return new Color3i(r + o.r, g + o.g, b + o.b);
}
public Color3i sub(Color3i o) {
return new Color3i(r - o.r, g - o.g, b - o.b);
}
public Color3i mul(double d) {
return new Color3i((int) (d * r), (int) (d * g), (int) (d * b));
}
public int diff(Color3i 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));
}
}
并将我的功能更改为:
for (int y = 0; y < dithImage.getHeight(); ++y) {
for (int x = 0; x < dithImage.getWidth(); ++x) {
final int clr = dithImage.getRGB(x, y);
final int r = (clr & 0xFF0000) >> 16;
final int g = (clr & 0xFF00) >> 8;
final int b = clr & 0xFF;
if(382.5>(r+g+b)) {
buffer.add(0);
} else {
buffer.add(1);
}
}
}
但输出结果看起来……很奇怪?
真不明白为什么会有这样的波浪
BufferedImage image = ...
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
Color color = new Color(image.getRGB(x, y));
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
int mono = (red+green+blue)/255;
//Adding to image buffer
int col = (0 << 24) | (mono << 16) | (mono << 8) | mono;
image.setRGB(x,y,col);
}
}
试试这个
您做错的是,您没有尝试将图片转换为灰度,而是尝试将其转换为黑白。
我终于成功了!我改进了diff
函数,把if(382.5>(r+g+b))
改成了if(765==(r+g+b))
。
我的DitheringUtils
-class:
import java.awt.Color;
import java.awt.image.BufferedImage;
public class DitheringUtils {
public static BufferedImage dithering(BufferedImage image) {
Color3i[] palette = new Color3i[] {
new Color3i(0, 0, 0),
new Color3i(255, 255, 255)
};
int width = image.getWidth();
int height = image.getHeight();
Color3i[][] buffer = new Color3i[height][width];
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
buffer[y][x] = new Color3i(image.getRGB(x, y));
}
}
for(int y=0; y<image.getHeight();y++) {
for(int x=0; x<image.getWidth();x++) {
Color3i old = buffer[y][x];
Color3i nem = findClosestPaletteColor(old, palette);
image.setRGB(x, y, nem.toColor().getRGB());
Color3i error = old.sub(nem);
if (x+1 < width) buffer[y ][x+1] = buffer[y ][x+1].add(error.mul(7./16));
if (x-1>=0 && y+1<height) buffer[y+1][x-1] = buffer[y+1][x-1].add(error.mul(3./16));
if (y+1 < height) buffer[y+1][x ] = buffer[y+1][x ].add(error.mul(5./16));
if (x+1<width && y+1<height) buffer[y+1][x+1] = buffer[y+1][x+1].add(error.mul(1./16));
}
}
return image;
}
private static Color3i findClosestPaletteColor(Color3i match, Color3i[] palette) {
Color3i closest = palette[0];
for(Color3i color : palette) {
if(color.diff(match) < closest.diff(match)) {
closest = color;
}
}
return closest;
}
}
class Color3i {
private int r, g, b;
public Color3i(int c) {
Color color = new Color(c);
this.r = color.getRed();
this.g = color.getGreen();
this.b = color.getBlue();
}
public Color3i(int r, int g, int b) {
this.r = r;
this.g = g;
this.b = b;
}
public Color3i add(Color3i o) {
return new Color3i(r + o.r, g + o.g, b + o.b);
}
public Color3i sub(Color3i o) {
return new Color3i(r - o.r, g - o.g, b - o.b);
}
public Color3i mul(double d) {
return new Color3i((int) (d * r), (int) (d * g), (int) (d * b));
}
public int diff(Color3i o) {
int Rdiff = o.r - r;
int Gdiff = o.g - g;
int Bdiff = o.b - b;
int distanceSquared = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
return distanceSquared;
}
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));
}
}
最终写入函数:
for (int y = 0; y < dithImage.getHeight(); ++y) {
for (int x = 0; x < dithImage.getWidth(); ++x) {
final int clr = dithImage.getRGB(x, y);
final int r = (clr & 0xFF0000) >> 16;
final int g = (clr & 0xFF00) >> 8;
final int b = clr & 0xFF;
if(765==(r+g+b)) {
buffer.add(0);
} else {
buffer.add(1);
}
}
}
谢谢大家!
最近,我们的老师给了我们一个任务,使用 Java 将彩色图像转换为 1 位图像。经过一些实验后,我得到了以下结果:
BufferedImage image = ...
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int clr = image.getRGB(x, y);
int r = (clr & 0x00ff0000) >> 16;
int g = (clr & 0x0000ff00) >> 8;
int b = clr & 0x000000ff;
double mono = 0.2126*r + 0.7152*g + 0.0722*b;
int c = mono < 128 ? 1 : 0;
//Adding to image buffer
buffer.add(c);
}
}
好吧,它可以工作,但不幸的是丢失了很多细节。这是一个比较:
原文:
输出:
我想要的:(总部:https://i.stack.imgur.com/vlEAE.png)
我正在考虑将抖动添加到我的转换器中,但我还没有找到可行的方法,更不用说任何伪代码了。
谁能帮帮我?
编辑:
所以我创建了一个DitheringUtils
-class:
import java.awt.Color;
import java.awt.image.BufferedImage;
public class DitheringUtils {
public static BufferedImage dithering(BufferedImage image) {
Color3i[] palette = new Color3i[] {
new Color3i(0, 0, 0),
new Color3i(255, 255, 255)
};
int width = image.getWidth();
int height = image.getHeight();
Color3i[][] buffer = new Color3i[height][width];
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
buffer[y][x] = new Color3i(image.getRGB(x, y));
}
}
for(int y=0; y<image.getHeight();y++) {
for(int x=0; x<image.getWidth();x++) {
Color3i old = buffer[y][x];
Color3i nem = findClosestPaletteColor(old, palette);
image.setRGB(x, y, nem.toColor().getRGB());
Color3i error = old.sub(nem);
if (x+1 < width) buffer[y ][x+1] = buffer[y ][x+1].add(error.mul(7./16));
if (x-1>=0 && y+1<height) buffer[y+1][x-1] = buffer[y+1][x-1].add(error.mul(3./16));
if (y+1 < height) buffer[y+1][x ] = buffer[y+1][x ].add(error.mul(5./16));
if (x+1<width && y+1<height) buffer[y+1][x+1] = buffer[y+1][x+1].add(error.mul(1./16));
}
}
return image;
}
private static Color3i findClosestPaletteColor(Color3i match, Color3i[] palette) {
Color3i closest = palette[0];
for(Color3i color : palette) {
if(color.diff(match) < closest.diff(match)) {
closest = color;
}
}
return closest;
}
}
class Color3i {
private int r, g, b;
public Color3i(int c) {
Color color = new Color(c);
this.r = color.getRed();
this.g = color.getGreen();
this.b = color.getBlue();
}
public Color3i(int r, int g, int b) {
this.r = r;
this.g = g;
this.b = b;
}
public Color3i add(Color3i o) {
return new Color3i(r + o.r, g + o.g, b + o.b);
}
public Color3i sub(Color3i o) {
return new Color3i(r - o.r, g - o.g, b - o.b);
}
public Color3i mul(double d) {
return new Color3i((int) (d * r), (int) (d * g), (int) (d * b));
}
public int diff(Color3i 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));
}
}
并将我的功能更改为:
for (int y = 0; y < dithImage.getHeight(); ++y) {
for (int x = 0; x < dithImage.getWidth(); ++x) {
final int clr = dithImage.getRGB(x, y);
final int r = (clr & 0xFF0000) >> 16;
final int g = (clr & 0xFF00) >> 8;
final int b = clr & 0xFF;
if(382.5>(r+g+b)) {
buffer.add(0);
} else {
buffer.add(1);
}
}
}
但输出结果看起来……很奇怪?
真不明白为什么会有这样的波浪
BufferedImage image = ...
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
Color color = new Color(image.getRGB(x, y));
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
int mono = (red+green+blue)/255;
//Adding to image buffer
int col = (0 << 24) | (mono << 16) | (mono << 8) | mono;
image.setRGB(x,y,col);
}
}
试试这个
您做错的是,您没有尝试将图片转换为灰度,而是尝试将其转换为黑白。
我终于成功了!我改进了diff
函数,把if(382.5>(r+g+b))
改成了if(765==(r+g+b))
。
我的DitheringUtils
-class:
import java.awt.Color;
import java.awt.image.BufferedImage;
public class DitheringUtils {
public static BufferedImage dithering(BufferedImage image) {
Color3i[] palette = new Color3i[] {
new Color3i(0, 0, 0),
new Color3i(255, 255, 255)
};
int width = image.getWidth();
int height = image.getHeight();
Color3i[][] buffer = new Color3i[height][width];
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
buffer[y][x] = new Color3i(image.getRGB(x, y));
}
}
for(int y=0; y<image.getHeight();y++) {
for(int x=0; x<image.getWidth();x++) {
Color3i old = buffer[y][x];
Color3i nem = findClosestPaletteColor(old, palette);
image.setRGB(x, y, nem.toColor().getRGB());
Color3i error = old.sub(nem);
if (x+1 < width) buffer[y ][x+1] = buffer[y ][x+1].add(error.mul(7./16));
if (x-1>=0 && y+1<height) buffer[y+1][x-1] = buffer[y+1][x-1].add(error.mul(3./16));
if (y+1 < height) buffer[y+1][x ] = buffer[y+1][x ].add(error.mul(5./16));
if (x+1<width && y+1<height) buffer[y+1][x+1] = buffer[y+1][x+1].add(error.mul(1./16));
}
}
return image;
}
private static Color3i findClosestPaletteColor(Color3i match, Color3i[] palette) {
Color3i closest = palette[0];
for(Color3i color : palette) {
if(color.diff(match) < closest.diff(match)) {
closest = color;
}
}
return closest;
}
}
class Color3i {
private int r, g, b;
public Color3i(int c) {
Color color = new Color(c);
this.r = color.getRed();
this.g = color.getGreen();
this.b = color.getBlue();
}
public Color3i(int r, int g, int b) {
this.r = r;
this.g = g;
this.b = b;
}
public Color3i add(Color3i o) {
return new Color3i(r + o.r, g + o.g, b + o.b);
}
public Color3i sub(Color3i o) {
return new Color3i(r - o.r, g - o.g, b - o.b);
}
public Color3i mul(double d) {
return new Color3i((int) (d * r), (int) (d * g), (int) (d * b));
}
public int diff(Color3i o) {
int Rdiff = o.r - r;
int Gdiff = o.g - g;
int Bdiff = o.b - b;
int distanceSquared = Rdiff * Rdiff + Gdiff * Gdiff + Bdiff * Bdiff;
return distanceSquared;
}
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));
}
}
最终写入函数:
for (int y = 0; y < dithImage.getHeight(); ++y) {
for (int x = 0; x < dithImage.getWidth(); ++x) {
final int clr = dithImage.getRGB(x, y);
final int r = (clr & 0xFF0000) >> 16;
final int g = (clr & 0xFF00) >> 8;
final int b = clr & 0xFF;
if(765==(r+g+b)) {
buffer.add(0);
} else {
buffer.add(1);
}
}
}
谢谢大家!