youkii-xr commited on
Commit
da8eae7
ยท
verified ยท
1 Parent(s): cea3ef8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +175 -107
app.py CHANGED
@@ -5,47 +5,34 @@ import os
5
  from huggingface_hub import hf_hub_download
6
  import numpy as np
7
 
8
- # --- 1. SETUP & MODEL LOADING ---
9
  MODEL_REPO = "youkii-xr/hieroglyphic-detection"
10
  MODEL_FILENAME = "best.pt"
11
 
12
- print(f"Server Status: Public MCP Endpoint Active")
13
- print(f"Security: Model weights are protected (private repo)")
14
-
15
  try:
16
  model_path = hf_hub_download(
17
  repo_id=MODEL_REPO,
18
  filename=MODEL_FILENAME,
19
  token=os.environ.get("HF_TOKEN")
20
  )
21
- print(f"System: Model loaded successfully from private storage.")
22
  model = YOLO(model_path)
23
  except Exception as e:
24
- print(f"CRITICAL ERROR: Could not load model. Check HF_TOKEN in Settings. {e}")
25
  model = None
26
 
27
- # --- 2. DETECTION LOGIC ---
28
  def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25):
29
- if image is None:
30
- return None, {"error": "No image provided"}
31
-
32
- if model is None:
33
- return None, {"error": "Server Error: Model not loaded."}
34
 
35
  try:
36
- results = model.predict(
37
- source=image,
38
- conf=conf_threshold,
39
- iou=0.45,
40
- imgsz=640,
41
- verbose=False,
42
- device='cpu',
43
- max_det=300
44
- )
45
 
 
46
  annotated_array = results[0].plot()
47
  annotated_image = Image.fromarray(annotated_array[..., ::-1])
48
 
 
49
  detections = []
50
  gardiner_counts = {}
51
 
@@ -55,131 +42,212 @@ def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25):
55
  if 0 <= cls_id < len(model.names):
56
  code = model.names[cls_id]
57
  conf = float(box.conf[0])
58
-
59
  if code not in gardiner_counts: gardiner_counts[code] = 0
60
  gardiner_counts[code] += 1
61
-
62
- detections.append({
63
- "code": code,
64
- "confidence": round(conf, 2),
65
- "box": [round(x, 1) for x in box.xyxy[0].cpu().numpy().tolist()]
66
- })
67
 
68
  summary = {
69
  "status": "success",
70
  "total_found": len(detections),
71
- "unique_symbols": list(gardiner_counts.keys()),
72
  "counts": gardiner_counts
73
  }
74
-
75
  return annotated_image, summary
76
-
77
  except Exception as e:
78
- print(f"Inference Error: {e}")
79
  return None, {"error": str(e)}
80
 
81
- # --- 3. UI/UX CONFIGURATION ---
82
 
 
83
  custom_css = """
84
  @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap');
85
 
86
- /* Force background color via CSS since Theme is disabled */
 
 
 
 
 
 
 
87
  body, .gradio-container {
88
- background-color: #fdf6e3 !important;
 
89
  }
90
 
91
- h1, h2, h3, span {
92
- font-family: 'Cinzel', serif !important;
93
- color: #8b4513 !important;
 
 
 
 
 
94
  }
95
 
96
- /* The Magic Button Animation */
97
- @keyframes goldenPulse {
98
- 0% { box-shadow: 0 0 0 0 rgba(212, 175, 55, 0.7); transform: scale(1); }
99
- 50% { box-shadow: 0 0 0 10px rgba(212, 175, 55, 0); transform: scale(1.02); }
100
- 100% { box-shadow: 0 0 0 0 rgba(212, 175, 55, 0); transform: scale(1); }
 
 
 
 
 
 
 
101
  }
102
 
103
- #magic-btn {
104
- background: linear-gradient(135deg, #b8860b 0%, #d4af37 100%);
105
- border: 1px solid #8b4513;
106
- color: white !important;
107
- font-family: 'Cinzel', serif;
108
- font-weight: bold;
109
- font-size: 1.2em;
110
- animation: goldenPulse 2s infinite;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  transition: all 0.3s ease;
 
112
  }
113
-
114
- #magic-btn:hover {
115
- animation: none;
116
  transform: translateY(-2px);
117
- box-shadow: 0 5px 15px rgba(139, 69, 19, 0.4);
118
  }
119
 
120
- .json-output {
121
- background-color: #fff8dc;
122
- border: 1px solid #d4af37;
 
 
123
  }
124
  """
125
 
