Search code examples
rubyimagemagickrmagick

Extend a gif with ImageMagick


I am working on a project that has multiple plugins, and one of the plugins needs to composite two animated images on top of one another. The problem is that one of these will be user-defined and will most likely not match up frame-wise to the other.

How I get around this currently is by loading both images, finding the lowest common multiple of their frame count, then extracting them over and over until I hit their LCM, then re-combining the extracted images into separate gif files, then compositing those two together. Here is the relevant code as far as the extending goes:

def self.extend_gifs(one, two)
    img_one = Magick::ImageList.new(one).coalesce
    img_two = Magick::ImageList.new(two).coalesce

    lcm = img_one.length.lcm(img_two.length)

    i = 0
    while i < lcm
        img_one[i % img_one.length].write("tmp/1_#{i.to_s.rjust(4, "0")}.png")
        img_two[i % img_two.length].write("tmp/2_#{i.to_s.rjust(4, "0")}.png")
        i += 1
    end

    %x{convert -limit memory 256MiB -dispose Background tmp/1_*.png tmp/extend_1.gif}
    %x{convert -limit memory 256MiB -dispose Background tmp/2_*.png tmp/extend_2.gif}

    Dir["tmp/*.png"].each { |f| File.delete f }
end

The problem is that this is deployed on a server with 1GB of memory, and running this code in ruby takes upwards of 500MB or more depending on how many frames or how big the user defined gif is, not including the memory that the convert process takes. Because of this, I want to move away from using rmagick and ruby and drop the process onto the external tools I have available which can limit memory usage to some degree.

This brings me to my question: How would I replicate this process using the imagemagick command line tools?

Edit:

By request, here are some example images and output. This should work for any two images though, assuming one has a transparent background.

Background image (3 frames):

Foreground image (11 frames):

Result (foreground repeated 3 times, background repeated 11 times).


Solution

  • So after some messing around and viewing some other questions that were unrelated I found out that you can specify the same image multiple times to continue extraction. My updated code isn't cleaned yet, but here it is:

    def self.extend_gifs(one, two)
        img_one = Magick::ImageList.new(one)
        img_two = Magick::ImageList.new(two)
    
        # Get the lcm of the frame counts
        lcm = img_one.length.lcm(img_two.length)
    
        # Get how many times to extract for each
        mult_one = lcm / img_one.length
        mult_two = lcm / img_two.length
    
        # Multiply the image + coalecence out that many times
        string_one = (" #{one} -coalesce ") * mult_one
        string_two = (" #{two} -coalesce ") * mult_two
    
        # Split images up so their frames match
        %x{convert -limit memory 256MiB #{string_one} tmp/1_%04d.png}
        %x{convert -limit memory 256MiB #{string_two} tmp/2_%04d.png}
    
        # Merge gifs back together
        %x{convert -limit memory 256MiB -dispose Background tmp/1_*.png tmp/extend_1.gif}
        %x{convert -limit memory 256MiB -dispose Background tmp/2_*.png tmp/extend_2.gif}
    
        # Delete temp files
        Dir["tmp/*.png"].each { |f| File.delete f }
    end