English
tinymyo
emg
bio-signals
foundation-model
MatteoFasulo commited on
Commit
d9b6f8e
·
unverified ·
1 Parent(s): 7be20ea

refactor: improve documentation, db5 preprocessing, and removed unused downstream tasks

Browse files
Files changed (3) hide show
  1. scripts/README.md +0 -2
  2. scripts/avespeech.py +0 -199
  3. scripts/db5.py +5 -5
scripts/README.md CHANGED
@@ -35,5 +35,3 @@ This guide provides commands to process raw EMG data into HDF5 format using slid
35
  | **UCI EMG** | Gesture | 1000 (5s) | 250 | `python scripts/uci.py --data_dir $DATA_PATH/UCI_EMG/EMG_data_for_gestures-master/ --save_dir $DATA_PATH/UCI_EMG/EMG_data_for_gestures-master/h5_5sec/ --seq_len 1000 --stride 250` |
36
  | **NinaPro DB8** | Regression | 200 (0.1s) | 200 | `python scripts/db8.py --data_dir $DATA_PATH/ninapro/DB8/ --save_dir $DATA_PATH/ninapro/DB8/h5_100/ --seq_len 200 --stride 200` |
37
  | **NinaPro DB8** | Regression | 1000 (0.5s) | 1000 | `python scripts/db8.py --data_dir $DATA_PATH/ninapro/DB8/ --save_dir $DATA_PATH/ninapro/DB8/h5_500/ --seq_len 1000 --stride 1000` |
38
- | **AVE-Speech** | Speech | 2000 (2s) | N/A | `python scripts/avespeech.py --data_dir $DATA_PATH/AVE-Speech/ --save_dir $DATA_PATH/AVE-Speech/h5/` |
39
-
 
35
  | **UCI EMG** | Gesture | 1000 (5s) | 250 | `python scripts/uci.py --data_dir $DATA_PATH/UCI_EMG/EMG_data_for_gestures-master/ --save_dir $DATA_PATH/UCI_EMG/EMG_data_for_gestures-master/h5_5sec/ --seq_len 1000 --stride 250` |
36
  | **NinaPro DB8** | Regression | 200 (0.1s) | 200 | `python scripts/db8.py --data_dir $DATA_PATH/ninapro/DB8/ --save_dir $DATA_PATH/ninapro/DB8/h5_100/ --seq_len 200 --stride 200` |
37
  | **NinaPro DB8** | Regression | 1000 (0.5s) | 1000 | `python scripts/db8.py --data_dir $DATA_PATH/ninapro/DB8/ --save_dir $DATA_PATH/ninapro/DB8/h5_500/ --seq_len 1000 --stride 1000` |
 
 
