File size: 2,914 Bytes
f382c2d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
898530d
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
import cv2
import zipfile
import os
import tempfile
import gradio as gr


def extract_frames_to_zip(video_path, num_frames=10):
    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        raise RuntimeError(f"Could not open video: {video_path}")

    frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    if frame_count <= 0:
        raise RuntimeError("Could not determine frame count.")

    num_frames = min(num_frames, frame_count)

    if num_frames == 1:
        frame_indices = [0]
    else:
        frame_indices = [
            round(i * (frame_count - 1) / (num_frames - 1))
            for i in range(num_frames)
        ]

    # Create a temp directory for this run
    tmp_dir = tempfile.mkdtemp()
    zip_path = os.path.join(tmp_dir, "frames.zip")

    with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
        for idx, frame_idx in enumerate(frame_indices):
            cap.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)
            ret, frame = cap.read()
            if not ret:
                print(f"Warning: Could not read frame at index {frame_idx}")
                continue

            success, buffer = cv2.imencode(".jpg", frame)
            if not success:
                print(f"Warning: Could not encode frame at index {frame_idx}")
                continue

            filename_in_zip = f"frame_{idx:02d}.jpg"
            zf.writestr(filename_in_zip, buffer.tobytes())

    cap.release()
    return zip_path


def gradio_fn(video_file, num_frames):
    """
    video_file: path to uploaded video (from Gradio Video input)
    num_frames: integer from slider
    """
    if video_file is None:
        raise gr.Error("Please upload a video first.")

    # Gradio passes a dict for Video { 'name': ..., 'data': ... } in some versions,
    # but in newer versions it passes a filepath string. Handle both.
    if isinstance(video_file, dict):
        video_path = video_file.get("name") or video_file.get("data")
    else:
        video_path = video_file

    if not video_path or not os.path.exists(video_path):
        raise gr.Error("Uploaded video file not found on the server.")

    zip_path = extract_frames_to_zip(video_path, int(num_frames))
    return zip_path


with gr.Blocks() as demo:
    gr.Markdown("# Video Frame Extractor\nUpload a video and get N evenly spaced frames as a ZIP.")

    with gr.Row():
        video_input = gr.Video(label="Upload video", sources=["upload"])
        num_frames_input = gr.Slider(
            minimum=2,
            maximum=30,
            value=10,
            step=1,
            label="Number of frames to extract",
        )

    zip_output = gr.File(label="Download frames ZIP")

    run_btn = gr.Button("Extract frames")

    run_btn.click(
        fn=gradio_fn,
        inputs=[video_input, num_frames_input],
        outputs=[zip_output],
    )

if __name__ == "__main__":
    demo.launch()