如何将图像保存和读取为文本,嵌入在代码中

Posted

技术标签:

【中文标题】如何将图像保存和读取为文本,嵌入在代码中【英文标题】:How to save and read image as text, embedded in the code 【发布时间】:2017-02-14 13:33:18 【问题描述】:

我知道这是一个奇怪的问题,但我真的需要它来工作。 我有一个非常小的.png 图像,我需要将其导入(“键入”)到 java 源文件中,而无需从文件系统中读取。 (至少在程序运行时,我可以用其他虚拟应用程序读取系统上的图像来解析是二进制的,或者我不知道还有什么)。

换句话说,我确实需要代表图像的代码行,我只会将其放在那里一次(我不需要即时导入,我只需键入它并永远留在那里)。

图像需要存储为ImageIcon,并且由于您可以输入字节数组,我不明白为什么不能创建一个读取图像、打印字节数组的小罐子到 .txt 文件,然后我可以将其复制并放入源文件中。我只是不知道如何准确地执行这些步骤。

注意: 我知道这不是不是一个人会或应该做的事情,它是针对特定项目的,我需要这样做,原因超出了我的范围。

注意 2: 我不能使用任何外部库或依赖项。

【问题讨论】:

myjeeva.com/… 我认为这篇文章的布局很好。还提供了示例代码。 非常抱歉我忘记添加了,该文件必须没有依赖项,我不能使用 Apache。无论如何,我会看一下这篇文章,看看我是否可以从中做出一些事情,谢谢 您是否考虑过在您的应用程序中使用实际的 png 作为资源? 【参考方案1】:

仅供参考,与此类似的问题可能有助于提供更深入的解释/答案是here 和here。

我的回答是为了解决你的问题,

我确实需要代表图像的代码行,我只会将其放在那里一次(我不需要即时导入,我只需输入它并永远留在那里)。

是使用 Base64 并以特定格式读取图像代码。

例如,以下代码制作了一个简单的 JFrame 以将 base64 字符串显示为图像图标:

import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Scanner;

import javax.imageio.ImageIO;
import javax.swing.*;

public class App extends JFrame 
    public static void main(String args[]) 
        SwingUtilities.invokeLater(new Runnable() 
            public void run() 
                App app = new App();
                app.setVisible(true);
            
        );
    
    public App() 
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel photo = new JLabel();
        photo.setVerticalTextPosition(JLabel.BOTTOM);
        photo.setHorizontalTextPosition(JLabel.CENTER);
        photo.setHorizontalAlignment(JLabel.CENTER);
        photo.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
        add(photo, BorderLayout.CENTER);
        setSize(200, 100);
        setLocationRelativeTo(null);

        // Ex. ***.ico (then converted to png for this example)
        String imgBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAJ+SURBVFhH7ZXbS1RRFMb7Y5qHiu4PEfUQRS9B9FBEBBVFT70WvQQhmJpdLUkMS0mNikDKlKxIDR+iCz3YmJgWCZmlKWPNPvd9vvZeZ8+ZS3NmzlzoYn2wHmatYe/f2etbey/Ab9Z/gPkFwCcGoTVtB/8yojL5VTYA1/gGdnIV4hULoV3cBFefU5XcKusJ2G96BECEIPT2A4LKUZVglQYgNuBTo+qHJ7P3PAHIMB+dUtlglQRgdh1HvHIx7Jc3VEbI5dCvH1QQEdjRblXIrqIB3NhHsDNr/a81Oo4AlubVhB+0+s1erWop+OdhymdTSSfgfp+C3rLbh9AatoB/fU81Pv0OrGYl5VndBrhslvKZKt2E3KFeJ8zHalbAHrpPJWfksZ/XW/eSZzJVMIA92CkW7qVep8oZfuiPodzU7KkScDas/nqVE6aUuQwVBODN+mpaTLuwEdbTFsolxGc+QGvc6m+oN++COzcJ/eYhsOpl6WZVKghAjpxcNLGBDFa9HGZ3hd972AaMO0eT9dNr4IwNgM+Oe/UMFeUB/inqbSJGMAkTocvHGe2n9tivbiN+YgnYufV0CkEqyYQum4H15BLY2XUpIKI9YgStZ61wxl/kfRdCAzhv+4QB74rxGvvZzcJs9ut70K7sSAOxBhrVH4IVGkC/tie5uLhc9Ks7qffyqPnkEEFIyRfR6DhMl5RrximXS+EB2vbTjKd+YVpULoJ2eRuMzmOwnrfTRIRRYR4Q5pJut6Nd4vKpFZfLPrBabywzQ7YsjAIBmttuUYSRG5sQF9EDmH119BCR87UY1fKtUxaAXCobQLZjzhUJzR+AYvX3A5QrgvTnAvwq/esAwA/q0/BiaJrlDgAAAABJRU5ErkJggg==";
        String data = imgBase64.substring(imgBase64.indexOf(",") + 1);
        ByteArrayInputStream stream = new ByteArrayInputStream(Base64.getDecoder().decode(data.getBytes()));
        BufferedImage image = null;
        try 
            image = ImageIO.read(stream);
         catch (IOException e) 
            e.printStackTrace();
        
        ImageIcon imgIcon = new ImageIcon(image.getScaledInstance(32, 32, 0));
        photographLabel.setIcon(imgIcon);
    

