Drawing performance

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
msasha

Drawing performance

Post by msasha »

Hello,

I've written a chessboard diagram generator using ImageMagick and now that it's becoming more and more popular, I need to optimize it. A typical, simple board is generated with the following ImageMagick command:

Code: Select all

convert -size 320x320 xc:none -fill 'rgb(255,204,153)' -draw 'rectangle 0,0,39,39 rectangle 0,80,39,119 rectangle 0,160,39,199 rectangle 0,240,39,279 rectangle 40,40,79,79 rectangle 40,120,79,159 rectangle 40,200,79,239 rectangle 40,280,79,319 rectangle 80,0,119,39 rectangle 80,80,119,119 rectangle 80,160,119,199 rectangle 80,240,119,279 rectangle 120,40,159,79 rectangle 120,120,159,159 rectangle 120,200,159,239 rectangle 120,280,159,319 rectangle 160,0,199,39 rectangle 160,80,199,119 rectangle 160,160,199,199 rectangle 160,240,199,279 rectangle 200,40,239,79 rectangle 200,120,239,159 rectangle 200,200,239,239 rectangle 200,280,239,319 rectangle 240,0,279,39 rectangle 240,80,279,119 rectangle 240,160,279,199 rectangle 240,240,279,279 rectangle 280,40,319,79 rectangle 280,120,319,159 rectangle 280,200,319,239 rectangle 280,280,319,319 ' -fill 'rgb(143,96,79)' -draw 'rectangle 0,40,39,79 rectangle 0,120,39,159 rectangle 0,200,39,239 rectangle 0,280,39,319 rectangle 40,0,79,39 rectangle 40,80,79,119 rectangle 40,160,79,199 rectangle 40,240,79,279 rectangle 80,40,119,79 rectangle 80,120,119,159 rectangle 80,200,119,239 rectangle 80,280,119,319 rectangle 120,0,159,39 rectangle 120,80,159,119 rectangle 120,160,159,199 rectangle 120,240,159,279 rectangle 160,40,199,79 rectangle 160,120,199,159 rectangle 160,200,199,239 rectangle 160,280,199,319 rectangle 200,0,239,39 rectangle 200,80,239,119 rectangle 200,160,239,199 rectangle 200,240,239,279 rectangle 240,40,279,79 rectangle 240,120,279,159 rectangle 240,200,279,239 rectangle 240,280,279,319 rectangle 280,0,319,39 rectangle 280,80,319,119 rectangle 280,160,319,199 rectangle 280,240,319,279 ' -draw 'image Over 0,0 0,0 pieces/alpha/40/br.png image Over 40,0 0,0 pieces/alpha/40/bn.png image Over 80,0 0,0 pieces/alpha/40/bb.png image Over 120,0 0,0 pieces/alpha/40/bq.png image Over 160,0 0,0 pieces/alpha/40/bk.png image Over 200,0 0,0 pieces/alpha/40/bb.png image Over 240,0 0,0 pieces/alpha/40/bn.png image Over 280,0 0,0 pieces/alpha/40/br.png image Over 0,40 0,0 pieces/alpha/40/bp.png image Over 40,40 0,0 pieces/alpha/40/bp.png image Over 80,40 0,0 pieces/alpha/40/bp.png image Over 120,40 0,0 pieces/alpha/40/bp.png image Over 160,40 0,0 pieces/alpha/40/bp.png image Over 200,40 0,0 pieces/alpha/40/bp.png image Over 240,40 0,0 pieces/alpha/40/bp.png image Over 280,40 0,0 pieces/alpha/40/bp.png image Over 0,240 0,0 pieces/alpha/40/wp.png image Over 40,240 0,0 pieces/alpha/40/wp.png image Over 80,240 0,0 pieces/alpha/40/wp.png image Over 120,240 0,0 pieces/alpha/40/wp.png image Over 160,240 0,0 pieces/alpha/40/wp.png image Over 200,240 0,0 pieces/alpha/40/wp.png image Over 240,240 0,0 pieces/alpha/40/wp.png image Over 280,240 0,0 pieces/alpha/40/wp.png image Over 0,280 0,0 pieces/alpha/40/wr.png image Over 40,280 0,0 pieces/alpha/40/wn.png image Over 80,280 0,0 pieces/alpha/40/wb.png image Over 120,280 0,0 pieces/alpha/40/wq.png image Over 160,280 0,0 pieces/alpha/40/wk.png image Over 200,280 0,0 pieces/alpha/40/wb.png image Over 240,280 0,0 pieces/alpha/40/wn.png image Over 280,280 0,0 pieces/alpha/40/wr.png '       -quality 80 -depth 8 -comment "Created by Alexander Maryanovsky's chess diagram generator" +matte +dither -colors 128 png:'cache/e4cadb04f82f4e0ad120a004f5c6636a'
The command consists of roughly 4 parts:
  1. Create the canvas.
  2. Draw the board using rectangles (one for each square).
  3. Draw the pieces from pre-rendered PNG images.
  4. Specify output options and draw the image.
