这与此相反:
我需要将 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);
}
}
图像应生成如下所示的十六进制:
000700070007FFFFFFFF00070007000700000000FFFFFFFFE1C7E1C7E1C7E1C7E00700000000183C787EF0C7E187E187E30F7E1E3C1C00000000000700070007FFFFFFFF00070007000700000000FFFFFFFF00000000FFFFFFFF00FC07E03F00FFFFFFFF000000003FFCFFFEE007E707E7077F3EFF3C0000
您的代码大部分是正确的。只有两个小错误。
您的代码的这一部分有一个差一错误:
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)
。
当颜色比黑色更接近白色时,您需要将
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
本质上与您代码中的逻辑相同。