解析连续存储在流中的多个 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 的信息。
我有一个可以包含一个或多个图像的流,连续存储,没有单独的标志。当我使用 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 的信息。