从图像文件中提取感兴趣区域而不读取整个图像



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

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

我意识到这可能不适用于压缩格式,例如 jpegs,但从理论上讲,听起来 bmp 或 tiffs 应该允许这种类型的读取。

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并写入x.jpg)

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

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

JPG 和条带 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 因子的可选参数。您可以设置任何文件保存选项。

ITK可以用一些格式来做到这一点。有一个方法CanStreamRead对于支持的格式返回true,例如MetaImageIO。可以在此处找到一个示例。您可以在ITK的论坛上提出更详细的问题。

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

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

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

TIFF有点不那么容易,因为有很多子格式。但TIFF库(TIFFlib)支持"面向磁贴"的I/O模式。http://www.libtiff.org/libtiff.html#Tiles

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

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

如果要通过网络传输提取的数据,则可以考虑在通过网络发送之前将提取的 ROI 压缩到内存中(如果它相对较大)。

最新更新