import json import os from functools import lru_cache from pathlib import Path from typing import Any, Dict, Tuple os.environ["CUDA_VISIBLE_DEVICES"] = "" import gradio as gr from huggingface_hub import hf_hub_download from ultralytics import YOLO SPACE_ROOT = Path(__file__).resolve().parent DEFAULT_MODEL_FILE = "best.pt" DEFAULT_MODEL_PATH = SPACE_ROOT / DEFAULT_MODEL_FILE def _resolve_model_path() -> Path: if DEFAULT_MODEL_PATH.exists(): return DEFAULT_MODEL_PATH return Path( hf_hub_download( repo_id="acwz/TeaLeafDetection", filename="best.pt", ) ) @lru_cache(maxsize=1) def get_model() -> YOLO: """Load the YOLO model once and reuse it for all requests.""" model_path = _resolve_model_path() return YOLO(str(model_path), task="detect") def predict(image, confidence: float = 0.25) -> Tuple[Any, Dict[str, Any]]: """Run inference and return the plotted image plus structured predictions.""" if image is None: raise gr.Error("Please upload an image before running detection.") model = get_model() results = model.predict(source=image, conf=confidence, save=False, verbose=False) if not results: return image, {"error": "No predictions were produced."} result = results[0] plotted = result.plot() detections_raw = result.tojson() try: detections = json.loads(detections_raw) if isinstance(detections_raw, str) else detections_raw except json.JSONDecodeError: detections = {"raw": detections_raw} return plotted, detections CSS = """ :root { --bg-start: #f2f8f1; --bg-end: #dbe8db; --panel: rgba(255, 255, 255, 0.82); --panel-border: rgba(39, 89, 59, 0.12); --text: #12311f; --muted: #4f6658; --accent: #1f7a4d; } body, .gradio-container { background: linear-gradient(160deg, var(--bg-start), var(--bg-end)); color: var(--text); } .wrap { max-width: 1120px !important; } .hero { border: 1px solid var(--panel-border); border-radius: 28px; background: linear-gradient(135deg, rgba(31, 122, 77, 0.94), rgba(18, 49, 31, 0.94)); color: white; padding: 1.35rem 1.5rem; box-shadow: 0 18px 40px rgba(18, 49, 31, 0.16); } .hero h1 { margin: 0; font-size: clamp(1.7rem, 3.8vw, 2.8rem); line-height: 1.05; } .hero p { margin: 0.5rem 0 0; opacity: 0.92; } .panel { border-radius: 24px !important; border: 1px solid var(--panel-border) !important; background: var(--panel) !important; backdrop-filter: blur(10px); box-shadow: 0 12px 30px rgba(18, 49, 31, 0.08); } .panel h3, .panel h4, .panel label { color: var(--text) !important; } .panel .wrap { max-width: none !important; } .gr-button-primary { background: linear-gradient(135deg, #1f7a4d, #2ea66b) !important; border: 0 !important; } .gr-button-secondary { border: 1px solid rgba(31, 122, 77, 0.26) !important; } """ with gr.Blocks(title="Tea Leaf Detection API", theme=gr.themes.Soft(), css=CSS) as demo: with gr.Column(elem_classes=["wrap"]): gr.Markdown( """
Upload a tea leaf image, run YOLO inference, and call the same endpoint from code via the Gradio API.