解析连续存储在流中的多个 jpeg

Parsing multiple jpegs stored contiguously in a stream

我有一个可以包含一个或多个图像的流,连续存储,没有单独的标志。当我使用 javas ImageIO 打开和解析流时,它正确解析了第一张图像,但它关闭了流,这使我无法流式传输下一张图像。

所以我想弄清楚文件中每个压缩图像的大小,以便我可以将该图像的确切字节数读入缓冲区,从该缓冲区创建一个 ByteArrayInputStream,然后创建使用新的较小缓冲区的 BufferedImage。我的问题是弄清楚图像有多大,width/height 没有帮助,因为压缩后的图像小于宽度 * 高度。

有没有办法读取缓冲区并轻松找出每张图像的结束位置?没有 soi, eoi, sof, eof, 标签,除了流结尾的一个eof。

或者有更好的方法吗? ImageIO.read() 的方法不会关闭流并让我继续使用它?

首先,ImageIO.read(...) 是一种方便的方法,它只能读取任何格式的任何文件的单个(第一个)图像。

相反,如果您获得输入的 ImageReader,您可以(通常)使用 getNumImages(boolean allowSearch) 获取输入中的图像数量,并使用读取(或跳过)单个图像read(int imageIndex, ImageReadParam param)

类似于:

// Create input stream
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
    // Get the reader
    Iterator<ImageReader> readers = ImageIO.getImageReaders(input);

    if (!readers.hasNext()) {
        throw new IllegalArgumentException("No reader for: " + file);
    }

    ImageReader reader = readers.next();

    try {
        reader.setInput(input);

        // Optionally, listen for read warnings, progress, etc.
        reader.addIIOReadWarningListener(...);
        reader.addIIOReadProgressListener(...);

        ImageReadParam param = reader.getDefaultReadParam();

        // Note: For some formats (ie. GIF), finding the number of images
        // requires searching the entire stream. In this case, it may 
        // be more convenient to just read until you get an IndexOutOfBoundsException
        for (int i = 0; i < reader.getNumImages(true); i++) {
            // Optionally, control read settings like sub sampling, source region or destination etc.
            param.setSourceSubsampling(...);
            param.setSourceRegion(...);
            param.setDestination(...);
            // ...

            // Finally read the image, using settings from param
            BufferedImage image = reader.read(i, param);

            // Optionally, read thumbnails, meta data, etc...
            int numThumbs = reader.getNumThumbnails(0);
            // ...
        }
    }
    finally {
        // Dispose reader in finally block to avoid memory leaks
        reader.dispose();
    }
}

JRE 捆绑 JPEGImageReader 理论上应该也支持单个流中的多个 JFIF 子流。但是,我认为您会发现支持已损坏(至少我的 ImageIO 插件项目中有一个 open issue to fix this)。

不幸的是,无法在不解码的情况下找到 JPEG 压缩图像的长度(或将长度存储在 JFIF 流之外)。

您可以尝试的一件事是(可能包装 ImageInputStream)读取第一张图像,然后向前扫描 寻找下一个 SOI("Start-of-Image", 0xffd8) 标记,回溯两个字节(对于 SOI 标记)并尝试读取下一张图像。 SOI 标记不能存在于 JPEG 编码数据中。

您可以在维基百科上阅读更多关于 JPEG and JFIF segments 的信息。