File size: 4,386 Bytes
3a330e0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import json
from pathlib import Path
from typing import Any, Dict, List, Tuple

# Default candidate labels for CLIP zero-shot (PRD brand list)
DEFAULT_BRAND_CLIP_LABELS: List[str] = [
    "Chanel designer handbag",
    "Hermes designer handbag",
    "Louis Vuitton designer handbag",
    "Gucci designer handbag",
    "Fendi designer handbag",
    "Prada designer handbag",
    "Rolex luxury wristwatch",
    "Patek Philippe luxury wristwatch",
    "Cartier luxury wristwatch",
    "Audemars Piguet luxury wristwatch",
    "Monet impressionist oil painting",
    "Van Gogh impressionist oil painting",
    "Vermeer Dutch golden age painting",
    "Rembrandt Dutch golden age painting",
    "Faberge ornate jewelled egg or objet",
    "Tiffany fine jewellery",
    "Van Cleef and Arpels fine jewellery",
]

DEFAULT_LABEL_TO_BRAND_CATEGORY: Dict[str, Tuple[str, str]] = {
    "Chanel designer handbag": ("Chanel", "Handbag"),
    "Hermes designer handbag": ("Hermes", "Handbag"),
    "Louis Vuitton designer handbag": ("Louis Vuitton", "Handbag"),
    "Gucci designer handbag": ("Gucci", "Handbag"),
    "Fendi designer handbag": ("Fendi", "Handbag"),
    "Prada designer handbag": ("Prada", "Handbag"),
    "Rolex luxury wristwatch": ("Rolex", "Watch"),
    "Patek Philippe luxury wristwatch": ("Patek Philippe", "Watch"),
    "Cartier luxury wristwatch": ("Cartier", "Watch"),
    "Audemars Piguet luxury wristwatch": ("Audemars Piguet", "Watch"),
    "Monet impressionist oil painting": ("Monet", "Art"),
    "Van Gogh impressionist oil painting": ("Van Gogh", "Art"),
    "Vermeer Dutch golden age painting": ("Vermeer", "Art"),
    "Rembrandt Dutch golden age painting": ("Rembrandt", "Art"),
    "Faberge ornate jewelled egg or objet": ("Faberge", "Jewellery"),
    "Tiffany fine jewellery": ("Tiffany", "Jewellery"),
    "Van Cleef and Arpels fine jewellery": ("Van Cleef", "Jewellery"),
}

BRAND_CONFIDENCE_THRESHOLD = 0.35

DATA_DIR = Path(__file__).resolve().parent.parent / "data"
BRAND_LABELS_JSON = DATA_DIR / "brand_labels.json"


def _load_brand_assets() -> Tuple[List[str], Dict[str, Tuple[str, str]]]:
    if not BRAND_LABELS_JSON.exists():
        return DEFAULT_BRAND_CLIP_LABELS, DEFAULT_LABEL_TO_BRAND_CATEGORY

    try:
        payload = json.loads(BRAND_LABELS_JSON.read_text(encoding="utf-8"))
        clip_labels = payload.get("clip_labels")
        mapping = payload.get("label_to_brand_category")
        if not isinstance(clip_labels, list) or not isinstance(mapping, dict):
            return DEFAULT_BRAND_CLIP_LABELS, DEFAULT_LABEL_TO_BRAND_CATEGORY

        # mapping values can come as lists; normalize to tuples.
        normalized: Dict[str, Tuple[str, str]] = {}
        for k, v in mapping.items():
            if isinstance(v, (list, tuple)) and len(v) == 2:
                normalized[str(k)] = (str(v[0]), str(v[1]))

        clip_labels2 = [str(x) for x in clip_labels if isinstance(x, (str, int, float))]
        if not clip_labels2 or not normalized:
            return DEFAULT_BRAND_CLIP_LABELS, DEFAULT_LABEL_TO_BRAND_CATEGORY

        return clip_labels2, normalized
    except Exception:
        return DEFAULT_BRAND_CLIP_LABELS, DEFAULT_LABEL_TO_BRAND_CATEGORY


def identify_object(image, clip_pipe, blip_pipe) -> Dict[str, Any]:
    """Layer 2: caption (BLIP) + brand/category (CLIP)."""
    caption_result = blip_pipe(image)
    caption = caption_result[0]["generated_text"].strip()

    brand_clip_labels, label_to_brand_category = _load_brand_assets()

    brand_result = clip_pipe(image, candidate_labels=brand_clip_labels)
    top3 = brand_result[:3]
    top = top3[0]
    top_label = top["label"]
    top_score = float(top["score"])

    if top_score < BRAND_CONFIDENCE_THRESHOLD:
        return {
            "caption": caption,
            "brand": "Unknown luxury item",
            "category": "Unknown",
            "confidence": round(top_score, 4),
            "alt_guesses": [r["label"] for r in top3],
            "clip_label": top_label,
        }

    brand, category = label_to_brand_category.get(
        top_label, (top_label.split()[0], "Unknown")
    )
    alt_guesses = [r["label"] for r in top3[1:3]]

    return {
        "caption": caption,
        "brand": brand,
        "category": category,
        "confidence": round(top_score, 4),
        "alt_guesses": alt_guesses,
        "clip_label": top_label,
    }