Update app.py
Browse files
app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import base64
|
| 2 |
import os
|
| 3 |
import urllib.parse
|
|
@@ -77,7 +78,7 @@ caption_gen_pipe = hf_pipeline("text-generation", model="Qwen/Qwen2.5-0.5B-Instr
|
|
| 77 |
vqa_processor = BlipProcessor.from_pretrained("Salesforce/blip-vqa-base")
|
| 78 |
vqa_model = BlipForQuestionAnswering.from_pretrained("Salesforce/blip-vqa-base").to(DEVICE)
|
| 79 |
|
| 80 |
-
print("Loading segmentation and Stable Diffusion pipelines...")
|
| 81 |
seg_processor = SegformerImageProcessor.from_pretrained("mattmdjaga/segformer_b2_clothes")
|
| 82 |
seg_model = AutoModelForSemanticSegmentation.from_pretrained("mattmdjaga/segformer_b2_clothes").to(DEVICE)
|
| 83 |
inpaint_pipe = StableDiffusionInpaintPipeline.from_pretrained(
|
|
@@ -143,18 +144,28 @@ def pil_to_base64(img, max_size=280):
|
|
| 143 |
return base64.b64encode(buf.getvalue()).decode("utf-8")
|
| 144 |
|
| 145 |
# ---------------------------------------------------------------------------
|
| 146 |
-
# EMBEDDING + FAISS SEARCH
|
| 147 |
# ---------------------------------------------------------------------------
|
| 148 |
@torch.no_grad()
|
| 149 |
def embed_query_image(pil_image):
|
| 150 |
inputs = win_processor(images=pil_image, return_tensors="pt").to(DEVICE)
|
| 151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
return feats.cpu().numpy().astype("float32")
|
| 153 |
|
| 154 |
@torch.no_grad()
|
| 155 |
def embed_query_text(sentence):
|
| 156 |
inputs = win_processor(text=[sentence], return_tensors="pt", padding=True, truncation=True).to(DEVICE)
|
| 157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
return feats.cpu().numpy().astype("float32")
|
| 159 |
|
| 160 |
def build_feature_sentence(skin_tone, undertone, style_preference, gender=None, age_group=None):
|
|
@@ -192,7 +203,7 @@ def faiss_filtered_search(query_emb, top_k=3, exclude_idx=None, gender=None, age
|
|
| 192 |
return np.array(kept_i), df.iloc[kept_i], np.array(kept_d)
|
| 193 |
|
| 194 |
# ---------------------------------------------------------------------------
|
| 195 |
-
# PATTERN LOGIC & COMPONENT BUILDERS
|
| 196 |
# ---------------------------------------------------------------------------
|
| 197 |
def generate_stylist_caption(row):
|
| 198 |
user_prompt = (
|
|
@@ -236,8 +247,6 @@ def generate_new_outfit_image(pil_image, row, target_gender=None):
|
|
| 236 |
resized_protect_img = Image.fromarray(protect.astype(np.uint8) * 255).resize((head_w, head_h), resample=Image.NEAREST)
|
| 237 |
|
| 238 |
canvas = Image.new("RGB", (canvas_w, canvas_h), color=(240, 238, 235))
|
| 239 |
-
paste_x = (canvas_w - head_w) // 2
|
| 240 |
-
paste_y = int(canvas_h * 0.03)
|
| 241 |
canvas.paste(resized_face_crop, (paste_x, paste_y))
|
| 242 |
|
| 243 |
mask_arr = np.full((canvas_h, canvas_w), 255, dtype=np.uint8)
|
|
@@ -252,7 +261,6 @@ def generate_new_outfit_image(pil_image, row, target_gender=None):
|
|
| 252 |
generated = inpaint_pipe(prompt=prompt, image=canvas, mask_image=mask, num_inference_steps=25, height=canvas_h, width=canvas_w).images[0]
|
| 253 |
return Image.composite(generated, canvas, mask), prompt
|
| 254 |
|
| 255 |
-
# --- CRITICAL FIX: DEFINING THE MISSING HEADER AND PROFILE CARD BUILDER ---
|
| 256 |
def build_style_card_html(row, caption):
|
| 257 |
colors = [c.strip() for c in str(row["recommended_colors"]).split(",") if c.strip()][:4]
|
| 258 |
swatches = "".join(
|
|
@@ -285,7 +293,6 @@ def to_shop_link(retailer, value, gender=None):
|
|
| 285 |
return f"https://www.google.com/search?q={retailer}+{query_enc}"
|
| 286 |
|
| 287 |
def build_outfit_component_cards_html(row):
|
| 288 |
-
"""Generates the clean 6 core component cards layout mirroring LookMatch perfectly"""
|
| 289 |
colors = [c.strip() for c in str(row["recommended_colors"]).split(",") if c.strip()] or ["neutral"]
|
| 290 |
components = [
|
| 291 |
("TOP", row.get("outfit_top", "Top"), "zara", "search_query_zara"),
|
|
@@ -440,7 +447,8 @@ h1, h2, h3, p, span, label, input, select, textarea, button { color: #2C2A29 !im
|
|
| 440 |
# ---------------------------------------------------------------------------
|
| 441 |
# INTERFACE BUILD
|
| 442 |
# ---------------------------------------------------------------------------
|
| 443 |
-
|
|
|
|
| 444 |
gr.HTML("""
|
| 445 |
<div style="text-align:center; padding:24px 20px 10px;">
|
| 446 |
<div style="font-size:11px; font-weight:700; color:#D2527F; letter-spacing:.18em; text-transform:uppercase; margin-bottom:8px;">Personal Color Styling</div>
|
|
@@ -471,8 +479,9 @@ with gr.Blocks(title="Personal Color Styling", css=CUSTOM_CSS, theme=gr.themes.S
|
|
| 471 |
[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 472 |
)
|
| 473 |
|
|
|
|
| 474 |
gr.Examples(
|
| 475 |
-
examples=[[p,
|
| 476 |
inputs=[photo_in, gender_a, age_a, question_a],
|
| 477 |
outputs=[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 478 |
fn=recommend_from_photo,
|
|
@@ -509,4 +518,5 @@ with gr.Blocks(title="Personal Color Styling", css=CUSTOM_CSS, theme=gr.themes.S
|
|
| 509 |
)
|
| 510 |
|
| 511 |
if __name__ == "__main__":
|
| 512 |
-
|
|
|
|
|
|
| 1 |
+
|
| 2 |
import base64
|
| 3 |
import os
|
| 4 |
import urllib.parse
|
|
|
|
| 78 |
vqa_processor = BlipProcessor.from_pretrained("Salesforce/blip-vqa-base")
|
| 79 |
vqa_model = BlipForQuestionAnswering.from_pretrained("Salesforce/blip-vqa-base").to(DEVICE)
|
| 80 |
|
| 81 |
+
print("Loading clothing segmentation and Stable Diffusion pipelines...")
|
| 82 |
seg_processor = SegformerImageProcessor.from_pretrained("mattmdjaga/segformer_b2_clothes")
|
| 83 |
seg_model = AutoModelForSemanticSegmentation.from_pretrained("mattmdjaga/segformer_b2_clothes").to(DEVICE)
|
| 84 |
inpaint_pipe = StableDiffusionInpaintPipeline.from_pretrained(
|
|
|
|
| 144 |
return base64.b64encode(buf.getvalue()).decode("utf-8")
|
| 145 |
|
| 146 |
# ---------------------------------------------------------------------------
|
| 147 |
+
# EMBEDDING + FAISS SEARCH (תיקון חילוץ הוקטורים מתוך אובייקט ה-CLIP)
|
| 148 |
# ---------------------------------------------------------------------------
|
| 149 |
@torch.no_grad()
|
| 150 |
def embed_query_image(pil_image):
|
| 151 |
inputs = win_processor(images=pil_image, return_tensors="pt").to(DEVICE)
|
| 152 |
+
outputs = win_model.get_image_features(**inputs)
|
| 153 |
+
# תיקון קריטי: חילוץ וקטור המאפיינים האמיתי מתוך ה-BaseModelOutput
|
| 154 |
+
if hasattr(outputs, "image_embeds"):
|
| 155 |
+
feats = outputs.image_embeds
|
| 156 |
+
else:
|
| 157 |
+
feats = outputs
|
| 158 |
return feats.cpu().numpy().astype("float32")
|
| 159 |
|
| 160 |
@torch.no_grad()
|
| 161 |
def embed_query_text(sentence):
|
| 162 |
inputs = win_processor(text=[sentence], return_tensors="pt", padding=True, truncation=True).to(DEVICE)
|
| 163 |
+
outputs = win_model.get_text_features(**inputs)
|
| 164 |
+
# תיקון קריטי: חילוץ וקטור המאפיינים האמיתי מתוך ה-BaseModelOutput
|
| 165 |
+
if hasattr(outputs, "text_embeds"):
|
| 166 |
+
feats = outputs.text_embeds
|
| 167 |
+
else:
|
| 168 |
+
feats = outputs
|
| 169 |
return feats.cpu().numpy().astype("float32")
|
| 170 |
|
| 171 |
def build_feature_sentence(skin_tone, undertone, style_preference, gender=None, age_group=None):
|
|
|
|
| 203 |
return np.array(kept_i), df.iloc[kept_i], np.array(kept_d)
|
| 204 |
|
| 205 |
# ---------------------------------------------------------------------------
|
| 206 |
+
# PATTERN LOGIC & COMPONENT BUILDERS
|
| 207 |
# ---------------------------------------------------------------------------
|
| 208 |
def generate_stylist_caption(row):
|
| 209 |
user_prompt = (
|
|
|
|
| 247 |
resized_protect_img = Image.fromarray(protect.astype(np.uint8) * 255).resize((head_w, head_h), resample=Image.NEAREST)
|
| 248 |
|
| 249 |
canvas = Image.new("RGB", (canvas_w, canvas_h), color=(240, 238, 235))
|
|
|
|
|
|
|
| 250 |
canvas.paste(resized_face_crop, (paste_x, paste_y))
|
| 251 |
|
| 252 |
mask_arr = np.full((canvas_h, canvas_w), 255, dtype=np.uint8)
|
|
|
|
| 261 |
generated = inpaint_pipe(prompt=prompt, image=canvas, mask_image=mask, num_inference_steps=25, height=canvas_h, width=canvas_w).images[0]
|
| 262 |
return Image.composite(generated, canvas, mask), prompt
|
| 263 |
|
|
|
|
| 264 |
def build_style_card_html(row, caption):
|
| 265 |
colors = [c.strip() for c in str(row["recommended_colors"]).split(",") if c.strip()][:4]
|
| 266 |
swatches = "".join(
|
|
|
|
| 293 |
return f"https://www.google.com/search?q={retailer}+{query_enc}"
|
| 294 |
|
| 295 |
def build_outfit_component_cards_html(row):
|
|
|
|
| 296 |
colors = [c.strip() for c in str(row["recommended_colors"]).split(",") if c.strip()] or ["neutral"]
|
| 297 |
components = [
|
| 298 |
("TOP", row.get("outfit_top", "Top"), "zara", "search_query_zara"),
|
|
|
|
| 447 |
# ---------------------------------------------------------------------------
|
| 448 |
# INTERFACE BUILD
|
| 449 |
# ---------------------------------------------------------------------------
|
| 450 |
+
# CRITICAL FIX: Moving custom CSS and theme injection parameters directly to the final launch function to comply with Gradio 6
|
| 451 |
+
with gr.Blocks(title="Personal Color Styling") as demo:
|
| 452 |
gr.HTML("""
|
| 453 |
<div style="text-align:center; padding:24px 20px 10px;">
|
| 454 |
<div style="font-size:11px; font-weight:700; color:#D2527F; letter-spacing:.18em; text-transform:uppercase; margin-bottom:8px;">Personal Color Styling</div>
|
|
|
|
| 479 |
[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 480 |
)
|
| 481 |
|
| 482 |
+
# CRITICAL FIX: All expected positional inputs must map corresponding elements to prevent load_example mismatches
|
| 483 |
gr.Examples(
|
| 484 |
+
examples=[[p, "woman", "adult", "What style would suit me best?"] for p in SAMPLE_PHOTOS],
|
| 485 |
inputs=[photo_in, gender_a, age_a, question_a],
|
| 486 |
outputs=[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 487 |
fn=recommend_from_photo,
|
|
|
|
| 518 |
)
|
| 519 |
|
| 520 |
if __name__ == "__main__":
|
| 521 |
+
# CRITICAL FIX: Safe execution of Gradio 6 theme parameters
|
| 522 |
+
demo.launch(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="amber"))
|