【问题标题】:Converting BufferedImage pixel data into a readable output?将 BufferedImage 像素数据转换为可读输出?
【发布时间】:2014-06-17 07:22:00
【问题描述】:

我正在尝试编写一个程序,将旧 GBA 游戏的地图划分为 16x16 的图块,保存每个图块,然后将每个抓取的图块的原始图像数据与另一个保存的图块列表进行比较,并自动确定它是否是相同的图像。到目前为止,我已经成功地将地图划分为 16x16 的图块,自动将每个 16x16 的图块保存到一个新的图像文件中,然后将所有这些图像文件作为 BufferedImages 加载到一个数组中。

 public TileSorter()
 {
   for (int a = 0; a < 1269; a++)
   {
     try {
       img[a] = ImageIO.read(new File("img" + a + ".jpg"));
     } catch (IOException e) {}
     System.out.println("img" + a + ".jpg loaded into Array.");
   }
 }

我现在要做的是分析一个数组中每个图块(BufferedImage)的原始数据,并确定它是否与另一个数组中保存的任何 BufferedImage 图块相同。 我四处寻找答案并尝试了 getRGB() 方法和 getData() 栅格方法:

    img[a].getRGB(0,0,16,16,rawImgData[a],0,0);

    rawImgData[a] = ((DataBufferByte) img[a].getRaster().getDataBuffer()).getData();

问题是:据我所见,从这些方法返回的 byte[] 和 int[] 数据对于任何两张完全相同的图片都不相同。我需要一种将图像数据转换为可用于比较两张完全相同的图片的原始 int[] 或 byte[] 数据的方法。 (IE;如果两张图片都只是一个 16x16 的黑色像素数组,它们应该输出相同的 getRGB() 或 getData() 值。) 有没有一种方法可以用来将这些 BufferedImage 转换为可以轻松相互比较的原始图像数据?

任何建议将不胜感激,谢谢。

【问题讨论】:

    标签: java image awt


    【解决方案1】:

    首先:JPG 格式不是无损压缩。这意味着对于像

    这样的序列
    BufferedImage imageA = loadImage("image.jpg");
    saveAs("saved.jpg");
    BufferedImage imageB = loadImage("saved.jpg");
    
    somehowCompare(imageA, imageB);
    

    somehowCompare 方法很可能会发现图像相等,因为 JPG 压缩引入了伪影。

    毫无疑问,您应该将图像存储为 PNG,因为它是无损的,并且对于上述序列,图像将被视为“相等”——这意味着它们具有完全相同的 RGB 像素值。

    但这是您的实际问题:如何比较这些图像?

    获取数据缓冲区的方法将是最有效的方法。但仍有一些注意事项:图像是否包含DataBufferIntDataBufferByte 取决于细微的细节。它可能取决于文件类型(JPG、GIF 或 PNG),或者图像是否包含透明度(如果是 PNG 或 GIF),或者取决于压缩方法。

    解决此问题的一种方法是将每个新加载的图像绘制成您知道包含DataBufferInt 的图像。这意味着您可以使用类似的方法

    public static BufferedImage convertToARGB(BufferedImage image)
    {
        BufferedImage newImage = new BufferedImage(
            image.getWidth(), image.getHeight(),
            BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = newImage.createGraphics();
        g.drawImage(image, 0, 0, null);
        g.dispose();
        return newImage;
    }
    

    然后加载你的图片

    img[a] = convertToARGB(ImageIO.read(new File("img" + a + ".png")));
    

    生成的图像将具有DataBufferInt。从这个缓冲区中,您可以获得int[] 数组形式的数据。在最好的情况下,您可以使用

    简单地比较两个这样的图像的数组
    DataBufferInt bufferA = (DataBufferInt)imageA.getRaster().getDataBuffer();
    DataBufferInt bufferB = (DataBufferInt)imageB.getRaster().getDataBuffer();
    int arrayA[] = bufferA.getData();
    int arrayB[] = bufferB.getData();
    if (Arrays.equal(arrayA, arrayB))
    {
        // Images are equal!
    }
    

    另一种方法是简单地从图像中获取单个像素:

    // Assuming equal-sized images:
    for (int y=0; y<imageA.getHeight(); y++)
    {
        for (int x=0; x<imageA.getWidth(); x++)
        {
            int rgbA = imageA.getRGB(x,y);
            int rgbB = imageB.getRGB(x,y);
            if (rgbA != rgbB) 
            { 
                // Images are NOT equal!
            }
        }
    }
    

    后一种方法的优势在于,通过适当调整if-statement,当图像具有“非常相似”(但不完全相等)的像素值时,您可以将它们视为“相等”。

    【讨论】:

    • 不幸的是,我尝试了上述两种方法,但都没有奏效。我确保所有使用的图像都是 .png 格式,并将它们转换/加载为 int ARBG,但是 getData() 方法不断为两个(字面意思相同,以 .png 格式复制)图块吐出完全不同的数字。关于为什么会这样的任何建议或想法?
    • @ChristianAbbott 您不应将此答案标记为“已接受”,只要它无助于解决您的问题。您可以通过上传并编辑原始问题来提供这两个(“相同”)图像吗?那么可能会更容易找出那里可能出现的问题。
    • 抱歉,它一开始似乎有效,所以我将其标记为已接受。稍作修改后,我意识到我正在解析瓷砖的原始图片并非像我想象的那样无损。我解决了这个问题并重新尝试了这个解决方案,一切正常。感谢您的帮助,这现在可以成为公认的答案。
    【解决方案2】:

    首先想到的是 JPG 是一种有损格式,不应该用于精确像素图形,尤其是在 16x16 分辨率下。回读 JPG 时,您不应期望得到与最初写入 JPG 完全相同的 RGB 值。

    我已经成功地在单元测试中使用 PNG 作为参考图像,以与生成的 BufferedImage 进行比较。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多