jhh6576 commited on
Commit
d6f0215
·
verified ·
1 Parent(s): 13e93f7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -55
app.py CHANGED
@@ -5,115 +5,126 @@ import gc
5
  from diffusers import FluxImg2ImgPipeline
6
  from PIL import Image
7
 
8
- # --- 1. SETUP: TURBO MODE (CPU) ---
9
- # "Schnell" means "Fast" in German. This IS the Turbo model for Flux.
10
- # It is designed to look good in just 4 steps.
11
  MODEL_ID = "black-forest-labs/FLUX.1-schnell"
12
  DEVICE = "cpu"
 
13
  DTYPE = torch.bfloat16
14
 
15
- print(f"Loading Flux Turbo ({MODEL_ID}) on {DEVICE}...")
16
 
17
- # Load the pipeline
18
- pipe = FluxImg2ImgPipeline.from_pretrained(
19
- MODEL_ID,
20
- torch_dtype=DTYPE
21
- )
 
 
 
22
 
23
- print("Turbo Model Loaded.")
24
-
25
- # --- 2. THE FACE LOCK (Symmetry Injection) ---
26
- # This mirrors the pixels of the 'Good Side' BEFORE the AI edits it.
27
- def inject_symmetry(image, side="Left"):
28
  if image is None: return None
29
 
 
30
  img_array = np.array(image.convert("RGB"))
31
  height, width, _ = img_array.shape
32
  midpoint = width // 2
33
 
 
34
  if side == "Left":
35
- # Keep Left, Mirror to Right
36
  left_side = img_array[:, :midpoint, :]
37
  right_side = np.fliplr(left_side)
38
- # Fix odd pixel widths
 
39
  if right_side.shape[1] != left_side.shape[1]:
40
  right_side = right_side[:, :left_side.shape[1], :]
 
41
  locked_data = np.concatenate((left_side, right_side), axis=1)
42
  else:
43
- # Keep Right, Mirror to Left
44
  right_side = img_array[:, midpoint:, :]
45
  left_side = np.fliplr(right_side)
46
  locked_data = np.concatenate((left_side, right_side), axis=1)
47
 
48
  return Image.fromarray(locked_data)
49
 
50
- # --- 3. INFERENCE (4 STEPS) ---
51
  def process_image(prompt, image_input, side, strength, seed):
52
  if image_input is None:
53
  raise gr.Error("Please upload an image.")
54
 
55
- # A. Memory Cleanup
56
  gc.collect()
57
 
58
- # B. Inject Symmetry (The Lock)
59
- print("Step 1: Injecting Symmetry Constraint...")
60
- processed_image = inject_symmetry(image_input, side)
61
 
62
- # C. Resize for CPU Speed (Critical)
63
- # 512x512 is FAST. 1024x1024 is SLOW.
64
- # We resize to maintain aspect ratio but keep max dimension 768.
65
  w, h = processed_image.size
66
  scale = 768 / max(w, h)
