|
|
|
import gradio as gr |
|
import cv2 |
|
import numpy as np |
|
import os |
|
from datetime import datetime |
|
from scenedetect import open_video, SceneManager |
|
from scenedetect.detectors import ContentDetector |
|
from moviepy.editor import VideoFileClip |
|
|
|
def convert_to_tuple(list): |
|
return tuple(list) |
|
|
|
def clear_app(): |
|
return None, 27, None, None, None |
|
|
|
def find_scenes(video_path, threshold): |
|
|
|
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") |
|
output_dir = f"output_{timestamp}" |
|
os.makedirs(output_dir, exist_ok=True) |
|
|
|
filename = os.path.splitext(os.path.basename(video_path))[0] |
|
video = open_video(video_path) |
|
scene_manager = SceneManager() |
|
scene_manager.add_detector(ContentDetector(threshold=threshold)) |
|
|
|
scene_manager.detect_scenes(video, show_progress=True) |
|
scene_list = scene_manager.get_scene_list() |
|
|
|
data_outputs = [] |
|
gradio_components_outputs = [] |
|
|
|
data_outputs.append(scene_list) |
|
gradio_components_outputs.append("json") |
|
|
|
timecodes = [] |
|
if not scene_list: |
|
gr.Warning("No scenes detected in this video") |
|
return None, None, None |
|
|
|
timecodes.append({"title": filename + ".mp4", "fps": scene_list[0][0].get_framerate()}) |
|
|
|
shots = [] |
|
stills = [] |
|
|
|
for i, shot in enumerate(scene_list): |
|
framerate = shot[0].get_framerate() |
|
shot_in = shot[0].get_frames() / framerate |
|
shot_out = shot[1].get_frames() / framerate |
|
|
|
tc_in = shot[0].get_timecode() |
|
tc_out = shot[1].get_timecode() |
|
frame_in = shot[0].get_frames() |
|
frame_out = shot[1].get_frames() |
|
|
|
timecode = {"tc_in": tc_in, "tc_out": tc_out, "frame_in": frame_in, "frame_out": frame_out} |
|
timecodes.append(timecode) |
|
|
|
|
|
target_name = os.path.join(output_dir, f"shot_{i+1}_{filename}.mp4") |
|
|
|
with VideoFileClip(video_path) as clip: |
|
subclip = clip.subclip(shot_in, shot_out) |
|
subclip.write_videofile( |
|
target_name, |
|
codec="libx264", |
|
audio_codec="aac", |
|
threads=4, |
|
preset="fast", |
|
ffmpeg_params=["-crf", "23"] |
|
) |
|
|
|
shots.append(target_name) |
|
data_outputs.append(target_name) |
|
gradio_components_outputs.append("video") |
|
|
|
vid = cv2.VideoCapture(video_path) |
|
frame_id = shot[0].get_frames() |
|
vid.set(cv2.CAP_PROP_POS_FRAMES, frame_id) |
|
ret, frame = vid.read() |
|
|
|
|
|
img = os.path.join(output_dir, f"{frame_id}_screenshot.png") |
|
cv2.imwrite(img, frame) |
|
stills.append((img, f'shot {i+1}')) |
|
vid.release() |
|
|
|
data_outputs.append(shots) |
|
gradio_components_outputs.append("file") |
|
data_outputs.append(stills) |
|
gradio_components_outputs.append("gallery") |
|
|
|
results = convert_to_tuple(data_outputs) |
|
print(f"所有输出文件保存在:{os.path.abspath(output_dir)}") |
|
|
|
return timecodes, shots, stills |
|
|
|
with gr.Blocks() as demo: |
|
with gr.Column(): |
|
gr.Markdown(""" |
|
# Scene Edit Detection |
|
Automatically find all the shots in a video. |
|
Accepts mp4 format. Works only with videos that have cuts in them. |
|
""") |
|
|
|
with gr.Row(): |
|
with gr.Column(): |
|
video_input = gr.Video(sources="upload", format="mp4", label="视频输入", mirror_webcam=False) |
|
threshold = gr.Slider(label="场景切换检测阈值(越低越敏感)", minimum=15.0, maximum=40.0, value=27.0) |
|
with gr.Row(): |
|
clear_button = gr.Button("清除") |
|
run_button = gr.Button("开始处理", variant="primary") |
|
|
|
with gr.Column(): |
|
json_output = gr.JSON(label="场景分析结果") |
|
file_output = gr.File(label="下载分割片段") |
|
gallery_output = gr.Gallery(label="场景缩略图", object_fit="cover", columns=3) |
|
|
|
run_button.click(fn=find_scenes, inputs=[video_input, threshold], outputs=[json_output, file_output, gallery_output]) |
|
clear_button.click(fn=clear_app, inputs=None, outputs=[video_input, threshold, json_output, file_output, gallery_output]) |
|
|
|
|
|
gr.Examples( |
|
examples=[ |
|
["anime_kiss.mp4", 27], |
|
], |
|
inputs=[video_input, threshold], |
|
outputs=[json_output, file_output, gallery_output], |
|
fn=find_scenes, |
|
cache_examples=False, |
|
label="示例视频" |
|
) |
|
|
|
demo.queue().launch(debug=True, share=True) |