Java - 将图像转换为黑白图像 - 失败并显示明亮的颜色

Java - Convert Image to black and white - fails with bright colors

我正在尝试将图像仅转换为黑白(而非灰度)。

我用过这个:

BufferedImage blackAndWhiteImage = new BufferedImage(
        dWidth.intValue(),
        dHeight.intValue(),
        BufferedImage.TYPE_BYTE_BINARY);
Graphics2D graphics = blackAndWhiteImage.createGraphics();
graphics.drawImage(colourImage, 0, 0, null);

return blackAndWhiteImage;

一切都很好,直到我决定尝试更亮的颜色,例如 Google 徽标:

结果是这样的:

然后我先尝试通过灰度图,首先使用:

BufferedImage blackAndWhiteImage2 = new BufferedImage(
        dWidth.intValue(),
        dHeight.intValue(),
        BufferedImage.TYPE_USHORT_GRAY);

它似乎保存了蓝色,但不是最亮的颜色(在本例中是黄色),而且您可能会看到它的质量下降了:

非常感谢任何建议;我相信我正在寻找的是将除白色(这将是背景颜色)之外的所有颜色转换为黑色,这在应用 TYPE_BYTE_BINARY 删除 alpha 通道时已经完成.


编辑: 可能我没有解释的很清楚:

**1 - 在某些情况下图像实际上是黑底白字..这很烦人 (whiteOnBlackExample) 因为它使这个过程复杂化了很多,我稍后会把它留在这里,因为现在的首要任务是转换 "normal" 个图像。

我所做的是,如果存在 Alpha 通道,则首先将其剥离 -> 因此将 Alpha 通道转换为白色;然后将所有其他颜色转换为黑色

如果您使用 JavaFX,您可以使用亮度为 -1(最小值)的 ColorAdjust 效果,这会使所有(非白色)颜色变为黑色:

public class Main extends Application {

    Image image = new Image("https://i.stack.imgur.com/UPmqE.png");

    @Override
    public void start(Stage primaryStage) {
        ImageView colorView = new ImageView(image);
        ImageView bhView = new ImageView(image);

        ColorAdjust colorAdjust = new ColorAdjust();
        colorAdjust.setBrightness(-1);
        bhView.setEffect(colorAdjust);

        primaryStage.setScene(new Scene(new VBox(colorView, bhView)));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

这些 Effect 已经过优化,因此它们可能比您手动应用它们所达到的速度更快。

编辑

既然你的要求是

  1. 任何不透明的像素都应转换为白色,并且
  2. 任何不是白色的像素都应转换为黑色,

据我所知,预先设计的效果不适合您 - 它们太具体了。您可以逐个像素地进行操作:

WritableImage writableImage = new WritableImage(image.getPixelReader(), (int) image.getWidth(), (int) image.getHeight());
PixelWriter pixelWriter = writableImage.getPixelWriter();
PixelReader pixelReader = writableImage.getPixelReader();
for (int i = 0; i < writableImage.getHeight(); i++) {
    for (int j = 0; j < writableImage.getWidth(); j++) {
        Color c = pixelReader.getColor(j, i);
        if (c.getOpacity() < 1) {
            pixelWriter.setColor(j, i, Color.WHITE);
        }
        if (c.getRed() > 0 || c.getGreen() > 0 || c.getBlue() > 0) {
            pixelWriter.setColor(j, i, Color.BLACK);
        }
    }
}
ImageView imageView = new ImageView(writableImage);

请注意,应用规则的顺序很重要。如果先应用 1 再应用 2,透明的非白色像素将变为白色,但如果先应用 2 再应用 1,它将最终变为黑色。这是因为预定义的 WHITEBLACK 颜色是不透明的。您可以手动设置红色、绿色和蓝色值,而不更改 alpha 值。这完全取决于您的具体要求。

请记住,由于某些文件格式的有损压缩,您可能根本找不到真正的白色,但接近真正白色的值您的眼睛将无法分辨。

这是我评论中的例子。首先打开输入图像并创建一个新图像用于输出。

BufferedImage myColorImage = ImageIO.read(fileInput);
BufferedImage myBWImage = new BufferedImage(myColorImage.getWidth(), myColorImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);

然后遍历所有像素并将 rgb 值与阈值进行比较:

for (int x = 0; x < myColorImage.getWidth(); x++)
    for (int y = 0; y < myColorImage.getHeight(); y++)
        if (rgbToGray(myColorImage.getRGB(x, y), MODE.AVERAGE) > threshold)
            myBWImage.setRGB(x, y, 0);
        else
            myBWImage.setRGB(x, y, 0xffffff); 

这里是 rgbToGray 与阈值比较的方法实现:

private static int rgbToGray(int rgb, MODE mode) {
    // split rgb integer into R, G and B components
    int r = (rgb >> 16) & 0xff;
    int g = (rgb >> 8) & 0xff;
    int b = rgb & 0xff;
    int gray;
    // Select mode
    switch (mode) {
        case LIGHTNESS:
            gray = Math.round((Math.max(r, Math.max(g, b)) + Math.min(r, Math.min(g, b))) / 2);
            break;
        case LUMINOSITY:
            gray = Math.round(0.21f * r + 0.72f * g + 0.07f * b);
            break;
        case AVERAGE:
        default:
            gray = Math.round((r + g + b) / 3);
            break;
    }
    return gray;
}

实用程序枚举:

private enum MODE {
    LIGHTNESS, AVERAGE, LUMINOSITY
}

我得到以下结果:

注意:你的google图片连threshold = 1也合适,其他图片你自己挑[0..255] 范围内的另一个值。对于照片,很可能更合适的值约为 100-150。 MODE也会影响最终结果。