Photoplethysmogram-based Real-Time Cognitive Load Assessment Using Multi-Feature Fusion Model
- macOS (Recommended)
- Python 2.7
- Pip
- Virtualenv
On Unix, Linux, BSD, macOS, and Cygwin:
git clone https://github.com/iRB-Lab/PPG.git
cd PPG
virtualenv venv
source venv/bin/activate
pip install -r requirements.txtOn Unix, Linux, BSD, macOS, and Cygwin:
./scripts/process_data.sh
./scripts/classify.shpython segment.pypython preprocess.pypython extract.pypython split.pypython classify.py- Location:
data/raw/ - Filename format:
<participant>-<label>.txt
109
110
109
109
...
- Location:
data/segmented/ - Filename format:
<participant>.json
{
"<label>": {
"sample_rate": <value>,
"signal": [ ... ]
},
...
}- Location:
data/preprocessed/ - Filename format:
<participant>.json
{
"<label>": {
"sample_rate": <value>,
"single_waveforms": [
[ ... ],
...
]
},
...
}- Location:
data/extracted/ - Filename format:
<participant>.json
{
"<label>": {
"sample_rate": <value>,
"ppg45": [
[ ... ],
...
],
"svri": [ ... ]
},
...
}- Location:
data/splited/ - Filename format:
<participant>.json
{
"train": {
"<label>": [
{
"ppg45": [
[ ... ],
...
],
"svri": [ ... ]
},
...
],
...
},
"test": { ... }
}| Sensor | Feature | Dimension |
|---|---|---|
| PPG finger clip | PPG-45 (39 time-domain, 9 frequency-domain) | 45 |
| Stress-induced vascular response index (sVRI) | 1 |
| # | Feature | Description |
|---|---|---|
| 1 | x |
Systolic peak |
| 2 | y |
Diastolic peak |
| 3 | z |
Dicrotic notch |
| 4 | tpi |
Pulse interval |
| 5 | y/x |
Augmentation index |
| 6 | (x-y)/x |
Relative augmentation index |
| 7 | z/x |
|
| 8 | (y-z)/x |
|
| 9 | t1 |
Systolic peak time |
| 10 | t2 |
Diastolic peak time |
| 11 | t3 |
Dicrotic notch time |
| 12 | ∆T |
Time between systolic and diastolic peaks |
| 13 | w |
Full width at half systolic peak |
| 14 | A2/A1 |
Inflection point area ratio |
| 15 | t1/x |
Systolic peak rising slope |
| 16 | y/(tpi-t3) |
Diastolic peak falling slope |
| 17 | t1/tpi |
|
| 18 | t2/tpi |
|
| 19 | t3/tpi |
|
| 20 | ∆T/tpi |
|
| 21 | ta1 |
|
| 22 | tb1 |
|
| 23 | te1 |
|
| 24 | tf1 |
|
| 25 | b2/a2 |
|
| 26 | e2/a2 |
|
| 27 | (b2+e2)/a2 |
|
| 28 | ta2 |
|
| 29 | tb2 |
|
| 30 | ta1/tpi |
|
| 31 | tb1/tpi |
|
| 32 | te1/tpi |
|
| 33 | tf1/tpi |
|
| 34 | ta2/tpi |
|
| 35 | tb2/tpi |
|
| 36 | (ta1+ta2)/tpi |
|
| 37 | (tb1+tb2)/tpi |
|
| 38 | (te1+t2)/tpi |
|
| 39 | (tf1+t3)/tpi |
|
| 40 | fbase |
Fundamental component frequency |
| 41 | |sbase| |
Fundamental component magnitude |
| 42 | f2 |
2nd harmonic frequency |
| 43 | |s2| |
2nd harmonic magnitude |
| 44 | f3 |
3rd harmonic frequency |
| 45 | |s3| |
3rd harmonic magnitude |
Excerpt from ppg/__init__.py:
BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))Excerpt from ppg/params.py:
MINIMUM_PULSE_CYCLE = 0.5
MAXIMUM_PULSE_CYCLE = 1.2
PPG_SAMPLE_RATE = 200
PPG_FIR_FILTER_TAP_NUM = 200
PPG_FILTER_CUTOFF = [0.5, 5.0]
PPG_SYSTOLIC_PEAK_DETECTION_THRESHOLD_COEFFICIENT = 0.5
TRAINING_DATA_RATIO = 0.75extrema = find_extrema(signal)smoothed_ppg_signal = smooth_ppg_signal(
signal,
sample_rate=PPG_SAMPLE_RATE,
numtaps=PPG_FIR_FILTER_TAP_NUM,
cutoff=PPG_FILTER_CUTOFF
)result = validate_ppg_single_waveform(single_waveform, sample_rate=PPG_SAMPLE_RATE)single_waveforms = extract_ppg_single_waveform(signal, sample_rate=PPG_SAMPLE_RATE)extract_ppg45(single_waveform, sample_rate=PPG_SAMPLE_RATE)svri = extract_svri(single_waveform)train_data, test_data = split_data_set(data, ratio)train_features, train_labels, test_features, test_labels = get_feature_set(data, label_set, feature_type_set)classifier = logistic_regression_classifier(features, labels)classifier = support_vector_classifier(features, labels)classifier = gaussian_naive_bayes_classifier(features, labels)classifier = decision_tree_classifier(features, labels)classifier = random_forest_classifier(features, labels)classifier = adaboost_classifier(features, labels)classifier = gradient_boosting_classifier(features, labels)classifier = voting_classifier(estimators, features, labels)make_dirs_for_file(pathname)boolean = exist_file(pathname, overwrite=False, display_info=True)text_data = load_text(pathname, display_info=True)json_data = load_json(pathname, display_info=True)dump_json(data, pathname, overwrite=False, display_info=True)classifier_object = load_model(pathname, display_info=True)dump_model(model, pathname, overwrite=False, display_info=True)export_csv(data, fieldnames, pathname, overwrite=False, display_info=True)datetime = parse_iso_time_string(timestamp)set_matplotlib_backend(backend=None)plot(args, backend=None)semilogy(args, backend=None)├── data/
│ ├── raw/
│ │ ├── <participant>-<session_id>-<block_id>-<task_level>.json
│ │ └── ...
│ ├── segmented/
│ │ ├── <participant>.json
│ │ └── ...
│ ├── preprocessed/
│ │ ├── <participant>.json
│ │ └── ...
│ └── extracted/
│ ├── <participant>.json
│ └── ...
├── models/
│ └── ...
├── results/
│ └── ...
├── ppg/
│ ├── __init__.py
│ ├── params.py
│ ├── signal.py
│ ├── feature.py
│ ├── learn.py
│ └── utils.py
├── scripts/
│ ├── process_data.sh
│ └── classify.sh
├── segment.py
├── preprocess.py
├── extract.py
├── split.py
├── classify.py
├── requirements.txt
├── README.md
├── LICENSE
└── .gitignore