In 2022 you also have the option to use ImageIO to do this, which IMHO is much more hasslefree and readable.
import imageio.v3 as iio
for idx, frame in enumerate(iio.imiter("imageio:cockatoo.mp4")):
iio.imwrite(f"extracted_images/frame{idx:03d}.jpg", frame)
Sidenote 1: "imageio:cockatoo.mp4"
is a standard image provided by ImageIO for testing and demonstration purposes. You can simply replace it with "path/to/your/video.mp4"
.
Sidenote 2: You will have to install one of ImageIO's optional dependencies to read video data, which can be done via pip install imageio-ffmpeg
or pip install av
.
You can time this against OpenCV and you will find that, there isn't that much to gain from OpenCV on this front either:
Read-Only Timings
=================
OpenCV: 0.453
imageio_ffmpeg: 0.765
imageio_pyav: 0.272
Read + Write Timings
====================
OpenCV: 3.237
imageio_ffmpeg: 1.597
imageio_pyav: 1.506
By default, OpenCV and ImageIO+av are about equally fast when reading. Both direct bind into the FFmpeg libraries under the hood so this is rather unsurprising. However, ImageIO allows you to tweak FFmpeg's default threadding model (thread_type="FRAME"
) which is much faster when bulk reading.
More importantly, ImageIO is much faster at writing JPEG compared to OpenCV. This is because pillow is faster than OpenCV here which ImageIO capitalizes on. Writing images dominates runtime for this scenario, so you end up with an overall 2x improvement when using ImageIO instead of OpenCV.
Here is the code for reference:
import imageio.v3 as iio
import cv2
import timeit
from pathlib import Path
# create a common local file for benchmarking
video_file = "shared_video.mp4"
if not Path(video_file).exists():
frames = iio.imread("imageio:cockatoo.mp4")
meta = iio.immeta("imageio:cockatoo.mp4", exclude_applied=False)
iio.imwrite(video_file, frames, fps=meta["fps"])
repeats = 10
def read_cv2():
vidcap = cv2.VideoCapture(video_file)
success, image = vidcap.read()
idx = 0
while success:
cv2.imwrite(f"extracted_images/frame{idx:03d}.jpg", image)
success, image = vidcap.read()
idx += 1
def read_imageio_ffmpeg():
for idx, frame in enumerate(iio.imiter(video_file, plugin="FFMPEG")):
iio.imwrite(f"extracted_images/frame{idx:03d}.jpg", frame)
def read_imageio_pyav():
for idx, frame in enumerate(
iio.imiter(video_file, plugin="pyav", format="rgb24", thread_type="FRAME")
):
iio.imwrite(f"extracted_images/frame{idx:03d}.jpg", frame)
time_cv2 = (
timeit.timeit("read_cv2()", setup="from __main__ import read_cv2", number=repeats)
/ repeats
)
time_imageio_ffmpeg = (
timeit.timeit(
"read_imageio_ffmpeg()",
setup="from __main__ import read_imageio_ffmpeg",
number=repeats,
)
/ repeats
)
time_imageio_pyav = (
timeit.timeit(
"read_imageio_pyav()",
setup="from __main__ import read_imageio_pyav",
number=repeats,
)
/ repeats
)
print(
f"""
Timings
=======
OpenCV: {time_cv2:<3.3f}
imageio_ffmpeg: {time_imageio_ffmpeg:<3.3f}
imageio_pyav: {time_imageio_pyav:<3.3f}
"""
)