├── .directory ├── .github └── workflows │ └── python_lint.yaml ├── .gitignore ├── .travis.yml ├── Procfile ├── README.md ├── assets ├── deploy.prototxt.txt ├── haarcascade_eye.xml ├── haarcascade_frontalface_default.xml ├── haarcascade_smile.xml ├── image.png ├── res10_300x300_ssd_iter_140000.caffemodel └── sample.jpg ├── filters ├── cat.png ├── dog.png ├── dog2.png ├── doggy_ears.png ├── doggy_nose.png ├── doggy_tongue.png ├── glasses.png ├── hat.png ├── mustache.png └── rainbow.png ├── main.py ├── manage.py ├── requirements.txt ├── settings.py ├── tests ├── __init__.py ├── test_case1.py ├── test_case2.py ├── test_case3.py └── test_case4.py ├── uploads └── sample.jpg └── utils ├── __init__.py ├── apply_mask.py ├── faceDetection_v1.py ├── faceDetection_v2.py ├── faceFilter_v1.py └── faceFilter_v2.py /.directory: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Icon=applications-development 3 | 4 | [Dolphin] 5 | Timestamp=2020,11,27,19,44,9 6 | Version=4 7 | VisibleRoles=Icons_text,Icons_size 8 | -------------------------------------------------------------------------------- /.github/workflows/python_lint.yaml: -------------------------------------------------------------------------------- 1 | on: [pull_request] 2 | 3 | jobs: 4 | lint: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Set up Python 9 | uses: actions/setup-python@v2 10 | with: 11 | python-version: '3.8' 12 | - name: Install flake8 13 | run: | 14 | python -m pip install --upgrade pip 15 | pip install flake8 16 | - name: Lint with flake8 17 | run: flake8 . --isolated --exclude=.cache,.venv,.svn,CVS,.bzr,.hg,.git,__pycache__,.tox,**/migrations/** --ignore=E203,W503,E501,F401 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | .vscode 131 | temp.jpeg 132 | test.py -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | 4 | dist: xenial 5 | 6 | language: python 7 | 8 | script: 9 | - python -m pytest -v -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: uvicorn manage:app --host=0.0.0.0 --port=${PORT:-5000} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opencv_facefilter_restapi 2 | OpenCV API for Face Detection and Face Filters with FastAPI/Flask. Hit A 🌟 Star in Right Corner. 3 | 4 | ## Usages 5 | 6 | [Interactive API](http://opencv-api.herokuapp.com/docs) 7 | 8 | 9 | ## API ENDPOINTS: 10 | 11 | ## Face Detection Version 1 12 | endpoint: api/v1/facedetection/ 13 | 14 | ## Face Detection Version 2 15 | endpoint: api/v2/facedetection/ 16 | 17 | ## Face Filter Version 1 18 | 19 | ## Face Filter Version 2 20 | 21 | 1. Upload Picture 22 | 2. Select mask from 1-3 and execute 23 | -------------------------------------------------------------------------------- /assets/deploy.prototxt.txt: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/assets/image.png -------------------------------------------------------------------------------- /assets/res10_300x300_ssd_iter_140000.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/assets/res10_300x300_ssd_iter_140000.caffemodel -------------------------------------------------------------------------------- /assets/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/assets/sample.jpg -------------------------------------------------------------------------------- /filters/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/cat.png -------------------------------------------------------------------------------- /filters/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/dog.png -------------------------------------------------------------------------------- /filters/dog2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/dog2.png -------------------------------------------------------------------------------- /filters/doggy_ears.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/doggy_ears.png -------------------------------------------------------------------------------- /filters/doggy_nose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/doggy_nose.png -------------------------------------------------------------------------------- /filters/doggy_tongue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/doggy_tongue.png -------------------------------------------------------------------------------- /filters/glasses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/glasses.png -------------------------------------------------------------------------------- /filters/hat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/hat.png -------------------------------------------------------------------------------- /filters/mustache.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/mustache.png -------------------------------------------------------------------------------- /filters/rainbow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/filters/rainbow.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | """ 2 | FastAPI-0.61.2 3 | 4 | Main Script for GET and POST Request Route. 5 | """ 6 | import os 7 | import cv2 8 | import shutil 9 | from PIL import Image 10 | from os.path import join as joinpath 11 | from fastapi import FastAPI, File, UploadFile, Form, status 12 | from fastapi.responses import FileResponse 13 | from fastapi.openapi.utils import get_openapi 14 | 15 | import settings 16 | from settings import UPLOADS_DIR, base_url 17 | from utils import faceDetectionv1, faceDetectionv2 18 | from utils import faceFilterv1, faceFilterv2 19 | 20 | app = FastAPI() 21 | 22 | 23 | @app.get("/") 24 | async def home(): 25 | """ OpenCV FaceFilter RestAPI""" 26 | return ( 27 | { 28 | "title": settings.title, 29 | "api_version": settings.api_version, 30 | "documentation": f"{settings.documentation_url}", 31 | "face_filter_v1": f"{base_url}/api/v1/facefilter", 32 | "face_detection_v1": f"{base_url}/api/v1/facedetection", 33 | "face_detection_v2": f"{base_url}/api/v2/facedetection", 34 | "author": "Deepak Raj", 35 | "github": "https://github.com/py-contributors", 36 | "email": "deepak008@live.com", 37 | "supported_image_type": "{Jpg, Png}", 38 | "time": settings.current_time, 39 | } 40 | ) 41 | 42 | 43 | @app.get('/api/v1/facedetection/') 44 | async def face_detection_version_1_get(): 45 | return ( 46 | { 47 | "title": "Face Detection Version 1", 48 | "API Version": settings.api_version, 49 | "status": "Create post request for face-detection", 50 | "documentation": f"{settings.documentation_url}", 51 | } 52 | ) 53 | 54 | 55 | @app.post('/api/v1/facedetection/', status_code=status.HTTP_201_CREATED) 56 | async def face_detection_version_1(image: UploadFile = File(None, 57 | media_type='image/jpeg')): 58 | if image.content_type == 'image/jpeg': 59 | filename = image.filename 60 | image_path = joinpath(UPLOADS_DIR, filename) 61 | with open(image_path, 'wb') as buffer: 62 | shutil.copyfileobj(image.file, buffer) 63 | 64 | preprocess_img, _ = faceDetectionv1(image_path) 65 | # change BGR image RGb 66 | preprocess_img = cv2.cvtColor(preprocess_img, cv2.COLOR_BGR2RGB) 67 | output_image = Image.fromarray(preprocess_img, "RGB") 68 | output_image.save(image_path) 69 | 70 | return { 71 | "title": settings.title, 72 | "api_version": settings.api_version, 73 | "file_name": filename, 74 | "output_image_url": f"{base_url}/uploads/{filename}", 75 | "time": settings.current_time, 76 | "documentation": f"{settings.documentation_url}", 77 | } 78 | return { 79 | 'status': 'Input File is not A Image' 80 | } 81 | 82 | 83 | @app.get('/api/v2/facedetection/') 84 | async def face_detection_version_2_get(): 85 | return ( 86 | { 87 | "title": "Face Detection Version 2", 88 | "API Version": settings.api_version, 89 | "status": "Create post request for face-detection", 90 | "documentation": f"{settings.documentation_url}", 91 | } 92 | ) 93 | 94 | 95 | @app.post('/api/v2/facedetection/', status_code=status.HTTP_201_CREATED) 96 | async def face_detection_version_2(image: UploadFile = File(None, 97 | media_type='image/jpeg')): 98 | if image.content_type == 'image/jpeg': 99 | filename = image.filename 100 | image_path = joinpath(UPLOADS_DIR, filename) 101 | with open(image_path, 'wb') as buffer: 102 | shutil.copyfileobj(image.file, buffer) 103 | 104 | preprocess_img = faceDetectionv2(image_path) 105 | # change BGR image RGb 106 | preprocess_img = cv2.cvtColor(preprocess_img, cv2.COLOR_BGR2RGB) 107 | output_image = Image.fromarray(preprocess_img, "RGB") 108 | output_image.save(image_path) 109 | 110 | return { 111 | "title": settings.title, 112 | "api_version": settings.api_version, 113 | "file_name": filename, 114 | "output_image_url": f"{base_url}/uploads/{filename}", 115 | "time": settings.current_time, 116 | "documentation": f"{settings.documentation_url}", 117 | } 118 | return { 119 | 'status': 'Input File is not A Image' 120 | } 121 | 122 | 123 | @app.get('/api/v1/facefilter/') 124 | async def face_filter_version_1_get(): 125 | return ( 126 | { 127 | "title": "Face Detection Version 1", 128 | "API Version": settings.api_version, 129 | "status": "Create post request for face-detection", 130 | "documentation": f"{settings.documentation_url}", 131 | } 132 | ) 133 | 134 | 135 | @app.post('/api/v1/facefilter/', status_code=status.HTTP_201_CREATED) 136 | async def face_filter_version_1(image: UploadFile = File(None, 137 | media_type='image/jpeg'), 138 | mask_num: int = Form(...)): 139 | if image.content_type == 'image/jpeg': 140 | filename = image.filename 141 | image_path = joinpath(UPLOADS_DIR, filename) 142 | with open(image_path, 'wb') as buffer: 143 | shutil.copyfileobj(image.file, buffer) 144 | 145 | preprocess_img = faceFilterv1(image_path, mask_num) 146 | # change BGR image RGb 147 | preprocess_img = cv2.cvtColor(preprocess_img, cv2.COLOR_BGR2RGB) 148 | output_image = Image.fromarray(preprocess_img, "RGB") 149 | output_image.save(image_path) 150 | 151 | return { 152 | "title": settings.title, 153 | "api_version": settings.api_version, 154 | "file_name": filename, 155 | "output_image_url": f"{base_url}/uploads/{filename}", 156 | "time": settings.current_time, 157 | "documentation": f"{settings.documentation_url}", 158 | } 159 | return { 160 | 'status': 'Input File is not A Image' 161 | } 162 | 163 | 164 | @app.post('/api/v1/facefilter/', status_code=status.HTTP_201_CREATED) 165 | async def face_filter_version_2(image: UploadFile = File(None, 166 | media_type='image/jpeg'), 167 | mask_num: int = Form(...)): 168 | 169 | if image.content_type == 'image/jpeg': 170 | filename = image.filename 171 | image_path = joinpath(UPLOADS_DIR, filename) 172 | with open(image_path, 'wb') as buffer: 173 | shutil.copyfileobj(image.file, buffer) 174 | 175 | preprocess_img = faceFilterv2(image_path, mask_num) 176 | # change BGR image RGb 177 | preprocess_img = cv2.cvtColor(preprocess_img, cv2.COLOR_BGR2RGB) 178 | output_image = Image.fromarray(preprocess_img, "RGB") 179 | output_image.save(image_path) 180 | 181 | return { 182 | "title": settings.title, 183 | "api_version": settings.api_version, 184 | "file_name": filename, 185 | "output_image_url": f"{base_url}/uploads/{filename}", 186 | "time": settings.current_time, 187 | "documentation": f"{settings.documentation_url}", 188 | } 189 | return { 190 | 'status': 'Input File is not A Image' 191 | } 192 | 193 | 194 | @app.get('/uploads/{image_dest}/') 195 | async def get_img_from_server(image_dest): 196 | return FileResponse(f"uploads/{image_dest}") 197 | 198 | 199 | @app.get("/uploads/{image_dest}/delete/") 200 | async def delete_image_from_server(image_dest): 201 | """ Delete image from serer """ 202 | try: 203 | os.remove(joinpath(UPLOADS_DIR, image_dest)) 204 | except Exception as error: 205 | print(error) 206 | return ({"file_name": image_dest, "delete_it_from_server": True}) 207 | 208 | 209 | @app.get("/command/delete/") 210 | async def delete_dir_from_server(): 211 | """ 212 | Command for recreate uploads directory,s 213 | It's not for production purpose. 214 | """ 215 | settings.recreate_uploads_dir() 216 | return ({"status": "clearning uploads folder"}) 217 | 218 | 219 | @app.get("/command/show/") 220 | async def show_dir_status(): 221 | """ Show content of uploads dir """ 222 | image_name = os.listdir(UPLOADS_DIR) 223 | return ( 224 | {"total_image": len(image_name), "image_name": image_name} 225 | ) 226 | 227 | 228 | """ empty the uploads dir if total no. of image is more than 50. """ 229 | if settings.num_of_image_on_server > 50: 230 | settings.recreate_uploads_dir() 231 | 232 | 233 | def custom_openapi(): 234 | if app.openapi_schema: 235 | return app.openapi_schema 236 | openapi_schema = get_openapi( 237 | title="OpenCV-API", 238 | version="0.0.2-alpha", 239 | description="Rest API for Face Detection and Face Filters using ", 240 | routes=app.routes, 241 | ) 242 | openapi_schema["info"]["x-logo"] = { 243 | "url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" 244 | } 245 | app.openapi_schema = openapi_schema 246 | return app.openapi_schema 247 | 248 | 249 | app.openapi = custom_openapi 250 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | import uvicorn 2 | 3 | from main import app 4 | 5 | if __name__ == "__main__": 6 | uvicorn.run(app) 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | chardet==3.0.4 2 | fastapi==0.61.2 3 | h11==0.11.0 4 | idna==2.10 5 | numpy==1.19.4 6 | opencv-python-headless==4.4.0.46 7 | Pillow==8.3.2 8 | pydantic==1.7.2 9 | python-multipart==0.0.5 10 | requests==2.25.0 11 | six==1.15.0 12 | starlette==0.13.6 13 | urllib3==1.26.2 14 | uvicorn==0.12.3 15 | aiofiles==0.6.0 16 | python-multipart -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | extra settings for flask api configurations 3 | """ 4 | 5 | import os 6 | import shutil 7 | from pathlib import Path 8 | from datetime import datetime 9 | from os.path import isdir, join as joinpath 10 | 11 | BASE_DIR = Path(__file__).resolve().parent 12 | 13 | UPLOADS_DIR = joinpath(BASE_DIR, "uploads") 14 | ASSETS_DIR = joinpath(BASE_DIR, "assets") 15 | FILTERS_DIR = joinpath(BASE_DIR, "filters") 16 | 17 | 18 | def create_directory(folder_name): 19 | """ Create new directory """ 20 | if not isdir(folder_name): 21 | os.mkdir(folder_name) 22 | 23 | 24 | def recreate_uploads_dir(): 25 | """ Recreate the entire uploads directory """ 26 | try: 27 | shutil.rmtree(joinpath(UPLOADS_DIR)), 28 | except Exception as error: 29 | print(error) 30 | create_directory("uploads") 31 | try: 32 | shutil.copy( 33 | joinpath(ASSETS_DIR, "sample.jpg"), 34 | joinpath(UPLOADS_DIR, "sample.jpg") 35 | ) 36 | except Exception as error: 37 | print(error) 38 | 39 | 40 | # creating uploads directory 41 | create_directory('uploads') 42 | 43 | """ Json data """ 44 | title = "OpenCV Rest API with FastAPI for Face Detection and Face Filters" 45 | api_version = "1.0.0" 46 | base_url = "https://face-filter-api.herokuapp.com/" 47 | documentation_url = "https://face-filter-api.herokuapp.com/docs" 48 | current_time = datetime.utcnow() 49 | num_of_image_on_server = len(os.listdir(UPLOADS_DIR)) 50 | 51 | MAX_CONTENT_LENGTH = 2097152 52 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_case1.py: -------------------------------------------------------------------------------- 1 | from fastapi.testclient import TestClient 2 | 3 | from manage import app 4 | 5 | client = TestClient(app) 6 | 7 | 8 | def test_app(): 9 | """ Test case for get Request """ 10 | response = client.get("/") 11 | 12 | assert response.status_code == 200 13 | assert type(response.json()) == dict 14 | 15 | """ Test case for face detection version 1 """ 16 | response = client.get("api/v1/facedetection") 17 | 18 | assert response.status_code == 200 19 | assert type(response.json()) == dict 20 | 21 | """ Test case for face detection version 2 """ 22 | response = client.get("api/v2/facedetection") 23 | 24 | assert response.status_code == 200 25 | assert type(response.json()) == dict 26 | 27 | """ Test case for Face Filter Version 1 """ 28 | response = client.get("api/v1/facefilter") 29 | 30 | assert response.status_code == 200 31 | assert type(response.json()) == dict 32 | 33 | """ Test case for get image """ 34 | response = client.get("/uploads/sample.jpg") 35 | 36 | assert response.status_code == 200 37 | # assert type(response.json()) == bytes 38 | 39 | """ Test case for show command """ 40 | response = client.get("/command/show") 41 | 42 | assert response.status_code == 200 43 | assert type(response.json()) == dict 44 | 45 | """ Test Case for delete command """ 46 | response = client.get("/command/delete") 47 | 48 | assert response.status_code == 200 49 | assert type(response.json()) == dict 50 | -------------------------------------------------------------------------------- /tests/test_case2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test Case 2 3 | 4 | Test Case Locally for post request 5 | """ 6 | import os 7 | 8 | from fastapi.testclient import TestClient 9 | 10 | from manage import app 11 | from settings import ASSETS_DIR 12 | 13 | client = TestClient(app) 14 | 15 | file_path = os.path.join(ASSETS_DIR, "sample.jpg") 16 | 17 | 18 | def test_app(): 19 | pass 20 | -------------------------------------------------------------------------------- /tests/test_case3.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test Case 3 3 | Test Case for Heroku GET request 4 | """ 5 | import requests 6 | 7 | from settings import base_url 8 | 9 | 10 | def test_app(): 11 | # test for get request 12 | response = requests.get(f"{base_url}") 13 | 14 | assert response.status_code == 200 15 | assert type(response.json()) == dict 16 | 17 | # Face Detection version 1 18 | response = requests.get(f"{base_url}/api/v1/facedetection") 19 | 20 | assert response.status_code == 200 21 | assert type(response.json()) == dict 22 | 23 | # Face Detection version 2 24 | response = requests.get(f"{base_url}/api/v2/facedetection") 25 | 26 | assert response.status_code == 200 27 | assert type(response.json()) == dict 28 | 29 | # Face Filter version 1 30 | response = requests.get(f"{base_url}/api/v1/facefilter") 31 | 32 | assert response.status_code == 200 33 | assert type(response.json()) == dict 34 | 35 | # get request test for access files 36 | response = requests.get(f"{base_url}/uploads/sample.jpg") 37 | 38 | assert response.status_code == 200 39 | 40 | # test for show command 41 | response = requests.get(f"{base_url}/command/show") 42 | 43 | assert response.status_code == 200 44 | assert type(response.json()) == dict 45 | -------------------------------------------------------------------------------- /tests/test_case4.py: -------------------------------------------------------------------------------- 1 | """ Test Case 4 2 | 3 | Test case for Heroku Post request 4 | """ 5 | import os 6 | 7 | from settings import ASSETS_DIR 8 | 9 | file_path = os.path.join(ASSETS_DIR, "sample.jpg") 10 | 11 | 12 | def test_app(): 13 | 14 | """ Test case for face detection version 1 """ 15 | pass 16 | -------------------------------------------------------------------------------- /uploads/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Py-Contributors/Face_Filter/ec6df4104b640bd554f66afa5adc0643537215c1/uploads/sample.jpg -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | from utils.faceDetection_v1 import faceDetectionv1 2 | from utils.faceDetection_v2 import faceDetectionv2 3 | 4 | from utils.faceFilter_v1 import faceFilterv1 5 | from utils.faceFilter_v2 import faceFilterv2 6 | 7 | __all__ = [ 8 | faceDetectionv1, 9 | faceDetectionv2, 10 | faceFilterv1, 11 | faceFilterv2 12 | ] 13 | -------------------------------------------------------------------------------- /utils/apply_mask.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | 4 | 5 | def applyMask(face: np.array, mask: int): 6 | """Add the mask to the provided face, and return the face with mask. 7 | 8 | args: 9 | face (np.array): Face image 10 | mask (int): Mask number to apply 11 | 12 | returns: 13 | face (np.array): Face image with mask applied 14 | 15 | """ 16 | mask_h, mask_w, _ = mask.shape 17 | face_h, face_w, _ = face.shape 18 | 19 | # Resize the mask to fit on face 20 | factor = min(face_h / mask_h, face_w / mask_w) 21 | new_mask_w = int(factor * mask_w) 22 | new_mask_h = int(factor * mask_h) 23 | new_mask_shape = (new_mask_w, new_mask_h) 24 | resized_mask = cv2.resize(mask, new_mask_shape) 25 | 26 | # Add mask to face - ensure mask is centered 27 | face_with_mask = face.copy() 28 | non_white_pixels = (resized_mask < 250).all(axis=2) 29 | off_h = int((face_h - new_mask_h) / 2) 30 | off_w = int((face_w - new_mask_w) / 2) 31 | face_with_mask[off_h: off_h + new_mask_h, off_w: off_w + new_mask_w][ 32 | non_white_pixels 33 | ] = resized_mask[non_white_pixels] 34 | 35 | return face_with_mask 36 | -------------------------------------------------------------------------------- /utils/faceDetection_v1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Face Detection V1 3 | 4 | Classifier Type : 5 | HarrCascade Frontal Face Classifier 6 | Haarcascade Eyes Classifier 7 | Haarcascade Smile Classifier 8 | 9 | Output : 10 | Image with rectangle mark on face 11 | Total number of detected face 12 | """ 13 | import os 14 | import cv2 15 | from settings import ASSETS_DIR 16 | 17 | 18 | face_path = os.path.join(ASSETS_DIR, "haarcascade_frontalface_default.xml") 19 | eye_path = os.path.join(ASSETS_DIR, "haarcascade_eye.xml") 20 | smile_path = os.path.join(ASSETS_DIR, "haarcascade_smile.xml") 21 | 22 | face_cascade = cv2.CascadeClassifier(face_path) 23 | eye_cascade = cv2.CascadeClassifier(eye_path) 24 | smile_cascade = cv2.CascadeClassifier(smile_path) 25 | 26 | 27 | def faceDetectionv1(img): 28 | """ 29 | Face Detection V1 using OpenCV Haar Cascade Frontal Face Classifier 30 | 31 | Args: 32 | img (numpy.ndarray): Image to detect face 33 | 34 | Returns: 35 | img (numpy.ndarray): Image with rectangle mark on face 36 | """ 37 | img = cv2.imread(img) 38 | 39 | gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) 40 | 41 | faces = face_cascade.detectMultiScale(gray, 1.4, 5) 42 | 43 | for face in faces: 44 | x, y, width, height = face 45 | # draw a rectangle for detection 46 | cv2.rectangle( 47 | img, 48 | (x, y), 49 | (x + width, y + height), 50 | (0, 0, 255), 51 | 1, 52 | ) 53 | roi_gray = gray[y: y + height, x: x + width] 54 | roi_color = img[y: y + height, x: x + width] 55 | 56 | smiles = smile_cascade.detectMultiScale(roi_gray) 57 | for (sx, sy, sw, sh) in smiles: 58 | cv2.rectangle(roi_color, 59 | (sx, sy), 60 | ((sx + sw), 61 | (sy + sh)), 62 | (0, 0, 255), 63 | 3) 64 | 65 | eyes = eye_cascade.detectMultiScale(roi_gray) 66 | for (ex, ey, ew, eh) in eyes: 67 | cv2.rectangle( 68 | roi_color, 69 | (ex, ey), 70 | (ex + ew, ey + eh), 71 | (0, 255, 0), 72 | 2, 73 | ) 74 | return img, len(faces) 75 | -------------------------------------------------------------------------------- /utils/faceDetection_v2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Face Detection V2 3 | 4 | Classifier Type : 5 | deploy.prototxt.txt 6 | res10_300x300_ssd_iter_140000 7 | 8 | Output : 9 | Image with rectangle mark on face 10 | """ 11 | import os 12 | import numpy as np 13 | import cv2 14 | 15 | from settings import ASSETS_DIR 16 | 17 | # Global Declarations 18 | proto = os.path.join(ASSETS_DIR, "deploy.prototxt.txt") 19 | model = os.path.join(ASSETS_DIR, "res10_300x300_ssd_iter_140000.caffemodel") 20 | confThresh = 0.8 21 | net = cv2.dnn.readNetFromCaffe(proto, model) 22 | 23 | 24 | def faceDetectionv2(imgPath): 25 | """ 26 | Face Detection V2 using OpenCV DNN 27 | 28 | Args: 29 | imgPath (str): Image path to detect face 30 | 31 | Returns: 32 | img (numpy.ndarray) : Image with rectangle mark on face 33 | """ 34 | img = cv2.imread(imgPath) 35 | 36 | (h, w) = img.shape[:2] 37 | blob = cv2.dnn.blobFromImage( 38 | cv2.resize(img, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0) 39 | ) 40 | 41 | net.setInput(blob) 42 | detections = net.forward() 43 | for i in range(0, detections.shape[2]): 44 | confidence = detections[0, 0, i, 2] 45 | if confidence < confThresh: 46 | continue 47 | 48 | box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) 49 | (startX, startY, endX, endY) = box.astype("int") 50 | cv2.rectangle(img, (startX, startY), (endX, endY), (0, 255, 0), 3) 51 | 52 | return img 53 | -------------------------------------------------------------------------------- /utils/faceFilter_v1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Face Filter V1 3 | 4 | Classifier Type : 5 | HarrCascade Frontal Face Classifier 6 | 7 | Output : 8 | Applied mask on face 9 | """ 10 | import os 11 | import cv2 12 | 13 | from utils.apply_mask import applyMask 14 | from settings import ASSETS_DIR 15 | 16 | 17 | def faceFilterv1(input_image: str, mask_num: int): 18 | """ 19 | Face Filter V1 using OpenCV Haar Cascade Frontal Face Classifier 20 | 21 | Args: 22 | input_image (str): Path to image 23 | mask_num (int): Mask number to apply 24 | 25 | Returns: 26 | img (numpy.ndarray): Image with mask applied 27 | """ 28 | 29 | input_image = cv2.imread(input_image) 30 | 31 | all_mask = { 32 | 1: "filters/dog.png", 33 | 2: "filters/cat.png", 34 | 3: "filters/dog2.png", 35 | } 36 | 37 | mask = cv2.imread(all_mask[int(mask_num)]) 38 | 39 | cascade = cv2.CascadeClassifier( 40 | os.path.join(ASSETS_DIR, "haarcascade_frontalface_default.xml") 41 | ) 42 | 43 | # Capture frame-by-frame 44 | frame_h, frame_w, _ = input_image.shape 45 | # Convert to black-and-white 46 | gray = cv2.cvtColor(input_image, cv2.COLOR_RGB2GRAY) 47 | blackwhite = cv2.equalizeHist(gray) 48 | # Detect faces by creating a rectangle around each face 49 | rects = cascade.detectMultiScale( 50 | blackwhite, 51 | scaleFactor=1.3, 52 | minNeighbors=4, 53 | minSize=(30, 30), 54 | flags=cv2.CASCADE_SCALE_IMAGE, 55 | ) 56 | # Iterate over each face and apply the mask 57 | for x, y, w, h in rects: 58 | # crop a frame slightly larger than the face 59 | y0, y1 = int(y - 0.25 * h), int(y + 0.75 * h) 60 | x0, x1 = x, x + w 61 | # give up if the cropped frame would be out-of-bounds 62 | if x0 < 0 or y0 < 0 or x1 > frame_w or y1 > frame_h: 63 | continue 64 | input_image[y0:y1, x0:x1] = applyMask(input_image[y0:y1, x0:x1], mask) 65 | return input_image 66 | -------------------------------------------------------------------------------- /utils/faceFilter_v2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Face Detection V2 3 | 4 | Classifier Type : 5 | deploy.prototxt.txt 6 | res10_300x300_ssd_iter_140000 7 | 8 | Output : 9 | Image with rectangle mark on face 10 | """ 11 | 12 | import os 13 | import numpy as np 14 | import cv2 15 | 16 | from utils.apply_mask import applyMask 17 | from settings import ASSETS_DIR 18 | 19 | 20 | # Global Declarations 21 | proto = os.path.join(ASSETS_DIR, "deploy.prototxt.txt") 22 | model = os.path.join(ASSETS_DIR, "res10_300x300_ssd_iter_140000.caffemodel") 23 | confThresh = 0.8 24 | net = cv2.dnn.readNetFromCaffe(proto, model) 25 | 26 | 27 | async def faceFilterv2(imgPath, mask_num): 28 | """ 29 | FaceFilter V2 using OpenCV DNN 30 | 31 | Args: 32 | imgPath (str): Path to image 33 | mask_num (int): Mask number to apply 34 | 35 | Returns: 36 | img (numpy.ndarray): Image with mask applied 37 | """ 38 | img = cv2.imread(imgPath) 39 | 40 | all_mask = { 41 | 1: "filters/dog.png", 42 | 2: "filters/cat.png", 43 | 3: "filters/dog2.png", 44 | } 45 | 46 | mask = cv2.imread(all_mask[int(mask_num)]) 47 | 48 | (h, w) = img.shape[:2] 49 | blob = cv2.dnn.blobFromImage( 50 | cv2.resize(img, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0) 51 | ) 52 | 53 | net.setInput(blob) 54 | detections = net.forward() 55 | for i in range(0, detections.shape[2]): 56 | confidence = detections[0, 0, i, 2] 57 | if confidence < confThresh: 58 | continue 59 | 60 | box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) 61 | (startX, startY, endX, endY) = box.astype("int") 62 | 63 | img[startY:endY, startX:endX] = applyMask(img[startY:endY, 64 | startX:endX], mask) 65 | 66 | return img 67 | --------------------------------------------------------------------------------