I'm using ImageMagick (Version: ImageMagick 6.9.7-4 Q16 x86_64 20170114) to create a particular kind of composite, overlayed image. Using Python as the control structure, I'm essentially running a series of shell commands (convert
and composite
) to do the following:
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
Currently, I create W, O and WO in PNG format. My question relates only to speed ... Using the very crude structure that I have, is there a choice of image format that would lead to the process running faster than my current choice of PNG format.
Edit following the request for a real example ...
#!/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')
Note that the constant nDroppingDiscs
would typically be around 4000. Also, its worth adding that my ultimate purpose is to explore some things about the image statistics of this type of pattern.
Going via the filesystem will be very slow for iterative processes like this. I think you'd probably be better off with something like numpy.
I had a quick go in pyvips, just because I know it well:
#!/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")
On this laptop I see:
$ time ./disc2.py
real 0m6.086s
user 0m12.182s
sys 0m1.656s
So 1,000 discs in about 6s. pyvips isn't a great fit for this kind of task -- numpy would be much faster again.