scripts/avespeech.py DELETED
@@ -1,199 +0,0 @@
1
- import os
2
-
3
- import librosa
4
- import h5py
5
- import numpy as np
6
- import scipy.io as sio
7
- import scipy.signal as signal
8
- from pathlib import Path
9
- from typing import Tuple, List
10
- import re
11
- import argparse
12
- from huggingface_hub import snapshot_download
13
- from joblib import Parallel, delayed
14
- from tqdm import tqdm
15
-
16
-
17
- def download_emg_only(save_dir: str):
18
- repo_id = "MML-Group/AVE-Speech"
19
-
20
- allow_patterns = [
21
- "Train/EMG/**",
22
- "Val/EMG/**",
23
- "Test/EMG/**",
24
- "phonetic_transcription.xlsx",
25
- ]
26
-
27
- snapshot_download(
28
- repo_id=repo_id,
29
- repo_type="dataset",
30
- local_dir=save_dir,
31
- allow_patterns=allow_patterns,
32
- )
33
-
34
-
35
- def unzip_file(zip_path: str, extract_to: str) -> None:
36
- import zipfile
37
- with zipfile.ZipFile(zip_path, "r") as zip_ref:
38
- zip_ref.extractall(extract_to)
39
-
40
-
41
- def unzip_all_subjects(base_dir: str):
42
- base_path = Path(base_dir)
43
- pattern = re.compile(r"subject_(\d+)\.zip")
44
-
45
- for zip_file in base_path.rglob("*.zip"):
46
- match = pattern.search(zip_file.name)
47
- if not match:
48
- continue
49
-
50
- subject_id = match.group(1)
51
- extract_dir = zip_file.parent / f"subject_{subject_id}"
52
- extract_dir.mkdir(exist_ok=True)
53
-
54
- print(f"Unzipping {zip_file} -> {extract_dir}")
55
- unzip_file(str(zip_file), str(extract_dir))
56
-
57
- zip_file.unlink()
58
-
59
-
60
- def filter(raw_data):
61
- fs=1000
62
- b1, a1 = signal.iirnotch(50, 30, fs)
63
- b2, a2 = signal.iirnotch(150, 30, fs)
64
- b3, a3 = signal.iirnotch(250, 30, fs)
65
- b4, a4 = signal.iirnotch(350, 30, fs)
66
- b5, a5 = signal.butter(4, [10/(fs/2), 400/(fs/2)], 'bandpass')
67
-
68
- x = signal.filtfilt(b1, a1, raw_data, axis=1)
69
- x = signal.filtfilt(b2, a2, x, axis=1)
70
- x = signal.filtfilt(b3, a3, x, axis=1)
71
- x = signal.filtfilt(b4, a4, x, axis=1)
72
- x = signal.filtfilt(b5, a5, x, axis=1)
73
- return x
74
-
75
-
76
- def zscore(x: np.ndarray) -> np.ndarray:
77
- mu = x.mean(axis=1, keepdims=True)
78
- std = x.std(axis=1, keepdims=True) + 1e-8
79
- return (x - mu) / std
80
-
81
- def EMG_MFSC(x):
82
- x = x[:,250:,:]
83
- n_mels = 36
84
- sr = 1000
85
- channel_list = []
86
- for j in range(x.shape[-1]):
87
- mfsc_x = np.zeros((x.shape[0], 36, n_mels))
88
- for i in range(x.shape[0]):
89
- # norm_x = x[i, :, j]/np.max(abs(x[i, :, j]))
90
- norm_x = np.asfortranarray(x[i, :, j])
91
- tmp = librosa.feature.melspectrogram(y=norm_x, sr=sr, n_mels=n_mels, n_fft=200, hop_length=50)
92
- tmp = librosa.power_to_db(tmp).T
93
- mfsc_x[i, :, :] = tmp
94
-
95
- mfsc_x = np.expand_dims(mfsc_x, axis=-1)
96
- channel_list.append(mfsc_x)
97
- data_x = np.concatenate(channel_list, axis=-1)
98
- mu = np.mean(data_x)
99
- std = np.std(data_x)
100
- data_x = (data_x - mu) / std
101
- data_x = data_x.transpose(0,3,1,2) # Shape: (N, C, F, T)
102
- return data_x
103
-
104
-
105
- def process_subject(subject_path: Path, use_mfsc: bool) -> Tuple[List[np.ndarray], List[int]]:
106
- X_list, y_list = [], []
107
-
108
- for mat_file in subject_path.rglob("*.mat"):
109
- emg = sio.loadmat(mat_file) # [2000, 6]
110
- emg = np.expand_dims(emg["data"], axis=0) # Shape: (1, 2000, 6)
111
- emg = filter(emg)
112
-
113
- if use_mfsc:
114
- emg = EMG_MFSC(emg)
115
- else:
116
- emg = zscore(emg)
117
- emg = emg.squeeze(0) # Shape: (2000, 6)
118
- emg = emg.transpose(1, 0) # Shape: (6, 2000) [C, T]
119
-
120
- label = int(mat_file.stem)
121
-
122
- X_list.append(emg)
123
- y_list.append(label)
124
-
125
- return X_list, y_list
126
-
127
-
128
- def process_dataset(
129
- data_dir: str,
130
- save_dir: str,
131
- use_mfsc: bool,
132
- n_jobs: int,
133
- ):
134
- splits = ["Train", "Val", "Test"]
135
- os.makedirs(save_dir, exist_ok=True)
136
-
137
- for split in splits:
138
- split_path = Path(data_dir) / split / "EMG"
139
- if not split_path.exists():
140
- continue
141
-
142
- print(f"\nProcessing {split}...")
143
-
144
- subjects = [p for p in split_path.iterdir() if p.is_dir()]
145
-
146
- # Parallel process subjects
147
- results = Parallel(n_jobs=n_jobs, backend="loky")(
148
- delayed(process_subject)(subj, use_mfsc) for subj in tqdm(subjects)
149
- )
150
-
151
- X_all, y_all = [], []
152
- for X_list, y_list in results:
153
- if X_list is None:
154
- continue
155
- X_all.extend(X_list)
156
- y_all.extend(y_list)
157
-
158
- if len(X_all) == 0:
159
- continue
160
-
161
- X = np.array(X_all, dtype=np.float32)
162
- y = np.array(y_all, dtype=np.int64)
163
-
164
- # Save to HDF5
165
- with h5py.File(os.path.join(save_dir, f"{split.lower()}.h5"), "w") as f:
166
- f.create_dataset("data", data=X)
167
- f.create_dataset("label", data=y)
168
-
169
- print(f"{split}: Processed {len(X)} samples.")
170
- print(f"Saved shapes -> X: {X.shape}, y: {y.shape}")
171
-
172
-
173
- if __name__ == "__main__":
174
- parser = argparse.ArgumentParser()
175
- parser.add_argument("--data_dir", type=str, required=True)
176
- parser.add_argument("--save_dir", type=str, required=True)
177
- parser.add_argument("--download", action="store_true")
178
- parser.add_argument("--use_mfsc", action="store_true")
179
- parser.add_argument("--n_jobs", type=int, default=-1)
180
-
181
- args = parser.parse_args()
182
-
183
- os.makedirs(args.data_dir, exist_ok=True)
184
- os.makedirs(args.save_dir, exist_ok=True)
185
-
186
- if args.download:
187
- print("Downloading dataset...")
188
- download_emg_only(args.data_dir)
189
-
190
- print("Unzipping dataset...")
191
- unzip_all_subjects(args.data_dir)
192
-
193
- print("Processing dataset...")
194
- process_dataset(
195
- data_dir=args.data_dir,
196
- save_dir=args.save_dir,
197
- use_mfsc=args.use_mfsc,
198
- n_jobs=args.n_jobs
199
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
scripts/db5.py CHANGED
@@ -259,6 +259,9 @@ def main():
259
  all_lbls = {"train": [], "val": [], "test": []}
260
 
261
  for subj in sorted(os.listdir(data_dir)):
 
 
 
262
  subj_path = os.path.join(data_dir, subj)
263
  if not os.path.isdir(subj_path):
264
  continue
@@ -282,10 +285,7 @@ def main():
282
  emg_filt = notch_filter(emg_filt, 50, 30, fs=fs)
283
 
284
  # z-score
285
- mu = emg_filt.mean(axis=0)
286
- sd = emg_filt.std(axis=0, ddof=1)
287
- sd[sd == 0] = 1.0
288
- emg_z = (emg_filt - mu) / sd
289
 
290
  # segment
291
  segs, lbls, reps = process_emg_features(
@@ -327,7 +327,7 @@ def main():
327
 
328
  # print stats
329
  for split, (shape, dist) in stats.items():
330
- print(f"\n{split} → X={shape}, label distribution:")
331
  for lab, count in dist.items():
332
  print(f" label {lab}: {count} samples")
333
 
 
259
  all_lbls = {"train": [], "val": [], "test": []}
260
 
261
  for subj in sorted(os.listdir(data_dir)):
262
+ if subj.startswith("h5"):
263
+ # Skip folders created by this script when run multiple times
264
+ continue
265
  subj_path = os.path.join(data_dir, subj)
266
  if not os.path.isdir(subj_path):
267
  continue
 
285
  emg_filt = notch_filter(emg_filt, 50, 30, fs=fs)
286
 
287
  # z-score
288
+ emg_z = (emg_filt - emg_filt.mean(axis=0)) / emg_filt.std(axis=0, ddof=1)
 
 
 
289
 
290
  # segment
291
  segs, lbls, reps = process_emg_features(
 
327
 
328
  # print stats
329
  for split, (shape, dist) in stats.items():
330
+ print(f"\n{split} → X={shape} [N, C, T]\nlabel distribution:")
331
  for lab, count in dist.items():
332
  print(f" label {lab}: {count} samples")
333