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

查看:227
本文介绍了无法以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

它说文件太长".我进行了一些搜索,发现此线程使用JAI加载JPEG2000图像,并且完全相同像我这样的问题.根据线程,问题不是文件大小,而是jp2文件中的文件箱大小(无论如何).该线程还提供了指向异常源的链接

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.

该文件确实由多个框"和较大的框"组成.确实不被支持.但是,仅当conten大于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.

您可以在

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"});
}

文件打开并显示为正常.

File opens and appears normal.

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

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