svjack commited on
Commit
d99713a
·
verified ·
1 Parent(s): d3dfae2

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +201 -0
app.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import glob
3
+ import gradio as gr
4
+ from moviepy.editor import VideoFileClip, concatenate_videoclips
5
+ import subprocess
6
+ import tempfile
7
+ import shutil
8
+
9
+ def get_latest_transition_video():
10
+ """获取最新生成的过渡视频文件"""
11
+ transition_files = glob.glob('*.mp4')
12
+ if not transition_files:
13
+ raise FileNotFoundError("未找到过渡视频文件")
14
+ latest_file = max(transition_files, key=os.path.getctime)
15
+ return latest_file
16
+
17
+ def concatenate_videos(video1_path, video2_path, transition_path, output_path, num_frames, fps):
18
+ """拼接三个视频部分"""
19
+ clip1 = VideoFileClip(video1_path)
20
+ clip2 = VideoFileClip(video2_path)
21
+ transition = VideoFileClip(transition_path)
22
+
23
+ transition_duration = num_frames / fps
24
+
25
+ duration1 = clip1.duration - transition_duration
26
+ part1 = clip1.subclip(0, max(0, duration1))
27
+
28
+ start_time2 = transition_duration
29
+ part2 = clip2.subclip(min(start_time2, clip2.duration))
30
+
31
+ final_clip = concatenate_videoclips([part1, transition, part2])
32
+ final_clip.write_videofile(output_path, codec='libx264', audio_codec='aac')
33
+
34
+ clip1.close()
35
+ clip2.close()
36
+ transition.close()
37
+ final_clip.close()
38
+
39
+ def get_video_info(video_path):
40
+ """获取视频信息"""
41
+ clip = VideoFileClip(video_path)
42
+ fps = clip.fps
43
+ duration = clip.duration
44
+ total_frames = int(duration * fps)
45
+ clip.close()
46
+ return fps, duration, total_frames
47
+
48
+ def process_videos(video1, video2, animation, num_frames):
49
+ # 创建临时目录
50
+ temp_dir = tempfile.mkdtemp()
51
+
52
+ try:
53
+ # 保存上传的视频到临时目录
54
+ video1_path = os.path.join(temp_dir, "video1.mp4")
55
+ video2_path = os.path.join(temp_dir, "video2.mp4")
56
+ shutil.copyfile(video1, video1_path)
57
+ shutil.copyfile(video2, video2_path)
58
+
59
+ # 获取视频信息
60
+ fps1, duration1, frames1 = get_video_info(video1_path)
61
+ fps2, duration2, frames2 = get_video_info(video2_path)
62
+
63
+ # 使用两个视频中较小的FPS
64
+ fps = min(fps1, fps2)
65
+
66
+ # 计算最大可用帧数
67
+ max_possible_frames = min(
68
+ int(duration1 * fps),
69
+ int(duration2 * fps)
70
+ )
71
+ num_frames = min(num_frames, max_possible_frames)
72
+
73
+ # 1. 生成过渡部分
74
+ transition_cmd = [
75
+ "python", "vid_transition.py",
76
+ "-i", video1_path, video2_path,
77
+ "--animation", animation,
78
+ "--num_frames", str(num_frames),
79
+ "--max_brightness", "1.5",
80
+ "-m", "y"
81
+ ]
82
+ subprocess.run(transition_cmd, check=True)
83
+
84
+ # 2. 获取过渡视频
85
+ transition_path = get_latest_transition_video()
86
+
87
+ # 3. 拼接视频
88
+ output_path = os.path.join(temp_dir, "output.mp4")
89
+ concatenate_videos(video1_path, video2_path, transition_path, output_path, num_frames, fps)
90
+
91
+ return output_path
92
+
93
+ except Exception as e:
94
+ raise gr.Error(f"处理视频时出错: {str(e)}")
95
+ finally:
96
+ # 清理临时目录
97
+ shutil.rmtree(temp_dir, ignore_errors=True)
98
+
99
+ def validate_inputs(video1, video2, num_frames):
100
+ """验证输入参数"""
101
+ if not video1 or not video2:
102
+ raise gr.Error("请上传两个视频文件")
103
+
104
+ try:
105
+ fps1, duration1, frames1 = get_video_info(video1)
106
+ fps2, duration2, frames2 = get_video_info(video2)
107
+ except:
108
+ raise gr.Error("上传的视频文件无效")
109
+
110
+ fps = min(fps1, fps2)
111
+ max_possible_frames = min(frames1, frames2)
112
+
113
+ if num_frames > max_possible_frames:
114
+ raise gr.Error(f"视频太短,最大可用过渡帧数为: {max_possible_frames}")
115
+
116
+ return fps, max_possible_frames
117
+
118
+ def process_and_validate(video1, video2, animation, num_frames):
119
+ try:
120
+ fps, max_frames = validate_inputs(video1, video2, num_frames)
121
+ if num_frames > max_frames:
122
+ num_frames = max_frames
123
+ gr.Info(f"自动调整过渡帧数为: {num_frames}")
124
+
125
+ output_path = process_videos(video1, video2, animation, num_frames)
126
+ return output_path
127
+ except Exception as e:
128
+ raise gr.Error(str(e))
129
+
130
+ # 创建Gradio界面
131
+ with gr.Blocks(title="视频过渡与拼接工具") as demo:
132
+ gr.Markdown("""
133
+ # 视频过渡与拼接工具
134
+ 上传两个视频,选择过渡效果,生成平滑过渡的合并视频。
135
+ """)
136
+
137
+ with gr.Row():
138
+ with gr.Column():
139
+ video1 = gr.Video(label="第一个视频")
140
+ video2 = gr.Video(label="第二个视频")
141
+
142
+ animation = gr.Dropdown(
143
+ label="过渡动画效果",
144
+ choices=[
145
+ 'translation', 'translation_inv',
146
+ 'rotation', 'rotation_inv',
147
+ 'zoom_in', 'zoom_out',
148
+ 'long_translation', 'long_translation_inv'
149
+ ],
150
+ value='translation'
151
+ )
152
+
153
+ num_frames = gr.Slider(
154
+ label="过渡帧数",
155
+ minimum=10,
156
+ maximum=100,
157
+ step=5,
158
+ value=30,
159
+ info="会根据视频长度自动调整"
160
+ )
161
+
162
+ submit_btn = gr.Button("生成过渡视频", variant="primary")
163
+
164
+ with gr.Column():
165
+ output_video = gr.Video(label="合并后的视频")
166
+ info_box = gr.Textbox(label="处理信息", visible=False)
167
+
168
+ submit_btn.click(
169
+ fn=process_and_validate,
170
+ inputs=[video1, video2, animation, num_frames],
171
+ outputs=output_video
172
+ )
173
+
174
+ # 示例部分 - 使用0000.mp4和0001.mp4展示所有动画效果
175
+ examples = []
176
+ animations = [
177
+ 'translation', 'translation_inv',
178
+ 'rotation', 'rotation_inv',
179
+ 'zoom_in', 'zoom_out',
180
+ 'long_translation', 'long_translation_inv'
181
+ ]
182
+
183
+ for anim in animations:
184
+ examples.append([
185
+ "examples/0000.mp4",
186
+ "examples/0001.mp4",
187
+ anim,
188
+ 30 # 使用30帧作为示例
189
+ ])
190
+
191
+ gr.Examples(
192
+ examples=examples,
193
+ inputs=[video1, video2, animation, num_frames],
194
+ outputs=output_video,
195
+ fn=process_and_validate,
196
+ cache_examples=True,
197
+ label="点击示例快速体验不同过渡效果"
198
+ )
199
+
200
+ if __name__ == "__main__":
201
+ demo.launch(share = True)