Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -444,6 +444,27 @@ def decode_b64_image(b64_str: str) -> Image.Image:
|
|
| 444 |
raw = base64.b64decode(b64_str)
|
| 445 |
return Image.open(io.BytesIO(raw)).convert("RGB")
|
| 446 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 448 |
# JSON EXTRACTION
|
| 449 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -749,13 +770,16 @@ class ImageInput(BaseModel):
|
|
| 749 |
description="One of: front_right, front_left, rear_right, rear_left, inside, door",
|
| 750 |
example="front_left",
|
| 751 |
)
|
| 752 |
-
|
| 753 |
...,
|
| 754 |
description=(
|
| 755 |
-
"
|
|
|
|
|
|
|
| 756 |
"(e.g. 'data:image/jpeg;base64,...'). "
|
| 757 |
"Supported formats: JPEG, PNG, WEBP."
|
| 758 |
),
|
|
|
|
| 759 |
)
|
| 760 |
|
| 761 |
|
|
@@ -766,12 +790,12 @@ class InspectRequest(BaseModel):
|
|
| 766 |
max_length=6,
|
| 767 |
description="List of labeled images. Each label may appear at most once.",
|
| 768 |
example=[
|
| 769 |
-
{"label": "front_left", "
|
| 770 |
-
{"label": "front_right", "
|
| 771 |
-
{"label": "rear_left", "
|
| 772 |
-
{"label": "rear_right", "
|
| 773 |
-
{"label": "inside", "
|
| 774 |
-
{"label": "door", "
|
| 775 |
],
|
| 776 |
)
|
| 777 |
|
|
@@ -803,7 +827,7 @@ def inspect(request: InspectRequest):
|
|
| 803 |
"""
|
| 804 |
Run full trailer inspection on all submitted images in parallel.
|
| 805 |
|
| 806 |
-
**Input:** Up to 6 labeled base64
|
| 807 |
**Output:** Per-label report with component detection results.
|
| 808 |
|
| 809 |
Labels accepted: `front_right`, `front_left`, `rear_right`, `rear_left`, `inside`, `door`
|
|
@@ -832,17 +856,17 @@ def inspect(request: InspectRequest):
|
|
| 832 |
status_code=422,
|
| 833 |
detail=f"Duplicate label '{item.label}'. Each label may only appear once.",
|
| 834 |
)
|
| 835 |
-
seen_labels[item.label] = item.
|
| 836 |
|
| 837 |
-
#
|
| 838 |
decoded: dict[str, Image.Image] = {}
|
| 839 |
-
for label,
|
| 840 |
try:
|
| 841 |
-
decoded[label] =
|
| 842 |
except Exception as e:
|
| 843 |
raise HTTPException(
|
| 844 |
status_code=422,
|
| 845 |
-
detail=f"Could not
|
| 846 |
)
|
| 847 |
|
| 848 |
# ββ Run label analyses in small batches βββββββββββββββββββββββββββββββββ
|
|
|
|
| 444 |
raw = base64.b64decode(b64_str)
|
| 445 |
return Image.open(io.BytesIO(raw)).convert("RGB")
|
| 446 |
|
| 447 |
+
|
| 448 |
+
def fetch_image_from_url(url: str, timeout: int = 20) -> Image.Image:
|
| 449 |
+
"""Download an image from a URL and return it as a PIL Image."""
|
| 450 |
+
resp = requests.get(url, timeout=timeout)
|
| 451 |
+
resp.raise_for_status()
|
| 452 |
+
content_type = resp.headers.get("Content-Type", "")
|
| 453 |
+
if content_type and not content_type.startswith("image/"):
|
| 454 |
+
raise ValueError(f"URL did not return an image (Content-Type: {content_type})")
|
| 455 |
+
return Image.open(io.BytesIO(resp.content)).convert("RGB")
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def load_image(image_url: str) -> Image.Image:
|
| 459 |
+
"""
|
| 460 |
+
Load an image from either a URL (http/https) or a base64 string / data-URI.
|
| 461 |
+
This is the single entry point for all image loading in the inspect route.
|
| 462 |
+
"""
|
| 463 |
+
stripped = image_url.strip()
|
| 464 |
+
if stripped.startswith("http://") or stripped.startswith("https://"):
|
| 465 |
+
return fetch_image_from_url(stripped)
|
| 466 |
+
return decode_b64_image(stripped)
|
| 467 |
+
|
| 468 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 469 |
# JSON EXTRACTION
|
| 470 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 770 |
description="One of: front_right, front_left, rear_right, rear_left, inside, door",
|
| 771 |
example="front_left",
|
| 772 |
)
|
| 773 |
+
image_url: str = Field(
|
| 774 |
...,
|
| 775 |
description=(
|
| 776 |
+
"Image source β either a public/signed HTTPS URL "
|
| 777 |
+
"(e.g. a Firebase Storage download URL) "
|
| 778 |
+
"OR a base64-encoded string / data-URI "
|
| 779 |
"(e.g. 'data:image/jpeg;base64,...'). "
|
| 780 |
"Supported formats: JPEG, PNG, WEBP."
|
| 781 |
),
|
| 782 |
+
example="https://firebasestorage.googleapis.com/...",
|
| 783 |
)
|
| 784 |
|
| 785 |
|
|
|
|
| 790 |
max_length=6,
|
| 791 |
description="List of labeled images. Each label may appear at most once.",
|
| 792 |
example=[
|
| 793 |
+
{"label": "front_left", "image_url": "https://firebasestorage.googleapis.com/..."},
|
| 794 |
+
{"label": "front_right", "image_url": "https://firebasestorage.googleapis.com/..."},
|
| 795 |
+
{"label": "rear_left", "image_url": "https://firebasestorage.googleapis.com/..."},
|
| 796 |
+
{"label": "rear_right", "image_url": "https://firebasestorage.googleapis.com/..."},
|
| 797 |
+
{"label": "inside", "image_url": "https://firebasestorage.googleapis.com/..."},
|
| 798 |
+
{"label": "door", "image_url": "https://firebasestorage.googleapis.com/..."},
|
| 799 |
],
|
| 800 |
)
|
| 801 |
|
|
|
|
| 827 |
"""
|
| 828 |
Run full trailer inspection on all submitted images in parallel.
|
| 829 |
|
| 830 |
+
**Input:** Up to 6 labeled images β each as a signed/public HTTPS URL or base64 string.
|
| 831 |
**Output:** Per-label report with component detection results.
|
| 832 |
|
| 833 |
Labels accepted: `front_right`, `front_left`, `rear_right`, `rear_left`, `inside`, `door`
|
|
|
|
| 856 |
status_code=422,
|
| 857 |
detail=f"Duplicate label '{item.label}'. Each label may only appear once.",
|
| 858 |
)
|
| 859 |
+
seen_labels[item.label] = item.image_url
|
| 860 |
|
| 861 |
+
# Load all images (URL download or base64 decode)
|
| 862 |
decoded: dict[str, Image.Image] = {}
|
| 863 |
+
for label, image_url in seen_labels.items():
|
| 864 |
try:
|
| 865 |
+
decoded[label] = load_image(image_url)
|
| 866 |
except Exception as e:
|
| 867 |
raise HTTPException(
|
| 868 |
status_code=422,
|
| 869 |
+
detail=f"Could not load image for label '{label}': {e}",
|
| 870 |
)
|
| 871 |
|
| 872 |
# ββ Run label analyses in small batches βββββββββββββββββββββββββββββββββ
|