Devashri26 commited on
Commit
c355bcf
·
verified ·
1 Parent(s): 93d23fe

Sync from GitHub via hub-sync

Browse files
Files changed (2) hide show
  1. README.md +1 -0
  2. app.py +77 -17
README.md CHANGED
@@ -6,6 +6,7 @@ colorTo: purple
6
  sdk: gradio
7
  sdk_version: "6.11.0"
8
  python_version: "3.10"
 
9
  app_file: app.py
10
  pinned: false
11
  license: mit
 
6
  sdk: gradio
7
  sdk_version: "6.11.0"
8
  python_version: "3.10"
9
+ suggested_hardware: zero-a10g
10
  app_file: app.py
11
  pinned: false
12
  license: mit
app.py CHANGED
@@ -1,15 +1,30 @@
1
  """
2
  GastroGen — Generative AI for Gastrointestinal Health (v2.0)
3
- Official Hugging Face Gradio Deployment Studio
4
 
5
  Lead Clinician / Researcher: Devashri
6
- Framework: PyTorch 2.x • Gradio 4.x/5.x • Kvasir-v2 Medical Endoscopy Benchmark
7
  """
8
 
9
  import os
10
  import io
11
  import math
12
  import time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  import torch
14
  import torch.nn as nn
15
  import torch.nn.functional as F
@@ -28,11 +43,11 @@ from model import Autoencoder, VAE, TransformerVAE
28
  # Startup diagnostic
29
  print("GastroGen Hugging Face Space started")
30
  print("PyTorch version:", torch.__version__)
31
- print("CUDA available:", torch.cuda.is_available())
32
 
33
- # Device setup
 
34
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
35
- print(f"[+] GastroGen running on device: {device}")
36
 
37
  # -------------------------------------------------------------
38
  # 1. Model Initialization
@@ -63,8 +78,9 @@ def load_all_models():
63
  path = config["path"]
64
  if os.path.exists(path):
65
  try:
66
- model = config["class_fn"]().to(device)
67
- checkpoint = torch.load(path, map_location=device)
 
68
  if isinstance(checkpoint, dict) and "state_dict" in checkpoint:
69
  model.load_state_dict(checkpoint["state_dict"])
70
  elif isinstance(checkpoint, dict) and "model_state_dict" in checkpoint:
@@ -116,10 +132,11 @@ def preprocess_image(pil_img):
116
  return None, None
117
  if pil_img.mode != "RGB":
118
  pil_img = pil_img.convert("RGB")
119
- tensor = transform_pipe(pil_img).unsqueeze(0).to(device)
120
  orig_np = tensor.squeeze(0).permute(1, 2, 0).cpu().numpy()
121
  return tensor, orig_np
122
 
 
123
  def run_autoencoder_inference(input_img):
124
  if input_img is None:
125
  return None, "Please upload or select an endoscopy image.", "", ""
@@ -129,6 +146,10 @@ def run_autoencoder_inference(input_img):
129
  if model is None:
130
  return None, "Autoencoder model weight checkpoint not loaded.", "", ""
131
 
 
 
 
 
132
  start_t = time.time()
133
  with torch.no_grad():
134
  recon_tensor = model(tensor)
@@ -150,12 +171,13 @@ def run_autoencoder_inference(input_img):
150
  | **SSIM Score** | **`{ssim:.4f}`** | $> 0.950$ | 🟢 Optimal High-Fidelity |
151
  | **PSNR** | **`{psnr:.2f} dB`** | $> 30.0$ dB | 🟢 Low-Noise Signal |
152
  | **MSE Loss** | **`{mse:.6f}`** | $< 0.0020$ | 🟢 Minimal Variance |
