youkii-xr commited on
Commit
eb337ce
ยท
verified ยท
1 Parent(s): 200b1a9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +199 -133
app.py CHANGED
@@ -8,8 +8,6 @@ import numpy as np
8
  # --- 1. SETUP & MODEL LOADING ---
9
  MODEL_REPO = "youkii-xr/hieroglyphic-detection"
10
  MODEL_FILENAME = "best.pt"
11
-
12
- # Fix for the Ultralytics config warning in containers
13
  os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
14
 
15
  try:
@@ -25,19 +23,43 @@ except Exception as e:
25
  print(f"Error: {e}")
26
  model = None
27
 
28
- # --- 2. DETECTION LOGIC ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25):
30
- if image is None: return None, None
31
- if model is None: return None, {"error": "Model failed to load"}
 
 
32
 
33
  try:
34
  results = model.predict(source=image, conf=conf_threshold, iou=0.45, imgsz=640, verbose=False, device='cpu', max_det=300)
35
 
36
- # Visual
37
  annotated_array = results[0].plot()
38
  annotated_image = Image.fromarray(annotated_array[..., ::-1])
39
 
40
- # Data
41
  detections = []
42
  gardiner_counts = {}
43
 
@@ -51,24 +73,29 @@ def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25):
51
  gardiner_counts[code] += 1
52
  detections.append({"code": code, "confidence": round(conf, 2)})
53
 
54
- summary = {
55
  "status": "success",
56
  "total_found": len(detections),
57
  "counts": gardiner_counts
58
  }
59
- return annotated_image, summary
60
- except Exception as e:
61
- return None, {"error": str(e)}
 
 
62
 
63
- # --- 3. UI/UX: THE EGYPTIAN NIGHT STYLE ---
 
64
 
 
65
  custom_css = """
66
  @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap');
 
67
 
68
  :root {
69
  --body-background-fill: #050510 !important;
70
  --background-fill-primary: #0a0f1e !important;
71
- --border-color-primary: #d4af37 !important; /* Gold */
72
  --text-body: #e0e7ff !important;
73
  --gold-glow: 0 0 15px rgba(212, 175, 55, 0.3);
74
  }
@@ -78,59 +105,28 @@ body, .gradio-container {
78
  font-family: 'Cinzel', serif !important;
79
  }
80
 
81
- /* --- THE GLASS CARDS --- */
82
  .card {
83
- background: rgba(10, 15, 30, 0.6) !important;
84
  border: 1px solid rgba(212, 175, 55, 0.3) !important;
85
- border-radius: 20px;
86
- padding: 24px;
87
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
88
  backdrop-filter: blur(10px);
 
89
  }
90
-
91
  .card-title {
92
- font-size: 18px;
93
  font-weight: 700;
94
  color: #d4af37;
95
  text-transform: uppercase;
96
  letter-spacing: 2px;
97
- margin-bottom: 20px;
98
- display: flex;
99
- align-items: center;
100
- gap: 10px;
101
- border-bottom: 1px solid rgba(212, 175, 55, 0.1);
102
- padding-bottom: 10px;
103
- }
104
-
105
- /* --- THE TERMINAL WINDOW (Quick Start) --- */
106
- .terminal-window {
107
- background: #0f0f15;
108
- border: 1px solid #333;
109
- border-radius: 12px;
110
- overflow: hidden;
111
- font-family: 'Courier New', monospace;
112
- box-shadow: 0 10px 30px rgba(0,0,0,0.8);
113
- }
114
- .terminal-header {
115
- background: #1a1a20;
116
- padding: 10px 15px;
117
- display: flex;
118
- gap: 8px;
119
- border-bottom: 1px solid #333;
120
  }
121
- .dot { width: 12px; height: 12px; border-radius: 50%; }
122
- .red { background: #ff5f56; } .yellow { background: #ffbd2e; } .green { background: #27c93f; }
123
 
124
- .terminal-body {
125
- padding: 20px;
126
- color: #a9b1d6;
127
- font-size: 13px;
128
- line-height: 1.6;
129
- }
130
- .json-key { color: #7aa2f7; }
131
- .json-string { color: #e0af68; }
132
-
133
- /* --- BUTTONS --- */
134
  button.primary-btn {
135
  background: linear-gradient(135deg, #b8860b 0%, #d4af37 100%) !important;
136
  border: 1px solid #ffd700 !important;
@@ -139,113 +135,183 @@ button.primary-btn {
139
  font-family: 'Cinzel', serif !important;
140
  text-transform: uppercase;
141
  letter-spacing: 1px;
142
- transition: all 0.3s ease;
143
  box-shadow: var(--gold-glow);
144
  }
145
- button.primary-btn:hover {
146
- transform: translateY(-2px);
147
- box-shadow: 0 0 25px rgba(212, 175, 55, 0.6);
 
 
 
 
 
148
  }
149
 
150
- /* --- ELEMENT VISIBILITY --- */
151
- .gradio-image, .gradio-json {
152
- background: transparent !important;
153
- border: none !important;
 
 
 
 
 
 
154
  }
 
 
 
155
  """
