multimodalart's picture
multimodalart HF Staff
Upload app.py with huggingface_hub
9144e5d verified
Raw
History Blame Contribute Delete
6.78 kB
import os
import random
import gradio as gr
import numpy as np
import spaces
import torch
from diffusers import Flux2KleinPipeline
from PIL import Image
BASE_MODEL = "black-forest-labs/FLUX.2-klein-9B"
LORA_REPO = "dx8152/Flux2-Klein-9B-Migration"
LORA_FILE = "Klein-Migration.safetensors"
MAX_SEED = np.iinfo(np.int32).max
PROMPT_PRESETS = {
"Light migration": "将图1角色的光线替换为图2的光线",
"Style migration": "将图1变为图2的画风",
}
DEFAULT_PRESET = "Light migration"
dtype = torch.bfloat16
device = "cuda" if torch.cuda.is_available() else "cpu"
pipe = Flux2KleinPipeline.from_pretrained(
BASE_MODEL,
torch_dtype=dtype,
token=os.environ.get("HF_TOKEN"),
).to(device)
pipe.load_lora_weights(LORA_REPO, weight_name=LORA_FILE, adapter_name="migration")
pipe.set_adapters(["migration"], adapter_weights=[1.0])
pipe.fuse_lora(adapter_names=["migration"], lora_scale=1.0)
pipe.unload_lora_weights()
@spaces.GPU
def infer(
image,
reference,
prompt,
seed=0,
randomize_seed=True,
num_inference_steps=4,
width=0,
height=0,
progress=gr.Progress(track_tqdm=True),
):
"""
Migrate lighting or art style from a reference image (Image 2)
onto a target image (Image 1) using FLUX.2 Klein 9B with the
dx8152/Flux2-Klein-9B-Migration LoRA.
Args:
image: Target image (Image 1).
reference: Reference image (Image 2) providing the lighting
or style to migrate.
prompt: Instruction describing the migration (Chinese prompts work
best, e.g. 将图1角色的光线替换为图2的光线).
seed: Random seed. Ignored when randomize_seed is True.
randomize_seed: Pick a random seed per call.
num_inference_steps: Denoising steps (Klein is distilled to 4).
width: Output width. 0 = derive from Image 1.
height: Output height. 0 = derive from Image 1.
Returns:
The edited image and the seed used.
"""
if image is None:
raise gr.Error("Please upload a target image (Image 1).")
if reference is None:
raise gr.Error("Please upload a reference image (Image 2).")
source = image.convert("RGB")
ref = reference.convert("RGB")
if not prompt or not prompt.strip():
raise gr.Error("Please provide a prompt.")
if randomize_seed:
seed = random.randint(0, MAX_SEED)
generator = torch.Generator(device=device).manual_seed(seed)
result = pipe(
image=[source, ref],
prompt=prompt,
width=width if width else None,
height=height if height else None,
num_inference_steps=num_inference_steps,
guidance_scale=1.0,
generator=generator,
).images[0]
return result, seed
def apply_preset(preset):
return PROMPT_PRESETS.get(preset, "")
def update_dimensions(image):
if image is None:
return 0, 0
w, h = image.size
if w * h > 1024 * 1024:
scale = (1024 * 1024 / (w * h)) ** 0.5
w, h = int(w * scale), int(h * scale)
return (w // 16) * 16, (h // 16) * 16
css = """
#col-container { max-width: 1100px; margin: 0 auto; }
.dark .progress-text { color: white !important }
#examples { max-width: 1100px; margin: 0 auto; }
"""
with gr.Blocks() as demo:
with gr.Column(elem_id="col-container"):
gr.Markdown("## 🪄 FLUX.2 Klein — Anything Migration")
gr.Markdown("""
Migrate **lighting** or **art style** from a reference image onto your target image ⚡
Using [dx8152's Flux2-Klein-9B-Migration LoRA](https://huggingface.co/dx8152/Flux2-Klein-9B-Migration)
on [FLUX.2 Klein 9B](https://huggingface.co/black-forest-labs/FLUX.2-klein-9B) — 4-step distilled inference 💨
""")
with gr.Row():
with gr.Column():
with gr.Row():
image = gr.Image(
label="Image 1 (Target)",
type="pil",
)
reference = gr.Image(
label="Image 2 (Reference)",
type="pil",
)
preset = gr.Radio(
label="Migration type",
choices=list(PROMPT_PRESETS.keys()),
value=DEFAULT_PRESET,
)
prompt = gr.Textbox(
label="Prompt",
value=PROMPT_PRESETS[DEFAULT_PRESET],
lines=2,
info="Editable — Chinese prompts work best. 图1 = Image 1, 图2 = Image 2.",
)
run_btn = gr.Button("🪄 Migrate", variant="primary", size="lg")
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)
num_inference_steps = gr.Slider(
label="Inference Steps", minimum=1, maximum=16, step=1, value=4
)
width = gr.Slider(
label="Width (0 = auto from Image 1)",
minimum=0, maximum=2048, step=16, value=0,
)
height = gr.Slider(
label="Height (0 = auto from Image 1)",
minimum=0, maximum=2048, step=16, value=0,
)
with gr.Column():
result = gr.Image(label="Output Image", interactive=False)
seed_used = gr.Number(label="Seed Used", interactive=False)
gr.Examples(
examples=[
["character_1.png", "light_1.png", PROMPT_PRESETS["Light migration"]],
["character_2.png", "light_4.png", PROMPT_PRESETS["Light migration"]],
["place_1.png", "light_6.png", PROMPT_PRESETS["Light migration"]],
["character_1.png", "metropolis.jpg", PROMPT_PRESETS["Style migration"]],
],
inputs=[image, reference, prompt],
outputs=[result, seed_used],
fn=infer,
cache_examples=True,
cache_mode="lazy",
elem_id="examples",
)
preset.change(fn=apply_preset, inputs=[preset], outputs=[prompt])
image.change(fn=update_dimensions, inputs=[image], outputs=[width, height])
run_btn.click(
fn=infer,
inputs=[image, reference, prompt, seed, randomize_seed, num_inference_steps, width, height],
outputs=[result, seed_used],
)
demo.launch(mcp_server=True, theme=gr.themes.Citrus(), css=css, footer_links=["api", "gradio", "settings"])