Page 1 of 1

[SOLVED] MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-26T14:09:08-07:00
by GlowingApple
I'm working on a project and need to combine a directory of PNGs into an animated GIF. So far my code is working fine, except none of the MagickSetOption calls seem to be taking any effect. I wrote a very basic C program (my full project is in Python, using Wand to interface with MagickWand) to read the looped GIF, set looping to "1", and save the file, but it's not working either. The resulting GIF is still looping...

Code: Select all

#include <wand/magick_wand.h>

int main(int argc, char* argv[])
{
    MagickWand *wand = NULL;
    MagickWandGenesis();

    wand = NewMagickWand();
	if (! MagickReadImage(wand, "looped.gif")) {
        return 1;
    }
    MagickSetOption(wand, "loop" , "1");
	if (! MagickWriteImages(wand, "unlooped.gif", MagickTrue)) {
        return 1;
    }

	if (wand) {
        wand = DestroyMagickWand(wand);
    }
	MagickWandTerminus();

    return 0;
}
Running ImageMagick with "looped.gif" on the CLI

Code: Select all

convert -loop 1 looped.gif unlooped.gif
gives me a non-looping GIF as expected. So it appears the issue is with how I'm using the MagickWand API.

Any ideas on what I'm doing wrong? Thanks!

Re: MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-26T14:39:04-07:00
by GlowingApple
I just tried taking the bunny.c example (https://www.imagemagick.org/MagickWand/bunny.htm) and changing

Code: Select all

MagickSetOption(aw,"loop","0");
to

Code: Select all

MagickSetOption(aw,"loop","1");
, but it is generating a looped GIF still.

Re: MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-27T01:51:56-07:00
by snibgo
Try

Code: Select all

MagickSetImageIterations(aw,1);

Re: MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-27T03:52:42-07:00
by GlowingApple
I added MagickSetImageIterations to both my test program and the "bunny" example program, but unfortunately there's no change to either GIF.

I added

Code: Select all

printf("%lu\n", MagickGetImageIterations(aw));
after the MagickSetImageIterations line, and confirmed that iterations are set to 1, but the GIFs still loop continuously.

Re: MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-27T04:32:40-07:00
by snibgo
MagickSetImageIterations(aw,1) instead of MagickSetOption(aw,"loop","1"), works for me with IM v7.0.7-28, viewing the Gif with Firefox v59.0.2. This is on Windows 8.1, building with Cygwin tools, though I doubt that makes any difference.

The bunny.c program you cite was last updated in 2012. I don't know when Iterations superceded Option.

If your IM is older than 7.0.7-28, I suggest you try an upgrade.

Re: MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-27T07:46:27-07:00
by GlowingApple
I was using IM v6.9.6-8 (because the Python library, Wand, has not been updated to support 7), so I installed IM v7.0.7-28. I'm still not seeing MagickSetImageIterations take effect. I'm on macOS, building with homebrew, and viewing the GIF in Firefox v59.0.2 as well.

Here's my latest code:

Code: Select all

#include <stdio.h>
#include <MagickWand/MagickWand.h>

int main(int argc, char* argv[])
{
    MagickWand *wand = NULL;
    MagickWandGenesis();

    wand = NewMagickWand();
    if (! MagickReadImage(wand, "looped.gif")) {
        return 1;
    }
    printf("%lu\n", MagickGetImageIterations(wand));
    printf("%s\n", MagickSetImageIterations(wand, 1)? "true" : "false");
    printf("%lu\n", MagickGetImageIterations(wand));
    if (! MagickWriteImages(wand, "unlooped.gif", MagickTrue)) {
        return 1;
    }

    if (wand) {
        wand = DestroyMagickWand(wand);
    }
    MagickWandTerminus();

    return 0;
}
I'm compiling with

Code: Select all

cc -o MagicWandTest MagicWandTest.c `pkg-config MagickWand --cflags --libs` -Wno-empty-body
I've been testing with http://www.imagemagick.org/Usage/anim_b ... y_bgnd.gif, and renaming the file to 'looped.gif'. I've tried with a few looped GIFs, but it doesn't seem to work on any of them.

Is the only difference then that I'm running ImageMagick on macOS?

Re: MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-27T12:29:06-07:00
by snibgo
GlowingApple wrote:Is the only difference then that I'm running ImageMagick on macOS?
No. The difference is that you are running a different test from me. I was using "bunny.c", which builds a new wand and saves it as a gif. Your test reads a gif, modifies it (setting the iterations) and writes it.

Your test also fails for me.

This isn't an area of IM I know well, but the problem seems to be that when reading a GIF, IM sets the iteration for each individual image. It then sets the GIF file iterations from the iterations field of the first image.

So the fix is to re-initialize individual iterations:

Code: Select all

    size_t i;
    for(i=0; i<MagickGetNumberImages(wand); i++) {
        MagickSetIteratorIndex(wand,i);
        MagickSetImageIterations(wand, 0);
    }
    MagickResetIterator(wand);
When I insert that code in your program before setting iterations for the overall wand, the program works.

My added code modifies all the individual images. Perhaps it would work if only the first one was modified. It seems cleaner to modify them all. But I'm happy to be corrected (as always).

Re: MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-27T13:43:42-07:00
by GlowingApple
Excellent! This is working for me both in my C program and my Python program (I was able to get Wand working with ImageMagick 7).

I confirmed that setting the iterations on the first image also works:

Code: Select all

MagickSetIteratorIndex(wand, 0);
MagickSetImageIterations(wand, 1);
Though I can't imaging running this on every image frame is that expensive, and my end goal is to assemble a GIF from a collection of images, so I will need to set a delay on each frame individually anyway.

Thank you very much for all your help! Having access to the ImageMagick API from within my Python program is going to make things much easier, and likely will eliminate some other image library dependencies that can be replaced with just the single ImageMagick API.

Re: [SOLVED] MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-27T13:49:50-07:00
by GlowingApple
For anyone who's looking to do combine images into an animated GIF using Python3/Wand/ImageMagick, here is the code I'm using:

Code: Select all

import sys

from wand.image import Image
from wand.api import library

def main(filenames):
    with Image() as gif:
        print(f"Adding {len(filenames)} frames...")
        for filename in filenames:
            gif.sequence.append(Image(filename=filename))
        for frame in gif.sequence:
            with frame:
                # Set GIF to not looping
                # (technically only needs to be done on the first frame)
                library.MagickSetImageIterations(gif.wand, 1)
                frame.delay = 20
        print('Saving GIF...')
        gif.type = 'optimize'
        gif.save(filename='test.gif')

    return 0

if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))
I'm running this Python script followed by a list of image file paths.