153
- | **Inference Latency** | **`{latency:.1f} ms`** | Real-time | ⚡ Accelerated |
154
  """
155
  # Anomaly status
156
  normal_model = loaded_models.get("normal_only_autoencoder")
157
  anomaly_md = ""
158
  if normal_model:
 
159
  with torch.no_grad():
160
  norm_recon = normal_model(tensor).squeeze(0).clamp(0, 1).permute(1, 2, 0).cpu().numpy()
161
  anomaly_score = float(np.mean((orig_np - norm_recon) ** 2))
@@ -163,9 +185,16 @@ def run_autoencoder_inference(input_img):
163
  status_color = "🔴 Pathology / Abnormality Detected" if is_pathology else "🟢 Healthy Mucosal Landmark"
164
  anomaly_md = f"**Abnormality Screening**: {status_color} *(Score: `{anomaly_score:.6f}` | Cutoff: `{ANOMALY_THRESHOLD:.6f}`)*"
165
 
 
 
 
 
 
 
166
  return recon_pil, metrics_md, anomaly_md
167
 
168
 
 
169
  def run_vae_inference(input_img, noise_scale):
170
  if input_img is None:
171
  return None, "Please upload or select an endoscopy image."
@@ -175,6 +204,10 @@ def run_vae_inference(input_img, noise_scale):
175
  if model is None:
176
  return None, "VAE model weight checkpoint not loaded."
177
 
 
 
 
 
178
  start_t = time.time()
179
  with torch.no_grad():
180
  mu, logvar, skips = model.encode(tensor)
@@ -200,11 +233,16 @@ def run_vae_inference(input_img, noise_scale):
200
  | **SSIM Score** | **`{ssim:.4f}`** | Structural texture preservation across latent manifold |
201
  | **PSNR** | **`{psnr:.2f} dB`** | Peak Signal-to-Noise Ratio |
202
  | **Sobel Edge MSE** | **`{mse:.6f}`** | Edge variance regularized by Sobel gradient loss |
203
- | **Latent Sampling Latency** | **`{latency:.1f} ms`** | Reparameterization forward pass |
204
  """
 
 
 
 
205
  return recon_pil, metrics_md
206
 
207
 
 
208
  def run_vit_inference(input_img, view_mode):
209
  if input_img is None:
210
  return None, {}, "Please upload or select an endoscopy image."
@@ -214,6 +252,10 @@ def run_vit_inference(input_img, view_mode):
214
  if model is None:
215
  return None, {}, "Vision Transformer weight checkpoint not loaded."
216
 
 
 
 
 
217
  start_t = time.time()
218
  with torch.no_grad():
219
  recon_tensor, mu, logvar = model(tensor)
@@ -265,10 +307,16 @@ def run_vit_inference(input_img, view_mode):
265
  out_pil = Image.blend(orig_pil_128, heatmap_pil, alpha=0.45)
266
 
267
  top_class = max(prob_dict, key=prob_dict.get)
268
- info_md = f"**Primary ViT Diagnosis**: `{top_class}` ({prob_dict[top_class]*100:.1f}%) | Latency: `{latency:.1f} ms`"
 
 
 
 
 
269
  return out_pil, prob_dict, info_md
270
 
271
 
 
272
  def run_model_comparison(input_img):
273
  if input_img is None:
274
  return None, None, None, None, "Please upload or select an endoscopy image."
@@ -280,9 +328,13 @@ def run_model_comparison(input_img):
280
  vae_model = loaded_models.get("vae")
281
  vit_model = loaded_models.get("transformer_vae")
282
 
 
 
 
283
  # 1. Autoencoder
 
284
  with torch.no_grad():
285
- ae_tensor = ae_model(tensor).squeeze(0).clamp(0, 1)
286
  ae_np = ae_tensor.permute(1, 2, 0).cpu().numpy()
287
  ae_pil = Image.fromarray((ae_np * 255).astype(np.uint8))
288
  ae_ssim = float(ssim_fn(orig_np, ae_np, channel_axis=2, data_range=1.0))
@@ -290,8 +342,9 @@ def run_model_comparison(input_img):
290
  ae_mse = float(np.mean((orig_np - ae_np) ** 2))
291
 
292
  # 2. VAE
 
293
  with torch.no_grad():
294
- mu, logvar, skips = vae_model.encode(tensor)
295
  vae_tensor = vae_model.decoder(mu, skips).squeeze(0).clamp(0, 1)
296
  vae_np = vae_tensor.permute(1, 2, 0).cpu().numpy()
