首页 > 解决方案 > 加载大图像时达到硬内存限制,加载时可能会下采样?

问题描述

我需要从可能巨大的图像(10,000 x 10,000)中生成小缩略图。我正在使用在 Google App Engine 中运行的 ImageMagick 和 PHP。我的 GAE 的硬内存限制为 512 MB。

我一直在尝试阅读 imagemagick php 扩展的文档,但是文档很薄。我发现这个 SO 帖子得到了维护者的回答,并试图调整他们的代码。我试图设置资源区域,以便将大于 2000x2000 像素的图像换出到磁盘而不是保存在内存中,但要么我误解了代码,要么我做错了什么。我的 GAE 仍然崩溃,说我已经填满了内存。

try {
    Imagick::setResourceLimit(Imagick::RESOURCETYPE_AREA, 2000 * 2000);
    Imagick::setResourceLimit(Imagick::RESOURCETYPE_DISK, 1024*1024*1024*2);

    $im = new \Imagick($path);
    
    // ...

} catch (\Exception $e) {
    error_log("Exception: " . $e->getMessage() . "\n");
}

Exceeded hard memory limit of 512 MB with 514 MB...

在 Android 设备上,当从磁盘加载位图时,可以选择在将其加载到内存时对其进行下采样。这使我可以将较小版本的图像加载到内存中,并避免巨大文件破坏所有内容的问题。

ImageMagick 中有类似的东西吗?我看到了ImageMagick::setSamplingFactors()但文档根本没有解释参数,所以我不确定如何使用它。

如何在不达到 Google App Engine 的硬内存限制的情况下从一张巨大的图像生成一个小缩略图?

标签: google-app-engineimagemagickout-of-memoryphp-7imagick

解决方案


如果您正在缩小 JPEG 图像,那么 imagemagick 支持加载时缩小。例如,这是一个 10k x 10k 像素的 JPEG 图像,其尺寸缩小到 200x200。

$ /usr/bin/time -f %M:%e \
    convert wtc.jpg -resize 200x200 x.jpg
713340:2.98

那是 720MB 的峰值内存使用和将近 3 秒的 CPU 时间。现在试试这个:

$ /usr/bin/time -f %M:%e \
    convert -define jpeg:size=400x400 wtc.jpg -resize 200x200 x.jpg
35952:0.32

低至 35MB 的内存和 300ms 的 CPU。

提示 JPEG 加载器-define jpeg:size=400x400您需要至少 400x400 像素的图像,因此(在这种情况下)在加载期间,它将以 1/8 大小获取。您需要加载提示大小至少比最终输出大小大 2 倍以避免混叠。

您可以使用 imagick 进行设置setOption

不幸的是,许多加载器不支持加载收缩。PNG尤其糟糕:

$ /usr/bin/time -f %M:%e \
    convert wtc.png -resize 200x200 x.jpg
828376:5.62

830MB 和 5.6 秒。

您可以考虑其他调整大小的程序。vipsthumbnail对于几乎所有文件格式来说都是快速且低内存的,例如:

$ /usr/bin/time -f %M:%e \
    vipsthumbnail wtc.png --size 200x200 -o x.jpg
58780:2.29

同一个 PNG 文件需要 60MB 和 2.3 秒。质量与 imagemagick 相同。

它也有一个PHP 绑定——你可以这样写:

$image = Vips\Image::thumbnail('somefile.jpg', 200);
$image->writeToFile('tiny.jpg');

推荐阅读