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