Spaces:
Running
Running
Add multi-file upload support with automatic WFDB pair detection
Browse files
app.py
CHANGED
|
@@ -11,6 +11,7 @@ import numpy as np
|
|
| 11 |
import plotly.graph_objects as go
|
| 12 |
from huggingface_hub import hf_hub_download
|
| 13 |
import tempfile
|
|
|
|
| 14 |
from pathlib import Path
|
| 15 |
|
| 16 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
@@ -310,7 +311,7 @@ except Exception as e:
|
|
| 310 |
traceback.print_exc()
|
| 311 |
|
| 312 |
def predict_ecg(file_obj):
|
| 313 |
-
"""Main prediction function"""
|
| 314 |
|
| 315 |
if model is None:
|
| 316 |
return (
|
|
@@ -320,11 +321,47 @@ def predict_ecg(file_obj):
|
|
| 320 |
)
|
| 321 |
|
| 322 |
try:
|
| 323 |
-
#
|
| 324 |
-
|
| 325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
else:
|
| 327 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
|
| 329 |
# Load ECG using universal loader
|
| 330 |
print(f"Loading file: {file_path}")
|
|
@@ -494,13 +531,15 @@ with gr.Blocks(
|
|
| 494 |
gr.Markdown("""
|
| 495 |
### Upload Your ECG
|
| 496 |
|
|
|
|
|
|
|
| 497 |
**Clinical & Standardized Formats:**
|
| 498 |
- `.dcm` β DICOM (medical imaging, PACS systems)
|
| 499 |
- `.scp` β SCP-ECG (European interoperability standard)
|
| 500 |
- `.xml` β HL7 aECG / FDA XML (clinical trials, regulatory)
|
| 501 |
|
| 502 |
-
**
|
| 503 |
-
- `.hea` + `.dat` β WFDB (MIT-BIH, PhysioNet) **
|
| 504 |
- `.edf` β European Data Format (multi-channel biosignals)
|
| 505 |
|
| 506 |
**Generic / Export Formats:**
|
|
@@ -509,6 +548,7 @@ with gr.Blocks(
|
|
| 509 |
- `.mat` β MATLAB format
|
| 510 |
- `.h5 / .hdf5` β HDF5 (efficient large-scale datasets)
|
| 511 |
- `.raw / .bin` β Binary ECG data
|
|
|
|
| 512 |
|
| 513 |
**Architecture Auto-Conversion:**
|
| 514 |
- Multi-lead (12 leads): Used directly
|
|
@@ -519,14 +559,15 @@ with gr.Blocks(
|
|
| 519 |
|
| 520 |
---
|
| 521 |
|
| 522 |
-
**WFDB
|
| 523 |
""")
|
| 524 |
|
| 525 |
file_input = gr.File(
|
| 526 |
-
label="ECG File",
|
|
|
|
| 527 |
file_types=[".csv", ".txt", ".tsv", ".npy", ".hea", ".dat",
|
| 528 |
".dcm", ".mat", ".h5", ".hdf5", ".edf", ".xml",
|
| 529 |
-
".raw", ".bin", ".bat", ".ecg"],
|
| 530 |
type="filepath"
|
| 531 |
)
|
| 532 |
|
|
|
|
| 11 |
import plotly.graph_objects as go
|
| 12 |
from huggingface_hub import hf_hub_download
|
| 13 |
import tempfile
|
| 14 |
+
import shutil
|
| 15 |
from pathlib import Path
|
| 16 |
|
| 17 |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
|
|
| 311 |
traceback.print_exc()
|
| 312 |
|
| 313 |
def predict_ecg(file_obj):
|
| 314 |
+
"""Main prediction function - handles single or multiple files"""
|
| 315 |
|
| 316 |
if model is None:
|
| 317 |
return (
|
|
|
|
| 321 |
)
|
| 322 |
|
| 323 |
try:
|
| 324 |
+
# Handle multiple file uploads (list) or single file
|
| 325 |
+
file_path = None
|
| 326 |
+
|
| 327 |
+
if isinstance(file_obj, list):
|
| 328 |
+
# Multiple files uploaded
|
| 329 |
+
if not file_obj:
|
| 330 |
+
return ("**Error**: No files uploaded", None)
|
| 331 |
+
|
| 332 |
+
# Look for WFDB pairs (.hea + .dat)
|
| 333 |
+
files = [str(f.name) if hasattr(f, 'name') else str(f) for f in file_obj]
|
| 334 |
+
hea_files = [f for f in files if f.lower().endswith('.hea')]
|
| 335 |
+
dat_files = [f for f in files if f.lower().endswith('.dat')]
|
| 336 |
+
|
| 337 |
+
if hea_files and dat_files:
|
| 338 |
+
# WFDB pair detected - both files present
|
| 339 |
+
# Copy .dat file next to .hea file for WFDB to work
|
| 340 |
+
import shutil
|
| 341 |
+
hea_path = hea_files[0]
|
| 342 |
+
dat_path = dat_files[0]
|
| 343 |
+
|
| 344 |
+
# Get directory of .hea file
|
| 345 |
+
hea_dir = Path(hea_path).parent
|
| 346 |
+
dat_filename = Path(dat_path).name
|
| 347 |
+
target_dat_path = hea_dir / dat_filename
|
| 348 |
+
|
| 349 |
+
# Copy .dat file to same directory as .hea if not already there
|
| 350 |
+
if str(target_dat_path) != dat_path:
|
| 351 |
+
shutil.copy(dat_path, target_dat_path)
|
| 352 |
+
|
| 353 |
+
file_path = hea_path
|
| 354 |
+
print(f"WFDB pair detected: {hea_path} + {dat_path}")
|
| 355 |
+
else:
|
| 356 |
+
# No WFDB pair, use first file
|
| 357 |
+
file_path = str(file_obj[0].name) if hasattr(file_obj[0], 'name') else str(file_obj[0])
|
| 358 |
+
print(f"Multiple files uploaded, using first: {file_path}")
|
| 359 |
else:
|
| 360 |
+
# Single file (backward compatible)
|
| 361 |
+
if isinstance(file_obj, str):
|
| 362 |
+
file_path = file_obj
|
| 363 |
+
else:
|
| 364 |
+
file_path = file_obj.name if hasattr(file_obj, 'name') else str(file_obj)
|
| 365 |
|
| 366 |
# Load ECG using universal loader
|
| 367 |
print(f"Loading file: {file_path}")
|
|
|
|
| 531 |
gr.Markdown("""
|
| 532 |
### Upload Your ECG
|
| 533 |
|
| 534 |
+
**β
Multi-file upload supported!** Upload multiple files at once, especially for WFDB pairs.
|
| 535 |
+
|
| 536 |
**Clinical & Standardized Formats:**
|
| 537 |
- `.dcm` β DICOM (medical imaging, PACS systems)
|
| 538 |
- `.scp` β SCP-ECG (European interoperability standard)
|
| 539 |
- `.xml` β HL7 aECG / FDA XML (clinical trials, regulatory)
|
| 540 |
|
| 541 |
+
**Research & PhysioNet Formats:**
|
| 542 |
+
- `.hea` + `.dat` β WFDB (MIT-BIH, PhysioNet) **Upload both files together**
|
| 543 |
- `.edf` β European Data Format (multi-channel biosignals)
|
| 544 |
|
| 545 |
**Generic / Export Formats:**
|
|
|
|
| 548 |
- `.mat` β MATLAB format
|
| 549 |
- `.h5 / .hdf5` β HDF5 (efficient large-scale datasets)
|
| 550 |
- `.raw / .bin` β Binary ECG data
|
| 551 |
+
- `.zip` β Archive with multiple files
|
| 552 |
|
| 553 |
**Architecture Auto-Conversion:**
|
| 554 |
- Multi-lead (12 leads): Used directly
|
|
|
|
| 559 |
|
| 560 |
---
|
| 561 |
|
| 562 |
+
**π‘ WFDB Tip:** Upload both `.hea` and `.dat` files together in one go. The system will automatically detect the pair and process them correctly!
|
| 563 |
""")
|
| 564 |
|
| 565 |
file_input = gr.File(
|
| 566 |
+
label="ECG File(s)",
|
| 567 |
+
file_count="multiple",
|
| 568 |
file_types=[".csv", ".txt", ".tsv", ".npy", ".hea", ".dat",
|
| 569 |
".dcm", ".mat", ".h5", ".hdf5", ".edf", ".xml",
|
| 570 |
+
".raw", ".bin", ".bat", ".ecg", ".zip"],
|
| 571 |
type="filepath"
|
| 572 |
)
|
| 573 |
|