├── .gitignore ├── 01_install.bat ├── 02_gather.bat ├── 03_trainLiveness.bat ├── 04_runLiveness.bat ├── 05_webcam.bat ├── LICENSE ├── README.md ├── dataset ├── ezgif-1-085534fa4973.gif ├── fake │ └── git.keep └── real │ └── git.keep ├── face_detector ├── deploy.prototxt └── res10_300x300_ssd_iter_140000.caffemodel ├── gather_examples.py ├── le.pickle ├── liveness_demo.py ├── pyimagesearch ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ └── livenessnet.cpython-36.pyc └── livenessnet.py ├── requirements.txt ├── train_liveness.py ├── videos └── git.keep └── webcam.py /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # macOS 3 | ################################################################################ 4 | 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | ################################################################################ 34 | # Windows 35 | ################################################################################ 36 | 37 | # Windows thumbnail cache files 38 | Thumbs.db 39 | Thumbs.db:encryptable 40 | ehthumbs.db 41 | ehthumbs_vista.db 42 | 43 | # Dump file 44 | *.stackdump 45 | 46 | # Folder config file 47 | [Dd]esktop.ini 48 | 49 | # Recycle Bin used on file shares 50 | $RECYCLE.BIN/ 51 | 52 | # Windows Installer files 53 | *.cab 54 | *.msi 55 | *.msix 56 | *.msm 57 | *.msp 58 | 59 | # Windows shortcuts 60 | *.lnk 61 | 62 | ################################################################################ 63 | # Python 64 | ################################################################################ 65 | 66 | # Byte-compiled / optimized / DLL files 67 | __pycache__/ 68 | *.py[cod] 69 | *$py.class 70 | 71 | # C extensions 72 | *.so 73 | 74 | # Distribution / packaging 75 | .Python 76 | build/ 77 | develop-eggs/ 78 | dist/ 79 | downloads/ 80 | eggs/ 81 | .eggs/ 82 | lib/ 83 | lib64/ 84 | parts/ 85 | sdist/ 86 | var/ 87 | wheels/ 88 | pip-wheel-metadata/ 89 | share/python-wheels/ 90 | *.egg-info/ 91 | .installed.cfg 92 | *.egg 93 | MANIFEST 94 | 95 | # PyInstaller 96 | # Usually these files are written by a python script from a template 97 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 98 | *.manifest 99 | *.spec 100 | 101 | # Installer logs 102 | pip-log.txt 103 | pip-delete-this-directory.txt 104 | 105 | # Unit test / coverage reports 106 | htmlcov/ 107 | .tox/ 108 | .nox/ 109 | .coverage 110 | .coverage.* 111 | .cache 112 | nosetests.xml 113 | coverage.xml 114 | *.cover 115 | .hypothesis/ 116 | .pytest_cache/ 117 | 118 | # Translations 119 | *.mo 120 | *.pot 121 | 122 | # Django stuff: 123 | *.log 124 | local_settings.py 125 | db.sqlite3 126 | db.sqlite3-journal 127 | 128 | # Flask stuff: 129 | instance/ 130 | .webassets-cache 131 | 132 | # Scrapy stuff: 133 | .scrapy 134 | 135 | # Sphinx documentation 136 | docs/_build/ 137 | 138 | # PyBuilder 139 | target/ 140 | 141 | # Jupyter Notebook 142 | .ipynb_checkpoints 143 | 144 | # IPython 145 | profile_default/ 146 | ipython_config.py 147 | 148 | # pyenv 149 | .python-version 150 | 151 | # pipenv 152 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 153 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 154 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 155 | # install all needed dependencies. 156 | #Pipfile.lock 157 | 158 | # celery beat schedule file 159 | celerybeat-schedule 160 | 161 | # SageMath parsed files 162 | *.sage.py 163 | 164 | # Environments 165 | .env 166 | .venv 167 | env/ 168 | venv/ 169 | ENV/ 170 | env.bak/ 171 | venv.bak/ 172 | 173 | # Spyder project settings 174 | .spyderproject 175 | .spyproject 176 | 177 | # Rope project settings 178 | .ropeproject 179 | 180 | # mkdocs documentation 181 | /site 182 | 183 | # mypy 184 | .mypy_cache/ 185 | .dmypy.json 186 | dmypy.json 187 | 188 | # Pyre type checker 189 | .pyre/ 190 | 191 | ################################################################################ 192 | # torchface 193 | ################################################################################ 194 | 195 | .vscode/ 196 | liveness.model/* 197 | *.pb 198 | *.png 199 | *.jpg 200 | *.jpeg 201 | *.mp4 -------------------------------------------------------------------------------- /01_install.bat: -------------------------------------------------------------------------------- 1 | call conda.bat activate 2 | call conda create -n liveness python=3.8 3 | call conda activate liveness 4 | call pip install -r requirements.txt 5 | PAUSE -------------------------------------------------------------------------------- /02_gather.bat: -------------------------------------------------------------------------------- 1 | call conda.bat activate 2 | call conda activate liveness 3 | python gather_examples.py -i ./videos/fake.mp4 -o ./dataset/fake -d ./face_detector -c 0.9 -s 1 -f 0 4 | python gather_examples.py -i ./videos/real.mp4 -o ./dataset/real -d ./face_detector -c 0.9 -s 1 -f 0 5 | PAUSE -------------------------------------------------------------------------------- /03_trainLiveness.bat: -------------------------------------------------------------------------------- 1 | call conda.bat activate 2 | call conda activate liveness 3 | python train_liveness.py -d ./dataset -m liveness.model -l le.pickle 4 | PAUSE -------------------------------------------------------------------------------- /04_runLiveness.bat: -------------------------------------------------------------------------------- 1 | call conda.bat activate 2 | call conda activate liveness 3 | python liveness_demo.py -m liveness.model -l le.pickle -d ./face_detector -c 0.5 4 | PAUSE -------------------------------------------------------------------------------- /05_webcam.bat: -------------------------------------------------------------------------------- 1 | call conda.bat activate 2 | call conda activate liveness 3 | python webcam.py -m liveness.model -l le.pickle -d ./face_detector -c 0.5 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 PyImageSearch.com 2 | 3 | SIMPLE VERSION 4 | Feel free to use this code for your own projects, whether they are 5 | purely educational, for fun, or for profit. THE EXCEPTION BEING if 6 | you are developing a course, book, or other educational product. 7 | Under *NO CIRCUMSTANCE* may you use this code for your own paid 8 | educational or self-promotional ventures without written consent 9 | from Adrian Rosebrock and PyImageSearch.com. 10 | 11 | LONGER, FORMAL VERSION 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files 14 | (the "Software"), to deal in the Software without restriction, 15 | including without limitation the rights to use, copy, modify, merge, 16 | publish, distribute, sublicense, and/or sell copies of the Software, 17 | and to permit persons to whom the Software is furnished to do so, 18 | subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | Notwithstanding the foregoing, you may not use, copy, modify, merge, 24 | publish, distribute, sublicense, create a derivative work, and/or 25 | sell copies of the Software in any work that is designed, intended, 26 | or marketed for pedagogical or instructional purposes related to 27 | programming, coding, application development, or information 28 | technology. Permission for such use, copying, modification, and 29 | merger, publication, distribution, sub-licensing, creation of 30 | derivative works, or sale is expressly withheld. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 33 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 34 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 35 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 36 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 37 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 38 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 39 | SOFTWARE. 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Liveness Detection 2 | 3 | * This code trains and implements via video from the [pyimagesearch liveness detection blog](https://www.pyimagesearch.com/2019/03/11/liveness-detection-with-opencv) 4 | 5 | * We modified the blog's original shallow CNN model to Resnet50 that can achieve better accuracy 6 | 7 |

8 | 9 |

