I am trying to chromakey a GIF and the result is weird. The GIF should have a framerate of 30 FPS, but when I put the chromakeyed and transparent frames back together the GIF isn't what it should be. The GIF displays a waveform which should sync up to a specific song. When I play the input GIF under the waveform it does sync up correctly, the output GIF does not. It feels like it's to fast, or even the frames are not in order. It is hard to describe but the GIF changes rapidly, the "loudness" of the waveform starts earlier than the song gets "loud". This is the code I am using to chromakey and save the frames.
The code used to get the frames:
import imageioimport numpy as npimport osfrom PIL import Imagedef chromakey(image, color, tolerance): if image.mode != 'RGBA': image = image.convert('RGBA') data = np.array(image) color = np.array(color)[np.newaxis, np.newaxis, :] lower_bound = color - tolerance upper_bound = color + tolerance mask = np.all(np.logical_and(data[:, :, :3] >= lower_bound, data[:, :, :3] <= upper_bound), axis=-1) data[mask] = [0, 0, 0, 0] return Image.fromarray(data)def extract_frames(input_path, output_folder, color, tolerance): if not os.path.exists(output_folder): os.makedirs(output_folder) gif = imageio.get_reader(input_path) for i, frame in enumerate(gif): frame = Image.fromarray(frame) processed_frame = chromakey(frame, color, tolerance) frame_path = os.path.join(output_folder, f"frame_{i:03d}.png") processed_frame.save(frame_path)input_gif_path = 'input.gif'output_folder = 'frames'black_color = (0, 0, 0)tolerance = 25extract_frames(input_gif_path, output_folder, black_color, tolerance)
The code to combine them to a GIF again:
import osfrom PIL import Imagedef read_frames_from_folder(folder): # Get a sorted list of frame files frame_files = sorted([f for f in os.listdir(folder) if f.endswith('.png')]) frames = [] for frame_file in frame_files: frame_path = os.path.join(folder, frame_file) frame = Image.open(frame_path) frames.append(frame) return framesdef create_gif_from_frames(frames, output_gif_path, fps): # Calculate duration per frame based on FPS duration = int(1000 / fps) # Duration in milliseconds # Save the frames as a new GIF with disposal=2 frames[0].save(output_gif_path, save_all=True, append_images=frames[1:], duration=duration, loop=0, disposal=2)output_folder = 'frames'output_gif_path = 'output.gif'fps = 20 # Set the desired FPS here# Read frames from folderframes = read_frames_from_folder(output_folder)# Create GIF from framescreate_gif_from_frames(frames, output_gif_path, fps)
Here is what the original GIF looks like:https://streamable.com/qcut9l
And here is the processed one:https://streamable.com/ff1aq4
Thank you in advance!
I tried to change the framerate, but that still gave me the same problem, just more choppy.Then I checked the metadata of the output GIF which has the correct duration.I then thought the problem may not be in the part where the GIF is generated but where the frames are generated, but the amount of frames generated is correct too. Asking ChatGPT-4o didn't help anything and I didn't find any answers online either.