126
- claude_config_content = """
127
- {
128
- "mcpServers": {
129
- "hieroglyph-detector": {
130
- "command": "uv",
131
- "args": [
132
- "python",
133
- "client.py"
134
- ],
135
- "env": {
136
- "GRADIO_SERVER_URL": "YOUR_SPACE_URL_HERE"
137
- }
138
- }
139
- }
140
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  """
142
 
143
- # --- 4. BUILD THE APP WITH BLOCKS ---
144
- # FIX: Removed 'theme' argument to prevent TypeError on older Gradio versions
145
  with gr.Blocks(css=custom_css, title="Horus Vision") as demo:
146
 
 
 
 
 
147
  with gr.Row():
 
148
  with gr.Column(scale=1):
149
- gr.Markdown("""
150
- # ๐Ÿ‘๏ธ Horus Vision
151
- ### AI Hieroglyphic Decoder
152
- """)
153
- with gr.Column(scale=3):
154
- gr.Markdown("""
155
- > *"The eye sees all."* Upload an image of Egyptian text.
 
 
 
 
156
  """)
157
-
158
- with gr.Tabs():
159
- with gr.TabItem("๐Ÿ” Decoder"):
160
- with gr.Row():
161
- with gr.Column():
162
- img_input = gr.Image(type="pil", label="Upload Papyrus", sources=["upload", "clipboard"])
163
- conf_slider = gr.Slider(minimum=0.1, maximum=1.0, value=0.25, label="Confidence")
164
- analyze_btn = gr.Button("๐Ÿ”ฎ Decipher Symbols", elem_id="magic-btn", variant="primary")
165
- with gr.Column():
166
- img_output = gr.Image(label="Annotated Result", interactive=False)
167
- json_output = gr.JSON(label="Glyph Data", elem_classes="json-output")
168
 
169
- analyze_btn.click(
170
- fn=detect_hieroglyphs,
171
- inputs=[img_input, conf_slider],
172
- outputs=[img_output, json_output]
173
- )
174
 
175
- with gr.TabItem("๐Ÿค– Connect to Claude"):
176
- gr.Markdown("### MCP Server Configuration")
177
- gr.Code(value=claude_config_content, language="json", label="claude_desktop_config.json", interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
- # --- 5. LAUNCH ---
180
  if __name__ == "__main__":
181
- demo.launch(
182
- mcp_server=True,
183
- ssr_mode=False,
184
- allowed_paths=["/tmp"]
185
- )
 
5
  from huggingface_hub import hf_hub_download
6
  import numpy as np
7
 
8
+ # --- 1. SETUP & MODEL LOADING (LOGIC UNCHANGED) ---
9
  MODEL_REPO = "youkii-xr/hieroglyphic-detection"
10
  MODEL_FILENAME = "best.pt"
11
 
 
 
 
12
  try:
13
  model_path = hf_hub_download(
14
  repo_id=MODEL_REPO,
15
  filename=MODEL_FILENAME,
16
  token=os.environ.get("HF_TOKEN")
17
  )
 
18
  model = YOLO(model_path)
19
  except Exception as e:
20
+ print(f"Error: {e}")
21
  model = None
22
 
23
+ # --- 2. DETECTION LOGIC (LOGIC UNCHANGED) ---
24
  def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25):
25
+ if image is None: return None, None
26
+ if model is None: return None, None
 
 
 
27
 
28
  try:
29
+ results = model.predict(source=image, conf=conf_threshold, iou=0.45, imgsz=640, verbose=False, device='cpu', max_det=300)
 
 
 
 
 
 
 
 
30
 
31
+ # Visual
32
  annotated_array = results[0].plot()
33
  annotated_image = Image.fromarray(annotated_array[..., ::-1])
34
 
35
+ # Data
36
  detections = []
37
  gardiner_counts = {}
38
 
 
42
  if 0 <= cls_id < len(model.names):
43
  code = model.names[cls_id]
44
  conf = float(box.conf[0])
 
45
  if code not in gardiner_counts: gardiner_counts[code] = 0
46
  gardiner_counts[code] += 1
47
+ detections.append({"code": code, "confidence": round(conf, 2)})
 
 
 
 
 
48
 
49
  summary = {
50
  "status": "success",
51
  "total_found": len(detections),
 
52
  "counts": gardiner_counts
53
  }
 
54
  return annotated_image, summary
 
55
  except Exception as e:
 
56
  return None, {"error": str(e)}
57
 
58
+ # --- 3. UI/UX: THE EGYPTIAN NIGHT STYLE ---
59
 
