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" os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics" try: print("System: Downloading model weights...") model_path = hf_hub_download( repo_id=MODEL_REPO, filename=MODEL_FILENAME, token=os.environ.get("HF_TOKEN") ) model = YOLO(model_path) print("System: Model loaded successfully.") except Exception as e: print(f"Error: {e}") model = None # --- 2. LOGIC --- def generate_human_report(detections, counts): if not detections: return "The Eye of Horus sees no intelligible glyphs in this image." total = len(detections) sorted_counts = sorted(counts.items(), key=lambda item: item[1], reverse=True) report = f"🔎 ANALYSIS COMPLETE\n" report += f"-----------------------------------\n" report += f"Total Glyphs Detected: {total}\n\n" report += "📝 SYMBOL INVENTORY:\n" for code, count in sorted_counts: report += f"• Gardiner Code '{code}': {count} instance(s)\n" report += f"\n-----------------------------------\n" report += f"CONFIDENCE ASSESSMENT: High\n" report += f"TRANSLATION STATUS: Ready for context analysis." return report def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25): if image is None: return None, None, "Please provide an image input." if model is None: return None, {"error": "Model failed"}, "System 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)}) summary_json = { "status": "success", "total_found": len(detections), "counts": gardiner_counts } text_report = generate_human_report(detections, gardiner_counts) return annotated_image, summary_json, text_report except Exception as e: return None, {"error": str(e)}, f"Critical Error: {str(e)}" # --- 3. THE MYSTICAL UI (CSS) --- # We use CSS Variables (--name) to handle Light/Dark mode switching automatically. # Base64 SVG Cursor (Eye of Horus) cursor_url = "url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIj4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiNkNGFmMzciIHN0cm9rZS13aWR0aD0iMiI+CiAgICA8cGF0aCBkPSZNMTYsOCBDNiwyMCAyNiwyMCAxNiw4IFoiIGZpbGw9InJnYmEoMjEyLCAxNzUsIDU1LqmAuMykiLz4KICAgIDxjaXJjbGUgY3g9IjE2IiBjeT0iMTUiIHI9IjMiIGZpbGw9IiNkNGFmMzciLz4KICAgIDxwYXRoIGQ9Ik0xNiwyMiBMMTYsMjggTDEwLDI4Ii8+CiAgPC9nPgo8L3N2Zz4=')" custom_css = f""" @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap'); /* --- 1. THEME VARIABLES (DAY vs NIGHT) --- */ :root {{ /* LIGHT MODE (PAPYRUS DAY) */ --bg-gradient: linear-gradient(135deg, #fdf6e3 0%, #f5e6d3 100%); --card-bg: rgba(255, 255, 255, 0.6); --text-primary: #5c4033; /* Dark Brown */ --text-accent: #b8860b; /* Dark Gold */ --border-color: #d4af37; --highlight-bg: rgba(212, 175, 55, 0.1); --warning-bg: rgba(255, 0, 0, 0.05); --warning-border: #cc0000; }} .dark {{ /* DARK MODE (EGYPTIAN NIGHT) */ --bg-gradient: radial-gradient(circle at 50% 0%, #1a1f35 0%, #050510 100%); --card-bg: rgba(10, 15, 30, 0.7); --text-primary: #e0e7ff; /* Light Blue */ --text-accent: #ffd700; /* Bright Gold */ --border-color: #d4af37; --highlight-bg: rgba(212, 175, 55, 0.1); --warning-bg: rgba(255, 99, 71, 0.1); --warning-border: #ff6b6b; }} /* --- 2. GLOBAL STYLES --- */ body, .gradio-container {{ background: var(--bg-gradient) !important; font-family: 'Cinzel', serif !important; color: var(--text-primary) !important; /* MYSTICAL CURSOR */ cursor: {cursor_url} 16 16, auto !important; }} /* BUTTONS & LINKS CURSOR OVERRIDE */ button, a, .cursor-pointer {{ cursor: {cursor_url} 16 16, pointer !important; }} /* --- 3. ANIMATED CARDS --- */ .card {{ background: var(--card-bg) !important; border: 1px solid rgba(212, 175, 55, 0.3) !important; border-radius: 15px; padding: 20px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); backdrop-filter: blur(10px); margin-bottom: 20px; transition: all 0.4s ease; /* Animation Speed */ }} /* HOVER ANIMATION: Glow and Lift */ .card:hover {{ transform: translateY(-5px); border-color: var(--border-color) !important; box-shadow: 0 0 20px rgba(212, 175, 55, 0.4), inset 0 0 10px rgba(212, 175, 55, 0.1); }} .card-title {{ font-size: 16px; font-weight: 700; color: var(--text-accent); text-transform: uppercase; letter-spacing: 2px; border-bottom: 1px solid rgba(212, 175, 55, 0.2); padding-bottom: 8px; margin-bottom: 15px; }} /* --- 4. BUTTONS --- */ button.primary-btn {{ background: linear-gradient(135deg, #b8860b 0%, #d4af37 100%) !important; border: 1px solid #ffd700 !important; color: #fff !important; font-weight: bold !important; font-family: 'Cinzel', serif !important; text-transform: uppercase; letter-spacing: 1px; transition: all 0.3s ease; }} button.primary-btn:hover {{ transform: scale(1.02); box-shadow: 0 0 25px rgba(212, 175, 55, 0.6); }} /* --- 5. IMPORTANT NOTES HIGHLIGHTING --- */ .warning-box {{ background-color: var(--warning-bg); border-left: 5px solid var(--warning-border); padding: 15px; margin: 10px 0; border-radius: 0 10px 10px 0; font-family: 'Courier Prime', monospace; /* Monospace for tech look */ font-size: 13px; }} .highlight-path {{ background-color: rgba(255, 255, 255, 0.1); border: 1px dashed var(--border-color); padding: 2px 6px; border-radius: 4px; color: var(--text-accent); font-weight: bold; }} /* REPORT TEXTBOX */ .report-box textarea {{ background-color: rgba(0,0,0,0.2) !important; border: 1px solid var(--border-color) !important; font-family: 'Courier Prime', monospace !important; color: var(--text-accent) !important; }} /* REMOVE DEFAULT GRADIO BORDERS */ .gradio-image, .gradio-json {{ background: transparent !important; border: none !important; }} """ # HTML HEADER header_html = """
HIEROGLYPHIC TRANSLATION & PRESERVATION
To preserve the past is to save the future.
This tool uses AI to digitize Ancient Egyptian inscriptions.
(Hover over cards to see the aura)
C:\\Python313\\python.exe.where python.%APPDATA%\\Claude\\claude_desktop_config.json