vid2grid / app.py
ZachNagengast's picture
Update logic and add font
d8c4344
raw
history blame
No virus
6.31 kB
import gradio as gr
from PIL import Image, ImageDraw, ImageFont, ImageSequence
import numpy as np
def create_grid(image_file, grid_x, grid_y, font_size, font_color, position, border_size, border_color):
image_file.file.seek(0) # Seek to the beginning of the file
# Open the image from bytes
img = Image.open(image_file.name)
total_frames = img.n_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 = [img.seek(i) or img.copy() 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": (img.width - 20 - font_size, 20),
"Bottom Left": (20, img.height - 20 - font_size),
"Bottom Right": (img.width - 20 - font_size, img.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 gif_info(image_file, grid_x, grid_y, font_size, font_color, position, border_size, border_color):
image_file.file.seek(0)
img = Image.open(image_file.name)
total_frames = img.n_frames
frame_rate = img.info.get('duration', 100) / 1000.0 # Convert to seconds
# Generate grid using create_grid function
grid_img, output_info = create_grid(image_file, grid_x, grid_y, font_size, font_color, position, border_size, border_color)
gif_details = f"![image](/file={image_file.name})\n\n**Total Frames:** {total_frames}\n\n**Frame Rate:** {frame_rate} sec/frame\n\n{output_info}"
return grid_img, gif_details
with gr.Blocks() as app:
gr.Markdown('## GIF Grid Generator')
gr.Markdown('Upload a GIF to generate a grid from its frames. Use the sliders to adjust the grid size and text settings.')
with gr.Row():
with gr.Column():
control_image = gr.File(label="Upload a GIF", type="file", elem_id="file_upload", file_types=[".gif"])
gif_details = gr.Markdown("Upload a gif")
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.change(gif_info, inputs=[control_image, 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])
grid_x_slider.change(gif_info, inputs=[control_image, 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])
grid_y_slider.change(gif_info, inputs=[control_image, 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(gif_info, inputs=[control_image, 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(gif_info, inputs=[control_image, 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(gif_info, inputs=[control_image, 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(gif_info, inputs=[control_image, 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(gif_info, inputs=[control_image, 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__":
app.launch()