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

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +331 -456
app.py CHANGED
@@ -10,9 +10,7 @@ import zipfile
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
 
@@ -28,15 +26,16 @@ class CometDetectorAPI:
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 = []
@@ -77,7 +76,10 @@ class CometDetectorAPI:
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')
@@ -101,312 +103,37 @@ class CometDetectorAPI:
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,176 +141,324 @@ custom_html = """
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)
 
10
  import tempfile
11
  from pathlib import Path
12
  import cv2
13
+ from datetime import datetime
 
 
14
  import base64
15
  import io
16
 
 
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 = []
 
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')
 
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
  </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)