0

I'm using this bit of code to call gm in Node.js to overlay text on animated GIFs:

var gm = require('gm');

gm(infile)
    .stroke("#000000")
    .fill('#ffffff')
    .font("./impact.ttf", 42)
    .dither(false)
    .drawText(0, 0, text, 'South')
    .write(outfile, function (err) {
        if (!err) {
            console.log('Image processing done.');
        }
        else console.log(err);
});

It works fine, however a bit on the slow side. I think (or at least i hope) i can do better.

Now, that thing really translates to (i spied on gm):

$ convert /tmp/input.gif -stroke "#000000" -fill "#ffffff" \
          -pointsize 42 -font ./impact.ttf \
          -draw "gravity south text 0,0 SOME TEXT" \
          -dither None output.gif

This takes about 30 seconds to process.

I've tried a few things:

  • Adding -colors 128 shaves off around 7 seconds (but if the original .gif is 256 colors, some quality is lost).
  • -coalesce adds 20 more seconds to the 30, no go.

The text is overlay on every frame, here's the final product (NOT the .gif i'm testing with - just a sample):

moneyburn

QUESTION #1

Is there anything i can do to make this faster, other than throw more compute at it? I'm open to lowering image quality.. but you know.. sensibly.

QUESTION #2

How would i go about getting some progress indicator in my frontend? Can i listen for an event that has an approximate idea of how much data/time remains? An example would be fantastic.

Since all i'm doing is change a few pixels at the bottom on the input GIF, the live file size of input GIF vs resulting GIF is a pretty good indicator of how much time is left, but wondering if there's a better approach.

EDIT:

This is the GIF i'm testing with:
https://m.popkey.co/bca7ab/ygQJw.gif

$ identify -version
Version: ImageMagick 6.8.9-9 Q16 i686 2016-06-01 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2014 ImageMagick Studio LLC
Features: DPC Modules OpenMP
Delegates: bzlib cairo djvu fftw fontconfig freetype jbig jng jpeg lcms
           lqr ltdl lzma openexr pangocairo png rsvg tiff wmf x xml zlib
evilSnobu
  • 24,582
  • 8
  • 41
  • 71
  • Mmm, that's odd - it takes 0.3s on my iMac. Have you got OpenCL enabled by any chance - try `identify -version` – Mark Setchell Sep 13 '16 at 16:43
  • I'm running in AWS on a tiny instance. I don't have OpenCL ..or a GPU. – evilSnobu Sep 13 '16 at 16:45
  • Oh and more importantly I'm testing with a 10MB gif. Not the one above. – evilSnobu Sep 13 '16 at 16:46
  • Try running `identify -list resource` and seeing how much memory is available to ImageMagick. If on the low side, you can increase the RAM available to ImageMagick with `convert -limit memory 512MiB ... usual stuff ...` You can also debug paging out to disk with `convert -debug cache .... usual stuff ...` You could also use `convert -debug all ...` to get an idea of progress. – Mark Setchell Sep 15 '16 at 23:00

1 Answers1

1

I see you are running the Q16 version with 16-bit quantisation and that is probably overkill if you are happy with 128 colour GIFs anyway.

I got a 50% time reduction (albeit from 6s to 3s) on my desktop iMac by changing from the 16-bit to the 8-bit version. To get the 8-bit version, you need to rebuild using:

./configure --with-quantum-depth=8 ... your usual options...

Not only does that speed things up, but it also relieves "memory pressure" so it may help your application in other ways too by minimising paging.

You could also try adding in --enable-zero-configuration to prevent ImageMagick reading lots of XML config files at startup.

Some things run faster without OpenMP, so you could also consider disabling that when you run ./configure.

See also my comment about resources:

identify -list resource

and maybe increasing the memory available to ImageMagick using:

convert -limit memory 512MiB ....

As regards following progress, you could use -monitor like this:

convert -monitor anim.gif -stroke "#000000" -fill "#ffffff" -pointsize 42 -draw "gravity south text 0,0 'SOME TEXT'" -dither None output.gif

Sample Output