My testing (by disabling various parts) revealed that each part takes approximately the following amount of time:
  1. N/A
  2. 25ms.
  3. 50ms.
  4. 20ms.
Now, I can't say how long the 4th part "should" take, but it seems to me that parts 2 and 3 should really be much faster. Most surprising is part 2, which is just filling rectangles with solid color. I can't imagine why this would take 25ms on a modern computer.

Perhaps I am doing this the wrong way? Could you suggest ways to improve performance?


Thanks,
Alexander (aka Sasha) Maryanovsky.
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Drawing performance

Post by fmw42 »

draw the board by tiling a 2x2 pattern.

convert \( -size 15x15 xc:black xc:white -append \) \( +clone -flip \) +append \
-write mpr:checks +delete -size 240x240 tile:mpr:checks board.png


You can play with the colors and size of the squares and the size of the result to achieve your board design.
msasha

Re: Drawing performance

Post by msasha »

Is "mpr" a pseudo-format that keeps the image data in memory? I've been looking for something like that but couldn't find it in the documentation. I've tried a pre-rendered board image (loaded from a PNG file), but that's even slower than just drawing the rectangles.

I can't use your method exactly (because xc: doesn't support all the color formats that -fill does), but I'll see if I can squeeze any performance from using "mpr" (and tiling).
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Drawing performance

Post by fmw42 »

msasha wrote:Is "mpr" a pseudo-format that keeps the image data in memory? I've been looking for something like that but couldn't find it in the documentation. I've tried a pre-rendered board image (loaded from a PNG file), but that's even slower than just drawing the rectangles.

I can't use your method exactly (because xc: doesn't support all the color formats that -fill does), but I'll see if I can squeeze any performance from using "mpr" (and tiling).

Yes, mpr is kept in memory (but does not carry from one command line to another). see http://www.imagemagick.org/Usage/files/#mpr (This is needed for this type of tiling)

There is also memory mapped disc file. see http://www.imagemagick.org/Usage/files/#mpc

xc: does indeed allow any IM valid color representation. I do it all the time in my scripts. You just have to enclose the color in quotes or double quotes, esp for hex or rgb or hsl or hsb colors. see http://www.imagemagick.org/script/color.php
msasha

Re: Drawing performance

Post by msasha »

fmw42 wrote:Yes, mpr is kept in memory (but does not carry from one command line to another).
It would be quite magical if it did :-)

fmw42 wrote:xc: does indeed allow any IM valid color representation. I do it all the time in my scripts. You just have to enclose the color in quotes or double quotes, esp for hex or rgb or hsl or hsb colors. see http://www.imagemagick.org/script/color.php
Ah, you're right. I forgot to escape the parentheses for the rgb(r,g,b) format.
msasha

Re: Drawing performance

Post by msasha »

Can I use the mpr image inside a "draw 'image'" command? The page you linked seems to imply you can - it says:
It is also the only way to use the -draw 'image' method to overlay images using a generated in-memory image, though there are lots of other better methods to do this.
But when I run this:

Code: Select all

convert -size 40x40 xc:black -write mpr:light +delete -size 320x320 xc:none -draw 'image Over 0,0 40,40 mpr:light' test.png
I get

Code: Select all

convert: unable to open image `mpr': No such file or directory @ blob.c/OpenBlob/2480.
convert: Non-conforming drawing primitive definition `:' @ draw.c/DrawImage/3140.
convert: unable to open image `mpr': No such file or directory @ blob.c/OpenBlob/2480.
convert: Non-conforming drawing primitive definition `:' @ draw.c/DrawImage/3140.
msasha

Re: Drawing performance

Post by msasha »

Never mind - quoting the mpr filename worked:

Code: Select all

-draw 'image Over 0,0 40,40 "mpr:light"'
msasha

Re: Drawing performance

Post by msasha »

Unfortunately, I can't use the method you suggested as-is, because the board itself doesn't always cover the entire image (there are options, such as square coordinates which cause things to be drawn outside the board itself), and all my other attempts to use what you suggest (creating an mpr image and then tiling it) result in worse performance. For example, this (creating a 2-by-2 square tile and then using draw 'rectangle' to fill the board):

Code: Select all

convert \( -size 40x40  xc:'rgb(255,204,153)'  xc:'rgb(143,96,79)' -append \) \( +clone -flip \) +append -write mpr:tileableBoard +delete -size 320x320 xc:none -tile mpr:tileableBoard -draw 'rectangle 0,0,319,319'        -quality 80 -depth 8 -comment "Created by Alexander Maryanovsky's chess diagram generator" +matte +dither -colors 128 png:'cache/4484f66c13a7e3ec1b11c8c01a46d06d'
takes 140ms while the basic drawing of rectangle squares takes 40ms (your method as-is, takes 30ms).

