Spaces:
Running
on
Zero
Running
on
Zero
import random | |
import gradio as gr | |
import numpy as np | |
import torch | |
import spaces | |
from diffusers import FluxPipeline | |
from PIL import Image | |
from diffusers.utils import export_to_gif | |
from transformers import pipeline | |
# ------------------------- | |
# Configuration constants | |
# ------------------------- | |
FRAMES = 4 # number of stills laid out horizontally | |
DEFAULT_HEIGHT = 256 # per‑frame size (px) | |
DEFAULT_FPS = 8 # smoother playback than the original 4 fps | |
MAX_SEED = np.iinfo(np.int32).max | |
# ------------------------- | |
# Model initialisation | |
# ------------------------- | |
device = "cuda" if torch.cuda.is_available() else "cpu" | |
pipe = ( | |
FluxPipeline.from_pretrained( | |
"black-forest-labs/FLUX.1-dev", | |
torch_dtype=torch.float16, # slightly higher precision than bfloat16 for crisper output | |
) | |
.to(device) | |
) | |
# English is the primary UI language, but Korean prompts are still accepted & translated. | |
translator = pipeline("translation", model="Helsinki-NLP/opus-mt-ko-en") | |
# ------------------------- | |
# Helper functions | |
# ------------------------- | |
def split_image(input_image: Image.Image, frame_size: int) -> list[Image.Image]: | |
"""Cut a wide strip into equal square frames.""" | |
return [ | |
input_image.crop((i * frame_size, 0, (i + 1) * frame_size, frame_size)) | |
for i in range(FRAMES) | |
] | |
def translate_to_english(text: str) -> str: | |
"""Translate Korean text to English if necessary.""" | |
return translator(text)[0]["translation_text"] | |
def predict( | |
prompt: str, | |
seed: int = 42, | |
randomize_seed: bool = False, | |
guidance_scale: float = 7.0, | |
num_inference_steps: int = 40, | |
height: int = DEFAULT_HEIGHT, | |
fps: int = DEFAULT_FPS, | |
progress: gr.Progress = gr.Progress(track_tqdm=True), | |
): | |
# 1) Language handling | |
if any("\u3131" <= ch <= "\u318E" or "\uAC00" <= ch <= "\uD7A3" for ch in prompt): | |
prompt = translate_to_english(prompt) | |
# 2) Prompt template | |
prompt_template = ( | |
f"A side-by-side {FRAMES} frame image showing consecutive stills from a looped gif moving left to right. " | |
f"The gif is of {prompt}." | |
) | |
# 3) Seed control | |
if randomize_seed: | |
seed = random.randint(0, MAX_SEED) | |
width = FRAMES * height # maintain square frames | |
# 4) Generation | |
image = pipe( | |
prompt=prompt_template, | |
guidance_scale=guidance_scale, | |
num_inference_steps=num_inference_steps, | |
num_images_per_prompt=1, | |
generator=torch.Generator(device).manual_seed(seed), | |
height=height, | |
width=width, | |
).images[0] | |
# 5) Assemble gif | |
gif_path = export_to_gif(split_image(image, height), "flux.gif", fps=fps) | |
return gif_path, image, seed | |
# ------------------------- | |
# Interface | |
# ------------------------- | |
css = """ | |
#col-container {max-width: 820px; margin: 0 auto;} | |
footer {visibility: hidden;} | |
""" | |
examples = [ | |
"cat lazily swinging its paws in mid-air", | |
"panda shaking its hips", | |
"flower blooming in timelapse", | |
] | |
with gr.Blocks(theme="soft", css=css) as demo: | |
gr.Markdown("<h1 style='text-align:center'>FLUX GIF Generator</h1>") | |
gr.HTML( | |
""" | |
<div class='container' style='display:flex; justify-content:center; gap:12px;'> | |
<a href="https://huggingface.co/spaces/openfree/Best-AI" target="_blank"> | |
<img src="https://img.shields.io/static/v1?label=OpenFree&message=BEST%20AI%20Services&color=%230000ff&labelColor=%23000080&logo=huggingface&logoColor=%23ffa500&style=for-the-badge" alt="OpenFree badge"> | |
</a> | |
<a href="https://discord.gg/openfreeai" target="_blank"> | |
<img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%230000ff&labelColor=%23800080&logo=discord&logoColor=white&style=for-the-badge" alt="Discord badge"> | |
</a> | |
</div> | |
""" | |
) | |
with gr.Column(elem_id="col-container"): | |
# Prompt row | |
with gr.Row(): | |
prompt = gr.Text( | |
label="", show_label=False, max_lines=1, placeholder="Enter your prompt here…" | |
) | |
submit = gr.Button("Generate", scale=0) | |
# Outputs | |
output_gif = gr.Image(label="", show_label=False) | |
output_stills = gr.Image(label="", show_label=False, elem_id="stills") | |
# Advanced controls | |
with gr.Accordion("Advanced settings", open=False): | |
seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0) | |
randomize_seed = gr.Checkbox(label="Randomize seed", value=True) | |
with gr.Row(): | |
guidance_scale = gr.Slider( | |
label="Guidance scale", minimum=1, maximum=15, step=0.1, value=7.0 | |
) | |
num_inference_steps = gr.Slider( | |
label="Inference steps", minimum=10, maximum=60, step=1, value=40 | |
) | |
with gr.Row(): | |
height = gr.Slider( | |
label="Frame size (px)", minimum=256, maximum=512, step=64, value=DEFAULT_HEIGHT | |
) | |
fps = gr.Slider( | |
label="GIF FPS", minimum=4, maximum=20, step=1, value=DEFAULT_FPS | |
) | |
# Example prompts | |
gr.Examples( | |
examples=examples, | |
fn=predict, | |
inputs=[prompt], | |
outputs=[output_gif, output_stills, seed], | |
cache_examples="lazy", | |
) | |
# Event wiring | |
gr.on( | |
triggers=[submit.click, prompt.submit], | |
fn=predict, | |
inputs=[ | |
prompt, | |
seed, | |
randomize_seed, | |
guidance_scale, | |
num_inference_steps, | |
height, | |
fps, | |
], | |
outputs=[output_gif, output_stills, seed], | |
) | |
demo.launch() | |