pad-train 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,301 @@
1
+ Metadata-Version: 2.4
2
+ Name: pad_train
3
+ Version: 1.0.0
4
+ Summary: k-NN presentation attack detection (PAD) for finger vein data, trained on real or synthetic attack samples.
5
+ Author: Philipp Fuchs
6
+ Author-email: Andreas Auer <andreas.auer03@gmail.com>, Sarah Goetz <sarahgoetz0603@gmail.com>
7
+ Project-URL: Repository, https://github.com/Kneidl18/PAD_train
8
+ Keywords: biometrics,finger vein,presentation attack detection,k-NN,synthetic data
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: Image Recognition
13
+ Classifier: Typing :: Typed
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: mahotas>=1.4.18
17
+ Requires-Dist: numpy>=1.26
18
+ Requires-Dist: pandas>=2.0
19
+ Requires-Dist: pillow>=10.0
20
+ Requires-Dist: scikit-learn>=1.4
21
+ Provides-Extra: dev
22
+ Requires-Dist: mypy>=1.10; extra == "dev"
23
+ Requires-Dist: pandas-stubs; extra == "dev"
24
+ Requires-Dist: pytest>=8.0; extra == "dev"
25
+ Requires-Dist: ruff>=0.6; extra == "dev"
26
+
27
+ # pad_train
28
+
29
+ k-NN presentation attack detection (PAD) for finger vein images. The package
30
+ answers one question: **can a PAD system that is trained on synthetic attack
31
+ samples reliably tell real attacks from bona fide samples?**
32
+
33
+ It returns APCER, BPCER and ACER as a `pandas.DataFrame`.
34
+
35
+ ## Method
36
+
37
+ Three sample types are involved:
38
+
39
+ | Type | Meaning | Source |
40
+ |------|-------------------------------------------|------------------------------|
41
+ | (1) | bona fide ("real") | `Data_FV_Spoofing_WS2025_26` |
42
+ | (2) | real presentation attack ("spoof") | `Data_FV_Spoofing_WS2025_26` |
43
+ | (3) | synthetic presentation attack (diffusion) | `AE_Res-diff`, `VAE-diff` |
44
+
45
+ Each dataset (PLUS, IDIAP, SCUT) is evaluated with a five-fold cross-validation.
46
+ The folds partition the **subjects**, so no person is in training and test at
47
+ the same time. Only type (1) samples that have a type (2) counterpart are used
48
+ (balanced sets). Two protocols run on identical folds:
49
+
50
+ | `protocol` | Training (4/5 of the subjects) | Test (remaining 1/5) |
51
+ |-------------|--------------------------------|----------------------|
52
+ | `baseline` | (1) vs. (2) | (1) vs. (2) |
53
+ | `synthetic` | (1) vs. (3) | (1) vs. (2) |
54
+
55
+ The classifier is a k-nearest-neighbour classifier on top of an exchangeable
56
+ feature extractor. The reported metrics follow ISO/IEC 30107-3:
57
+
58
+ - **APCER** – share of attacks classified as bona fide
59
+ - **BPCER** – share of bona fide samples classified as attack
60
+ - **ACER** – mean of the two
61
+
62
+ Details worth knowing:
63
+
64
+ - The synthetic samples carry no subject identity, so they cannot be split by
65
+ subject. By default each fold draws as many synthetic samples as it has bona
66
+ fide training samples (`balance_synthetic=True`), which keeps the training set
67
+ the same size as in the baseline. Synthetic samples are never part of a test set.
68
+ - "Has a counterpart" is decided per file name for PLUS and IDIAP (real and
69
+ spoof captures share their names) and per subject for SCUT.
70
+ - The metrics are computed per fold and then averaged.
71
+
72
+ ## Installation
73
+
74
+ From PyPI:
75
+
76
+ ```bash
77
+ pip install pad-train
78
+ ```
79
+
80
+ Directly from the repository, over SSH or HTTPS. The repository is private, so either way
81
+ you need access to it:
82
+
83
+ ```bash
84
+ # SSH (needs an SSH key registered with GitHub)
85
+ pip install "git+ssh://git@github.com/Kneidl18/PAD_train.git" # latest main
86
+ pip install "git+ssh://git@github.com/Kneidl18/PAD_train.git@1.0.0" # a release
87
+
88
+ # HTTPS (uses your git credential helper, or asks for user name and access token)
89
+ pip install "git+https://github.com/Kneidl18/PAD_train.git" # latest main
90
+ pip install "git+https://github.com/Kneidl18/PAD_train.git@1.0.0" # a release
91
+ ```
92
+
93
+ For local development:
94
+
95
+ ```bash
96
+ git clone git@github.com:Kneidl18/PAD_train.git
97
+ cd PAD_train
98
+ conda env create -f environment.yml # installs the package in editable mode
99
+ conda activate pad_train
100
+ ```
101
+
102
+ ## Data
103
+
104
+ The three archives (about 3 GB in total) are downloaded on demand into the
105
+ data root and removed after extraction:
106
+
107
+ ```text
108
+ <data root>/
109
+ ├── Data_FV_Spoofing_WS2025_26/ # types (1) and (2)
110
+ ├── AE_Res-diff/ # type (3), residual auto encoder
111
+ └── VAE-diff/ # type (3), variational auto encoder
112
+ ```
113
+
114
+ The data root defaults to `./data` (relative to the working directory). Change
115
+ it in Python, through the environment, or on the command line:
116
+
117
+ ```python
118
+ from pathlib import Path
119
+ import pad_train
120
+
121
+ pad_train.settings.data_root = Path("/mnt/datasets/finger_vein")
122
+ ```
123
+
124
+ ```bash
125
+ export PAD_TRAIN_DATA_ROOT=/mnt/datasets/finger_vein
126
+ pad-train --data-root /mnt/datasets/finger_vein download
127
+ ```
128
+
129
+ All paths, URLs and dataset layouts live in
130
+ [`src/pad_train/config.py`](src/pad_train/config.py).
131
+
132
+ Fetching happens in one of two ways:
133
+
134
+ - **Explicitly:** `pad-train download` (or `pad_train.download_data()`).
135
+ - **Implicitly:** `load_data()` and `evaluate()` download whatever is missing
136
+ and simply use what is already there. Pass `download=False` to forbid it.
137
+
138
+ > `pip` cannot run code at install time for wheels, so there is no
139
+ > `pip install --download-datasets`. Run `pad-train download` after installing.
140
+
141
+ ## Usage
142
+
143
+ ```python
144
+ import pad_train
145
+
146
+ results = pad_train.evaluate(extractors=["fourier", "haralick"])
147
+ print(results)
148
+ ```
149
+
150
+ ```text
151
+ dataset extractor protocol train_attack APCER BPCER ACER
152
+ PLUS fourier baseline spoof ...
153
+ PLUS fourier synthetic AE_Res ...
154
+ PLUS fourier synthetic VAE ...
155
+ ...
156
+ ```
157
+
158
+ `train_attack` names the attack samples used for training: `spoof` for the
159
+ baseline, otherwise the synthetic source. All rates are fractions in `[0, 1]`.
160
+
161
+ Useful arguments of `evaluate()`:
162
+
163
+ | Argument | Default | Meaning |
164
+ |---------------------|----------------|-------------------------------------------------|
165
+ | `datasets` | all | e.g. `["PLUS"]` |
166
+ | `extractors` | `("fourier",)` | registered names or callables |
167
+ | `synthetic` | all | e.g. `["VAE"]` |
168
+ | `protocols` | both | `"baseline"` and/or `"synthetic"` |
169
+ | `n_neighbors` | per dataset | `k` of the k-NN (PLUS 6, IDIAP 1, SCUT 1) |
170
+ | `balance_synthetic` | `True` | sub-sample the synthetic training samples |
171
+ | `per_fold` | `False` | one row per fold instead of the mean |
172
+ | `download` | `True` | fetch missing data |
173
+
174
+ Lower-level building blocks: `load_data()` returns the images of one dataset,
175
+ `evaluate_dataset()` evaluates images, `cross_validate()` evaluates precomputed
176
+ feature matrices.
177
+
178
+ The same from the command line:
179
+
180
+ ```bash
181
+ pad-train evaluate --datasets PLUS IDIAP --extractors fourier haralick --output results.csv
182
+ pad-train evaluate --help
183
+ ```
184
+
185
+ ## Feature extractors
186
+
187
+ A feature extractor is any callable that maps a grayscale image (2-D `uint8`
188
+ array) to a 1-D feature vector whose length does not depend on the image size.
189
+
190
+ | Name | Description |
191
+ |------------|--------------------------------------------------------------------------|
192
+ | `fourier` | log energy of the magnitude spectrum in radial bands, thirds per dataset |
193
+ | `haralick` | 13 Haralick texture statistics, averaged over the four directions |
194
+
195
+ ### Fourier settings
196
+
197
+ `fourier` uses only the magnitude of the spectrum; the phase is discarded. The
198
+ 30 bands are grouped into thirds (1 = low, 2 = middle, 3 = high frequencies),
199
+ and each dataset uses its own selection:
200
+
201
+ | Dataset | `fourier_thirds` | Bands |
202
+ |---------|------------------|-------|
203
+ | PLUS | `(1, 2, 3)` | 0–29 |
204
+ | IDIAP | `(1, 2)` | 0–19 |
205
+ | SCUT | `(1,)` | 0–9 |
206
+
207
+ `evaluate()` and the command line apply these automatically. To change them,
208
+ edit `fourier_thirds` in `_default_datasets()` in
209
+ [`src/pad_train/config.py`](src/pad_train/config.py), or override them at run time:
210
+
211
+ ```python
212
+ pad_train.settings.datasets["SCUT"].fourier_thirds = (1, 2)
213
+ ```
214
+
215
+ ### Adding an extractor
216
+
217
+ Adding one takes a single decorator, in this package
218
+ (`src/pad_train/features/`) or in your own code:
219
+
220
+ ```python
221
+ import numpy as np
222
+ import pad_train
223
+
224
+
225
+ @pad_train.register_extractor("mean_std")
226
+ def mean_std(image):
227
+ return np.array([image.mean(), image.std()])
228
+
229
+
230
+ # a parametrised variant of a built-in one: the 10 lowest frequency bands
231
+ pad_train.register_extractor("fourier_low", pad_train.make_fourier_extractor(bands=range(10)))
232
+
233
+ pad_train.evaluate(extractors=["fourier", "fourier_low", "mean_std"])
234
+ ```
235
+
236
+ ## Additional synthetic sources
237
+
238
+ Any folder with the layout `<path>/<dataset>/spoof/samples/*.png` can be used
239
+ as a source of type (3) samples, for example post-processed versions of the
240
+ provided ones:
241
+
242
+ ```python
243
+ from pathlib import Path
244
+ from pad_train import DataSource, evaluate, settings
245
+
246
+ settings.synthetic_sources["VAE_cleaned"] = DataSource("VAE_cleaned", Path("/data/VAE_cleaned"))
247
+ evaluate(synthetic=["VAE", "VAE_cleaned"])
248
+ ```
249
+
250
+ The dataset folder names are `PLUS_matched`, `IDIAP` and `SCUT`.
251
+
252
+ ## Development
253
+
254
+ ```bash
255
+ ruff format . # formatting
256
+ ruff check . # linting (annotations and docstrings included)
257
+ mypy # static type checking
258
+ pytest # tests; they generate their own toy data, no download needed
259
+ ```
260
+
261
+ ### Releasing
262
+
263
+ 1. Set the new `version` in `pyproject.toml`, merge it to `main`.
264
+ 2. Publish a GitHub release on `main` whose tag is exactly that version (e.g. `1.0.0`).
265
+
266
+ The [`Publish to PyPI`](.github/workflows/publish.yml) workflow then checks,
267
+ builds and uploads the package.
268
+
269
+ ## Contributing
270
+
271
+ Contributions are welcome through pull requests on
272
+ [GitHub](https://github.com/Kneidl18/PAD_train):
273
+
274
+ 1. Fork the repository, or create a branch if you have write access, and
275
+ branch off `dev`.
276
+ 2. Make your change. New feature extractors go into `src/pad_train/features/`
277
+ and need a test.
278
+ 3. Run `ruff format .`, `ruff check .`, `mypy` and `pytest`; all four must pass.
279
+ 4. Open a pull request against `dev` and describe what changed and why.
280
+
281
+ `main` is protected: it only receives changes from `dev` through a pull
282
+ request, and releases are tags on `main`. Bugs and ideas can be reported as
283
+ [issues](https://github.com/Kneidl18/PAD_train/issues).
284
+
285
+ ## Authors
286
+
287
+ - Andreas Auer (<andreas.auer03@gmail.com>)
288
+ - Sarah Goetz (<sarahgoetz0603@gmail.com>)
289
+ - Philipp Fuchs
290
+
291
+ The Haralick feature extraction (`src/pad_train/features/haralick.py`) is based
292
+ on code by Chumakov, Demir and Waclawek.
293
+
294
+ ## Acknowledgements
295
+
296
+ The datasets used by this package are provided by **Andreas Uhl** (Department
297
+ of Artificial Intelligence and Human Interfaces, University of Salzburg): the
298
+ bona fide and presentation attack samples of PLUS, IDIAP and SCUT as well as
299
+ the diffusion-based synthetic samples. The evaluation protocol follows the
300
+ tasks he set in his courses on image processing and multimedia security. Thank
301
+ you for making the data and the topic available.
@@ -0,0 +1,275 @@
1
+ # pad_train
2
+
3
+ k-NN presentation attack detection (PAD) for finger vein images. The package
4
+ answers one question: **can a PAD system that is trained on synthetic attack
5
+ samples reliably tell real attacks from bona fide samples?**
6
+
7
+ It returns APCER, BPCER and ACER as a `pandas.DataFrame`.
8
+
9
+ ## Method
10
+
11
+ Three sample types are involved:
12
+
13
+ | Type | Meaning | Source |
14
+ |------|-------------------------------------------|------------------------------|
15
+ | (1) | bona fide ("real") | `Data_FV_Spoofing_WS2025_26` |
16
+ | (2) | real presentation attack ("spoof") | `Data_FV_Spoofing_WS2025_26` |
17
+ | (3) | synthetic presentation attack (diffusion) | `AE_Res-diff`, `VAE-diff` |
18
+
19
+ Each dataset (PLUS, IDIAP, SCUT) is evaluated with a five-fold cross-validation.
20
+ The folds partition the **subjects**, so no person is in training and test at
21
+ the same time. Only type (1) samples that have a type (2) counterpart are used
22
+ (balanced sets). Two protocols run on identical folds:
23
+
24
+ | `protocol` | Training (4/5 of the subjects) | Test (remaining 1/5) |
25
+ |-------------|--------------------------------|----------------------|
26
+ | `baseline` | (1) vs. (2) | (1) vs. (2) |
27
+ | `synthetic` | (1) vs. (3) | (1) vs. (2) |
28
+
29
+ The classifier is a k-nearest-neighbour classifier on top of an exchangeable
30
+ feature extractor. The reported metrics follow ISO/IEC 30107-3:
31
+
32
+ - **APCER** – share of attacks classified as bona fide
33
+ - **BPCER** – share of bona fide samples classified as attack
34
+ - **ACER** – mean of the two
35
+
36
+ Details worth knowing:
37
+
38
+ - The synthetic samples carry no subject identity, so they cannot be split by
39
+ subject. By default each fold draws as many synthetic samples as it has bona
40
+ fide training samples (`balance_synthetic=True`), which keeps the training set
41
+ the same size as in the baseline. Synthetic samples are never part of a test set.
42
+ - "Has a counterpart" is decided per file name for PLUS and IDIAP (real and
43
+ spoof captures share their names) and per subject for SCUT.
44
+ - The metrics are computed per fold and then averaged.
45
+
46
+ ## Installation
47
+
48
+ From PyPI:
49
+
50
+ ```bash
51
+ pip install pad-train
52
+ ```
53
+
54
+ Directly from the repository, over SSH or HTTPS. The repository is private, so either way
55
+ you need access to it:
56
+
57
+ ```bash
58
+ # SSH (needs an SSH key registered with GitHub)
59
+ pip install "git+ssh://git@github.com/Kneidl18/PAD_train.git" # latest main
60
+ pip install "git+ssh://git@github.com/Kneidl18/PAD_train.git@1.0.0" # a release
61
+
62
+ # HTTPS (uses your git credential helper, or asks for user name and access token)
63
+ pip install "git+https://github.com/Kneidl18/PAD_train.git" # latest main
64
+ pip install "git+https://github.com/Kneidl18/PAD_train.git@1.0.0" # a release
65
+ ```
66
+
67
+ For local development:
68
+
69
+ ```bash
70
+ git clone git@github.com:Kneidl18/PAD_train.git
71
+ cd PAD_train
72
+ conda env create -f environment.yml # installs the package in editable mode
73
+ conda activate pad_train
74
+ ```
75
+
76
+ ## Data
77
+
78
+ The three archives (about 3 GB in total) are downloaded on demand into the
79
+ data root and removed after extraction:
80
+
81
+ ```text
82
+ <data root>/
83
+ ├── Data_FV_Spoofing_WS2025_26/ # types (1) and (2)
84
+ ├── AE_Res-diff/ # type (3), residual auto encoder
85
+ └── VAE-diff/ # type (3), variational auto encoder
86
+ ```
87
+
88
+ The data root defaults to `./data` (relative to the working directory). Change
89
+ it in Python, through the environment, or on the command line:
90
+
91
+ ```python
92
+ from pathlib import Path
93
+ import pad_train
94
+
95
+ pad_train.settings.data_root = Path("/mnt/datasets/finger_vein")
96
+ ```
97
+
98
+ ```bash
99
+ export PAD_TRAIN_DATA_ROOT=/mnt/datasets/finger_vein
100
+ pad-train --data-root /mnt/datasets/finger_vein download
101
+ ```
102
+
103
+ All paths, URLs and dataset layouts live in
104
+ [`src/pad_train/config.py`](src/pad_train/config.py).
105
+
106
+ Fetching happens in one of two ways:
107
+
108
+ - **Explicitly:** `pad-train download` (or `pad_train.download_data()`).
109
+ - **Implicitly:** `load_data()` and `evaluate()` download whatever is missing
110
+ and simply use what is already there. Pass `download=False` to forbid it.
111
+
112
+ > `pip` cannot run code at install time for wheels, so there is no
113
+ > `pip install --download-datasets`. Run `pad-train download` after installing.
114
+
115
+ ## Usage
116
+
117
+ ```python
118
+ import pad_train
119
+
120
+ results = pad_train.evaluate(extractors=["fourier", "haralick"])
121
+ print(results)
122
+ ```
123
+
124
+ ```text
125
+ dataset extractor protocol train_attack APCER BPCER ACER
126
+ PLUS fourier baseline spoof ...
127
+ PLUS fourier synthetic AE_Res ...
128
+ PLUS fourier synthetic VAE ...
129
+ ...
130
+ ```
131
+
132
+ `train_attack` names the attack samples used for training: `spoof` for the
133
+ baseline, otherwise the synthetic source. All rates are fractions in `[0, 1]`.
134
+
135
+ Useful arguments of `evaluate()`:
136
+
137
+ | Argument | Default | Meaning |
138
+ |---------------------|----------------|-------------------------------------------------|
139
+ | `datasets` | all | e.g. `["PLUS"]` |
140
+ | `extractors` | `("fourier",)` | registered names or callables |
141
+ | `synthetic` | all | e.g. `["VAE"]` |
142
+ | `protocols` | both | `"baseline"` and/or `"synthetic"` |
143
+ | `n_neighbors` | per dataset | `k` of the k-NN (PLUS 6, IDIAP 1, SCUT 1) |
144
+ | `balance_synthetic` | `True` | sub-sample the synthetic training samples |
145
+ | `per_fold` | `False` | one row per fold instead of the mean |
146
+ | `download` | `True` | fetch missing data |
147
+
148
+ Lower-level building blocks: `load_data()` returns the images of one dataset,
149
+ `evaluate_dataset()` evaluates images, `cross_validate()` evaluates precomputed
150
+ feature matrices.
151
+
152
+ The same from the command line:
153
+
154
+ ```bash
155
+ pad-train evaluate --datasets PLUS IDIAP --extractors fourier haralick --output results.csv
156
+ pad-train evaluate --help
157
+ ```
158
+
159
+ ## Feature extractors
160
+
161
+ A feature extractor is any callable that maps a grayscale image (2-D `uint8`
162
+ array) to a 1-D feature vector whose length does not depend on the image size.
163
+
164
+ | Name | Description |
165
+ |------------|--------------------------------------------------------------------------|
166
+ | `fourier` | log energy of the magnitude spectrum in radial bands, thirds per dataset |
167
+ | `haralick` | 13 Haralick texture statistics, averaged over the four directions |
168
+
169
+ ### Fourier settings
170
+
171
+ `fourier` uses only the magnitude of the spectrum; the phase is discarded. The
172
+ 30 bands are grouped into thirds (1 = low, 2 = middle, 3 = high frequencies),
173
+ and each dataset uses its own selection:
174
+
175
+ | Dataset | `fourier_thirds` | Bands |
176
+ |---------|------------------|-------|
177
+ | PLUS | `(1, 2, 3)` | 0–29 |
178
+ | IDIAP | `(1, 2)` | 0–19 |
179
+ | SCUT | `(1,)` | 0–9 |
180
+
181
+ `evaluate()` and the command line apply these automatically. To change them,
182
+ edit `fourier_thirds` in `_default_datasets()` in
183
+ [`src/pad_train/config.py`](src/pad_train/config.py), or override them at run time:
184
+
185
+ ```python
186
+ pad_train.settings.datasets["SCUT"].fourier_thirds = (1, 2)
187
+ ```
188
+
189
+ ### Adding an extractor
190
+
191
+ Adding one takes a single decorator, in this package
192
+ (`src/pad_train/features/`) or in your own code:
193
+
194
+ ```python
195
+ import numpy as np
196
+ import pad_train
197
+
198
+
199
+ @pad_train.register_extractor("mean_std")
200
+ def mean_std(image):
201
+ return np.array([image.mean(), image.std()])
202
+
203
+
204
+ # a parametrised variant of a built-in one: the 10 lowest frequency bands
205
+ pad_train.register_extractor("fourier_low", pad_train.make_fourier_extractor(bands=range(10)))
206
+
207
+ pad_train.evaluate(extractors=["fourier", "fourier_low", "mean_std"])
208
+ ```
209
+
210
+ ## Additional synthetic sources
211
+
212
+ Any folder with the layout `<path>/<dataset>/spoof/samples/*.png` can be used
213
+ as a source of type (3) samples, for example post-processed versions of the
214
+ provided ones:
215
+
216
+ ```python
217
+ from pathlib import Path
218
+ from pad_train import DataSource, evaluate, settings
219
+
220
+ settings.synthetic_sources["VAE_cleaned"] = DataSource("VAE_cleaned", Path("/data/VAE_cleaned"))
221
+ evaluate(synthetic=["VAE", "VAE_cleaned"])
222
+ ```
223
+
224
+ The dataset folder names are `PLUS_matched`, `IDIAP` and `SCUT`.
225
+
226
+ ## Development
227
+
228
+ ```bash
229
+ ruff format . # formatting
230
+ ruff check . # linting (annotations and docstrings included)
231
+ mypy # static type checking
232
+ pytest # tests; they generate their own toy data, no download needed
233
+ ```
234
+
235
+ ### Releasing
236
+
237
+ 1. Set the new `version` in `pyproject.toml`, merge it to `main`.
238
+ 2. Publish a GitHub release on `main` whose tag is exactly that version (e.g. `1.0.0`).
239
+
240
+ The [`Publish to PyPI`](.github/workflows/publish.yml) workflow then checks,
241
+ builds and uploads the package.
242
+
243
+ ## Contributing
244
+
245
+ Contributions are welcome through pull requests on
246
+ [GitHub](https://github.com/Kneidl18/PAD_train):
247
+
248
+ 1. Fork the repository, or create a branch if you have write access, and
249
+ branch off `dev`.
250
+ 2. Make your change. New feature extractors go into `src/pad_train/features/`
251
+ and need a test.
252
+ 3. Run `ruff format .`, `ruff check .`, `mypy` and `pytest`; all four must pass.
253
+ 4. Open a pull request against `dev` and describe what changed and why.
254
+
255
+ `main` is protected: it only receives changes from `dev` through a pull
256
+ request, and releases are tags on `main`. Bugs and ideas can be reported as
257
+ [issues](https://github.com/Kneidl18/PAD_train/issues).
258
+
259
+ ## Authors
260
+
261
+ - Andreas Auer (<andreas.auer03@gmail.com>)
262
+ - Sarah Goetz (<sarahgoetz0603@gmail.com>)
263
+ - Philipp Fuchs
264
+
265
+ The Haralick feature extraction (`src/pad_train/features/haralick.py`) is based
266
+ on code by Chumakov, Demir and Waclawek.
267
+
268
+ ## Acknowledgements
269
+
270
+ The datasets used by this package are provided by **Andreas Uhl** (Department
271
+ of Artificial Intelligence and Human Interfaces, University of Salzburg): the
272
+ bona fide and presentation attack samples of PLUS, IDIAP and SCUT as well as
273
+ the diffusion-based synthetic samples. The evaluation protocol follows the
274
+ tasks he set in his courses on image processing and multimedia security. Thank
275
+ you for making the data and the topic available.
@@ -0,0 +1,74 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pad_train"
7
+ version = "1.0.0"
8
+ description = "k-NN presentation attack detection (PAD) for finger vein data, trained on real or synthetic attack samples."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ authors = [
12
+ { name = "Andreas Auer", email = "andreas.auer03@gmail.com" },
13
+ { name = "Sarah Goetz", email = "sarahgoetz0603@gmail.com"},
14
+ { name = "Philipp Fuchs"},
15
+ ]
16
+ keywords = ["biometrics", "finger vein", "presentation attack detection", "k-NN", "synthetic data"]
17
+ classifiers = [
18
+ "Development Status :: 5 - Production/Stable",
19
+ "Intended Audience :: Science/Research",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Scientific/Engineering :: Image Recognition",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = [
25
+ "mahotas>=1.4.18",
26
+ "numpy>=1.26",
27
+ "pandas>=2.0",
28
+ "pillow>=10.0",
29
+ "scikit-learn>=1.4",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "mypy>=1.10",
35
+ "pandas-stubs",
36
+ "pytest>=8.0",
37
+ "ruff>=0.6",
38
+ ]
39
+
40
+ [project.scripts]
41
+ pad-train = "pad_train.cli:main"
42
+
43
+ [project.urls]
44
+ Repository = "https://github.com/Kneidl18/PAD_train"
45
+
46
+ [tool.setuptools.packages.find]
47
+ where = ["src"]
48
+
49
+ [tool.setuptools.package-data]
50
+ pad_train = ["py.typed"]
51
+
52
+ [tool.ruff]
53
+ line-length = 100
54
+ src = ["src", "tests"]
55
+
56
+ [tool.ruff.lint]
57
+ select = ["E", "F", "W", "I", "N", "UP", "B", "ANN", "D", "SIM", "PTH", "RUF"]
58
+
59
+ [tool.ruff.lint.pydocstyle]
60
+ convention = "google"
61
+
62
+ [tool.ruff.lint.per-file-ignores]
63
+ "tests/*" = ["D103"]
64
+
65
+ [tool.mypy]
66
+ strict = true
67
+ files = ["src"]
68
+
69
+ [[tool.mypy.overrides]]
70
+ module = ["mahotas.*", "sklearn.*"]
71
+ ignore_missing_imports = true
72
+
73
+ [tool.pytest.ini_options]
74
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+