daniel0708 commited on
Commit
0449649
ยท
verified ยท
1 Parent(s): d8a3216

feat: add CLIP 512d embedding to predict output

Browse files
Files changed (1) hide show
  1. app.py +102 -33
app.py CHANGED
@@ -1,33 +1,102 @@
1
- import gradio as gr
2
- from ultralytics import YOLO
3
- import cv2
4
-
5
- # ๋ชจ๋ธ ๋กœ๋“œ
6
- model = YOLO("best.pt")
7
-
8
- def predict(image):
9
- # YOLO ์ถ”๋ก 
10
- results = model(image)
11
-
12
- # ์ƒ์˜/ํ•˜์˜ ์ขŒํ‘œ ๋ฐ ๋ผ๋ฒจ ์ถ”์ถœ (๊ธฐ์กด ํ”„๋กœ์ ํŠธ JSON ํ˜•์‹์— ๋งž์ถฐ ๊ฐ€๊ณต)
13
- detections = []
14
- for r in results:
15
- for box in r.boxes:
16
- x1, y1, x2, y2 = box.xyxy[0].tolist()
17
- label = model.names[int(box.cls[0])]
18
- conf = float(box.conf[0])
19
- detections.append({
20
- "label": label,
21
- "confidence": conf,
22
- "box": [int(x1), int(y1), int(x2), int(y2)]
23
- })
24
- return detections
25
-
26
- # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์„ค์ • (API ์ „์šฉ์ด๋ฏ€๋กœ ๋‹จ์ˆœํ•˜๊ฒŒ ๊ตฌ์„ฑ)
27
- demo = gr.Interface(
28
- fn=predict,
29
- inputs=gr.Image(type="numpy"),
30
- outputs="json"
31
- )
32
-
33
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from ultralytics import YOLO
3
+ import numpy as np
4
+ from PIL import Image
5
+ import torch
6
+ from transformers import CLIPProcessor, CLIPModel
7
+ import logging
8
+
9
+ logging.basicConfig(level=logging.INFO)
10
+ logger = logging.getLogger(__name__)
11
+
12
+ logger.info("YOLO ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘...")
13
+ yolo_model = YOLO("best.pt")
14
+ logger.info("YOLO ๋กœ๋“œ ์™„๋ฃŒ")
15
+
16
+ logger.info("CLIP ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘ (openai/clip-vit-base-patch32)...")
17
+ clip_model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
18
+ clip_processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
19
+ clip_model.eval()
20
+ logger.info("CLIP ๋กœ๋“œ ์™„๋ฃŒ dim=512")
21
+
22
+ CATEGORY_MAP = {
23
+ "top": "top",
24
+ "bottom": "bottom",
25
+ "outer": "outer",
26
+ }
27
+
28
+ def _l2_normalize(vec):
29
+ norm = np.linalg.norm(vec)
30
+ if norm < 1e-9:
31
+ return vec.tolist()
32
+ return (vec / norm).tolist()
33
+
34
+ def predict(image):
35
+ if image is None:
36
+ return {"boxes": [], "embedding": None, "label": "unknown", "category": None}
37
+
38
+ pil_img = Image.fromarray(image).convert("RGB")
39
+ w, h = pil_img.size
40
+
41
+ # 1. YOLO ์˜๋ฅ˜ ํƒ์ง€
42
+ detections = []
43
+ try:
44
+ results = yolo_model(pil_img, conf=0.30, iou=0.6)
45
+ for r in results:
46
+ if r.boxes is None:
47
+ continue
48
+ for box in r.boxes:
49
+ x1, y1, x2, y2 = [int(v) for v in box.xyxy[0].tolist()]
50
+ conf = float(box.conf[0].item())
51
+ cls_id = int(box.cls[0].item())
52
+ raw_label = r.names.get(cls_id, "top")
53
+ x1, x2 = max(0, min(x1, w - 1)), max(x1 + 1, min(x2, w))
54
+ y1, y2 = max(0, min(y1, h - 1)), max(y1 + 1, min(y2, h))
55
+ detections.append({
56
+ "label": raw_label,
57
+ "confidence": round(conf, 4),
58
+ "box": [x1, y1, x2, y2],
59
+ })
60
+ detections.sort(key=lambda b: b["confidence"], reverse=True)
61
+ except Exception as e:
62
+ logger.error(f"YOLO error: {e}")
63
+
64
+ # 2. ํƒ์ง€ ์˜์—ญ ํฌ๋กญ (5% ํŒจ๋”ฉ)
65
+ crop_img = pil_img
66
+ if detections:
67
+ b = detections[0]
68
+ x1, y1, x2, y2 = b["box"]
69
+ bw, bh = x2 - x1, y2 - y1
70
+ pad_w, pad_h = int(bw * 0.05), int(bh * 0.05)
71
+ cx1, cy1 = max(0, x1 - pad_w), max(0, y1 - pad_h)
72
+ cx2, cy2 = min(w, x2 + pad_w), min(h, y2 + pad_h)
73
+ if cx2 > cx1 and cy2 > cy1:
74
+ crop_img = pil_img.crop((cx1, cy1, cx2, cy2))
75
+
76
+ # 3. CLIP 512์ฐจ์› ์ž„๋ฒ ๋”ฉ ์ถ”์ถœ
77
+ embedding = None
78
+ try:
79
+ inputs = clip_processor(images=crop_img, return_tensors="pt")
80
+ with torch.no_grad():
81
+ features = clip_model.get_image_features(**inputs)
82
+ vec = features[0].cpu().numpy().astype(np.float32)
83
+ embedding = _l2_normalize(vec)
84
+ logger.info(f"CLIP embedding done dim={len(embedding)}")
85
+ except Exception as e:
86
+ logger.error(f"CLIP error: {e}")
87
+
88
+ top_label = detections[0]["label"] if detections else "unknown"
89
+ category = CATEGORY_MAP.get(top_label.strip().lower(), top_label.lower())
90
+
91
+ return {"boxes": detections, "embedding": embedding, "label": top_label, "category": category}
92
+
93
+
94
+ demo = gr.Interface(
95
+ fn=predict,
96
+ inputs=gr.Image(type="numpy", label="Fashion Image"),
97
+ outputs=gr.JSON(label="Result (boxes + 512d embedding)"),
98
+ title="Lookalike YOLO + CLIP",
99
+ description="YOLOv11 clothing detection + CLIP 512d embedding extraction API",
100
+ )
101
+
102
+ demo.launch()