Rosetta-Decoder / app.py
youkii-xr's picture
Update app.py
f6fedbb verified
Raw
History Blame
7.04 kB
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 (UNCHANGED) ---
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 (UNCHANGED) ---
def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25):
"""
Analyzes an image to find Egyptian hieroglyphs.
"""
if image is None:
return None, {"error": "No image provided"}
if model is None:
return None, {"error": "Server Error: Model not loaded."}
try:
# Run Inference
results = model.predict(
source=image,
conf=conf_threshold,
iou=0.45,
imgsz=640,
verbose=False,
device='cpu',
max_det=300
)
# 1. Generate Visual Output (RGB Image)
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),
"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 ---
# A. Custom CSS for Animations and Fonts
# Imports 'Cinzel' font for that ancient feel and defines a pulsing gold button
custom_css = """
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap');
body, .gradio-container {
background-color: #fdf6e3; /* Light Papyrus */
}
h1, h2, h3 {
font-family: 'Cinzel', serif !important;
color: #8b4513 !important; /* SaddleBrown */
}
/* 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%); /* Gold Gradient */
border: 1px solid #8b4513;
color: white;
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; /* Cornsilk */
border: 1px solid #d4af37;
}
"""
# B. Custom Theme (Sand, Gold, Lapis)
theme = gr.themes.Soft(
primary_hue="amber",
secondary_hue="slate",
neutral_hue="stone",
).set(
body_background_fill="#fdf6e3",
block_background_fill="#ffffff",
block_border_color="#d4af37", # Gold borders
button_primary_background_fill="#d4af37",
button_primary_text_color="white",
)
# C. Claude Configuration JSON Generator
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 ---
with gr.Blocks(theme=theme, css=custom_css, title="Horus Vision") as demo:
# Header Area
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("""
# ๐Ÿ‘๏ธ Horus Vision
### AI Hieroglyphic Decoder & Classifier
""")
with gr.Column(scale=3):
gr.Markdown("""
> *"The eye sees all."* Upload an image of Egyptian text.
> This AI identifies Gardiner codes using the YOLO architecture.
""")
# Main Tabs
with gr.Tabs():
# TAB 1: The Detector
with gr.TabItem("๐Ÿ” Decoder"):
with gr.Row():
# Left Column: Inputs
with gr.Column():
img_input = gr.Image(type="pil", label="Upload Papyrus/Image", sources=["upload", "clipboard"])
conf_slider = gr.Slider(minimum=0.1, maximum=1.0, value=0.25, step=0.05, label="Confidence Threshold")
# The Animated Button
analyze_btn = gr.Button("๐Ÿ”ฎ Decipher Symbols", elem_id="magic-btn", variant="primary")
# Right Column: Outputs
with gr.Column():
img_output = gr.Image(label="Annotated Result", interactive=False)
json_output = gr.JSON(label="Glyph Data", elem_classes="json-output")
# Event Listener
analyze_btn.click(
fn=detect_hieroglyphs,
inputs=[img_input, conf_slider],
outputs=[img_output, json_output]
)
# TAB 2: Claude Desktop Config
with gr.TabItem("๐Ÿค– Connect to Claude"):
gr.Markdown("### MCP Server Configuration")
gr.Markdown("Copy the JSON below into your Claude Desktop config file to use this model directly within Claude.")
gr.Code(
value=claude_config_content,
language="json",
label="claude_desktop_config.json",
interactive=False
)
# Footer
gr.Markdown("---")
gr.Markdown(f"*Powered by YOLOv8 | Model: {MODEL_REPO} | Private Weights Active*")
# --- 5. LAUNCH ---
if __name__ == "__main__":
demo.launch(
mcp_server=True,
ssr_mode=False,
allowed_paths=["/tmp"]
)