Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
|
@@ -152,7 +152,107 @@ def generate_image(
|
|
| 152 |
return image, debug_images, seed
|
| 153 |
|
| 154 |
|
| 155 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
_CUSTOM_CSS_ = """
|
| 157 |
:root {
|
| 158 |
--primary-color: #f8c3cd; /* Sakura pink - primary accent */
|
|
@@ -419,7 +519,6 @@ def create_demo():
|
|
| 419 |
|
| 420 |
with gr.Row():
|
| 421 |
with gr.Column(scale=6):
|
| 422 |
-
# Input panel - using a Group div with custom class instead of Box
|
| 423 |
with gr.Group(elem_id="input-panel", elem_classes="panel-box"):
|
| 424 |
gr.Markdown("### ๐ธ Reference Images")
|
| 425 |
with gr.Row():
|
|
@@ -461,19 +560,21 @@ def create_demo():
|
|
| 461 |
gr.HTML(_CITE_)
|
| 462 |
|
| 463 |
with gr.Column(scale=6):
|
| 464 |
-
# Output panel - using a Group div with custom class instead of Box
|
| 465 |
with gr.Group(elem_id="output-panel", elem_classes="panel-box"):
|
| 466 |
gr.Markdown("### ๐ผ๏ธ Generated Result")
|
| 467 |
output_image = gr.Image(label="Generated Image", elem_id="output-image", format='png')
|
| 468 |
seed_output = gr.Textbox(label="Used Seed", elem_id="seed-output")
|
| 469 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 470 |
gr.Markdown("### ๐ Preprocessing")
|
| 471 |
debug_image = gr.Gallery(
|
| 472 |
label="Preprocessing Results (including face crop and background removal)",
|
| 473 |
elem_id="debug-gallery",
|
| 474 |
)
|
| 475 |
|
| 476 |
-
# Examples panel - using a Group div with custom class instead of Box
|
| 477 |
with gr.Group(elem_id="examples-panel", elem_classes="panel-box"):
|
| 478 |
gr.Markdown("## ๐ Examples")
|
| 479 |
example_inps = [
|
|
@@ -559,6 +660,7 @@ def create_demo():
|
|
| 559 |
fn=generate_image,
|
| 560 |
)
|
| 561 |
|
|
|
|
| 562 |
generate_btn.click(
|
| 563 |
fn=generate_image,
|
| 564 |
inputs=[
|
|
@@ -583,9 +685,22 @@ def create_demo():
|
|
| 583 |
outputs=[output_image, debug_image, seed_output],
|
| 584 |
)
|
| 585 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 586 |
return demo
|
| 587 |
|
| 588 |
|
| 589 |
if __name__ == '__main__':
|
| 590 |
demo = create_demo()
|
| 591 |
-
demo.launch()
|
|
|
|
| 152 |
return image, debug_images, seed
|
| 153 |
|
| 154 |
|
| 155 |
+
# -----------------------------
|
| 156 |
+
# (1) ์ฌ๊ธฐ์ ์์ API ํธ์ถ์ ์ํ ์ถ๊ฐ ์ฝ๋
|
| 157 |
+
# -----------------------------
|
| 158 |
+
import requests
|
| 159 |
+
import random
|
| 160 |
+
import tempfile
|
| 161 |
+
import subprocess
|
| 162 |
+
from gradio_client import Client, handle_file
|
| 163 |
+
|
| 164 |
+
# ์์: ์๊ฒฉ ์๋ฒ Endpoint (ํ์ํ๋ค๋ฉด ์์ )
|
| 165 |
+
REMOTE_ENDPOINT = "http://211.233.58.201:7875/"
|
| 166 |
+
|
| 167 |
+
client = Client(REMOTE_ENDPOINT)
|
| 168 |
+
|
| 169 |
+
def run_process_video_api(image_path: str, prompt: str, video_length: float = 2.0):
|
| 170 |
+
"""
|
| 171 |
+
์๊ฒฉ /process ์๋ํฌ์ธํธ ํธ์ถํ์ฌ ์์์ ์์ฑ.
|
| 172 |
+
(์์: prompt, negative_prompt, seed ๋ฑ์ ํ๋์ฝ๋ฉํ๊ฑฐ๋ ์ํ๋๋๋ก ์กฐ์ ๊ฐ๋ฅ)
|
| 173 |
+
"""
|
| 174 |
+
# ๋๋ค ์๋
|
| 175 |
+
seed_val = random.randint(0, 9999999)
|
| 176 |
+
# negative_prompt = "" ๋ฑ ๊ณ ์
|
| 177 |
+
negative_prompt = ""
|
| 178 |
+
use_teacache = True
|
| 179 |
+
|
| 180 |
+
# /process ํธ์ถ (gradio_client)
|
| 181 |
+
result = client.predict(
|
| 182 |
+
input_image=handle_file(image_path),
|
| 183 |
+
prompt=prompt,
|
| 184 |
+
n_prompt=negative_prompt,
|
| 185 |
+
seed=seed_val,
|
| 186 |
+
use_teacache=use_teacache,
|
| 187 |
+
video_length=video_length,
|
| 188 |
+
api_name="/process",
|
| 189 |
+
)
|
| 190 |
+
# result๋ (video_dict, preview_dict, md_text, html_text) ๊ตฌ์กฐ
|
| 191 |
+
video_dict, preview_dict, md_text, html_text = result
|
| 192 |
+
video_path = video_dict.get("video") if isinstance(video_dict, dict) else None
|
| 193 |
+
return video_path
|
| 194 |
+
|
| 195 |
+
def add_watermark_to_video(input_video_path: str, watermark_text="Ginigen.com") -> str:
|
| 196 |
+
"""
|
| 197 |
+
FFmpeg๋ก ์์์ ์ค๋ฅธ์ชฝ ํ๋จ ์ํฐ๋งํฌ๋ฅผ ์ถ๊ฐํ ์ ์์์ ๋ฆฌํด
|
| 198 |
+
"""
|
| 199 |
+
if not os.path.exists(input_video_path):
|
| 200 |
+
raise FileNotFoundError(f"Input video not found: {input_video_path}")
|
| 201 |
+
|
| 202 |
+
# ์ถ๋ ฅ ๊ฒฝ๋ก
|
| 203 |
+
base, ext = os.path.splitext(input_video_path)
|
| 204 |
+
watermarked_path = base + "_wm" + ext
|
| 205 |
+
# ffmpeg ๋ช
๋ น์ด ๊ตฌ์ฑ
|
| 206 |
+
# - y: ๋ฎ์ด์ฐ๊ธฐ
|
| 207 |
+
# drawtext ํํฐ๋ก ์ค๋ฅธ์ชฝ ํ๋จ(x=w-tw-10, y=h-th-10)์ boxcolor=black ๋ฐํฌ๋ช
๋ฐ์ค
|
| 208 |
+
cmd = [
|
| 209 |
+
"ffmpeg", "-y",
|
| 210 |
+
"-i", input_video_path,
|
| 211 |
+
"-vf", f"drawtext=fontsize=20:fontcolor=white:text='{watermark_text}':x=w-tw-10:y=h-th-10:box=1:boxcolor=black@0.5:boxborderw=5",
|
| 212 |
+
"-codec:a", "copy",
|
| 213 |
+
watermarked_path
|
| 214 |
+
]
|
| 215 |
+
try:
|
| 216 |
+
subprocess.run(cmd, check=True)
|
| 217 |
+
except Exception as e:
|
| 218 |
+
print(f"[WARN] FFmpeg watermark failed: {e}")
|
| 219 |
+
return input_video_path # ์คํจ ์ ์๋ณธ ๋ฐํ
|
| 220 |
+
|
| 221 |
+
return watermarked_path
|
| 222 |
+
|
| 223 |
+
def generate_video_from_image(image_array: np.ndarray):
|
| 224 |
+
"""
|
| 225 |
+
1) Numpy ์ด๋ฏธ์ง๋ฅผ ์์ ํ์ผ๋ก ์ ์ฅ
|
| 226 |
+
2) ์๊ฒฉ API๋ก 2์ด ์์ ์์ฑ (๊ธฐ๋ณธ prompt ๊ณ ์ )
|
| 227 |
+
3) FFmpeg๋ก 'Ginigen.com' ์ํฐ๋งํฌ ์ถ๊ฐ
|
| 228 |
+
4) ์ต์ข
mp4 ๊ฒฝ๋ก ๋ฐํ
|
| 229 |
+
"""
|
| 230 |
+
if image_array is None:
|
| 231 |
+
raise gr.Error("์ด๋ฏธ์ง๊ฐ ์์ต๋๋ค.")
|
| 232 |
+
|
| 233 |
+
# (1) ์์ ํ์ผ๋ก ์ ์ฅ
|
| 234 |
+
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as fp:
|
| 235 |
+
temp_img_path = fp.name
|
| 236 |
+
Image.fromarray(image_array).save(temp_img_path, format="PNG")
|
| 237 |
+
|
| 238 |
+
# (2) ์๊ฒฉ API ํธ์ถ
|
| 239 |
+
default_video_prompt = "Generate a video with smooth and natural movement. Objects should have visible motion while maintaining fluid transitions."
|
| 240 |
+
result_video_path = run_process_video_api(
|
| 241 |
+
image_path=temp_img_path,
|
| 242 |
+
prompt=default_video_prompt,
|
| 243 |
+
video_length=2.0,
|
| 244 |
+
)
|
| 245 |
+
if result_video_path is None:
|
| 246 |
+
raise gr.Error("์์ API ํธ์ถ ์คํจ ๋๋ ๊ฒฐ๊ณผ ์์")
|
| 247 |
+
|
| 248 |
+
# (3) FFmpeg ์ํฐ๋งํฌ ์ถ๊ฐ
|
| 249 |
+
final_video = add_watermark_to_video(result_video_path, watermark_text="Ginigen.com")
|
| 250 |
+
return final_video
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
# -----------------------------
|
| 254 |
+
# Custom CSS, Headers, etc.
|
| 255 |
+
# -----------------------------
|
| 256 |
_CUSTOM_CSS_ = """
|
| 257 |
:root {
|
| 258 |
--primary-color: #f8c3cd; /* Sakura pink - primary accent */
|
|
|
|
| 519 |
|
| 520 |
with gr.Row():
|
| 521 |
with gr.Column(scale=6):
|
|
|
|
| 522 |
with gr.Group(elem_id="input-panel", elem_classes="panel-box"):
|
| 523 |
gr.Markdown("### ๐ธ Reference Images")
|
| 524 |
with gr.Row():
|
|
|
|
| 560 |
gr.HTML(_CITE_)
|
| 561 |
|
| 562 |
with gr.Column(scale=6):
|
|
|
|
| 563 |
with gr.Group(elem_id="output-panel", elem_classes="panel-box"):
|
| 564 |
gr.Markdown("### ๐ผ๏ธ Generated Result")
|
| 565 |
output_image = gr.Image(label="Generated Image", elem_id="output-image", format='png')
|
| 566 |
seed_output = gr.Textbox(label="Used Seed", elem_id="seed-output")
|
| 567 |
|
| 568 |
+
# (2) ์์ ์์ฑ ๋ฒํผ & ์ถ๋ ฅ ์์ญ ์ถ๊ฐ
|
| 569 |
+
generate_video_btn = gr.Button("๐ฌ Generate Video from Image")
|
| 570 |
+
output_video = gr.Video(label="Generated Video", elem_id="video-output")
|
| 571 |
+
|
| 572 |
gr.Markdown("### ๐ Preprocessing")
|
| 573 |
debug_image = gr.Gallery(
|
| 574 |
label="Preprocessing Results (including face crop and background removal)",
|
| 575 |
elem_id="debug-gallery",
|
| 576 |
)
|
| 577 |
|
|
|
|
| 578 |
with gr.Group(elem_id="examples-panel", elem_classes="panel-box"):
|
| 579 |
gr.Markdown("## ๐ Examples")
|
| 580 |
example_inps = [
|
|
|
|
| 660 |
fn=generate_image,
|
| 661 |
)
|
| 662 |
|
| 663 |
+
# ๊ธฐ์กด ์ด๋ฏธ์ง ์์ฑ ํจ์์ ์ฐ๊ฒฐ
|
| 664 |
generate_btn.click(
|
| 665 |
fn=generate_image,
|
| 666 |
inputs=[
|
|
|
|
| 685 |
outputs=[output_image, debug_image, seed_output],
|
| 686 |
)
|
| 687 |
|
| 688 |
+
# (3) ์์ ์์ฑ ๋ฒํผ ํด๋ฆญ -> generate_video_from_image() ํธ์ถ
|
| 689 |
+
def on_click_generate_video(img):
|
| 690 |
+
if img is None:
|
| 691 |
+
raise gr.Error("๋จผ์ ์ด๋ฏธ์ง๋ฅผ ์์ฑํด์ฃผ์ธ์.")
|
| 692 |
+
video_path = generate_video_from_image(img)
|
| 693 |
+
return video_path
|
| 694 |
+
|
| 695 |
+
generate_video_btn.click(
|
| 696 |
+
fn=on_click_generate_video,
|
| 697 |
+
inputs=[output_image],
|
| 698 |
+
outputs=[output_video],
|
| 699 |
+
)
|
| 700 |
+
|
| 701 |
return demo
|
| 702 |
|
| 703 |
|
| 704 |
if __name__ == '__main__':
|
| 705 |
demo = create_demo()
|
| 706 |
+
demo.launch(server_port=args.port)
|