60
+ # This CSS mimics the VoiceKit structure but swaps Purple/Indigo for Lapis/Gold
61
  custom_css = """
62
  @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap');
63
 
64
+ :root {
65
+ --body-background-fill: #050510 !important;
66
+ --background-fill-primary: #0a0f1e !important;
67
+ --border-color-primary: #d4af37 !important; /* Gold */
68
+ --text-body: #e0e7ff !important;
69
+ --gold-glow: 0 0 15px rgba(212, 175, 55, 0.3);
70
+ }
71
+
72
  body, .gradio-container {
73
+ background: radial-gradient(circle at 50% 0%, #1a1f35 0%, #050510 100%) !important;
74
+ font-family: 'Cinzel', serif !important; /* The Ancient Font */
75
  }
76
 
77
+ /* --- THE GLASS CARDS --- */
78
+ .card {
79
+ background: rgba(10, 15, 30, 0.6) !important;
80
+ border: 1px solid rgba(212, 175, 55, 0.3) !important;
81
+ border-radius: 20px;
82
+ padding: 24px;
83
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
84
+ backdrop-filter: blur(10px);
85
  }
86
 
87
+ .card-title {
88
+ font-size: 18px;
89
+ font-weight: 700;
90
+ color: #d4af37; /* Gold */
91
+ text-transform: uppercase;
92
+ letter-spacing: 2px;
93
+ margin-bottom: 20px;
94
+ display: flex;
95
+ align-items: center;
96
+ gap: 10px;
97
+ border-bottom: 1px solid rgba(212, 175, 55, 0.1);
98
+ padding-bottom: 10px;
99
  }
100
 
101
+ /* --- THE TERMINAL WINDOW (Quick Start) --- */
102
+ .terminal-window {
103
+ background: #0f0f15;
104
+ border: 1px solid #333;
105
+ border-radius: 12px;
106
+ overflow: hidden;
107
+ font-family: 'Courier New', monospace;
108
+ box-shadow: 0 10px 30px rgba(0,0,0,0.8);
109
+ }
110
+ .terminal-header {
111
+ background: #1a1a20;
112
+ padding: 10px 15px;
113
+ display: flex;
114
+ gap: 8px;
115
+ border-bottom: 1px solid #333;
116
+ }
117
+ .dot { width: 12px; height: 12px; border-radius: 50%; }
118
+ .red { background: #ff5f56; } .yellow { background: #ffbd2e; } .green { background: #27c93f; }
119
+
120
+ .terminal-body {
121
+ padding: 20px;
122
+ color: #a9b1d6;
123
+ font-size: 13px;
124
+ line-height: 1.6;
125
+ }
126
+ .json-key { color: #7aa2f7; } /* Blue */
127
+ .json-string { color: #e0af68; } /* Gold-ish */
128
+
129
+ /* --- BUTTONS --- */
130
+ /* The "Action" Button */
131
+ button.primary-btn {
132
+ background: linear-gradient(135deg, #b8860b 0%, #d4af37 100%) !important;
133
+ border: 1px solid #ffd700 !important;
134
+ color: #000 !important;
135
+ font-weight: bold !important;
136
+ font-family: 'Cinzel', serif !important;
137
+ text-transform: uppercase;
138
+ letter-spacing: 1px;
139
  transition: all 0.3s ease;
140
+ box-shadow: var(--gold-glow);
141
  }
142
+ button.primary-btn:hover {
 
 
143
  transform: translateY(-2px);
144
+ box-shadow: 0 0 25px rgba(212, 175, 55, 0.6);
145
  }
146
 
147
+ /* --- INPUTS & OUTPUTS --- */
148
+ /* Transparent backgrounds for images/json to blend into cards */
149
+ .gradio-image, .gradio-json {
150
+ background: transparent !important;
151
+ border: none !important;
152
  }
153
  """
154
 
