Spaces:
Sleeping
Sleeping
| import os | |
| import glob | |
| import gradio as gr | |
| from moviepy.editor import VideoFileClip, concatenate_videoclips | |
| import subprocess | |
| import tempfile | |
| import shutil | |
| def get_latest_transition_video(): | |
| """获取最新生成的过渡视频文件""" | |
| transition_files = glob.glob('*.mp4') | |
| if not transition_files: | |
| raise FileNotFoundError("未找到过渡视频文件") | |
| latest_file = max(transition_files, key=os.path.getctime) | |
| return latest_file | |
| def concatenate_videos(video1_path, video2_path, transition_path, output_path, num_frames, fps): | |
| """拼接三个视频部分""" | |
| clip1 = VideoFileClip(video1_path) | |
| clip2 = VideoFileClip(video2_path) | |
| transition = VideoFileClip(transition_path) | |
| transition_duration = num_frames / fps | |
| duration1 = clip1.duration - transition_duration | |
| part1 = clip1.subclip(0, max(0, duration1)) | |
| start_time2 = transition_duration | |
| part2 = clip2.subclip(min(start_time2, clip2.duration)) | |
| final_clip = concatenate_videoclips([part1, transition, part2]) | |
| final_clip.write_videofile(output_path, codec='libx264', audio_codec='aac') | |
| clip1.close() | |
| clip2.close() | |
| transition.close() | |
| final_clip.close() | |
| def get_video_info(video_path): | |
| """获取视频信息""" | |
| clip = VideoFileClip(video_path) | |
| fps = clip.fps | |
| duration = clip.duration | |
| total_frames = int(duration * fps) | |
| clip.close() | |
| return fps, duration, total_frames | |
| def process_videos(video1, video2, animation, num_frames): | |
| # 创建临时目录 | |
| temp_dir = tempfile.mkdtemp() | |
| try: | |
| # 保存上传的视频到临时目录 | |
| video1_path = os.path.join(temp_dir, "video1.mp4") | |
| video2_path = os.path.join(temp_dir, "video2.mp4") | |
| shutil.copyfile(video1, video1_path) | |
| shutil.copyfile(video2, video2_path) | |
| # 获取视频信息 | |
| fps1, duration1, frames1 = get_video_info(video1_path) | |
| fps2, duration2, frames2 = get_video_info(video2_path) | |
| # 使用两个视频中较小的FPS | |
| fps = min(fps1, fps2) | |
| # 计算最大可用帧数 | |
| max_possible_frames = min( | |
| int(duration1 * fps), | |
| int(duration2 * fps) | |
| ) | |
| num_frames = min(num_frames, max_possible_frames) | |
| # 1. 生成过渡部分 | |
| transition_cmd = [ | |
| "python", "vid_transition.py", | |
| "-i", video1_path, video2_path, | |
| "--animation", animation, | |
| "--num_frames", str(num_frames), | |
| "--max_brightness", "1.5", | |
| "-m", "y" | |
| ] | |
| subprocess.run(transition_cmd, check=True) | |
| # 2. 获取过渡视频 | |
| transition_path = get_latest_transition_video() | |
| # 3. 拼接视频 | |
| #output_path = os.path.join(temp_dir, "output.mp4") | |
| output_path = "output.mp4" | |
| concatenate_videos(video1_path, video2_path, transition_path, output_path, num_frames, fps) | |
| return output_path | |
| except Exception as e: | |
| raise gr.Error(f"处理视频时出错: {str(e)}") | |
| finally: | |
| # 清理临时目录 | |
| shutil.rmtree(temp_dir, ignore_errors=True) | |
| def validate_inputs(video1, video2, num_frames): | |
| """验证输入参数""" | |
| if not video1 or not video2: | |
| raise gr.Error("请上传两个视频文件") | |
| try: | |
| fps1, duration1, frames1 = get_video_info(video1) | |
| fps2, duration2, frames2 = get_video_info(video2) | |
| except: | |
| raise gr.Error("上传的视频文件无效") | |
| fps = min(fps1, fps2) | |
| max_possible_frames = min(frames1, frames2) | |
| if num_frames > max_possible_frames: | |
| raise gr.Error(f"视频太短,最大可用过渡帧数为: {max_possible_frames}") | |
| return fps, max_possible_frames | |
| def process_and_validate(video1, video2, animation, num_frames): | |
| try: | |
| fps, max_frames = validate_inputs(video1, video2, num_frames) | |
| if num_frames > max_frames: | |
| num_frames = max_frames | |
| gr.Info(f"自动调整过渡帧数为: {num_frames}") | |
| output_path = process_videos(video1, video2, animation, num_frames) | |
| return output_path | |
| except Exception as e: | |
| raise gr.Error(str(e)) | |
| # 创建Gradio界面 | |
| with gr.Blocks(title="视频过渡与拼接工具") as demo: | |
| gr.Markdown(""" | |
| # 视频过渡与拼接工具 | |
| 上传两个视频,选择过渡效果,生成平滑过渡的合并视频。 | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| video1 = gr.Video(label="第一个视频") | |
| video2 = gr.Video(label="第二个视频") | |
| animation = gr.Dropdown( | |
| label="过渡动画效果", | |
| choices=[ | |
| 'translation', 'translation_inv', | |
| 'rotation', 'rotation_inv', | |
| 'zoom_in', 'zoom_out', | |
| 'long_translation', 'long_translation_inv' | |
| ], | |
| value='translation' | |
| ) | |
| num_frames = gr.Slider( | |
| label="过渡帧数", | |
| minimum=10, | |
| maximum=100, | |
| step=5, | |
| value=30, | |
| info="会根据视频长度自动调整" | |
| ) | |
| submit_btn = gr.Button("生成过渡视频", variant="primary") | |
| with gr.Column(): | |
| output_video = gr.Video(label="合并后的视频") | |
| info_box = gr.Textbox(label="处理信息", visible=False) | |
| submit_btn.click( | |
| fn=process_and_validate, | |
| inputs=[video1, video2, animation, num_frames], | |
| outputs=output_video | |
| ) | |
| # 示例部分 - 使用0000.mp4和0001.mp4展示所有动画效果 | |
| examples = [] | |
| animations = [ | |
| 'translation', 'translation_inv', | |
| 'rotation', 'rotation_inv', | |
| 'zoom_in', 'zoom_out', | |
| 'long_translation', 'long_translation_inv' | |
| ] | |
| for anim in animations: | |
| examples.append([ | |
| "examples/abc.mp4", | |
| "examples/bcd.mp4", | |
| anim, | |
| 30 # 使用30帧作为示例 | |
| ]) | |
| gr.Examples( | |
| examples=examples, | |
| inputs=[video1, video2, animation, num_frames], | |
| outputs=output_video, | |
| fn=process_and_validate, | |
| cache_examples=False, | |
| label="不同过渡效果" | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(share = True) |