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

Posted

技术标签:

【中文标题】从图像文件中提取感兴趣区域而不读取整个图像【英文标题】:Extracting a region of interest from an image file without reading the entire image 【发布时间】:2018-06-25 03:39:06 【问题描述】:

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

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

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

【问题讨论】:

您必须更具体。大多数图像格式压缩的。否则图像很大,存储它们是浪费空间。您始终可以将自定义文件映射读取写入特定位置,尽管这可能不会比读取大部分图像至少一次更快或占用更少的内存。 图像存储在不同位置的 NAS 上,因此空间不是问题。问题是图像可以拥有 >100 兆像素,只有其中的一部分(我们已经知道坐标)将被使用,并且读取文件未使用部分所需的额外带宽成为性能瓶颈。 AFAIK bmp 是未压缩的,而 tiff 也可以是未压缩的,这就是为什么我很好奇这种读取方法是否存在任何现有的实现。 NAS 是否允许随机读取文件? 访问原始数组中的子区域非常简单。从每个 ROI 行的开头开始读取。您必须在这与压缩图像带宽减少之间取得平衡。 NAS 将基于 NFS,因此理论上我们希望它允许随机访问。 【参考方案1】:

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 因子的可选参数。可以设置any of the file save options。

【讨论】:

这似乎是最好的选择。 Cris Luengo 的答案是在不读取整个文件的情况下从 tiff 中提取特定切片很有用,但是这解决了提取真正自定义 ROI 的问题,同时为问题增加了最小的编码开销! 我会使用 python 接口pypi.python.org/pypi/pyvips 你应该得到很好的性能,它也使编码变得非常简单。如果您有任何问题,请在 pyvips 问题跟踪器上提问github.com/jcupitt/pyvips/issues 有没有办法让vips 写入stdout 而不是基于磁盘的文件,例如上面的x.jpg 嘿,马克,有一个操作员可以将其作为 MIME 类型写入stdoutvips jpegsave_mime x.tif,但您不能将它与命令行上的裁剪结合使用。您需要运行两个命令(裁剪到文件,然后将文件写入 mime),或者使用 Python 之类的东西。 我添加了一个python示例。感谢您的建议!【参考方案2】:

ITK 可以使用某些格式。有一个方法CanStreamRead 对支持 的格式返回true,例如MetaImageIO。可以在here 找到一个示例。您可以在 ITK 的forum 上询问更详细的问题。

【讨论】:

【参考方案3】:

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

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

【讨论】:

使用 libtiff 提取切片已经过测试,似乎可以按预期工作(即通过低带宽连接从大型 tiff 提取切片的性能良好)。我还在研究其他解决方案,但这绝对解决了问题:) @andrei,确实,这正是平铺 TIFF 格式的设计目的!【参考方案4】:

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

TIFF 不太容易,因为有很多子格式。但是 TIFF 库 (TIFFlib) 支持“面向切片”的 I/O 模式。 http://www.libtiff.org/libtiff.html#Tiles

【讨论】:

对于 LibTiff 来说,这是一个过时的网站。请改用simplesystems.org/libtiff。【参考方案5】:

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

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

如果您要通过网络传输提取的数据,您可能会考虑在通过网络发送之前压缩提取的 ROIin-memory(如果它相对较大)。

【讨论】:

以上是关于从图像文件中提取感兴趣区域而不读取整个图像的主要内容,如果未能解决你的问题,请参考以下文章

用Python-OpenCV提取图像中的感兴趣区域以及图像的深拷贝和浅拷贝问题附示例代码

请问大虾们,opencv如何只对图像选择区域提取特征点

使用 DLib 提取感兴趣区域

有没有办法在不读取整个文件的情况下推断文件是啥图像格式?

有没有办法在不读取整个文件的情况下推断文件是啥图像格式?

有没有办法在不读取整个文件的情况下推断文件是啥图像格式?