Spaces:
Runtime error
Runtime error
File size: 10,857 Bytes
aa85975 f647b33 6474363 aa85975 6474363 f647b33 6474363 f647b33 6474363 f647b33 6474363 d8c4344 6474363 f647b33 d8c4344 f647b33 d8c4344 f647b33 d8c4344 f647b33 d8c4344 6474363 d8c4344 f647b33 d8c4344 f647b33 d8c4344 6474363 d8c4344 6474363 d8c4344 6474363 d8c4344 6474363 d8c4344 6474363 d8c4344 6474363 d8c4344 6474363 d8c4344 |
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 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
import gradio as gr
from PIL import Image, ImageDraw, ImageFont, ImageSequence
import numpy as np
import cv2
import os
import tempfile
global stored_frames
def load_and_store_frames(image_file):
global stored_frames
# Make sure file exists
if image_file is None:
return "File not found", ""
print("Loading frames for {}".format(image_file.name))
if image_file.name.endswith('.mp4'):
frames = extract_frames_from_video(image_file.name)
video_path = image_file.name
else: # it's a gif
img = Image.open(image_file.name)
frames = []
for i in range(0, img.n_frames):
img.seek(i)
frames.append(img.copy())
# Convert GIF to MP4 for preview
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
video_path = tmp_file.name
convert_gif_to_video(image_file.name, tmp_file.name, 1 / (img.info.get('duration', 100) / 1000.0))
stored_frames = frames # Store the frames for later use
return "Frames loaded successfully", video_path
def generate_grid(grid_x, grid_y, font_size, font_color, position, border_size, border_color):
global stored_frames
print(f"Processing grid with {grid_x} x {grid_y} grid size, font size {font_size}, font color {font_color}, position {position}, border size {border_size}, border color {border_color}")
if stored_frames is None:
load_and_store_frames()
grid_img, output_info = create_grid(stored_frames, grid_x, grid_y, font_size, font_color, position, border_size, border_color)
details = f"Total Frames: {len(stored_frames)}\n\n{output_info}"
return grid_img, details
def create_grid(frames, grid_x, grid_y, font_size, font_color, position, border_size, border_color):
total_frames = len(frames)
selected_frames_count = grid_x * grid_y
# Select evenly spaced frames
selected_frames_indices = np.linspace(0, total_frames - 1, selected_frames_count).astype(int)
selected_frames = [frames[i] for i in selected_frames_indices]
# Modify frames by adding border and number
modified_frames = []
try:
font = ImageFont.truetype("Lato-Regular.ttf", font_size)
except IOError:
print("Font not found, using default font.")
font = ImageFont.load_default()
positions = {
"Top Left": (20, 20),
"Top Right": (frames[0].width - 20 - font_size, 20),
"Bottom Left": (20, frames[0].height - 20 - font_size),
"Bottom Right": (frames[0].width - 20 - font_size, frames[0].height - 20 - font_size)
}
for i, frame in enumerate(selected_frames):
# Add border
border_width = border_size
frame_with_border = Image.new('RGB', (frame.width + 2*border_width, frame.height + 2*border_width), border_color.lower())
frame_with_border.paste(frame, (border_width, border_width))
# Add number
draw = ImageDraw.Draw(frame_with_border)
text = str(i + 1)
text_position = (border_width + positions[position][0], border_width + positions[position][1])
draw.text(text_position, text, font=font, fill=font_color)
modified_frames.append(frame_with_border)
# Combine modified frames into a grid
grid_width = modified_frames[0].width * grid_x
grid_height = modified_frames[0].height * grid_y
grid_img = Image.new('RGB', (grid_width, grid_height), border_color.lower())
for i, frame in enumerate(modified_frames):
x_offset = (i % grid_x) * frame.width
y_offset = (i // grid_x) * frame.height
grid_img.paste(frame, (x_offset, y_offset))
output_info = f"Grid size: {grid_x} x {grid_y}\n\nSelected Frames: {selected_frames_count} / {total_frames} ({selected_frames_count / total_frames * 100:.2f}%)"
return grid_img, output_info
def extract_frames_from_video(video_file):
"""Extract frames from an MP4 video."""
frames = []
cap = cv2.VideoCapture(video_file)
while True:
ret, frame = cap.read()
if not ret:
break
# Convert BGR format (used by OpenCV) to RGB
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frames.append(Image.fromarray(frame_rgb))
cap.release()
return frames
def convert_gif_to_video(gif_path, output_video_path, frame_rate):
# Load the gif
gif = Image.open(gif_path)
# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output_video_path, fourcc, frame_rate, (gif.width, gif.height))
# Iterate over the frames of the gif
for frame_index in range(gif.n_frames):
gif.seek(frame_index)
# Convert the PIL Image to an array
frame_arr = np.array(gif.convert("RGB"))
# Convert RGB to BGR format
frame_bgr = cv2.cvtColor(frame_arr, cv2.COLOR_RGB2BGR)
# Write the frame to the video
out.write(frame_bgr)
out.release()
def gif_or_video_info(image_file, grid_x, grid_y, font_size, font_color, position, border_size, border_color):
image_file.file.seek(0)
video_path = ""
if image_file.name.endswith('.mp4'):
video_path = image_file.name
cap = cv2.VideoCapture(image_file.name)
frame_rate = cap.get(cv2.CAP_PROP_FPS) # Get the actual frame rate of the video
frames = extract_frames_from_video(image_file.name)
total_frames = len(frames)
cap.release()
else: # it's a gif
img = Image.open(image_file.name)
frames = []
for i in range(0, img.n_frames):
img.seek(i)
frames.append(img.copy())
total_frames = img.n_frames
frame_rate = 1 / (img.info.get('duration', 100) / 1000.0) # Convert to seconds
# Convert GIF to MP4 and save it to a temp path
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
tmp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
video_path = tmp_file.name
convert_gif_to_video(image_file.name, tmp_file.name, frame_rate)
grid_img, output_info = create_grid(frames, grid_x, grid_y, font_size, font_color, position, border_size, border_color)
details = f"**Total Frames:** {total_frames}\n\n**Frame Rate:** {frame_rate} frames/sec\n\n{output_info}"
return grid_img, details, video_path
def gif_info(image_file, grid_x, grid_y, font_size, font_color, position, border_size, border_color):
return gif_or_video_info(image_file, grid_x, grid_y, font_size, font_color, position, border_size, border_color)
def mirror(x):
return x
with gr.Blocks() as app:
gr.Markdown('## vid2grid Generator')
gr.Markdown('Upload a GIF or MP4 to generate a grid from its frames. Use the sliders to adjust the grid size and text settings.\n\nThis is particularly useful for use with multi modal models such as GPT-4V to retrieve descriptions of short videos or gifs, [more example here.](https://twitter.com/zachnagengast/status/1712896232170180651)\n\n **Note:** The grid will be generated only after clicking the "Generate Grid" button.')
with gr.Row():
with gr.Column():
control_image = gr.File(label="Upload a short MP4 or GIF", type="file", elem_id="file_upload", file_types=[".gif", ".mp4"])
video_preview = gr.Video(interactive=False, label="Preview", format="mp4")
gif_details = gr.Markdown("No file found.")
# gr.Examples(
# examples=[os.path.join(os.path.dirname(__file__), "demo.mp4")],
# inputs=[control_image],
# outputs=[gif_details, video_preview],
# fn=load_and_store_frames,
# cache_examples=True,
# )
process_button = gr.Button("Generate Grid") # New button to trigger the heavy computation
grid_x_slider = gr.Slider(minimum=1, maximum=10, step=1, value=3, label="Grid X Size")
grid_y_slider = gr.Slider(minimum=1, maximum=10, step=1, value=3, label="Grid Y Size")
font_color_dropdown = gr.Dropdown(choices=["Black", "White", "Red", "Green", "Blue"], value="White", label="Numbering Color")
position_radio = gr.Radio(choices=["Top Left", "Top Right", "Bottom Left", "Bottom Right"], value="Top Left", label="Numbering Position")
font_size_slider = gr.Slider(minimum=10, maximum=100, step=5, value=40, label="Font Size")
border_color_dropdown = gr.Dropdown(choices=["Black", "White", "Red", "Green", "Blue"], value="White", label="Border Color")
border_size_slider = gr.Slider(minimum=0, maximum=100, step=5, value=10, label="Border Size")
with gr.Column():
result_image = gr.Image(label="Generated Grid")
# Use .change() method to listen for changes in any of the controls
control_image.upload(load_and_store_frames, inputs=[control_image], outputs=[gif_details, video_preview])
# grid_x_slider.change(generate_grid, inputs=[grid_x_slider, grid_y_slider, font_size_slider, font_color_dropdown, position_radio, border_size_slider, border_color_dropdown], outputs=[result_image, gif_details, video_preview])
# grid_y_slider.change(generate_grid, inputs=[grid_x_slider, grid_y_slider, font_size_slider, font_color_dropdown, position_radio, border_size_slider, border_color_dropdown], outputs=[result_image, gif_details])
# font_size_slider.change(generate_grid, inputs=[grid_x_slider, grid_y_slider, font_size_slider, font_color_dropdown, position_radio, border_size_slider, border_color_dropdown], outputs=[result_image, gif_details])
# font_color_dropdown.change(generate_grid, inputs=[grid_x_slider, grid_y_slider, font_size_slider, font_color_dropdown, position_radio, border_size_slider, border_color_dropdown], outputs=[result_image, gif_details])
# position_radio.change(generate_grid, inputs=[grid_x_slider, grid_y_slider, font_size_slider, font_color_dropdown, position_radio, border_size_slider, border_color_dropdown], outputs=[result_image, gif_details])
# border_size_slider.change(generate_grid, inputs=[grid_x_slider, grid_y_slider, font_size_slider, font_color_dropdown, position_radio, border_size_slider, border_color_dropdown], outputs=[result_image, gif_details])
# border_color_dropdown.change(generate_grid, inputs=[grid_x_slider, grid_y_slider, font_size_slider, font_color_dropdown, position_radio, border_size_slider, border_color_dropdown], outputs=[result_image, gif_details])
process_button.click(generate_grid, inputs=[grid_x_slider, grid_y_slider, font_size_slider, font_color_dropdown, position_radio, border_size_slider, border_color_dropdown], outputs=[result_image, gif_details])
if __name__ == "__main__":
stored_frames = None
app.launch()
|