297
  vae_pil = Image.fromarray((vae_np * 255).astype(np.uint8))
@@ -300,8 +353,9 @@ def run_model_comparison(input_img):
300
  vae_mse = float(np.mean((orig_np - vae_np) ** 2))
301
 
302
  # 3. ViT
 
303
  with torch.no_grad():
304
- vit_tensor, _, _ = vit_model(tensor)
305
  vit_np = vit_tensor.squeeze(0).clamp(0, 1).permute(1, 2, 0).cpu().numpy()
306
  vit_pil = Image.fromarray((vit_np * 255).astype(np.uint8))
307
  vit_ssim = float(ssim_fn(orig_np, vit_np, channel_axis=2, data_range=1.0))
@@ -312,10 +366,16 @@ def run_model_comparison(input_img):
312
  ### 🏆 Clinical Multi-Model Benchmark Results
313
  | Architecture | Latent Space | SSIM (Fidelity) | PSNR | MSE Loss | Device |
314
  | :--- | :--- | :--- | :--- | :--- | :--- |
315
- | **Autoencoder v2** | 256-D Bottleneck | **`{ae_ssim:.4f}`** | **`{ae_psnr:.2f} dB`** | **`{ae_mse:.6f}`** | {device.type.upper()} |
316
- | **Spatial VAE v2** | $8 \\times 32 \\times 32$ Map | **`{vae_ssim:.4f}`** | **`{vae_psnr:.2f} dB`** | **`{vae_mse:.6f}`** | {device.type.upper()} |
317
- | **Vision Transformer (ViT)** | 196 Patch Tokens | **`{vit_ssim:.4f}`** | **`{vit_psnr:.2f} dB`** | **`{vit_mse:.6f}`** | {device.type.upper()} |
318
  """
 
 
 
 
 
 
319
  return orig_pil, ae_pil, vae_pil, vit_pil, table_md
320
 
321
 
 
1
  """
2
  GastroGen — Generative AI for Gastrointestinal Health (v2.0)
3
+ Official Hugging Face Gradio Deployment Studio (ZeroGPU Accelerated)
4
 
5
  Lead Clinician / Researcher: Devashri
6
+ Framework: PyTorch 2.x • Gradio 6.x • ZeroGPU • Kvasir-v2 Medical Endoscopy Benchmark
7
  """
8
 
9
  import os
10
  import io
11
  import math
12
  import time
13
+
14
+ # ZeroGPU initialization: spaces MUST be imported BEFORE torch or any CUDA-related library
15
+ try:
16
+ import spaces
17
+ except ImportError:
18
+ class _SpacesMock:
19
+ @staticmethod
20
+ def GPU(fn=None, duration=None):
21
+ if fn is None:
22
+ def decorator(func):
23
+ return func
24
+ return decorator
25
+ return fn
26
+ spaces = _SpacesMock()
27
+
28
  import torch
29
  import torch.nn as nn
30
  import torch.nn.functional as F
 
43
  # Startup diagnostic
44
  print("GastroGen Hugging Face Space started")
45
  print("PyTorch version:", torch.__version__)
 
46
 
47
+ # Device strategy: Support local CPU/CUDA development and HF ZeroGPU
48
+ # Models remain on CPU initially; GPU is dynamically allocated inside @spaces.GPU functions
49
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
50
+ print(f"[+] GastroGen default host device: {device}")
51
 
52
  # -------------------------------------------------------------
53
  # 1. Model Initialization
 
78
  path = config["path"]
79
  if os.path.exists(path):
80
  try:
81
+ # Load all models initially into CPU memory for ZeroGPU compatibility
82
+ model = config["class_fn"]().to("cpu")
83
+ checkpoint = torch.load(path, map_location="cpu")
84
  if isinstance(checkpoint, dict) and "state_dict" in checkpoint:
85
  model.load_state_dict(checkpoint["state_dict"])
86
  elif isinstance(checkpoint, dict) and "model_state_dict" in checkpoint:
 
132
  return None, None
133
  if pil_img.mode != "RGB":
134
  pil_img = pil_img.convert("RGB")
135
+ tensor = transform_pipe(pil_img).unsqueeze(0)
136
  orig_np = tensor.squeeze(0).permute(1, 2, 0).cpu().numpy()
137
  return tensor, orig_np
138
 
139
+ @spaces.GPU
140
  def run_autoencoder_inference(input_img):
141
  if input_img is None:
142
  return None, "Please upload or select an endoscopy image.", "", ""
 
146
  if model is None:
147
  return None, "Autoencoder model weight checkpoint not loaded.", "", ""
148
 
149
+ infer_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
150
+ model = model.to(infer_device)
151
+ tensor = tensor.to(infer_device)
152
+
153
  start_t = time.time()
154
  with torch.no_grad():
155
  recon_tensor = model(tensor)
 
171
  | **SSIM Score** | **`{ssim:.4f}`** | $> 0.950$ | 🟢 Optimal High-Fidelity |
172
  | **PSNR** | **`{psnr:.2f} dB`** | $> 30.0$ dB | 🟢 Low-Noise Signal |
173
  | **MSE Loss** | **`{mse:.6f}`** | $< 0.0020$ | 🟢 Minimal Variance |
174
+ | **Inference Latency** | **`{latency:.1f} ms`** | Real-time | ⚡ Accelerated ({infer_device.type.upper()}) |
175
  """
