【问题标题】:ImageMagick: speed of processing with different file formatsImageMagick:不同文件格式的处理速度
【发布时间】:2020-04-18 12:58:48
【问题描述】:

我正在使用 ImageMagick(版本:ImageMagick 6.9.7-4 Q16 x86_64 20170114)来创建特定类型的复合叠加图像。使用 Python 作为控制结构,我实际上是在运行一系列 shell 命令(convertcomposite)来执行以下操作:

create an initial (transparent) working image W
for n = 1 to numberOfOverlappingShapesWanted
  use convert to create a transparent overlay image (O) with a random shape at a random place
  use composite to merge down W (on top) with O (beneath) and produce WO
  replace W with WO
use convert to change W into any desired final image format

目前,我以 PNG 格式创建 W、O 和 WO。我的问题只与速度有关......使用我拥有的非常粗略的结构,是否有一种图像格式的选择会导致进程运行得比我当前选择的 PNG 格式更快。

根据真实示例的要求进行编辑...

#!/usr/bin/python3
import os
import random

# The overall algorithm here is to create a blank working image
# called w.png and then to drop a disc onto a new blank image
# calle l.png . The old working image is then combined with
# the newly dropped disc into an image called wl.png .
# And wl.png then becomes the new working image

# Python is used only for the control structure. The real
# work is done by ImageMagick from Python os.system commands. 

nDroppingDiscs = 6
discRadius = 64
imageX = 2048
imageY = 2048

# Names for the three images
workingImage = 'w.png'
discImage = 'o.png'
combinedImage = 'wo.png'

# Create the first part of the ImageMagick 'convert' command
# of the form:
#   convert -size 2048x2048 xc:transparent
baseWorkingCommand = 'convert -size ' + \
  str( imageX ) + 'x' + str( imageY ) + ' ' + \
  'xc:transparent '

# Create initial blank working image
#     convert -size 2048x2048 xc:transparent w.png
os.system( baseWorkingCommand + workingImage )

for n in range( nDroppingDiscs ) :

  # Create the initial portion of the ImageMagick 'convert'
  # command for dropping a disc onto a transparent canvas
  baseDiscCommand = 'convert -size ' + \
    str( imageX ) + 'x' + str( imageY ) + ' ' + \
    'xc:transparent +antialias -fill '

  # Ensure that each disc is a different colour
  discNAsColourString = "#%06x" % n

  baseDiscCommand = baseDiscCommand + " '" + \
    discNAsColourString + "' " 

  # Determine the drop-point for the disc
  discDropPointX =  random.randint(1, imageX)
  discDropPointY =  random.randint(1, imageY) 

  discRadiusIndicatorX = discDropPointX
  discRadiusIndicatorY = discDropPointY + discRadius

  # Put the pieces of the 'convert' command together
  baseDiscCommand = baseDiscCommand + \
    " -draw 'circle " + \
    str( discDropPointX ) + "," + \
    str( discDropPointY ) + " " + \
    str( discRadiusIndicatorX ) + "," + \
    str( discRadiusIndicatorY ) + "'"

  # Use ImageMagick to create the new randomly dropped disc
  os.system( baseDiscCommand + " "  + discImage )

  # Overlay the existing image onto the newly created dropped disc
  # to produce a combined image
  os.system('composite ' + workingImage + " " + discImage + " " + combinedImage )
  # The combined image is now the new working image
  os.system('mv ' + combinedImage  + ' ' + workingImage )

# Final conversion. Convert the working image from whatever format
# I was using earlier to a final PNG format.
os.system('convert ' +  workingImage + ' final.png')

请注意,常量nDroppingDiscs 通常在 4000 左右。另外,值得补充的是,我的最终目的是探索有关此类模式的图像统计信息的一些事情。

【问题讨论】:

  • 你能发布一个小的、完整的、可运行的程序吗?它必须给出具体的优化建议,而没有具体的基准测试(你可以尝试 tiff 而不是 png ...... png 非常慢)。
  • 如果使用 Imagemagick,您可能会在中间步骤中使用其 MIFF 格式获得最快的性能。
  • 进程 I/O 受限还是 CPU 受限?
  • Composite 是 ImageMagick 中的一种旧方法。您应该使用转换语法来进行合成。如果您需要在 Python 中执行此操作,请尝试使用基于 ImageMagick 的 Python Wand。

标签: imagemagick imagemagick-convert


【解决方案1】:

对于像这样的迭代过程,通过文件系统会非常慢。我认为您可能会更好地使用 numpy 之类的东西。

我快速进入pyvips,只是因为我很了解:

#!/usr/bin/python3

import random
import pyvips

n_dropping_discs = 1000
disc_radius = 64
image_width = 2048
image_height = 2048

image = pyvips.Image.black(image_width, image_height, bands=4) \
             .copy(interpretation="srgb") 

for i in range(n_dropping_discs):
    ink = [(i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff, 0xff]
    x = random.randint(0, image_width) - disc_radius
    y = random.randint(0, image_height) - disc_radius
    image = image.draw_circle(ink, x, y, disc_radius, fill=True)

image.write_to_file("final.png")

在这台笔记本电脑上我看到了:

$ time ./disc2.py 
real    0m6.086s
user    0m12.182s
sys 0m1.656s

所以 1,000 张光盘大约需要 6 秒。 pyvips 不太适合这种任务——numpy 会更快。

【讨论】:

  • 哇哦!我应该考虑看一个 python 库来完成整个事情。谢谢。
【解决方案2】:

我有时间尝试一下 John 的建议,并将他的代码重新散列到 NumpyOpenCV 中,如下所示。在我的机器上,它似乎比 pyvips 快 40 倍左右:

#!/usr/bin/env python3

import random
import cv2

n_dropping_discs = 1000
disc_radius = 64
image_width = 2048
image_height = 2048

def numpyidea():
    image = np.zeros((image_height,image_width,4), dtype=np.uint8)

    for i in range(n_dropping_discs):
        ink = [i & 0xff, (i >> 8) & 0xff, (i >> 16) & 0xff, 0xff]
        x = random.randint(0, image_width) - disc_radius
        y = random.randint(0, image_height) - disc_radius
        cv2.circle(image, (x,y), disc_radius, ink, cv2.FILLED)

    cv2.imwrite("numpy.png", image)
    return

【讨论】:

  • 哦,太好了!是的,pyvips 是非破坏性的,因此它必须为每个绘制操作复制 2k x 2k 像素的图像(!)。
猜你喜欢
  • 1970-01-01
  • 2018-04-07
  • 1970-01-01
  • 2011-08-30
  • 2022-01-24
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多