FrameVis / app.py
GenAIJake's picture
first commit
d80a719
raw
history blame
7.39 kB
import gradio as gr
import cv2
import numpy as np
import tempfile
import os
from framevis import FrameVis
import json
class InteractiveFrameVis(FrameVis):
"""Extended FrameVis class that tracks frame positions"""
def visualize(self, source, nframes, height=None, width=None, direction="horizontal", trim=False, quiet=True):
"""Extended visualize method that returns both the visualization and frame data"""
video = cv2.VideoCapture(source)
if not video.isOpened():
raise FileNotFoundError("Source Video Not Found")
# Calculate frame positions and timestamps
total_frames = video.get(cv2.CAP_PROP_FRAME_COUNT)
fps = video.get(cv2.CAP_PROP_FPS)
keyframe_interval = total_frames / nframes
# Get the visualization
output_image = super().visualize(source, nframes, height, width, direction, trim, quiet)
# Calculate frame positions and timestamps
frame_data = []
img_height, img_width = output_image.shape[:2]
for i in range(nframes):
frame_pos = int(keyframe_interval * (i + 0.5)) # Same calculation as in visualize
timestamp = frame_pos / fps
if direction == "horizontal":
x_start = (i * img_width) // nframes
x_end = ((i + 1) * img_width) // nframes
frame_info = {
"frame": frame_pos,
"time": timestamp,
"x_start": int(x_start),
"x_end": int(x_end),
"y_start": 0,
"y_end": img_height
}
else: # vertical
y_start = (i * img_height) // nframes
y_end = ((i + 1) * img_height) // nframes
frame_info = {
"frame": frame_pos,
"time": timestamp,
"x_start": 0,
"x_end": img_width,
"y_start": int(y_start),
"y_end": int(y_end)
}
frame_data.append(frame_info)
video.release()
return output_image, frame_data
def extract_frame(video_path, frame_number):
"""Extract a specific frame from the video"""
if not video_path:
return None
cap = cv2.VideoCapture(video_path)
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
ret, frame = cap.read()
cap.release()
if ret:
return cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
return None
def process_video(video_path, nframes, height, width, direction, trim, average, blur_amount):
"""Process video using FrameVis and return the visualization with frame data"""
try:
fv = InteractiveFrameVis()
# Process the video
output_image, frame_data = fv.visualize(
video_path,
nframes=nframes,
height=height if height > 0 else None,
width=width if width > 0 else None,
direction=direction,
trim=trim,
quiet=False
)
# Apply post-processing if requested
if average:
output_image = fv.average_image(output_image, direction)
elif blur_amount > 0:
output_image = fv.motion_blur(output_image, direction, blur_amount)
# Convert from BGR to RGB for Gradio
output_image = cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB)
# Store frame data in a temporary file
temp_dir = tempfile.gettempdir()
data_path = os.path.join(temp_dir, "frame_data.json")
with open(data_path, "w") as f:
json.dump({"video_path": video_path, "frames": frame_data}, f)
return output_image, data_path
except Exception as e:
raise gr.Error(str(e))
def on_mouse_move(evt: gr.EventData, frame_data_path):
"""Handle mouseover on the visualization image"""
if not frame_data_path:
return None
try:
# Load frame data
with open(frame_data_path) as f:
data = json.load(f)
video_path = data["video_path"]
frames = data["frames"]
# Get mouse coordinates
x, y = evt.index[0], evt.index[1] # Extract x, y from index
# Find which frame was hovered
for frame in frames:
if (frame["x_start"] <= x <= frame["x_end"] and
frame["y_start"] <= y <= frame["y_end"]):
# Extract and return the frame
preview = extract_frame(video_path, frame["frame"])
if preview is not None:
return preview, f"Frame {frame['frame']} (Time: {frame['time']:.2f}s)"
except Exception as e:
print(f"Error handling mouseover: {e}")
return None, ""
# Create the Gradio interface
with gr.Blocks(title="FrameVis - Video Frame Visualizer") as demo:
gr.Markdown("""
# 🎬 FrameVis - Video Frame Visualizer
Upload a video to create a beautiful visualization of its frames. The tool will extract frames at regular intervals
and combine them into a single image. **Move your mouse over the visualization to see the original frames!**
""")
with gr.Row():
with gr.Column(scale=1):
# Input components
video_input = gr.Video(label="Upload Video")
with gr.Row():
nframes = gr.Slider(minimum=1, maximum=500, value=100, step=1,
label="Number of Frames")
direction = gr.Radio(["horizontal", "vertical"], value="horizontal",
label="Direction")
with gr.Row():
height = gr.Number(value=0, label="Frame Height (0 for auto)")
width = gr.Number(value=0, label="Frame Width (0 for auto)")
with gr.Row():
trim = gr.Checkbox(label="Auto-trim black bars")
average = gr.Checkbox(label="Average colors")
blur_amount = gr.Slider(minimum=0, maximum=200, value=0, step=1,
label="Motion Blur Amount")
process_btn = gr.Button("Generate Visualization", variant="primary")
with gr.Column(scale=2):
# Output components
frame_data = gr.State() # Hidden component to store frame data
output_image = gr.Image(label="Visualization Result", interactive=True, height=300)
frame_info = gr.Markdown("Hover over the visualization to see frame details")
preview_frame = gr.Image(label="Frame Preview", interactive=False, height=300)
# Handle processing
result = process_btn.click(
fn=process_video,
inputs=[
video_input,
nframes,
height,
width,
direction,
trim,
average,
blur_amount
],
outputs=[output_image, frame_data]
)
# Handle mouseover events
output_image.mouseover(
fn=on_mouse_move,
inputs=[frame_data],
outputs=[preview_frame, frame_info]
)
if __name__ == "__main__":
demo.launch()