156
 
157
- # HTML for the Header (Floating Style)
158
  header_html = """
159
- <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 30px; padding: 20px;">
160
- <div style="display: flex; align-items: center; gap: 20px;">
161
- <svg width="60" height="60" viewBox="0 0 100 100" fill="none">
162
- <path d="M10,50 Q50,10 90,50 Q50,90 10,50" stroke="#d4af37" stroke-width="3" fill="none"/>
163
- <circle cx="50" cy="50" r="15" fill="#d4af37"/>
164
- <path d="M50,65 L50,90 L30,90" stroke="#d4af37" stroke-width="3" fill="none"/>
165
- </svg>
166
- <div>
167
- <h1 style="margin: 0; font-size: 36px; color: #d4af37; text-shadow: 0 0 10px rgba(212,175,55,0.5);">HORUS VISION</h1>
168
- <p style="margin: 0; color: #a5b4fc; font-size: 14px; letter-spacing: 2px;">HIEROGLYPHIC INTELLIGENCE SYSTEM</p>
169
- </div>
170
- </div>
171
- <div style="border: 1px solid #d4af37; padding: 5px 15px; border-radius: 20px; color: #d4af37; font-size: 12px;">
172
- โ— SYSTEM ONLINE
173
  </div>
174
  </div>
175
  """
176
 
177
- terminal_html = """
178
  <div class="card">
179
- <div class="card-title">โšก CLAUDE DESKTOP CONFIG</div>
180
- <div class="terminal-window">
181
- <div class="terminal-header">
182
- <div class="dot red"></div><div class="dot yellow"></div><div class="dot green"></div>
183
- <div style="margin-left: auto; color: #555; font-size: 10px;">claude_config.json</div>
184
- </div>
185
- <div class="terminal-body">
186
- {<br>
187
- &nbsp;&nbsp;<span class="json-key">"mcpServers"</span>: {<br>
188
- &nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"horus-vision"</span>: {<br>
189
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="json-key">"command"</span>: <span class="json-string">"uv"</span>,<br>
190
- &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>
191
- &nbsp;&nbsp;&nbsp;&nbsp;}<br>
192
- &nbsp;&nbsp;}<br>
193
- }
194
- </div>
 
195
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  </div>
197
  """
198
 
199
- # --- 4. APP ASSEMBLY ---
200
- # FIX: Removed 'css' argument here. We inject it via gr.HTML below.
201
  with gr.Blocks(title="Horus Vision") as demo:
202
-
203
- # FIX: Inject CSS directly into the HTML. This works on ALL Gradio versions.
204
  gr.HTML(f"<style>{custom_css}</style>")
205
-
206
- # 1. Header Injection
207
  gr.HTML(header_html)
 
208
 
209
- # 2. Top Row: Info & Config
210
  with gr.Row():
 
 
211
  with gr.Column(scale=1):