load image[anim.gif]: 1 of 2, 100% complete
load image[anim.gif]: 2 of 3, 100% complete
load image[anim.gif]: 3 of 4, 100% complete
load image[anim.gif]: 4 of 5, 100% complete
load image[anim.gif]: 5 of 6, 100% complete
load image[anim.gif]: 6 of 7, 100% complete
load image[anim.gif]: 7 of 8, 100% complete
load image[anim.gif]: 8 of 9, 100% complete
load image[anim.gif]: 9 of 10, 100% complete
load image[anim.gif]: 10 of 11, 100% complete
load image[anim.gif]: 11 of 12, 100% complete
load image[anim.gif]: 12 of 13, 100% complete
load image[anim.gif]: 13 of 14, 100% complete
load image[anim.gif]: 14 of 15, 100% complete
load image[anim.gif]: 15 of 16, 100% complete
load image[anim.gif]: 16 of 17, 100% complete
load image[anim.gif]: 17 of 18, 100% complete
load image[anim.gif]: 18 of 19, 100% complete
load image[anim.gif]: 19 of 20, 100% complete
load image[anim.gif]: 20 of 21, 100% complete
load image[anim.gif]: 21 of 22, 100% complete
load image[anim.gif]: 22 of 23, 100% complete
load image[anim.gif]: 23 of 24, 100% complete
load image[anim.gif]: 24 of 25, 100% complete
load image[anim.gif]: 25 of 26, 100% complete
load image[anim.gif]: 26 of 27, 100% complete
load image[anim.gif]: 27 of 28, 100% complete
load image[anim.gif]: 28 of 29, 100% complete
load image[anim.gif]: 29 of 30, 100% complete
load image[anim.gif]: 30 of 31, 100% complete
load image[anim.gif]: 31 of 32, 100% complete
load image[anim.gif]: 32 of 33, 100% complete
load image[anim.gif]: 33 of 34, 100% complete
load image[anim.gif]: 34 of 35, 100% complete
load image[anim.gif]: 35 of 36, 100% complete
load image[anim.gif]: 36 of 37, 100% complete
load image[anim.gif]: 37 of 38, 100% complete
load image[anim.gif]: 38 of 39, 100% complete
load image[anim.gif]: 39 of 40, 100% complete
load image[anim.gif]: 40 of 41, 100% complete
load image[anim.gif]: 41 of 42, 100% complete
load image[anim.gif]: 42 of 43, 100% complete
load image[anim.gif]: 43 of 44, 100% complete
load image[anim.gif]: 44 of 45, 100% complete
load image[anim.gif]: 45 of 46, 100% complete
load image[anim.gif]: 46 of 47, 100% complete
load image[anim.gif]: 47 of 48, 100% complete
load image[anim.gif]: 48 of 49, 100% complete
load image[anim.gif]: 49 of 50, 100% complete
load image[anim.gif]: 50 of 51, 100% complete
load image[anim.gif]: 51 of 52, 100% complete
load image[anim.gif]: 52 of 53, 100% complete
load image[anim.gif]: 53 of 54, 100% complete
load image[anim.gif]: 54 of 55, 100% complete
load image[anim.gif]: 55 of 56, 100% complete
load image[anim.gif]: 56 of 57, 100% complete
load image[anim.gif]: 57 of 58, 100% complete
load image[anim.gif]: 58 of 59, 100% complete
load image[anim.gif]: 59 of 60, 100% complete
load image[anim.gif]: 60 of 61, 100% complete
load image[anim.gif]: 61 of 62, 100% complete
load image[anim.gif]: 62 of 63, 100% complete
load image[anim.gif]: 63 of 64, 100% complete
load image[anim.gif]: 64 of 65, 100% complete
load image[anim.gif]: 65 of 66, 100% complete
load image[anim.gif]: 66 of 67, 100% complete
load image[anim.gif]: 67 of 68, 100% complete
load image[anim.gif]: 68 of 69, 100% complete
load image[anim.gif]: 69 of 70, 100% complete
load image[anim.gif]: 70 of 71, 100% complete
load image[anim.gif]: 71 of 72, 100% complete
load image[anim.gif]: 72 of 73, 100% complete
load image[anim.gif]: 73 of 74, 100% complete
load image[anim.gif]: 74 of 75, 100% complete
load image[anim.gif]: 75 of 76, 100% complete
load image[anim.gif]: 76 of 77, 100% complete
load image[anim.gif]: 77 of 78, 100% complete
load image[anim.gif]: 78 of 79, 100% complete
load image[anim.gif]: 79 of 80, 100% complete
load image[anim.gif]: 80 of 81, 100% complete
load image[anim.gif]: 81 of 82, 100% complete
load image[anim.gif]: 82 of 83, 100% complete
load image[anim.gif]: 83 of 84, 100% complete
load image[anim.gif]: 84 of 85, 100% complete
load image[anim.gif]: 85 of 86, 100% complete
load image[anim.gif]: 86 of 87, 100% complete
load image[anim.gif]: 87 of 88, 100% complete
load image[anim.gif]: 88 of 89, 100% complete
load image[anim.gif]: 89 of 90, 100% complete
load image[anim.gif]: 90 of 91, 100% complete
load image[anim.gif]: 91 of 92, 100% complete
load image[anim.gif]: 92 of 93, 100% complete
load image[anim.gif]: 93 of 94, 100% complete
load image[anim.gif]: 94 of 95, 100% complete
load image[anim.gif]: 95 of 96, 100% complete
load image[anim.gif]: 96 of 97, 100% complete
load image[anim.gif]: 97 of 98, 100% complete
load image[anim.gif]: 98 of 99, 100% complete
load image[anim.gif]: 99 of 100, 100% complete
load image[anim.gif]: 100 of 101, 100% complete
load image[anim.gif]: 101 of 102, 100% complete
load image[anim.gif]: 102 of 103, 100% complete
load image[anim.gif]: 103 of 104, 100% complete
load image[anim.gif]: 104 of 105, 100% complete
load image[anim.gif]: 105 of 106, 100% complete
mogrify image[anim.gif]: 106 of 107, 100% complete
classify image colors[output.gif]: 313 of 314, 100% complete
assign image colors[output.gif]: 313 of 314, 100% complete
classify image colors[output.gif]: 313 of 314, 100% complete
...
...
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • That's probably the worst progress indicator of all time, but it's not your fault. Here's the bounty! Thanks for the effort and for well documenting your answer! – evilSnobu Sep 19 '16 at 18:54