Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,7 +2,7 @@ import os
|
|
| 2 |
import random
|
| 3 |
import warnings
|
| 4 |
import logging
|
| 5 |
-
import urllib.parse
|
| 6 |
from fastapi import FastAPI, File, UploadFile, Form
|
| 7 |
from fastapi.middleware.cors import CORSMiddleware
|
| 8 |
from PIL import Image
|
|
@@ -13,6 +13,7 @@ import cv2
|
|
| 13 |
import mediapipe as mp
|
| 14 |
from transformers import Owlv2Processor, Owlv2ForObjectDetection
|
| 15 |
from transformers import CLIPProcessor, CLIPModel
|
|
|
|
| 16 |
|
| 17 |
# --- AYARLAR ---
|
| 18 |
warnings.filterwarnings("ignore")
|
|
@@ -32,17 +33,17 @@ app.add_middleware(
|
|
| 32 |
print("⏳ Modeller Yükleniyor...")
|
| 33 |
device = "cpu"
|
| 34 |
|
| 35 |
-
# OWL-v2
|
| 36 |
owl_id = "google/owlv2-base-patch16-ensemble"
|
| 37 |
owl_processor = Owlv2Processor.from_pretrained(owl_id)
|
| 38 |
owl_model = Owlv2ForObjectDetection.from_pretrained(owl_id).to(device)
|
| 39 |
|
| 40 |
-
# CLIP
|
| 41 |
clip_id = "openai/clip-vit-base-patch32"
|
| 42 |
clip_processor = CLIPProcessor.from_pretrained(clip_id)
|
| 43 |
clip_model = CLIPModel.from_pretrained(clip_id).to(device)
|
| 44 |
|
| 45 |
-
# MediaPipe
|
| 46 |
mp_face_mesh = mp.solutions.face_mesh
|
| 47 |
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1, refine_landmarks=True)
|
| 48 |
|
|
@@ -51,64 +52,94 @@ print("✅ Sunucu Hazır!")
|
|
| 51 |
# --- SÖZLÜKLER ---
|
| 52 |
TR_LABELS = {
|
| 53 |
"acne": "Akne", "pimple": "Sivilce", "dark spot": "Leke",
|
| 54 |
-
"wrinkles": "Kırışıklık", "oily skin": "Yağlanma",
|
| 55 |
"dry flaky skin": "Kuruluk", "skin redness": "Kızarıklık",
|
| 56 |
"peeling skin": "Deri Soyulması", "rough skin": "Pürüzlü",
|
| 57 |
"whitehead pimple": "Beyaz Uçlu Sivilce", "red acne bumps": "Kızarık Akne"
|
| 58 |
}
|
| 59 |
|
| 60 |
-
# ---
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
"
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
"
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
-
#
|
| 99 |
-
if
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
})
|
| 110 |
-
|
| 111 |
-
return products
|
| 112 |
|
| 113 |
def generate_prescription_blocks(skin_type, issues):
|
| 114 |
blocks = []
|
|
@@ -118,38 +149,25 @@ def generate_prescription_blocks(skin_type, issues):
|
|
| 118 |
blocks.append({"title": "Günlük Temizleme", "key": "YAĞLI_TEMİZLİK", "search": "Yağlı Cilt Temizleme Jeli"})
|
| 119 |
elif "KURU" in skin_type:
|
| 120 |
blocks.append({"title": "Nemlendirici Bakım", "key": "KURU_NEM", "search": "Kuru Cilt Nemlendirici"})
|
| 121 |
-
elif "KARMA" in skin_type:
|
| 122 |
-
blocks.append({"title": "Dengeleyici Bakım", "key": "KARMA_BAKIM", "search": "Karma Cilt Temizleyici"})
|
| 123 |
|
| 124 |
# Sorun Bloğu
|
| 125 |
unique_issues = list(set(issues))
|
| 126 |
for issue in unique_issues:
|
| 127 |
if "acne" in issue or "pimple" in issue:
|
| 128 |
-
blocks.append({"title": "Akne Tedavisi", "key": "
|
| 129 |
elif "dark spot" in issue:
|
| 130 |
-
blocks.append({"title": "Leke Giderici", "key": "LEKE_TEDAVİ", "search": "
|
| 131 |
-
elif "redness" in issue:
|
| 132 |
-
blocks.append({"title": "Yatıştırıcı Bakım", "key": "
|
| 133 |
-
elif "dry" in issue or "peeling" in issue:
|
| 134 |
-
if "KURU" not in skin_type: # Zaten kuru cilt önerisi varsa tekrar etme
|
| 135 |
-
blocks.append({"title": "Onarıcı Bakım", "key": "KURULUK", "search": "Panthenol Krem"})
|
| 136 |
elif "wrinkles" in issue:
|
| 137 |
-
blocks.append({"title": "Yaşlanma Karşıtı", "key": "
|
| 138 |
|
| 139 |
return blocks
|
| 140 |
|
| 141 |
-
# --- FONKSİYONLAR ---
|
| 142 |
-
def get_skin_type(image):
|
| 143 |
-
prompts = ["extremely oily shiny skin", "very dry flaky skin", "normal skin", "combination skin"]
|
| 144 |
-
labels = ["YAĞLI", "KURU", "NORMAL", "KARMA"]
|
| 145 |
-
inputs = clip_processor(text=prompts, images=image, return_tensors="pt", padding=True).to(device)
|
| 146 |
-
with torch.no_grad(): probs = clip_model(**inputs).logits_per_image.softmax(dim=1)
|
| 147 |
-
return labels[torch.max(probs, 1).indices.item()]
|
| 148 |
-
|
| 149 |
# --- API ENDPOINT ---
|
| 150 |
@app.get("/")
|
| 151 |
def home():
|
| 152 |
-
return {"status": "Pure Sense API (
|
| 153 |
|
| 154 |
@app.post("/analyze")
|
| 155 |
async def analyze_skin(
|
|
@@ -162,13 +180,13 @@ async def analyze_skin(
|
|
| 162 |
# 1. Cilt Tipi
|
| 163 |
skin_type = get_skin_type(image)
|
| 164 |
|
| 165 |
-
# 2. Sorun Tespiti (
|
| 166 |
-
|
|
|
|
| 167 |
inputs = owl_processor(text=text_queries, images=image, return_tensors="pt").to(device)
|
| 168 |
with torch.no_grad(): outputs = owl_model(**inputs)
|
| 169 |
|
| 170 |
target_sizes = torch.Tensor([image.size[::-1]])
|
| 171 |
-
# Threshold'u 0.02 (%2) yaptık. Çok düşük olursa gürültü olur, çok yüksek olursa kaçırır.
|
| 172 |
results = owl_processor.post_process_object_detection(outputs=outputs, target_sizes=target_sizes, threshold=0.02)[0]
|
| 173 |
|
| 174 |
detections = []
|
|
@@ -178,12 +196,15 @@ async def analyze_skin(
|
|
| 178 |
lbl_en = text_queries[0][label]
|
| 179 |
conf = round(score.item() * 100, 1)
|
| 180 |
|
| 181 |
-
# --- FİLTRELEME
|
| 182 |
-
#
|
| 183 |
-
if lbl_en
|
|
|
|
|
|
|
|
|
|
| 184 |
|
| 185 |
-
#
|
| 186 |
-
if
|
| 187 |
|
| 188 |
lbl_tr = TR_LABELS.get(lbl_en, lbl_en)
|
| 189 |
if lbl_tr not in issues_found: issues_found.append(lbl_tr)
|
|
@@ -195,17 +216,12 @@ async def analyze_skin(
|
|
| 195 |
})
|
| 196 |
|
| 197 |
# 3. Reçete Hazırla
|
| 198 |
-
|
| 199 |
-
raw_issues = []
|
| 200 |
-
for i in issues_found:
|
| 201 |
-
for k, v in TR_LABELS.items():
|
| 202 |
-
if v == i: raw_issues.append(k)
|
| 203 |
-
|
| 204 |
prescription_blocks = generate_prescription_blocks(skin_type, raw_issues)
|
| 205 |
|
| 206 |
final_prescriptions = []
|
| 207 |
for block in prescription_blocks:
|
| 208 |
-
products =
|
| 209 |
final_prescriptions.append({
|
| 210 |
"title": block['title'],
|
| 211 |
"ingredient": block['search'],
|
|
|
|
| 2 |
import random
|
| 3 |
import warnings
|
| 4 |
import logging
|
| 5 |
+
import urllib.parse
|
| 6 |
from fastapi import FastAPI, File, UploadFile, Form
|
| 7 |
from fastapi.middleware.cors import CORSMiddleware
|
| 8 |
from PIL import Image
|
|
|
|
| 13 |
import mediapipe as mp
|
| 14 |
from transformers import Owlv2Processor, Owlv2ForObjectDetection
|
| 15 |
from transformers import CLIPProcessor, CLIPModel
|
| 16 |
+
from duckduckgo_search import DDGS
|
| 17 |
|
| 18 |
# --- AYARLAR ---
|
| 19 |
warnings.filterwarnings("ignore")
|
|
|
|
| 33 |
print("⏳ Modeller Yükleniyor...")
|
| 34 |
device = "cpu"
|
| 35 |
|
| 36 |
+
# OWL-v2
|
| 37 |
owl_id = "google/owlv2-base-patch16-ensemble"
|
| 38 |
owl_processor = Owlv2Processor.from_pretrained(owl_id)
|
| 39 |
owl_model = Owlv2ForObjectDetection.from_pretrained(owl_id).to(device)
|
| 40 |
|
| 41 |
+
# CLIP
|
| 42 |
clip_id = "openai/clip-vit-base-patch32"
|
| 43 |
clip_processor = CLIPProcessor.from_pretrained(clip_id)
|
| 44 |
clip_model = CLIPModel.from_pretrained(clip_id).to(device)
|
| 45 |
|
| 46 |
+
# MediaPipe
|
| 47 |
mp_face_mesh = mp.solutions.face_mesh
|
| 48 |
face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1, refine_landmarks=True)
|
| 49 |
|
|
|
|
| 52 |
# --- SÖZLÜKLER ---
|
| 53 |
TR_LABELS = {
|
| 54 |
"acne": "Akne", "pimple": "Sivilce", "dark spot": "Leke",
|
| 55 |
+
"deep wrinkles": "Kırışıklık", "oily skin": "Yağlanma", # Prompt değişti
|
| 56 |
"dry flaky skin": "Kuruluk", "skin redness": "Kızarıklık",
|
| 57 |
"peeling skin": "Deri Soyulması", "rough skin": "Pürüzlü",
|
| 58 |
"whitehead pimple": "Beyaz Uçlu Sivilce", "red acne bumps": "Kızarık Akne"
|
| 59 |
}
|
| 60 |
|
| 61 |
+
# --- MEGA ÜRÜN VERİTABANI ---
|
| 62 |
+
MEGA_DATABASE = {
|
| 63 |
+
"YAĞLI_AKNE": [
|
| 64 |
+
{"marka": "La Roche-Posay", "urun": "Effaclar Duo (+)", "link": "https://www.trendyol.com/sr?q=effaclar+duo"},
|
| 65 |
+
{"marka": "The Purest Solutions", "urun": "Salisilik Asit Tonik", "link": "https://www.trendyol.com/sr?q=the+purest+salisilik"},
|
| 66 |
+
{"marka": "CeraVe", "urun": "Blemish Control Gel", "link": "https://www.hepsiburada.com/ara?q=cerave+blemish"},
|
| 67 |
+
{"marka": "Bioderma", "urun": "Sebium Kerato+", "link": "https://www.trendyol.com/sr?q=bioderma+sebium+kerato"},
|
| 68 |
+
{"marka": "Cosrx", "urun": "BHA Blackhead Power Liquid", "link": "https://www.korendy.com.tr"},
|
| 69 |
+
{"marka": "Vichy", "urun": "Normaderm Phytosolution", "link": "https://www.trendyol.com/sr?q=vichy+normaderm"}
|
| 70 |
+
],
|
| 71 |
+
"YAĞLI_TEMİZLİK": [
|
| 72 |
+
{"marka": "CeraVe", "urun": "Foaming Cleanser", "link": "https://www.trendyol.com/sr?q=cerave+foaming"},
|
| 73 |
+
{"marka": "La Roche-Posay", "urun": "Effaclar Jel", "link": "https://www.trendyol.com/sr?q=effaclar+jel"},
|
| 74 |
+
{"marka": "Bioderma", "urun": "Sebium Foaming Gel", "link": "https://www.trendyol.com/sr?q=bioderma+sebium+gel"},
|
| 75 |
+
{"marka": "Simple", "urun": "Daily Skin Detox", "link": "https://www.gratis.com"}
|
| 76 |
+
],
|
| 77 |
+
"KURU_NEM": [
|
| 78 |
+
{"marka": "CeraVe", "urun": "Moisturizing Cream", "link": "https://www.trendyol.com/sr?q=cerave+moisturizing"},
|
| 79 |
+
{"marka": "Kiehl's", "urun": "Ultra Facial Cream", "link": "https://www.kiehls.com.tr"},
|
| 80 |
+
{"marka": "La Roche-Posay", "urun": "Lipikar Baume AP+M", "link": "https://www.trendyol.com/sr?q=lipikar+baume"},
|
| 81 |
+
{"marka": "Bioderma", "urun": "Atoderm Intensive Balm", "link": "https://www.trendyol.com/sr?q=bioderma+atoderm"}
|
| 82 |
+
],
|
| 83 |
+
"HASSAS_YATIŞTIRICI": [
|
| 84 |
+
{"marka": "Dr. Jart+", "urun": "Cicapair Tiger Grass", "link": "https://www.sephora.com.tr"},
|
| 85 |
+
{"marka": "La Roche-Posay", "urun": "Cicaplast Baume B5", "link": "https://www.trendyol.com/sr?q=cicaplast"},
|
| 86 |
+
{"marka": "Skin1004", "urun": "Madagascar Centella", "link": "https://www.korendy.com.tr"},
|
| 87 |
+
{"marka": "Avene", "urun": "Cicalfate+ Repair Cream", "link": "https://www.trendyol.com/sr?q=avene+cicalfate"}
|
| 88 |
+
],
|
| 89 |
+
"LEKE_TEDAVİSİ": [
|
| 90 |
+
{"marka": "Caudalie", "urun": "Vinoperfect Serum", "link": "https://www.trendyol.com/sr?q=caudalie+vinoperfect"},
|
| 91 |
+
{"marka": "SkinCeuticals", "urun": "Discoloration Defense", "link": "https://www.trendyol.com/sr?q=skinceuticals+discoloration"},
|
| 92 |
+
{"marka": "The Ordinary", "urun": "Alpha Arbutin 2% + HA", "link": "https://www.trendyol.com/sr?q=ordinary+arbutin"},
|
| 93 |
+
{"marka": "Garnier", "urun": "C Vitamini Parlaklık Serumu", "link": "https://www.gratis.com"}
|
| 94 |
+
],
|
| 95 |
+
"YAŞLANMA_KARŞITI": [
|
| 96 |
+
{"marka": "L'Oreal", "urun": "Revitalift Retinol Serum", "link": "https://www.trendyol.com/sr?q=loreal+retinol"},
|
| 97 |
+
{"marka": "La Roche-Posay", "urun": "Retinol B3 Serum", "link": "https://www.trendyol.com/sr?q=la+roche+retinol"},
|
| 98 |
+
{"marka": "The Purest", "urun": "Retinol %1 Serum", "link": "https://www.trendyol.com/sr?q=the+purest+retinol"},
|
| 99 |
+
{"marka": "Kiehl's", "urun": "Retinol Skin-Renewing", "link": "https://www.kiehls.com.tr"}
|
| 100 |
+
]
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
# --- FONKSİYONLAR ---
|
| 104 |
+
def get_skin_type(image):
|
| 105 |
+
prompts = ["extremely oily shiny skin", "very dry flaky skin", "normal skin", "combination skin"]
|
| 106 |
+
labels = ["YAĞLI", "KURU", "NORMAL", "KARMA"]
|
| 107 |
+
inputs = clip_processor(text=prompts, images=image, return_tensors="pt", padding=True).to(device)
|
| 108 |
+
with torch.no_grad(): probs = clip_model(**inputs).logits_per_image.softmax(dim=1)
|
| 109 |
+
return labels[torch.max(probs, 1).indices.item()]
|
| 110 |
+
|
| 111 |
+
def get_products(category_key, search_term, is_premium=False):
|
| 112 |
+
products_to_show = []
|
| 113 |
|
| 114 |
+
# 1. Veritabanından Çek
|
| 115 |
+
if category_key in MEGA_DATABASE:
|
| 116 |
+
db_products = MEGA_DATABASE[category_key][:] # Kopyasını al
|
| 117 |
+
random.shuffle(db_products)
|
| 118 |
+
limit = 6 if is_premium else 2
|
| 119 |
+
products_to_show = db_products[:limit]
|
| 120 |
+
|
| 121 |
+
# 2. Eğer veritabanı boşsa Canlı Arama Yap
|
| 122 |
+
if len(products_to_show) < 2:
|
| 123 |
+
try:
|
| 124 |
+
with DDGS() as ddgs:
|
| 125 |
+
query = f"site:trendyol.com {search_term} en çok satanlar"
|
| 126 |
+
results = list(ddgs.text(query, max_results=3))
|
| 127 |
+
for r in results:
|
| 128 |
+
products_to_show.append({
|
| 129 |
+
"marka": "Trend",
|
| 130 |
+
"urun": r['title'].split("|")[0],
|
| 131 |
+
"link": r['href']
|
| 132 |
+
})
|
| 133 |
+
except: pass
|
| 134 |
+
|
| 135 |
+
final_list = []
|
| 136 |
+
for p in products_to_show:
|
| 137 |
+
final_list.append({
|
| 138 |
+
"title": f"[{p['marka']}] {p['urun']}",
|
| 139 |
+
"link": p['link'],
|
| 140 |
+
"source": "Öneri"
|
| 141 |
})
|
| 142 |
+
return final_list
|
|
|
|
| 143 |
|
| 144 |
def generate_prescription_blocks(skin_type, issues):
|
| 145 |
blocks = []
|
|
|
|
| 149 |
blocks.append({"title": "Günlük Temizleme", "key": "YAĞLI_TEMİZLİK", "search": "Yağlı Cilt Temizleme Jeli"})
|
| 150 |
elif "KURU" in skin_type:
|
| 151 |
blocks.append({"title": "Nemlendirici Bakım", "key": "KURU_NEM", "search": "Kuru Cilt Nemlendirici"})
|
|
|
|
|
|
|
| 152 |
|
| 153 |
# Sorun Bloğu
|
| 154 |
unique_issues = list(set(issues))
|
| 155 |
for issue in unique_issues:
|
| 156 |
if "acne" in issue or "pimple" in issue:
|
| 157 |
+
blocks.append({"title": "Akne Tedavisi", "key": "YAĞLI_AKNE", "search": "Akne Kremi"})
|
| 158 |
elif "dark spot" in issue:
|
| 159 |
+
blocks.append({"title": "Leke Giderici", "key": "LEKE_TEDAVİ", "search": "Leke Serumu"})
|
| 160 |
+
elif "redness" in issue or "dry" in issue:
|
| 161 |
+
blocks.append({"title": "Yatıştırıcı Bakım", "key": "HASSAS_YATIŞTIRICI", "search": "Cica Krem"})
|
|
|
|
|
|
|
|
|
|
| 162 |
elif "wrinkles" in issue:
|
| 163 |
+
blocks.append({"title": "Yaşlanma Karşıtı", "key": "YAŞLANMA_KARŞITI", "search": "Retinol Serum"})
|
| 164 |
|
| 165 |
return blocks
|
| 166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
# --- API ENDPOINT ---
|
| 168 |
@app.get("/")
|
| 169 |
def home():
|
| 170 |
+
return {"status": "Pure Sense API (Wrinkle Fix) Çalışıyor"}
|
| 171 |
|
| 172 |
@app.post("/analyze")
|
| 173 |
async def analyze_skin(
|
|
|
|
| 180 |
# 1. Cilt Tipi
|
| 181 |
skin_type = get_skin_type(image)
|
| 182 |
|
| 183 |
+
# 2. Sorun Tespiti (GÜNCELLENMİŞ PROMPTLAR)
|
| 184 |
+
# "wrinkles" yerine "deep wrinkles" yazdık ki ince çizgileri görmezden gelsin.
|
| 185 |
+
text_queries = [["acne", "pimple", "dark spot", "skin redness", "dry flaky skin", "deep wrinkles"]]
|
| 186 |
inputs = owl_processor(text=text_queries, images=image, return_tensors="pt").to(device)
|
| 187 |
with torch.no_grad(): outputs = owl_model(**inputs)
|
| 188 |
|
| 189 |
target_sizes = torch.Tensor([image.size[::-1]])
|
|
|
|
| 190 |
results = owl_processor.post_process_object_detection(outputs=outputs, target_sizes=target_sizes, threshold=0.02)[0]
|
| 191 |
|
| 192 |
detections = []
|
|
|
|
| 196 |
lbl_en = text_queries[0][label]
|
| 197 |
conf = round(score.item() * 100, 1)
|
| 198 |
|
| 199 |
+
# --- GELİŞMİŞ FİLTRELEME ---
|
| 200 |
+
# KIRIŞIKLIK FİLTRESİ: Sadece %40'tan eminse göstersin.
|
| 201 |
+
if lbl_en == "deep wrinkles" and conf < 40: continue
|
| 202 |
+
|
| 203 |
+
# AKNE FİLTRESİ: %10 altı yok.
|
| 204 |
+
if lbl_en in ["acne", "pimple"] and conf < 10: continue
|
| 205 |
|
| 206 |
+
# Diğerleri için %3 yeterli
|
| 207 |
+
if conf < 3: continue
|
| 208 |
|
| 209 |
lbl_tr = TR_LABELS.get(lbl_en, lbl_en)
|
| 210 |
if lbl_tr not in issues_found: issues_found.append(lbl_tr)
|
|
|
|
| 216 |
})
|
| 217 |
|
| 218 |
# 3. Reçete Hazırla
|
| 219 |
+
raw_issues = [i.lower() for i in text_queries[0] if TR_LABELS.get(i,i) in issues_found]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
prescription_blocks = generate_prescription_blocks(skin_type, raw_issues)
|
| 221 |
|
| 222 |
final_prescriptions = []
|
| 223 |
for block in prescription_blocks:
|
| 224 |
+
products = get_products(block['key'], block['search'], is_premium)
|
| 225 |
final_prescriptions.append({
|
| 226 |
"title": block['title'],
|
| 227 |
"ingredient": block['search'],
|