import gradio as gr from ultralytics import YOLO from PIL import Image import os from huggingface_hub import hf_hub_download import numpy as np # --- 1. SETUP & MODEL LOADING --- # We download the model securely at startup MODEL_REPO = "youkii-xr/hieroglyphic-detection" # <--- REPLACE THIS MODEL_FILENAME = "best.pt" print(f"Attempting to download {MODEL_FILENAME} from {MODEL_REPO}...") try: model_path = hf_hub_download( repo_id=MODEL_REPO, filename=MODEL_FILENAME, token=os.environ.get("HF_TOKEN") # Needs 'HF_TOKEN' secret in Space settings ) print(f"Model downloaded to: {model_path}") model = YOLO(model_path) except Exception as e: print(f"CRITICAL ERROR loading model: {e}") model = None # --- 2. DETECTION LOGIC --- # NOTE: Type hints (image: Image.Image) and Docstrings are MANDATORY for MCP def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25): """ Detects Egyptian hieroglyph symbols in an image. Args: image: The image to analyze (uploaded file). conf_threshold: Confidence threshold for detection (default 0.25). Returns: A tuple containing the annotated image with bounding boxes and a JSON summary of findings. """ if image is None: return None, {"error": "No image provided"} if model is None: return None, {"error": "Model failed to load on server."} try: # Run Inference results = model.predict( source=image, conf=conf_threshold, iou=0.45, imgsz=640, verbose=False, device='cpu', # Spaces usually run on CPU unless you pay for GPU max_det=300 ) # 1. Generate Visual Output # plot() returns BGR numpy array, convert to RGB PIL annotated_array = results[0].plot() annotated_image = Image.fromarray(annotated_array[..., ::-1]) # 2. Generate Data Output (JSON) detections = [] gardiner_counts = {} for box in results[0].boxes: if box.cls.numel() > 0: cls_id = int(box.cls[0]) if 0 <= cls_id < len(model.names): code = model.names[cls_id] conf = float(box.conf[0]) if code not in gardiner_counts: gardiner_counts[code] = 0 gardiner_counts[code] += 1 detections.append({ "code": code, "confidence": round(conf, 2), # Convert bbox to list for JSON serialization "box": [round(x, 1) for x in box.xyxy[0].cpu().numpy().tolist()] }) summary = { "status": "success", "total_detected": len(detections), "unique_symbols": list(gardiner_counts.keys()), "counts": gardiner_counts } return annotated_image, summary except Exception as e: print(f"Error during inference: {e}") return None, {"error": str(e)} # --- 3. INTERFACE & SERVER --- # mcp_server=True creates the endpoint automatically demo = gr.Interface( fn=detect_hieroglyphs, inputs=[ gr.Image(type="pil", label="Upload Image"), gr.Number(value=0.25, label="Confidence Threshold") ], outputs=[ gr.Image(label="Annotated Result"), gr.JSON(label="Detection Data") ], title="Egyptian Hieroglyph MCP Server", description="MCP-compatible server for Hieroglyph Detection. Connect this to Claude Desktop." ) if __name__ == "__main__": demo.launch(mcp_server=True)