67
  new_w = int((w * scale) // 16 * 16)
68
  new_h = int((h * scale) // 16 * 16)
69
- processed_image = processed_image.resize((new_w, new_h))
70
 
71
- print(f"Step 2: Turbo Generation ({new_w}x{new_h}) with prompt: '{prompt}'")
 
 
72
 
73
- # D. Run Flux (Turbo Settings)
 
74
  generator = torch.Generator(DEVICE).manual_seed(int(seed))
75
 
76
- result = pipe(
77
- prompt=prompt,
78
- image=processed_image,
79
- strength=strength, # Controls how much we edit the face
80
- num_inference_steps=4, # <--- HARDCODED TURBO SPEED (4 Steps)
81
- guidance_scale=0.0, # Schnell does not use guidance
82
- generator=generator
83
- ).images[0]
84
 
85
- return result
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
- # --- 4. UI ---
88
  css = """
89
- #col-container { max-width: 900px; margin: 0 auto; }
 
90
  """
91
 
92
- with gr.Blocks(css=css) as demo:
93
  with gr.Column(elem_id="col-container"):
94
- gr.Markdown("# Flux Face Symmetry (Turbo CPU)")
95
- gr.Markdown("Locked to **4 Steps** for speed. Uses **Symmetry Injection** to lock identity.")
96
 
97
  with gr.Row():
98
  with gr.Column():
99
  img_in = gr.Image(label="Upload Face", type="pil")
100
 
101
- # Controls
102
- prompt = gr.Text(label="Editing Prompt", value="perfect symmetry, smooth skin, photorealistic, 8k")
103
- side = gr.Radio(["Left", "Right"], label="Keep Side", value="Left")
104
-
105
- # Strength Explanation:
106
- # 0.25 = Locks face perfectly, just fixes seams.
107
- # 0.40 = Allows text edits (like 'add glasses') but changes face slightly.
108
- strength = gr.Slider(0.1, 0.6, value=0.30, step=0.01, label="Editing Strength (0.3 = Lock, 0.5 = Edit)")
109
-
110
- seed = gr.Number(label="Seed", value=42)
111
 
112
- btn = gr.Button("Generate (4 Steps)", variant="primary")
113
 
114
  with gr.Column():
115
- img_out = gr.Image(label="Turbo Result")
116
 
117
  btn.click(process_image, inputs=[prompt, img_in, side, strength, seed], outputs=[img_out])
118
 
119
- demo.queue().launch()
 
 
5
  from diffusers import FluxImg2ImgPipeline
6
  from PIL import Image
7
 
8
+ # 1. SETUP: LOAD FLUX TURBO (CPU OPTIMIZED)
 
 
9
  MODEL_ID = "black-forest-labs/FLUX.1-schnell"
10
  DEVICE = "cpu"
11
+ # We use bfloat16 for speed and lower memory usage
12
  DTYPE = torch.bfloat16
13
 
14
+ print(f"--- Loading {MODEL_ID} on {DEVICE} ---")
15
 
16
+ try:
17
+ pipe = FluxImg2ImgPipeline.from_pretrained(
18
+ MODEL_ID,
19
+ torch_dtype=DTYPE
20
+ )
21
+ print("--- Model Loaded Successfully ---")
22
+ except Exception as e:
23
+ print(f"Error loading model: {e}")
24
 
25
+ # 2. THE UNET SYMMETRY INJECTION
26
+ # This function acts as a constraint injection.
27
+ # It locks the geometric latents before the UNet processing starts.
28
+ def inject_symmetry_lock(image, side="Left"):
 
29
  if image is None: return None
30
 
31
+ # Convert to standard RGB array
32
  img_array = np.array(image.convert("RGB"))
33
  height, width, _ = img_array.shape
34
  midpoint = width // 2
35
 
36
+ # Execute Geometric Locking
37
  if side == "Left":
38
+ # Lock Left, Mirror to Right
39
  left_side = img_array[:, :midpoint, :]
40
  right_side = np.fliplr(left_side)
41
+
42
+ # Handle odd widths (pixel precision)
43
  if right_side.shape[1] != left_side.shape[1]:
44
  right_side = right_side[:, :left_side.shape[1], :]
45
+
46
  locked_data = np.concatenate((left_side, right_side), axis=1)
47
  else:
48
+ # Lock Right, Mirror to Left
49
  right_side = img_array[:, midpoint:, :]
50
  left_side = np.fliplr(right_side)
51
  locked_data = np.concatenate((left_side, right_side), axis=1)
52
 
53
  return Image.fromarray(locked_data)
54
 
55
+ # 3. GENERATION LOOP (TURBO 4-STEPS)
56
  def process_image(prompt, image_input, side, strength, seed):
57
  if image_input is None:
58
  raise gr.Error("Please upload an image.")
59
 
60
+ # A. Garbage Collection (Prevent RAM Freeze)
61
  gc.collect()
62
 
63
+ # B. Inject Symmetry
64
+ print(">>> Phase 1: Injecting Symmetry Constraints")
65
+ processed_image = inject_symmetry_lock(image_input, side)
66
 
67
+ # C. CPU Optimization (Resize)
68
+ # We force the image to be 768px max to prevent 15-minute wait times.
 
69
  w, h = processed_image.size
70
  scale = 768 / max(w, h)
71
  new_w = int((w * scale) // 16 * 16)
72
  new_h = int((h * scale) // 16 * 16)
 
73
 
74
+ if new_w != w or new_h != h:
75
+ print(f">>> Resizing to {new_w}x{new_h} for CPU speed")
76
+ processed_image = processed_image.resize((new_w, new_h))
77
 
78
+ # D. Flux Inference
79
+ print(f">>> Phase 2: Running Flux (4 Steps) - Prompt: {prompt}")
80
  generator = torch.Generator(DEVICE).manual_seed(int(seed))
81
 
82
+ # Strength Logic:
83
+ # 0.25 - 0.30 is the "Golden Zone" for Face Locking + Seam Fixing
 
 
 
 
 
 
84
 
85
+ try:
86
+ result = pipe(
87
+ prompt=prompt,
88
+ image=processed_image,
89
+ strength=strength,
90
+ num_inference_steps=4, # Hardcoded Turbo Steps
91
+ guidance_scale=0.0, # Schnell uses 0 guidance
92
+ generator=generator
93
+ ).images[0]
94
+
95
+ return result
96
+ except Exception as e:
97
+ return None
98
 
99
+ # 4. USER INTERFACE
100
  css = """
101
+ #col-container { max-width: 900px; margin: 0 auto; background-color: #f9f9f9; padding: 20px; border-radius: 10px; }
102
+ h1 { text-align: center; }
103
  """
104
 
105
+ with gr.Blocks() as demo:
106
  with gr.Column(elem_id="col-container"):
107
+ gr.Markdown("# Flux 4B Face Symmetry (CPU Turbo)")
108
+ gr.Markdown("Status: **Running on CPU** | Mode: **Identity Lock**")
109
 
110
  with gr.Row():
111
  with gr.Column():
112
  img_in = gr.Image(label="Upload Face", type="pil")
113
 
114
+ with gr.Group():
115
+ side = gr.Radio(["Left", "Right"], label="Select Better Side", value="Left")
116
+ prompt = gr.Text(label="Editing Prompt", value="perfect symmetry, photorealistic, 8k, smooth skin")
117
+
118
+ # Range restricted to ensure Identity Lock
119
+ strength = gr.Slider(0.15, 0.45, value=0.28, step=0.01, label="Denoise Strength (Keep < 0.35 to lock ID)")
120
+ seed = gr.Number(label="Seed", value=12345)
 
 
 
121
 
122
+ btn = gr.Button("Generate (Fast)", variant="primary")
123
 
124
  with gr.Column():
125
+ img_out = gr.Image(label="Symmetrical Result")
126
 
127
  btn.click(process_image, inputs=[prompt, img_in, side, strength, seed], outputs=[img_out])
128
 
129
+ # Fix for the CSS warning: We pass CSS here
130
+ demo.launch(css=css)