Using scripts made for single images on animated GIFs

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?".
Post Reply
AlicanC
Posts: 9
Joined: 2015-10-22T01:43:05-07:00
Authentication code: 1151

Using scripts made for single images on animated GIFs

Post by AlicanC »

I have found a few cool scripts on fmw42's website that I want to use on some animated GIFs. It currently looks like I have to dissect the GIF, run the script for every frame and stitch it back again.

I was wondering if there is a way to make the cartoon script and other complicated scripts like that work on GIFs.

Code: Select all

convert.exe in.miff +repage -depth 8 -selective-blur 0x5+10% blur.miff

convert.exe blur.miff -level 0x70% -set colorspace RGB -set colorspace gray -posterize 6 -depth 8 -colorspace sRGB posterize.miff

convert.exe blur.miff
  ( posterize.miff -blur 0x1 )
  ( -clone 0 -clone 1 -compose over -compose multiply -composite -modulate 100,150,100 )
  ( -clone 0 -set colorspace RGB -colorspace gray )
  ( -clone 3 -negate -blur 0x2 )
  ( -clone 3 -clone 4 -compose over -compose colordodge -composite -evaluate pow 4 -threshold 90% -statistic median 3x3 )
  -delete 0,1,3,4 -compose over -compose multiply -composite out.gif
The first two commands work perfectly but the third command messes things up and I end up with a weird looking single frame GIF. If I extract the first frame of my source and run these three commands on it, I get the perfect result so I am sure that the commands are fine. (Besides the third one not being able to work on animated GIFs.)

Any help is appreciated.
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Using scripts made for single images on animated GIFs

Post by snibgo »

When IM reads an image file, it creates a list of the images. There might be only one image in the list, or a thousand. An animated GIF contains many images.

Most IM operators, like "-level" and "-blur", operate on the entire list. So these work fine with animated GIFs, provided all the images can be held in memory at the same time.

Operators like "-clone 0" operate on a single image from the list, in this case the first image. So they won't apply the same processing to all the images in the list.
snibgo's IM pages: im.snibgo.com
AlicanC
Posts: 9
Joined: 2015-10-22T01:43:05-07:00
Authentication code: 1151

Re: Using scripts made for single images on animated GIFs

Post by AlicanC »

I suspected the same thing.

What I am hoping for is something like a foreach loop in "convert" that can run the "-clone"s for each frame, thus eliminating the need for copying all the frames into their own files. I/O takes time and it really slows down the process. For example, a 4 second long GIF with 30fps has 120 frames which means writing 120 files, starting 120 instances of "convert" and reading 120 files in total. That's a lot of unnecessary work.

So for example, something like this would solve the problem:

Code: Select all

convert in.gif ... blur.miff
convert blur.miff ... posterize.miff
convert blur.miff -foreachframe ( blur.miff[frameId] ( -clone whatever ... posterize.miff[frameId] ) ... ) out.gif
If I can't do that, could this be a thing?:

Code: Select all

convert in.gif ... blur.miff
convert blur.miff ... posterize.miff
convert blur.miff[0] ... posterize.miff[0] ... out.miff[0]
convert blur.miff[1] ... posterize.miff[1] ... out.miff[1]
convert blur.miff[2] ... posterize.miff[2] ... out.miff[2]
convert blur.miff[3] ... posterize.miff[3] ... out.miff[3]
convert out.miff .. out.gif
This would at least save me writing of 120 frame files. (For the 4 sec gif @ 30fps example.) I couldn't find a way to do "out.miff[index]" though. Is there a way that I can replace a single frame of an out file?
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Using scripts made for single images on animated GIFs

Post by snibgo »

Is there a way that I can replace a single frame of an out file?
Sadly, no. File.ext[n] works only for inputs.

But you can use piping. Bash syntax:

Code: Select all

( convert in.gif[0] {do something} miff:-
  {other commands}
  convert in.gif[999] {do something} miff:-
) |convert miff:- out.gif
The code in parentheses can be a loop. Windows BAT example:

Code: Select all

( @for %%C in (blue,green) do @%IM%convert -size 50x50 xc:%%C miff:- ) | %IM%convert miff:- +append out.png
snibgo's IM pages: im.snibgo.com
AlicanC
Posts: 9
Joined: 2015-10-22T01:43:05-07:00
Authentication code: 1151

Re: Using scripts made for single images on animated GIFs

Post by AlicanC »

Now my script looks like this:
1- Read frame count of GIF: "${sourcePath}" -format "%n " info:
2- Read frames directly from source GIF and write them to files: "${sourcePath}[${frameIndex}]" ... "processed-%04d.miff"
3- Read each processed frame and write the resulting GIF: "processed-*.miff" out.gif

So for 4sec@30fps: (1 read) + 120 * (1 read + 1 write) + (120 read + 1 write) = 121 files written, 122 processes

