A deep learning and ensemble classification pipeline for detecting Parkinson's disease (PD) from digitizer handwriting recordings. The project implements and extends the dynamically enhanced static handwriting approach introduced by Diaz et al. (2019), replacing the original VGG16 backbone with EfficientNetB3 and expanding the image representation set from three to five channels.
Parkinson's disease produces measurable changes in handwriting: slower strokes, greater within-stroke variability, and characteristic in-air pauses that healthy writers do not exhibit. Rather than extracting hand-crafted kinematic features from the raw signal, this pipeline converts handwriting recordings into synthetic images that embed velocity and in-air movement information — making the temporal structure of the handwriting visible to a convolutional neural network.
The core idea, due to Diaz et al., is to plot individual sample points instead of connecting them with lines. Because the digitizer samples at 200 Hz, slow writing produces densely clustered dark points while fast writing produces sparser, lighter regions — encoding velocity implicitly through spatial density. On-surface samples are rendered in black; in-air samples in gray.
This project is built on the method described in:
Diaz, M., Ferrer, M. A., Impedovo, D., Pirlo, G., & Vessio, G. (2019). Dynamically enhanced static handwriting representation for Parkinson's disease detection. Pattern Recognition Letters, 125, 755–762. https://doi.org/10.1016/j.patrec.2019.07.008
Key differences from the original paper:
| Component | Diaz et al. (2019) | This project |
|---|---|---|
| CNN backbone | VGG16 (150×150 px, 8192-d) | EfficientNetB3 (300×300 px, 1536-d) |
| Image representations | 3 (raw, residual, edge) | 5 (+ velocity map, additional branch) |
| Combined feature dim. | 24,576-d per task | 7,680-d per task |
| Classifiers | SVM-lin, SVM-RBF, RF, ET, ADA | 8 classifiers including LR, GradBoost, HistGB |
| Task selection | Best-5 by accuracy | Best-5 by composite TaskScore |
| Ensemble | Majority vote | Weighted vote (top-3 classifiers per task) |
The pipeline uses the PaHaW (Parkinson's Disease Handwriting Database), a publicly available corpus recorded with a Wacom Intuos 4M digitizing tablet at 200 samples per second.
- Subjects: 72 participants after excluding incomplete records — 36 PD patients and 36 age- and gender-matched healthy controls (HC)
- Tasks: 8 standardized handwriting tasks per subject (spiral drawing, repeated letters/bigrams/trigrams, Czech words, and a full sentence)
- Signal format: Per-sample rows with 7 columns — y, x, timestamp, button_status (1 = on surface, 0 = in air), azimuth, altitude, pressure
- Total files: 576
.svcfiles (72 subjects × 8 tasks)
PaHaW is described in detail in Drotár et al. (2016) and is available from the original authors upon request.
Raw .svc files
│
▼
Coordinate normalization (x shifted, y centered; global scale from training fold)
│
▼
5 image representations per subject per task
├── Raw enhanced image (point-plot, on-surface=black, in-air=gray)
├── Median residual image (3×3 median filter difference, highlights texture)
├── Edge image (Sobel magnitude, captures stroke boundaries)
├── Velocity image (luminance ∝ instantaneous speed)
└── Fifth representation branch
│
▼
EfficientNetB3 feature extraction (frozen ImageNet weights)
└── 1,536-d per representation → 7,680-d combined per subject per task
│
▼
Nested feature selection (LinearSVC ranking, k ∈ {50,100,200,500,1000,2000},
chosen by 5-fold inner CV on training split)
│
▼
8 classifiers trained per task
(LogReg, SVM-lin, SVM-RBF, RF, ET, AdaBoost, GradBoost, HistGB)
│
▼
Top-3 classifiers per task combined by weighted vote (weights ∝ TaskScore)
│
▼
Top-5 tasks selected by TaskScore = 0.6 × BalancedAcc + 0.4 × AUC
│
▼
Final majority vote across 5 task-level predictions → PD / HC
All evaluation is performed via stratified 10-fold cross-validation with feature selection nested inside each fold. Out-of-fold (OOF) predictions cover all 72 subjects without any test-set leakage.
| Metric | Value |
|---|---|
| Accuracy | 77.78% |
| Sensitivity (PD recall) | 75.00% |
| Specificity (HC recall) | 80.56% |
| Balanced Accuracy | 77.78% |
| AUC (ROC) | 0.8009 |
| Ensemble | Tasks | Accuracy | Sensitivity | Specificity | AUC |
|---|---|---|---|---|---|
| Top-3 tasks | l_letter, le_bigram, porovnat | 70.83% | 75.00% | 66.67% | 0.7824 |
| Top-5 tasks | l_letter, le_bigram, porovnat, sentence, spiral | 77.78% | 75.00% | 80.56% | 0.8009 |
| All 8 tasks | All | 69.44% | 61.11% | 77.78% | 0.7338 |
The repeated letter l and the bigram le tasks achieve the highest individual TaskScores (0.73 and 0.72) and are the only tasks to individually meet the sensitivity threshold of 0.65. The sentence and spiral tasks contribute complementary discriminative signal at the ensemble level despite not meeting the per-task sensitivity threshold individually.
tensorflow>=2.15
scikit-learn
scikit-image
scipy
numpy
pandas
openpyxl
Pillow
matplotlib
seaborn
Install with:
pip install scikit-learn pandas numpy matplotlib seaborn tensorflow Pillow \
scikit-image openpyxl scipyThe notebook was developed and tested on Google Colab with GPU acceleration (TensorFlow 2.19.0). A GPU is strongly recommended — feature extraction with EfficientNetB3 across all 576 images takes approximately 19 minutes in EXACT_FOLD_SCALE=True mode on a Colab T4.
-
Obtain the PaHaW dataset and place it so the directory structure matches:
PaHaW/ ├── PaHaW_files/ │ └── corpus_PaHaW.xlsx └── PaHaW_public/ ├── 00001/ │ ├── 00001__1_1.svc │ └── ... └── ... -
Update the path config in Section 2 of the notebook. The four path entries in
CONFIGare marked with inline comments — change them to point to your local copies:CONFIG = { 'PAHAW_ROOT': '/path/to/PaHaW', # ← root dataset folder 'SUBJECTS_DIR': '/path/to/PaHaW/PaHaW_public', # ← .svc files 'CORPUS_XLSX': '/path/to/PaHaW/PaHaW_files/corpus_PaHaW.xlsx', # ← metadata spreadsheet 'OUTPUT_DIR': '/path/to/output_directory', # ← where cached features and results are saved ... }
The Google Drive mount block (Section 1) is wrapped in a
try/except ImportErrorand will silently skip if you are not running in Colab — no edits needed for local use. -
Run all cells in order. The notebook is organized into 10 numbered sections with markdown headers:
- §1 Setup — dependencies, imports, optional Drive mount
- §2 Configuration — all hyperparameters and paths
- §3 Data Loading — corpus metadata and SVC files
- §4 Image Generation — enhanced images and 5 representations
- §5 Feature Extraction — EfficientNetB3, feature caching
- §6 Feature Selection — nested LinearSVC ranking
- §7 Classifier Training — 8 classifiers × 10-fold CV
- §8 Results — per-task tables and ensemble construction
- §9 Sanity Checks — automated assertions
- §10 Final Summary — comparison table and JSON export
-
Results are saved to
OUTPUT_DIR/results_summary.json.
Key parameters in the CONFIG dictionary:
| Parameter | Default | Description |
|---|---|---|
CNN_INPUT_SIZE |
(300, 300) |
Input resolution for EfficientNetB3 |
CNN_FEATURES |
1536 |
Feature dimension per representation |
N_REPRESENTATIONS |
5 |
Number of image representations per task |
K_CANDIDATES |
[50,100,200,500,1000,2000] |
Feature selection search space |
OUTER_FOLDS |
10 |
Outer cross-validation folds |
TOP_N_TASKS |
5 |
Number of tasks in final ensemble |
N_ESTIMATORS |
500 |
Trees for RF, ET, AdaBoost |
EXACT_FOLD_SCALE |
True |
Recompute coordinate scale per fold (slower but methodologically pure) |
RANDOM_STATE |
42 |
Global random seed |
- Small cohort: 72 subjects is a limited sample for deep learning generalization. The frozen EfficientNetB3 backbone mitigates overfitting, but results may not transfer to larger or more heterogeneous populations.
- No backbone fine-tuning: EfficientNetB3 weights are entirely frozen. Fine-tuning late convolutional layers could improve feature discriminability at increased overfitting risk.
- High feature dimensionality: The 7,680-dimensional feature space substantially exceeds the 72-subject cohort size. Nested feature selection reduces but does not eliminate the risk of fold-to-fold instability in which features are selected.
- Task 4 and 5 near chance: The
les_trigramandlektorkatasks consistently perform near 50% accuracy, suggesting these tasks may not carry discriminating information under the current representation scheme.
Diaz, M., Ferrer, M. A., Impedovo, D., Pirlo, G., & Vessio, G. (2019). Dynamically enhanced static handwriting representation for Parkinson's disease detection. Pattern Recognition Letters, 125, 755–762.
Drotár, P., Mekyska, J., Rektorová, I., Masarová, L., Smékal, Z., & Faundez-Zanuy, M. (2016). Evaluation of handwriting kinematics and pressure for differential diagnosis of Parkinson's disease. Artificial Intelligence in Medicine, 67, 39–46.
Drotár, P., Mekyska, J., Rektorová, I., Masarová, L., Smékal, Z., & Faundez-Zanuy, M. (2015). Decision support framework for Parkinson's disease based on novel handwriting markers. IEEE Transactions on Neural Systems and Rehabilitation Engineering, 23(3), 508–516.
Moetesum, M., Siddiqi, I., Vincent, N., & Cloppet, F. (2019). Assessing visual attributes of handwriting for prediction of neurological disorders — a case study on Parkinson's disease. Pattern Recognition Letters, 121, 19–27.
Simonyan, K., & Zisserman, A. (2014). Very deep convolutional networks for large-scale image recognition. arXiv preprint arXiv:1409.1556.
This project is released for academic and research use. The PaHaW dataset is subject to its own terms of use; please cite the original dataset paper (Drotár et al., 2016) when using it.