155
+ # HTML for the Header (Floating Style)
156
+ header_html = """
157
+ <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 30px; padding: 20px;">
158
+ <div style="display: flex; align-items: center; gap: 20px;">
159
+ <svg width="60" height="60" viewBox="0 0 100 100" fill="none">
160
+ <path d="M10,50 Q50,10 90,50 Q50,90 10,50" stroke="#d4af37" stroke-width="3" fill="none"/>
161
+ <circle cx="50" cy="50" r="15" fill="#d4af37"/>
162
+ <path d="M50,65 L50,90 L30,90" stroke="#d4af37" stroke-width="3" fill="none"/>
163
+ </svg>
164
+ <div>
165
+ <h1 style="margin: 0; font-size: 36px; color: #d4af37; text-shadow: 0 0 10px rgba(212,175,55,0.5);">HORUS VISION</h1>
166
+ <p style="margin: 0; color: #a5b4fc; font-size: 14px; letter-spacing: 2px;">HIEROGLYPHIC INTELLIGENCE SYSTEM</p>
167
+ </div>
168
+ </div>
169
+ <div style="border: 1px solid #d4af37; padding: 5px 15px; border-radius: 20px; color: #d4af37; font-size: 12px;">
170
+ โ— SYSTEM ONLINE
171
+ </div>
172
+ </div>
173
+ """
174
+
175
+ # HTML for the Claude Config "Terminal"
176
+ terminal_html = """
177
+ <div class="card">
178
+ <div class="card-title">โšก CLAUDE DESKTOP CONFIG</div>
179
+ <div class="terminal-window">
180
+ <div class="terminal-header">
181
+ <div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div>
182
+ <div style="margin-left: auto; color: #555; font-size: 10px;">claude_config.json</div>
183
+ </div>
184
+ <div class="terminal-body">
185
+ {<br>
186
+ &nbsp;&nbsp;<span class="json-key">"mcpServers"</span>: {<br>
187
+ &nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"horus-vision"</span>: {<br>
188
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"command"</span>: <span class="json-string">"uv"</span>,<br>
189
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"args"</span>: [<span class="json-string">"python"</span>, <span class="json-string">"client.py"</span>]<br>
190
+ &nbsp;&nbsp;&nbsp;&nbsp;}<br>
191
+ &nbsp;&nbsp;}<br>
192
+ }
193
+ </div>
194
+ </div>
195
+ </div>
196
  """
197
 
198
+ # --- 4. APP ASSEMBLY ---
 
199
  with gr.Blocks(css=custom_css, title="Horus Vision") as demo:
200
 
201
+ # 1. Header Injection
202
+ gr.HTML(header_html)
203
+
204
+ # 2. Top Row: Info & Config
205
  with gr.Row():
206
+ # Left: The Model Info Card
207
  with gr.Column(scale=1):
208
+ gr.HTML("""
209
+ <div class="card" style="height: 100%;">
210
+ <div class="card-title">๐Ÿ“œ MISSION BRIEF</div>
211
+ <p style="color: #ccc; line-height: 1.6;">
212
+ This system utilizes the YOLOv8 architecture to detect and classify Gardiner codes from images of papyrus or stone.
213
+ <br><br>
214
+ <span style="color: #d4af37;">> Model:</span> YOLOv8 Custom<br>
215
+ <span style="color: #d4af37;">> Weights:</span> Private (Secure)<br>
216
+ <span style="color: #d4af37;">> Security:</span> MCP Standard
217
+ </p>
218
+ </div>
219
  """)
 
 
 
 
 
 
 
 
 
 
 
220
 
221
+ # Right: The Terminal Card
222
+ with gr.Column(scale=1):
223
+ gr.HTML(terminal_html)
 
 
224
 
225
+ # 3. Main Detector Area (The "Workstation")
226
+ gr.HTML("<br>") # Spacer
227
+
228
+ with gr.Row():
229
+ with gr.Column():
230
+ # We wrap the input area in a HTML card wrapper logic
231
+ gr.HTML('<div class="card"> <div class="card-title">๐Ÿ‘๏ธ INPUT SOURCE</div>')
232
+ img_input = gr.Image(type="pil", label="Upload Papyrus", sources=["upload", "clipboard", "webcam"], elem_classes="gradio-image")
233
+ conf_slider = gr.Slider(0.1, 1.0, 0.25, step=0.05, label="Detection Confidence")
234
+
235
+ # The Golden Button
236
+ analyze_btn = gr.Button("๐Ÿ”ฎ DECIPHER SYMBOLS", elem_classes="primary-btn")
237
+ gr.HTML('</div>') # Close card
238
+
239
+ with gr.Column():
240
+ gr.HTML('<div class="card"> <div class="card-title">๐Ÿ’Ž DECODED ARTIFACT</div>')
241
+ img_output = gr.Image(label="Annotated Result", interactive=False, elem_classes="gradio-image")
242
+ json_output = gr.JSON(label="Glyph Data", elem_classes="gradio-json")
243
+ gr.HTML('</div>') # Close card
244
+
245
+ # 4. Wiring
246
+ analyze_btn.click(
247
+ fn=detect_hieroglyphs,
248
+ inputs=[img_input, conf_slider],
249
+ outputs=[img_output, json_output]
250
+ )
251
 
 
252
  if __name__ == "__main__":
253
+ demo.launch(mcp_server=True, ssr_mode=False, allowed_paths=["/tmp"])