Page 1 of 2

Drawing performance

Posted: 2010-03-16T15:14:35-07:00
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.

Re: Drawing performance

Posted: 2010-03-16T15:38:25-07:00
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.

Re: Drawing performance

Posted: 2010-03-16T16:16:31-07:00
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).

Re: Drawing performance

Posted: 2010-03-16T17:57:32-07:00
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

Re: Drawing performance

Posted: 2010-03-17T08:47:42-07:00
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.

Re: Drawing performance

Posted: 2010-03-17T12:45:08-07:00
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.

Re: Drawing performance

Posted: 2010-03-17T13:09:10-07:00
by msasha
Never mind - quoting the mpr filename worked:

Code: Select all

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

Re: Drawing performance

Posted: 2010-03-17T14:36:01-07:00
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'

Re: Drawing performance

Posted: 2010-03-17T16:21:53-07:00
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.

Re: Drawing performance

Posted: 2010-03-17T17:11:08-07:00
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.

Re: Drawing performance

Posted: 2010-03-17T18:15:04-07:00
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.

Re: Drawing performance

Posted: 2010-03-17T18:22:36-07:00
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.

Re: Drawing performance

Posted: 2010-03-17T18:57:21-07:00
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".

Re: Drawing performance

Posted: 2010-03-17T18:58:55-07:00
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.

Re: Drawing performance

Posted: 2010-03-17T20:13:15-07:00
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