Skip to content

Commit 862d327

Browse files
committed
add extracting frames from video tutorial
1 parent 58258fb commit 862d327

File tree

6 files changed

+134
-0
lines changed

6 files changed

+134
-0
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ This is a repository of all the tutorials of [The Python Code](https://www.thepy
170170
- [How to Combine a Static Image with Audio in Python](https://www.thepythoncode.com/article/add-static-image-to-audio-in-python). ([code](python-for-multimedia/add-photo-to-audio))
171171
- [How to Concatenate Video Files in Python](https://www.thepythoncode.com/article/concatenate-video-files-in-python). ([code](python-for-multimedia/combine-video))
172172
- [How to Concatenate Audio Files in Python](https://www.thepythoncode.com/article/concatenate-audio-files-in-python). ([code](python-for-multimedia/combine-audio))
173+
- [How to Extract Frames from Video in Python](https://www.thepythoncode.com/article/extract-frames-from-videos-in-python). ([code](python-for-multimedia/extract-frames-from-video))
173174

174175

175176
For any feedback, please consider pulling requests.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# [How to Extract Frames from Video in Python](https://www.thepythoncode.com/article/extract-frames-from-videos-in-python)
2+
To run this:
3+
- `pip3 install -r requirements.txt`
4+
- Use `extract_frames_opencv.py` for using OpenCV to extract frames, or use `extract_frames_moviepy.py` for using MoviePy library.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from moviepy.editor import VideoFileClip
2+
import numpy as np
3+
import os
4+
from datetime import timedelta
5+
6+
# i.e if video of duration 30 seconds, saves 10 frame per second = 300 frames saved in total
7+
SAVING_FRAMES_PER_SECOND = 10
8+
9+
def format_timedelta(td):
10+
"""Utility function to format timedelta objects in a cool way (e.g 00:00:20.05)
11+
omitting microseconds and retaining milliseconds"""
12+
result = str(td)
13+
try:
14+
result, ms = result.split(".")
15+
except ValueError:
16+
return result + ".00".replace(":", "-")
17+
ms = int(ms)
18+
ms = round(ms / 1e4)
19+
return f"{result}.{ms:02}".replace(":", "-")
20+
21+
22+
def main(video_file):
23+
# load the video clip
24+
video_clip = VideoFileClip(video_file)
25+
# make a folder by the name of the video file
26+
filename, _ = os.path.splitext(video_file)
27+
filename += "-moviepy"
28+
if not os.path.isdir(filename):
29+
os.mkdir(filename)
30+
31+
# if the SAVING_FRAMES_PER_SECOND is above video FPS, then set it to FPS (as maximum)
32+
saving_frames_per_second = min(video_clip.fps, SAVING_FRAMES_PER_SECOND)
33+
# if SAVING_FRAMES_PER_SECOND is set to 0, step is 1/fps, else 1/SAVING_FRAMES_PER_SECOND
34+
step = 1 / video_clip.fps if saving_frames_per_second == 0 else 1 / saving_frames_per_second
35+
# iterate over each possible frame
36+
for current_duration in np.arange(0, video_clip.duration, step):
37+
# format the file name and save it
38+
frame_duration_formatted = format_timedelta(timedelta(seconds=current_duration)).replace(":", "-")
39+
frame_filename = os.path.join(filename, f"frame{frame_duration_formatted}.jpg")
40+
# save the frame with the current duration
41+
video_clip.save_frame(frame_filename, current_duration)
42+
43+
44+
if __name__ == "__main__":
45+
import sys
46+
video_file = sys.argv[1]
47+
main(video_file)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from datetime import timedelta
2+
import cv2
3+
import numpy as np
4+
import os
5+
6+
# i.e if video of duration 30 seconds, saves 10 frame per second = 300 frames saved in total
7+
SAVING_FRAMES_PER_SECOND = 10
8+
9+
def format_timedelta(td):
10+
"""Utility function to format timedelta objects in a cool way (e.g 00:00:20.05)
11+
omitting microseconds and retaining milliseconds"""
12+
result = str(td)
13+
try:
14+
result, ms = result.split(".")
15+
except ValueError:
16+
return result + ".00".replace(":", "-")
17+
ms = int(ms)
18+
ms = round(ms / 1e4)
19+
return f"{result}.{ms:02}".replace(":", "-")
20+
21+
22+
def get_saving_frames_durations(cap, saving_fps):
23+
"""A function that returns the list of durations where to save the frames"""
24+
s = []
25+
# get the clip duration by dividing number of frames by the number of frames per second
26+
clip_duration = cap.get(cv2.CAP_PROP_FRAME_COUNT) / cap.get(cv2.CAP_PROP_FPS)
27+
# use np.arange() to make floating-point steps
28+
for i in np.arange(0, clip_duration, 1 / saving_fps):
29+
s.append(i)
30+
return s
31+
32+
33+
def main(video_file):
34+
filename, _ = os.path.splitext(video_file)
35+
filename += "-opencv"
36+
# make a folder by the name of the video file
37+
if not os.path.isdir(filename):
38+
os.mkdir(filename)
39+
# read the video file
40+
cap = cv2.VideoCapture(video_file)
41+
# get the FPS of the video
42+
fps = cap.get(cv2.CAP_PROP_FPS)
43+
# if the SAVING_FRAMES_PER_SECOND is above video FPS, then set it to FPS (as maximum)
44+
saving_frames_per_second = min(fps, SAVING_FRAMES_PER_SECOND)
45+
# get the list of duration spots to save
46+
saving_frames_durations = get_saving_frames_durations(cap, saving_frames_per_second)
47+
# start the loop
48+
count = 0
49+
while True:
50+
is_read, frame = cap.read()
51+
if not is_read:
52+
# break out of the loop if there are no frames to read
53+
break
54+
# get the duration by dividing the frame count by the FPS
55+
frame_duration = count / fps
56+
try:
57+
# get the earliest duration to save
58+
closest_duration = saving_frames_durations[0]
59+
except IndexError:
60+
# the list is empty, all duration frames were saved
61+
break
62+
if frame_duration >= closest_duration:
63+
# if closest duration is less than or equals the frame duration,
64+
# then save the frame
65+
frame_duration_formatted = format_timedelta(timedelta(seconds=frame_duration))
66+
cv2.imwrite(os.path.join(filename, f"frame{frame_duration_formatted}.jpg"), frame)
67+
# drop the duration spot from the list, since this duration spot is already saved
68+
try:
69+
saving_frames_durations.pop(0)
70+
except IndexError:
71+
pass
72+
# increment the frame count
73+
count += 1
74+
75+
76+
77+
if __name__ == "__main__":
78+
import sys
79+
video_file = sys.argv[1]
80+
main(video_file)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
python-opencv
2+
moviepy
719 KB
Binary file not shown.

0 commit comments

Comments
 (0)