如何高效地对一张600 MP的图像进行采样

4

我有一个进程可以产生非常高分辨率的600+ MP图像。这些图像加载到 RAM 中时大约为2GB(高度压缩后为40MB)。我正在对它们进行索引并通过 PHP 网络应用程序使它们可用。

我有数据告诉我在像素单位中感兴趣的区域,因此我想知道是否有一种方法可以在不将整个图像加载到内存中的情况下读取特定区域的图像。就像通过文件指针移动,并选择何时读取一样。目标是创建感兴趣区域的小图片。

我知道 PHP 中有一些图像处理库,Python 也有不少,但是我真的不知道关于这些库应该问什么问题。

我真正寻找的是 PHP 或 Python 中的解决方案


看看这个是否有帮助。https://dev59.com/xWIj5IYBdhLWcg3w_5vl#19772446 - Rockybilly
该方法需要未压缩的BMP格式图像。我的图像是JPEG压缩的,完全提取它们是一项艰巨的任务。尽管我对压缩知之甚少,但我担心有人会告诉我必须解压整个图像才能处理它。 - Kenneth
@CrisLuengo 那个回答没有提供 PHP 或 Python 的解决方案。 谢谢。 - Kenneth
@Kenneth:我提供这个问题的链接是因为我认为它非常清楚地解释了你需要做什么:将图像存储为平铺的TIFF格式。Python具有从TIFF文件中读取单个瓦片的功能,我相信PHP也有类似的功能。那部分很简单。我所知道的任何其他图像文件类型都需要将整个文件读入内存(或者至少大部分)才能提取一个小区域。你真的想要先以正确的方式存储数据。 - Cris Luengo
@Kenneth,Cris提供的解决方案适用于Python和PHP -- 在被接受的答案底部有一个Python示例。我在这里添加一个PHP版本。 - jcupitt
显示剩余3条评论
2个回答

2
您可以尝试使用ImageMagick,我已经在Java中成功地使用它来完成非常类似的任务。
需要一些学习曲线,但功能强大。我相信在“选择图像区域”页面中的命令行示例可以说明您所描述的内容(从一个较大的图像中提取一个小的已知AoI)。该示例位于以下网址:https://www.imagemagick.org/script/command-line-processing.php

听起来很有前途。我会去调查! - Kenneth

1

php-vips会在可能的情况下只读取您需要的部分。它通常比imagemagick快3倍到5倍,并且需要更少的内存。

许多图像格式不允许随机访问。 JPEG、PNG、GIF和许多其他格式将强制您至少解压缩要处理的像素之前的像素,对于您正在处理的大型图像而言,这将非常缓慢。

一种解决方案是切换到JPEG压缩的平铺式TIFF。该格式将图像分成(默认情况下)256x256个像素的瓷砖,并单独压缩每个瓷砖。瓷砖存储在带有索引的TIFF文件中,因此您可以非常快速地提取单个瓷砖。

例如,您可以使用libvips将巨大的JPEG图像转换为JPEG压缩的平铺式tiff:

$ time vips copy wac_nearside.jpg wac_nearside.tif[tile,compression=jpeg]
real    0m3.891s
user    0m6.332s
sys     0m0.198s
peak RES 40mb

索引会使图像略微变大,但并不太糟糕:
$ ls -l wac_nearside.* 
-rw-r--r-- 1 john john 74661771 May  7  2015 wac_nearside.jpg
-rw-r--r-- 1 john john 76049323 Feb 24 15:39 wac_nearside.tif
$ vipsheader wac_nearside.jpg wac_nearside.jpg: 24000x24000 uchar, 1 band, b-w, jpegload

您可以在PHP中像这样读取它的随机区域:
#!/usr/bin/env php
<?php

require __DIR__ . '/vendor/autoload.php';

use Jcupitt\Vips;

$image = Vips\Image::newFromFile($argv[1]);

$region_width = 100;
$region_height = 100;

for ($i = 0; $i < 100; $i++) {
    $left = rand(0, $image->width - $region_width - 1);
    $top = rand(0, $image->height - $region_height - 1);
    $region = $image->crop($left, $top, $region_width, $region_height);
    $region->writeToFile($i . ".jpg");
}

我可以这样运行那个程序:

$ time ./crop.php ~/pics/wac_nearside.tif 
real    0m0.207s
user    0m0.181s
sys     0m0.042s
peak RES 36mb

在这台老旧的笔记本电脑上,它仅用0.2秒多一点的时间读取(并创建)100个JPEG文件。

我尝试在Windows中使用Python安装Vips包遇到了很多问题,我放弃了!你知道有没有什么资源可以帮助我在Windows中使用PHP来安装它吗? - Kenneth
1
哦,亲爱的,这应该很容易。在 pyvips 跟踪器上打开一个问题,我会在那里提供帮助:https://github.com/jcupitt/pyvips/issues ... 应该是这样的:下载共享库,下载 64 位 Python(32 位将无法工作),将您的 PATH 设置为包括共享库 DLL 文件夹,然后运行 'pip install pyvips'。 - jcupitt
1
对于在 Windows 上使用 PHP,它是一个本地扩展,因此您需要一个可用的编译器。在 Windows 上使用 pyvips 更容易:它是纯 Python,并且可以在没有编译器的情况下工作,尽管速度较慢。 - jcupitt
1
你也可以直接使用vips命令行,并从python/php中调用它。vips crop huge.tif small.jpg 10 10 100 100将非常快速地从huge.tif中左上角10x10开始裁剪一个100x100像素的区域,并将其写入small.jpg - jcupitt
@user894736 我已经使用PHP工作了大约15年,但从未深入研究过扩展或编译任何东西。我发现了这个资源https://wiki.php.net/internals/windows/stepbystepbuild,关于设置编译器(我以前用过VS进行c++项目)。据我所知,因为它是本地扩展,必须将其编译到PHP中?这是将此类型功能添加到php中的唯一方法吗?我正在使用WAMP服务器,所以我确定添加自定义构建的PHP会变得非常复杂... - Kenneth
这是在 Linux 上很容易,但在 Windows 上却很棘手的一件事情,因为没有标准的 C 编译器。是的,本机扩展是从 PHP 调用外部库的唯一方法(没有 FFI 系统)。如果您不是编译器专家,我建议您仅使用 shell_exec() 运行 vips 命令行。您需要确保 vips 二进制目录已添加到 PATH。 - jcupitt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接