无法在 Java JAI 中读取 JPEG 2000 图像,错误:“文件太长". [英] Unable to read JPEG 2000 images in Java JAI, Error: "File too long."

查看:66
本文介绍了无法在 Java JAI 中读取 JPEG 2000 图像,错误:“文件太长".的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要某种方法来使用 Java 读取某些 JPEG 2000 图像并将它们加载到 BufferedImage 中.我一直在使用 JAI-ImageIO 来读取 JPEG 2000 图像,因为常规 ImageIO.read 不支持该格式.我首先使用图像编辑器制作了一个自定义的 jp2 图像,Java 程序运行流畅并加载了图像.但这只是一个测试.真实图像大小约为 100MB.但是,每当我在它们上运行代码时,都会出现此错误:

I need some way to read certain JPEG 2000 images using Java and load them into a BufferedImage. I've been using JAI-ImageIO to read the JPEG 2000 images, since regular ImageIO.read doesn't support that format. I first made a custom jp2 image using an image editor and the Java program ran smoothly and loaded the image. But that was just a test. The real images are around 100MB in size. However, whenever I run the code on them, I get this error:

Exception in thread "main" java.lang.RuntimeException: An uncaught runtime exception has occurred
    at com.sun.media.imageioimpl.plugins.jpeg2000.J2KReadState.initializeRead(J2KReadState.java:708)
    at com.sun.media.imageioimpl.plugins.jpeg2000.J2KReadState.<init>(J2KReadState.java:209)
    at com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReader.read(J2KImageReader.java:449)
    at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1468)
    at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1315)
    at JPEG2000Handler.getImage(JPEG2000Handler.java:18)
    at JPEG2000Handler.main(JPEG2000Handler.java:13)
Caused by: java.io.IOException: File too long.
    at jj2000.j2k.fileformat.reader.FileFormatReader.readFileFormat(FileFormatReader.java:207)
    at com.sun.media.imageioimpl.plugins.jpeg2000.J2KReadState.initializeRead(J2KReadState.java:418)
    ... 6 more

它说文件太长".我做了一些搜索,发现这个线程 Loading JPEG2000 Images using JAI,完全相同像我这样的问题.根据线程,问题不在于文件大小,而是 jp2 文件中的框大小(无论这意味着什么).该线程还提供了异常来源的链接 https://github.com/Unidata/jj2000/blame/073c3878e4f7799e55d5ff93bc130d01c4260b6d/src/main/java/ucar/jpeg2000b6d/src/main/java/ucar/jpeg20000000000000000java#L149,还说JJ2000不支持这个文件.我花了数周时间拼命寻找一种使用 Java 读取 JPEG 2000 文件的方法,但没有任何效果.我已经检查了 JDeli,但它不是免费的.我只需要某种方式来加载这些文件,它甚至不必使用 JAI.

It says "File too long". I did some searching and found this thread Loading JPEG2000 Images using JAI, with the exact same problem as me. According to the thread, the problem was not the file size, but the box size within the jp2 file (whatever that means). The thread also provided a link to the source of the exception https://github.com/Unidata/jj2000/blame/073c3878e4f7799e55d5ff93bc130d01c4260b6d/src/main/java/ucar/jpeg/jj2000/j2k/fileformat/reader/FileFormatReader.java#L149, but also said that JJ2000 doesn't support this file. I've spent weeks desperately looking for a way to read JPEG 2000 files using Java, but nothing's working. I've checked out JDeli, but it's not free. I just need some way to load these files, it doesn't even have to use JAI.

任何想法都将不胜感激,因为它开始看起来不可能.

Any ideas would be much appreciated, since it's starting to seem impossible.

推荐答案

首先,我不是一个有信誉的来源,我以前从未处理过图像.尽管如此.

First of all, Im not in any way a reputable source, I never worked with images before. Nonetheless.

文件确实由多个盒子"组成,大盒子"确实不支持.但是只有当内容大于 2^32 字节时才需要大框,这里不是这种情况.实际上,如果您的图像实际大于该值,它可能会存储在 0 长度的框中,根据规范,这意味着一直到文件末尾.