176
  # Anomaly status
177
  normal_model = loaded_models.get("normal_only_autoencoder")
178
  anomaly_md = ""
179
  if normal_model:
180
+ normal_model = normal_model.to(infer_device)
181
  with torch.no_grad():
182
  norm_recon = normal_model(tensor).squeeze(0).clamp(0, 1).permute(1, 2, 0).cpu().numpy()
183
  anomaly_score = float(np.mean((orig_np - norm_recon) ** 2))
 
185
  status_color = "🔴 Pathology / Abnormality Detected" if is_pathology else "🟢 Healthy Mucosal Landmark"
186
  anomaly_md = f"**Abnormality Screening**: {status_color} *(Score: `{anomaly_score:.6f}` | Cutoff: `{ANOMALY_THRESHOLD:.6f}`)*"
187
 
188
+ if infer_device.type == "cuda":
189
+ model.to("cpu")
190
+ if normal_model:
191
+ normal_model.to("cpu")
192
+ torch.cuda.empty_cache()
193
+
194
  return recon_pil, metrics_md, anomaly_md
195
 
196
 
197
+ @spaces.GPU
198
  def run_vae_inference(input_img, noise_scale):
199
  if input_img is None:
200
  return None, "Please upload or select an endoscopy image."
 
204
  if model is None:
205
  return None, "VAE model weight checkpoint not loaded."
206
 
207
+ infer_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
208
+ model = model.to(infer_device)
209
+ tensor = tensor.to(infer_device)
210
+
211
  start_t = time.time()
212
  with torch.no_grad():
213
  mu, logvar, skips = model.encode(tensor)
 
233
  | **SSIM Score** | **`{ssim:.4f}`** | Structural texture preservation across latent manifold |
234
  | **PSNR** | **`{psnr:.2f} dB`** | Peak Signal-to-Noise Ratio |
235
  | **Sobel Edge MSE** | **`{mse:.6f}`** | Edge variance regularized by Sobel gradient loss |
