You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While idling around on SO and answering a question I noticed that n_frames is surprisingly slow for GIF:
# setup code based on ImageIO # because it's short and we can avoid measuring disk IOimportimageio.v3asiiofromPILimportImageimportioimg=iio.imread("imageio:newtonscradle.gif", index=None)
img_bytes=iio.imwrite("<bytes>", img, format="GIF")
13.2 ms ± 94.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
This is much more time than it should take to check a series of block headers. So I checked, and - indeed - n_frames parses pixel data, which (in pillow9) involves a conversion to RGB(A) which is what makes this slow. Here is an alternative approach that only reads headers and is - more or less - a drop-in replacement:
%%timeit# based on SO: https://stackoverflow.com/a/7506880/6753182withImage.open(io.BytesIO(img_bytes)) asfile:
defskip_color_table(flags):
ifflags&0x80:
file.fp.seek(3<< ((flags&7) +1), 1)
current_fp=file.fp.tell()
total_frames=file.tell() # start counting from the current frame# seek to beginning of next blockbuffer_start=file.tile[0][2]
file.fp.seek(buffer_start)
whileTrue:
size=file.fp.read(1)
ifsizeandsize[0]:
file.fp.seek(size[0], 1)
else:
break# count the remaining blockswhileTrue:
block=file.fp.read(1)
ifblock==b';':
breakifblock==b'!':
file.fp.seek(1, 1)
elifblock==b',':
total_frames+=1file.fp.seek(8, 1)
skip_color_table(ord(file.fp.read(1)))
file.fp.seek(1, 1)
else: raiseRuntimeError("unknown block type")
# skip to next block instead of loading pixelswhileTrue:
l=ord(file.fp.read(1))
ifnotl: breakfile.fp.seek(l, 1)
file.fp.seek(current_fp)
The timings:
211 µs ± 529 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
So if we only check block headers we can be about 60x faster than we are now. If desired, and there is somebody willing to review, I can submit a PR that adds this some time next week :)
(tests were done on Windows 11 with a AMD Ryzen 7 5800X 8-Core Processor and 2x Kingston KHX3200C16D4/32GX 32GB)
The text was updated successfully, but these errors were encountered:
@radarhere I build that branch and re-ran the timings. It is indeed a major improvement and gets close to the alternative I proposed above:
img.n_frames: 673 µs ± 18.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
manual parsing: 201 µs ± 548 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
So about 3x slower than it could be but much improved compared to main. If you want, I can leave a review in the next couple of days once I have a bit more time on my hands and see if I spot areas where we could improve further. At the same time, this gets close enough, so I am also happy if it gets merged as is.
While idling around on SO and answering a question I noticed that
n_frames
is surprisingly slow for GIF:The desired way to do things:
Timing for this approach:
This is much more time than it should take to check a series of block headers. So I checked, and - indeed -
n_frames
parses pixel data, which (in pillow9) involves a conversion to RGB(A) which is what makes this slow. Here is an alternative approach that only reads headers and is - more or less - a drop-in replacement:The timings:
So if we only check block headers we can be about 60x faster than we are now. If desired, and there is somebody willing to review, I can submit a PR that adds this some time next week :)
(tests were done on Windows 11 with a AMD Ryzen 7 5800X 8-Core Processor and 2x Kingston KHX3200C16D4/32GX 32GB)
The text was updated successfully, but these errors were encountered: