MohammedSameerSyed commited on
Commit
abe2ac7
Β·
verified Β·
1 Parent(s): 1a4b0b8

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +482 -357
app.py CHANGED
@@ -10,7 +10,9 @@ import zipfile
10
  import tempfile
11
  from pathlib import Path
12
  import cv2
13
- from datetime import datetime
 
 
14
  import base64
15
  import io
16
 
@@ -24,19 +26,18 @@ class CometDetectorAPI:
24
  self.transform = transforms.Compose([
25
  transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
26
  ])
27
-
28
  def fetch_soho_recent(self):
29
- """Generate demo SOHO data"""
30
- images = []
31
- for i in range(12):
32
- # Create synthetic comet-like data with a moving bright spot
33
- img = np.random.rand(1024, 1024) * 20
34
- # Add a "comet" that moves
35
- y, x = 400 + i * 10, 300 + i * 15
36
- img[max(0,y-30):min(1024,y+30), max(0,x-30):min(1024,x+30)] += 100
37
- images.append(img.astype(np.float32))
38
- return np.array(images)
39
-
40
  def load_fits_from_zip(self, zip_file):
41
  images = []
42
  with tempfile.TemporaryDirectory() as tmpdir:
@@ -54,7 +55,7 @@ class CometDetectorAPI:
54
  except:
55
  continue
56
  return np.array(images)
57
-
58
  def create_difference_images(self, images):
59
  diff_images = []
60
  for i in range(len(images) - 1):
@@ -62,7 +63,7 @@ class CometDetectorAPI:
62
  diff_images.append(diff)
63
  max_proj = np.max(np.abs(np.array(diff_images)), axis=0)
64
  return max_proj
65
-
66
  def classify_image(self, max_proj):
67
  img = (max_proj - max_proj.min()) / (max_proj.max() - max_proj.min() + 1e-8)
68
  img = cv2.resize(img, (512, 512))
@@ -75,65 +76,337 @@ class CometDetectorAPI:
75
  pred_class = torch.argmax(probs, dim=1).item()
76
  confidence = probs[0][pred_class].item()
77
  return pred_class, confidence
78
-
79
- def generate_html_result(self, images, max_proj, pred_class, confidence, source, num_images):
80
- """Generate HTML result card"""
81
-
82
- # Create matplotlib figure
83
  plt.style.use('dark_background')
84
  fig, axes = plt.subplots(1, 2, figsize=(14, 6))
85
  fig.patch.set_facecolor('#0a0e27')
86
-
87
  axes[0].imshow(images[0], cmap='gray')
88
  axes[0].set_title('Original Frame', fontsize=12, color='#00d9ff')
89
  axes[0].axis('off')
90
-
91
  axes[1].imshow(max_proj, cmap='hot')
92
  if pred_class == 1:
93
  axes[1].set_title('COMET DETECTED', fontsize=14, color='#00ff88', weight='bold')
94
  else:
95
  axes[1].set_title('Background', fontsize=12, color='#ffb366')
96
  axes[1].axis('off')
97
-
98
  plt.tight_layout()
99
-
100
  buf = io.BytesIO()
101
  plt.savefig(buf, format='png', facecolor='#0a0e27', dpi=100)
102
  buf.seek(0)
103
  img_base64 = base64.b64encode(buf.read()).decode()
104
  plt.close()
105
-
106
- # Generate HTML result card
107
- status_class = "status-detected" if pred_class == 1 else "status-not-detected"
108
- status_text = "🌟 COMET DETECTED!" if pred_class == 1 else "πŸŒ‘ No Comet Activity"
109
- border_class = "result-detected" if pred_class == 1 else "result-not-detected"
110
-
111
- confidence_pct = int(confidence * 100)
112
- timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')
113
-
114
- html = f"""
115
- <div class="results {border_class}" style="display: block;">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  <div class="result-header">
117
- <div class="result-status {status_class}">{status_text}</div>
118
  <div class="confidence-bar">
119
- <div class="confidence-fill" style="width: {confidence_pct}%">{confidence_pct}%</div>
120
  </div>
121
  </div>
122
-
123
- <img class="result-image" src="data:image/png;base64,{img_base64}" alt="Analysis Result">
124
-
125
  <div class="metadata">
126
  <div class="metadata-item">
127
  <div class="metadata-label">Images Analyzed</div>
128
- <div class="metadata-value">{num_images}</div>
129
  </div>
130
  <div class="metadata-item">
131
  <div class="metadata-label">Data Source</div>
132
- <div class="metadata-value">{source}</div>
133
  </div>
134
  <div class="metadata-item">
135
  <div class="metadata-label">Analysis Time</div>
136
- <div class="metadata-value">{timestamp}</div>
137
  </div>
138
  <div class="metadata-item">
139
  <div class="metadata-label">Model Accuracy</div>
@@ -141,324 +414,176 @@ class CometDetectorAPI:
141
  </div>
142
  </div>
143
  </div>
144
-
145
- <style>
146
- .results {{
147
- background: linear-gradient(135deg, rgba(10, 14, 39, 0.95) 0%, rgba(0, 20, 40, 0.9) 100%);
148
- border-radius: 15px;
149
- padding: 30px;
150
- border: 2px solid rgba(0, 217, 255, 0.3);
151
- backdrop-filter: blur(10px);
152
- animation: slideIn 0.5s ease;
153
- margin-top: 20px;
154
- }}
155
-
156
- @keyframes slideIn {{
157
- from {{ opacity: 0; transform: translateY(20px); }}
158
- to {{ opacity: 1; transform: translateY(0); }}
159
- }}
160
-
161
- .result-detected {{ border-color: #00ff88; box-shadow: 0 0 40px rgba(0, 255, 136, 0.3); }}
162
- .result-not-detected {{ border-color: #ffb366; box-shadow: 0 0 40px rgba(255, 179, 102, 0.3); }}
163
-
164
- .result-header {{ text-align: center; margin-bottom: 30px; }}
165
-
166
- .result-status {{
167
- font-family: 'Orbitron', sans-serif;
168
- font-size: 2.5em;
169
- font-weight: bold;
170
- margin-bottom: 10px;
171
- }}
172
-
173
- .status-detected {{ color: #00ff88; text-shadow: 0 0 20px rgba(0, 255, 136, 0.8); }}
174
- .status-not-detected {{ color: #ffb366; text-shadow: 0 0 20px rgba(255, 179, 102, 0.8); }}
175
-
176
- .confidence-bar {{
177
- width: 100%;
178
- height: 30px;
179
- background: rgba(255, 255, 255, 0.1);
180
- border-radius: 15px;
181
- overflow: hidden;
182
- margin: 20px 0;
183
- }}
184
-
185
- .confidence-fill {{
186
- height: 100%;
187
- background: linear-gradient(90deg, #00d9ff 0%, #00ff88 100%);
188
- display: flex;
189
- align-items: center;
190
- justify-content: center;
191
- font-weight: bold;
192
- color: #000;
193
- transition: width 1s ease;
194
- }}
195
-
196
- .result-image {{
197
- width: 100%;
198
- border-radius: 10px;
199
- margin: 20px 0;
200
- border: 2px solid rgba(0, 217, 255, 0.3);
201
- }}
202
-
203
- .metadata {{
204
- display: grid;
205
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
206
- gap: 15px;
207
- margin-top: 20px;
208
- }}
209
-
210
- .metadata-item {{
211
- background: rgba(0, 217, 255, 0.1);
212
- padding: 15px;
213
- border-radius: 8px;
214
- border: 1px solid rgba(0, 217, 255, 0.3);
215
- }}
216
-
217
- .metadata-label {{ color: #00d9ff; font-size: 0.9em; margin-bottom: 5px; }}
218
- .metadata-value {{ font-size: 1.2em; font-weight: bold; color: #fff; }}
219
- </style>
220
- """
221
-
222
- return html
223
-
224
- def analyze_uploaded(self, zip_file, progress=gr.Progress()):
225
- """Analyze uploaded ZIP with progress tracking"""
226
- if zip_file is None:
227
- return "<div class='error' style='display:block;'>❌ No file uploaded</div>"
228
-
229
- try:
230
- progress(0.1, desc="Extracting ZIP file...")
231
- images = self.load_fits_from_zip(zip_file)
232
-
233
- if len(images) < 2:
234
- return "<div class='error' style='display:block;'>❌ Need at least 2 FITS images</div>"
235
-
236
- progress(0.4, desc=f"Processing {len(images)} images...")
237
- max_proj = self.create_difference_images(images)
238
-
239
- progress(0.7, desc="Running AI classification...")
240
- pred_class, confidence = self.classify_image(max_proj)
241
-
242
- progress(1.0, desc="Complete!")
243
-
244
- return self.generate_html_result(
245
- images, max_proj, pred_class, confidence,
246
- "Uploaded Data", len(images)
247
- )
248
-
249
- except Exception as e:
250
- return f"<div class='error' style='display:block;'>❌ Error: {str(e)}</div>"
251
-
252
- def analyze_soho_live(self, progress=gr.Progress()):
253
- """Fetch and analyze live SOHO data with progress"""
254
- try:
255
- progress(0.2, desc="Fetching SOHO data...")
256
- images = self.fetch_soho_recent()
257
-
258
- progress(0.5, desc="Creating difference images...")
259
- max_proj = self.create_difference_images(images)
260
-
261
- progress(0.8, desc="Running AI classification...")
262
- pred_class, confidence = self.classify_image(max_proj)
263
-
264
- progress(1.0, desc="Complete!")
265
-
266
- return self.generate_html_result(
267
- images, max_proj, pred_class, confidence,
268
- "Live SOHO Data (Demo)", len(images)
269
- )
270
-
271
- except Exception as e:
272
- return f"<div class='error' style='display:block;'>❌ Error: {str(e)}</div>"
273
 
274
- detector = CometDetectorAPI('best_model.pth')
 
 
 
 
 
 
 
 
 
 
275
 
276
- # Custom CSS
277
- custom_css = """
278
- @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap');
279
-
280
- * { margin: 0; padding: 0; box-sizing: border-box; }
281
-
282
- body {
283
- font-family: 'Share Tech Mono', monospace;
284
- background: linear-gradient(135deg, #000814 0%, #001d3d 50%, #000814 100%);
285
- color: #fff;
286
- }
287
-
288
- .gradio-container {
289
- max-width: 1400px !important;
290
- margin: 0 auto !important;
291
- }
292
-
293
- .header {
294
- text-align: center;
295
- padding: 40px 20px;
296
- background: linear-gradient(135deg, rgba(10, 14, 39, 0.9) 0%, rgba(0, 20, 40, 0.8) 100%);
297
- border-radius: 20px;
298
- border: 2px solid rgba(0, 217, 255, 0.3);
299
- box-shadow: 0 0 40px rgba(0, 217, 255, 0.2);
300
- margin-bottom: 30px;
301
- backdrop-filter: blur(10px);
302
- }
303
-
304
- .title {
305
- font-family: 'Orbitron', sans-serif;
306
- font-size: 4em;
307
- font-weight: 900;
308
- background: linear-gradient(135deg, #00d9ff 0%, #00ff88 50%, #ff00ff 100%);
309
- -webkit-background-clip: text;
310
- -webkit-text-fill-color: transparent;
311
- margin-bottom: 10px;
312
- animation: glow 2s ease-in-out infinite alternate;
313
- }
314
-
315
- @keyframes glow {
316
- from { filter: drop-shadow(0 0 5px rgba(0, 217, 255, 0.5)); }
317
- to { filter: drop-shadow(0 0 20px rgba(0, 217, 255, 0.8)); }
318
- }
319
-
320
- .subtitle {
321
- font-size: 1.2em;
322
- color: #00d9ff;
323
- letter-spacing: 3px;
324
- margin-bottom: 20px;
325
- }
326
-
327
- .team {
328
- font-size: 1.1em;
329
- color: #00ff88;
330
- margin-top: 15px;
331
- }
332
-
333
- .team-member {
334
- display: inline-block;
335
- padding: 8px 20px;
336
- margin: 0 10px;
337
- background: linear-gradient(135deg, rgba(0, 255, 136, 0.2) 0%, rgba(0, 217, 255, 0.2) 100%);
338
- border: 2px solid #00ff88;
339
- border-radius: 25px;
340
- font-weight: bold;
341
- animation: pulse 2s ease-in-out infinite;
342
- box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
343
- }
344
-
345
- @keyframes pulse {
346
- 0%, 100% { transform: scale(1); box-shadow: 0 0 20px rgba(0, 255, 136, 0.3); }
347
- 50% { transform: scale(1.05); box-shadow: 0 0 30px rgba(0, 255, 136, 0.5); }
348
- }
349
-
350
- .panel {
351
- background: linear-gradient(135deg, rgba(10, 14, 39, 0.95) 0%, rgba(0, 20, 40, 0.9) 100%);
352
- border-radius: 15px;
353
- padding: 30px;
354
- border: 2px solid rgba(0, 217, 255, 0.3);
355
- backdrop-filter: blur(10px);
356
- margin-bottom: 20px;
357
- }
358
-
359
- .gr-button-primary {
360
- background: linear-gradient(135deg, #00d9ff 0%, #00ff88 100%) !important;
361
- border: none !important;
362
- color: #000 !important;
363
- font-family: 'Orbitron', sans-serif !important;
364
- font-weight: bold !important;
365
- font-size: 1.1em !important;
366
- padding: 15px 40px !important;
367
- letter-spacing: 2px !important;
368
- transition: all 0.3s ease !important;
369
- }
370
-
371
- .gr-button-primary:hover {
372
- transform: translateY(-3px) !important;
373
- box-shadow: 0 10px 30px rgba(0, 217, 255, 0.5) !important;
374
- }
375
-
376
- .gr-button-secondary {
377
- background: linear-gradient(135deg, #ff00ff 0%, #ff0080 100%) !important;
378
- border: none !important;
379
- color: #fff !important;
380
- font-family: 'Orbitron', sans-serif !important;
381
- font-weight: bold !important;
382
- font-size: 1.1em !important;
383
- padding: 15px 40px !important;
384
- letter-spacing: 2px !important;
385
- transition: all 0.3s ease !important;
386
- }
387
-
388
- .gr-button-secondary:hover {
389
- transform: translateY(-3px) !important;
390
- box-shadow: 0 10px 30px rgba(255, 0, 255, 0.5) !important;
391
- }
392
-
393
- .error {
394
- background: rgba(255, 0, 0, 0.1);
395
- border: 2px solid rgba(255, 0, 0, 0.5);
396
- color: #ff4444;
397
- padding: 20px;
398
- border-radius: 10px;
399
- margin: 20px 0;
400
- font-size: 1.1em;
401
- }
 
 
 
 
 
 
 
 
402
  """
403
 
404
- # Create Gradio Interface
405
- with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="COMET-SEE Mission Control") as demo:
406
-
407
- gr.HTML("""
408
- <div class="header">
409
- <div class="title">COMET-SEE</div>
410
- <div class="subtitle">COmet Motion Extraction & Tracking – Statistical Exploration Engine</div>
411
- <div class="team">
412
- <span class="team-member">πŸ‘©β€πŸš€ SAMBHAVI</span>
413
- <span class="team-member">πŸ‘©β€πŸ”¬ EMILY</span>
414
- <span class="team-member">πŸ‘¨β€πŸ’» MOHAMMED</span>
415
- </div>
416
- </div>
417
- """)
418
-
419
- with gr.Row():
420
- with gr.Column(scale=1):
421
- gr.HTML('<div class="panel"><h2 style="color: #00d9ff; font-family: Orbitron;">πŸ“€ Upload FITS Data</h2></div>')
422
- file_input = gr.File(
423
- label="Upload ZIP of FITS Images",
424
- file_types=[".zip"],
425
- type="filepath"
426
- )
427
- upload_btn = gr.Button("πŸ” Analyze Upload", variant="primary", size="lg")
428
-
429
- with gr.Column(scale=1):
430
- gr.HTML('<div class="panel"><h2 style="color: #00d9ff; font-family: Orbitron;">πŸ“‘ Live SOHO Data</h2></div>')
431
- gr.HTML("""
432
- <div style="text-align: center; padding: 20px;">
433
- <div style="font-size: 3em; margin-bottom: 15px;">πŸ›°οΈ</div>
434
- <p style="color: #aaa; margin-bottom: 20px;">
435
- Fetch recent SOHO/LASCO C3 images<br>and analyze for comet activity
436
- </p>
437
- </div>
438
- """)
439
- fetch_btn = gr.Button("🌐 Fetch Live Data", variant="secondary", size="lg")
440
-
441
- # Results section
442
- result_output = gr.HTML(label="Analysis Results")
443
-
444
- # Connect buttons
445
- upload_btn.click(
446
- fn=detector.analyze_uploaded,
447
- inputs=[file_input],
448
- outputs=[result_output]
449
- )
450
-
451
- fetch_btn.click(
452
- fn=detector.analyze_soho_live,
453
- outputs=[result_output]
454
- )
455
-
456
- gr.HTML("""
457
- <div style="text-align: center; margin-top: 40px; padding: 20px; color: #888;">
458
- <p>Model Performance: 97.7% Accuracy β€’ 98% Precision β€’ 99% Recall</p>
459
- <p style="margin-top: 10px;">Built with ❀️ by Sambhavi, Emily & Mohammed</p>
460
- </div>
461
- """)
462
 
463
  if __name__ == "__main__":
464
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
10
  import tempfile
11
  from pathlib import Path
12
  import cv2
13
+ import requests
14
+ from datetime import datetime, timedelta
15
+ import json
16
  import base64
17
  import io
18
 
 
26
  self.transform = transforms.Compose([
27
  transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
28
  ])
29
+
30
  def fetch_soho_recent(self):
31
+ """Fetch recent SOHO data (demo implementation)"""
32
+ try:
33
+ images = []
34
+ for i in range(12):
35
+ img = np.random.rand(1024, 1024) * 50 + np.random.rand(1024, 1024) * 50
36
+ images.append(img.astype(np.float32))
37
+ return np.array(images), "Live SOHO Data (Demo)"
38
+ except Exception as e:
39
+ return None, str(e)
40
+
 
41
  def load_fits_from_zip(self, zip_file):
42
  images = []
43
  with tempfile.TemporaryDirectory() as tmpdir:
 
55
  except:
56
  continue
57
  return np.array(images)
58
+
59
  def create_difference_images(self, images):
60
  diff_images = []
61
  for i in range(len(images) - 1):
 
63
  diff_images.append(diff)
64
  max_proj = np.max(np.abs(np.array(diff_images)), axis=0)
65
  return max_proj
66
+
67
  def classify_image(self, max_proj):
68
  img = (max_proj - max_proj.min()) / (max_proj.max() - max_proj.min() + 1e-8)
69
  img = cv2.resize(img, (512, 512))
 
76
  pred_class = torch.argmax(probs, dim=1).item()
77
  confidence = probs[0][pred_class].item()
78
  return pred_class, confidence
79
+
80
+ def generate_visualization(self, images, max_proj, pred_class, confidence):
 
 
 
81
  plt.style.use('dark_background')
82
  fig, axes = plt.subplots(1, 2, figsize=(14, 6))
83
  fig.patch.set_facecolor('#0a0e27')
84
+
85
  axes[0].imshow(images[0], cmap='gray')
86
  axes[0].set_title('Original Frame', fontsize=12, color='#00d9ff')
87
  axes[0].axis('off')
88
+
89
  axes[1].imshow(max_proj, cmap='hot')
90
  if pred_class == 1:
91
  axes[1].set_title('COMET DETECTED', fontsize=14, color='#00ff88', weight='bold')
92
  else:
93
  axes[1].set_title('Background', fontsize=12, color='#ffb366')
94
  axes[1].axis('off')
95
+
96
  plt.tight_layout()
97
+
98
  buf = io.BytesIO()
99
  plt.savefig(buf, format='png', facecolor='#0a0e27', dpi=100)
100
  buf.seek(0)
101
  img_base64 = base64.b64encode(buf.read()).decode()
102
  plt.close()
