FrameVis / app.py
GenAIJake's picture
fix
0a7a9be
raw
history blame
7.38 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. **Click anywhere on 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", tool="select", height=300)
frame_info = gr.Markdown("Click on 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 selection events
output_image.select(
fn=on_mouse_move,
inputs=[frame_data],
outputs=[preview_frame, frame_info]
)
if __name__ == "__main__":
demo.launch()