将 PNG 转换为位图自定义十六进制字符串

问题描述 投票:0回答:1

这与此相反:

从十六进制字符串 Java 创建位图图像

我需要将 PNG 作为输入,并将其转换为十六进制字符串。十六进制字符串的创建方式规范如下:

从下到上读取每个像素列,根据附加的编码方案将四个像素编码为字母/数字(A-F 0-9)。

位图代码是为硬件开发的十六进制代码。代码中的每个数字控制显示器上四个虚拟像素的垂直部分。第一个代码字母控制从左下角开始向上的四个像素。第二个代码字母控制第一个代码字母上方的四个像素,第三个代码字母控制第二个代码字母上方的接下来的四个像素,第四个字母控制左侧第一列最上面的四个像素。然后第五个字母控制第二列底部的四个像素,依此类推。

这是我尝试过的方法,但不起作用:-(

public AppResponse<String> getBitmapCode(MultipartFile pngFile) throws IOException {

        BufferedImage img = ImageIO.read(pngFile.getInputStream());

        int height = img.getHeight();
        int width = img.getWidth();

        StringBuilder bitmapCode = new StringBuilder();

        // Process each column
        for (int x = 0; x < width; x++) {
            int pixelValue = 0;

            // Process each pixel in the column from bottom to top
            for (int y = height - 1; y >= 0; y--) {
                int rgb = img.getRGB(x, y);

                // Check if the pixel is closer to white or black using a threshold
                int bit = (getColorDistance(rgb, Color.WHITE) < getColorDistance(rgb, Color.BLACK)) ? 0 : 1;

                pixelValue = (pixelValue << 1) | bit;

                // Check if 4 pixels have been processed
                if ((height - 1 - y) % 4 == 0) {
                    // Convert the pixel value to the corresponding character
                    char encodedChar = encodePixelValue(pixelValue);
                    bitmapCode.append(encodedChar);
                    pixelValue = 0;
                }
            }
        }

        return AppResponse.responseOk("Success", bitmapCode.toString());
    }

    private static int getColorDistance(int rgb1, Color color2) {
        Color color1 = new Color(rgb1 & 0x00FFFFFF); // Remove alpha channel
        return (int) Math.sqrt(
                Math.pow(color1.getRed() - color2.getRed(), 2) +
                        Math.pow(color1.getGreen() - color2.getGreen(), 2) +
                        Math.pow(color1.getBlue() - color2.getBlue(), 2)
        );
    }

    private static char encodePixelValue(int pixelValue) {
        if (pixelValue >= 0 && pixelValue <= 9) {
            return (char) ('0' + pixelValue);
        } else if (pixelValue >= 10 && pixelValue <= 15) {
            return (char) ('A' + (pixelValue - 10));
        } else {
            throw new IllegalArgumentException("Invalid pixel value: " + pixelValue);
        }
    }

PNG附在这里:

图像应生成如下所示的十六进制:

000700070007FFFFFFFF00070007000700000000FFFFFFFFE1C7E1C7E1C7E1C7E00700000000183C787EF0C7E187E187E30F7E1E3C1C00000000000700070007FFFFFFFF00070007000700000000FFFFFFFF00000000FFFFFFFF00FC07E03F00FFFFFFFF000000003FFCFFFEE007E707E7077F3EFF3C0000

我从代码中得到的输出:

java javax.imageio twelvemonkeys
1个回答
0
投票

您的代码大部分是正确的。只有两个小错误。

  1. 您的代码的这一部分有一个差一错误:

    if ((height - 1 - y) % 4 == 0) {
        // Convert the pixel value to the corresponding character
        char encodedChar = encodePixelValue(pixelValue);
        bitmapCode.append(encodedChar);
        pixelValue = 0;
    }
    

    将条件更改为

    (height - y) % 4 == 0)

  2. 当颜色比黑色更接近白色时,您需要将

    bit
    变量设置为
    1
    ,而不是
    0
    。换句话说,将
    condition ? 0 : 1
    更改为
    condition ? 1 : 0


如果您想了解另一种解决方案,那么这里的解决方案基本上是上一个问题的答案中的代码的一对一反转。

首先,这是另一个问题的代码,稍作修改。请注意,它使用了 Java 17 中添加的

HexFormat
类。

static BufferedImage decode(String hex, int height) {
  if (height % 8 != 0) {
    throw new IllegalArgumentException("height must be multiple of 8");
  }
  var bytes = HexFormat.of().parseHex(hex);
  var image = new BufferedImage(bytes.length / (height / 8), height, TYPE_BYTE_BINARY);
  for (int x = 0; x < image.getWidth(); x++) {
    for (int y = 0; y < height; y++) {
      int pos = height * x + y;
      int bit = (bytes[pos / 8] >> (7 - (pos % 8))) & 1;
      int rgb = bit == 0 ? 0x000000 : 0xFFFFFF;
      image.setRGB(x, height - y - 1, rgb);
    }
  }
  return image;
}

这是执行相反操作的代码(即,它将位存储在字节中,而不是从字节中提取位)。

static String encode(BufferedImage image) {
  if (image.getHeight() % 8 != 0) {
    throw new IllegalArgumentException("image height must be multiple of 8");
  }
  var bytes = new byte[image.getWidth() * (image.getHeight() / 8)];
  for (int x = 0; x < image.getWidth(); x++) {
    for (int y = 0; y < image.getHeight(); y++) {
      int rgb = image.getRGB(x, image.getHeight() - y - 1);
      if (isCloserToWhiteThanBlack(rgb)) {
        int pos = image.getHeight() * x + y;
        bytes[pos / 8] |= 1 << (7 - (pos % 8));
      }
    }
  }
  return HexFormat.of().withUpperCase().formatHex(bytes);
}

其中

isCloserToWhiteThanBlack
本质上与您代码中的逻辑相同。

© www.soinside.com 2019 - 2024. All rights reserved.