-
Notifications
You must be signed in to change notification settings - Fork 0
/
gif_utils.py
166 lines (135 loc) · 5.31 KB
/
gif_utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
from IPython.display import display, Image as IPImage
import cv2
import os
from pathlib import Path
from icecream import ic
import numpy as np
import torch
import moviepy.editor as mpy
from PIL import Image
def save_frames_to_gif(gif_path, frames, frame_duration=40):
"""
Saves a list of frames as a gif to the given path.
Args:
gif_path (str): The path to save the gif to.
frames (list): A list of PIL Image objects.
frame_duration (int, optional): The duration of each frame in ms. Defaults to 40.
"""
frames[0].save(gif_path,
save_all=True,
append_images=frames[1:],
duration=frame_duration,
loop=0) # duration in ms per frame
def save_frames_to_video(video_path, frames, fps=30):
"""
Saves a list of frames as a video to the given path.
Args:
video_path (str): The path to save the video to.
frames (list): A list of PIL Image objects or a 4D tensor of frames with shape (num_frames, height, width, channels).
frame_duration (int, optional): The duration of each frame in ms. Defaults to 30.
"""
# if frames are PIL images
if isinstance(frames[0], Image.Image):
frames_array = []
for frame in frames:
frames_array.append(np.array(frame))
else:
frames_tensor = np.stack(frames, axis=0)[..., ::-1]
frames_array = [frames_tensor[k] for k in range(frames_tensor.shape[0])]
clip = mpy.ImageSequenceClip(frames_array, fps=fps)
clip.write_videofile(video_path, audio=False)
def show_gif(gif_path):
"""
Displays a GIF image from a specified file path.
Args:
gif_path (str): The path to the GIF file to be displayed.
"""
display(IPImage(filename=gif_path))
def extract_video_frames(video_path, save_to_png=False, output_dir=None):
"""
Extract frames from a video and save them as PNGs to a specified directory.
Args:
video_path (str): The path to the video file.
save_to_png (bool, optional): If True, saves each frame as a PNG to the specified output directory. Defaults to False.
output_dir (str, optional): The directory to save the frames to. Defaults to a directory named after the video file without extension, e.g. "video_frames".
Returns:
list: A list of numpy arrays representing the frames of the video. If save_to_png is True, the list will be empty.
"""
# Determine the output directory
if save_to_png and output_dir is None:
# Get the video filename without extension
video_filename = Path(video_path).stem
output_dir = os.path.join(os.path.dirname(video_path), f"{video_filename}_frames")
# ic(output_dir)
# Ensure output directory exists
os.makedirs(output_dir, exist_ok=True)
# Open video and extract frames
cap = cv2.VideoCapture(video_path)
frames = []
frame_count = 0
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# Save frame as PNG
if save_to_png:
filename = f"frame_{frame_count:05d}.png"
filepath = os.path.join(output_dir, filename)
try:
cv2.imwrite(filepath, frame)
frame_count += 1
except Exception as e:
print(f"Error saving frame {frame_count}: {e}")
frames.append(frame)
cap.release()
return frames
def save_binary_mask_as_png(mask, output_path, mask_name, print_flag=True):
"""
Saves a binary mask as a PNG image.
Args:
mask (torch.Tensor or numpy.ndarray): Binary mask (boolean or uint8).
output_path (str): Path to save the PNG file.
"""
# Check if mask is a PyTorch tensor
if isinstance(mask, torch.Tensor):
# Convert tensor to CPU and then to numpy array
mask = mask.to('cpu').numpy()
# Check if mask is a PyTorch tensor
if isinstance(mask, torch.Tensor):
# Convert tensor to CPU and then to numpy array
mask = mask.to('cpu').numpy()
# Convert boolean mask to uint8 if necessary
if mask.dtype == bool:
mask = mask.astype(np.uint8) * 255
# Scale values to 0-255 range
mask_scaled = cv2.convertScaleAbs(mask)
# Save as PNG
if '.png' not in mask_name:
mask_name = mask_name+'.png'
output_file = os.path.join(output_path, mask_name)
success = cv2.imwrite(output_file, mask_scaled)
if print_flag:
if success:
print(f"Binary mask saved successfully as {output_file}")
else:
print(f"Failed to save binary mask as {output_file}")
def save_frame_as_png(frame, output_path, frame_name, print_flag=True):
"""
Saves a frame as a PNG image.
Args:
frame (numpy.ndarray): The frame to be saved.
output_path (str): The path to save the PNG file.
frame_name (str): The name of the frame (without extension).
Returns:
None
"""
# Save as PNG
if '.png' not in frame_name:
frame_name = frame_name+'.png'
output_file = os.path.join(output_path, frame_name)
success = cv2.imwrite(output_file, frame)
if print_flag:
if success:
print(f"Frame saved successfully as {output_file}")
else:
print(f"Failed to save frame as {output_file}")