Update emg2pose with s3 AWS CLI download
Browse files- scripts/README.md +1 -1
- scripts/emg2pose.py +96 -1
- scripts/requirements.txt +2 -1
scripts/README.md
CHANGED
|
@@ -36,4 +36,4 @@ This guide provides commands to process raw EMG data into HDF5 format using slid
|
|
| 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 |
|
| 39 |
-
>Note: For DB5, we used the `--data-augment` flag to augment the training data by a factor of 3 (see `--augment-factor` in `scripts/db5.py`).
|
|
|
|
| 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 |
|
| 39 |
+
>Note: For DB5, we used the `--data-augment` flag to augment the training data by a factor of 3 (see `--augment-factor` in `scripts/db5.py`).
|
scripts/emg2pose.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
| 1 |
import os
|
| 2 |
import gc
|
|
|
|
|
|
|
|
|
|
| 3 |
from pathlib import Path
|
| 4 |
from typing import Tuple, List, Optional, Union, Dict, Any
|
| 5 |
|
|
@@ -11,6 +14,80 @@ from joblib import Parallel, delayed
|
|
| 11 |
from scipy.signal import iirnotch
|
| 12 |
from tqdm import tqdm
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
def sequence_to_seconds(seq_len: int, fs: float) -> float:
|
| 15 |
"""Converts a sequence length in samples to time in seconds.
|
| 16 |
|
|
@@ -135,6 +212,13 @@ def main():
|
|
| 135 |
args = argparse.ArgumentParser(description="Process EMG data from DB5.")
|
| 136 |
args.add_argument("--data_dir", type=str)
|
| 137 |
args.add_argument("--save_dir", type=str)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
args.add_argument(
|
| 139 |
"--seq_len", type=int, help="Size of the window in samples for segmentation."
|
| 140 |
)
|
|
@@ -165,6 +249,17 @@ def main():
|
|
| 165 |
save_dir = args.save_dir
|
| 166 |
os.makedirs(save_dir, exist_ok=True)
|
| 167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
fs = 2000.0 # original sampling rate
|
| 169 |
window_size, stride = args.seq_len, args.stride
|
| 170 |
|
|
@@ -230,4 +325,4 @@ def main():
|
|
| 230 |
|
| 231 |
|
| 232 |
if __name__ == "__main__":
|
| 233 |
-
main()
|
|
|
|
| 1 |
import os
|
| 2 |
import gc
|
| 3 |
+
import subprocess
|
| 4 |
+
import tarfile
|
| 5 |
+
import tempfile
|
| 6 |
from pathlib import Path
|
| 7 |
from typing import Tuple, List, Optional, Union, Dict, Any
|
| 8 |
|
|
|
|
| 14 |
from scipy.signal import iirnotch
|
| 15 |
from tqdm import tqdm
|
| 16 |
|
| 17 |
+
|
| 18 |
+
EMG2POSE_S3_URI = "s3://fb-ctrl-oss/emg2pose/emg2pose_dataset.tar"
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def download_emg2pose(
|
| 22 |
+
data_dir: str,
|
| 23 |
+
archive_path: Optional[str] = None,
|
| 24 |
+
max_concurrent_requests: int = 64,
|
| 25 |
+
multipart_chunksize: str = "128MB",
|
| 26 |
+
io_chunksize: str = "64MB",
|
| 27 |
+
max_queue_size: int = 1000,
|
| 28 |
+
keep_archive: bool = False,
|
| 29 |
+
) -> None:
|
| 30 |
+
"""Download and extract the public EMG2Pose S3 archive.
|
| 31 |
+
|
| 32 |
+
AWS CLI S3 tuning is kept in a temporary config file so this script does
|
| 33 |
+
not modify the user's global AWS configuration. The public object is
|
| 34 |
+
downloaded without credentials and can be resumed by AWS CLI when a
|
| 35 |
+
partial file is present.
|
| 36 |
+
"""
|
| 37 |
+
data_path = Path(data_dir).expanduser().resolve()
|
| 38 |
+
data_path.mkdir(parents=True, exist_ok=True)
|
| 39 |
+
archive = (
|
| 40 |
+
Path(archive_path).expanduser().resolve()
|
| 41 |
+
if archive_path
|
| 42 |
+
else data_path.parent / "emg2pose_dataset.tar"
|
| 43 |
+
)
|
| 44 |
+
archive.parent.mkdir(parents=True, exist_ok=True)
|
| 45 |
+
|
| 46 |
+
aws_config = (
|
| 47 |
+
"[default]\n"
|
| 48 |
+
"s3 =\n"
|
| 49 |
+
f" max_concurrent_requests = {max_concurrent_requests}\n"
|
| 50 |
+
f" max_queue_size = {max_queue_size}\n"
|
| 51 |
+
f" multipart_chunksize = {multipart_chunksize}\n"
|
| 52 |
+
f" io_chunksize = {io_chunksize}\n"
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
print(f"Downloading EMG2Pose to {archive}")
|
| 56 |
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".aws-config", delete=False) as config_file:
|
| 57 |
+
config_file.write(aws_config)
|
| 58 |
+
config_path = config_file.name
|
| 59 |
+
|
| 60 |
+
env = os.environ.copy()
|
| 61 |
+
env["AWS_CONFIG_FILE"] = config_path
|
| 62 |
+
try:
|
| 63 |
+
subprocess.run(
|
| 64 |
+
[
|
| 65 |
+
"aws",
|
| 66 |
+
"s3",
|
| 67 |
+
"cp",
|
| 68 |
+
EMG2POSE_S3_URI,
|
| 69 |
+
str(archive),
|
| 70 |
+
"--no-sign-request",
|
| 71 |
+
],
|
| 72 |
+
check=True,
|
| 73 |
+
env=env,
|
| 74 |
+
)
|
| 75 |
+
finally:
|
| 76 |
+
os.unlink(config_path)
|
| 77 |
+
|
| 78 |
+
print(f"Extracting {archive} into {data_path}")
|
| 79 |
+
with tarfile.open(archive, "r") as tar:
|
| 80 |
+
root = data_path.resolve()
|
| 81 |
+
for member in tar.getmembers():
|
| 82 |
+
target = (data_path / member.name).resolve()
|
| 83 |
+
if target != root and root not in target.parents:
|
| 84 |
+
raise RuntimeError(f"Unsafe archive member: {member.name}")
|
| 85 |
+
tar.extractall(data_path)
|
| 86 |
+
|
| 87 |
+
if not keep_archive:
|
| 88 |
+
archive.unlink()
|
| 89 |
+
print("Removed downloaded archive")
|
| 90 |
+
|
| 91 |
def sequence_to_seconds(seq_len: int, fs: float) -> float:
|
| 92 |
"""Converts a sequence length in samples to time in seconds.
|
| 93 |
|
|
|
|
| 212 |
args = argparse.ArgumentParser(description="Process EMG data from DB5.")
|
| 213 |
args.add_argument("--data_dir", type=str)
|
| 214 |
args.add_argument("--save_dir", type=str)
|
| 215 |
+
args.add_argument("--download_data", action="store_true")
|
| 216 |
+
args.add_argument("--archive_path", type=str, default=None)
|
| 217 |
+
args.add_argument("--keep_archive", action="store_true")
|
| 218 |
+
args.add_argument("--aws_max_concurrent_requests", type=int, default=64)
|
| 219 |
+
args.add_argument("--aws_multipart_chunksize", type=str, default="128MB")
|
| 220 |
+
args.add_argument("--aws_io_chunksize", type=str, default="64MB")
|
| 221 |
+
args.add_argument("--aws_max_queue_size", type=int, default=1000)
|
| 222 |
args.add_argument(
|
| 223 |
"--seq_len", type=int, help="Size of the window in samples for segmentation."
|
| 224 |
)
|
|
|
|
| 249 |
save_dir = args.save_dir
|
| 250 |
os.makedirs(save_dir, exist_ok=True)
|
| 251 |
|
| 252 |
+
if args.download_data:
|
| 253 |
+
download_emg2pose(
|
| 254 |
+
data_dir=data_dir,
|
| 255 |
+
archive_path=args.archive_path,
|
| 256 |
+
max_concurrent_requests=args.aws_max_concurrent_requests,
|
| 257 |
+
multipart_chunksize=args.aws_multipart_chunksize,
|
| 258 |
+
io_chunksize=args.aws_io_chunksize,
|
| 259 |
+
max_queue_size=args.aws_max_queue_size,
|
| 260 |
+
keep_archive=args.keep_archive,
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
fs = 2000.0 # original sampling rate
|
| 264 |
window_size, stride = args.seq_len, args.stride
|
| 265 |
|
|
|
|
| 325 |
|
| 326 |
|
| 327 |
if __name__ == "__main__":
|
| 328 |
+
main()
|
scripts/requirements.txt
CHANGED
|
@@ -3,4 +3,5 @@ numpy
|
|
| 3 |
scipy
|
| 4 |
joblib
|
| 5 |
tqdm
|
| 6 |
-
pandas
|
|
|
|
|
|
| 3 |
scipy
|
| 4 |
joblib
|
| 5 |
tqdm
|
| 6 |
+
pandas
|
| 7 |
+
awscli
|