Update app.py
Browse files
app.py
CHANGED
|
@@ -143,20 +143,23 @@ 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 |
-
outputs = win_model
|
| 152 |
-
|
| 153 |
-
|
|
|
|
| 154 |
feats = outputs.image_embeds
|
| 155 |
-
elif
|
| 156 |
-
feats = outputs
|
|
|
|
|
|
|
| 157 |
else:
|
| 158 |
feats = outputs
|
| 159 |
-
|
| 160 |
if hasattr(feats, "detach"):
|
| 161 |
feats = feats.detach()
|
| 162 |
return feats.cpu().numpy().astype("float32")
|
|
@@ -164,12 +167,15 @@ def embed_query_image(pil_image):
|
|
| 164 |
@torch.no_grad()
|
| 165 |
def embed_query_text(sentence):
|
| 166 |
inputs = win_processor(text=[sentence], return_tensors="pt", padding=True, truncation=True).to(DEVICE)
|
| 167 |
-
outputs = win_model
|
| 168 |
-
|
| 169 |
-
|
|
|
|
| 170 |
feats = outputs.text_embeds
|
| 171 |
-
elif
|
| 172 |
-
feats = outputs
|
|
|
|
|
|
|
| 173 |
else:
|
| 174 |
feats = outputs
|
| 175 |
|
|
@@ -193,7 +199,7 @@ def faiss_filtered_search(query_emb, top_k=3, exclude_idx=None, gender=None, age
|
|
| 193 |
if idx == -1 or (exclude_idx is not None and idx == exclude_idx):
|
| 194 |
continue
|
| 195 |
row = df.iloc[idx]
|
| 196 |
-
if require_gender
|
| 197 |
continue
|
| 198 |
if require_age and age_group and str(row["age_group"]).lower() != str(age_group).lower():
|
| 199 |
continue
|
|
@@ -256,7 +262,6 @@ def generate_new_outfit_image(pil_image, row, target_gender=None):
|
|
| 256 |
resized_protect_img = Image.fromarray(protect.astype(np.uint8) * 255).resize((head_w, head_h), resample=Image.NEAREST)
|
| 257 |
|
| 258 |
canvas = Image.new("RGB", (canvas_w, canvas_h), color=(240, 238, 235))
|
| 259 |
-
# תיקון קריטי: הגדרת משתני המיקום המקומיים לחישוב הקנבס
|
| 260 |
paste_x = (canvas_w - head_w) // 2
|
| 261 |
paste_y = int(canvas_h * 0.03)
|
| 262 |
canvas.paste(resized_face_crop, (paste_x, paste_y))
|
|
@@ -292,18 +297,6 @@ def build_style_card_html(row, caption):
|
|
| 292 |
html += f'</div>'
|
| 293 |
return html
|
| 294 |
|
| 295 |
-
def to_shop_link(retailer, value, gender=None):
|
| 296 |
-
g = str(gender).strip().lower() if gender is not None else ""
|
| 297 |
-
dept = "men" if g in ("male", "man", "men", "m") else "women"
|
| 298 |
-
query_enc = urllib.parse.quote(str(value))
|
| 299 |
-
if retailer == "zara":
|
| 300 |
-
return f"https://www.zara.com/us/en/search?searchTerm={query_enc}"
|
| 301 |
-
if retailer == "asos":
|
| 302 |
-
return f"https://www.asos.com/us/search/?q={query_enc}"
|
| 303 |
-
if retailer == "hm":
|
| 304 |
-
return f"https://www2.hm.com/en_us/search-results.html?q={query_enc}"
|
| 305 |
-
return f"https://www.google.com/search?q={retailer}+{query_enc}"
|
| 306 |
-
|
| 307 |
def build_outfit_component_cards_html(row):
|
| 308 |
colors = [c.strip() for c in str(row["recommended_colors"]).split(",") if c.strip()] or ["neutral"]
|
| 309 |
components = [
|
|
@@ -423,6 +416,15 @@ h1, h2, h3, p, span, label, input, select, textarea, button { color: #2C2A29 !im
|
|
| 423 |
.dark-panel { background: #FFFFFF !important; border-radius: 16px !important; padding: 24px !important; border: 1px solid #ECE4D6 !important; margin-bottom: 20px; }
|
| 424 |
.dark-panel label, .dark-panel span { color: #2C2A29 !important; font-weight: 600; }
|
| 425 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 426 |
.palette-premium-banner { background: #FAF3ED; padding: 24px; border-radius: 12px; margin-bottom: 20px; border-left: 5px solid #C69E6E; }
|
| 427 |
.section-split { display: grid !important; grid-template-columns: repeat(2, 1fr) !important; gap: 20px !important; margin-top: 20px !important; width: 100% !important; }
|
| 428 |
@media (max-width: 768px) { .section-split { grid-template-columns: 1fr !important; } }
|
|
@@ -446,7 +448,6 @@ h1, h2, h3, p, span, label, input, select, textarea, button { color: #2C2A29 !im
|
|
| 446 |
.prod-title { font-size: 15px; font-weight: 700; color: #111111 !important; margin: 0 0 4px 0; line-height: 1.3; min-height: 40px; display: flex; align-items: center; }
|
| 447 |
.prod-brand { font-size: 13px; color: #999999 !important; margin-bottom: 14px; display: block; }
|
| 448 |
|
| 449 |
-
/* כפתורי הרכישה המקוריים */
|
| 450 |
.shop-btn { display: block; width: 100%; background: #161617; color: #FFFFFF !important; text-align: center; padding: 11px 0; border-radius: 8px; font-size: 13px; font-weight: 700; text-decoration: none !important; letter-spacing: 0.5px; }
|
| 451 |
.shop-btn:hover { background: #2D2D2F; color: #FFFFFF !important; }
|
| 452 |
.more-matches-label { font-size: 11px; font-weight: 700; color: #9C8E82 !important; letter-spacing: 1.6px; text-transform: uppercase; margin: 24px 0 12px; }
|
|
@@ -487,7 +488,7 @@ with gr.Blocks(title="Personal Color Styling") as demo:
|
|
| 487 |
[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 488 |
)
|
| 489 |
|
| 490 |
-
# תיקון: הגדרת
|
| 491 |
gr.Examples(
|
| 492 |
examples=[
|
| 493 |
[SAMPLE_PHOTOS[0], "woman", "adult", "What style would suit me best?"],
|
|
@@ -497,7 +498,7 @@ with gr.Blocks(title="Personal Color Styling") as demo:
|
|
| 497 |
inputs=[photo_in, gender_a, age_a, question_a],
|
| 498 |
outputs=[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 499 |
fn=recommend_from_photo,
|
| 500 |
-
cache_examples=
|
| 501 |
label="Quick Starters",
|
| 502 |
)
|
| 503 |
|
|
@@ -530,5 +531,4 @@ with gr.Blocks(title="Personal Color Styling") as demo:
|
|
| 530 |
)
|
| 531 |
|
| 532 |
if __name__ == "__main__":
|
| 533 |
-
# העברת פרמטרי ה-CSS וה-Theme באופן בטוח לתוך פונקציית הריצה הסופית כנדרש בגרדיו 6
|
| 534 |
demo.launch(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="amber"))
|
|
|
|
| 143 |
return base64.b64encode(buf.getvalue()).decode("utf-8")
|
| 144 |
|
| 145 |
# ---------------------------------------------------------------------------
|
| 146 |
+
# EMBEDDING + FAISS SEARCH (תיקון ה-TypeError וה-AttributeError לחילוץ וקטורים)
|
| 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 |
+
outputs = win_model(**inputs)
|
| 152 |
+
|
| 153 |
+
# שימוש ישיר ובטוח בחילוץ הוקטור ללא תלות בסוג העטיפה של BaseModelOutput
|
| 154 |
+
if hasattr(outputs, "image_embeds") and outputs.image_embeds is not None:
|
| 155 |
feats = outputs.image_embeds
|
| 156 |
+
elif hasattr(outputs, "pooler_output") and outputs.pooler_output is not None:
|
| 157 |
+
feats = outputs.pooler_output
|
| 158 |
+
elif isinstance(outputs, tuple) or isinstance(outputs, list):
|
| 159 |
+
feats = outputs[0]
|
| 160 |
else:
|
| 161 |
feats = outputs
|
| 162 |
+
|
| 163 |
if hasattr(feats, "detach"):
|
| 164 |
feats = feats.detach()
|
| 165 |
return feats.cpu().numpy().astype("float32")
|
|
|
|
| 167 |
@torch.no_grad()
|
| 168 |
def embed_query_text(sentence):
|
| 169 |
inputs = win_processor(text=[sentence], return_tensors="pt", padding=True, truncation=True).to(DEVICE)
|
| 170 |
+
outputs = win_model(**inputs)
|
| 171 |
+
|
| 172 |
+
# שימוש ישיר ובטוח בחילוץ הוקטור ללא תלות בסוג העטיפה של BaseModelOutput
|
| 173 |
+
if hasattr(outputs, "text_embeds") and outputs.text_embeds is not None:
|
| 174 |
feats = outputs.text_embeds
|
| 175 |
+
elif hasattr(outputs, "pooler_output") and outputs.pooler_output is not None:
|
| 176 |
+
feats = outputs.pooler_output
|
| 177 |
+
elif isinstance(outputs, tuple) or isinstance(outputs, list):
|
| 178 |
+
feats = outputs[0]
|
| 179 |
else:
|
| 180 |
feats = outputs
|
| 181 |
|
|
|
|
| 199 |
if idx == -1 or (exclude_idx is not None and idx == exclude_idx):
|
| 200 |
continue
|
| 201 |
row = df.iloc[idx]
|
| 202 |
+
if require_gender && gender and str(row["gender"]).lower() != str(gender).lower():
|
| 203 |
continue
|
| 204 |
if require_age and age_group and str(row["age_group"]).lower() != str(age_group).lower():
|
| 205 |
continue
|
|
|
|
| 262 |
resized_protect_img = Image.fromarray(protect.astype(np.uint8) * 255).resize((head_w, head_h), resample=Image.NEAREST)
|
| 263 |
|
| 264 |
canvas = Image.new("RGB", (canvas_w, canvas_h), color=(240, 238, 235))
|
|
|
|
| 265 |
paste_x = (canvas_w - head_w) // 2
|
| 266 |
paste_y = int(canvas_h * 0.03)
|
| 267 |
canvas.paste(resized_face_crop, (paste_x, paste_y))
|
|
|
|
| 297 |
html += f'</div>'
|
| 298 |
return html
|
| 299 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
def build_outfit_component_cards_html(row):
|
| 301 |
colors = [c.strip() for c in str(row["recommended_colors"]).split(",") if c.strip()] or ["neutral"]
|
| 302 |
components = [
|
|
|
|
| 416 |
.dark-panel { background: #FFFFFF !important; border-radius: 16px !important; padding: 24px !important; border: 1px solid #ECE4D6 !important; margin-bottom: 20px; }
|
| 417 |
.dark-panel label, .dark-panel span { color: #2C2A29 !important; font-weight: 600; }
|
| 418 |
|
| 419 |
+
input, select, .secondary, .wrap, .slots, .single-select, .select-wrap {
|
| 420 |
+
color: #2C2A29 !important;
|
| 421 |
+
background-color: #FFFFFF !important;
|
| 422 |
+
border: 1px solid #E3DFDA !important;
|
| 423 |
+
border-radius: 4px !important;
|
| 424 |
+
}
|
| 425 |
+
div.form { background: transparent !important; border: none !important; box-shadow: none !important; }
|
| 426 |
+
fieldset { display: flex !important; justify-content: center !important; gap: 24px !important; border: none !important; background: transparent !important; }
|
| 427 |
+
|
| 428 |
.palette-premium-banner { background: #FAF3ED; padding: 24px; border-radius: 12px; margin-bottom: 20px; border-left: 5px solid #C69E6E; }
|
| 429 |
.section-split { display: grid !important; grid-template-columns: repeat(2, 1fr) !important; gap: 20px !important; margin-top: 20px !important; width: 100% !important; }
|
| 430 |
@media (max-width: 768px) { .section-split { grid-template-columns: 1fr !important; } }
|
|
|
|
| 448 |
.prod-title { font-size: 15px; font-weight: 700; color: #111111 !important; margin: 0 0 4px 0; line-height: 1.3; min-height: 40px; display: flex; align-items: center; }
|
| 449 |
.prod-brand { font-size: 13px; color: #999999 !important; margin-bottom: 14px; display: block; }
|
| 450 |
|
|
|
|
| 451 |
.shop-btn { display: block; width: 100%; background: #161617; color: #FFFFFF !important; text-align: center; padding: 11px 0; border-radius: 8px; font-size: 13px; font-weight: 700; text-decoration: none !important; letter-spacing: 0.5px; }
|
| 452 |
.shop-btn:hover { background: #2D2D2F; color: #FFFFFF !important; }
|
| 453 |
.more-matches-label { font-size: 11px; font-weight: 700; color: #9C8E82 !important; letter-spacing: 1.6px; text-transform: uppercase; margin: 24px 0 12px; }
|
|
|
|
| 488 |
[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 489 |
)
|
| 490 |
|
| 491 |
+
# תיקון: הגדרת cache_examples=False כדי למנוע קריסות והדמיית קלט כפויה
|
| 492 |
gr.Examples(
|
| 493 |
examples=[
|
| 494 |
[SAMPLE_PHOTOS[0], "woman", "adult", "What style would suit me best?"],
|
|
|
|
| 498 |
inputs=[photo_in, gender_a, age_a, question_a],
|
| 499 |
outputs=[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 500 |
fn=recommend_from_photo,
|
| 501 |
+
cache_examples=False,
|
| 502 |
label="Quick Starters",
|
| 503 |
)
|
| 504 |
|
|
|
|
| 531 |
)
|
| 532 |
|
| 533 |
if __name__ == "__main__":
|
|
|
|
| 534 |
demo.launch(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="amber"))
|