File size: 3,498 Bytes
7f4c99b
 
ca0ffa2
 
9b51150
ca0ffa2
7f4c99b
efe0511
9b51150
ca0ffa2
 
025a849
d6f0215
efe0511
 
7f4c99b
efe0511
d6f0215
ca0ffa2
 
 
 
 
9b51150
ca0ffa2
 
 
e3af6fd
 
9b51150
ca0ffa2
 
e3af6fd
9b51150
e3af6fd
b0751aa
efe0511
e3af6fd
ca0ffa2
e3af6fd
02b00c0
ca0ffa2
9b51150
efe0511
 
d6f0215
e3af6fd
efe0511
 
e3af6fd
efe0511
 
 
e3af6fd
 
9b51150
efe0511
 
9b51150
efe0511
 
ca0ffa2
 
d6f0215
 
 
 
 
efe0511
 
d6f0215
 
 
 
 
efe0511
d6f0215
9b51150
efe0511
b0751aa
efe0511
7f4c99b
 
efe0511
7f4c99b
efe0511
 
9b51150
7f4c99b
e3af6fd
 
efe0511
 
 
 
 
ca0ffa2
e3af6fd
efe0511
e3af6fd
 
7f4c99b
efe0511
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import gradio as gr
import torch
import numpy as np
import gc
from diffusers import FluxImg2ImgPipeline
from PIL import Image

# 1. SETUP
MODEL_ID = "black-forest-labs/FLUX.1-schnell"
DEVICE = "cpu"
DTYPE = torch.bfloat16

print(f"--- Loading {MODEL_ID} on {DEVICE} ---")
pipe = FluxImg2ImgPipeline.from_pretrained(MODEL_ID, torch_dtype=DTYPE)
print("--- Model Loaded ---")

# 2. SYMMETRY LOGIC
def inject_symmetry_lock(image, side="Left"):
    if image is None: return None
    img_array = np.array(image.convert("RGB"))
    height, width, _ = img_array.shape
    midpoint = width // 2
    
    if side == "Left":
        left_side = img_array[:, :midpoint, :]
        right_side = np.fliplr(left_side)
        if right_side.shape[1] != left_side.shape[1]:
             right_side = right_side[:, :left_side.shape[1], :]
        locked_data = np.concatenate((left_side, right_side), axis=1)
    else:
        right_side = img_array[:, midpoint:, :]
        left_side = np.fliplr(right_side)
        locked_data = np.concatenate((left_side, right_side), axis=1)
        
    return Image.fromarray(locked_data)

# 3. GENERATION (SAFE MODE)
def process_image(prompt, image_input, side, strength, seed):
    if image_input is None:
        raise gr.Error("Please upload an image.")
    
    gc.collect()
    
    # Step A: Symmetry
    print(">>> Step 1: Injecting Symmetry")
    processed_image = inject_symmetry_lock(image_input, side)
    
    # Step B: EXTREME RESIZE (512px Max)
    # This is the only way to prevent Timeout on Free CPU
    w, h = processed_image.size
    MAX_SIZE = 512
    scale = MAX_SIZE / max(w, h)
    
    new_w = int((w * scale) // 16 * 16)
    new_h = int((h * scale) // 16 * 16)
    
    print(f">>> Step 2: Resizing to {new_w}x{new_h} to prevent crash...")
    processed_image = processed_image.resize((new_w, new_h))
    
    # Step C: Flux Inference
    print(f">>> Step 3: Running Flux (Prompt: {prompt})")
    generator = torch.Generator(DEVICE).manual_seed(int(seed))
    
    try:
        result = pipe(
            prompt=prompt,
            image=processed_image,
            strength=strength,      
            num_inference_steps=4,  # Turbo Steps
            guidance_scale=0.0,
            generator=generator
        ).images[0]
        
        return result
    except Exception as e:
        print(f"ERROR: {e}")
        return None

# 4. UI
css = """
#col-container { max-width: 900px; margin: 0 auto; background-color: #f0f2f6; padding: 20px; border-radius: 10px; }
"""

with gr.Blocks(css=css) as demo:
    with gr.Column(elem_id="col-container"):
        gr.Markdown("# ⚡ Flux Face Symmetry (Safe Mode)")
        gr.Markdown("Optimized for CPU Stability (Max 512px).")
        
        with gr.Row():
            with gr.Column():
                img_in = gr.Image(label="Upload Face", type="pil")
                side = gr.Radio(["Left", "Right"], label="Best Side", value="Left")
                prompt = gr.Text(label="Prompt", value="perfect symmetry, smooth skin")
                strength = gr.Slider(0.15, 0.45, value=0.25, step=0.01, label="Strength")
                seed = gr.Number(label="Seed", value=123)
                btn = gr.Button("Generate", variant="primary")
            
            with gr.Column():
                img_out = gr.Image(label="Result")

    btn.click(process_image, inputs=[prompt, img_in, side, strength, seed], outputs=[img_out])

# Enable Queue to handle timeouts better
demo.queue(max_size=2).launch()