Spaces:
Runtime error
Runtime error
| 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"] | |
| ) |