6-1_gif / app.py
Kims12's picture
Create app.py
2d0b48d verified
raw
history blame
8.36 kB
import os
import uuid
import moviepy.editor as mp
from PIL import Image
import gradio as gr
# ----------------------------------
# ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉํ•  ๋กœ๊ทธ ๋ฆฌ์ŠคํŠธ
# ----------------------------------
global_logs = []
def add_log(message: str):
"""
๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๋Š” ํ•จ์ˆ˜
"""
global_logs.append(message)
print(message)
def parse_time_to_seconds(time_str: str):
"""
์‹œ:๋ถ„:์ดˆ ํ˜•ํƒœ์˜ ๋ฌธ์ž์—ด(์˜ˆ: '00:00:10' ๋˜๋Š” '00:01:02')์„ ์ดˆ ๋‹จ์œ„(float)๋กœ ๋ณ€ํ™˜
"""
try:
h, m, s = time_str.split(":")
total_seconds = int(h) * 3600 + int(m) * 60 + float(s)
return total_seconds
except Exception:
return 0.0
def generate_thumbnail(video_clip, time_point):
"""
ํŠน์ • ์‹œ์ ์˜ ํ”„๋ ˆ์ž„์„ ์ธ๋„ค์ผ๋กœ ์ƒ์„ฑํ•˜์—ฌ PIL.Image๋กœ ๋ฐ˜ํ™˜
"""
try:
frame = video_clip.get_frame(time_point)
thumbnail_img = Image.fromarray(frame)
return thumbnail_img
except:
# ๋งŒ์•ฝ time_point๊ฐ€ ์žฌ์ƒ์‹œ๊ฐ„ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ์ฒซ ํ”„๋ ˆ์ž„์„ ์ธ๋„ค์ผ๋กœ
frame = video_clip.get_frame(0)
thumbnail_img = Image.fromarray(frame)
return thumbnail_img
def process_video(video,
start_time_str,
end_time_str,
resolution_factor,
frame_rate_factor,
speed_factor,
repeat_count):
"""
์ „์ฒด GIF ๋ณ€ํ™˜ ๊ณผ์ •์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜
"""
global global_logs
global_logs = [] # ํ˜ธ์ถœ ์‹œ๋งˆ๋‹ค ๋กœ๊ทธ๋ฅผ ์ดˆ๊ธฐํ™”
# ๋กœ๊ทธ 1: ์—…๋กœ๋“œ๋œ ํŒŒ์ผ ํ™•์ธ
add_log("[LOG 1] ๋น„๋””์˜ค ์—…๋กœ๋“œ ๋ฐ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
# ๋น„๋””์˜ค ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
add_log("[LOG 2] ๋น„๋””์˜ค ๋กœ๋“œ ์ค‘...")
input_video = mp.VideoFileClip(video.name)
# ์žฌ์ƒ์‹œ๊ฐ„ ์ถœ๋ ฅ
duration = input_video.duration
add_log(f"[LOG 3] ์—…๋กœ๋“œ๋œ ์˜์ƒ์˜ ์žฌ์ƒ์‹œ๊ฐ„: {duration:.2f}์ดˆ")
# ์‹œ์ž‘/๋ ์‹œ๊ฐ„ parse
add_log("[LOG 4] ์‹œ์ž‘/๋ ์‹œ๊ฐ„ ํŒŒ์‹ฑ ์ค‘...")
start_sec = parse_time_to_seconds(start_time_str)
end_sec = parse_time_to_seconds(end_time_str)
# ์ž˜๋ชป๋œ ์ž…๋ ฅ ํ˜น์€ ๋ฒ”์œ„ ๋ฒ—์–ด๋‚˜๋ฉด ์ž๋™ ๋ณด์ •
if start_sec < 0:
start_sec = 0
if end_sec <= 0 or end_sec > duration:
end_sec = duration
if start_sec >= end_sec:
start_sec = 0
end_sec = duration
add_log(f"[LOG 5] ์ ์šฉ๋œ ์‹œ์ž‘ ์‹œ๊ฐ„: {start_sec}์ดˆ, ์ข…๋ฃŒ ์‹œ๊ฐ„: {end_sec}์ดˆ")
# ํ•ด๋‹น ๊ตฌ๊ฐ„ ์ž๋ฅด๊ธฐ(subclip)
add_log("[LOG 6] ์˜์ƒ ์ž๋ฅด๊ธฐ ์ž‘์—… ์ง„ํ–‰...")
clip = input_video.subclip(start_sec, end_sec)
# ์†๋„ ์กฐ์ ˆ
if abs(speed_factor - 1.0) > 1e-3:
add_log(f"[LOG 7] ์†๋„ {speed_factor}๋ฐฐ๋กœ ์กฐ์ ˆ ์ค‘...")
clip = clip.fx(mp.vfx.speedx, speed_factor)
# ํ•ด์ƒ๋„ ์กฐ์ ˆ (๊ธฐ๋ณธ 1.0 = ์›๋ณธ)
if abs(resolution_factor - 1.0) > 1e-3:
add_log(f"[LOG 8] ํ•ด์ƒ๋„ {resolution_factor*100:.1f}%๋กœ ์กฐ์ ˆ ์ค‘...")
clip = clip.resize(resolution_factor)
# ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ์กฐ์ ˆ (๊ธฐ๋ณธ ์›๋ณธ fps)
original_fps = clip.fps
target_fps = original_fps * frame_rate_factor
add_log(f"[LOG 9] ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ๋ฅผ {target_fps:.2f}๋กœ ์กฐ์ ˆ ์ค‘... (์›๋ณธ {original_fps} FPS)")
clip = clip.set_fps(target_fps)
# ๋ฐ˜๋ณต ํšŒ์ˆ˜ (์ตœ๋Œ€ 10ํšŒ)
repeat_count = min(max(int(repeat_count), 1), 10)
add_log(f"[LOG 10] ๋ฐ˜๋ณต ํšŒ์ˆ˜: {repeat_count}ํšŒ")
repeated_clips = [clip] * repeat_count
final_clip = mp.concatenate_videoclips(repeated_clips)
# GIF ์ƒ์„ฑ
add_log("[LOG 11] GIF ์ƒ์„ฑ ์ค‘...")
# ์ž„์‹œ ํŒŒ์ผ ์ด๋ฆ„ ์ƒ์„ฑ
output_filename = f"temp_{uuid.uuid4().hex}.gif"
final_clip.write_gif(output_filename, fps=target_fps)
add_log("[LOG 12] GIF ์ƒ์„ฑ ์™„๋ฃŒ! ํŒŒ์ผ๋ช…: " + output_filename)
# GIF ๋ฏธ๋ฆฌ๋ณด๊ธฐ์šฉ ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜ (์ฒซ ํ”„๋ ˆ์ž„)
gif_preview = Image.open(output_filename)
return gif_preview, output_filename, "\n".join(global_logs)
def update_thumbnails(video, start_time_str, end_time_str):
"""
์‹œ์ž‘/๋ ์ธ๋„ค์ผ์„ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜
"""
input_video = mp.VideoFileClip(video.name)
duration = input_video.duration
start_sec = parse_time_to_seconds(start_time_str)
end_sec = parse_time_to_seconds(end_time_str)
# ๋ฒ”์œ„ ๋ณด์ •
if start_sec < 0:
start_sec = 0
if end_sec <= 0 or end_sec > duration:
end_sec = duration
if start_sec >= end_sec:
start_sec = 0
end_sec = duration
start_thumb = generate_thumbnail(input_video, start_sec)
end_thumb = generate_thumbnail(input_video, end_sec)
return start_thumb, end_thumb
# ------------------------------
# Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ (Blocks)
# ------------------------------
with gr.Blocks() as demo:
gr.Markdown("## ๋™์˜์ƒ์„ GIF๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ๋ฐ๋ชจ")
with gr.Tab("GIF ๋ณ€ํ™˜"):
# 1. ์—…๋กœ๋“œ ์ž…๋ ฅ์ฐฝ
video_input = gr.Video(label="๋™์˜์ƒ ์—…๋กœ๋“œ", type="file") # type="file"๋กœ ์—…๋กœ๋“œ ์‹œ 'video.name' ์ ‘๊ทผ ๊ฐ€๋Šฅ
# 4. ์‹œ์ž‘/๋ ๋ถ€๋ถ„ ์‹œ๊ฐ„ ์ž…๋ ฅ
start_time = gr.Textbox(label="์‹œ์ž‘ ์‹œ๊ฐ„ (์˜ˆ: 00:00:05)", value="00:00:00")
end_time = gr.Textbox(label="์ข…๋ฃŒ ์‹œ๊ฐ„ (์˜ˆ: 00:00:10)", value="00:00:05")
# ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ธ๋„ค์ผ (์‹œ์ž‘, ์ข…๋ฃŒ)
start_thumb_output = gr.Image(label="์‹œ์ž‘ ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
end_thumb_output = gr.Image(label="์ข…๋ฃŒ ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
# 6. ํ•ด์ƒ๋„ ์กฐ์ ˆ ์Šฌ๋ผ์ด๋”(๊ธฐ๋ณธ 1.0)
resolution_slider = gr.Slider(label="ํ•ด์ƒ๋„ ๋น„์œจ ์กฐ์ ˆ(0.1 ~ 1.0)", minimum=0.1, maximum=1.0, step=0.1, value=1.0)
# 7. ํ”„๋ ˆ์ž„ ์†๋„ ์กฐ์ ˆ ์Šฌ๋ผ์ด๋”(๊ธฐ๋ณธ 1.0)
fps_slider = gr.Slider(label="ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ ์กฐ์ ˆ(0.1 ~ 2.0)", minimum=0.1, maximum=2.0, step=0.1, value=1.0)
# 8. ์†๋„ ์กฐ์ ˆ ์Šฌ๋ผ์ด๋”(๊ธฐ๋ณธ 1๋ฐฐ)
speed_slider = gr.Slider(label="์žฌ์ƒ ์†๋„ ์กฐ์ ˆ(0.5 ~ 2.0)", minimum=0.5, maximum=2.0, step=0.1, value=1.0)
# 9. ๋ฐ˜๋ณต ํšŸ์ˆ˜
repeat_slider = gr.Slider(label="GIF ๋ฐ˜๋ณต ํšŸ์ˆ˜(1 ~ 10)", minimum=1, maximum=10, step=1, value=1)
# 10. GIF ์ƒ์„ฑ ๋ฒ„ํŠผ
generate_button = gr.Button("GIF ์ƒ์„ฑํ•˜๊ธฐ")
# 11, 12. ๊ฒฐ๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ(์ด๋ฏธ์ง€), ๋‹ค์šด๋กœ๋“œ(๋งํฌ)
gif_preview_output = gr.Image(label="์™„์„ฑ๋œ GIF ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
download_output = gr.File(label="GIF ๋‹ค์šด๋กœ๋“œ ๋งํฌ")
# ๋กœ๊ทธ ์ถœ๋ ฅ
logs_output = gr.Textbox(label="๋กœ๊ทธ ์ถœ๋ ฅ", lines=10)
# ์ธ๋„ค์ผ ์—…๋ฐ์ดํŠธ ์ด๋ฒคํŠธ
# (์ž…๋ ฅ ์‹œ๊ฐ„์ด ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์ธ๋„ค์ผ ๊ฐฑ์‹ )
start_time.change(
fn=update_thumbnails,
inputs=[video_input, start_time, end_time],
outputs=[start_thumb_output, end_thumb_output]
)
end_time.change(
fn=update_thumbnails,
inputs=[video_input, start_time, end_time],
outputs=[start_thumb_output, end_thumb_output]
)
# ์˜์ƒ ์—…๋กœ๋“œ๋  ๋•Œ๋„ ์ธ๋„ค์ผ ์ž๋™ ์ƒ์„ฑ
video_input.change(
fn=update_thumbnails,
inputs=[video_input, start_time, end_time],
outputs=[start_thumb_output, end_thumb_output]
)
# GIF ์ƒ์„ฑ ๋ฒ„ํŠผ ๋™์ž‘
generate_button.click(
fn=process_video,
inputs=[
video_input,
start_time,
end_time,
resolution_slider,
fps_slider,
speed_slider,
repeat_slider
],
outputs=[
gif_preview_output,
download_output,
logs_output
]
)
gr.Markdown("### [์‚ฌ์šฉ ๊ฐ€์ด๋“œ]\n1. ๋™์˜์ƒ์„ ์—…๋กœ๋“œํ•˜์„ธ์š”.\n2. ์‹œ์ž‘/๋ ์‹œ๊ฐ„์„ ์ ์ ˆํžˆ ์ž…๋ ฅํ•˜์„ธ์š”.\n3. ํ•ด์ƒ๋„, ํ”„๋ ˆ์ž„ ์†๋„, ์žฌ์ƒ ์†๋„, ๋ฐ˜๋ณต ํšŸ์ˆ˜๋ฅผ ์กฐ์ ˆํ•œ ๋’ค `GIF ์ƒ์„ฑํ•˜๊ธฐ` ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด GIF๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.\n4. ๊ฒฐ๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์™€ ๋‹ค์šด๋กœ๋“œ ๋งํฌ๋ฅผ ํ†ตํ•ด GIF๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
# ์‹คํ–‰ (Spaces์—์„œ๋Š” demo.launch()๋ฅผ ์ƒ๋žตํ•˜๊ฑฐ๋‚˜ ๊ทธ๋Œ€๋กœ ๋‘์–ด๋„ ๋ฉ๋‹ˆ๋‹ค)
if __name__ == "__main__":
demo.launch()