为什么 ImageIO 不读取 BMP 文件,直到将其重新保存在 MS Paint 中?

发布于 2024-12-17 06:13:54 字数 1859 浏览 3 评论 0原文

我有一个位图文件 test3.bmp,我可以使用我测试过的每个图像查看器来查看和编辑该文件。

也就是说,我无法将其读入我的 Java 应用程序。如果我在 MS Paint 中编辑 BMP,保存它,撤消更改,然后保存它 (test3_resaved.bmp),我会得到相同的图像,但文件大小不同。不同的文件大小不关心我......我关心的是我的应用程序可以读取重新保存的文件。

谁能告诉我为什么一张图像适用于我的代码,而另一张图像则不能?

图片文件:

这是一个最小的测试应用程序:

package Test;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

@SuppressWarnings("serial")
public class Test extends JFrame {
    private ImageIcon imageIcon;

    public Test(String filename) throws IOException {
        super();
        BufferedImage image = javax.imageio.ImageIO.read(new File(filename));
        imageIcon = new ImageIcon(image);
        setVisible(true);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        repaint();
    }

    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        setSize(imageIcon.getIconWidth(), imageIcon.getIconHeight());
        if (imageIcon != null)
            g2d.drawImage(imageIcon.getImage(), 0, 0, this);
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            if (args.length > 0)
                new Test(args[0]);
            else
                System.out.println("usage - specify image filename on command line");
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }

}

I have a bitmap file, test3.bmp, which I can view and edit with every image viewer I have tested with.

That said, I cannot read it into my Java application. If I edit the BMP in MS Paint, save it, undo the change, and save it (test3_resaved.bmp), I have the same image, but with a different file size. The different file sizes do not concern me... what does, is that my application can read the re-saved file.

Could anyone enlighten me on why one image works with my code but the other does not?

Images files:

Here is a minimal test application:

package Test;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.swing.ImageIcon;
import javax.swing.JFrame;

@SuppressWarnings("serial")
public class Test extends JFrame {
    private ImageIcon imageIcon;

    public Test(String filename) throws IOException {
        super();
        BufferedImage image = javax.imageio.ImageIO.read(new File(filename));
        imageIcon = new ImageIcon(image);
        setVisible(true);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        repaint();
    }

    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        setSize(imageIcon.getIconWidth(), imageIcon.getIconHeight());
        if (imageIcon != null)
            g2d.drawImage(imageIcon.getImage(), 0, 0, this);
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            if (args.length > 0)
                new Test(args[0]);
            else
                System.out.println("usage - specify image filename on command line");
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

瘫痪情歌 2024-12-24 06:13:54

(扩展我的评论)

问题归结为:人们通常认为以下命令给出的“格式”:

ImageIO.getReaderFileSuffixes();

受 Java 支持。

但这不是它应该被阅读/理解的方式,因为这根本不是它的工作原理。

错误:“ImageIO 可以读取使用这些格式之一编码的任何文件”

正确:“ImageIO 无法读取使用非这些格式之一的格式编码的图像”

但现在这对列表中出现的格式有何说明?嗯...这变得很棘手。

例如,该列表通常返回“PNG”和“BMP”(以及其他格式)。但不存在“一个”PNG 或“一个”BMP。明天我可以带着一个“有效”的 PNG(子)格式来,这种格式完全没问题,但是没有任何一个 PNG 解码器可以解码(它必须经过验证和接受:但是一旦被接受,它就会“破坏”。 “所有现有的 PNG 解码器)。幸运的是,对于 PNG 图片来说,问题还不算太严重。

BMP 格式非常复杂。您可以进行压缩,也可以不进行压缩(这可以解释您所看到的不同文件大小)。您可以有各种标题(不同长度,这也可以解释您所看到的不同文件大小)。哎呀,BMP 实际上是如此复杂,以至于我认为您可以将 PNG 编码的像素嵌入到 BMP“外壳”中。

基本上有两种有问题的 BMP 文件类型:

  • 创建 Java 解码器后出现的 BMP 变体
  • 足够晦涩的 BMP 变体,以致 Java ImageIO 实现者认为不值得支持

“错误”在于认为存在一个PNG 或一种 BMP 格式。这两种格式(以及其他图像格式)实际上都是“可扩展的”。每次新变体出现时,它都有可能破坏任何解码器。

因此,您的情况是这样的:

  1. 您正在从 MS Paint 读取原始 BMP 文件,并且 MS Paint 能够读取该文件,因为它恰好是 MS Paint 能够理解的 BMP 格式。

  2. 相同的 BMP 格式与您正在使用的 Java 版本不同(希望其他 Java 版本会支持它,但我不指望它)。

  3. 当您从 MS Paint 重新保存该文件时,您保存的 BMP 格式绝对与原始格式相同(不同的文件大小很能说明问题)< /p>

  4. < p>您的版本恰好支持其他格式Java。

现在实际解决您的问题:根据我的经验,像 ImageMagick 这样的图像库能够比默认的 Java ImageIO API 读取更多的图片,因此我会看看其他图像库或包装器ImageMagick

这些库的更新速度通常也比 Java 快得多,以支持更新的变体和更新的格式。例如,来自 Google 的令人惊叹的 WebP 格式(在无损+半透明图像上比 PNG 好 28% 到 34%)已经得到相当多的图像处理库的支持,但当我它来执行ImageIO.read(someWebPpicture)...

另一种选择是使用PNG:即使理论上PNG可以扩展,你也不太可能在文件中找到“不支持”的PNG荒野。对于 BMP,这种情况太常见了。

(expanding on my comments)

The problem boils down to this: people typically believe that the "format" given by the following command:

ImageIO.getReaderFileSuffixes();

are supported by Java.

But that's not how it should be read/understood because that is simply not how it works.

Wrong: "ImageIO can read any file encoded with one of these format"

Correct: "ImageIO cannot read image encoded with a format that is not one of these format"

But now what does that say about formats appearing in that list? Well... It gets tricky.

For example that list typically returns both "PNG" and "BMP" (and other formats). But there's not "one" PNG nor "one" BMP. I can come tomorrow with a "valid" PNG (sub)format that would be perfectly fine but that no single PNG decoder out there would decode (it would have to be validated and accepted: but once it would be accepted, it would "break" all the existing PNG decoders out there). Luckily, for the PNG pictures the problem ain't too bad.

The BMP format is very complicated. You can have compression or not (which may explain the varying file size you've seen). You can have various headers (of differing length, which may also explain the varying file sized you've seen). Heck, BMP is actually so complex that I think that you can embed PNG encoded pixels inside a BMP "shell".

There are basically two problematic types of BMP files:

  • BMP variants that appeared after the Java decoder was created
  • BMP variants that are obscure enough so that the Java ImageIO implementors didn't consider it worthy of support

The "error" consists in thinking that there's one PNG or one BMP format. Both formats (and other image formats too) are actually "extensible". An everytime a new variant comes out it has the potential to break any decoder out there.

So what's happening in your case is this:

  1. you're reading your original BMP file from MS Paint and MS Paint is able to read that file because it happens to be a BMP format that MS Paint understands.

  2. that same BMP format is alien to the Java version you're using (there's hope that it will be supported in another Java version but I wouldn't count on it).

  3. when you re-save that file from MS Paint, you're saving in a BMP format that is definitely not the same as the original format (the varying file size being quite a tell)

  4. that other format happens to be supported by your version of Java.

Now to actually solve your problem: in my experience image libraries like ImageMagick are able to read much much more pictures than the default Java ImageIO API so I'd give a look at either other image libraries or wrappers around ImageMagick.

These libraries are also typically updated to support newer variants and newer formats much faster than Java is. For example the amazing WebP format from Google (up to 28% to 34% better than PNG on lossless+translucent images) is already supported by quite some image manipulation libraries but I'm not holding my breath when it comes to do a ImageIO.read( someWebPpicture)...

Another option would be to use PNG: even though theoretically PNG can be extended you're less likely to find "non supported" PNGs in the wild. With BMPs it's all too common.

无敌元气妹 2024-12-24 06:13:54

这里有一些示例代码 http://www.java2s.com/ Code/Java/2D-Graphics-GUI/ListAllreaderandwriterformatssupportedbyImageIO.htm 将枚举 JDK 支持的图像格式。

高级图像工具包支持 BMP http ://www.oracle.com/technetwork/java/release-jai-imageio-1-0-01-140367.html但我知道它里面也有一些现在由基础支持的东西JDK也是。所以如果两者都支持,那么也许JAI支持更全面。但这似乎不太可能,因为它没有多大意义。哦,今天是太阳。

如果你使用JDK 6,你绝对可以做PNG(它更便携),你能转换你的图像吗? IIRC MS Paint 将保存 png。

There's some example code here http://www.java2s.com/Code/Java/2D-Graphics-GUI/ListAllreaderandwriterformatssupportedbyImageIO.htm that will enumerate supported image formats by your JDK.

BMP is supported by the advanced image toolkit http://www.oracle.com/technetwork/java/release-jai-imageio-1-0-01-140367.html but I know that it also has things in it that are now supported by the base JDK too. So if its supported by both, then maybe the JAI support is more comprehensive. That seems unlikely though, as it doesn't make much sense. OTOH, it was Sun.

If you're using JDK 6, you can definitely do PNG (which is more portable), can you convert your images? IIRC MS Paint will save a png.

终止放荡 2024-12-24 06:13:54

我使用自己的 Java BMP 解码器测试了这两个图像。它还转储图像的一些信息。我发现原来的bmp是32位的,重新保存的是24位的。所以我假设 imageio bmp 阅读器无法正确处理 32 位 bmp。

更新:为了证实我很久以前的猜测,我再次测试了图像,但仍然存在问题。问题是没有图像显示,原因是 Java ImageIO 认为图像是完全透明的。以下是 Java ImageIO 创建的 BufferedImage 的转储:

 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000
 IntegerInterleavedRaster: width = 494 height = 516 #Bands = 4 xOff = 0 yOff = 0
 dataOffset[0] 0
 java.awt.image.SinglePixelPackedSampleModel@80809ee

我们可以看到这里有 4 个波段代表 Java ImageIO 解释的 RGBA。事实上,第四个波段或第四个字节不适用于 32 位 Windows BMP 图像的 Alpha 通道。它只是垃圾或使其双字对齐。

Windows 3.x BMP 解码器以及更多内容来自这里 https://github.com/dragon66/icafe

I tested the two images using my own Java BMP decoder. It also dumps some information of the image. I found the original one is a 32 bits bmp and the re-saved one is a 24 bits one. So I assume imageio bmp reader cannot handle 32 bit bmp correctly.

Update: To confirm my long time ago guess, I tested the image again and still problematic. The problem is there is no image showing and the reason is Java ImageIO thinks the image is completely transparent. The following is a dump from Java ImageIO created BufferedImage:

 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000
 IntegerInterleavedRaster: width = 494 height = 516 #Bands = 4 xOff = 0 yOff = 0
 dataOffset[0] 0
 java.awt.image.SinglePixelPackedSampleModel@80809ee

We can see here there are 4 bands representing RGBA as Java ImageIO interpreted it. The truth is the fourth band or the fourth byte is not for alpha channel for a 32 bit Windows BMP image. It is simply garbage or to make it double word aligned.

A Windows 3.x BMP decoder and lots more from here https://github.com/dragon66/icafe

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文