Update app.py
Browse files
app.py
CHANGED
|
@@ -166,14 +166,13 @@ def generate(face_image, style_image, prompt, negative_prompt,
|
|
| 166 |
gen = None if seed is None or int(seed) < 0 else torch.Generator(device=DEVICE).manual_seed(int(seed))
|
| 167 |
|
| 168 |
# visage → carré 512 pour détection stable
|
|
|
|
| 169 |
face = ImageOps.exif_transpose(face_image).convert("RGB")
|
| 170 |
-
ms = min(face.size)
|
| 171 |
-
x = (face.width - ms) // 2
|
| 172 |
-
y = (face.height - ms) // 2
|
| 173 |
face_sq = face.crop((x, y, x + ms, y + ms)).resize((512, 512), Image.Resampling.LANCZOS)
|
| 174 |
|
| 175 |
# InsightFace : embedding (torch [1,D]) + landmarks
|
| 176 |
-
face_emb, kps_img = extract_face_embed_and_kps(face_sq)
|
| 177 |
|
| 178 |
# IP-Adapter scales
|
| 179 |
try:
|
|
@@ -189,27 +188,21 @@ def generate(face_image, style_image, prompt, negative_prompt,
|
|
| 189 |
if isinstance(cn, (list, tuple)):
|
| 190 |
n_cn = len(cn)
|
| 191 |
else:
|
| 192 |
-
try:
|
| 193 |
-
|
| 194 |
-
except Exception:
|
| 195 |
-
n_cn = 1
|
| 196 |
|
| 197 |
-
image_arg = [kps_img] * n_cn if n_cn > 1 else (
|
| 198 |
-
[kps_img] if isinstance(cn, (list, tuple)) else kps_img
|
| 199 |
-
)
|
| 200 |
scale_val = float(identity_strength)
|
| 201 |
-
scale_arg = [scale_val] * n_cn if n_cn > 1 else (
|
| 202 |
-
[scale_val] if isinstance(cn, (list, tuple)) else scale_val
|
| 203 |
-
)
|
| 204 |
|
| 205 |
-
#
|
| 206 |
gen_kwargs = dict(
|
| 207 |
prompt=(prompt or "").strip(),
|
| 208 |
negative_prompt=(negative_prompt or "").strip(),
|
| 209 |
image=image_arg,
|
| 210 |
-
image_embeds=face_emb,
|
| 211 |
-
added_conditions={"image_embeds": face_emb},
|
| 212 |
-
added_cond_kwargs={"image_embeds": face_emb},
|
| 213 |
controlnet_conditioning_scale=scale_arg,
|
| 214 |
num_inference_steps=int(steps),
|
| 215 |
guidance_scale=float(cfg),
|
|
@@ -217,24 +210,46 @@ def generate(face_image, style_image, prompt, negative_prompt,
|
|
| 217 |
height=int(height),
|
| 218 |
generator=gen,
|
| 219 |
)
|
| 220 |
-
|
| 221 |
-
# Si un style est fourni
|
| 222 |
if HAS_STYLE_ADAPTER and style_image is not None:
|
| 223 |
try:
|
| 224 |
gen_kwargs["ip_adapter_image"] = ImageOps.exif_transpose(style_image).convert("RGB")
|
| 225 |
except Exception as e:
|
| 226 |
print(f"ℹ️ ip_adapter_image ignoré: {e}")
|
| 227 |
|
| 228 |
-
#
|
| 229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
return images[0], "", "\n".join(load_logs)
|
| 231 |
|
| 232 |
except torch.cuda.OutOfMemoryError:
|
| 233 |
return None, "CUDA OOM: baisse la résolution ou les steps.", "\n".join(load_logs)
|
| 234 |
except Exception:
|
|
|
|
| 235 |
return None, "Erreur:\n" + traceback.format_exc(), "\n".join(load_logs)
|
| 236 |
|
| 237 |
|
|
|
|
| 238 |
EX_PROMPT = (
|
| 239 |
"one piece style, Eiichiro Oda style, anime portrait, upper body, pirate outfit, "
|
| 240 |
"clean lineart, cel shading, vibrant colors, expressive eyes, dynamic composition, simple background"
|
|
|
|
| 166 |
gen = None if seed is None or int(seed) < 0 else torch.Generator(device=DEVICE).manual_seed(int(seed))
|
| 167 |
|
| 168 |
# visage → carré 512 pour détection stable
|
| 169 |
+
from PIL import ImageOps
|
| 170 |
face = ImageOps.exif_transpose(face_image).convert("RGB")
|
| 171 |
+
ms = min(face.size); x = (face.width - ms) // 2; y = (face.height - ms) // 2
|
|
|
|
|
|
|
| 172 |
face_sq = face.crop((x, y, x + ms, y + ms)).resize((512, 512), Image.Resampling.LANCZOS)
|
| 173 |
|
| 174 |
# InsightFace : embedding (torch [1,D]) + landmarks
|
| 175 |
+
face_emb, kps_img = extract_face_embed_and_kps(face_sq) # face_emb: torch.Tensor [1,D] on DEVICE/DTYPE
|
| 176 |
|
| 177 |
# IP-Adapter scales
|
| 178 |
try:
|
|
|
|
| 188 |
if isinstance(cn, (list, tuple)):
|
| 189 |
n_cn = len(cn)
|
| 190 |
else:
|
| 191 |
+
try: n_cn = len(cn)
|
| 192 |
+
except Exception: n_cn = 1
|
|
|
|
|
|
|
| 193 |
|
| 194 |
+
image_arg = [kps_img] * n_cn if n_cn > 1 else ([kps_img] if isinstance(cn, (list, tuple)) else kps_img)
|
|
|
|
|
|
|
| 195 |
scale_val = float(identity_strength)
|
| 196 |
+
scale_arg = [scale_val] * n_cn if n_cn > 1 else ([scale_val] if isinstance(cn, (list, tuple)) else scale_val)
|
|
|
|
|
|
|
| 197 |
|
| 198 |
+
# kwargs d’inférence (on met aussi ici pour compat)
|
| 199 |
gen_kwargs = dict(
|
| 200 |
prompt=(prompt or "").strip(),
|
| 201 |
negative_prompt=(negative_prompt or "").strip(),
|
| 202 |
image=image_arg,
|
| 203 |
+
image_embeds=face_emb, # compat pipeline
|
| 204 |
+
added_conditions={"image_embeds": face_emb}, # diffusers ≥ 0.30.x (si propagé)
|
| 205 |
+
added_cond_kwargs={"image_embeds": face_emb}, # diffusers 0.29.x (si propagé)
|
| 206 |
controlnet_conditioning_scale=scale_arg,
|
| 207 |
num_inference_steps=int(steps),
|
| 208 |
guidance_scale=float(cfg),
|
|
|
|
| 210 |
height=int(height),
|
| 211 |
generator=gen,
|
| 212 |
)
|
|
|
|
|
|
|
| 213 |
if HAS_STYLE_ADAPTER and style_image is not None:
|
| 214 |
try:
|
| 215 |
gen_kwargs["ip_adapter_image"] = ImageOps.exif_transpose(style_image).convert("RGB")
|
| 216 |
except Exception as e:
|
| 217 |
print(f"ℹ️ ip_adapter_image ignoré: {e}")
|
| 218 |
|
| 219 |
+
# 🔧 MONKEY-PATCH: injecter image_embeds au niveau du UNet.forward
|
| 220 |
+
orig_forward = pipe.unet.forward
|
| 221 |
+
|
| 222 |
+
def forward_patch(*args, **kwargs):
|
| 223 |
+
# on fusionne proprement pour n’écraser rien
|
| 224 |
+
ac = kwargs.get("added_conditions")
|
| 225 |
+
if ac is None:
|
| 226 |
+
ac = {}
|
| 227 |
+
else:
|
| 228 |
+
ac = dict(ac)
|
| 229 |
+
ac["image_embeds"] = face_emb
|
| 230 |
+
kwargs["added_conditions"] = ac
|
| 231 |
+
# compat pour 0.29.x
|
| 232 |
+
kwargs["added_cond_kwargs"] = ac
|
| 233 |
+
return orig_forward(*args, **kwargs)
|
| 234 |
+
|
| 235 |
+
pipe.unet.forward = forward_patch
|
| 236 |
+
|
| 237 |
+
try:
|
| 238 |
+
images = pipe(**gen_kwargs).images
|
| 239 |
+
finally:
|
| 240 |
+
# toujours restaurer le forward d'origine
|
| 241 |
+
pipe.unet.forward = orig_forward
|
| 242 |
+
|
| 243 |
return images[0], "", "\n".join(load_logs)
|
| 244 |
|
| 245 |
except torch.cuda.OutOfMemoryError:
|
| 246 |
return None, "CUDA OOM: baisse la résolution ou les steps.", "\n".join(load_logs)
|
| 247 |
except Exception:
|
| 248 |
+
import traceback
|
| 249 |
return None, "Erreur:\n" + traceback.format_exc(), "\n".join(load_logs)
|
| 250 |
|
| 251 |
|
| 252 |
+
|
| 253 |
EX_PROMPT = (
|
| 254 |
"one piece style, Eiichiro Oda style, anime portrait, upper body, pirate outfit, "
|
| 255 |
"clean lineart, cel shading, vibrant colors, expressive eyes, dynamic composition, simple background"
|