Convert animated webp to mp4 faster
Solution 1:
Video transcoding is usually an "embarrassingly parallel" task, and processImage
is doing things in one big sequence. If processImage
is the slow part, you can use multiprocessing.Pool and assign each worker (which can run on a separate CPU core) its own range of frames to process. PIL objects aren't pickle-able, so you'll have to write temp files, which it seems you're already doing.
I don't know much about PIL, so if there's a better way to use the lib instead, I'm not going to see it. Maybe saving each frame as PNG is slow; worth trying TIF or JPEG. (I'd try it myself, but my Python installation isn't set up on this laptop.)
Solution 2:
A few optimisation pointers from looking at the code:
-
You read the image from the disk in both
analyseImage
andprocessImage
. This isn't a huge thing since it only happens once per image, but reading from disk is still a relatively slow operation so it's best not to do it more than necessary. Instead, you could just open in inprocessImage
and pass the opened image toanalyseImage
. -
Your code for handling partial frames does more work than it needs to:
new_frame = PIL.Image.new('RGBA', im.size) if mode == 'partial': new_frame.paste(last_frame) new_frame.paste(im, (0, 0), im.convert('RGBA'))
(one new image, two pastes, one conversion)
could become
im = im.convert("RGBA") if mode == 'partial': new_frame = last_frame new_frame.paste(im, (0, 0), im) else: new_frame = im
(no new images, one paste, one conversion)
-
I suspect the biggest slowdown is writing every frame to the disk, then reading them again. It would be better to keep them in memory (and ideally, only one in memory at once).
imageio
, the library thatmoviepy
uses internally, provides a way to do this. You just create a video writer usingimageio.get_writer(path)
, and add frames to it sequentially withwriter.append_data(frame_data)
.I tried this myself but I ended up with mixed up colours, so I don't have working code to provide. As a hint though, you can convert a
PIL
image to the raw frame data thatimageio
expects with something likenumpy.array(im)
.