212
- gr.HTML("""
213
- <div class="card" style="height: 100%;">
214
- <div class="card-title">๐Ÿ“œ MISSION BRIEF</div>
215
- <p style="color: #ccc; line-height: 1.6;">
216
- This system utilizes the YOLOv8 architecture to detect and classify Gardiner codes.
217
- <br><br>
218
- <span style="color: #d4af37;">> Model:</span> YOLOv8 Custom<br>
219
- <span style="color: #d4af37;">> Security:</span> MCP Standard
220
- </p>
221
- </div>
222
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
 
 
 
224
  with gr.Column(scale=1):
225
- gr.HTML(terminal_html)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
- # 3. Main Detector Area
228
- gr.HTML("<br>")
 
 
 
 
 
229
 
230
- with gr.Row():
231
- with gr.Column():
232
- gr.HTML('<div class="card"> <div class="card-title">๐Ÿ‘๏ธ INPUT SOURCE</div>')
233
- img_input = gr.Image(type="pil", label="Upload Papyrus", sources=["upload", "clipboard", "webcam"], elem_classes="gradio-image")
234
- conf_slider = gr.Slider(0.1, 1.0, 0.25, step=0.05, label="Detection Confidence")
235
- analyze_btn = gr.Button("๐Ÿ”ฎ DECIPHER SYMBOLS", elem_classes="primary-btn")
236
- gr.HTML('</div>')
237
-
238
- with gr.Column():
239
- gr.HTML('<div class="card"> <div class="card-title">๐Ÿ’Ž DECODED ARTIFACT</div>')
240
- img_output = gr.Image(label="Annotated Result", interactive=False, elem_classes="gradio-image")
241
- json_output = gr.JSON(label="Glyph Data", elem_classes="gradio-json")
242
- gr.HTML('</div>')
243
-
244
- # 4. Wiring
245
- analyze_btn.click(
246
  fn=detect_hieroglyphs,
247
- inputs=[img_input, conf_slider],
248
- outputs=[img_output, json_output]
249
  )
250
 
251
  if __name__ == "__main__":
 
8
  # --- 1. SETUP & MODEL LOADING ---
9
  MODEL_REPO = "youkii-xr/hieroglyphic-detection"
10
  MODEL_FILENAME = "best.pt"
 
 
11
  os.environ["YOLO_CONFIG_DIR"] = "/tmp/Ultralytics"
12
 
13
  try:
 
23
  print(f"Error: {e}")
24
  model = None
25
 
26
+ # --- 2. LOGIC: DETECTION & REPORTING ---
27
+ def generate_human_report(detections, counts):
28
+ """Generates a natural language summary of the findings."""
29
+ if not detections:
30
+ return "The Eye of Horus sees no intelligible glyphs in this image."
31
+
32
+ total = len(detections)
33
+ # Sort counts by frequency
34
+ sorted_counts = sorted(counts.items(), key=lambda item: item[1], reverse=True)
35
+
36
+ report = f"๐Ÿ”Ž ANALYSIS COMPLETE\n"
37
+ report += f"-----------------------------------\n"
38
+ report += f"Total Glyphs Detected: {total}\n\n"
39
+ report += "๐Ÿ“ SYMBOL INVENTORY:\n"
40
+
41
+ for code, count in sorted_counts:
42
+ report += f"โ€ข Gardiner Code '{code}': {count} instance(s)\n"
43
+
44
+ report += f"\n-----------------------------------\n"
45
+ report += f"CONFIDENCE ASSESSMENT: High\n"
46
+ report += f"TRANSLATION STATUS: Ready for context analysis."
47
+ return report
48
+
49
  def detect_hieroglyphs(image: Image.Image, conf_threshold: float = 0.25):
50
+ if image is None:
51
+ return None, None, "Please provide an image input."
52
+ if model is None:
53
+ return None, {"error": "Model failed"}, "System Error: Model not loaded."
54
 
55
  try:
56
  results = model.predict(source=image, conf=conf_threshold, iou=0.45, imgsz=640, verbose=False, device='cpu', max_det=300)
57
 
58
+ # 1. Visual
59
  annotated_array = results[0].plot()
60
  annotated_image = Image.fromarray(annotated_array[..., ::-1])
61
 
62
+ # 2. Data
63
  detections = []
64
  gardiner_counts = {}
65
 
 
73
  gardiner_counts[code] += 1
74
  detections.append({"code": code, "confidence": round(conf, 2)})
75
 
76
+ summary_json = {
77
  "status": "success",
78
  "total_found": len(detections),
79
  "counts": gardiner_counts
80
  }
81
+
82
+ # 3. Text Report
83
+ text_report = generate_human_report(detections, gardiner_counts)
84
+
85
+ return annotated_image, summary_json, text_report
86
 
87
+ except Exception as e:
88
+ return None, {"error": str(e)}, f"Critical Error: {str(e)}"
89
 
90
+ # --- 3. UI STYLING (EGYPTIAN NIGHT) ---
91
  custom_css = """
92
  @import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap');
93
+ @import url('https://fonts.googleapis.com/css2?family=Courier+Prime&display=swap');
94
 
95
  :root {
96
  --body-background-fill: #050510 !important;
97
  --background-fill-primary: #0a0f1e !important;
98
+ --border-color-primary: #d4af37 !important;
99
  --text-body: #e0e7ff !important;
100
  --gold-glow: 0 0 15px rgba(212, 175, 55, 0.3);
101
  }
 
105
  font-family: 'Cinzel', serif !important;
106
  }
107
 
108
+ /* CARDS */
109
  .card {
110
+ background: rgba(10, 15, 30, 0.7) !important;
111
  border: 1px solid rgba(212, 175, 55, 0.3) !important;
112
+ border-radius: 15px;
113
+ padding: 20px;
114
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
115
  backdrop-filter: blur(10px);
116
+ margin-bottom: 20px;
117
  }
 