Both of the following attempts (creating a full board mpr image and then drawing it at the right place) take 50ms:

Code: Select all

convert \( -size 40x40 xc:'rgb(255,204,153)' xc:'rgb(143,96,79)' -append \) \( +clone -flip \) +append -write mpr:tileableBoard +delete -size 320x320 tile:mpr:tileableBoard -write mpr:board +delete -size 320x320 xc:none -draw 'image Over 0,0 0,0 "mpr:board"'        -quality 80 -depth 8 -comment "Created by Alexander Maryanovsky's chess diagram generator" +matte +dither -colors 128 png:'cache/15497ec69ba9919af448128cb339860c'

Code: Select all

convert \( -size 40x40 xc:'rgb(255,204,153)' xc:'rgb(143,96,79)' -append \) \( +clone -flip \) +append +clone -append +clone +append +clone -append +clone +append -write mpr:board +delete -size 320x320 xc:none -draw 'image Over 0,0 0,0 "mpr:board"'        -quality 80 -depth 8 -comment "Created by Alexander Maryanovsky's chess diagram generator" +matte +dither -colors 128 png:'cache/15497ec69ba9919af448128cb339860c'
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Drawing performance

Post by snibgo »

In the code in your first posting, you seem to draw all the black squares and all the white squares. Have you tried drawing the entire board black, then just drawing the white squares? I suppose IM would then write half the pixels twice, but the reduction in parsing and housekeeping may be worthwhile.
snibgo's IM pages: im.snibgo.com
msasha

Re: Drawing performance

Post by msasha »

snibgo wrote:In the code in your first posting, you seem to draw all the black squares and all the white squares. Have you tried drawing the entire board black, then just drawing the white squares? I suppose IM would then write half the pixels twice, but the reduction in parsing and housekeeping may be worthwhile.
That was what I had assumed as well, and before I started optimizing, this is how it was done. Drawing each square individually turns out to be slightly faster. In fact, I have noticed that a lot of operations take time proportionate to the amount of pixels "touched" by them. This seems pretty unusual to me (for such small images, anyway), and makes me think that ImageMagick's drawing routines use something where "touching" a pixel is very expensive.
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Drawing performance

Post by fmw42 »

I've tried a pre-rendered board image (loaded from a PNG file), but that's even slower than just drawing the rectangles.
Since you presumably have only a binary or just a few colors in your board, why not use a precomputed board stored as a gif with just a few colors in its palette. That would have a very small filesize I would think.

Do you re-draw the board after each move. You could keep the board stored in a mpc (memory mapped disk file) from command line to command line and that would presumably give you fast access to it for each move.

I don't know the comparison times, but thought you might want to look into those options.
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Drawing performance

Post by magick »

What version of ImageMagick are you using? Recent versions run in parallel if you have a multi-core system. There are also performance improvements in recent versions.
msasha

Re: Drawing performance

Post by msasha »

fmw42 wrote:
I've tried a pre-rendered board image (loaded from a PNG file), but that's even slower than just drawing the rectangles.
Since you presumably have only a binary or just a few colors in your board, why not use a precomputed board stored as a gif with just a few colors in its palette. That would have a very small filesize I would think.

Do you re-draw the board after each move. You could keep the board stored in a mpc (memory mapped disk file) from command line to command line and that would presumably give you fast access to it for each move.

I don't know the comparison times, but thought you might want to look into those options.
I don't think it will be faster. An uncompressed PNG file with the board drawing is already a mere 309 bytes, so I don't think there's anything to be gained by making the file smaller. Also, from playing with the mpr images, it seems that even drawing a pre-made mpr image with "-draw" is not any faster than drawing the individual rectangles, so an mpc image will not be any faster either. This is what I meant by operations taking time proportionate to the amount of pixels "touched".
msasha

Re: Drawing performance

Post by msasha »

magick wrote:What version of ImageMagick are you using? Recent versions run in parallel if you have a multi-core system. There are also performance improvements in recent versions.

Code: Select all

[~]convert -version
Version: ImageMagick 6.5.8-3 2009-12-06 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2009 ImageMagick Studio LLC
Features: OpenMP 

I will try to compile a newer version and see if that improves things.
el_supremo
Posts: 1015
Joined: 2005-03-21T21:16:57-07:00

Re: Drawing performance

Post by el_supremo »

@Magick: In this situation would Q8 be any faster than Q16? It looks like the images end up on the web, so they don't need the precision of Q16.

Pete
Sorry, my ISP shutdown all personal webspace so my MagickWand Examples in C is offline.
See my message in this topic for a link to a zip of all the files.
Post Reply