File size: 3,518 Bytes
3820d5b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
114
115
116
117
118
119
120
121
122
123
"""Standardized JSON responses for the Wizara Vision API."""

from __future__ import annotations

import json
import os
from typing import Any

PROVIDER_NAME = "LocateAnything"
DEFAULT_MODEL_NAME = "LocateAnything-3B"


def model_name() -> str:
    path = os.environ.get("MODEL_PATH", DEFAULT_MODEL_NAME)
    return path.split("/")[-1] if "/" in path else path


def success_response(
    *,
    image_width: int,
    image_height: int,
    objects: list[dict[str, Any]],
    task: str,
    extra: dict[str, Any] | None = None,
) -> dict[str, Any]:
    payload: dict[str, Any] = {
        "success": True,
        "provider": PROVIDER_NAME,
        "model": model_name(),
        "task": task,
        "image": {"width": image_width, "height": image_height},
        "objects": objects,
    }
    if extra:
        payload.update(extra)
    return payload


def error_response(message: str, *, code: str | None = None) -> dict[str, Any]:
    payload: dict[str, Any] = {"success": False, "error": message}
    if code:
        payload["code"] = code
    return payload


def unsupported_response(task: str) -> dict[str, Any]:
    return {
        "success": True,
        "supported": False,
        "provider": PROVIDER_NAME,
        "model": model_name(),
        "task": task,
        "message": "Not implemented by this model.",
    }


def parse_advanced_settings(raw: str | None) -> dict[str, Any]:
    if not raw or not str(raw).strip():
        return {}
    try:
        parsed = json.loads(raw)
        return parsed if isinstance(parsed, dict) else {}
    except json.JSONDecodeError:
        return {}


def infer_category(label: str) -> str:
    text = (label or "object").lower()
    rules = (
        (("person", "man", "woman", "child", "human"), "person"),
        (("car", "truck", "bus", "vehicle", "bike", "bicycle", "motorcycle"), "vehicle"),
        (("dog", "cat", "bird", "animal"), "animal"),
        (("tree", "plant", "flower"), "plant"),
        (("building", "house", "window", "door"), "building"),
        (("chair", "table", "desk", "sofa", "bed"), "furniture"),
        (("phone", "laptop", "monitor", "screen", "keyboard"), "electronics"),
        (("food", "plate", "bowl", "cup", "bottle"), "food"),
    )
    for keywords, category in rules:
        if any(keyword in text for keyword in keywords):
            return category
    return "custom"


def normalize_bbox(coords: list[float]) -> dict[str, float] | None:
    if len(coords) < 4:
        return None
    x1, y1, x2, y2 = coords[:4]
    left = min(x1, x2) / 1000.0
    top = min(y1, y2) / 1000.0
    right = max(x1, x2) / 1000.0
    bottom = max(y1, y2) / 1000.0
    width = max(0.0, right - left)
    height = max(0.0, bottom - top)
    if width <= 0 or height <= 0:
        return None
    return {
        "x": round(left, 6),
        "y": round(top, 6),
        "width": round(width, 6),
        "height": round(height, 6),
    }


def detections_to_objects(detections: list[dict[str, Any]]) -> list[dict[str, Any]]:
    objects: list[dict[str, Any]] = []
    for det in detections:
        if det.get("type") != "box":
            continue
        bbox = normalize_bbox(det.get("coords") or [])
        if not bbox:
            continue
        label = str(det.get("label") or "object")
        objects.append(
            {
                "label": label,
                "category": infer_category(label),
                "confidence": None,
                "bbox": bbox,
            }
        )
    return objects