【问题标题】:Fastest way to create multiple thumbnails from a single large image in Python在 Python 中从单个大图像创建多个缩略图的最快方法
【发布时间】:2014-07-08 01:18:39
【问题描述】:

我有一个大图像库(8000x6000px ~13mb),我想为其生成多个较小尺寸的缩略图,宽度分别为 3000px、2000px、1000px、500px、250px 和 100px。

源图像存储在平面文件中,生成的缩略图也将存储在平面文件中。

我一直在考虑在 Python 中执行此操作的最佳方法,这些是立即浮现在脑海中的潜在问题:

  • 从源图像生成每个缩略图是否有意义,或者我可以从任何稍大的缩略图创建较小的缩略图吗?例如,8000px -> 3000px, 3000px -> 2000px, 1000px -> 500px 等等……那不是跑得更快吗?
  • 在生成缩略图之前将源图像加载到内存中是否有意义?
  • 我应该使用 ImageMagick 吗?通过命令行还是通过 API?
  • 有什么方法可以利用 GPU?
  • 在这种情况下多线程有意义吗?

在优化缩略图生成时还有其他需要注意的事项吗?非常感谢您开始使用示例代码。谢谢。

【问题讨论】:

  • “图像存储在平面文件中”是什么意思?你的意思是JPEG,还是TIFF 或类似的?什么是凹凸不平的文件?
  • 迟到回答这个问题,但“平面文件”通常指的是操作系统文件系统文件——而不是像数据库等那样存储在结构化存储中。

标签: python image-processing python-3.x imagemagick imagemagick-convert


【解决方案1】:

我制作了一些图像并进行了一些测试,以便您可以看到各种技术对性能的影响。

我使图像包含随机的、难以压缩的数据,其尺寸和文件大小与您的匹配,即

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 内核整除 - 稍后请参阅并行测试。

现在是方法...

方法一

这是一种幼稚的方法 - 您只需一个接一个地创建您要求的所有文件。

#!/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 秒

方法二

这里我们只读取每个图像一次,但从一个输入图像生成所有输出大小,并且速度要快得多。

#!/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 级别并执行更少的 I/O。这称为“加载时收缩”。

#!/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 您需要多大的图像时,为了演示所需的时间、I/O 和内存减少,比较这两个命令,都读取您的一个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 秒 - 输出图像具有相同的内容和质量。

方法四

在这里,我们全力以赴并使用 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 秒

总之,我们可以将处理时间从 27 分钟减少到 56 秒,方法是避免不必要地读取图像并在每个输入中执行尽可能多的输出,方法是预先告诉 ImageMagick 它需要读取多少输入图像和通过使用 GNU Parallel 让所有可爱的 CPU 内核保持忙碌。 HTH。

【讨论】:

  • 谢谢。非常棒的答案,以及出色的细节。
猜你喜欢
  • 2014-04-09
  • 2015-06-15
  • 1970-01-01
  • 2019-11-17
  • 2010-11-21
  • 1970-01-01
  • 2017-09-03
  • 2012-07-25
  • 2011-04-12
相关资源
最近更新 更多