├── .gitignore ├── README.md └── scripts └── convert.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | __pycache__ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sd-webui-video-frames 2 | 3 | Extract frames from video / Merge frames to video extension, Used for [AUTOMATIC1111's stable diffusion webui](https://github.com/AUTOMATIC1111/stable-diffusion-webui) 4 | -------------------------------------------------------------------------------- /scripts/convert.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import gradio as gr 3 | import os 4 | from tqdm import tqdm 5 | from modules import script_callbacks 6 | 7 | 8 | def add_tab(): 9 | with gr.Blocks(analytics_enabled=False) as ui: 10 | with gr.Row().style(equal_height=False): 11 | with gr.Column(variant='panel'): 12 | gr.HTML(value="

Extract frames from video

") 13 | input_video = gr.Textbox(label="Input video path") 14 | output_folder = gr.Textbox(label="Output frames folder path") 15 | output_fps = gr.Slider(minimum=1, maximum=120, step=1, label="Save every N frame", value=1) 16 | custom_resolution_checkbox = gr.Checkbox(label="Use custom resolution") 17 | custom_width_slider = gr.Slider(minimum=1, maximum=1920, step=1, label="Custom Width", value=512) 18 | custom_height_slider = gr.Slider(minimum=1, maximum=1080, step=1, label="Custom Height", value=768) 19 | extract_frames_btn = gr.Button(label="Extract Frames", variant='primary') 20 | 21 | with gr.Column(variant='panel'): 22 | gr.HTML(value="

Merge frames to video

") 23 | input_folder = gr.Textbox(label="Input frames folder path") 24 | output_video = gr.Textbox(label="Output video path") 25 | output_video_fps = gr.Slider(minimum=1, maximum=60, step=1, label="Video FPS", value=30) 26 | merge_frames_btn = gr.Button(label="Merge Frames", variant='primary') 27 | 28 | extract_frames_btn.click( 29 | fn=extract_frames, 30 | inputs=[ 31 | input_video, 32 | output_folder, 33 | output_fps, 34 | custom_resolution_checkbox, 35 | custom_width_slider, 36 | custom_height_slider 37 | ] 38 | ) 39 | 40 | merge_frames_btn.click( 41 | fn=merge_frames, 42 | inputs=[ 43 | input_folder, 44 | output_video, 45 | output_video_fps 46 | ] 47 | ) 48 | 49 | return [(ui, "Video<->Frame", "vf_converter")] 50 | 51 | 52 | def extract_frames(video_path: str, output_path: str, custom_fps=None,use_custom_resolution=False, custom_width=512, custom_height=768): 53 | """从视频文件中提取帧并输出为png格式 54 | 55 | Args: 56 | video_path (str): 视频文件的路径 57 | output_path (str): 输出帧的路径 58 | custom_fps (int, optional): 自定义输出帧率(默认为None,表示与视频帧率相同) 59 | use_custom_resolution (bool): 是否使用自定义分辨率 60 | custom_width (int): 自定义宽度 61 | custom_height (int): 自定义高度 62 | 63 | Returns: 64 | None 65 | """ 66 | cap = cv2.VideoCapture(video_path) 67 | fps = int(cap.get(cv2.CAP_PROP_FPS)) 68 | custom_fps = int(custom_fps) 69 | frame_count = 0 70 | 71 | print(f"Extracting {video_path} to {output_path}...") 72 | while True: 73 | ret, frame = cap.read() 74 | if not ret: 75 | break 76 | 77 | if use_custom_resolution: 78 | frame = cv2.resize(frame, (custom_width, custom_height)) 79 | 80 | if custom_fps: 81 | if frame_count % custom_fps == 0: 82 | output_name = os.path.join(output_path, '{:06d}.png'.format(frame_count)) 83 | cv2.imwrite(output_name, frame) 84 | else: 85 | output_name = os.path.join(output_path, '{:06d}.png'.format(frame_count)) 86 | cv2.imwrite(output_name, frame) 87 | frame_count += 1 88 | 89 | cap.release() 90 | print("Extract finished.") 91 | 92 | 93 | def merge_frames(frames_path: str, output_path: str, fps=None): 94 | """将指定文件夹内的所有png图片按顺序合并为一个mp4视频文件 95 | 96 | Args: 97 | frames_path (str): 输入帧的路径(所有帧必须为png格式) 98 | output_path (str): 输出视频的路径(需以.mp4为文件扩展名) 99 | fps (int, optional): 输出视频的帧率(默认为None,表示与输入帧相同) 100 | 101 | Returns: 102 | None 103 | """ 104 | 105 | # 获取所有png图片 106 | frames = [f for f in os.listdir(frames_path) if f.endswith('.png')] 107 | img = cv2.imread(os.path.join(frames_path, frames[0])) 108 | height, width, _ = img.shape 109 | fps = fps or int(cv2.CAP_PROP_FPS) 110 | 111 | fourcc = cv2.VideoWriter_fourcc(*'mp4v') 112 | video_writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height)) 113 | 114 | print(f"Merging {len(frames)} frames to video...") 115 | for f in tqdm(frames): 116 | img = cv2.imread(os.path.join(frames_path, f)) 117 | video_writer.write(img) 118 | 119 | video_writer.release() 120 | print("Merge finished") 121 | 122 | 123 | script_callbacks.on_ui_tabs(add_tab) --------------------------------------------------------------------------------