AymanAmeen commited on
Commit
88675f1
·
verified ·
1 Parent(s): c25fafe

SimEIT Demo

Browse files
Files changed (3) hide show
  1. README.md +57 -11
  2. app.py +599 -0
  3. requirements.txt +8 -0
README.md CHANGED
@@ -1,13 +1,59 @@
 
 
 
 
 
 
 
 
 
1
  ---
2
- title: SimEIT Demo
3
- emoji: 🚀
4
- colorFrom: gray
5
- colorTo: green
6
- sdk: gradio
7
- sdk_version: 5.49.1
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SimEIT: Large-Scale Electrical Impedance Tomography Dataset Visualizer
2
+
3
+ **A Scalable Simulation Framework for Generating Physically Consistent, AI-Ready EIT Training Data**
4
+
5
+ *Ayman A. Ameen<sup>1</sup>, Franziska Mathis-Ullrich<sup>1</sup>, Bernhard Kainz<sup>2</sup>*
6
+
7
+ <sup>1</sup>Friedrich-Alexander University Erlangen-Nürnberg
8
+ <sup>2</sup>Imperial College London
9
+
10
  ---
 
 
 
 
 
 
 
 
 
 
11
 
12
+ This repository contains an interactive visualization tool for exploring large-scale synthetic EIT (Electrical Impedance Tomography) datasets generated using the **SimEIT framework**—a scalable simulation platform for creating physically consistent, AI-ready training data.
13
+
14
+ ## About SimEIT
15
+
16
+ Electrical Impedance Tomography (EIT) offers advantages over conventional imaging methods, such as X-ray and MRI, but suffers from an ill-posed inverse problem. Deep learning can alleviate this challenge, yet progress is limited by the lack of large, diverse, and reproducible datasets.
17
+
18
+ **SimEIT** enables high-throughput creation of diverse geometries and conductivity maps using parallelized finite element simulations, reproducible seeding, and automated validation. The framework provides multi-resolution, AI-ready HDF5 outputs with PyTorch integration, bridging the gap between physical simulation and AI training.
19
+
20
+ ## Features
21
+
22
+ - 🔄 **Streaming Mode**: Load datasets without downloading them entirely
23
+ - 🖼️ **Multi-resolution Images**: View images at different resolutions (256x256, 128x128, 64x64, 32x32)
24
+ - 📊 **Voltage Plots**: Visualize voltage data per electrode
25
+ - 🎲 **Random/Manual Selection**: Choose samples randomly or by index
26
+
27
+ ## Setup
28
+
29
+ ```bash
30
+ conda create -n SimEIT python=3.13
31
+
32
+ conda activate SimEIT
33
+
34
+ conda install pip
35
+
36
+ pip install -r requirements.txt
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ Run the application:
42
+
43
+ ```bash
44
+ python appfile.py
45
+ ```
46
+
47
+ The Gradio interface will launch in your browser where you can:
48
+ - Generate random sample indices or enter specific ones (0-100,000)
49
+ - Click "Show Images" to visualize the selected sample
50
+ - View images at different resolutions (256x256, 128x128, 64x64, 32x32)
51
+ - View voltage plots per electrode
52
+
53
+ ## File Structure
54
+
55
+ ```
56
+ ├── appfile.py # Main application (all-in-one)
57
+ ├── requirements.txt # Python dependencies
58
+ └── README.md # This file
59
+ ```
app.py ADDED
@@ -0,0 +1,599 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SimEIT Dataset Visualizer
3
+ A Gradio-based application for visualizing EIT (Electrical Impedance Tomography) datasets
4
+ from Hugging Face Hub with interactive plots and configurations.
5
+
6
+ Author: Ayman A. Ameen
7
+ """
8
+
9
+ import random
10
+ import numpy as np
11
+ import h5py
12
+ import gradio as gr
13
+ import plotly.graph_objects as go
14
+ from huggingface_hub import HfFileSystem
15
+
16
+
17
+ # ============================================================================
18
+ # CONFIGURATION
19
+ # ============================================================================
20
+
21
+ DATASET_CONFIG = {
22
+ 'hf_dataset': 'AymanAmeen/SimEIT-dataset',
23
+ 'hf_split': 'train',
24
+ 'hf_subset': 'FourObjects', # Options: 'FourObjects' or 'CirclesOnly'
25
+ }
26
+
27
+ AVAILABLE_SUBSETS = ['FourObjects', 'CirclesOnly']
28
+ AVAILABLE_RESOLUTIONS = ['256', '128_log', '64_log', '32_log']
29
+ AVAILABLE_COLORMAPS = [
30
+ 'Jet', 'Viridis', 'Plasma', 'Inferno', 'Magma', 'Cividis',
31
+ 'Hot', 'Cool', 'RdBu', 'RdYlBu', 'Spectral', 'Turbo',
32
+ 'Blues', 'Greens', 'Reds', 'YlOrRd', 'Portland', 'Picnic'
33
+ ]
34
+
35
+
36
+ # ============================================================================
37
+ # DATA LOADER
38
+ # ============================================================================
39
+
40
+ class HFDatasetLoader:
41
+ """
42
+ Loads samples from Hugging Face dataset HDF5 file via streaming.
43
+
44
+ Features:
45
+ - Streams data directly from Hugging Face Hub without downloading
46
+ - Implements LRU cache for frequently accessed samples
47
+ - Supports lazy loading of specific resolutions
48
+ """
49
+
50
+ def __init__(self, dataset_name, split="train", subset="FourObjects", cache_size=50):
51
+ """
52
+ Initialize the dataset loader.
53
+
54
+ Args:
55
+ dataset_name: Name of the HuggingFace dataset
56
+ split: Dataset split (default: "train")
57
+ subset: Dataset subset (e.g., "FourObjects", "CirclesOnly")
58
+ cache_size: Number of samples to cache (default: 50)
59
+ """
60
+ self.dataset_name = dataset_name
61
+ self.split = split
62
+ self.subset = subset
63
+ self.cache_size = cache_size
64
+ self._cache = {}
65
+ self._cache_order = []
66
+
67
+ print(f"Connecting to dataset {dataset_name} (subset: {subset}) via streaming...")
68
+
69
+ # Initialize HuggingFace filesystem for streaming
70
+ self.fs = HfFileSystem()
71
+ self.h5_path = f"datasets/{dataset_name}/{subset}/dataset.h5"
72
+
73
+ # Open HDF5 file in streaming mode and keep it open
74
+ self._file_handle = self.fs.open(self.h5_path, 'rb')
75
+ self.h5file = h5py.File(self._file_handle, 'r')
76
+
77
+ # Get dataset size
78
+ self.num_samples = self.h5file['image']['256'].shape[2]
79
+
80
+ print(f"✓ Dataset connected successfully!")
81
+ print(f" Total samples: {self.num_samples:,}")
82
+ print(f" Cache enabled: storing last {cache_size} samples")
83
+
84
+ def __del__(self):
85
+ """Clean up file handles on object destruction."""
86
+ if hasattr(self, 'h5file'):
87
+ self.h5file.close()
88
+ if hasattr(self, '_file_handle'):
89
+ self._file_handle.close()
90
+
91
+ def get_sample(self, index, image_resolution=None):
92
+ """
93
+ Get a specific sample by index from the HDF5 file.
94
+
95
+ Args:
96
+ index: Sample index to load (0 to num_samples-1)
97
+ image_resolution: Specific resolution to load (e.g., '256', '128_log')
98
+ If None, loads all resolutions (slower)
99
+
100
+ Returns:
101
+ dict: Sample data containing voltage and image data
102
+
103
+ Raises:
104
+ ValueError: If index is out of range
105
+ """
106
+ # Check if index is out of range and clamp to last sample
107
+ if index < 0 or index >= self.num_samples:
108
+ print(f"⚠ Index {index} out of range [0, {self.num_samples}), using last sample {self.num_samples - 1}")
109
+ index = self.num_samples - 1
110
+
111
+ # Create cache key based on index and resolution
112
+ cache_key = (index, image_resolution)
113
+
114
+ # Check if already in cache
115
+ if cache_key in self._cache:
116
+ print(f"✓ Cache hit for sample {index}, resolution {image_resolution}")
117
+ return self._cache[cache_key]
118
+
119
+ print(f"Loading sample {index}, resolution {image_resolution}...")
120
+ sample = {}
121
+
122
+ # Load voltage data (stored as [256, num_samples])
123
+ sample['volt_16'] = self.h5file['volt']['16'][:, index]
124
+
125
+ # Lazy load: only load the requested image resolution
126
+ if image_resolution:
127
+ sample[f'image_{image_resolution}'] = self.h5file['image'][image_resolution][:, :, index]
128
+ else:
129
+ # Load all resolutions (backward compatibility)
130
+ for res in AVAILABLE_RESOLUTIONS:
131
+ sample[f'image_{res}'] = self.h5file['image'][res][:, :, index]
132
+
133
+ # Add to cache
134
+ self._add_to_cache(cache_key, sample)
135
+
136
+ return sample
137
+
138
+ def _add_to_cache(self, key, value):
139
+ """
140
+ Add item to cache with LRU (Least Recently Used) eviction.
141
+
142
+ Args:
143
+ key: Cache key (tuple of index and resolution)
144
+ value: Sample data to cache
145
+ """
146
+ if key in self._cache:
147
+ # Move to end (most recent)
148
+ self._cache_order.remove(key)
149
+ self._cache_order.append(key)
150
+ else:
151
+ # Add new item
152
+ if len(self._cache) >= self.cache_size:
153
+ # Evict oldest item
154
+ oldest_key = self._cache_order.pop(0)
155
+ del self._cache[oldest_key]
156
+
157
+ self._cache[key] = value
158
+ self._cache_order.append(key)
159
+
160
+
161
+ # ============================================================================
162
+ # VISUALIZATION FUNCTIONS
163
+ # ============================================================================
164
+
165
+ def create_heatmap_plot(key, index=0, colorscale='Jet'):
166
+ """
167
+ Create a Plotly heatmap from dataset image.
168
+
169
+ Args:
170
+ key: Image resolution key (e.g., '256', '128_log')
171
+ index: Sample index
172
+ colorscale: Plotly colorscale name
173
+
174
+ Returns:
175
+ plotly.graph_objects.Figure: Heatmap figure
176
+ """
177
+ global _hf_loader
178
+
179
+ try:
180
+ # Lazy load: only fetch the specific resolution needed
181
+ sample = _hf_loader.get_sample(index, image_resolution=key)
182
+ img = sample.get(f'image_{key}')
183
+
184
+ if img is None:
185
+ print(f"✗ Missing image_{key} in sample {index}")
186
+ return go.Figure()
187
+
188
+ # Convert to numpy array
189
+ img = np.array(img)
190
+
191
+ # Handle log-scaled images (negative values)
192
+ if len(img.shape) == 2 and np.min(img) < 0:
193
+ img = np.exp(img) # Convert from log back to linear
194
+
195
+ # If RGB image, convert to grayscale for heatmap
196
+ if len(img.shape) == 3 and img.shape[-1] == 3:
197
+ img = np.mean(img, axis=2)
198
+
199
+ # Normalize image values using mean and std for this sample
200
+ img_mean = np.mean(img)
201
+ img_std = np.std(img)
202
+ if img_std > 0: # Avoid division by zero
203
+ img_normalized = (img - img_mean) / img_std
204
+ else:
205
+ img_normalized = img - img_mean
206
+
207
+ # Create heatmap
208
+ fig = go.Figure(data=go.Heatmap(
209
+ z=img_normalized,
210
+ colorscale=colorscale,
211
+ showscale=True,
212
+ colorbar=dict(title="Normalized Conductivity")
213
+ ))
214
+
215
+ fig.update_layout(
216
+ title=dict(text=f"{key} Image (Normalized) - Sample {index}", x=0.5, xanchor='center'),
217
+ width=450,
218
+ height=450,
219
+ xaxis=dict(showticklabels=False, showgrid=False),
220
+ yaxis=dict(showticklabels=False, showgrid=False, scaleanchor="x", scaleratio=1),
221
+ margin=dict(l=20, r=20, t=50, b=20),
222
+ autosize=False
223
+ )
224
+
225
+ return fig
226
+
227
+ except Exception as e:
228
+ print(f"✗ Error creating heatmap for image_{key}: {e}")
229
+ import traceback
230
+ traceback.print_exc()
231
+ return go.Figure()
232
+
233
+
234
+ def draw_voltage_plot(index=0):
235
+ """
236
+ Draw voltage plot from dataset.
237
+
238
+ Args:
239
+ index: Sample index
240
+
241
+ Returns:
242
+ plotly.graph_objects.Figure: Voltage plot figure
243
+ """
244
+ global _hf_loader
245
+
246
+ try:
247
+ # Load only voltage data (no images needed)
248
+ sample = _hf_loader.get_sample(index, image_resolution=None)
249
+ volt_data = sample.get('volt_16')
250
+
251
+ if volt_data is None:
252
+ print(f"✗ Missing voltage data in sample {index}")
253
+ return go.Figure()
254
+
255
+ volt_data = np.array(volt_data, dtype=np.float64)
256
+ if len(volt_data.shape) > 1:
257
+ volt_data = volt_data.flatten()
258
+
259
+ # Normalize voltage values using mean and std for this sample
260
+ volt_mean = np.mean(volt_data)
261
+ volt_std = np.std(volt_data)
262
+ if volt_std > 0: # Avoid division by zero
263
+ volt_normalized = (volt_data - volt_mean) / volt_std
264
+ else:
265
+ volt_normalized = volt_data - volt_mean
266
+
267
+ electrodes = np.arange(1, len(volt_normalized) + 1)
268
+
269
+ # Create line plot
270
+ fig = go.Figure()
271
+ fig.add_trace(go.Scatter(
272
+ x=electrodes,
273
+ y=volt_normalized,
274
+ mode='lines+markers',
275
+ marker=dict(size=6, color='royalblue'),
276
+ line=dict(width=2, color='royalblue')
277
+ ))
278
+
279
+ fig.update_layout(
280
+ title=dict(text=f"Voltage Measurement (Normalized) - Sample {index}", x=0.5, xanchor='center'),
281
+ xaxis_title="Electrode Number (n)",
282
+ yaxis_title="Normalized Voltage (a.u.)",
283
+ template="plotly_white",
284
+ showlegend=False,
285
+ width=450,
286
+ height=450,
287
+ margin=dict(l=60, r=20, t=50, b=50),
288
+ autosize=False
289
+ )
290
+ fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='LightGray')
291
+ fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='LightGray')
292
+
293
+ return fig
294
+
295
+ except Exception as e:
296
+ print(f"✗ Error plotting voltage: {e}")
297
+ return go.Figure()
298
+
299
+
300
+ # ============================================================================
301
+ # GRADIO UI HELPER FUNCTIONS
302
+ # ============================================================================
303
+
304
+ def get_dataset_info():
305
+ """Get current dataset information string."""
306
+ global _hf_loader
307
+ return f"HuggingFace: {DATASET_CONFIG['hf_dataset']} (subset: {_hf_loader.subset}, split: {DATASET_CONFIG['hf_split']})"
308
+
309
+
310
+ def get_max_index():
311
+ """Get maximum valid index in current dataset."""
312
+ global _hf_loader
313
+ return _hf_loader.num_samples - 1
314
+
315
+
316
+ def generate_random_index(state):
317
+ """
318
+ Generate a random valid index and update state.
319
+
320
+ Args:
321
+ state: Current state list of indices
322
+
323
+ Returns:
324
+ tuple: (random_index, updated_state)
325
+ """
326
+ num = random.randint(0, get_max_index())
327
+ new_list = state + [num]
328
+ return num, new_list
329
+
330
+
331
+ def select_index(n, state):
332
+ """
333
+ Select a specific index with validation.
334
+
335
+ Args:
336
+ n: Index to select
337
+ state: Current state list of indices
338
+
339
+ Returns:
340
+ tuple: (validated_index, updated_state)
341
+ """
342
+ if n is None or n == "":
343
+ return "", state
344
+
345
+ max_idx = get_max_index()
346
+ if not (0 <= n <= max_idx):
347
+ return f"Number must be between 0 and {max_idx}.", state
348
+
349
+ new_list = state + [int(n)]
350
+ return int(n), new_list
351
+
352
+
353
+ def show_images(state, image_res, colorscale):
354
+ """
355
+ Display images for the last selected index in state.
356
+
357
+ Args:
358
+ state: State list containing selected indices
359
+ image_res: Image resolution to display
360
+ colorscale: Colorscale for heatmap
361
+
362
+ Returns:
363
+ tuple: (image_plot, voltage_plot, status_message)
364
+ """
365
+ if not state:
366
+ return go.Figure(), go.Figure(), "No index selected"
367
+
368
+ last_index = state[-1]
369
+ return (
370
+ create_heatmap_plot(image_res, last_index, colorscale),
371
+ draw_voltage_plot(last_index),
372
+ f"✓ Loaded sample {last_index} with {image_res} resolution and colormap: {colorscale}"
373
+ )
374
+
375
+
376
+ def generate_random_and_show(state, image_res, colorscale):
377
+ """Generate random index and show corresponding images."""
378
+ num, new_list = generate_random_index(state)
379
+ outputs = show_images(new_list, image_res, colorscale)
380
+ return (num, new_list) + outputs
381
+
382
+
383
+ def select_n_and_show(n, state, image_res, colorscale):
384
+ """Select specific index and show corresponding images."""
385
+ _, new_list = select_index(n, state)
386
+ outputs = show_images(new_list, image_res, colorscale)
387
+ return (new_list,) + outputs
388
+
389
+
390
+ def reload_dataset(subset, state, image_res, colorscale):
391
+ """
392
+ Reload the dataset with a new subset and display a sample.
393
+
394
+ Args:
395
+ subset: New subset to load
396
+ state: Current state list
397
+ image_res: Image resolution
398
+ colorscale: Colorscale for heatmap
399
+
400
+ Returns:
401
+ tuple: Updated UI components
402
+ """
403
+ global _hf_loader
404
+
405
+ try:
406
+ # Close old loader
407
+ if _hf_loader is not None:
408
+ del _hf_loader
409
+
410
+ # Create new loader with selected subset
411
+ dataset_name = DATASET_CONFIG['hf_dataset']
412
+ split = DATASET_CONFIG['hf_split']
413
+ _hf_loader = HFDatasetLoader(dataset_name, split, subset)
414
+ max_idx = get_max_index()
415
+
416
+ # Update dataset info
417
+ info_md = f"""
418
+ # SimEIT: Dataset Visualizer
419
+
420
+ **Dataset:** `{get_dataset_info()}` | **Total Samples:** {max_idx + 1:,}
421
+ """
422
+
423
+ # Determine which sample to display
424
+ if state and len(state) > 0:
425
+ last_index = state[-1]
426
+ sample_index = last_index if last_index <= max_idx else random.randint(0, max_idx)
427
+ else:
428
+ sample_index = random.randint(0, max_idx)
429
+
430
+ # Update state with the new sample
431
+ new_state = [sample_index]
432
+
433
+ # Generate plots for the sample
434
+ image_plot = create_heatmap_plot(image_res, sample_index, colorscale)
435
+ volt_plot = draw_voltage_plot(sample_index)
436
+ status_msg = f"✓ Loaded subset: {subset} ({max_idx + 1:,} samples) - Displaying sample {sample_index}"
437
+
438
+ return (
439
+ info_md,
440
+ gr.Number(label=f"Enter an integer (0–{max_idx})", precision=0, value=sample_index),
441
+ new_state,
442
+ image_plot,
443
+ volt_plot,
444
+ status_msg
445
+ )
446
+ except Exception as e:
447
+ return (
448
+ gr.Markdown(),
449
+ gr.Number(),
450
+ [],
451
+ go.Figure(),
452
+ go.Figure(),
453
+ f"��� Error loading subset {subset}: {str(e)}"
454
+ )
455
+
456
+
457
+ # ============================================================================
458
+ # MAIN APPLICATION
459
+ # ============================================================================
460
+
461
+ # Global dataset loader instance
462
+ _hf_loader = None
463
+
464
+
465
+ def create_gradio_interface():
466
+ """
467
+ Create and configure the Gradio interface.
468
+
469
+ Returns:
470
+ gr.Blocks: Configured Gradio application
471
+ """
472
+ global _hf_loader
473
+
474
+ # Initialize configuration
475
+ dataset_name = DATASET_CONFIG['hf_dataset']
476
+ split = DATASET_CONFIG['hf_split']
477
+ default_subset = DATASET_CONFIG['hf_subset']
478
+
479
+ # Initialize dataset loader with default subset
480
+ _hf_loader = HFDatasetLoader(dataset_name, split, default_subset)
481
+
482
+ with gr.Blocks(title="SimEIT Dataset Visualizer") as demo:
483
+ # Header
484
+ dataset_info_display = gr.Markdown(f"""
485
+ # SimEIT: Dataset Visualizer
486
+ **Dataset:** `{get_dataset_info()}` | **Total Samples:** {get_max_index() + 1:,}
487
+ """)
488
+
489
+ # Controls section
490
+ gr.Markdown("### Choose dataset subset, sample index, image resolution, and colormap")
491
+ with gr.Row():
492
+ with gr.Column():
493
+ subset_selector = gr.Dropdown(
494
+ choices=AVAILABLE_SUBSETS,
495
+ value=default_subset,
496
+ label="Select Dataset Subset"
497
+ )
498
+ user_input = gr.Number(
499
+ label=f"Enter an integer (0–{get_max_index()})",
500
+ precision=0
501
+ )
502
+ btn_select_n = gr.Button("Confirm Number")
503
+ btn_random = gr.Button("Generate Random Number")
504
+ with gr.Column():
505
+ image_selector = gr.Dropdown(
506
+ choices=AVAILABLE_RESOLUTIONS,
507
+ value='256',
508
+ label="Select Image Resolution"
509
+ )
510
+ colormap_dropdown = gr.Dropdown(
511
+ choices=AVAILABLE_COLORMAPS,
512
+ value='Jet',
513
+ label="Select Colormap"
514
+ )
515
+
516
+ # State for tracking indices
517
+ indices_list = gr.State(value=[])
518
+
519
+ # Visualization plots
520
+ with gr.Row(equal_height=True):
521
+ with gr.Column(scale=2):
522
+ image_plot = gr.Plot(label="Image Heatmap")
523
+ with gr.Column(scale=2):
524
+ volt_plot = gr.Plot(label="Voltage Plot")
525
+
526
+ # Status output
527
+ status_output = gr.Textbox(label="Status", interactive=False)
528
+
529
+ # Event handlers
530
+ subset_selector.change(
531
+ fn=reload_dataset,
532
+ inputs=[subset_selector, indices_list, image_selector, colormap_dropdown],
533
+ outputs=[dataset_info_display, user_input, indices_list, image_plot, volt_plot, status_output]
534
+ )
535
+
536
+ btn_random.click(
537
+ fn=generate_random_and_show,
538
+ inputs=[indices_list, image_selector, colormap_dropdown],
539
+ outputs=[user_input, indices_list, image_plot, volt_plot, status_output]
540
+ )
541
+
542
+ btn_select_n.click(
543
+ fn=select_n_and_show,
544
+ inputs=[user_input, indices_list, image_selector, colormap_dropdown],
545
+ outputs=[indices_list, image_plot, volt_plot, status_output]
546
+ )
547
+
548
+ # Allow Enter key to confirm the number
549
+ user_input.submit(
550
+ fn=select_n_and_show,
551
+ inputs=[user_input, indices_list, image_selector, colormap_dropdown],
552
+ outputs=[indices_list, image_plot, volt_plot, status_output]
553
+ )
554
+
555
+ image_selector.change(
556
+ fn=show_images,
557
+ inputs=[indices_list, image_selector, colormap_dropdown],
558
+ outputs=[image_plot, volt_plot, status_output]
559
+ )
560
+
561
+ colormap_dropdown.change(
562
+ fn=show_images,
563
+ inputs=[indices_list, image_selector, colormap_dropdown],
564
+ outputs=[image_plot, volt_plot, status_output]
565
+ )
566
+
567
+ # Load a random example at startup
568
+ demo.load(
569
+ fn=generate_random_and_show,
570
+ inputs=[indices_list, image_selector, colormap_dropdown],
571
+ outputs=[user_input, indices_list, image_plot, volt_plot, status_output]
572
+ )
573
+
574
+ # Citation section
575
+ gr.HTML("""
576
+ <!--Citation -->
577
+ <section class="section" id="Citation">
578
+ <div class="container is-max-desktop content">
579
+ <h2 class="title">Citation</h2>
580
+ <pre><code>@article{ameen2025simeit,
581
+ title={SimEIT: A Scalable Simulation Framework for Generating Large-Scale Electrical Impedance Tomography Datasets},
582
+ author={Ameen, Ayman A. and Mathis-Ullrich, Franziska and Kainz, Bernhard},
583
+ year={2025},
584
+ }</code></pre>
585
+ </div>
586
+ </section>
587
+ """)
588
+
589
+ return demo
590
+
591
+
592
+ def main():
593
+ """Main entry point for the application."""
594
+ demo = create_gradio_interface()
595
+ demo.launch(share=True)
596
+
597
+
598
+ if __name__ == "__main__":
599
+ main()
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ h5py
3
+ numpy
4
+ opencv-python
5
+ plotly
6
+ pyyaml
7
+ datasets
8
+ huggingface_hub