6-1_gif / app.py
Kims12's picture
Update app.py
4abd773 verified
raw
history blame
8.7 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 Exception as e:
add_log(f"[ERROR] ์ธ๋„ค์ผ ์ƒ์„ฑ ์‹คํŒจ: {e}")
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 = [] # ํ˜ธ์ถœ ์‹œ๋งˆ๋‹ค ๋กœ๊ทธ ์ดˆ๊ธฐํ™”
add_log("[LOG 1] ๋น„๋””์˜ค ์—…๋กœ๋“œ ๋ฐ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
# video ํŒŒ๋ผ๋ฏธํ„ฐ์—์„œ ํŒŒ์ผ ๊ฒฝ๋กœ ์ถ”์ถœ (Gradio์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์ด ๋ฌธ์ž์—ด์ผ ์ˆ˜ ์žˆ์Œ)
video_path = video if isinstance(video, str) else video.name
add_log("[LOG 2] ๋น„๋””์˜ค ๋กœ๋“œ ์ค‘...")
try:
input_video = mp.VideoFileClip(video_path)
except Exception as e:
add_log(f"[ERROR] ๋น„๋””์˜ค ๋กœ๋“œ ์‹คํŒจ: {e}")
return None, None, "\n".join(global_logs)
duration = input_video.duration
add_log(f"[LOG 3] ์—…๋กœ๋“œ๋œ ์˜์ƒ์˜ ์žฌ์ƒ์‹œ๊ฐ„: {duration:.2f}์ดˆ")
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}์ดˆ")
add_log("[LOG 6] ์˜์ƒ ์ž๋ฅด๊ธฐ ์ž‘์—… ์ง„ํ–‰...")
clip = input_video.subclip(start_sec, end_sec)
# ํ•ด์ƒ๋„ ์กฐ์ ˆ (ํŒŒ์ผ ํฌ๊ธฐ ์ ˆ๊ฐ์„ ์œ„ํ•ด)
if abs(resolution_factor - 1.0) > 1e-3:
add_log(f"[LOG 7] ํ•ด์ƒ๋„ {resolution_factor*100:.1f}%๋กœ ์กฐ์ ˆ ์ค‘...")
clip = clip.resize(resolution_factor)
# ์žฌ์ƒ์†๋„ ์กฐ์ ˆ: speedx๋ฅผ ์ ์šฉํ•˜๋ฉด clip.duration์ด ์ค„์–ด๋“ค์ง€๋งŒ fps๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋จ.
if abs(speed_factor - 1.0) > 1e-3:
add_log(f"[LOG 8] ์žฌ์ƒ์†๋„ {speed_factor}๋ฐฐ๋กœ ์กฐ์ ˆ ์ค‘...")
clip = clip.fx(mp.vfx.speedx, speed_factor)
# FPS ์กฐ์ ˆ: ์ตœ์ข… ์ถœ๋ ฅ FPS์— speed_factor์™€ frame_rate_factor๋ฅผ ๋ฐ˜์˜
original_fps = clip.fps
target_fps = original_fps * speed_factor * frame_rate_factor
add_log(f"[LOG 9] ์ตœ์ข… ์ถœ๋ ฅ FPS: {target_fps:.2f} (์›๋ณธ FPS: {original_fps})")
clip = clip.set_fps(target_fps)
# ๋ฐ˜๋ณต ํšŸ์ˆ˜: 0์ด๋ฉด ๋ฌดํ•œ๋ฐ˜๋ณต, 1~10๋ฉด ํ•ด๋‹น ํšŸ์ˆ˜๋งŒํผ ๋ฐ˜๋ณต (GIF์˜ loop ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์‚ฌ์šฉ)
add_log(f"[LOG 10] GIF ๋ฐ˜๋ณต ํšŸ์ˆ˜ ์„ค์ •: {repeat_count} (0์ด๋ฉด ๋ฌดํ•œ๋ฐ˜๋ณต)")
final_clip = clip
add_log("[LOG 11] GIF ์ƒ์„ฑ ์ค‘...")
output_filename = f"temp_{uuid.uuid4().hex}.gif"
try:
# loop=0์ด๋ฉด ๋ฌดํ•œ๋ฐ˜๋ณต, ๊ทธ ์™ธ์—๋Š” ํ•ด๋‹น ๊ฐ’ ์‚ฌ์šฉ
loop_param = 0 if int(repeat_count) == 0 else int(repeat_count)
final_clip.write_gif(output_filename, fps=target_fps, program='ffmpeg', loop=loop_param)
add_log("[LOG 12] GIF ์ƒ์„ฑ ์™„๋ฃŒ! ํŒŒ์ผ๋ช…: " + output_filename)
except Exception as e:
add_log(f"[ERROR] GIF ์ƒ์„ฑ ์‹คํŒจ: {e}")
return None, None, "\n".join(global_logs)
# ๋ฏธ๋ฆฌ๋ณด๊ธฐ: ์™„์„ฑ๋œ GIF ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฉด gr.Image์—์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
return output_filename, output_filename, "\n".join(global_logs)
def update_thumbnails(video, start_time_str, end_time_str):
"""
์‹œ์ž‘/๋ ์ธ๋„ค์ผ์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜
"""
video_path = video if isinstance(video, str) else video.name
try:
input_video = mp.VideoFileClip(video_path)
except Exception as e:
add_log(f"[ERROR] ๋น„๋””์˜ค ๋กœ๋“œ ์‹คํŒจ: {e}")
return None, None
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 ๋ณ€ํ™˜"):
# ๋™์˜์ƒ ์—…๋กœ๋“œ
video_input = gr.Video(label="๋™์˜์ƒ ์—…๋กœ๋“œ")
# ์‹œ์ž‘/๋ ์‹œ๊ฐ„ ์ž…๋ ฅ
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="์ข…๋ฃŒ ์ธ๋„ค์ผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ")
# ํ•ด์ƒ๋„, ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ, ์žฌ์ƒ ์†๋„, ๋ฐ˜๋ณต ํšŸ์ˆ˜ ์กฐ์ ˆ
resolution_slider = gr.Slider(label="ํ•ด์ƒ๋„ ๋น„์œจ ์กฐ์ ˆ (0.1 ~ 1.0)", minimum=0.1, maximum=1.0, step=0.1, value=0.5)
fps_slider = gr.Slider(label="ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ ์กฐ์ ˆ (0.1 ~ 2.0)", minimum=0.1, maximum=2.0, step=0.1, value=0.5)
speed_slider = gr.Slider(label="์žฌ์ƒ ์†๋„ ์กฐ์ ˆ (0.5 ~ 2.0)", minimum=0.5, maximum=2.0, step=0.1, value=1.0)
repeat_slider = gr.Slider(label="GIF ๋ฐ˜๋ณต ํšŸ์ˆ˜ (0: ๋ฌดํ•œ๋ฐ˜๋ณต, 1~10: ๋ฐ˜๋ณต ํšŸ์ˆ˜)", minimum=0, maximum=10, step=1, value=0)
# GIF ์ƒ์„ฑ ๋ฒ„ํŠผ ๋ฐ ๊ฒฐ๊ณผ ์ถœ๋ ฅ
generate_button = gr.Button("GIF ์ƒ์„ฑํ•˜๊ธฐ")
# ๋ฏธ๋ฆฌ๋ณด๊ธฐ: gr.Image๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜ GIF ๋ฏธ๋ฆฌ๋ณด๊ธฐ
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(
"### [์‚ฌ์šฉ ๊ฐ€์ด๋“œ]\n"
"1. ๋™์˜์ƒ์„ ์—…๋กœ๋“œํ•˜์„ธ์š”.\n"
"2. ์‹œ์ž‘/๋ ์‹œ๊ฐ„์„ ์ ์ ˆํžˆ ์ž…๋ ฅํ•˜์„ธ์š”.\n"
"3. ํ•ด์ƒ๋„, ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ ๋ฐฐ์œจ, ์žฌ์ƒ ์†๋„, ๋ฐ˜๋ณต ํšŸ์ˆ˜๋ฅผ ์กฐ์ ˆํ•œ ๋’ค `GIF ์ƒ์„ฑํ•˜๊ธฐ` ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด GIF๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.\n"
" - ๋ฐ˜๋ณต ํšŸ์ˆ˜: 0์„ ์„ ํƒํ•˜๋ฉด ๋ฌดํ•œ๋ฐ˜๋ณต, 1~10์€ ํ•ด๋‹น ํšŸ์ˆ˜๋งŒํผ ๋ฐ˜๋ณต๋ฉ๋‹ˆ๋‹ค.\n"
"4. ๊ฒฐ๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์™€ ๋‹ค์šด๋กœ๋“œ ๋งํฌ๋ฅผ ํ†ตํ•ด GIF๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
)
if __name__ == "__main__":
demo.launch()