103
+
104
+ return f"data:image/png;base64,{img_base64}"
105
+
106
+ def analyze_uploaded(self, zip_file):
107
+ if zip_file is None:
108
+ return {"error": "No file uploaded"}
109
+
110
+ try:
111
+ images = self.load_fits_from_zip(zip_file.name)
112
+ if len(images) < 2:
113
+ return {"error": "Need at least 2 FITS images"}
114
+
115
+ max_proj = self.create_difference_images(images)
116
+ pred_class, confidence = self.classify_image(max_proj)
117
+ img_data = self.generate_visualization(images, max_proj, pred_class, confidence)
118
+
119
+ return {
120
+ "success": True,
121
+ "detected": bool(pred_class),
122
+ "confidence": float(confidence),
123
+ "num_images": len(images),
124
+ "source": "Uploaded Data",
125
+ "timestamp": datetime.utcnow().isoformat(),
126
+ "image": img_data
127
+ }
128
+ except Exception as e:
129
+ return {"error": str(e)}
130
+
131
+ def analyze_soho_live(self):
132
+ try:
133
+ images, source = self.fetch_soho_recent()
134
+ if images is None:
135
+ return {"error": "Failed to fetch SOHO data"}
136
+
137
+ max_proj = self.create_difference_images(images)
138
+ pred_class, confidence = self.classify_image(max_proj)
139
+ img_data = self.generate_visualization(images, max_proj, pred_class, confidence)
140
+
141
+ return {
142
+ "success": True,
143
+ "detected": bool(pred_class),
144
+ "confidence": float(confidence),
145
+ "num_images": len(images),
146
+ "source": source,
147
+ "timestamp": datetime.utcnow().isoformat(),
148
+ "image": img_data
149
+ }
150
+ except Exception as e:
151
+ return {"error": str(e)}
152
+
153
+ detector = CometDetectorAPI('best_model.pth')
154
+
155
+ custom_html = """
156
+ <!DOCTYPE html>
157
+ <html lang="en">
158
+ <head>
159
+ <meta charset="UTF-8">
160
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
161
+ <title>COMET-SEE Mission Control</title>
162
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
163
+ <style>
164
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap');
165
+
166
+ * { margin: 0; padding: 0; box-sizing: border-box; }
167
+ body { font-family: 'Share Tech Mono', monospace; background: #000; color: #fff; overflow-x: hidden; }
168
+ #canvas-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; }
169
+ .container { position: relative; z-index: 1; max-width: 1400px; margin: 0 auto; padding: 20px; }
170
+
171
+ .header {
172
+ text-align: center; padding: 40px 20px;
173
+ background: linear-gradient(135deg, rgba(10, 14, 39, 0.9) 0%, rgba(0, 20, 40, 0.8) 100%);
174
+ border-radius: 20px; border: 2px solid rgba(0, 217, 255, 0.3);
175
+ box-shadow: 0 0 40px rgba(0, 217, 255, 0.2); margin-bottom: 30px;
176
+ backdrop-filter: blur(10px);
177
+ }
178
+
179
+ .title {
180
+ font-family: 'Orbitron', sans-serif; font-size: 4em; font-weight: 900;
181
+ background: linear-gradient(135deg, #00d9ff 0%, #00ff88 50%, #ff00ff 100%);
182
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
183
+ text-shadow: 0 0 30px rgba(0, 217, 255, 0.5); margin-bottom: 10px;
184
+ animation: glow 2s ease-in-out infinite alternate;
185
+ }
186
+
187
+ @keyframes glow {
188
+ from { filter: drop-shadow(0 0 5px rgba(0, 217, 255, 0.5)); }
189
+ to { filter: drop-shadow(0 0 20px rgba(0, 217, 255, 0.8)); }
190
+ }
191
+
192
+ .subtitle {
193
+ font-size: 1.2em; color: #00d9ff; letter-spacing: 3px; margin-bottom: 20px;
194
+ }
195
+
196
+ .team { font-size: 1.1em; color: #00ff88; margin-top: 15px; }
197
+ .team-member {
198
+ display: inline-block; padding: 5px 15px; margin: 0 10px;
199
+ background: rgba(0, 255, 136, 0.1); border: 1px solid #00ff88;
200
+ border-radius: 20px; font-weight: bold;
201
+ animation: pulse 2s ease-in-out infinite;
202
+ }
203
+
204
+ @keyframes pulse {
205
+ 0%, 100% { transform: scale(1); }
206
+ 50% { transform: scale(1.05); }
207
+ }
208
+
209
+ .control-panel { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-bottom: 30px; }
210
+
211
+ .panel {
212
+ background: linear-gradient(135deg, rgba(10, 14, 39, 0.95) 0%, rgba(0, 20, 40, 0.9) 100%);
213
+ border-radius: 15px; padding: 30px; border: 2px solid rgba(0, 217, 255, 0.3);
214
+ backdrop-filter: blur(10px); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
215
+ transition: all 0.3s ease;
216
+ }
217
+
218
+ .panel:hover {
219
+ border-color: rgba(0, 217, 255, 0.6);
220
+ box-shadow: 0 8px 40px rgba(0, 217, 255, 0.3);
221
+ transform: translateY(-5px);
222
+ }
223
+
224
+ .panel-title {
225
+ font-family: 'Orbitron', sans-serif; font-size: 1.5em; color: #00d9ff;
226
+ margin-bottom: 20px; text-transform: uppercase; letter-spacing: 2px;
227
+ }
228
+
229
+ .upload-zone {
230
+ border: 3px dashed rgba(0, 217, 255, 0.5); border-radius: 10px;
231
+ padding: 40px; text-align: center; cursor: pointer;
232
+ transition: all 0.3s ease; background: rgba(0, 217, 255, 0.05);
233
+ }
234
+
235
+ .upload-zone:hover {
236
+ border-color: #00d9ff; background: rgba(0, 217, 255, 0.1);
237
+ }
238
+
239
+ .upload-icon { font-size: 3em; margin-bottom: 15px; }
240
+ input[type="file"] { display: none; }
241
+
242
+ .btn {
243
+ font-family: 'Orbitron', sans-serif; padding: 15px 40px; font-size: 1.1em;
244
+ border: none; border-radius: 10px; cursor: pointer; text-transform: uppercase;
245
+ font-weight: bold; letter-spacing: 2px; transition: all 0.3s ease;
246
+ margin: 10px; position: relative; overflow: hidden;
247
+ }
248
+
249
+ .btn-primary {
250
+ background: linear-gradient(135deg, #00d9ff 0%, #00ff88 100%); color: #000;
251
+ }
252
+
253
+ .btn-secondary {
254
+ background: linear-gradient(135deg, #ff00ff 0%, #ff0080 100%); color: #fff;
255
+ }
256
+
257
+ .btn:hover {
258
+ transform: translateY(-3px); box-shadow: 0 10px 30px rgba(0, 217, 255, 0.5);
259
+ }
260
+
261
+ .results {
262
+ background: linear-gradient(135deg, rgba(10, 14, 39, 0.95) 0%, rgba(0, 20, 40, 0.9) 100%);
263
+ border-radius: 15px; padding: 30px; border: 2px solid rgba(0, 217, 255, 0.3);
264
+ backdrop-filter: blur(10px); display: none; animation: slideIn 0.5s ease;
265
+ }
266
+
267
+ @keyframes slideIn {
268
+ from { opacity: 0; transform: translateY(20px); }
269
+ to { opacity: 1; transform: translateY(0); }
270
+ }
271
+
272
+ .result-detected { border-color: #00ff88; box-shadow: 0 0 40px rgba(0, 255, 136, 0.3); }
273
+ .result-not-detected { border-color: #ffb366; box-shadow: 0 0 40px rgba(255, 179, 102, 0.3); }
274
+
275
+ .result-header { text-align: center; margin-bottom: 30px; }
276
+ .result-status { font-family: 'Orbitron', sans-serif; font-size: 2.5em; font-weight: bold; margin-bottom: 10px; }
277
+ .status-detected { color: #00ff88; text-shadow: 0 0 20px rgba(0, 255, 136, 0.8); }
278
+ .status-not-detected { color: #ffb366; text-shadow: 0 0 20px rgba(255, 179, 102, 0.8); }
279
+
280
+ .confidence-bar {
281
+ width: 100%; height: 30px; background: rgba(255, 255, 255, 0.1);
282
+ border-radius: 15px; overflow: hidden; margin: 20px 0;
283
+ }
284
+
285
+ .confidence-fill {
286
+ height: 100%; background: linear-gradient(90deg, #00d9ff 0%, #00ff88 100%);
287
+ transition: width 1s ease; display: flex; align-items: center;
288
+ justify-content: center; font-weight: bold; color: #000;
289
+ }
290
+
291
+ .result-image {
292
+ width: 100%; border-radius: 10px; margin: 20px 0;
293
+ border: 2px solid rgba(0, 217, 255, 0.3);
294
+ }
295
+
296
+ .metadata {
297
+ display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
298
+ gap: 15px; margin-top: 20px;
299
+ }
300
+
301
+ .metadata-item {
302
+ background: rgba(0, 217, 255, 0.1); padding: 15px; border-radius: 8px;
303
+ border: 1px solid rgba(0, 217, 255, 0.3);
304
+ }
305
+
306
+ .metadata-label { color: #00d9ff; font-size: 0.9em; margin-bottom: 5px; }
307
+ .metadata-value { font-size: 1.2em; font-weight: bold; color: #fff; }
308
+
309
+ .loading { text-align: center; padding: 40px; display: none; }
310
+ .spinner {
311
+ border: 4px solid rgba(0, 217, 255, 0.3); border-top: 4px solid #00d9ff;
312
+ border-radius: 50%; width: 60px; height: 60px;
313
+ animation: spin 1s linear infinite; margin: 0 auto 20px;
314
+ }
315
+
316
+ @keyframes spin {
317
+ 0% { transform: rotate(0deg); }
318
+ 100% { transform: rotate(360deg); }
319
+ }
320
+
321
+ .error {
322
+ background: rgba(255, 0, 0, 0.1); border: 2px solid rgba(255, 0, 0, 0.5);
323
+ color: #ff4444; padding: 20px; border-radius: 10px; margin: 20px 0; display: none;
324
+ }
325
+
326
+ .astronaut {
327
+ position: fixed; right: 50px; top: 50%; transform: translateY(-50%);
328
+ font-size: 5em; animation: float 6s ease-in-out infinite; z-index: 999;
329
+ filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.5));
330
+ }
331
+
332
+ @keyframes float {
333
+ 0%, 100% { transform: translateY(-50%) rotate(-5deg); }
334
+ 50% { transform: translateY(calc(-50% - 30px)) rotate(5deg); }
335
+ }
336
+
337
+ @media (max-width: 768px) {
338
+ .control-panel { grid-template-columns: 1fr; }
339
+ .title { font-size: 2.5em; }
340
+ .astronaut { display: none; }
341
+ }
342
+ </style>
343
+ </head>
344
+ <body>
345
+ <div id="canvas-container"></div>
346
+ <div class="astronaut">πŸ§‘β€πŸš€</div>
347
+
348
+ <div class="container">
349
+ <div class="header">
350
+ <div class="title">COMET-SEE</div>
351
+ <div class="subtitle">COmet Motion Extraction & Tracking – Statistical Exploration Engine</div>
352
+ <div class="team">
353
+ <span class="team-member">πŸ‘©β€πŸ”¬ SHAMBHAVI SRIVASTAVA </span>
354
+ <span class="team-member">πŸ‘©β€πŸš€ EMILY FOLEY</span>
355
+ <span class="team-member">πŸ‘¨β€πŸ’» MOHAMMED SAMEER SYED</span>
356
+ </div>
357
+ </div>
358
+
359
+ <div class="control-panel">
360
+ <div class="panel">
361
+ <h2 class="panel-title">πŸ“€ Upload FITS Data</h2>
362
+ <div class="upload-zone" onclick="document.getElementById('fileInput').click()">
363
+ <div class="upload-icon">πŸ“</div>
364
+ <div>Click to upload ZIP file</div>
365
+ <div style="font-size: 0.9em; color: #888; margin-top: 10px;">SOHO LASCO C3 FITS images</div>
366
+ </div>
367
+ <input type="file" id="fileInput" accept=".zip" onchange="handleFileUpload(event)">
368
+ <button class="btn btn-primary" onclick="analyzeUpload()">πŸ” Analyze Upload</button>
369
+ </div>
370
+
371
+ <div class="panel">
372
+ <h2 class="panel-title">πŸ“‘ Live SOHO Data</h2>
373
+ <div style="text-align: center; padding: 20px;">
374
+ <div style="font-size: 2em; margin-bottom: 20px;">πŸ›°οΈ</div>
375
+ <p style="margin-bottom: 20px; color: #aaa;">Fetch recent SOHO/LASCO C3 images and analyze for comet activity</p>
376
+ <button class="btn btn-secondary" onclick="fetchAndAnalyze()">🌐 Fetch Live Data</button>
377
+ </div>
378
+ </div>
379
+ </div>
380
+
381
+ <div class="loading" id="loading">
382
+ <div class="spinner"></div>
383
+ <div>Analyzing data...</div>
384
+ </div>
385
+
386
+ <div class="error" id="error"></div>
387
+
388
+ <div class="results" id="results">
389
  <div class="result-header">
390
+ <div class="result-status" id="status"></div>
391
  <div class="confidence-bar">
392
+ <div class="confidence-fill" id="confidence" style="width: 0%"></div>
393
  </div>
394
  </div>
395
+
396
+ <img class="result-image" id="resultImage" src="" alt="Analysis Result">
397
+
398
  <div class="metadata">
399
  <div class="metadata-item">
400
  <div class="metadata-label">Images Analyzed</div>
401
+ <div class="metadata-value" id="numImages">-</div>
402
  </div>
403
  <div class="metadata-item">
404
  <div class="metadata-label">Data Source</div>
405
+ <div class="metadata-value" id="source">-</div>
406
  </div>
407
  <div class="metadata-item">
408
  <div class="metadata-label">Analysis Time</div>
409
+ <div class="metadata-value" id="timestamp">-</div>
410
  </div>
411
  <div class="metadata-item">
412
  <div class="metadata-label">Model Accuracy</div>
 
414
  </div>
415
  </div>
416
  </div>
417
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
 
419
+ <script>
420
+ let scene, camera, renderer, stars;
421
+
422
+ function initStarfield() {
423
+ scene = new THREE.Scene();
424
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
425
+ camera.position.z = 5;
426
+
427
+ renderer = new THREE.WebGLRenderer({ alpha: true });
428
+ renderer.setSize(window.innerWidth, window.innerHeight);
429
+ document.getElementById('canvas-container').appendChild(renderer.domElement);
430
 
431
+ const starGeometry = new THREE.BufferGeometry();
432
+ const starMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 2, transparent: true });
433
+
434
+ const starVertices = [];
435
+ for (let i = 0; i < 10000; i++) {
436
+ const x = (Math.random() - 0.5) * 2000;
437
+ const y = (Math.random() - 0.5) * 2000;
438
+ const z = (Math.random() - 0.5) * 2000;
439
+ starVertices.push(x, y, z);
440
+ }
441
+
442
+ starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3));
443
+ stars = new THREE.Points(starGeometry, starMaterial);
444
+ scene.add(stars);
445
+
446
+ animate();
447
+ }
448
+
449
+ function animate() {
450
+ requestAnimationFrame(animate);
451
+ stars.rotation.y += 0.0002;
452
+ stars.rotation.x += 0.0001;
453
+ renderer.render(scene, camera);
454
+ }
455
+
456
+ window.addEventListener('resize', () => {
457
+ camera.aspect = window.innerWidth / window.innerHeight;
458
+ camera.updateProjectionMatrix();
459
+ renderer.setSize(window.innerWidth, window.innerHeight);
460
+ });
461
+
462
+ initStarfield();
463
+
464
+ let uploadedFile = null;
465
+
466
+ function handleFileUpload(event) {
467
+ uploadedFile = event.target.files[0];
468
+ if (uploadedFile) {
469
+ document.querySelector('.upload-zone').innerHTML = `
470
+ <div class="upload-icon">βœ…</div>
471
+ <div>${uploadedFile.name}</div>
472
+ <div style="font-size: 0.9em; color: #00ff88; margin-top: 10px;">Ready to analyze</div>
473
+ `;
474
+ }
475
+ }
476
+
477
+ function showLoading() {
478
+ document.getElementById('loading').style.display = 'block';
479
+ document.getElementById('results').style.display = 'none';
480
+ document.getElementById('error').style.display = 'none';
481
+ }
482
+
483
+ function showError(message) {
484
+ document.getElementById('loading').style.display = 'none';
485
+ document.getElementById('error').textContent = message;
486
+ document.getElementById('error').style.display = 'block';
487
+ }
488
+
489
+ function showResults(data) {
490
+ document.getElementById('loading').style.display = 'none';
491
+
492
+ const results = document.getElementById('results');
493
+ results.style.display = 'block';
494
+
495
+ if (data.detected) {
496
+ results.className = 'results result-detected';
497
+ document.getElementById('status').className = 'result-status status-detected';
498
+ document.getElementById('status').textContent = '🌟 COMET DETECTED!';
499
+ } else {
500
+ results.className = 'results result-not-detected';
501
+ document.getElementById('status').className = 'result-status status-not-detected';
502
+ document.getElementById('status').textContent = 'πŸŒ‘ No Comet Activity';
503
+ }
504
+
505
+ const confidence = Math.round(data.confidence * 100);
506
+ document.getElementById('confidence').style.width = confidence + '%';
507
+ document.getElementById('confidence').textContent = confidence + '%';
508
+
509
+ document.getElementById('resultImage').src = data.image;
510
+ document.getElementById('numImages').textContent = data.num_images;
511
+ document.getElementById('source').textContent = data.source;
512
+ document.getElementById('timestamp').textContent = new Date(data.timestamp).toLocaleString();
513
+
514
+ results.scrollIntoView({ behavior: 'smooth' });
515
+ }
516
+
517
+ async function analyzeUpload() {
518
+ if (!uploadedFile) {
519
+ showError('Please upload a ZIP file first');
520
+ return;
521
+ }
522
+
523
+ showLoading();
524
+
525
+ const formData = new FormData();
526
+ formData.append('file', uploadedFile);
527
+
528
+ try {
529
+ const response = await fetch('/api/analyze_upload', {
530
+ method: 'POST',
531
+ body: formData
532
+ });
533
+
534
+ const data = await response.json();
535
+
536
+ if (data.error) {
537
+ showError(data.error);
538
+ } else {
539
+ showResults(data);
540
+ }
541
+ } catch (error) {
542
+ showError('Error analyzing file: ' + error.message);
543
+ }
544
+ }
545
+
546
+ async function fetchAndAnalyze() {
547
+ showLoading();
548
+
549
+ try {
550
+ const response = await fetch('/api/analyze_soho');
551
+ const data = await response.json();
552
+
553
+ if (data.error) {
554
+ showError(data.error);
555
+ } else {
556
+ showResults(data);
557
+ }
558
+ } catch (error) {
559
+ showError('Error fetching SOHO data: ' + error.message);
560
+ }
561
+ }
562
+ </script>
563
+ </body>
564
+ </html>
565
  """
566
 
567
+ with gr.Blocks() as demo:
568
+ gr.HTML(custom_html)
569
+
570
+ with gr.Row(visible=False):
571
+ file_input = gr.File()
572
+ upload_output = gr.JSON()
573
+ soho_output = gr.JSON()
574
+
575
+ file_input.upload(
576
+ fn=detector.analyze_uploaded,
577
+ inputs=[file_input],
578
+ outputs=[upload_output],
579
+ api_name="analyze_upload"
580
+ )
581
+
582
+ gr.Button("Fetch SOHO").click(
583
+ fn=detector.analyze_soho_live,
584
+ outputs=[soho_output],
585
+ api_name="analyze_soho"
586
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587
 
588
  if __name__ == "__main__":
589
  demo.launch(server_name="0.0.0.0", server_port=7860)