If I pipe the data between step 1 and 2 instead of writing, files written will reduce dramatically (to 1) but I will still have 122 processes. If the GIF is like 15sec@30fps, that would mean I will have 450 processes running in parallel on step 2. That's more than half of what OS X allows (709) and it's crazy even if you don't consider the limit.

This means I should further complicate my script so it doesn't run more processes than the number of cores in the system at once or something. I could do it one step at a time, but then the process would be slow as a dying turtle. (It is still slow even with parallelism.)

Anyways, I will try piping instead of writing. Maybe I/O is the bigger problem.
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Using scripts made for single images on animated GIFs

Post by snibgo »

Scripting is enhanced in IM V7. Perhaps it includes a solution to this problem. I don't know when it will be released.

Another way, my current favourite, is to write one very long convert command (like an optimising compiler will unroll loops). The command is created by the script, and then run within the script. Only one convert, and only as many image file reads and writes as are logically required. But this won't work when you want to call a script for each frame because IM doesn't have a "shell-out" operation. And frames won't be worked on in parallel.
snibgo's IM pages: im.snibgo.com
AlicanC
Posts: 9
Joined: 2015-10-22T01:43:05-07:00
Authentication code: 1151

Re: Using scripts made for single images on animated GIFs

Post by AlicanC »

Spent a few hours trying to make this work with no success. Today I decided to create a small program that does the same thing so I can debug it easier, but I kept ending up with this:

Code: Select all

convert.exe: ImproperImageHeader `C:/Users/User/AppData/Local/Temp/magick-15480xxvx70OQrJCb' @ error/miff.c/ReadMIFFImage/507.
convert.exe: NoImagesDefined `result.gif' @ error/convert.c/ConvertImageCommand/3230.
Assuming I must be doing something horribly wrong, I started to try piping examples in the docs:

Code: Select all

> convert rose: gif:- | convert - -resize "200%" bigrose.jpg
convert.exe: corrupt image `C:/Users/User/AppData/Local/Temp/magick-1409670dIVu2CYoqE' @ error/gif.c/ReadGIFImage/1368.
convert.exe: no images defined `bigrose.jpg' @ error/convert.c/ConvertImageCommand/3230.

> convert logo: gif:- | convert gif:- test.png
convert.exe: corrupt image `-' @ error/gif.c/ReadGIFImage/1368.
convert.exe: no images defined `test.png' @ error/convert.c/ConvertImageCommand/3230.
I have two questions here:
1- What the hell am I missing here? :D
2- Why is there a temp file? My whole point was reducing I/O.

Edit:

Code: Select all

>osver
Microsoft Windows 8.1 Enterprise 6.3.9600.0

>convert
Version: ImageMagick 6.9.2-4 Q16 x64 2015-10-10 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Visual C++: 180031101
Features: Cipher DPC Modules OpenMP
Delegates (built-in): bzlib cairo freetype jng jp2 jpeg lcms lqr openexr pangocairo png ps rsvg tiff webp xml zlib
...
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Using scripts made for single images on animated GIFs

Post by snibgo »

Your commands work fine for me, Windows 8.1, IM 6.9.1-6. Do you have free space in C:/Users/User/AppData/Local/Temp/, which I suppose is %TEMP%? Can you create files there?
snibgo's IM pages: im.snibgo.com
AlicanC
Posts: 9
Joined: 2015-10-22T01:43:05-07:00
Authentication code: 1151

Re: Using scripts made for single images on animated GIFs

Post by AlicanC »

Creating a file there is perfectly fine:

Code: Select all

C:\Users\User\AppData\Local\Temp> convert rose: gif:- | convert - -resize "200%" bigrose.jpg
convert.exe: corrupt image `C:/Users/User/AppData/Local/Temp/magick-15076Pe9d_SFQpkYE' @ error/gif.c/ReadGIFImage/1368.
convert.exe: no images defined `bigrose.jpg' @ error/convert.c/ConvertImageCommand/3230.

C:\Users\User\AppData\Local\Temp> cat magick-15076Pe9d_SFQpkYE
cat : Cannot find path 'C:\Users\User\AppData\Local\Temp\magick-15076Pe9d_SFQpkYE' because it does not exist.

C:\Users\User\AppData\Local\Temp> echo "test" > magick-15076Pe9d_SFQpkYE

C:\Users\User\AppData\Local\Temp> cat magick-15076Pe9d_SFQpkYE
test
Also got 18GB space on that disk. Why does it create a temp file anyway?
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Using scripts made for single images on animated GIFs

Post by snibgo »

Does text piping work? For example:

Code: Select all

dir |more
How about other binary piping?
snibgo's IM pages: im.snibgo.com
AlicanC
Posts: 9
Joined: 2015-10-22T01:43:05-07:00
Authentication code: 1151

Re: Using scripts made for single images on animated GIFs

Post by AlicanC »

Piping didn't work in PowerShell, but it did work in regular "cmd.exe". I'll post my final code when it's open source ready.
Post Reply