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:[email protected]: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)
|