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 --- MODEL_REPO = "youkii-xr/hieroglyphic-detection" MODEL_FILENAME = "best.pt" print(f"Server Status: Public MCP Endpoint Active") print(f"Security: Model weights are protected (private repo)") try: model_path = hf_hub_download( repo_id=MODEL_REPO, filename=MODEL_FILENAME, token=os.environ.get("HF_TOKEN") ) print(f"System: Model loaded successfully from private storage.") model = YOLO(model_path) except Exception as e: print(f"CRITICAL ERROR: Could not load model. Check HF_TOKEN in Settings. {e}") model = None # --- 2. DETECTION LOGIC --- def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25): if image is None: return None, {"error": "No image provided"} if model is None: return None, {"error": "Server Error: Model not loaded."} try: results = model.predict( source=image, conf=conf_threshold, iou=0.45, imgsz=640, verbose=False, device='cpu', max_det=300 ) annotated_array = results[0].plot() annotated_image = Image.fromarray(annotated_array[..., ::-1]) 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), "box": [round(x, 1) for x in box.xyxy[0].cpu().numpy().tolist()] }) summary = { "status": "success", "total_found": len(detections), "unique_symbols": list(gardiner_counts.keys()), "counts": gardiner_counts } return annotated_image, summary except Exception as e: print(f"Inference Error: {e}") return None, {"error": str(e)} # --- 3. UI/UX CONFIGURATION --- custom_css = """ @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap'); /* Force background color via CSS since Theme is disabled */ body, .gradio-container { background-color: #fdf6e3 !important; } h1, h2, h3, span { font-family: 'Cinzel', serif !important; color: #8b4513 !important; } /* The Magic Button Animation */ @keyframes goldenPulse { 0% { box-shadow: 0 0 0 0 rgba(212, 175, 55, 0.7); transform: scale(1); } 50% { box-shadow: 0 0 0 10px rgba(212, 175, 55, 0); transform: scale(1.02); } 100% { box-shadow: 0 0 0 0 rgba(212, 175, 55, 0); transform: scale(1); } } #magic-btn { background: linear-gradient(135deg, #b8860b 0%, #d4af37 100%); border: 1px solid #8b4513; color: white !important; font-family: 'Cinzel', serif; font-weight: bold; font-size: 1.2em; animation: goldenPulse 2s infinite; transition: all 0.3s ease; } #magic-btn:hover { animation: none; transform: translateY(-2px); box-shadow: 0 5px 15px rgba(139, 69, 19, 0.4); } .json-output { background-color: #fff8dc; border: 1px solid #d4af37; } """ claude_config_content = """ { "mcpServers": { "hieroglyph-detector": { "command": "uv", "args": [ "python", "client.py" ], "env": { "GRADIO_SERVER_URL": "YOUR_SPACE_URL_HERE" } } } } """ # --- 4. BUILD THE APP WITH BLOCKS --- # FIX: Removed 'theme' argument to prevent TypeError on older Gradio versions with gr.Blocks(css=custom_css, title="Horus Vision") as demo: with gr.Row(): with gr.Column(scale=1): gr.Markdown(""" # 👁️ Horus Vision ### AI Hieroglyphic Decoder """) with gr.Column(scale=3): gr.Markdown(""" > *"The eye sees all."* Upload an image of Egyptian text. """) with gr.Tabs(): with gr.TabItem("🔍 Decoder"): with gr.Row(): with gr.Column(): img_input = gr.Image(type="pil", label="Upload Papyrus", sources=["upload", "clipboard"]) conf_slider = gr.Slider(minimum=0.1, maximum=1.0, value=0.25, label="Confidence") analyze_btn = gr.Button("🔮 Decipher Symbols", elem_id="magic-btn", variant="primary") with gr.Column(): img_output = gr.Image(label="Annotated Result", interactive=False) json_output = gr.JSON(label="Glyph Data", elem_classes="json-output") analyze_btn.click( fn=detect_hieroglyphs, inputs=[img_input, conf_slider], outputs=[img_output, json_output] ) with gr.TabItem("🤖 Connect to Claude"): gr.Markdown("### MCP Server Configuration") gr.Code(value=claude_config_content, language="json", label="claude_desktop_config.json", interactive=False) # --- 5. LAUNCH --- if __name__ == "__main__": demo.launch( mcp_server=True, ssr_mode=False, allowed_paths=["/tmp"] )