在Python中从单个大图像快速创建多个缩略图的方法

3 投票
1 回答
1603 浏览
提问于 2025-04-18 07:02

我有一组很大的图片(8000x6000像素,大小大约13MB),我想从这些图片生成多个小缩略图,宽度分别为3000px、2000px、1000px、500px、250px和100px。

原始图片存储在一个普通文件中,生成的缩略图也会存储在普通文件里。

我在考虑用Python来实现这个过程,以下是我想到的一些潜在问题:

  • 我应该直接从原始图片生成每个缩略图,还是可以从稍微大一点的缩略图来制作更小的缩略图?比如说,从8000px生成3000px,从3000px生成2000px,从1000px生成500px,等等……这样做会不会快很多?
  • 在生成缩略图之前,先把原始图片加载到内存中有必要吗?
  • 我应该使用ImageMagick吗?是通过命令行操作,还是通过API来使用?
  • 有没有办法利用GPU来加速处理?
  • 在这种情况下,使用多线程会有帮助吗?

在优化缩略图生成时,还有其他需要注意的事项吗?如果能提供一些示例代码来帮助我入门,那就太好了。谢谢!

1 个回答

5

我做了一些图片并进行了测试,这样你就可以看到不同技术对性能的影响。

我制作的图片包含随机的、难以压缩的数据,尺寸和文件大小都和你的相匹配,也就是说:

convert -size 8000x6000 xc:gray +noise random -quality 35 image.jpg

然后,使用 ls 命令显示的大小是13MB,如下所示:

-rw-r--r--  1 mark  staff    13M 23 Aug 17:55 image.jpg

我制作了128张这样的随机图片,因为这个数字正好可以被我电脑上的8个CPU核心整除——稍后会看到并行测试的结果。

现在来看看这些方法……

方法1

这是最简单的方法——你只需一个接一个地创建所有你需要的文件。

#!/bin/bash
for f in image*jpg; do
   for w in 3000 2000 1000 500 250 100; do
      convert $f -resize ${w}x res_${f}_${w}.jpg
   done 
done

耗时:26分钟46秒

方法2

在这个方法中,我们只读取每张图片一次,但从一张输入图片生成所有输出尺寸,这样速度快得多。

#!/bin/bash
for f in image*jpg; do
   convert $f -resize 3000x -write res_${f}_3000.jpg \
              -resize 2000x -write res_${f}_2000.jpg \
              -resize 1000x -write res_${f}_1000.jpg \
              -resize 500x  -write res_${f}_500.jpg  \
              -resize 250x  -write res_${f}_250.jpg  \
              -resize 100x  res_${f}_100.jpg
done

耗时:6分钟17秒

方法3

在这个方法中,我们提前告诉ImageMagick我们需要的最大图片尺寸是3000x2250像素,这样它可以使用更少的内存,读取更少的DCT级别,减少输入输出操作。这被称为“加载时缩小”。

#!/bin/bash
for f in image*jpg; do
   convert -define jpeg:size=3000x2250 $f            \
              -resize 3000x -write res_${f}_3000.jpg \
              -resize 2000x -write res_${f}_2000.jpg \
              -resize 1000x -write res_${f}_1000.jpg \
              -resize 500x  -write res_${f}_500.jpg  \
              -resize 250x  -write res_${f}_250.jpg  \
              -resize 100x  res_${f}_100.jpg
done

耗时:3分钟37秒

顺便提一下,为了展示提前告诉ImageMagick你需要的图片大小可以减少时间、输入输出和内存的使用,比较这两个命令,它们都读取你的一张8000x6000、13MB的图片,并生成相同的缩略图:

/usr/bin/time -l convert image.jpg -resize 500x result.jpg 2>&1 | egrep "resident|real"        
1.92 real         1.77 user         0.14 sys
415727616  maximum resident set size

也就是说,415 MB和2秒

/usr/bin/time -l convert -define jpeg:size=500x500 image.jpg -resize 500x result.jpg 2>&1 | egrep "resident|real"

0.24 real         0.23 user         0.01 sys
23592960  maximum resident set size

也就是说,23 MB和0.2秒——而输出的图片内容和质量是一样的。

方法4

在这个方法中,我们全力以赴,使用GNU Parallel以及之前提到的所有技术,让你的CPU、风扇和电力消耗都疯狂运转!!!

#!/bin/bash
for f in image*jpg; do
   cat<<EOF
convert -define jpeg:size=3000x2250 $f          \
              -resize 3000x -write res_${f}_3000.jpg \
              -resize 2000x -write res_${f}_2000.jpg \
              -resize 1000x -write res_${f}_1000.jpg \
              -resize 500x  -write res_${f}_500.jpg  \
              -resize 250x  -write res_${f}_250.jpg  \
              -resize 100x  res_${f}_100.jpg
EOF
done | parallel

耗时:56秒

总结一下,我们可以通过避免不必要的读取图片、尽可能多地从一张输入生成多个输出、提前告诉ImageMagick需要读取多少输入图片,以及使用GNU Parallel来让所有的CPU核心忙碌,从而将处理时间从27分钟减少到56秒。希望这对你有帮助。

撰写回答