I am looking some kind method to get gif frames number. I am looking on Google, StackOverflow and any other sites and I find only rubbish! Someone know how to do it? I need only simple number of gif frames.
To extract the frames, right-click on the GIF image, and select Extract Frames option. A new window will open. There, use the slider to set a range for frames. Finally, use the Extract Frames button, and then you can select the output folder and format to save frames as images.
Standard GIFs run between 15 and 24 frames per second.
I did some timings on the currently proposed answers, which might be of interest:
13.2 ms ± 58.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
115 µs ± 647 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
13.2 ms ± 169 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
13.1 ms ± 23.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
So despite being pure python, custom parsing is about 100x faster than using pillow ... interesting. Among the other solutions, I like the ImageIO one, because it is short; however, I'm one of the devs there so I am obviously biased.
Setup code:
# get a test GIF (as in-memory stream to avoid measuring file IO)
import imageio.v3 as iio
import io
gif_array = iio.imread("imageio:newtonscradle.gif", index=None)
test_file = io.BytesIO()
iio.imwrite(test_file, gif_array, format="GIF")
# ImageIO is more strict with file handling and would close test_file
# It does handle byte strings natively though, so we can pass that for timings
gif_bytes = iio.imwrite("<bytes>", gif_array, format="GIF")
Pillow seek:
%%timeit
from PIL import Image
test_file.seek(0) # reset file
n_frames = 1 # opening returns the first frame
with Image.open(test_file) as file:
# To iterate through the entire gif
try:
while 1:
file.seek(file.tell()+1)
n_frames += 1
# do something to im
except EOFError:
pass # end of sequence
assert n_frames == 36
Custom Parsing
%%timeit
class GIFError(Exception): pass
def get_gif_num_frames(filename):
frames = 0
with io.BytesIO(filename) as f: # I modified this line to side-step measuring IO
if f.read(6) not in (b'GIF87a', b'GIF89a'): # I added b to mark these as byte strings
raise GIFError('not a valid GIF file')
f.seek(4, 1)
def skip_color_table(flags):
if flags & 0x80: f.seek(3 << ((flags & 7) + 1), 1)
flags = ord(f.read(1))
f.seek(2, 1)
skip_color_table(flags)
while True:
block = f.read(1)
if block == b';': break # I also added a b'' here
if block == b'!': f.seek(1, 1) # I also added a b'' here
elif block == b',': # I also added a b'' here
frames += 1
f.seek(8, 1)
skip_color_table(ord(f.read(1)))
f.seek(1, 1)
else: raise GIFError('unknown block type')
while True:
l = ord(f.read(1))
if not l: break
f.seek(l, 1)
return frames
n_frames = get_gif_num_frames(gif_bytes)
assert n_frames == 36
Pillow n_frames:
%%timeit
from PIL import Image
test_file.seek(0) # reset file
with Image.open(test_file) as file:
# To iterate through the entire gif
n_frames = file.n_frames
assert n_frames == 36
ImageIO's improps (via pillow):
%%timeit
import imageio.v3 as iio
props = iio.improps(gif_bytes, index=None)
n_frames = props.shape[0]
assert n_frames == 36
There is also a new PyAV based plugin I'm writing which is faster than pillow, but still slower than the pure-python approach. Syntax-wise it's pretty similar to the ImageIO (via pillow) approach:
%%timeit
# IIO improps (via PyAV)
# 507 µs ± 17.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# Note: at the time of this writing this approach is still a PR.
# it needs documentation and test coverage before being merged.
import imageio.v3 as iio
props = iio.improps(gif_bytes, index=None, plugin="pyav")
n_frames = props.shape[0]
assert n_frames == 36
Which method are you using to load/manipulate the frame? Are you using PIL? If not, I suggest checking it out: Python Imaging Library and specifically the PIL gif page.
Now, assuming you are using PIL to read in the gif, it's a pretty simple matter to determine which frame you are looking at. seek will go to a specific frame and tell will return which frame you are looking at.
from PIL import Image
im = Image.open("animation.gif")
# To iterate through the entire gif
try:
while 1:
im.seek(im.tell()+1)
# do something to im
except EOFError:
pass # end of sequence
Otherwise, I believe you can only find the number of frames in the gif by seeking until an exception (EOFError) is raised.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With