118
  .card-title {
119
+ font-size: 16px;
120
  font-weight: 700;
121
  color: #d4af37;
122
  text-transform: uppercase;
123
  letter-spacing: 2px;
124
+ border-bottom: 1px solid rgba(212, 175, 55, 0.2);
125
+ padding-bottom: 8px;
126
+ margin-bottom: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  }
 
 
128
 
129
+ /* BUTTONS */
 
 
 
 
 
 
 
 
 
130
  button.primary-btn {
131
  background: linear-gradient(135deg, #b8860b 0%, #d4af37 100%) !important;
132
  border: 1px solid #ffd700 !important;
 
135
  font-family: 'Cinzel', serif !important;
136
  text-transform: uppercase;
137
  letter-spacing: 1px;
 
138
  box-shadow: var(--gold-glow);
139
  }
140
+
141
+ /* REPORT TEXTBOX */
142
+ .report-box textarea {
143
+ background-color: #1a1a25 !important;
144
+ border: 1px solid #d4af37 !important;
145
+ font-family: 'Courier Prime', monospace !important;
146
+ color: #d4af37 !important;
147
+ font-size: 14px !important;
148
  }
149
 
150
+ /* JSON CONFIG BOX */
151
+ .config-box {
152
+ background-color: #111 !important;
153
+ border: 1px solid #444 !important;
154
+ font-family: 'Courier Prime', monospace !important;
155
+ padding: 15px;
156
+ border-radius: 8px;
157
+ color: #0f0;
158
+ font-size: 12px;
159
+ overflow-x: auto;
160
  }
161
+
162
+ /* CLEANUP */
163
+ .gradio-image, .gradio-json { background: transparent !important; border: none !important; }
164
  """
165
 
166
+ # HTML COMPONENTS
167
  header_html = """
168
+ <div style="display: flex; align-items: center; gap: 20px; padding: 20px 0;">
169
+ <svg width="50" height="50" viewBox="0 0 100 100" fill="none">
170
+ <path d="M10,50 Q50,10 90,50 Q50,90 10,50" stroke="#d4af37" stroke-width="3" fill="none"/>
171
+ <circle cx="50" cy="50" r="15" fill="#d4af37"/>
172
+ <path d="M50,65 L50,90 L30,90" stroke="#d4af37" stroke-width="3" fill="none"/>
173
+ </svg>
174
+ <div>
175
+ <h1 style="margin: 0; font-size: 32px; color: #d4af37;">HORUS VISION</h1>
176
+ <p style="margin: 0; color: #a5b4fc; font-size: 12px; letter-spacing: 2px;">HIEROGLYPHIC TRANSLATION & PRESERVATION</p>
 
 
 
 
 
177
  </div>
178
  </div>
179
  """
180
 
181
+ mission_html = """
182
  <div class="card">
183
+ <div class="card-title">๐Ÿ“œ THE MISSION</div>
184
+ <p style="color: #ccc; font-size: 14px; line-height: 1.6;">
185
+ <b>To preserve the past is to save the future.</b><br>
186
+ This tool is part of a larger initiative to digitize and translate Ancient Egyptian inscriptions.
187
+ Using YOLOv8, we identify Gardiner codes instantly, bridging the gap between stone artifacts and modern understanding.
188
+ </p>
189
+ </div>
190
+ """
191
+
192
+ claude_guide_html = """
193
+ <div class="card" style="border-color: #ff6b6b;">
194
+ <div class="card-title" style="color: #ff6b6b;">๐Ÿค– CLAUDE DESKTOP INTEGRATION GUIDE</div>
195
+
196
+ <div style="background: rgba(255, 107, 107, 0.1); border-left: 4px solid #ff6b6b; padding: 10px; margin-bottom: 15px;">
197
+ <strong style="color: #ff6b6b;">โš ๏ธ IMPORTANT:</strong>
198
+ Do <b>NOT</b> drag and drop images directly into Claude Desktop. The local server cannot see them.
199
+ You must place images in the dedicated folder created below.
200
  </div>
201
+
202
+ <div style="color: #ddd; font-size: 14px; line-height: 1.8;">
203
+ <b>Step 1:</b> Create a folder on your computer: <code style="background:#333; padding:2px;">C:\\Claude_Work</code><br>
204
+ <b>Step 2:</b> Place your hieroglyph images inside that folder.<br>
205
+ <b>Step 3:</b> Open your Claude Desktop Config file (usually <code>%APPDATA%\\Claude\\claude_desktop_config.json</code>).<br>
206
+ <b>Step 4:</b> Paste the configuration below inside the <code>"mcpServers"</code> block.<br>
207
+ <b>Step 5:</b> Restart Claude Desktop.
208
+ </div>
209
+ <br>
210
+ <div class="config-box">
211
+ {
212
+ "mcpServers": {
213
+ "gradio": {
214
+ "command": "npx",
215
+ "args": [
216
+ "mcp-remote",
217
+ "https://youkii-xr-hieroglyph-mcp-server.hf.space/gradio_api/mcp/",
218
+ "--transport",
219
+ "streamable-http"
220
+ ]
221
+ },
222
+ "upload_helper": {
223
+ "command": "C:\\\\Python313\\\\python.exe",
224
+ "args": [
225
+ "-m",
226
+ "gradio",
227
+ "upload-mcp",
228
+ "https://youkii-xr-hieroglyph-mcp-server.hf.space/",
229
+ "C:\\\\Claude_Work"
230
+ ]
231
+ }
232
+ }
233
+ }
234
+ </div>
235
+ <p style="color: #888; font-size: 12px; margin-top: 10px;">
236
+ *Note: Check that 'C:\\Python313\\python.exe' matches your actual Python installation path.
237
+ </p>
238
  </div>
239
  """
240
 
241
+ # --- 4. MAIN APPLICATION ---
 
242
  with gr.Blocks(title="Horus Vision") as demo:
 
 
243
  gr.HTML(f"<style>{custom_css}</style>")
 
 
244
  gr.HTML(header_html)
245
+ gr.HTML(mission_html)
246
 
247
+ # --- WORKSPACE AREA ---
248
  with gr.Row():
249
+
250
+ # LEFT COLUMN: INPUTS (Now with Tabs)
251
  with gr.Column(scale=1):
252
+ gr.HTML('<div class="card"><div class="card-title">SOURCE ARTIFACT</div>')
253
+
254
+ with gr.Tabs():
255
+ # TAB 1: UPLOAD
256
+ with gr.TabItem("๐Ÿ“œ Upload Scroll"):
257
+ img_upload = gr.Image(
258
+ type="pil",
259
+ sources=["upload", "clipboard"],
260
+ label="Upload Image",
261
+ height=300
262
+ )
263
+ slider_upload = gr.Slider(0.1, 1.0, 0.25, label="Confidence")
264
+ btn_upload = gr.Button("๐Ÿ”ฎ DECIPHER SCROLL", elem_classes="primary-btn")
265
+
266
+ # TAB 2: CAMERA
267
+ with gr.TabItem("๐Ÿ‘๏ธ Divine Sight"):
268
+ img_cam = gr.Image(
269
+ type="pil",
270
+ sources=["webcam"],
271
+ label="Camera Input",
272
+ height=300
273
+ )
274
+ slider_cam = gr.Slider(0.1, 1.0, 0.25, label="Confidence")
275
+ btn_cam = gr.Button("๐Ÿ”ฎ DECIPHER VISION", elem_classes="primary-btn")
276
 
277
+ gr.HTML('</div>')
278
+
279
+ # RIGHT COLUMN: OUTPUTS
280
  with gr.Column(scale=1):
281
+ gr.HTML('<div class="card"><div class="card-title">INTERPRETATION</div>')
282
+
283
+ # The Annotated Image
284
+ out_image = gr.Image(label="Annotated Artifact", interactive=False)
285
+
286
+ # The Human Readable Summary
287
+ out_report = gr.Textbox(
288
+ label="Scribe's Report",
289
+ lines=6,
290
+ elem_classes="report-box",
291
+ placeholder="Analysis results will appear here..."
292
+ )
293
+
294
+ # The Raw JSON (Collapsed by default ideally, but standard here)
295
+ with gr.Accordion("Raw Glyph Data (JSON)", open=False):
296
+ out_json = gr.JSON(label="JSON Data")
297
+
298
+ gr.HTML('</div>')
299
+
300
+ # --- CLAUDE GUIDE FOOTER ---
301
+ gr.HTML(claude_guide_html)
302
 
303
+ # --- WIRING ---
304
+ # We bind both buttons to the same function, targeting the same outputs
305
+ btn_upload.click(
306
+ fn=detect_hieroglyphs,
307
+ inputs=[img_upload, slider_upload],
308
+ outputs=[out_image, out_json, out_report]
309
+ )
310
 
311
+ btn_cam.click(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  fn=detect_hieroglyphs,
313
+ inputs=[img_cam, slider_cam],
314
+ outputs=[out_image, out_json, out_report]
315
  )
316
 
317
  if __name__ == "__main__":