devorah-ai-2016 commited on
Commit
4368b0a
ยท
verified ยท
1 Parent(s): 78444d7

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -0
app.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ from PIL import Image, ImageOps
4
+ import gradio as gr
5
+ from moviepy.editor import ImageClip, concatenate_videoclips
6
+
7
+
8
+ OUTPUT_DIR = "outputs"
9
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
10
+
11
+
12
+ def resize_crop_image(image_path, aspect_ratio):
13
+ img = Image.open(image_path).convert("RGB")
14
+ img = ImageOps.exif_transpose(img)
15
+
16
+ if aspect_ratio == "9:16 ์œ ํŠœ๋ธŒ ์‡ผ์ธ ":
17
+ target_w, target_h = 1080, 1920
18
+ else:
19
+ target_w, target_h = 1920, 1080
20
+
21
+ img_ratio = img.width / img.height
22
+ target_ratio = target_w / target_h
23
+
24
+ if img_ratio > target_ratio:
25
+ new_h = target_h
26
+ new_w = int(new_h * img_ratio)
27
+ else:
28
+ new_w = target_w
29
+ new_h = int(new_w / img_ratio)
30
+
31
+ img = img.resize((new_w, new_h), Image.LANCZOS)
32
+
33
+ left = (new_w - target_w) // 2
34
+ top = (new_h - target_h) // 2
35
+ img = img.crop((left, top, left + target_w, top + target_h))
36
+
37
+ temp_path = os.path.join(OUTPUT_DIR, f"{uuid.uuid4()}.jpg")
38
+ img.save(temp_path, quality=95)
39
+ return temp_path
40
+
41
+
42
+ def make_video(images, aspect_ratio, seconds_per_photo, zoom_effect):
43
+ if not images:
44
+ raise gr.Error("์ด๋ฏธ์ง€๋ฅผ 1์žฅ ์ด์ƒ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.")
45
+
46
+ clips = []
47
+
48
+ for img_file in images:
49
+ img_path = img_file.name
50
+ resized_path = resize_crop_image(img_path, aspect_ratio)
51
+
52
+ clip = ImageClip(resized_path).set_duration(seconds_per_photo)
53
+
54
+ if zoom_effect:
55
+ clip = clip.resize(lambda t: 1 + 0.04 * t / seconds_per_photo)
56
+
57
+ if aspect_ratio == "9:16 ์œ ํŠœ๋ธŒ ์‡ผ์ธ ":
58
+ clip = clip.crop(
59
+ x_center=540,
60
+ y_center=960,
61
+ width=1080,
62
+ height=1920
63
+ )
64
+ else:
65
+ clip = clip.crop(
66
+ x_center=960,
67
+ y_center=540,
68
+ width=1920,
69
+ height=1080
70
+ )
71
+
72
+ clips.append(clip)
73
+
74
+ final = concatenate_videoclips(clips, method="compose")
75
+
76
+ output_path = os.path.join(OUTPUT_DIR, f"beecare_shorts_{uuid.uuid4()}.mp4")
77
+
78
+ final.write_videofile(
79
+ output_path,
80
+ fps=30,
81
+ codec="libx264",
82
+ audio=False,
83
+ preset="medium",
84
+ threads=2
85
+ )
86
+
87
+ return output_path
88
+
89
+
90
+ with gr.Blocks(title="์‚ฌ์ง„ โ†’ ์œ ํŠœ๋ธŒ ์˜์ƒ ์ƒ์„ฑ๊ธฐ") as demo:
91
+ gr.Markdown(
92
+ """
93
+ # ์‚ฌ์ง„ โ†’ ์œ ํŠœ๋ธŒ ์‡ผ์ธ  ์˜์ƒ ์ƒ์„ฑ๊ธฐ
94
+
95
+ ์‚ฌ์ง„์„ ์—…๋กœ๋“œํ•˜๋ฉด ์ž๋™์œผ๋กœ ์˜์ƒ์œผ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
96
+ ๊ธฐ๋ณธ๊ฐ’์€ **์‚ฌ์ง„ 1์žฅ๋‹น 5์ดˆ**, **9:16 ์œ ํŠœ๋ธŒ ์‡ผ์ธ  ๋น„์œจ**์ž…๋‹ˆ๋‹ค.
97
+ """
98
+ )
99
+
100
+ with gr.Row():
101
+ images = gr.File(
102
+ label="์ด๋ฏธ์ง€/์‚ฌ์ง„ ์—…๋กœ๋“œ",
103
+ file_count="multiple",
104
+ file_types=["image"]
105
+ )
106
+
107
+ with gr.Row():
108
+ aspect_ratio = gr.Radio(
109
+ choices=["9:16 ์œ ํŠœ๋ธŒ ์‡ผ์ธ ", "16:9 ์ผ๋ฐ˜ ์˜์ƒ"],
110
+ value="9:16 ์œ ํŠœ๋ธŒ ์‡ผ์ธ ",
111
+ label="์˜์ƒ ๋น„์œจ ์„ ํƒ"
112
+ )
113
+
114
+ seconds_per_photo = gr.Slider(
115
+ minimum=3,
116
+ maximum=8,
117
+ value=5,
118
+ step=1,
119
+ label="์‚ฌ์ง„ 1์žฅ๋‹น ์žฌ์ƒ ์‹œ๊ฐ„"
120
+ )
121
+
122
+ zoom_effect = gr.Checkbox(
123
+ value=True,
124
+ label="์ฒœ์ฒœํžˆ ์คŒ์ธ ํšจ๊ณผ ์ ์šฉ"
125
+ )
126
+
127
+ create_btn = gr.Button("์˜์ƒ ์ƒ์„ฑํ•˜๊ธฐ", variant="primary")
128
+
129
+ output_video = gr.Video(label="์ƒ์„ฑ๋œ ์˜์ƒ")
130
+ download_file = gr.File(label="MP4 ๋‹ค์šด๋กœ๋“œ")
131
+
132
+ create_btn.click(
133
+ fn=make_video,
134
+ inputs=[images, aspect_ratio, seconds_per_photo, zoom_effect],
135
+ outputs=[output_video, download_file]
136
+ )
137
+
138
+
139
+ if __name__ == "__main__":
140
+ demo.launch()