在不读取整个图像的情况下从图像文件中提取感兴趣区域

Extracting a region of interest from an image file without reading the entire image

我正在搜索能够读取图像文件(任何格式)的区域而无需最初读取整个图像文件的库(任何语言)。

我遇到过一些选项,例如 vips,它确实不会将整个图像保存在内存中,但似乎仍然需要完整地读取它才能开始。

我意识到这可能不适用于 jpeg 等压缩格式,但理论上 bmps 或 tiffs 应该允许这种类型的阅读。

BMP 格式(未压缩)非常简单,您可以自己编写函数。

TIFF 不太容易,因为有很多子格式。但是 TIFF 库 (TIFFlib) 支持 "tile-oriented" I/O 模式。 http://www.libtiff.org/libtiff.html#Tiles

我不知道这样的库解决方案。
低级别的文件读取访问是特定于格式的,特别是文件映射是 OS 特定的。

如果您可以访问原始字节,那么假设您知道宽度、高度、深度和通道数等,那么计算文件偏移量是微不足道的,所以只需自己动手。

如果您要通过网络传输提取的数据,您可能会考虑压缩提取的 ROI 内存中 如果它在通过网络发送之前相对较大。

ITK can do it with some formats. There is a method CanStreamRead which returns true for formats which support streaming, such as MetaImageIO. An example can be found here. You can ask more detailed questions on ITK's forum.

如果可以控制文件格式,我建议您使用平铺的 TIFF 文件。这些通常用于数字病理学整个幻灯片图像,平均大小为 100kx30k 像素左右。

LibTiff 可以轻松读取与所选 ROI 对应的图块。可以压缩图块而不会降低读取小区域的效率(无需解码整个扫描线)。

libvips 将尽可能只阅读您需要的部分。例如,如果您从大 PNG 的左上角裁剪 100x100 像素,速度很快:

$ time vips crop wtc.png x.jpg 0 0 100 100
real    0m0.063s
user    0m0.041s
sys 0m0.023s

(四个数字分别是wtc.png要裁剪区域的left,top,width,height写入x.jpg)

但是底部附近的 100x100 像素区域相当慢,因为它必须读取和解压缩您想要到达文件中正确位置的像素之前的像素:

$ time vips crop wtc.png x.jpg 0 9000 100 100
real    0m3.063s
user    0m2.884s
sys 0m0.181s

JPG 和 strip TIFF 的工作方式相同,但不太明显,因为它们是更快的格式。

一些格式支持真正的随机访问读取。例如,分块 TIFF 在任何地方都很快,因为 libvips 可以使用 libtiff 只读取它需要的分块:

$ vips copy wtc.png wtc.tif[tile]
$ time vips crop wtc.tif x.jpg 0 0 100 100
real    0m0.033s
user    0m0.013s
sys 0m0.021s
$ time vips crop wtc.tif x.jpg 0 9000 100 100
real    0m0.037s
user    0m0.021s
sys 0m0.017s

OpenSlide、vips、平铺 OpenEXR、FITS、二进制 PPM/PGM/PBM、HDR、RAW、Analyze、Matlab 和其他一些可能都支持这样的真正随机访问。

如果您对更多细节感兴趣,API 文档中有一章描述了 libvips 如何打开文件:

http://libvips.github.io/libvips/API/current/How-it-opens-files.md.html

这里使用 pyvips 在 Python 中裁剪并保存:

import pyvips

image = pyvips.Image.new_from_file(input_filename, access='sequential')
tile = image.crop(left, top, width, height)
tile.write_to_file(output_filename)

access= 是一个标志,它向 libvips 提示可以流式传输此图像,以防基础文件格式不支持随机访问。对于支持随机访问的格式,例如平铺 TIFF,您不需要此选项。

您不需要写入文件。例如,这将创建一个缓冲区对象,其中包含编码为 JPG 的文件:

buffer = tile.write_to_buffer('.jpg', Q=85)

或者这将直接写入 stdout:

target = pyvips.Target.new_from_descriptor(0)
tile.write_to_target('.jpg', Q=85)

Q=85 是一个可选参数,用于设置 JPG Q 因子。你可以设置any of the file save options.