236
+ | **Latent Sampling Latency** | **`{latency:.1f} ms`** | Reparameterization forward pass ({infer_device.type.upper()}) |
237
  """
238
+ if infer_device.type == "cuda":
239
+ model.to("cpu")
240
+ torch.cuda.empty_cache()
241
+
242
  return recon_pil, metrics_md
243
 
244
 
245
+ @spaces.GPU
246
  def run_vit_inference(input_img, view_mode):
247
  if input_img is None:
248
  return None, {}, "Please upload or select an endoscopy image."
 
252
  if model is None:
253
  return None, {}, "Vision Transformer weight checkpoint not loaded."
254
 
255
+ infer_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
256
+ model = model.to(infer_device)
257
+ tensor = tensor.to(infer_device)
258
+
259
  start_t = time.time()
260
  with torch.no_grad():
261
  recon_tensor, mu, logvar = model(tensor)
 
307
  out_pil = Image.blend(orig_pil_128, heatmap_pil, alpha=0.45)
308
 
309
  top_class = max(prob_dict, key=prob_dict.get)
310
+ info_md = f"**Primary ViT Diagnosis**: `{top_class}` ({prob_dict[top_class]*100:.1f}%) | Latency: `{latency:.1f} ms` ({infer_device.type.upper()})"
311
+
312
+ if infer_device.type == "cuda":
313
+ model.to("cpu")
314
+ torch.cuda.empty_cache()
315
+
316
  return out_pil, prob_dict, info_md
317
 
318
 
319
+ @spaces.GPU
320
  def run_model_comparison(input_img):
321
  if input_img is None:
322
  return None, None, None, None, "Please upload or select an endoscopy image."
 
328
  vae_model = loaded_models.get("vae")
329
  vit_model = loaded_models.get("transformer_vae")
330
 
331
+ infer_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
332
+ tensor_dev = tensor.to(infer_device)
333
+
334
  # 1. Autoencoder
335
+ ae_model = ae_model.to(infer_device)
336
  with torch.no_grad():
337
+ ae_tensor = ae_model(tensor_dev).squeeze(0).clamp(0, 1)
338
  ae_np = ae_tensor.permute(1, 2, 0).cpu().numpy()
339
  ae_pil = Image.fromarray((ae_np * 255).astype(np.uint8))
340
  ae_ssim = float(ssim_fn(orig_np, ae_np, channel_axis=2, data_range=1.0))
 
342
  ae_mse = float(np.mean((orig_np - ae_np) ** 2))
343
 
344
  # 2. VAE
345
+ vae_model = vae_model.to(infer_device)
346
  with torch.no_grad():
347
+ mu, logvar, skips = vae_model.encode(tensor_dev)
348
  vae_tensor = vae_model.decoder(mu, skips).squeeze(0).clamp(0, 1)
349
  vae_np = vae_tensor.permute(1, 2, 0).cpu().numpy()
350
  vae_pil = Image.fromarray((vae_np * 255).astype(np.uint8))
 
353
  vae_mse = float(np.mean((orig_np - vae_np) ** 2))
354
 
355
  # 3. ViT
356
+ vit_model = vit_model.to(infer_device)
357
  with torch.no_grad():
358
+ vit_tensor, _, _ = vit_model(tensor_dev)
359
  vit_np = vit_tensor.squeeze(0).clamp(0, 1).permute(1, 2, 0).cpu().numpy()
360
  vit_pil = Image.fromarray((vit_np * 255).astype(np.uint8))
361
  vit_ssim = float(ssim_fn(orig_np, vit_np, channel_axis=2, data_range=1.0))
 
366
  ### 🏆 Clinical Multi-Model Benchmark Results
367
  | Architecture | Latent Space | SSIM (Fidelity) | PSNR | MSE Loss | Device |
368
  | :--- | :--- | :--- | :--- | :--- | :--- |
369
+ | **Autoencoder v2** | 256-D Bottleneck | **`{ae_ssim:.4f}`** | **`{ae_psnr:.2f} dB`** | **`{ae_mse:.6f}`** | {infer_device.type.upper()} |
370
+ | **Spatial VAE v2** | $8 \\times 32 \\times 32$ Map | **`{vae_ssim:.4f}`** | **`{vae_psnr:.2f} dB`** | **`{vae_mse:.6f}`** | {infer_device.type.upper()} |
371
+ | **Vision Transformer (ViT)** | 196 Patch Tokens | **`{vit_ssim:.4f}`** | **`{vit_psnr:.2f} dB`** | **`{vit_mse:.6f}`** | {infer_device.type.upper()} |
372
  """
373
+ if infer_device.type == "cuda":
374
+ for m in [ae_model, vae_model, vit_model]:
375
+ if m:
376
+ m.to("cpu")
377
+ torch.cuda.empty_cache()
378
+
379
  return orig_pil, ae_pil, vae_pil, vit_pil, table_md
380
 
381