The file is indeed consists of multiple "boxes", and large "boxes" are indeed not supported. But large box is required only when conten is larger than 2^32 bytes which is not the case here. In fact if you actually have image larger than that it would probably be stored in a box with 0 length, which according to spec means that is goes till the end of the file.

您可以在 ISO/IEC 15444-1:2000,第 150 页.

You can read more info on boxes in ISO/IEC 15444-1:2000 at page 150.

以上所有只是我对原作者为什么不支持这一点的想法.然而实际上,当内容的大小根本无法保证时,没有人禁止创建大盒子.这就是您的问题的来源.生成该图像的人向其中添加了一些 xml 元数据,出于某种原因,他们决定将该元数据存储在一个大盒子中,尽管它的大小小于 2 Kb.您可以在靠近文件开头的任何十六进制编辑器中看到它.

All of the above is just my thoughts on why original authors didnt bother to support this. In reality however no one prohibits creating large boxes when size of their content doesnt warrant it at all. And this is where your problem comes from. Whoever generated that image added some xml metadata to it, and for some reason they decided to store that metadata in a large box, despite its size of less than 2 Kb. You can see it in any hex editor close to the start of the file.

考虑到这一点,我们有两个选择:

With this in mind we have two options:

  1. 修复"图书馆,所以它不会在看到一个大盒子时立即失败.请参阅此提交.
  2. 将此框转换为普通框,因为它实际上一点也不大.

以下是一些未经测试的转换文件的代码:

Here is some not tested code that converts the file:

public static void main(String[] args) throws IOException {
    RandomAccessIO in = new BEBufferedRandomAccessFile("SENTINEL-TEMP-ZIP-a8e1c94558e10dc1-6-2.jp2", "r");
    DataOutputStream out = new DataOutputStream(new FileOutputStream(new File("_SENTINEL-TEMP-ZIP-a8e1c94558e10dc1-6-2.jp2")));
    boolean done = false;
    while (!done) {
        try {
            int boxLength = in.readInt();
            if (boxLength == 1) {
                //convert large box
                int boxType = in.readInt();//skip box type
                long actualBoxLength = in.readLong();//the box is actually small
                if (actualBoxLength > Integer.MAX_VALUE) {
                    throw new RuntimeException("Unable to fix large box, size exceeds int");
                }
                out.writeInt((int) actualBoxLength - 8);
                out.writeInt(boxType);
                copyBytes(in, out, (int) actualBoxLength - 16);
            } else {
                //copy other stuff
                out.writeInt(boxLength);
                copyBytes(in, out, boxLength != 0 ? boxLength - 4 : 0);
            }
        } catch (EOFException e) {
            done = true;
        }
    }
    out.close();
    in.close();
}

private static void copyBytes(RandomAccessIO in, DataOutputStream out, int length) throws IOException {
    if (length != 0) {
        //copying set amount
        byte[] bytes = new byte[length];
        in.readFully(bytes, 0, bytes.length);
        out.write(bytes, 0, bytes.length);
    } else {
        //copying to the end of file
        byte[] bytes = new byte[10240];
        int lastPos = 0;
        try {
            while (true) {
                lastPos = in.getPos();
                in.readFully(bytes, 0, bytes.length);
                out.write(bytes, 0, bytes.length);
            }
        } catch (EOFException e) {
            out.write(bytes, 0, in.length() - lastPos);
        }
    }
}

BEBufferedRandomAccessFile 来自 https://github.com/Unidata/jj2000,它有一些处理这种文件的方便的功能,但不是必需的.

BEBufferedRandomAccessFile is from https://github.com/Unidata/jj2000, it has some handy function for working with this kind of file, but in no way necessary.

最后,这两个选项都会导致这个库在遇到未知类型的盒子时产生警告.测试:

In the end both options results in this library producing a warning on encountering unknown type of box. Tested with:

public static void main(String[] args) {
    JJ2KDecoder.main(new String[]{"-i", "_SENTINEL-TEMP-ZIP-a8e1c94558e10dc1-6-2.jp2", "-debug"});
}

文件打开并显示正常.

这篇关于无法在 Java JAI 中读取 JPEG 2000 图像,错误:“文件太长".的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