Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
|
| 2 |
import base64
|
| 3 |
import os
|
| 4 |
import urllib.parse
|
|
@@ -144,28 +143,38 @@ def pil_to_base64(img, max_size=280):
|
|
| 144 |
return base64.b64encode(buf.getvalue()).decode("utf-8")
|
| 145 |
|
| 146 |
# ---------------------------------------------------------------------------
|
| 147 |
-
# EMBEDDING + FAISS SEARCH (תיקון חילוץ הוקטורים מ
|
| 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 |
-
#
|
| 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 |
-
#
|
| 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):
|
|
@@ -247,6 +256,9 @@ def generate_new_outfit_image(pil_image, row, target_gender=None):
|
|
| 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)
|
|
@@ -400,7 +412,6 @@ body, .gradio-container { background-color: #F8F5F5 !important; font-family: 'In
|
|
| 400 |
.gradio-container { max-width: 850px !important; margin: 0 auto !important; padding-top: 20px !important; }
|
| 401 |
footer { display: none !important; }
|
| 402 |
|
| 403 |
-
/* Global Font Contrast Controls - No Invisible Text */
|
| 404 |
h1, h2, h3, p, span, label, input, select, textarea, button { color: #2C2A29 !important; }
|
| 405 |
|
| 406 |
.tab-nav button { font-size: 14px !important; font-weight: 600 !important; padding: 14px 24px !important; color: #555 !important; }
|
|
@@ -409,7 +420,6 @@ h1, h2, h3, p, span, label, input, select, textarea, button { color: #2C2A29 !im
|
|
| 409 |
#find-btn, .big-btn { background: #161617 !important; color: #FFFFFF !important; border: none !important; border-radius: 8px !important; font-weight: 700 !important; font-size: 14px !important; padding: 14px !important; text-transform: uppercase; letter-spacing: .08em !important; width: 100% !important; margin-top: 10px; }
|
| 410 |
#find-btn:hover, .big-btn:hover { background: #2D2D2F !important; }
|
| 411 |
|
| 412 |
-
/* Clean Pastel Premium Layout Blocks */
|
| 413 |
.dark-panel { background: #FFFFFF !important; border-radius: 16px !important; padding: 24px !important; border: 1px solid #ECE4D6 !important; margin-bottom: 20px; }
|
| 414 |
.dark-panel label, .dark-panel span { color: #2C2A29 !important; font-weight: 600; }
|
| 415 |
|
|
@@ -424,7 +434,6 @@ h1, h2, h3, p, span, label, input, select, textarea, button { color: #2C2A29 !im
|
|
| 424 |
.color-bubble { width: 44px; height: 44px; border-radius: 50%; margin: 0 auto 6px; border: 1px solid rgba(0,0,0,.06); box-shadow: inset 0 0 0 2px #FFF; }
|
| 425 |
.tip-text { font-size: 15px; color: #222222 !important; line-height: 1.6; margin: 0; font-style: italic; }
|
| 426 |
|
| 427 |
-
/* Perfect 6-Card Row Flow without Hiding or Squishing */
|
| 428 |
.outfit-grid { display: grid !important; grid-template-columns: repeat(3, 1fr) !important; gap: 20px !important; margin-top: 20px !important; width: 100% !important; }
|
| 429 |
@media (max-width: 768px) { .outfit-grid { grid-template-columns: repeat(2, 1fr) !important; } }
|
| 430 |
@media (max-width: 480px) { .outfit-grid { grid-template-columns: 1fr !important; } }
|
|
@@ -437,17 +446,16 @@ h1, h2, h3, p, span, label, input, select, textarea, button { color: #2C2A29 !im
|
|
| 437 |
.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; }
|
| 438 |
.prod-brand { font-size: 13px; color: #999999 !important; margin-bottom: 14px; display: block; }
|
| 439 |
|
|
|
|
| 440 |
.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; }
|
| 441 |
.shop-btn:hover { background: #2D2D2F; color: #FFFFFF !important; }
|
| 442 |
.more-matches-label { font-size: 11px; font-weight: 700; color: #9C8E82 !important; letter-spacing: 1.6px; text-transform: uppercase; margin: 24px 0 12px; }
|
| 443 |
.palette-name { font-family: 'Playfair Display', serif !important; }
|
| 444 |
-
.footer-note { font-size: 13px; color: #A0A0A0 !important; text-align: center; margin-top: 16px; }
|
| 445 |
"""
|
| 446 |
|
| 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;">
|
|
@@ -479,9 +487,13 @@ with gr.Blocks(title="Personal Color Styling") as demo:
|
|
| 479 |
[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 480 |
)
|
| 481 |
|
| 482 |
-
#
|
| 483 |
gr.Examples(
|
| 484 |
-
examples=[
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,5 +530,5 @@ with gr.Blocks(title="Personal Color Styling") as demo:
|
|
| 518 |
)
|
| 519 |
|
| 520 |
if __name__ == "__main__":
|
| 521 |
-
#
|
| 522 |
demo.launch(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="amber"))
|
|
|
|
|
|
|
| 1 |
import base64
|
| 2 |
import os
|
| 3 |
import urllib.parse
|
|
|
|
| 143 |
return base64.b64encode(buf.getvalue()).decode("utf-8")
|
| 144 |
|
| 145 |
# ---------------------------------------------------------------------------
|
| 146 |
+
# EMBEDDING + FAISS SEARCH (תיקון סופי ומאובטח לחילוץ הוקטורים מחבילת CLIP)
|
| 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.get_image_features(**inputs)
|
| 152 |
+
# חילוץ בטוח באמצעות בדיקת סוג האובייקט והפיכתו לטנסור טהור
|
| 153 |
if hasattr(outputs, "image_embeds"):
|
| 154 |
feats = outputs.image_embeds
|
| 155 |
+
elif isinstance(outputs, getattr(gr, "__package__", object)) or not hasattr(outputs, "cpu"):
|
| 156 |
+
feats = outputs[0] if (isinstance(outputs, tuple) or isinstance(outputs, list)) else outputs
|
| 157 |
else:
|
| 158 |
feats = outputs
|
| 159 |
+
|
| 160 |
+
if hasattr(feats, "detach"):
|
| 161 |
+
feats = feats.detach()
|
| 162 |
return feats.cpu().numpy().astype("float32")
|
| 163 |
|
| 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.get_text_features(**inputs)
|
| 168 |
+
# חילוץ בטוח באמצעות בדיקת סוג האובייקט והפיכתו לטנסור טהור
|
| 169 |
if hasattr(outputs, "text_embeds"):
|
| 170 |
feats = outputs.text_embeds
|
| 171 |
+
elif isinstance(outputs, getattr(gr, "__package__", object)) or not hasattr(outputs, "cpu"):
|
| 172 |
+
feats = outputs[0] if (isinstance(outputs, tuple) or isinstance(outputs, list)) else outputs
|
| 173 |
else:
|
| 174 |
feats = outputs
|
| 175 |
+
|
| 176 |
+
if hasattr(feats, "detach"):
|
| 177 |
+
feats = feats.detach()
|
| 178 |
return feats.cpu().numpy().astype("float32")
|
| 179 |
|
| 180 |
def build_feature_sentence(skin_tone, undertone, style_preference, gender=None, age_group=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))
|
| 263 |
|
| 264 |
mask_arr = np.full((canvas_h, canvas_w), 255, dtype=np.uint8)
|
|
|
|
| 412 |
.gradio-container { max-width: 850px !important; margin: 0 auto !important; padding-top: 20px !important; }
|
| 413 |
footer { display: none !important; }
|
| 414 |
|
|
|
|
| 415 |
h1, h2, h3, p, span, label, input, select, textarea, button { color: #2C2A29 !important; }
|
| 416 |
|
| 417 |
.tab-nav button { font-size: 14px !important; font-weight: 600 !important; padding: 14px 24px !important; color: #555 !important; }
|
|
|
|
| 420 |
#find-btn, .big-btn { background: #161617 !important; color: #FFFFFF !important; border: none !important; border-radius: 8px !important; font-weight: 700 !important; font-size: 14px !important; padding: 14px !important; text-transform: uppercase; letter-spacing: .08em !important; width: 100% !important; margin-top: 10px; }
|
| 421 |
#find-btn:hover, .big-btn:hover { background: #2D2D2F !important; }
|
| 422 |
|
|
|
|
| 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 |
|
|
|
|
| 434 |
.color-bubble { width: 44px; height: 44px; border-radius: 50%; margin: 0 auto 6px; border: 1px solid rgba(0,0,0,.06); box-shadow: inset 0 0 0 2px #FFF; }
|
| 435 |
.tip-text { font-size: 15px; color: #222222 !important; line-height: 1.6; margin: 0; font-style: italic; }
|
| 436 |
|
|
|
|
| 437 |
.outfit-grid { display: grid !important; grid-template-columns: repeat(3, 1fr) !important; gap: 20px !important; margin-top: 20px !important; width: 100% !important; }
|
| 438 |
@media (max-width: 768px) { .outfit-grid { grid-template-columns: repeat(2, 1fr) !important; } }
|
| 439 |
@media (max-width: 480px) { .outfit-grid { grid-template-columns: 1fr !important; } }
|
|
|
|
| 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; }
|
| 453 |
.palette-name { font-family: 'Playfair Display', serif !important; }
|
|
|
|
| 454 |
"""
|
| 455 |
|
| 456 |
# ---------------------------------------------------------------------------
|
| 457 |
# INTERFACE BUILD
|
| 458 |
# ---------------------------------------------------------------------------
|
|
|
|
| 459 |
with gr.Blocks(title="Personal Color Styling") as demo:
|
| 460 |
gr.HTML("""
|
| 461 |
<div style="text-align:center; padding:24px 20px 10px;">
|
|
|
|
| 487 |
[style_card_a, new_img_a, answer_a, outfit_cards_a],
|
| 488 |
)
|
| 489 |
|
| 490 |
+
# תיקון: הגדרת מערך ערכים מלא ומדויק לכל הפרמטרים של ה-Examples כדי למנוע את שגיאת ה-load_example
|
| 491 |
gr.Examples(
|
| 492 |
+
examples=[
|
| 493 |
+
[SAMPLE_PHOTOS[0], "woman", "adult", "What style would suit me best?"],
|
| 494 |
+
[SAMPLE_PHOTOS[1], "man", "adult", "What style would suit me best?"],
|
| 495 |
+
[SAMPLE_PHOTOS[2], "woman", "teen", "What style would suit me best?"]
|
| 496 |
+
],
|
| 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,
|
|
|
|
| 530 |
)
|
| 531 |
|
| 532 |
if __name__ == "__main__":
|
| 533 |
+
# העברת פרמטרי ה-CSS וה-Theme באופן בטוח לתוך פונקציית הריצה הסופית כנדרש בגרדיו 6
|
| 534 |
demo.launch(css=CUSTOM_CSS, theme=gr.themes.Soft(primary_hue="amber"))
|