Re: [SOLVED] MagickSetOption(wand, "loop" , "1") is not working

Posted: 2018-04-27T14:22:49-07:00
by snibgo
Good stuff, I'm glad that works. Thanks for the Python version.

I tested your C program under v6.9.3-7. With just the overall MagickSetImageIterations() it fails to prevent looping, but with the extra code it succeeds.

For clarity, here is the program (in the v6 version; for v7, change the include):

Code: Select all

/* Test program to read a GIF file,
   modify the numbr of iterations,
   then write as another GIF file.
*/

#include <stdio.h>
#include <wand/MagickWand.h>

int main(int argc, char* argv[])
{
    MagickWand *wand = NULL;
    MagickWandGenesis();

    wand = NewMagickWand();
    if (! MagickReadImage(wand, "looped.gif")) {
        return 1;
    }


    // These lines are needed to "really" set the iterations.
    // Tested under IM v6.9.3-7 and v7.0.7-28.
    //
    size_t i;
    for(i=0; i<MagickGetNumberImages(wand); i++) {
        MagickSetIteratorIndex(wand,i);
        MagickSetImageIterations(wand, 0);
    }
    MagickResetIterator(wand);


    // This doesn't set iterations.
    //MagickSetOption(wand,"loop","2");

    printf("%lu\n", MagickGetImageIterations(wand));
    printf("%s\n", MagickSetImageIterations(wand, 1)? "true" : "false");
    printf("%lu\n", MagickGetImageIterations(wand));
    if (! MagickWriteImages(wand, "unlooped.gif", MagickTrue)) {
        return 1;
    }

    if (wand) {
        wand = DestroyMagickWand(wand);
    }
    MagickWandTerminus();

    return 0;
}