此代码不使用任何外部库并完成您请求的大部分任务 - 因此,为了完全完成答案,我将如何从控制台读取该输入(也就是用户在控制台中键入)。注意:以下部分代码将添加在 ByteArrayInputStream 流对象声明行之后。

Scanner sc = new Scanner(System.in);
System.out.println("Type .png input (base64): ");
StringBuilder sb = new StringBuilder();
while (sc.hasNext())
    sb.append(sc.next());
String data = sb.toString();

这实际上将替换前面示例 sn-p 中的“String imgBase64”和“String data”两行。

希望这能回答您的问题,请提供反馈以进一步澄清或标记为答案;)

干杯

编辑: 对于从图像到字符串,图像->字符串: 注意:我在这个例子中使用了一个文件,你也可以选择查看可以在 ImageIO.read() 方法中交替使用的 URL 或 Stream 选项。 另请注意:try-with-resources 是 java 1.7+(如果需要早期版本,可以轻松恢复为 try-catch)

假设您从某个地方加载了一个 ImageIcon 对象,URL/流/文件,您可以命名它。在我的示例中,我将使用本地文件:

ImageIcon icon = new ImageIcon("C:\\Users\\Nick\\Desktop\\favicon.png");

现在获取这个图像图标并获取您要求的 base64 字符串?这是一种方法/我是如何做到的(使用已加载的 ImageIcon 图标对象):

try (ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageOutputStream ios = ImageIO.createImageOutputStream(os)) 
    BufferedImage img = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.BITMASK);
    Graphics g = img.createGraphics();
    icon.paintIcon(null, g, 0, 0);
    g.dispose();
    ImageIO.write(img, "png", ios);
    byte[] bytes = os.toByteArray();
    String data = Base64.getEncoder().encodeToString(bytes);
    System.out.println(data);
 catch (IOException e) 
    e.printStackTrace();

我想指出的一件事是使用了“BufferedImage.BITMASK”标志。这很容易混淆或切换,并导致与解码/编码的字符串值不一致。

更多关于 ImageIcon 类的信息在这里:https://docs.oracle.com/javase/7/docs/api/javax/swing/ImageIcon.html

使用此工具将 *** 图标转换为 base64 https://www.base64-image.de/

【讨论】:

【参考方案2】:

以netpbm 格式“输入”您的图像,然后将其转换为 PNG 或其他格式。

例如,我正在使用 SE markdown 编辑器创建此图像:

P3 3 3 255
0 0 0  0 0 0    0 0 0 
0 0 0  0 255 0  0 0 0
0 0 0  0 0 0    0 0 0

这是一个 3x3 的图像,由一个被黑框包围的单个绿色像素。

将其保存为“image.ppm”。 使用 ImageMagick 将其转换为 PNG(将其放大 20 倍以便您可以看到):

magick convert image.ppm -sample 2000% image.png

(在这里您可以将 image.ppm 转换为您的 ImageIcon 格式)

显示它:

您不想直接“输入”到 PNG 格式,因为这会很奇怪:

data:image/png;base64,
UDMgMyAzIDI1NQogICAgMCAwIDAgIDAgMCAwICAgIDAgMCAwIAogICAgMCAwIDAgIDAgMjU1IDAg
IDAgMCAwCiAgICAwIDAgMCAgMCAwIDAgICAgMCAwIDAK

如果您从 PNG 开始并想“键入”它,您可以再次使用 ImageMagick,但方向相反。

例如,获取您的头像并将其转换为 mark.ppm

magick convert 8ede7*.png -compress none mark.ppm
cat mark.ppm
P3 32 32 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 232 136 234 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 229 119
231 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 239 174 241 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 255 255 255 255 255 255 232 136 234 207 1 211 255 255
255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255 255
255 255 255 255 207 1 211 229 119 231 255 255 255 255 255 255 255 255
[...]

如果你能处理“C”风格的标题,你可以试试

 magick 8ede7*.png mark.gif
 magick mark.gif mark.h
 cat mark.h
  mark.h (GIF).
*/
static unsigned char
  MagickImage[] =
  
    0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x20, 0x00, 0x20, 0x00, 0xF3, 0x0E,
    0x00, 0xCF, 0x01, 0xD3, 0xD7, 0x30, 0xDB, 0xD9, 0x38, 0xDC, 0xDA, 0x40,
    0xDD, 0xDC, 0x49, 0xDF, 0xE2, 0x67, 0xE4, 0xE5, 0x77, 0xE7, 0xE8, 0x88,
    0xEA, 0xEF, 0xAE, 0xF1, 0xF1, 0xB6, 0xF2, 0xF3, 0xBF, 0xF4, 0xF4, 0xC7,
[...]

【讨论】:

但是如果图像恰好更复杂,例如高度压缩的照片怎么办?

以上是关于如何将图像保存和读取为文本,嵌入在代码中的主要内容,如果未能解决你的问题,请参考以下文章

如何将资源嵌入到单个可执行文件中?

如何使用 C# 将 InkML 文件读取为图像或文本

如何使用 python 从 PDF 表单中读取数据

如何在 C# 中从位图中读取文本? [关闭]

如何通过 tesseract OCR 读取黑色背景图像上的黑色文本?

如何在 Flutter 中从 firebase 存储读取和写入文本文件?