Spaces:
Running on Zero
Running on Zero
Professional Noob commited on
Update app.py
Browse files
app.py
CHANGED
|
@@ -10,6 +10,10 @@ from typing import Iterable
|
|
| 10 |
from gradio.themes import Soft
|
| 11 |
from gradio.themes.utils import colors, fonts, sizes
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
colors.orange_red = colors.Color(
|
| 14 |
name="orange_red",
|
| 15 |
c50="#FFF0E5",
|
|
@@ -78,6 +82,10 @@ class OrangeRedTheme(Soft):
|
|
| 78 |
|
| 79 |
orange_red_theme = OrangeRedTheme()
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 82 |
|
| 83 |
print("CUDA_VISIBLE_DEVICES=", os.environ.get("CUDA_VISIBLE_DEVICES"))
|
|
@@ -88,9 +96,12 @@ print("cuda device count:", torch.cuda.device_count())
|
|
| 88 |
if torch.cuda.is_available():
|
| 89 |
print("current device:", torch.cuda.current_device())
|
| 90 |
print("device name:", torch.cuda.get_device_name(torch.cuda.current_device()))
|
| 91 |
-
|
| 92 |
print("Using device:", device)
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
from diffusers import FlowMatchEulerDiscreteScheduler
|
| 95 |
from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
|
| 96 |
from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
|
|
@@ -109,7 +120,6 @@ pipe = QwenImageEditPlusPipeline.from_pretrained(
|
|
| 109 |
torch_dtype=dtype,
|
| 110 |
).to(device)
|
| 111 |
|
| 112 |
-
# Apply FA3 Optimization
|
| 113 |
try:
|
| 114 |
pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
|
| 115 |
print("Flash Attention 3 Processor set successfully.")
|
|
@@ -119,7 +129,7 @@ except Exception as e:
|
|
| 119 |
MAX_SEED = np.iinfo(np.int32).max
|
| 120 |
|
| 121 |
# -------------------------
|
| 122 |
-
#
|
| 123 |
# -------------------------
|
| 124 |
|
| 125 |
NONE_LORA = "None"
|
|
@@ -170,6 +180,13 @@ ADAPTER_SPECS = {
|
|
| 170 |
"adapter_name": "HRPortrait",
|
| 171 |
"strength": 1.0,
|
| 172 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
"Multiple-Angles": {
|
| 174 |
"type": "single",
|
| 175 |
"repo": "dx8152/Qwen-Edit-2509-Multiple-angles",
|
|
@@ -226,14 +243,13 @@ ADAPTER_SPECS = {
|
|
| 226 |
"adapter_name": "upscale-image",
|
| 227 |
"strength": 1.0,
|
| 228 |
},
|
| 229 |
-
# ✅ We'll auto-bump requested resolution when this is selected
|
| 230 |
"Upscale2K": {
|
| 231 |
"type": "single",
|
| 232 |
"repo": "valiantcat/Qwen-Image-Edit-2509-Upscale2K",
|
| 233 |
"weights": "qwen_image_edit_2509_upscale.safetensors",
|
| 234 |
"adapter_name": "upscale-2k",
|
| 235 |
"strength": 1.0,
|
| 236 |
-
"target_long_edge": 2048,
|
| 237 |
},
|
| 238 |
}
|
| 239 |
|
|
@@ -243,16 +259,36 @@ LORA_PRESET_PROMPTS = {
|
|
| 243 |
"AnyPose": "Make the person in image 1 do the exact same pose of the person in image 2. Changing the style and background of the image of the person in image 1 is undesirable, so don't do it. The new pose should be pixel accurate to the pose we are trying to copy. The position of the arms and head and legs should be the same as the pose we are trying to copy. Change the field of view and angle to match exactly image 2. Head tilt and eye gaze pose should match the person in image 2.",
|
| 244 |
"Hyperrealistic-Portrait": "Transform the image into an ultra-realistic photorealistic portrait with strict identity preservation, facing straight to the camera. Enhance pore-level skin textures, realistic moisture effects, and natural wet hair clumping against the skin. Apply cool-toned soft-box lighting with subtle highlights and shadows, maintain realistic green-hazel eye catchlights without synthetic gloss, and preserve soft natural lip texture. Use shallow depth of field with a clean bokeh background, an 85mm macro photographic look, and raw photo grading without retouching to maintain realism and original details.",
|
| 245 |
"Upscale2K": "Upscale this picture to 4K resolution.",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 246 |
}
|
| 247 |
|
| 248 |
-
# Track
|
| 249 |
LOADED_ADAPTERS = set()
|
| 250 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
def _round8(x: int) -> int:
|
| 252 |
return max(8, (int(x) // 8) * 8)
|
| 253 |
|
| 254 |
def compute_dimensions(image: Image.Image, long_edge: int) -> tuple[int, int]:
|
| 255 |
-
"""Preserve aspect ratio, set the longest edge to long_edge, then round to multiples of 8."""
|
| 256 |
w, h = image.size
|
| 257 |
if w >= h:
|
| 258 |
new_w = long_edge
|
|
@@ -262,21 +298,46 @@ def compute_dimensions(image: Image.Image, long_edge: int) -> tuple[int, int]:
|
|
| 262 |
new_w = int(round(long_edge * (w / h)))
|
| 263 |
return _round8(new_w), _round8(new_h)
|
| 264 |
|
| 265 |
-
def
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
|
|
|
|
|
|
| 270 |
|
| 271 |
-
def
|
| 272 |
-
|
| 273 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
|
| 279 |
-
|
|
|
|
|
|
|
| 280 |
|
| 281 |
def _ensure_loaded_and_get_active_adapters(selected_lora: str):
|
| 282 |
spec = ADAPTER_SPECS.get(selected_lora)
|
|
@@ -331,15 +392,14 @@ def _ensure_loaded_and_get_active_adapters(selected_lora: str):
|
|
| 331 |
|
| 332 |
return adapter_names, adapter_weights
|
| 333 |
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
return int(ADAPTER_SPECS["Upscale2K"].get("target_long_edge", 2048))
|
| 338 |
-
return 1024
|
| 339 |
|
| 340 |
@spaces.GPU
|
| 341 |
def infer(
|
| 342 |
-
|
|
|
|
| 343 |
prompt,
|
| 344 |
lora_adapter,
|
| 345 |
seed,
|
|
@@ -352,10 +412,15 @@ def infer(
|
|
| 352 |
if torch.cuda.is_available():
|
| 353 |
torch.cuda.empty_cache()
|
| 354 |
|
| 355 |
-
if
|
| 356 |
-
raise gr.Error("Please upload
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 357 |
|
| 358 |
-
# Handle "None"
|
| 359 |
if lora_adapter == NONE_LORA:
|
| 360 |
try:
|
| 361 |
pipe.set_adapters([], adapter_weights=[])
|
|
@@ -370,20 +435,30 @@ def infer(
|
|
| 370 |
seed = random.randint(0, MAX_SEED)
|
| 371 |
|
| 372 |
generator = torch.Generator(device=device).manual_seed(seed)
|
|
|
|
| 373 |
negative_prompt = (
|
| 374 |
"worst quality, low quality, bad anatomy, bad hands, text, error, missing fingers, "
|
| 375 |
"extra digit, fewer digits, cropped, jpeg artifacts, signature, watermark, username, blurry"
|
| 376 |
)
|
| 377 |
|
| 378 |
-
|
|
|
|
| 379 |
|
| 380 |
-
#
|
| 381 |
target_long_edge = get_target_long_edge_for_lora(lora_adapter)
|
| 382 |
-
width, height = compute_dimensions(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
|
| 384 |
try:
|
| 385 |
result = pipe(
|
| 386 |
-
image=
|
| 387 |
prompt=prompt,
|
| 388 |
negative_prompt=negative_prompt,
|
| 389 |
height=height,
|
|
@@ -401,16 +476,19 @@ def infer(
|
|
| 401 |
torch.cuda.empty_cache()
|
| 402 |
|
| 403 |
@spaces.GPU
|
| 404 |
-
def infer_example(
|
| 405 |
-
if
|
| 406 |
return None, 0
|
| 407 |
|
| 408 |
-
input_pil = input_image.convert("RGB")
|
| 409 |
guidance_scale = 1.0
|
| 410 |
steps = 4
|
| 411 |
-
result, seed = infer(
|
| 412 |
return result, seed
|
| 413 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 |
css = """
|
| 415 |
#col-container {
|
| 416 |
margin: 0 auto;
|
|
@@ -430,7 +508,10 @@ with gr.Blocks() as demo:
|
|
| 430 |
|
| 431 |
with gr.Row(equal_height=True):
|
| 432 |
with gr.Column():
|
| 433 |
-
|
|
|
|
|
|
|
|
|
|
| 434 |
|
| 435 |
prompt = gr.Text(
|
| 436 |
label="Edit Prompt",
|
|
@@ -457,33 +538,35 @@ with gr.Blocks() as demo:
|
|
| 457 |
guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
|
| 458 |
steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
|
| 459 |
|
|
|
|
| 460 |
lora_adapter.change(
|
| 461 |
-
fn=
|
| 462 |
inputs=[lora_adapter, prompt],
|
| 463 |
-
outputs=[prompt],
|
| 464 |
)
|
| 465 |
|
|
|
|
| 466 |
gr.Examples(
|
| 467 |
examples=[
|
| 468 |
-
["examples/1.jpg", "Transform into anime.", "Photo-to-Anime"],
|
| 469 |
-
["examples/5.jpg", "Remove shadows and relight the image using soft lighting.", "Light-Restoration"],
|
| 470 |
-
["examples/4.jpg", "Use a subtle golden-hour filter with smooth light diffusion.", "Relight"],
|
| 471 |
-
["examples/2.jpeg", "Rotate the camera 45 degrees to the left.", "Multiple-Angles"],
|
| 472 |
-
["examples/12.jpg", "flatcolor Desaturate the image and lower the contrast to create a flat, ungraded look similar to a camera log profile. Preserve details in the highlights and shadows.", "Flat-Log"],
|
| 473 |
-
["examples/7.jpg", "Light source from the Right Rear", "Multi-Angle-Lighting"],
|
| 474 |
-
["examples/10.jpeg", "Upscale the image.", "Upscale-Image"],
|
| 475 |
-
["examples/7.jpg", "Light source from the Below", "Multi-Angle-Lighting"],
|
| 476 |
-
["examples/2.jpeg", "Switch the camera to a top-down right corner view.", "Multiple-Angles"],
|
| 477 |
-
["examples/9.jpg", "The camera moves slightly forward as sunlight breaks through the clouds, casting a soft glow around the character's silhouette in the mist. Realistic cinematic style, atmospheric depth.", "Next-Scene"],
|
| 478 |
-
["examples/8.jpg", "Make the subjects skin details more prominent and natural.", "Edit-Skin"],
|
| 479 |
-
["examples/6.jpg", "Switch the camera to a bottom-up view.", "Multiple-Angles"],
|
| 480 |
-
["examples/6.jpg", "Rotate the camera 180 degrees upside down.", "Multiple-Angles"],
|
| 481 |
-
["examples/4.jpg", "Rotate the camera 45 degrees to the right.", "Multiple-Angles"],
|
| 482 |
-
["examples/4.jpg", "Switch the camera to a top-down view.", "Multiple-Angles"],
|
| 483 |
-
["examples/4.jpg", "Switch the camera to a wide-angle lens.", "Multiple-Angles"],
|
| 484 |
-
["examples/11.jpg", "Upscale this picture to 4K resolution.", "Upscale2K"],
|
| 485 |
],
|
| 486 |
-
inputs=[
|
| 487 |
outputs=[output_image, seed],
|
| 488 |
fn=infer_example,
|
| 489 |
cache_examples=False,
|
|
@@ -492,7 +575,7 @@ with gr.Blocks() as demo:
|
|
| 492 |
|
| 493 |
run_button.click(
|
| 494 |
fn=infer,
|
| 495 |
-
inputs=[
|
| 496 |
outputs=[output_image, seed],
|
| 497 |
)
|
| 498 |
|
|
|
|
| 10 |
from gradio.themes import Soft
|
| 11 |
from gradio.themes.utils import colors, fonts, sizes
|
| 12 |
|
| 13 |
+
# -------------------------
|
| 14 |
+
# Theme
|
| 15 |
+
# -------------------------
|
| 16 |
+
|
| 17 |
colors.orange_red = colors.Color(
|
| 18 |
name="orange_red",
|
| 19 |
c50="#FFF0E5",
|
|
|
|
| 82 |
|
| 83 |
orange_red_theme = OrangeRedTheme()
|
| 84 |
|
| 85 |
+
# -------------------------
|
| 86 |
+
# Device
|
| 87 |
+
# -------------------------
|
| 88 |
+
|
| 89 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 90 |
|
| 91 |
print("CUDA_VISIBLE_DEVICES=", os.environ.get("CUDA_VISIBLE_DEVICES"))
|
|
|
|
| 96 |
if torch.cuda.is_available():
|
| 97 |
print("current device:", torch.cuda.current_device())
|
| 98 |
print("device name:", torch.cuda.get_device_name(torch.cuda.current_device()))
|
|
|
|
| 99 |
print("Using device:", device)
|
| 100 |
|
| 101 |
+
# -------------------------
|
| 102 |
+
# Pipeline
|
| 103 |
+
# -------------------------
|
| 104 |
+
|
| 105 |
from diffusers import FlowMatchEulerDiscreteScheduler
|
| 106 |
from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
|
| 107 |
from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
|
|
|
|
| 120 |
torch_dtype=dtype,
|
| 121 |
).to(device)
|
| 122 |
|
|
|
|
| 123 |
try:
|
| 124 |
pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
|
| 125 |
print("Flash Attention 3 Processor set successfully.")
|
|
|
|
| 129 |
MAX_SEED = np.iinfo(np.int32).max
|
| 130 |
|
| 131 |
# -------------------------
|
| 132 |
+
# LoRAs + presets
|
| 133 |
# -------------------------
|
| 134 |
|
| 135 |
NONE_LORA = "None"
|
|
|
|
| 180 |
"adapter_name": "HRPortrait",
|
| 181 |
"strength": 1.0,
|
| 182 |
},
|
| 183 |
+
"BFS-Best-FaceSwap": {
|
| 184 |
+
"type": "single",
|
| 185 |
+
"repo": "Alissonerdx/BFS-Best-Face-Swap",
|
| 186 |
+
"weights": "bfs_head_v5_2511_merged_version_rank_16_fp16.safetensors",
|
| 187 |
+
"adapter_name": "BFS-Best-Faceswap",
|
| 188 |
+
"strength": 1.0,
|
| 189 |
+
},
|
| 190 |
"Multiple-Angles": {
|
| 191 |
"type": "single",
|
| 192 |
"repo": "dx8152/Qwen-Edit-2509-Multiple-angles",
|
|
|
|
| 243 |
"adapter_name": "upscale-image",
|
| 244 |
"strength": 1.0,
|
| 245 |
},
|
|
|
|
| 246 |
"Upscale2K": {
|
| 247 |
"type": "single",
|
| 248 |
"repo": "valiantcat/Qwen-Image-Edit-2509-Upscale2K",
|
| 249 |
"weights": "qwen_image_edit_2509_upscale.safetensors",
|
| 250 |
"adapter_name": "upscale-2k",
|
| 251 |
"strength": 1.0,
|
| 252 |
+
"target_long_edge": 2048,
|
| 253 |
},
|
| 254 |
}
|
| 255 |
|
|
|
|
| 259 |
"AnyPose": "Make the person in image 1 do the exact same pose of the person in image 2. Changing the style and background of the image of the person in image 1 is undesirable, so don't do it. The new pose should be pixel accurate to the pose we are trying to copy. The position of the arms and head and legs should be the same as the pose we are trying to copy. Change the field of view and angle to match exactly image 2. Head tilt and eye gaze pose should match the person in image 2.",
|
| 260 |
"Hyperrealistic-Portrait": "Transform the image into an ultra-realistic photorealistic portrait with strict identity preservation, facing straight to the camera. Enhance pore-level skin textures, realistic moisture effects, and natural wet hair clumping against the skin. Apply cool-toned soft-box lighting with subtle highlights and shadows, maintain realistic green-hazel eye catchlights without synthetic gloss, and preserve soft natural lip texture. Use shallow depth of field with a clean bokeh background, an 85mm macro photographic look, and raw photo grading without retouching to maintain realism and original details.",
|
| 261 |
"Upscale2K": "Upscale this picture to 4K resolution.",
|
| 262 |
+
"BFS-Best-FaceSwap": "head_swap: start with Picture 1 as the base image, keeping its lighting, environment, and background. remove the head from Picture 1 completely and replace it with the head from Picture 2, strictly preserving the hair, eye color, and nose structure of Picture 2. copy the eye direction, head rotation, and micro-expressions from Picture 1. high quality, sharp details, 4k",
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
# Which LoRAs require 2 images (and how to label them in UI)
|
| 266 |
+
# AnyPose: image1=character/base, image2=pose reference [oai_citation:2‡huggingface.co](https://huggingface.co/lilylilith/AnyPose)
|
| 267 |
+
# BFS Head V5: inverted order -> image1=body/base, image2=face/head donor [oai_citation:3‡huggingface.co](https://huggingface.co/Alissonerdx/BFS-Best-Face-Swap)
|
| 268 |
+
MULTI_INPUT_SPECS = {
|
| 269 |
+
"AnyPose": {
|
| 270 |
+
"image1_label": "Image 1 (Character / base)",
|
| 271 |
+
"image2_label": "Image 2 (Pose reference)",
|
| 272 |
+
"help": "AnyPose needs 2 images: Image 1 is the character/base, Image 2 is the pose you want to copy.",
|
| 273 |
+
},
|
| 274 |
+
"BFS-Best-FaceSwap": {
|
| 275 |
+
"image1_label": "Image 1 (Body / base)",
|
| 276 |
+
"image2_label": "Image 2 (Face/Head donor)",
|
| 277 |
+
"help": "BFS Head V5 needs 2 images (inverted order): Image 1 is the body/base, Image 2 is the face/head donor.",
|
| 278 |
+
},
|
| 279 |
}
|
| 280 |
|
| 281 |
+
# Track loaded adapters by adapter_name
|
| 282 |
LOADED_ADAPTERS = set()
|
| 283 |
|
| 284 |
+
# -------------------------
|
| 285 |
+
# Utility: dimensions / resolution
|
| 286 |
+
# -------------------------
|
| 287 |
+
|
| 288 |
def _round8(x: int) -> int:
|
| 289 |
return max(8, (int(x) // 8) * 8)
|
| 290 |
|
| 291 |
def compute_dimensions(image: Image.Image, long_edge: int) -> tuple[int, int]:
|
|
|
|
| 292 |
w, h = image.size
|
| 293 |
if w >= h:
|
| 294 |
new_w = long_edge
|
|
|
|
| 298 |
new_w = int(round(long_edge * (w / h)))
|
| 299 |
return _round8(new_w), _round8(new_h)
|
| 300 |
|
| 301 |
+
def get_target_long_edge_for_lora(lora_adapter: str) -> int:
|
| 302 |
+
spec = ADAPTER_SPECS.get(lora_adapter, {})
|
| 303 |
+
return int(spec.get("target_long_edge", 1024))
|
| 304 |
+
|
| 305 |
+
# -------------------------
|
| 306 |
+
# UI helpers
|
| 307 |
+
# -------------------------
|
| 308 |
|
| 309 |
+
def ui_on_lora_change(selected_lora: str, current_prompt: str):
|
| 310 |
+
# Prompt autofill (only if empty)
|
| 311 |
+
new_prompt = current_prompt
|
| 312 |
+
if selected_lora != NONE_LORA:
|
| 313 |
+
preset = LORA_PRESET_PROMPTS.get(selected_lora, "")
|
| 314 |
+
if preset and (current_prompt is None or str(current_prompt).strip() == ""):
|
| 315 |
+
new_prompt = preset
|
| 316 |
+
|
| 317 |
+
# Multi-image UI toggles
|
| 318 |
+
if selected_lora in MULTI_INPUT_SPECS:
|
| 319 |
+
spec = (MULTI_INPUT_SPECS[selected_lora] or {})
|
| 320 |
+
img1_label = spec.get("image1_label", "Image 1")
|
| 321 |
+
img2_label = spec.get("image2_label", "Image 2")
|
| 322 |
+
help_txt = spec.get("help", "")
|
| 323 |
+
return (
|
| 324 |
+
gr.update(value=new_prompt),
|
| 325 |
+
gr.update(visible=True, label=img1_label),
|
| 326 |
+
gr.update(visible=True, label=img2_label),
|
| 327 |
+
gr.update(visible=True, value=help_txt),
|
| 328 |
+
)
|
| 329 |
|
| 330 |
+
# Default: single image only
|
| 331 |
+
return (
|
| 332 |
+
gr.update(value=new_prompt),
|
| 333 |
+
gr.update(visible=True, label="Upload Image"),
|
| 334 |
+
gr.update(visible=False),
|
| 335 |
+
gr.update(visible=False, value=""),
|
| 336 |
+
)
|
| 337 |
|
| 338 |
+
# -------------------------
|
| 339 |
+
# LoRA loading + activation
|
| 340 |
+
# -------------------------
|
| 341 |
|
| 342 |
def _ensure_loaded_and_get_active_adapters(selected_lora: str):
|
| 343 |
spec = ADAPTER_SPECS.get(selected_lora)
|
|
|
|
| 392 |
|
| 393 |
return adapter_names, adapter_weights
|
| 394 |
|
| 395 |
+
# -------------------------
|
| 396 |
+
# Inference
|
| 397 |
+
# -------------------------
|
|
|
|
|
|
|
| 398 |
|
| 399 |
@spaces.GPU
|
| 400 |
def infer(
|
| 401 |
+
input_image_1,
|
| 402 |
+
input_image_2,
|
| 403 |
prompt,
|
| 404 |
lora_adapter,
|
| 405 |
seed,
|
|
|
|
| 412 |
if torch.cuda.is_available():
|
| 413 |
torch.cuda.empty_cache()
|
| 414 |
|
| 415 |
+
if input_image_1 is None:
|
| 416 |
+
raise gr.Error("Please upload Image 1.")
|
| 417 |
+
|
| 418 |
+
# Determine whether we need a second image
|
| 419 |
+
needs_two = lora_adapter in MULTI_INPUT_SPECS
|
| 420 |
+
if needs_two and input_image_2 is None:
|
| 421 |
+
raise gr.Error("This LoRA needs Image 2. Please upload Image 2 as well.")
|
| 422 |
|
| 423 |
+
# Handle "None" LoRA
|
| 424 |
if lora_adapter == NONE_LORA:
|
| 425 |
try:
|
| 426 |
pipe.set_adapters([], adapter_weights=[])
|
|
|
|
| 435 |
seed = random.randint(0, MAX_SEED)
|
| 436 |
|
| 437 |
generator = torch.Generator(device=device).manual_seed(seed)
|
| 438 |
+
|
| 439 |
negative_prompt = (
|
| 440 |
"worst quality, low quality, bad anatomy, bad hands, text, error, missing fingers, "
|
| 441 |
"extra digit, fewer digits, cropped, jpeg artifacts, signature, watermark, username, blurry"
|
| 442 |
)
|
| 443 |
|
| 444 |
+
img1 = input_image_1.convert("RGB")
|
| 445 |
+
img2 = input_image_2.convert("RGB") if input_image_2 is not None else None
|
| 446 |
|
| 447 |
+
# Resolution based on Image 1 (base/body)
|
| 448 |
target_long_edge = get_target_long_edge_for_lora(lora_adapter)
|
| 449 |
+
width, height = compute_dimensions(img1, target_long_edge)
|
| 450 |
+
|
| 451 |
+
# Build image input for pipeline
|
| 452 |
+
if needs_two:
|
| 453 |
+
# AnyPose expects: [image1(character/base), image2(pose)] [oai_citation:4‡huggingface.co](https://huggingface.co/lilylilith/AnyPose)
|
| 454 |
+
# BFS Head V5 expects: [image1(body/base), image2(face)] inverted order [oai_citation:5‡huggingface.co](https://huggingface.co/Alissonerdx/BFS-Best-Face-Swap)
|
| 455 |
+
pipe_image = [img1, img2]
|
| 456 |
+
else:
|
| 457 |
+
pipe_image = img1
|
| 458 |
|
| 459 |
try:
|
| 460 |
result = pipe(
|
| 461 |
+
image=pipe_image,
|
| 462 |
prompt=prompt,
|
| 463 |
negative_prompt=negative_prompt,
|
| 464 |
height=height,
|
|
|
|
| 476 |
torch.cuda.empty_cache()
|
| 477 |
|
| 478 |
@spaces.GPU
|
| 479 |
+
def infer_example(input_image_1, input_image_2, prompt, lora_adapter):
|
| 480 |
+
if input_image_1 is None:
|
| 481 |
return None, 0
|
| 482 |
|
|
|
|
| 483 |
guidance_scale = 1.0
|
| 484 |
steps = 4
|
| 485 |
+
result, seed = infer(input_image_1, input_image_2, prompt, lora_adapter, 0, True, guidance_scale, steps)
|
| 486 |
return result, seed
|
| 487 |
|
| 488 |
+
# -------------------------
|
| 489 |
+
# UI
|
| 490 |
+
# -------------------------
|
| 491 |
+
|
| 492 |
css = """
|
| 493 |
#col-container {
|
| 494 |
margin: 0 auto;
|
|
|
|
| 508 |
|
| 509 |
with gr.Row(equal_height=True):
|
| 510 |
with gr.Column():
|
| 511 |
+
input_image_1 = gr.Image(label="Upload Image", type="pil", height=290)
|
| 512 |
+
input_image_2 = gr.Image(label="Image 2 (only for certain LoRAs)", type="pil", height=290, visible=False)
|
| 513 |
+
|
| 514 |
+
multi_help = gr.Markdown(value="", visible=False)
|
| 515 |
|
| 516 |
prompt = gr.Text(
|
| 517 |
label="Edit Prompt",
|
|
|
|
| 538 |
guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
|
| 539 |
steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4)
|
| 540 |
|
| 541 |
+
# LoRA change: prompt autofill + toggle 2nd image visibility/labels
|
| 542 |
lora_adapter.change(
|
| 543 |
+
fn=ui_on_lora_change,
|
| 544 |
inputs=[lora_adapter, prompt],
|
| 545 |
+
outputs=[prompt, input_image_1, input_image_2, multi_help],
|
| 546 |
)
|
| 547 |
|
| 548 |
+
# Examples now include Image 2 as well (kept as duplicate for single-image examples)
|
| 549 |
gr.Examples(
|
| 550 |
examples=[
|
| 551 |
+
["examples/1.jpg", "examples/1.jpg", "Transform into anime.", "Photo-to-Anime"],
|
| 552 |
+
["examples/5.jpg", "examples/5.jpg", "Remove shadows and relight the image using soft lighting.", "Light-Restoration"],
|
| 553 |
+
["examples/4.jpg", "examples/4.jpg", "Use a subtle golden-hour filter with smooth light diffusion.", "Relight"],
|
| 554 |
+
["examples/2.jpeg", "examples/2.jpeg", "Rotate the camera 45 degrees to the left.", "Multiple-Angles"],
|
| 555 |
+
["examples/12.jpg", "examples/12.jpg", "flatcolor Desaturate the image and lower the contrast to create a flat, ungraded look similar to a camera log profile. Preserve details in the highlights and shadows.", "Flat-Log"],
|
| 556 |
+
["examples/7.jpg", "examples/7.jpg", "Light source from the Right Rear", "Multi-Angle-Lighting"],
|
| 557 |
+
["examples/10.jpeg", "examples/10.jpeg", "Upscale the image.", "Upscale-Image"],
|
| 558 |
+
["examples/7.jpg", "examples/7.jpg", "Light source from the Below", "Multi-Angle-Lighting"],
|
| 559 |
+
["examples/2.jpeg", "examples/2.jpeg", "Switch the camera to a top-down right corner view.", "Multiple-Angles"],
|
| 560 |
+
["examples/9.jpg", "examples/9.jpg", "The camera moves slightly forward as sunlight breaks through the clouds, casting a soft glow around the character's silhouette in the mist. Realistic cinematic style, atmospheric depth.", "Next-Scene"],
|
| 561 |
+
["examples/8.jpg", "examples/8.jpg", "Make the subjects skin details more prominent and natural.", "Edit-Skin"],
|
| 562 |
+
["examples/6.jpg", "examples/6.jpg", "Switch the camera to a bottom-up view.", "Multiple-Angles"],
|
| 563 |
+
["examples/6.jpg", "examples/6.jpg", "Rotate the camera 180 degrees upside down.", "Multiple-Angles"],
|
| 564 |
+
["examples/4.jpg", "examples/4.jpg", "Rotate the camera 45 degrees to the right.", "Multiple-Angles"],
|
| 565 |
+
["examples/4.jpg", "examples/4.jpg", "Switch the camera to a top-down view.", "Multiple-Angles"],
|
| 566 |
+
["examples/4.jpg", "examples/4.jpg", "Switch the camera to a wide-angle lens.", "Multiple-Angles"],
|
| 567 |
+
["examples/11.jpg", "examples/11.jpg", "Upscale this picture to 4K resolution.", "Upscale2K"],
|
| 568 |
],
|
| 569 |
+
inputs=[input_image_1, input_image_2, prompt, lora_adapter],
|
| 570 |
outputs=[output_image, seed],
|
| 571 |
fn=infer_example,
|
| 572 |
cache_examples=False,
|
|
|
|
| 575 |
|
| 576 |
run_button.click(
|
| 577 |
fn=infer,
|
| 578 |
+
inputs=[input_image_1, input_image_2, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
|
| 579 |
outputs=[output_image, seed],
|
| 580 |
)
|
| 581 |
|