10 |
11 |
12 | 13 | ## Get this code: 14 | ``` 15 | git clone https://github.com/joytsay/livenessDetection.git 16 | cd livenessDetection 17 | ``` 18 | 19 | ## Pre-Trained Model and Train/Test Videos: 20 | Download from [here](https://drive.google.com/drive/folders/1Uj49JwLSAY4Q4v6UVMNF0u9hobGrJoWC?usp=sharing) and put in root folder (`/livenessDetection`) 21 | 22 | ## Setup Environment: 23 | ### Tested on Windows 10 [mini-conda](https://docs.conda.io/en/latest/miniconda.html) environment via 24 | 25 | [Miniconda3-latest-Windows-x86_64.exe](https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe) 26 | 27 | ### Tested on MacOS M2 [mini-forge](https://github.com/conda-forge/miniforge) environment via 28 | `brew install miniforge` 29 | 30 | ## Install Code: 31 | ### Option 1: Auto run bat files 32 | run 01_XXX.bat files (01~05) sequentially: 33 | ``` 34 | 01_install.bat 35 | 02_gather.bat 36 | 03_trainLiveness.bat 37 | 04_runLiveness.bat 38 | 05_webcam.bat 39 | ``` 40 | ### Option 2: Cmd Line 41 | ``` 42 | conda create -n liveness python=3.8 43 | conda activate liveness 44 | pip install -r requirements.txt 45 | ``` 46 | 47 | ``` 48 | # data pre-process 49 | python gather_examples.py -i ./videos/fake.mp4 -o ./dataset/fake -d ./face_detector -c 0.9 -s 1 -f 0 50 | python gather_examples.py -i ./videos/real.mp4 -o ./dataset/real -d ./face_detector -c 0.9 -s 1 -f 0 51 | python gather_examples.py -i ./videos/mask.mp4 -o ./dataset/mask -d ./face_detector -c 0.9 -s 1 -f 0 52 | 53 | # train model 54 | python train_liveness.py -d ./dataset -m liveness.model -l le.pickle 55 | 56 | # run liveness model on test video 57 | python liveness_demo.py -m liveness.model -l le.pickle -d ./face_detector -c 0.5 58 | press "q" to quit 59 | 60 | # run liveness model on web cam 61 | python webcam.py -m liveness.model -l le.pickle -d ./face_detector -c 0.5 62 | 63 | ``` 64 | 65 | 66 | 67 | ## Reference 68 | The following link is the original pyimagesearch [liveness-detection-with-opencv example code](https://www.pyimagesearch.com/2019/03/11/liveness-detection-with-opencv) 69 | 70 | ### Find this project useful ? :heart: 71 | * Support it by clicking the :star: button on the upper right of this page. :v: 72 | 73 | ### Credits 74 | * The example has been taken from pyimagesearch liveness-detection-with-opencv example and modified model to Resnet50 75 | 76 | ### License 77 | Copyright (C) 2020 Adrian Rosebrock, [PyImageSearch](https://www.pyimagesearch.com/2019/03/11/liveness-detection-with-opencv/), accessed on March 11, 2019 78 | 79 | ### Contributing to livenessDetection 80 | Just make pull request. Thank you! 81 | -------------------------------------------------------------------------------- /dataset/ezgif-1-085534fa4973.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joytsay/livenessDetection/900a3fc3c1c23090857ae26fa2ae3720d9ec2f96/dataset/ezgif-1-085534fa4973.gif -------------------------------------------------------------------------------- /dataset/fake/git.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joytsay/livenessDetection/900a3fc3c1c23090857ae26fa2ae3720d9ec2f96/dataset/fake/git.keep -------------------------------------------------------------------------------- /dataset/real/git.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joytsay/livenessDetection/900a3fc3c1c23090857ae26fa2ae3720d9ec2f96/dataset/real/git.keep -------------------------------------------------------------------------------- /face_detector/deploy.prototxt: -------------------------------------------------------------------------------- 1 | input: "data" 2 | input_shape { 3 | dim: 1 4 | dim: 3 5 | dim: 300 6 | dim: 300 7 | } 8 | 9 | layer { 10 | name: "data_bn" 11 | type: "BatchNorm" 12 | bottom: "data" 13 | top: "data_bn" 14 | param { 15 | lr_mult: 0.0 16 | } 17 | param { 18 | lr_mult: 0.0 19 | } 20 | param { 21 | lr_mult: 0.0 22 | } 23 | } 24 | layer { 25 | name: "data_scale" 26 | type: "Scale" 27 | bottom: "data_bn" 28 | top: "data_bn" 29 | param { 30 | lr_mult: 1.0 31 | decay_mult: 1.0 32 | } 33 | param { 34 | lr_mult: 2.0 35 | decay_mult: 1.0 36 | } 37 | scale_param { 38 | bias_term: true 39 | } 40 | } 41 | layer { 42 | name: "conv1_h" 43 | type: "Convolution" 44 | bottom: "data_bn" 45 | top: "conv1_h" 46 | param { 47 | lr_mult: 1.0 48 | decay_mult: 1.0 49 | } 50 | param { 51 | lr_mult: 2.0 52 | decay_mult: 1.0 53 | } 54 | convolution_param { 55 | num_output: 32 56 | pad: 3 57 | kernel_size: 7 58 | stride: 2 59 | weight_filler { 60 | type: "msra" 61 | variance_norm: FAN_OUT 62 | } 63 | bias_filler { 64 | type: "constant" 65 | value: 0.0 66 | } 67 | } 68 | } 69 | layer { 70 | name: "conv1_bn_h" 71 | type: "BatchNorm" 72 | bottom: "conv1_h" 73 | top: "conv1_h" 74 | param { 75 | lr_mult: 0.0 76 | } 77 | param { 78 | lr_mult: 0.0 79 | } 80 | param { 81 | lr_mult: 0.0 82 | } 83 | } 84 | layer { 85 | name: "conv1_scale_h" 86 | type: "Scale" 87 | bottom: "conv1_h" 88 | top: "conv1_h" 89 | param { 90 | lr_mult: 1.0 91 | decay_mult: 1.0 92 | } 93 | param { 94 | lr_mult: 2.0 95 | decay_mult: 1.0 96 | } 97 | scale_param { 98 | bias_term: true 99 | } 100 | } 101 | layer { 102 | name: "conv1_relu" 103 | type: "ReLU" 104 | bottom: "conv1_h" 105 | top: "conv1_h" 106 | } 107 | layer { 108 | name: "conv1_pool" 109 | type: "Pooling" 110 | bottom: "conv1_h" 111 | top: "conv1_pool" 112 | pooling_param { 113 | kernel_size: 3 114 | stride: 2 115 | } 116 | } 117 | layer { 118 | name: "layer_64_1_conv1_h" 119 | type: "Convolution" 120 | bottom: "conv1_pool" 121 | top: "layer_64_1_conv1_h" 122 | param { 123 | lr_mult: 1.0 124 | decay_mult: 1.0 125 | } 126 | convolution_param { 127 | num_output: 32 128 | bias_term: false 129 | pad: 1 130 | kernel_size: 3 131 | stride: 1 132 | weight_filler { 133 | type: "msra" 134 | } 135 | bias_filler { 136 | type: "constant" 137 | value: 0.0 138 | } 139 | } 140 | } 141 | layer { 142 | name: "layer_64_1_bn2_h" 143 | type: "BatchNorm" 144 | bottom: "layer_64_1_conv1_h" 145 | top: "layer_64_1_conv1_h" 146 | param { 147 | lr_mult: 0.0 148 | } 149 | param { 150 | lr_mult: 0.0 151 | } 152 | param { 153 | lr_mult: 0.0 154 | } 155 | } 156 | layer { 157 | name: "layer_64_1_scale2_h" 158 | type: "Scale" 159 | bottom: "layer_64_1_conv1_h" 160 | top: "layer_64_1_conv1_h" 161 | param { 162 | lr_mult: 1.0 163 | decay_mult: 1.0 164 | } 165 | param { 166 | lr_mult: 2.0 167 | decay_mult: 1.0 168 | } 169 | scale_param { 170 | bias_term: true 171 | } 172 | } 173 | layer { 174 | name: "layer_64_1_relu2" 175 | type: "ReLU" 176 | bottom: "layer_64_1_conv1_h" 177 | top: "layer_64_1_conv1_h" 178 | } 179 | layer { 180 | name: "layer_64_1_conv2_h" 181 | type: "Convolution" 182 | bottom: "layer_64_1_conv1_h" 183 | top: "layer_64_1_conv2_h" 184 | param { 185 | lr_mult: 1.0 186 | decay_mult: 1.0 187 | } 188 | convolution_param { 189 | num_output: 32 190 | bias_term: false 191 | pad: 1 192 | kernel_size: 3 193 | stride: 1 194 | weight_filler { 195 | type: "msra" 196 | } 197 | bias_filler { 198 | type: "constant" 199 | value: 0.0 200 | } 201 | } 202 | } 203 | layer { 204 | name: "layer_64_1_sum" 205 | type: "Eltwise" 206 | bottom: "layer_64_1_conv2_h" 207 | bottom: "conv1_pool" 208 | top: "layer_64_1_sum" 209 | } 210 | layer { 211 | name: "layer_128_1_bn1_h" 212 | type: "BatchNorm" 213 | bottom: "layer_64_1_sum" 214 | top: "layer_128_1_bn1_h" 215 | param { 216 | lr_mult: 0.0 217 | } 218 | param { 219 | lr_mult: 0.0 220 | } 221 | param { 222 | lr_mult: 0.0 223 | } 224 | } 225 | layer { 226 | name: "layer_128_1_scale1_h" 227 | type: "Scale" 228 | bottom: "layer_128_1_bn1_h" 229 | top: "layer_128_1_bn1_h" 230 | param { 231 | lr_mult: 1.0 232 | decay_mult: 1.0 233 | } 234 | param { 235 | lr_mult: 2.0 236 | decay_mult: 1.0 237 | } 238 | scale_param { 239 | bias_term: true 240 | } 241 | } 242 | layer { 243 | name: "layer_128_1_relu1" 244 | type: "ReLU" 245 | bottom: "layer_128_1_bn1_h" 246 | top: "layer_128_1_bn1_h" 247 | } 248 | layer { 249 | name: "layer_128_1_conv1_h" 250 | type: "Convolution" 251 | bottom: "layer_128_1_bn1_h" 252 | top: "layer_128_1_conv1_h" 253 | param { 254 | lr_mult: 1.0 255 | decay_mult: 1.0 256 | } 257 | convolution_param { 258 | num_output: 128 259 | bias_term: false 260 | pad: 1 261 | kernel_size: 3 262 | stride: 2 263 | weight_filler { 264 | type: "msra" 265 | } 266 | bias_filler { 267 | type: "constant" 268 | value: 0.0 269 | } 270 | } 271 | } 272 | layer { 273 | name: "layer_128_1_bn2" 274 | type: "BatchNorm" 275 | bottom: "layer_128_1_conv1_h" 276 | top: "layer_128_1_conv1_h" 277 | param { 278 | lr_mult: 0.0 279 | } 280 | param { 281 | lr_mult: 0.0 282 | } 283 | param { 284 | lr_mult: 0.0 285 | } 286 | } 287 | layer { 288 | name: "layer_128_1_scale2" 289 | type: "Scale" 290 | bottom: "layer_128_1_conv1_h" 291 | top: "layer_128_1_conv1_h" 292 | param { 293 | lr_mult: 1.0 294 | decay_mult: 1.0 295 | } 296 | param { 297 | lr_mult: 2.0 298 | decay_mult: 1.0 299 | } 300 | scale_param { 301 | bias_term: true 302 | } 303 | } 304 | layer { 305 | name: "layer_128_1_relu2" 306 | type: "ReLU" 307 | bottom: "layer_128_1_conv1_h" 308 | top: "layer_128_1_conv1_h" 309 | } 310 | layer { 311 | name: "layer_128_1_conv2" 312 | type: "Convolution" 313 | bottom: "layer_128_1_conv1_h" 314 | top: "layer_128_1_conv2" 315 | param { 316 | lr_mult: 1.0 317 | decay_mult: 1.0 318 | } 319 | convolution_param { 320 | num_output: 128 321 | bias_term: false 322 | pad: 1 323 | kernel_size: 3 324 | stride: 1 325 | weight_filler { 326 | type: "msra" 327 | } 328 | bias_filler { 329 | type: "constant" 330 | value: 0.0 331 | } 332 | } 333 | } 334 | layer { 335 | name: "layer_128_1_conv_expand_h" 336 | type: "Convolution" 337 | bottom: "layer_128_1_bn1_h" 338 | top: "layer_128_1_conv_expand_h" 339 | param { 340 | lr_mult: 1.0 341 | decay_mult: 1.0 342 | } 343 | convolution_param { 344 | num_output: 128 345 | bias_term: false 346 | pad: 0 347 | kernel_size: 1 348 | stride: 2 349 | weight_filler { 350 | type: "msra" 351 | } 352 | bias_filler { 353 | type: "constant" 354 | value: 0.0 355 | } 356 | } 357 | } 358 | layer { 359 | name: "layer_128_1_sum" 360 | type: "Eltwise" 361 | bottom: "layer_128_1_conv2" 362 | bottom: "layer_128_1_conv_expand_h" 363 | top: "layer_128_1_sum" 364 | } 365 | layer { 366 | name: "layer_256_1_bn1" 367 | type: "BatchNorm" 368 | bottom: "layer_128_1_sum" 369 | top: "layer_256_1_bn1" 370 | param { 371 | lr_mult: 0.0 372 | } 373 | param { 374 | lr_mult: 0.0 375 | } 376 | param { 377 | lr_mult: 0.0 378 | } 379 | } 380 | layer { 381 | name: "layer_256_1_scale1" 382 | type: "Scale" 383 | bottom: "layer_256_1_bn1" 384 | top: "layer_256_1_bn1" 385 | param { 386 | lr_mult: 1.0 387 | decay_mult: 1.0 388 | } 389 | param { 390 | lr_mult: 2.0 391 | decay_mult: 1.0 392 | } 393 | scale_param { 394 | bias_term: true 395 | } 396 | } 397 | layer { 398 | name: "layer_256_1_relu1" 399 | type: "ReLU" 400 | bottom: "layer_256_1_bn1" 401 | top: "layer_256_1_bn1" 402 | } 403 | layer { 404 | name: "layer_256_1_conv1" 405 | type: "Convolution" 406 | bottom: "layer_256_1_bn1" 407 | top: "layer_256_1_conv1" 408 | param { 409 | lr_mult: 1.0 410 | decay_mult: 1.0 411 | } 412 | convolution_param { 413 | num_output: 256 414 | bias_term: false 415 | pad: 1 416 | kernel_size: 3 417 | stride: 2 418 | weight_filler { 419 | type: "msra" 420 | } 421 | bias_filler { 422 | type: "constant" 423 | value: 0.0 424 | } 425 | } 426 | } 427 | layer { 428 | name: "layer_256_1_bn2" 429 | type: "BatchNorm" 430 | bottom: "layer_256_1_conv1" 431 | top: "layer_256_1_conv1" 432 | param { 433 | lr_mult: 0.0 434 | } 435 | param { 436 | lr_mult: 0.0 437 | } 438 | param { 439 | lr_mult: 0.0 440 | } 441 | } 442 | layer { 443 | name: "layer_256_1_scale2" 444 | type: "Scale" 445 | bottom: "layer_256_1_conv1" 446 | top: "layer_256_1_conv1" 447 | param { 448 | lr_mult: 1.0 449 | decay_mult: 1.0 450 | } 451 | param { 452 | lr_mult: 2.0 453 | decay_mult: 1.0 454 | } 455 | scale_param { 456 | bias_term: true 457 | } 458 | } 459 | layer { 460 | name: "layer_256_1_relu2" 461 | type: "ReLU" 462 | bottom: "layer_256_1_conv1" 463 | top: "layer_256_1_conv1" 464 | } 465 | layer { 466 | name: "layer_256_1_conv2" 467 | type: "Convolution" 468 | bottom: "layer_256_1_conv1" 469 | top: "layer_256_1_conv2" 470 | param { 471 | lr_mult: 1.0 472 | decay_mult: 1.0 473 | } 474 | convolution_param { 475 | num_output: 256 476 | bias_term: false 477 | pad: 1 478 | kernel_size: 3 479 | stride: 1 480 | weight_filler { 481 | type: "msra" 482 | } 483 | bias_filler { 484 | type: "constant" 485 | value: 0.0 486 | } 487 | } 488 | } 489 | layer { 490 | name: "layer_256_1_conv_expand" 491 | type: "Convolution" 492 | bottom: "layer_256_1_bn1" 493 | top: "layer_256_1_conv_expand" 494 | param { 495 | lr_mult: 1.0 496 | decay_mult: 1.0 497 | } 498 | convolution_param { 499 | num_output: 256 500 | bias_term: false 501 | pad: 0 502 | kernel_size: 1 503 | stride: 2 504 | weight_filler { 505 | type: "msra" 506 | } 507 | bias_filler { 508 | type: "constant" 509 | value: 0.0 510 | } 511 | } 512 | } 513 | layer { 514 | name: "layer_256_1_sum" 515 | type: "Eltwise" 516 | bottom: "layer_256_1_conv2" 517 | bottom: "layer_256_1_conv_expand" 518 | top: "layer_256_1_sum" 519 | } 520 | layer { 521 | name: "layer_512_1_bn1" 522 | type: "BatchNorm" 523 | bottom: "layer_256_1_sum" 524 | top: "layer_512_1_bn1" 525 | param { 526 | lr_mult: 0.0 527 | } 528 | param { 529 | lr_mult: 0.0 530 | } 531 | param { 532 | lr_mult: 0.0 533 | } 534 | } 535 | layer { 536 | name: "layer_512_1_scale1" 537 | type: "Scale" 538 | bottom: "layer_512_1_bn1" 539 | top: "layer_512_1_bn1" 540 | param { 541 | lr_mult: 1.0 542 | decay_mult: 1.0 543 | } 544 | param { 545 | lr_mult: 2.0 546 | decay_mult: 1.0 547 | } 548 | scale_param { 549 | bias_term: true 550 | } 551 | } 552 | layer { 553 | name: "layer_512_1_relu1" 554 | type: "ReLU" 555 | bottom: "layer_512_1_bn1" 556 | top: "layer_512_1_bn1" 557 | } 558 | layer { 559 | name: "layer_512_1_conv1_h" 560 | type: "Convolution" 561 | bottom: "layer_512_1_bn1" 562 | top: "layer_512_1_conv1_h" 563 | param { 564 | lr_mult: 1.0 565 | decay_mult: 1.0 566 | } 567 | convolution_param { 568 | num_output: 128 569 | bias_term: false 570 | pad: 1 571 | kernel_size: 3 572 | stride: 1 # 2 573 | weight_filler { 574 | type: "msra" 575 | } 576 | bias_filler { 577 | type: "constant" 578 | value: 0.0 579 | } 580 | } 581 | } 582 | layer { 583 | name: "layer_512_1_bn2_h" 584 | type: "BatchNorm" 585 | bottom: "layer_512_1_conv1_h" 586 | top: "layer_512_1_conv1_h" 587 | param { 588 | lr_mult: 0.0 589 | } 590 | param { 591 | lr_mult: 0.0 592 | } 593 | param { 594 | lr_mult: 0.0 595 | } 596 | } 597 | layer { 598 | name: "layer_512_1_scale2_h" 599 | type: "Scale" 600 | bottom: "layer_512_1_conv1_h" 601 | top: "layer_512_1_conv1_h" 602 | param { 603 | lr_mult: 1.0 604 | decay_mult: 1.0 605 | } 606 | param { 607 | lr_mult: 2.0 608 | decay_mult: 1.0 609 | } 610 | scale_param { 611 | bias_term: true 612 | } 613 | } 614 | layer { 615 | name: "layer_512_1_relu2" 616 | type: "ReLU" 617 | bottom: "layer_512_1_conv1_h" 618 | top: "layer_512_1_conv1_h" 619 | } 620 | layer { 621 | name: "layer_512_1_conv2_h" 622 | type: "Convolution" 623 | bottom: "layer_512_1_conv1_h" 624 | top: "layer_512_1_conv2_h" 625 | param { 626 | lr_mult: 1.0 627 | decay_mult: 1.0 628 | } 629 | convolution_param { 630 | num_output: 256 631 | bias_term: false 632 | pad: 2 # 1 633 | kernel_size: 3 634 | stride: 1 635 | dilation: 2 636 | weight_filler { 637 | type: "msra" 638 | } 639 | bias_filler { 640 | type: "constant" 641 | value: 0.0 642 | } 643 | } 644 | } 645 | layer { 646 | name: "layer_512_1_conv_expand_h" 647 | type: "Convolution" 648 | bottom: "layer_512_1_bn1" 649 | top: "layer_512_1_conv_expand_h" 650 | param { 651 | lr_mult: 1.0 652 | decay_mult: 1.0 653 | } 654 | convolution_param { 655 | num_output: 256 656 | bias_term: false 657 | pad: 0 658 | kernel_size: 1 659 | stride: 1 # 2 660 | weight_filler { 661 | type: "msra" 662 | } 663 | bias_filler { 664 | type: "constant" 665 | value: 0.0 666 | } 667 | } 668 | } 669 | layer { 670 | name: "layer_512_1_sum" 671 | type: "Eltwise" 672 | bottom: "layer_512_1_conv2_h" 673 | bottom: "layer_512_1_conv_expand_h" 674 | top: "layer_512_1_sum" 675 | } 676 | layer { 677 | name: "last_bn_h" 678 | type: "BatchNorm" 679 | bottom: "layer_512_1_sum" 680 | top: "layer_512_1_sum" 681 | param { 682 | lr_mult: 0.0 683 | } 684 | param { 685 | lr_mult: 0.0 686 | } 687 | param { 688 | lr_mult: 0.0 689 | } 690 | } 691 | layer { 692 | name: "last_scale_h" 693 | type: "Scale" 694 | bottom: "layer_512_1_sum" 695 | top: "layer_512_1_sum" 696 | param { 697 | lr_mult: 1.0 698 | decay_mult: 1.0 699 | } 700 | param { 701 | lr_mult: 2.0 702 | decay_mult: 1.0 703 | } 704 | scale_param { 705 | bias_term: true 706 | } 707 | } 708 | layer { 709 | name: "last_relu" 710 | type: "ReLU" 711 | bottom: "layer_512_1_sum" 712 | top: "fc7" 713 | } 714 | 715 | layer { 716 | name: "conv6_1_h" 717 | type: "Convolution" 718 | bottom: "fc7" 719 | top: "conv6_1_h" 720 | param { 721 | lr_mult: 1 722 | decay_mult: 1 723 | } 724 | param { 725 | lr_mult: 2 726 | decay_mult: 0 727 | } 728 | convolution_param { 729 | num_output: 128 730 | pad: 0 731 | kernel_size: 1 732 | stride: 1 733 | weight_filler { 734 | type: "xavier" 735 | } 736 | bias_filler { 737 | type: "constant" 738 | value: 0 739 | } 740 | } 741 | } 742 | layer { 743 | name: "conv6_1_relu" 744 | type: "ReLU" 745 | bottom: "conv6_1_h" 746 | top: "conv6_1_h" 747 | } 748 | layer { 749 | name: "conv6_2_h" 750 | type: "Convolution" 751 | bottom: "conv6_1_h" 752 | top: "conv6_2_h" 753 | param { 754 | lr_mult: 1 755 | decay_mult: 1 756 | } 757 | param { 758 | lr_mult: 2 759 | decay_mult: 0 760 | } 761 | convolution_param { 762 | num_output: 256 763 | pad: 1 764 | kernel_size: 3 765 | stride: 2 766 | weight_filler { 767 | type: "xavier" 768 | } 769 | bias_filler { 770 | type: "constant" 771 | value: 0 772 | } 773 | } 774 | } 775 | layer { 776 | name: "conv6_2_relu" 777 | type: "ReLU" 778 | bottom: "conv6_2_h" 779 | top: "conv6_2_h" 780 | } 781 | layer { 782 | name: "conv7_1_h" 783 | type: "Convolution" 784 | bottom: "conv6_2_h" 785 | top: "conv7_1_h" 786 | param { 787 | lr_mult: 1 788 | decay_mult: 1 789 | } 790 | param { 791 | lr_mult: 2 792 | decay_mult: 0 793 | } 794 | convolution_param { 795 | num_output: 64 796 | pad: 0 797 | kernel_size: 1 798 | stride: 1 799 | weight_filler { 800 | type: "xavier" 801 | } 802 | bias_filler { 803 | type: "constant" 804 | value: 0 805 | } 806 | } 807 | } 808 | layer { 809 | name: "conv7_1_relu" 810 | type: "ReLU" 811 | bottom: "conv7_1_h" 812 | top: "conv7_1_h" 813 | } 814 | layer { 815 | name: "conv7_2_h" 816 | type: "Convolution" 817 | bottom: "conv7_1_h" 818 | top: "conv7_2_h" 819 | param { 820 | lr_mult: 1 821 | decay_mult: 1 822 | } 823 | param { 824 | lr_mult: 2 825 | decay_mult: 0 826 | } 827 | convolution_param { 828 | num_output: 128 829 | pad: 1 830 | kernel_size: 3 831 | stride: 2 832 | weight_filler { 833 | type: "xavier" 834 | } 835 | bias_filler { 836 | type: "constant" 837 | value: 0 838 | } 839 | } 840 | } 841 | layer { 842 | name: "conv7_2_relu" 843 | type: "ReLU" 844 | bottom: "conv7_2_h" 845 | top: "conv7_2_h" 846 | } 847 | layer { 848 | name: "conv8_1_h" 849 | type: "Convolution" 850 | bottom: "conv7_2_h" 851 | top: "conv8_1_h" 852 | param { 853 | lr_mult: 1 854 | decay_mult: 1 855 | } 856 | param { 857 | lr_mult: 2 858 | decay_mult: 0 859 | } 860 | convolution_param { 861 | num_output: 64 862 | pad: 0 863 | kernel_size: 1 864 | stride: 1 865 | weight_filler { 866 | type: "xavier" 867 | } 868 | bias_filler { 869 | type: "constant" 870 | value: 0 871 | } 872 | } 873 | } 874 | layer { 875 | name: "conv8_1_relu" 876 | type: "ReLU" 877 | bottom: "conv8_1_h" 878 | top: "conv8_1_h" 879 | } 880 | layer { 881 | name: "conv8_2_h" 882 | type: "Convolution" 883 | bottom: "conv8_1_h" 884 | top: "conv8_2_h" 885 | param { 886 | lr_mult: 1 887 | decay_mult: 1 888 | } 889 | param { 890 | lr_mult: 2 891 | decay_mult: 0 892 | } 893 | convolution_param { 894 | num_output: 128 895 | pad: 1 896 | kernel_size: 3 897 | stride: 1 898 | weight_filler { 899 | type: "xavier" 900 | } 901 | bias_filler { 902 | type: "constant" 903 | value: 0 904 | } 905 | } 906 | } 907 | layer { 908 | name: "conv8_2_relu" 909 | type: "ReLU" 910 | bottom: "conv8_2_h" 911 | top: "conv8_2_h" 912 | } 913 | layer { 914 | name: "conv9_1_h" 915 | type: "Convolution" 916 | bottom: "conv8_2_h" 917 | top: "conv9_1_h" 918 | param { 919 | lr_mult: 1 920 | decay_mult: 1 921 | } 922 | param { 923 | lr_mult: 2 924 | decay_mult: 0 925 | } 926 | convolution_param { 927 | num_output: 64 928 | pad: 0 929 | kernel_size: 1 930 | stride: 1 931 | weight_filler { 932 | type: "xavier" 933 | } 934 | bias_filler { 935 | type: "constant" 936 | value: 0 937 | } 938 | } 939 | } 940 | layer { 941 | name: "conv9_1_relu" 942 | type: "ReLU" 943 | bottom: "conv9_1_h" 944 | top: "conv9_1_h" 945 | } 946 | layer { 947 | name: "conv9_2_h" 948 | type: "Convolution" 949 | bottom: "conv9_1_h" 950 | top: "conv9_2_h" 951 | param { 952 | lr_mult: 1 953 | decay_mult: 1 954 | } 955 | param { 956 | lr_mult: 2 957 | decay_mult: 0 958 | } 959 | convolution_param { 960 | num_output: 128 961 | pad: 1 962 | kernel_size: 3 963 | stride: 1 964 | weight_filler { 965 | type: "xavier" 966 | } 967 | bias_filler { 968 | type: "constant" 969 | value: 0 970 | } 971 | } 972 | } 973 | layer { 974 | name: "conv9_2_relu" 975 | type: "ReLU" 976 | bottom: "conv9_2_h" 977 | top: "conv9_2_h" 978 | } 979 | layer { 980 | name: "conv4_3_norm" 981 | type: "Normalize" 982 | bottom: "layer_256_1_bn1" 983 | top: "conv4_3_norm" 984 | norm_param { 985 | across_spatial: false 986 | scale_filler { 987 | type: "constant" 988 | value: 20 989 | } 990 | channel_shared: false 991 | } 992 | } 993 | layer { 994 | name: "conv4_3_norm_mbox_loc" 995 | type: "Convolution" 996 | bottom: "conv4_3_norm" 997 | top: "conv4_3_norm_mbox_loc" 998 | param { 999 | lr_mult: 1 1000 | decay_mult: 1 1001 | } 1002 | param { 1003 | lr_mult: 2 1004 | decay_mult: 0 1005 | } 1006 | convolution_param { 1007 | num_output: 16 1008 | pad: 1 1009 | kernel_size: 3 1010 | stride: 1 1011 | weight_filler { 1012 | type: "xavier" 1013 | } 1014 | bias_filler { 1015 | type: "constant" 1016 | value: 0 1017 | } 1018 | } 1019 | } 1020 | layer { 1021 | name: "conv4_3_norm_mbox_loc_perm" 1022 | type: "Permute" 1023 | bottom: "conv4_3_norm_mbox_loc" 1024 | top: "conv4_3_norm_mbox_loc_perm" 1025 | permute_param { 1026 | order: 0 1027 | order: 2 1028 | order: 3 1029 | order: 1 1030 | } 1031 | } 1032 | layer { 1033 | name: "conv4_3_norm_mbox_loc_flat" 1034 | type: "Flatten" 1035 | bottom: "conv4_3_norm_mbox_loc_perm" 1036 | top: "conv4_3_norm_mbox_loc_flat" 1037 | flatten_param { 1038 | axis: 1 1039 | } 1040 | } 1041 | layer { 1042 | name: "conv4_3_norm_mbox_conf" 1043 | type: "Convolution" 1044 | bottom: "conv4_3_norm" 1045 | top: "conv4_3_norm_mbox_conf" 1046 | param { 1047 | lr_mult: 1 1048 | decay_mult: 1 1049 | } 1050 | param { 1051 | lr_mult: 2 1052 | decay_mult: 0 1053 | } 1054 | convolution_param { 1055 | num_output: 8 # 84 1056 | pad: 1 1057 | kernel_size: 3 1058 | stride: 1 1059 | weight_filler { 1060 | type: "xavier" 1061 | } 1062 | bias_filler { 1063 | type: "constant" 1064 | value: 0 1065 | } 1066 | } 1067 | } 1068 | layer { 1069 | name: "conv4_3_norm_mbox_conf_perm" 1070 | type: "Permute" 1071 | bottom: "conv4_3_norm_mbox_conf" 1072 | top: "conv4_3_norm_mbox_conf_perm" 1073 | permute_param { 1074 | order: 0 1075 | order: 2 1076 | order: 3 1077 | order: 1 1078 | } 1079 | } 1080 | layer { 1081 | name: "conv4_3_norm_mbox_conf_flat" 1082 | type: "Flatten" 1083 | bottom: "conv4_3_norm_mbox_conf_perm" 1084 | top: "conv4_3_norm_mbox_conf_flat" 1085 | flatten_param { 1086 | axis: 1 1087 | } 1088 | } 1089 | layer { 1090 | name: "conv4_3_norm_mbox_priorbox" 1091 | type: "PriorBox" 1092 | bottom: "conv4_3_norm" 1093 | bottom: "data" 1094 | top: "conv4_3_norm_mbox_priorbox" 1095 | prior_box_param { 1096 | min_size: 30.0 1097 | max_size: 60.0 1098 | aspect_ratio: 2 1099 | flip: true 1100 | clip: false 1101 | variance: 0.1 1102 | variance: 0.1 1103 | variance: 0.2 1104 | variance: 0.2 1105 | step: 8 1106 | offset: 0.5 1107 | } 1108 | } 1109 | layer { 1110 | name: "fc7_mbox_loc" 1111 | type: "Convolution" 1112 | bottom: "fc7" 1113 | top: "fc7_mbox_loc" 1114 | param { 1115 | lr_mult: 1 1116 | decay_mult: 1 1117 | } 1118 | param { 1119 | lr_mult: 2 1120 | decay_mult: 0 1121 | } 1122 | convolution_param { 1123 | num_output: 24 1124 | pad: 1 1125 | kernel_size: 3 1126 | stride: 1 1127 | weight_filler { 1128 | type: "xavier" 1129 | } 1130 | bias_filler { 1131 | type: "constant" 1132 | value: 0 1133 | } 1134 | } 1135 | } 1136 | layer { 1137 | name: "fc7_mbox_loc_perm" 1138 | type: "Permute" 1139 | bottom: "fc7_mbox_loc" 1140 | top: "fc7_mbox_loc_perm" 1141 | permute_param { 1142 | order: 0 1143 | order: 2 1144 | order: 3 1145 | order: 1 1146 | } 1147 | } 1148 | layer { 1149 | name: "fc7_mbox_loc_flat" 1150 | type: "Flatten" 1151 | bottom: "fc7_mbox_loc_perm" 1152 | top: "fc7_mbox_loc_flat" 1153 | flatten_param { 1154 | axis: 1 1155 | } 1156 | } 1157 | layer { 1158 | name: "fc7_mbox_conf" 1159 | type: "Convolution" 1160 | bottom: "fc7" 1161 | top: "fc7_mbox_conf" 1162 | param { 1163 | lr_mult: 1 1164 | decay_mult: 1 1165 | } 1166 | param { 1167 | lr_mult: 2 1168 | decay_mult: 0 1169 | } 1170 | convolution_param { 1171 | num_output: 12 # 126 1172 | pad: 1 1173 | kernel_size: 3 1174 | stride: 1 1175 | weight_filler { 1176 | type: "xavier" 1177 | } 1178 | bias_filler { 1179 | type: "constant" 1180 | value: 0 1181 | } 1182 | } 1183 | } 1184 | layer { 1185 | name: "fc7_mbox_conf_perm" 1186 | type: "Permute" 1187 | bottom: "fc7_mbox_conf" 1188 | top: "fc7_mbox_conf_perm" 1189 | permute_param { 1190 | order: 0 1191 | order: 2 1192 | order: 3 1193 | order: 1 1194 | } 1195 | } 1196 | layer { 1197 | name: "fc7_mbox_conf_flat" 1198 | type: "Flatten" 1199 | bottom: "fc7_mbox_conf_perm" 1200 | top: "fc7_mbox_conf_flat" 1201 | flatten_param { 1202 | axis: 1 1203 | } 1204 | } 1205 | layer { 1206 | name: "fc7_mbox_priorbox" 1207 | type: "PriorBox" 1208 | bottom: "fc7" 1209 | bottom: "data" 1210 | top: "fc7_mbox_priorbox" 1211 | prior_box_param { 1212 | min_size: 60.0 1213 | max_size: 111.0 1214 | aspect_ratio: 2 1215 | aspect_ratio: 3 1216 | flip: true 1217 | clip: false 1218 | variance: 0.1 1219 | variance: 0.1 1220 | variance: 0.2 1221 | variance: 0.2 1222 | step: 16 1223 | offset: 0.5 1224 | } 1225 | } 1226 | layer { 1227 | name: "conv6_2_mbox_loc" 1228 | type: "Convolution" 1229 | bottom: "conv6_2_h" 1230 | top: "conv6_2_mbox_loc" 1231 | param { 1232 | lr_mult: 1 1233 | decay_mult: 1 1234 | } 1235 | param { 1236 | lr_mult: 2 1237 | decay_mult: 0 1238 | } 1239 | convolution_param { 1240 | num_output: 24 1241 | pad: 1 1242 | kernel_size: 3 1243 | stride: 1 1244 | weight_filler { 1245 | type: "xavier" 1246 | } 1247 | bias_filler { 1248 | type: "constant" 1249 | value: 0 1250 | } 1251 | } 1252 | } 1253 | layer { 1254 | name: "conv6_2_mbox_loc_perm" 1255 | type: "Permute" 1256 | bottom: "conv6_2_mbox_loc" 1257 | top: "conv6_2_mbox_loc_perm" 1258 | permute_param { 1259 | order: 0 1260 | order: 2 1261 | order: 3 1262 | order: 1 1263 | } 1264 | } 1265 | layer { 1266 | name: "conv6_2_mbox_loc_flat" 1267 | type: "Flatten" 1268 | bottom: "conv6_2_mbox_loc_perm" 1269 | top: "conv6_2_mbox_loc_flat" 1270 | flatten_param { 1271 | axis: 1 1272 | } 1273 | } 1274 | layer { 1275 | name: "conv6_2_mbox_conf" 1276 | type: "Convolution" 1277 | bottom: "conv6_2_h" 1278 | top: "conv6_2_mbox_conf" 1279 | param { 1280 | lr_mult: 1 1281 | decay_mult: 1 1282 | } 1283 | param { 1284 | lr_mult: 2 1285 | decay_mult: 0 1286 | } 1287 | convolution_param { 1288 | num_output: 12 # 126 1289 | pad: 1 1290 | kernel_size: 3 1291 | stride: 1 1292 | weight_filler { 1293 | type: "xavier" 1294 | } 1295 | bias_filler { 1296 | type: "constant" 1297 | value: 0 1298 | } 1299 | } 1300 | } 1301 | layer { 1302 | name: "conv6_2_mbox_conf_perm" 1303 | type: "Permute" 1304 | bottom: "conv6_2_mbox_conf" 1305 | top: "conv6_2_mbox_conf_perm" 1306 | permute_param { 1307 | order: 0 1308 | order: 2 1309 | order: 3 1310 | order: 1 1311 | } 1312 | } 1313 | layer { 1314 | name: "conv6_2_mbox_conf_flat" 1315 | type: "Flatten" 1316 | bottom: "conv6_2_mbox_conf_perm" 1317 | top: "conv6_2_mbox_conf_flat" 1318 | flatten_param { 1319 | axis: 1 1320 | } 1321 | } 1322 | layer { 1323 | name: "conv6_2_mbox_priorbox" 1324 | type: "PriorBox" 1325 | bottom: "conv6_2_h" 1326 | bottom: "data" 1327 | top: "conv6_2_mbox_priorbox" 1328 | prior_box_param { 1329 | min_size: 111.0 1330 | max_size: 162.0 1331 | aspect_ratio: 2 1332 | aspect_ratio: 3 1333 | flip: true 1334 | clip: false 1335 | variance: 0.1 1336 | variance: 0.1 1337 | variance: 0.2 1338 | variance: 0.2 1339 | step: 32 1340 | offset: 0.5 1341 | } 1342 | } 1343 | layer { 1344 | name: "conv7_2_mbox_loc" 1345 | type: "Convolution" 1346 | bottom: "conv7_2_h" 1347 | top: "conv7_2_mbox_loc" 1348 | param { 1349 | lr_mult: 1 1350 | decay_mult: 1 1351 | } 1352 | param { 1353 | lr_mult: 2 1354 | decay_mult: 0 1355 | } 1356 | convolution_param { 1357 | num_output: 24 1358 | pad: 1 1359 | kernel_size: 3 1360 | stride: 1 1361 | weight_filler { 1362 | type: "xavier" 1363 | } 1364 | bias_filler { 1365 | type: "constant" 1366 | value: 0 1367 | } 1368 | } 1369 | } 1370 | layer { 1371 | name: "conv7_2_mbox_loc_perm" 1372 | type: "Permute" 1373 | bottom: "conv7_2_mbox_loc" 1374 | top: "conv7_2_mbox_loc_perm" 1375 | permute_param { 1376 | order: 0 1377 | order: 2 1378 | order: 3 1379 | order: 1 1380 | } 1381 | } 1382 | layer { 1383 | name: "conv7_2_mbox_loc_flat" 1384 | type: "Flatten" 1385 | bottom: "conv7_2_mbox_loc_perm" 1386 | top: "conv7_2_mbox_loc_flat" 1387 | flatten_param { 1388 | axis: 1 1389 | } 1390 | } 1391 | layer { 1392 | name: "conv7_2_mbox_conf" 1393 | type: "Convolution" 1394 | bottom: "conv7_2_h" 1395 | top: "conv7_2_mbox_conf" 1396 | param { 1397 | lr_mult: 1 1398 | decay_mult: 1 1399 | } 1400 | param { 1401 | lr_mult: 2 1402 | decay_mult: 0 1403 | } 1404 | convolution_param { 1405 | num_output: 12 # 126 1406 | pad: 1 1407 | kernel_size: 3 1408 | stride: 1 1409 | weight_filler { 1410 | type: "xavier" 1411 | } 1412 | bias_filler { 1413 | type: "constant" 1414 | value: 0 1415 | } 1416 | } 1417 | } 1418 | layer { 1419 | name: "conv7_2_mbox_conf_perm" 1420 | type: "Permute" 1421 | bottom: "conv7_2_mbox_conf" 1422 | top: "conv7_2_mbox_conf_perm" 1423 | permute_param { 1424 | order: 0 1425 | order: 2 1426 | order: 3 1427 | order: 1 1428 | } 1429 | } 1430 | layer { 1431 | name: "conv7_2_mbox_conf_flat" 1432 | type: "Flatten" 1433 | bottom: "conv7_2_mbox_conf_perm" 1434 | top: "conv7_2_mbox_conf_flat" 1435 | flatten_param { 1436 | axis: 1 1437 | } 1438 | } 1439 | layer { 1440 | name: "conv7_2_mbox_priorbox" 1441 | type: "PriorBox" 1442 | bottom: "conv7_2_h" 1443 | bottom: "data" 1444 | top: "conv7_2_mbox_priorbox" 1445 | prior_box_param { 1446 | min_size: 162.0 1447 | max_size: 213.0 1448 | aspect_ratio: 2 1449 | aspect_ratio: 3 1450 | flip: true 1451 | clip: false 1452 | variance: 0.1 1453 | variance: 0.1 1454 | variance: 0.2 1455 | variance: 0.2 1456 | step: 64 1457 | offset: 0.5 1458 | } 1459 | } 1460 | layer { 1461 | name: "conv8_2_mbox_loc" 1462 | type: "Convolution" 1463 | bottom: "conv8_2_h" 1464 | top: "conv8_2_mbox_loc" 1465 | param { 1466 | lr_mult: 1 1467 | decay_mult: 1 1468 | } 1469 | param { 1470 | lr_mult: 2 1471 | decay_mult: 0 1472 | } 1473 | convolution_param { 1474 | num_output: 16 1475 | pad: 1 1476 | kernel_size: 3 1477 | stride: 1 1478 | weight_filler { 1479 | type: "xavier" 1480 | } 1481 | bias_filler { 1482 | type: "constant" 1483 | value: 0 1484 | } 1485 | } 1486 | } 1487 | layer { 1488 | name: "conv8_2_mbox_loc_perm" 1489 | type: "Permute" 1490 | bottom: "conv8_2_mbox_loc" 1491 | top: "conv8_2_mbox_loc_perm" 1492 | permute_param { 1493 | order: 0 1494 | order: 2 1495 | order: 3 1496 | order: 1 1497 | } 1498 | } 1499 | layer { 1500 | name: "conv8_2_mbox_loc_flat" 1501 | type: "Flatten" 1502 | bottom: "conv8_2_mbox_loc_perm" 1503 | top: "conv8_2_mbox_loc_flat" 1504 | flatten_param { 1505 | axis: 1 1506 | } 1507 | } 1508 | layer { 1509 | name: "conv8_2_mbox_conf" 1510 | type: "Convolution" 1511 | bottom: "conv8_2_h" 1512 | top: "conv8_2_mbox_conf" 1513 | param { 1514 | lr_mult: 1 1515 | decay_mult: 1 1516 | } 1517 | param { 1518 | lr_mult: 2 1519 | decay_mult: 0 1520 | } 1521 | convolution_param { 1522 | num_output: 8 # 84 1523 | pad: 1 1524 | kernel_size: 3 1525 | stride: 1 1526 | weight_filler { 1527 | type: "xavier" 1528 | } 1529 | bias_filler { 1530 | type: "constant" 1531 | value: 0 1532 | } 1533 | } 1534 | } 1535 | layer { 1536 | name: "conv8_2_mbox_conf_perm" 1537 | type: "Permute" 1538 | bottom: "conv8_2_mbox_conf" 1539 | top: "conv8_2_mbox_conf_perm" 1540 | permute_param { 1541 | order: 0 1542 | order: 2 1543 | order: 3 1544 | order: 1 1545 | } 1546 | } 1547 | layer { 1548 | name: "conv8_2_mbox_conf_flat" 1549 | type: "Flatten" 1550 | bottom: "conv8_2_mbox_conf_perm" 1551 | top: "conv8_2_mbox_conf_flat" 1552 | flatten_param { 1553 | axis: 1 1554 | } 1555 | } 1556 | layer { 1557 | name: "conv8_2_mbox_priorbox" 1558 | type: "PriorBox" 1559 | bottom: "conv8_2_h" 1560 | bottom: "data" 1561 | top: "conv8_2_mbox_priorbox" 1562 | prior_box_param { 1563 | min_size: 213.0 1564 | max_size: 264.0 1565 | aspect_ratio: 2 1566 | flip: true 1567 | clip: false 1568 | variance: 0.1 1569 | variance: 0.1 1570 | variance: 0.2 1571 | variance: 0.2 1572 | step: 100 1573 | offset: 0.5 1574 | } 1575 | } 1576 | layer { 1577 | name: "conv9_2_mbox_loc" 1578 | type: "Convolution" 1579 | bottom: "conv9_2_h" 1580 | top: "conv9_2_mbox_loc" 1581 | param { 1582 | lr_mult: 1 1583 | decay_mult: 1 1584 | } 1585 | param { 1586 | lr_mult: 2 1587 | decay_mult: 0 1588 | } 1589 | convolution_param { 1590 | num_output: 16 1591 | pad: 1 1592 | kernel_size: 3 1593 | stride: 1 1594 | weight_filler { 1595 | type: "xavier" 1596 | } 1597 | bias_filler { 1598 | type: "constant" 1599 | value: 0 1600 | } 1601 | } 1602 | } 1603 | layer { 1604 | name: "conv9_2_mbox_loc_perm" 1605 | type: "Permute" 1606 | bottom: "conv9_2_mbox_loc" 1607 | top: "conv9_2_mbox_loc_perm" 1608 | permute_param { 1609 | order: 0 1610 | order: 2 1611 | order: 3 1612 | order: 1 1613 | } 1614 | } 1615 | layer { 1616 | name: "conv9_2_mbox_loc_flat" 1617 | type: "Flatten" 1618 | bottom: "conv9_2_mbox_loc_perm" 1619 | top: "conv9_2_mbox_loc_flat" 1620 | flatten_param { 1621 | axis: 1 1622 | } 1623 | } 1624 | layer { 1625 | name: "conv9_2_mbox_conf" 1626 | type: "Convolution" 1627 | bottom: "conv9_2_h" 1628 | top: "conv9_2_mbox_conf" 1629 | param { 1630 | lr_mult: 1 1631 | decay_mult: 1 1632 | } 1633 | param { 1634 | lr_mult: 2 1635 | decay_mult: 0 1636 | } 1637 | convolution_param { 1638 | num_output: 8 # 84 1639 | pad: 1 1640 | kernel_size: 3 1641 | stride: 1 1642 | weight_filler { 1643 | type: "xavier" 1644 | } 1645 | bias_filler { 1646 | type: "constant" 1647 | value: 0 1648 | } 1649 | } 1650 | } 1651 | layer { 1652 | name: "conv9_2_mbox_conf_perm" 1653 | type: "Permute" 1654 | bottom: "conv9_2_mbox_conf" 1655 | top: "conv9_2_mbox_conf_perm" 1656 | permute_param { 1657 | order: 0 1658 | order: 2 1659 | order: 3 1660 | order: 1 1661 | } 1662 | } 1663 | layer { 1664 | name: "conv9_2_mbox_conf_flat" 1665 | type: "Flatten" 1666 | bottom: "conv9_2_mbox_conf_perm" 1667 | top: "conv9_2_mbox_conf_flat" 1668 | flatten_param { 1669 | axis: 1 1670 | } 1671 | } 1672 | layer { 1673 | name: "conv9_2_mbox_priorbox" 1674 | type: "PriorBox" 1675 | bottom: "conv9_2_h" 1676 | bottom: "data" 1677 | top: "conv9_2_mbox_priorbox" 1678 | prior_box_param { 1679 | min_size: 264.0 1680 | max_size: 315.0 1681 | aspect_ratio: 2 1682 | flip: true 1683 | clip: false 1684 | variance: 0.1 1685 | variance: 0.1 1686 | variance: 0.2 1687 | variance: 0.2 1688 | step: 300 1689 | offset: 0.5 1690 | } 1691 | } 1692 | layer { 1693 | name: "mbox_loc" 1694 | type: "Concat" 1695 | bottom: "conv4_3_norm_mbox_loc_flat" 1696 | bottom: "fc7_mbox_loc_flat" 1697 | bottom: "conv6_2_mbox_loc_flat" 1698 | bottom: "conv7_2_mbox_loc_flat" 1699 | bottom: "conv8_2_mbox_loc_flat" 1700 | bottom: "conv9_2_mbox_loc_flat" 1701 | top: "mbox_loc" 1702 | concat_param { 1703 | axis: 1 1704 | } 1705 | } 1706 | layer { 1707 | name: "mbox_conf" 1708 | type: "Concat" 1709 | bottom: "conv4_3_norm_mbox_conf_flat" 1710 | bottom: "fc7_mbox_conf_flat" 1711 | bottom: "conv6_2_mbox_conf_flat" 1712 | bottom: "conv7_2_mbox_conf_flat" 1713 | bottom: "conv8_2_mbox_conf_flat" 1714 | bottom: "conv9_2_mbox_conf_flat" 1715 | top: "mbox_conf" 1716 | concat_param { 1717 | axis: 1 1718 | } 1719 | } 1720 | layer { 1721 | name: "mbox_priorbox" 1722 | type: "Concat" 1723 | bottom: "conv4_3_norm_mbox_priorbox" 1724 | bottom: "fc7_mbox_priorbox" 1725 | bottom: "conv6_2_mbox_priorbox" 1726 | bottom: "conv7_2_mbox_priorbox" 1727 | bottom: "conv8_2_mbox_priorbox" 1728 | bottom: "conv9_2_mbox_priorbox" 1729 | top: "mbox_priorbox" 1730 | concat_param { 1731 | axis: 2 1732 | } 1733 | } 1734 | 1735 | layer { 1736 | name: "mbox_conf_reshape" 1737 | type: "Reshape" 1738 | bottom: "mbox_conf" 1739 | top: "mbox_conf_reshape" 1740 | reshape_param { 1741 | shape { 1742 | dim: 0 1743 | dim: -1 1744 | dim: 2 1745 | } 1746 | } 1747 | } 1748 | layer { 1749 | name: "mbox_conf_softmax" 1750 | type: "Softmax" 1751 | bottom: "mbox_conf_reshape" 1752 | top: "mbox_conf_softmax" 1753 | softmax_param { 1754 | axis: 2 1755 | } 1756 | } 1757 | layer { 1758 | name: "mbox_conf_flatten" 1759 | type: "Flatten" 1760 | bottom: "mbox_conf_softmax" 1761 | top: "mbox_conf_flatten" 1762 | flatten_param { 1763 | axis: 1 1764 | } 1765 | } 1766 | 1767 | layer { 1768 | name: "detection_out" 1769 | type: "DetectionOutput" 1770 | bottom: "mbox_loc" 1771 | bottom: "mbox_conf_flatten" 1772 | bottom: "mbox_priorbox" 1773 | top: "detection_out" 1774 | include { 1775 | phase: TEST 1776 | } 1777 | detection_output_param { 1778 | num_classes: 2 1779 | share_location: true 1780 | background_label_id: 0 1781 | nms_param { 1782 | nms_threshold: 0.45 1783 | top_k: 400 1784 | } 1785 | code_type: CENTER_SIZE 1786 | keep_top_k: 200 1787 | confidence_threshold: 0.01 1788 | } 1789 | } 1790 | -------------------------------------------------------------------------------- /face_detector/res10_300x300_ssd_iter_140000.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joytsay/livenessDetection/900a3fc3c1c23090857ae26fa2ae3720d9ec2f96/face_detector/res10_300x300_ssd_iter_140000.caffemodel -------------------------------------------------------------------------------- /gather_examples.py: -------------------------------------------------------------------------------- 1 | # USAGE 2 | # python gather_examples.py --input videos/real.mov --output dataset/real --detector face_detector --skip 1 3 | # python gather_examples.py --input videos/fake.mp4 --output dataset/fake --detector face_detector --skip 4 4 | 5 | # import the necessary packages 6 | import numpy as np 7 | import argparse 8 | import cv2 9 | import os 10 | 11 | # construct the argument parse and parse the arguments 12 | ap = argparse.ArgumentParser() 13 | ap.add_argument("-i", "--input", type=str, required=True, 14 | help="path to input video") 15 | ap.add_argument("-o", "--output", type=str, required=True, 16 | help="path to output directory of cropped faces") 17 | ap.add_argument("-d", "--detector", type=str, required=True, 18 | help="path to OpenCV's deep learning face detector") 19 | ap.add_argument("-c", "--confidence", type=float, default=0.5, 20 | help="minimum probability to filter weak detections") 21 | ap.add_argument("-s", "--skip", type=int, default=16, 22 | help="# of frames to skip before applying face detection") 23 | ap.add_argument("-f", "--flip", type=int, default=0, 24 | help="# flip cropped faces") 25 | args = vars(ap.parse_args()) 26 | 27 | # load our serialized face detector from disk 28 | print("[INFO] loading face detector...") 29 | protoPath = os.path.sep.join([args["detector"], "deploy.prototxt"]) 30 | modelPath = os.path.sep.join([args["detector"], 31 | "res10_300x300_ssd_iter_140000.caffemodel"]) 32 | net = cv2.dnn.readNetFromCaffe(protoPath, modelPath) 33 | 34 | # open a pointer to the video file stream and initialize the total 35 | # number of frames read and saved thus far 36 | vs = cv2.VideoCapture(args["input"]) 37 | read = 0 38 | saved = 0 39 | 40 | # loop over frames from the video file stream 41 | while True: 42 | # grab the frame from the file 43 | (grabbed, frame) = vs.read() 44 | 45 | # if the frame was not grabbed, then we have reached the end 46 | # of the stream 47 | if not grabbed: 48 | break 49 | 50 | # increment the total number of frames read thus far 51 | read += 1 52 | 53 | # check to see if we should process this frame 54 | if read % args["skip"] != 0: 55 | continue 56 | 57 | # grab the frame dimensions and construct a blob from the frame 58 | (h, w) = frame.shape[:2] 59 | blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0, 60 | (300, 300), (104.0, 177.0, 123.0)) 61 | 62 | # pass the blob through the network and obtain the detections and 63 | # predictions 64 | net.setInput(blob) 65 | detections = net.forward() 66 | 67 | # ensure at least one face was found 68 | if len(detections) > 0: 69 | # we're making the assumption that each image has only ONE 70 | # face, so find the bounding box with the largest probability 71 | i = np.argmax(detections[0, 0, :, 2]) 72 | confidence = detections[0, 0, i, 2] 73 | 74 | # ensure that the detection with the largest probability also 75 | # means our minimum probability test (thus helping filter out 76 | # weak detections) 77 | if confidence > args["confidence"]: 78 | # compute the (x, y)-coordinates of the bounding box for 79 | # the face and extract the face ROI 80 | box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) 81 | (startX, startY, endX, endY) = box.astype("int") 82 | face = frame[startY:endY, startX:endX] 83 | 84 | # write the frame to disk 85 | p = os.path.sep.join([args["output"], 86 | "{}.png".format(saved)]) 87 | if args["flip"] != 0: 88 | _face = cv2.flip(face, 0) 89 | else: 90 | _face = face 91 | cv2.imwrite(p, _face) 92 | saved += 1 93 | print("[INFO] saved {} to disk".format(p)) 94 | 95 | # do a bit of cleanup 96 | vs.release() 97 | cv2.destroyAllWindows() -------------------------------------------------------------------------------- /le.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joytsay/livenessDetection/900a3fc3c1c23090857ae26fa2ae3720d9ec2f96/le.pickle -------------------------------------------------------------------------------- /liveness_demo.py: -------------------------------------------------------------------------------- 1 | # USAGE 2 | # python liveness_demo.py --model liveness.model --le le.pickle --detector face_detector 3 | 4 | # import the necessary packages 5 | from imutils.video import VideoStream 6 | from keras.preprocessing.image import img_to_array 7 | from keras.models import load_model 8 | import numpy as np 9 | import argparse 10 | import imutils 11 | import pickle 12 | import time 13 | import cv2 14 | import os 15 | 16 | # construct the argument parse and parse the arguments 17 | ap = argparse.ArgumentParser() 18 | ap.add_argument("-m", "--model", type=str, required=True, 19 | help="path to trained model") 20 | ap.add_argument("-l", "--le", type=str, required=True, 21 | help="path to label encoder") 22 | ap.add_argument("-d", "--detector", type=str, required=True, 23 | help="path to OpenCV's deep learning face detector") 24 | ap.add_argument("-c", "--confidence", type=float, default=0.5, 25 | help="minimum probability to filter weak detections") 26 | args = vars(ap.parse_args()) 27 | 28 | # load our serialized face detector from disk 29 | print("[INFO] loading face detector...") 30 | protoPath = os.path.sep.join([args["detector"], "deploy.prototxt"]) 31 | modelPath = os.path.sep.join([args["detector"], 32 | "res10_300x300_ssd_iter_140000.caffemodel"]) 33 | net = cv2.dnn.readNetFromCaffe(protoPath, modelPath) 34 | 35 | # load the liveness detector model and label encoder from disk 36 | print("[INFO] loading liveness detector...") 37 | model = load_model(args["model"]) 38 | le = pickle.loads(open(args["le"], "rb").read()) 39 | 40 | # initialize the video stream and allow the camera sensor to warmup 41 | print("[INFO] starting video stream...") 42 | cap = cv2.VideoCapture('videos/testLiveness.mp4') 43 | time.sleep(2.0) 44 | fourcc = cv2.VideoWriter_fourcc(*'MP4V') 45 | out = cv2.VideoWriter('LivenessResult.mp4', fourcc, 30.0, (640,480)) 46 | 47 | # loop over the frames from the video stream 48 | while True: 49 | # grab the frame from the threaded video stream and resize it 50 | # to have a maximum width of 600 pixels 51 | ret, frame = cap.read() 52 | frame = imutils.resize(frame, height=480, width=640) 53 | 54 | # grab the frame dimensions and convert it to a blob 55 | (h, w) = frame.shape[:2] 56 | blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0, 57 | (300, 300), (104.0, 177.0, 123.0)) 58 | 59 | # pass the blob through the network and obtain the detections and 60 | # predictions 61 | net.setInput(blob) 62 | detections = net.forward() 63 | 64 | # loop over the detections 65 | for i in range(0, detections.shape[2]): 66 | # extract the confidence (i.e., probability) associated with the 67 | # prediction 68 | confidence = detections[0, 0, i, 2] 69 | 70 | # filter out weak detections 71 | if confidence > args["confidence"]: 72 | # compute the (x, y)-coordinates of the bounding box for 73 | # the face and extract the face ROI 74 | box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) 75 | (startX, startY, endX, endY) = box.astype("int") 76 | 77 | # ensure the detected bounding box does fall outside the 78 | # dimensions of the frame 79 | startX = max(0, startX) 80 | startY = max(0, startY) 81 | endX = min(w, endX) 82 | endY = min(h, endY) 83 | 84 | # extract the face ROI and then preproces it in the exact 85 | # same manner as our training data 86 | face = frame[startY:endY, startX:endX] 87 | face = cv2.resize(face, (32, 32)) 88 | face = face.astype("float") / 255.0 89 | face = img_to_array(face) 90 | face = np.expand_dims(face, axis=0) 91 | 92 | # pass the face ROI through the trained liveness detector 93 | # model to determine if the face is "real" or "fake" 94 | preds = model.predict(face)[0] 95 | j = np.argmax(preds) 96 | label = le.classes_[j] 97 | #print(preds) 98 | #print(j) 99 | #print(label) 100 | 101 | # draw the label and bounding box on the frame 102 | label = "{}: {:.4f}".format(label, preds[j]) 103 | 104 | if preds[j] > 0.50 and j == 1: 105 | cv2.rectangle(frame, (startX, startY), (endX, endY), 106 | (0, 255, 0), 2) 107 | _label = "Liveness: {:.4f}".format(preds[j]) 108 | cv2.putText(frame, _label, (startX, startY - 10), 109 | cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) 110 | else: 111 | cv2.rectangle(frame, (startX, startY), (endX, endY), 112 | (0, 0, 255), 2) 113 | _label = "Fake: {:.4f}".format(preds[j]) 114 | cv2.putText(frame, _label, (startX, startY - 10), 115 | cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) 116 | 117 | # show the output frame and wait for a key press 118 | cv2.imshow("Frame", frame) 119 | out.write(frame) 120 | key = cv2.waitKey(1) & 0xFF 121 | 122 | # if the `q` key was pressed, break from the loop 123 | if key == ord("q"): 124 | break 125 | 126 | # do a bit of cleanup 127 | out.release() 128 | cv2.destroyAllWindows() 129 | vs.stop() -------------------------------------------------------------------------------- /pyimagesearch/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joytsay/livenessDetection/900a3fc3c1c23090857ae26fa2ae3720d9ec2f96/pyimagesearch/__init__.py -------------------------------------------------------------------------------- /pyimagesearch/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joytsay/livenessDetection/900a3fc3c1c23090857ae26fa2ae3720d9ec2f96/pyimagesearch/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /pyimagesearch/__pycache__/livenessnet.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joytsay/livenessDetection/900a3fc3c1c23090857ae26fa2ae3720d9ec2f96/pyimagesearch/__pycache__/livenessnet.cpython-36.pyc -------------------------------------------------------------------------------- /pyimagesearch/livenessnet.py: -------------------------------------------------------------------------------- 1 | # import the necessary packages 2 | from keras.src.layers.core import Flatten 3 | from keras.src.layers.core import Dropout 4 | from keras.src.layers.core import Dense 5 | from keras.models import Model 6 | from keras.applications.resnet50 import ResNet50 7 | 8 | # from tensorflow.python.keras.models import Model 9 | # from tensorflow.python.keras.layers import Flatten, Dense, Dropout 10 | # from tensorflow.python.keras.applications import ResNet50 11 | # from tensorflow.python.keras.models import Sequential 12 | 13 | 14 | class LivenessNet: 15 | @staticmethod 16 | def build(width, height, depth, classes): 17 | 18 | # net = ResNet50(include_top=False, weights='imagenet', input_tensor=None, 19 | # input_shape=(width, height, depth)) 20 | # res = net.output 21 | # res = GlobalAveragePooling2D()(res) 22 | # fc = Dense(classes, activation='softmax', name='fc1000')(res) 23 | # model = Model(inputs=net.input, outputs=fc) 24 | 25 | net = ResNet50(include_top=False, weights='imagenet', input_tensor=None, input_shape=(width, height, depth)) 26 | res = net.output 27 | res = Flatten()(res) 28 | res = Dropout(0.5)(res) 29 | fc = Dense(classes, activation='softmax', name='fc2')(res) 30 | model = Model(inputs=net.input, outputs=fc) 31 | 32 | # model = Sequential() 33 | # model.add(ResNet50(include_top=False, pooling='avg', weights='imagenet', input_shape=(width, height, depth))) 34 | # model.add(Flatten()) 35 | # # model.add(BatchNormalization()) 36 | # model.add(Dense(classes, activation='softmax', name='fc2')) 37 | 38 | # return the constructed network architecture 39 | 40 | return model 41 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | imutils 2 | keras 3 | keras-layer-normalization 4 | h5py 5 | opencv-python 6 | tensorflow 7 | scikit-learn 8 | matplotlib -------------------------------------------------------------------------------- /train_liveness.py: -------------------------------------------------------------------------------- 1 | # USAGE 2 | # python train_liveness.py --dataset dataset --model liveness.model --le le.pickle 3 | 4 | # set the matplotlib backend so figures can be saved in the background 5 | import matplotlib 6 | matplotlib.use("Agg") 7 | 8 | # import the necessary packages 9 | from pyimagesearch.livenessnet import LivenessNet 10 | from sklearn.preprocessing import LabelEncoder 11 | from sklearn.model_selection import train_test_split 12 | from sklearn.metrics import classification_report 13 | from keras.preprocessing.image import ImageDataGenerator 14 | from keras.optimizers import Adam 15 | from keras.src.utils import np_utils 16 | from imutils import paths 17 | import matplotlib.pyplot as plt 18 | import numpy as np 19 | import argparse 20 | import pickle 21 | import cv2 22 | import os 23 | 24 | # construct the argument parser and parse the arguments 25 | ap = argparse.ArgumentParser() 26 | ap.add_argument("-d", "--dataset", required=True, 27 | help="path to input dataset") 28 | ap.add_argument("-m", "--model", type=str, required=True, 29 | help="path to trained model") 30 | ap.add_argument("-l", "--le", type=str, required=True, 31 | help="path to label encoder") 32 | ap.add_argument("-p", "--plot", type=str, default="plot.png", 33 | help="path to output loss/accuracy plot") 34 | args = vars(ap.parse_args()) 35 | 36 | # initialize the initial learning rate, batch size, and number of 37 | # epochs to train for 38 | INIT_LR = 1e-4 39 | BS = 8 40 | EPOCHS = 50 41 | 42 | # grab the list of images in our dataset directory, then initialize 43 | # the list of data (i.e., images) and class images 44 | print("[INFO] loading images...") 45 | imagePaths = list(paths.list_images(args["dataset"])) 46 | data = [] 47 | labels = [] 48 | 49 | for imagePath in imagePaths: 50 | # extract the class label from the filename, load the image and 51 | # resize it to be a fixed 32x32 pixels, ignoring aspect ratio 52 | label = imagePath.split(os.path.sep)[-2] 53 | image = cv2.imread(imagePath) 54 | #print(imagePath) 55 | image = cv2.resize(image, (32, 32)) 56 | 57 | # update the data and labels lists, respectively 58 | data.append(image) 59 | labels.append(label) 60 | 61 | # convert the data into a NumPy array, then preprocess it by scaling 62 | # all pixel intensities to the range [0, 1] 63 | data = np.array(data, dtype="float") / 255.0 64 | 65 | # encode the labels (which are currently strings) as integers and then 66 | # one-hot encode them 67 | le = LabelEncoder() 68 | labels = le.fit_transform(labels) 69 | labels = np_utils.to_categorical(labels, 2) 70 | 71 | # partition the data into training and testing splits using 75% of 72 | # the data for training and the remaining 25% for testing 73 | (trainX, testX, trainY, testY) = train_test_split(data, labels, 74 | test_size=0.25, random_state=42) 75 | 76 | # construct the training image generator for data augmentation 77 | aug = ImageDataGenerator(rotation_range=20, zoom_range=0.15, 78 | width_shift_range=0.2, height_shift_range=0.2, shear_range=0.15, 79 | horizontal_flip=True, fill_mode="nearest") 80 | 81 | # initialize the optimizer and model 82 | print("[INFO] compiling model...") 83 | opt = Adam(learning_rate=INIT_LR) 84 | model = LivenessNet.build(width=32, height=32, depth=3, 85 | classes=len(le.classes_)) 86 | model.compile(loss="binary_crossentropy", optimizer=opt, 87 | metrics=["accuracy"]) 88 | 89 | # train the network 90 | print("[INFO] training network for {} epochs...".format(EPOCHS)) 91 | H = model.fit_generator(aug.flow(trainX, trainY, batch_size=BS), 92 | validation_data=(testX, testY), steps_per_epoch=len(trainX) // BS, 93 | epochs=EPOCHS) 94 | 95 | # evaluate the network 96 | print("[INFO] evaluating network...") 97 | predictions = model.predict(testX, batch_size=BS) 98 | print(classification_report(testY.argmax(axis=1), 99 | predictions.argmax(axis=1), target_names=le.classes_)) 100 | 101 | # save the network to disk 102 | print("[INFO] serializing network to '{}'...".format(args["model"])) 103 | model.save(args["model"]) 104 | 105 | # save the label encoder to disk 106 | f = open(args["le"], "wb") 107 | f.write(pickle.dumps(le)) 108 | f.close() 109 | 110 | # plot the training loss and accuracy 111 | plt.style.use("ggplot") 112 | plt.figure() 113 | plt.plot(np.arange(0, EPOCHS), H.history["loss"], label="train_loss") 114 | plt.plot(np.arange(0, EPOCHS), H.history["val_loss"], label="val_loss") 115 | plt.plot(np.arange(0, EPOCHS), H.history["val_acc"], label="val_acc") 116 | plt.title("Training Loss and Accuracy on Dataset") 117 | plt.xlabel("Epoch #") 118 | plt.ylabel("Loss/Accuracy") 119 | plt.legend(loc="lower left") 120 | plt.savefig(args["plot"]) -------------------------------------------------------------------------------- /videos/git.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joytsay/livenessDetection/900a3fc3c1c23090857ae26fa2ae3720d9ec2f96/videos/git.keep -------------------------------------------------------------------------------- /webcam.py: -------------------------------------------------------------------------------- 1 | from imutils.video import VideoStream 2 | from keras.preprocessing.image import img_to_array 3 | from keras.models import load_model 4 | import numpy as np 5 | import argparse 6 | import imutils 7 | import pickle 8 | import time 9 | import cv2 10 | import os 11 | 12 | # construct the argument parse and parse the arguments 13 | ap = argparse.ArgumentParser() 14 | ap.add_argument("-m", "--model", type=str, required=True, 15 | help="path to trained model") 16 | ap.add_argument("-l", "--le", type=str, required=True, 17 | help="path to label encoder") 18 | ap.add_argument("-d", "--detector", type=str, required=True, 19 | help="path to OpenCV's deep learning face detector") 20 | ap.add_argument("-c", "--confidence", type=float, default=0.5, 21 | help="minimum probability to filter weak detections") 22 | args = vars(ap.parse_args()) 23 | 24 | # load our serialized face detector from disk 25 | print("[INFO] loading face detector...") 26 | protoPath = os.path.sep.join([args["detector"], "deploy.prototxt"]) 27 | modelPath = os.path.sep.join([args["detector"], 28 | "res10_300x300_ssd_iter_140000.caffemodel"]) 29 | net = cv2.dnn.readNetFromCaffe(protoPath, modelPath) 30 | 31 | # load the liveness detector model and label encoder from disk 32 | print("[INFO] loading liveness detector...") 33 | model = load_model(args["model"]) 34 | le = pickle.loads(open(args["le"], "rb").read()) 35 | 36 | cap = cv2.VideoCapture(0) 37 | cap.set(3,640) 38 | cap.set(4,480) 39 | 40 | fourcc = cv2.VideoWriter_fourcc(*'MP4V') 41 | out = cv2.VideoWriter('output.mp4', fourcc, 30.0, (640,480)) 42 | 43 | while(True): 44 | ret, frame = cap.read() 45 | 46 | # grab the frame from the threaded video stream and resize it 47 | # to have a maximum width of 600 pixels 48 | frame = imutils.resize(frame, height=480, width=640) 49 | 50 | # grab the frame dimensions and convert it to a blob 51 | (h, w) = frame.shape[:2] 52 | blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0, 53 | (300, 300), (104.0, 177.0, 123.0)) 54 | 55 | # pass the blob through the network and obtain the detections and 56 | # predictions 57 | net.setInput(blob) 58 | detections = net.forward() 59 | 60 | # loop over the detections 61 | for i in range(0, detections.shape[2]): 62 | # extract the confidence (i.e., probability) associated with the 63 | # prediction 64 | confidence = detections[0, 0, i, 2] 65 | 66 | # filter out weak detections 67 | if confidence > args["confidence"]: 68 | # compute the (x, y)-coordinates of the bounding box for 69 | # the face and extract the face ROI 70 | box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) 71 | (startX, startY, endX, endY) = box.astype("int") 72 | 73 | # ensure the detected bounding box does fall outside the 74 | # dimensions of the frame 75 | startX = max(0, startX) 76 | startY = max(0, startY) 77 | endX = min(w, endX) 78 | endY = min(h, endY) 79 | 80 | # extract the face ROI and then preproces it in the exact 81 | # same manner as our training data 82 | face = frame[startY:endY, startX:endX] 83 | face = cv2.resize(face, (32, 32)) 84 | face = face.astype("float") / 255.0 85 | face = img_to_array(face) 86 | face = np.expand_dims(face, axis=0) 87 | 88 | # pass the face ROI through the trained liveness detector 89 | # model to determine if the face is "real" or "fake" 90 | preds = model.predict(face)[0] 91 | j = np.argmax(preds) 92 | label = le.classes_[j] 93 | #print(preds) 94 | #print(j) 95 | #print(label) 96 | 97 | # draw the label and bounding box on the frame 98 | label = "{}: {:.4f}".format(label, preds[j]) 99 | 100 | if preds[j] > 0.50 and j == 1: 101 | cv2.rectangle(frame, (startX, startY), (endX, endY), 102 | (0, 255, 0), 2) 103 | _label = "Liveness: {:.4f}".format(preds[j]) 104 | cv2.putText(frame, _label, (startX, startY - 10), 105 | cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) 106 | else: 107 | cv2.rectangle(frame, (startX, startY), (endX, endY), 108 | (0, 0, 255), 2) 109 | _label = "Fake: {:.4f}".format(preds[j]) 110 | cv2.putText(frame, _label, (startX, startY - 10), 111 | cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) 112 | 113 | 114 | out.write(frame) 115 | cv2.imshow('frame', frame) 116 | c = cv2.waitKey(1) 117 | if c & 0xFF == ord('q'): 118 | break 119 | 120 | cap.release() 121 | out.release() 122 | cv2.destroyAllWindows() --------------------------------------------------------------------------------