├── .gitignore ├── LICENSE ├── README.md ├── assets ├── images │ ├── friends │ │ ├── friends_01.jpg │ │ ├── friends_02.jpg │ │ ├── friends_03.jpg │ │ └── friends_04.jpg │ └── landscapes │ │ ├── landscape_01.jpg │ │ └── landscape_02.jpg └── videos │ └── faces.mp4 ├── environment.yml ├── models └── face_detector │ ├── deploy.prototxt.txt │ └── res10_300x300_ssd_iter_140000.caffemodel ├── pipeline ├── __init__.py ├── annotate_image.py ├── capture_images.py ├── capture_video.py ├── detect_faces.py ├── display_summary.py ├── display_video.py ├── libs │ ├── __init__.py │ ├── colors.py │ ├── face_detector.py │ ├── text.py │ ├── timeme.py │ └── utils.py ├── pipeline.py ├── save_faces.py ├── save_image.py ├── save_summary.py └── save_video.py ├── process_images.py ├── process_images_pipeline.py ├── process_video_pipeline.py ├── pytest.ini └── tests ├── __init__.py ├── config.py ├── pipeline ├── __init__.py ├── libs │ ├── __init__.py │ └── test_face_detector.py ├── test_capture_images.py ├── test_detect_faces.py └── test_pipeline.py └── test_environment.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Idea / PyCharm 2 | .idea/ 3 | 4 | # files from the mac 5 | .DS_Store 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *.class 11 | 12 | # Project dependant 13 | output/ 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jarek Gilewski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Image processing pipeline 2 | 3 | Modular image processing pipeline using OpenCV and Python generators. 4 | 5 | ## Setup environment 6 | 7 | This project is using [Conda](https://conda.io) for project environment management. 8 | 9 | Setup the project environment: 10 | 11 | $ conda env create -f environment.yml 12 | $ conda activate pipeline 13 | 14 | or update the environment if you `git pull` the repo: 15 | 16 | $ conda env update -f environment.yml 17 | 18 | ## Getting started 19 | 20 | For detailed description read the Medium stories in order: 21 | * [Modular image processing pipeline using OpenCV and Python generators](https://medium.com/deepvisionguru/modular-image-processing-pipeline-using-opencv-and-python-generators-9edca3ccb696) 22 | * [Video processing pipeline with OpenCV](https://medium.com/deepvisionguru/video-processing-pipeline-with-opencv-ac10187d75b) 23 | 24 | Don't forget to clap a bit if you like it. If you like it very much, you can clap a few times :) 25 | Thank you! 26 | 27 | ## Tests 28 | 29 | `pytest` is used as a test framework. All tests are stored in `tests` folder. Run the tests: 30 | 31 | ```bash 32 | $ pytest 33 | ``` 34 | 35 | ## Resources and Credits 36 | 37 | * For Unix like pipeline idea credits goes to this [Gist](https://gist.github.com/alexmacedo/1552724) 38 | * Source of the example images and videos is [pixbay](https://pixabay.com) 39 | * Some ideas and code snippets are borrowed from [pyimagesearch](https://www.pyimagesearch.com/) 40 | * Color constants from [Python Color Constants Module](https://www.webucator.com/blog/2015/03/python-color-constants-module/) 41 | 42 | ## License 43 | 44 | [MIT License](LICENSE) -------------------------------------------------------------------------------- /assets/images/friends/friends_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/assets/images/friends/friends_01.jpg -------------------------------------------------------------------------------- /assets/images/friends/friends_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/assets/images/friends/friends_02.jpg -------------------------------------------------------------------------------- /assets/images/friends/friends_03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/assets/images/friends/friends_03.jpg -------------------------------------------------------------------------------- /assets/images/friends/friends_04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/assets/images/friends/friends_04.jpg -------------------------------------------------------------------------------- /assets/images/landscapes/landscape_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/assets/images/landscapes/landscape_01.jpg -------------------------------------------------------------------------------- /assets/images/landscapes/landscape_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/assets/images/landscapes/landscape_02.jpg -------------------------------------------------------------------------------- /assets/videos/faces.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/assets/videos/faces.mp4 -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: pipeline 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.6.5 6 | - pip 7 | - numpy==1.17.2 8 | - tqdm=4.19.9 9 | - pytest==5.1.1 10 | - pip: 11 | - opencv-contrib-python==4.0.0.21 12 | -------------------------------------------------------------------------------- /models/face_detector/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 | -------------------------------------------------------------------------------- /models/face_detector/res10_300x300_ssd_iter_140000.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/models/face_detector/res10_300x300_ssd_iter_140000.caffemodel -------------------------------------------------------------------------------- /pipeline/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/pipeline/__init__.py -------------------------------------------------------------------------------- /pipeline/annotate_image.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | from pipeline.pipeline import Pipeline 4 | from pipeline.libs.colors import colors 5 | from pipeline.libs.text import put_text 6 | 7 | 8 | class AnnotateImage(Pipeline): 9 | """Pipeline task for image annotation.""" 10 | 11 | def __init__(self, dst): 12 | self.dst = dst 13 | super(AnnotateImage, self).__init__() 14 | 15 | def map(self, data): 16 | data = self.annotate_faces(data) 17 | 18 | return data 19 | 20 | def annotate_faces(self, data): 21 | """Annotates faces on the image with bounding box and confidence info.""" 22 | 23 | if "faces" not in data: # in the case we switch off the face detector 24 | return data 25 | 26 | annotated_image = data["image"].copy() 27 | faces = data["faces"] 28 | 29 | # Loop over the faces and draw a rectangle around each 30 | for i, face in enumerate(faces): 31 | box, confidence = face 32 | (x1, y1, x2, y2) = box.astype("int") 33 | cv2.rectangle(annotated_image, (x1, y1), (x2, y2), colors.get("green").to_bgr(), 2) 34 | put_text(annotated_image, f"{confidence:.2f}", (x1 - 1, y1), 35 | color=colors.get("white").to_bgr(), 36 | bg_color=colors.get("green").to_bgr(), 37 | org_pos="bl") 38 | 39 | data[self.dst] = annotated_image 40 | 41 | return data 42 | -------------------------------------------------------------------------------- /pipeline/capture_images.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | from pipeline.pipeline import Pipeline 4 | import pipeline.libs.utils as utils 5 | 6 | 7 | class CaptureImages(Pipeline): 8 | """Pipeline task to capture images from directory""" 9 | 10 | def __init__(self, src, valid_exts=(".jpg", ".png"), level=None): 11 | self.src = src 12 | self.valid_exts = valid_exts 13 | self.level = level 14 | 15 | super(CaptureImages, self).__init__() 16 | 17 | def generator(self): 18 | """Yields the image content and metadata.""" 19 | 20 | source = utils.list_files(self.src, self.valid_exts, self.level) 21 | while self.has_next(): 22 | try: 23 | image_file = next(source) 24 | image = cv2.imread(image_file) 25 | 26 | data = { 27 | "image_id": image_file, 28 | "image": image 29 | } 30 | 31 | if self.filter(data): 32 | yield self.map(data) 33 | except StopIteration: 34 | return 35 | -------------------------------------------------------------------------------- /pipeline/capture_video.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | from pipeline.pipeline import Pipeline 4 | 5 | 6 | class CaptureVideo(Pipeline): 7 | """Pipeline task to capture video stream from file or webcam.""" 8 | 9 | def __init__(self, src=0): 10 | self.cap = cv2.VideoCapture(src) 11 | if not self.cap.isOpened(): 12 | raise IOError(f"Cannot open video {src}") 13 | 14 | self.fps = int(self.cap.get(cv2.CAP_PROP_FPS)) 15 | self.frame_count = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) if isinstance(src, str) else -1 16 | 17 | super(CaptureVideo, self).__init__() 18 | 19 | def generator(self): 20 | """Yields the frame content and metadata.""" 21 | 22 | frame_idx = 0 23 | while self.has_next(): 24 | try: 25 | ret, image = self.cap.read() 26 | if not ret: 27 | # no frames has been grabbed 28 | break 29 | 30 | data = { 31 | "frame_idx": frame_idx, 32 | "image_id": f"{frame_idx:06d}", 33 | "image": image, 34 | } 35 | 36 | if self.filter(data): 37 | frame_idx += 1 38 | yield self.map(data) 39 | except StopIteration: 40 | return 41 | 42 | def cleanup(self): 43 | """Closes video file or capturing device. 44 | 45 | This function should be triggered after the pipeline completes. 46 | """ 47 | 48 | self.cap.release() 49 | -------------------------------------------------------------------------------- /pipeline/detect_faces.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from pipeline.pipeline import Pipeline 4 | from pipeline.libs.face_detector import FaceDetector 5 | 6 | 7 | class DetectFaces(Pipeline): 8 | """Pipeline task to detect faces from the image""" 9 | 10 | def __init__(self, prototxt, model, batch_size=1, confidence=0.5): 11 | self.detector = FaceDetector(prototxt, model, confidence=confidence) 12 | self.batch_size = batch_size 13 | 14 | super(DetectFaces, self).__init__() 15 | 16 | def generator(self): 17 | """Yields the image enriched with detected faces metadata""" 18 | 19 | batch = [] 20 | stop = False 21 | while self.has_next() and not stop: 22 | try: 23 | # Buffer the pipeline stream 24 | data = next(self.source) 25 | batch.append(data) 26 | except StopIteration: 27 | stop = True 28 | 29 | # Check if there is anything in batch. 30 | # Process it if the size match batch_size or there is the end of the input stream. 31 | if len(batch) and (len(batch) == self.batch_size or stop): 32 | # Prepare images batch 33 | images = [data["image"] for data in batch] 34 | # Detect faces on all images at once 35 | faces = self.detector.detect(images) 36 | 37 | # Extract the faces metadata and attache them to the proper image 38 | for image_idx, image_faces in faces.items(): 39 | batch[image_idx]["faces"] = image_faces 40 | 41 | # Yield all the data from buffer 42 | for data in batch: 43 | if self.filter(data): 44 | yield self.map(data) 45 | 46 | batch = [] 47 | -------------------------------------------------------------------------------- /pipeline/display_summary.py: -------------------------------------------------------------------------------- 1 | from pipeline.pipeline import Pipeline 2 | 3 | 4 | class DisplaySummary(Pipeline): 5 | def __init__(self): 6 | super(DisplaySummary, self).__init__() 7 | 8 | def map(self, data): 9 | image_id = data["image_id"] 10 | face_rects = data["faces"] 11 | 12 | print(f"[INFO] {image_id}: face detections {len(face_rects)}") 13 | 14 | return data 15 | -------------------------------------------------------------------------------- /pipeline/display_video.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | from pipeline.pipeline import Pipeline 4 | 5 | 6 | class DisplayVideo(Pipeline): 7 | """Pipeline task to display images as a video.""" 8 | 9 | def __init__(self, src, window_name=None, org=None): 10 | self.src = src 11 | self.window_name = window_name if window_name else src 12 | 13 | cv2.startWindowThread() 14 | cv2.namedWindow(self.window_name, cv2.WINDOW_AUTOSIZE) 15 | if org: 16 | # Set the window position 17 | x, y = org 18 | cv2.moveWindow(self.window_name, x, y) 19 | 20 | super(DisplayVideo, self).__init__() 21 | 22 | def map(self, data): 23 | image = data[self.src] 24 | 25 | cv2.imshow(self.window_name, image) 26 | 27 | # Exit? 28 | key = cv2.waitKey(1) & 0xFF 29 | # Esc key pressed or window closed? 30 | if key == 27 or cv2.getWindowProperty(self.window_name, cv2.WND_PROP_VISIBLE) < 1: 31 | raise StopIteration 32 | 33 | return data 34 | 35 | def cleanup(self): 36 | cv2.destroyWindow(self.window_name) 37 | -------------------------------------------------------------------------------- /pipeline/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/pipeline/libs/__init__.py -------------------------------------------------------------------------------- /pipeline/libs/colors.py: -------------------------------------------------------------------------------- 1 | """Provide RGB color constants and a colors dictionary with 2 | elements formatted: colors[colorname] = CONSTANT""" 3 | 4 | # Credits: https://www.webucator.com/blog/2015/03/python-color-constants-module/ 5 | 6 | from collections import namedtuple, OrderedDict 7 | 8 | Color = namedtuple("RGB", "red, green, blue") 9 | colors = {} # dict of colors 10 | 11 | 12 | class RGB(Color): 13 | def hex_format(self): 14 | """Returns color in hex format""" 15 | return "#{:02X}{:02X}{:02X}".format(self.red, self.green, self.blue) 16 | 17 | def to_bgr(self): 18 | return self.blue, self.green, self.red 19 | 20 | 21 | # Color Contants (http://cloford.com/resources/colours/500col.htm) 22 | ALICEBLUE = RGB(240, 248, 255) 23 | ANTIQUEWHITE = RGB(250, 235, 215) 24 | ANTIQUEWHITE1 = RGB(255, 239, 219) 25 | ANTIQUEWHITE2 = RGB(238, 223, 204) 26 | ANTIQUEWHITE3 = RGB(205, 192, 176) 27 | ANTIQUEWHITE4 = RGB(139, 131, 120) 28 | AQUA = RGB(0, 255, 255) 29 | AQUAMARINE1 = RGB(127, 255, 212) 30 | AQUAMARINE2 = RGB(118, 238, 198) 31 | AQUAMARINE3 = RGB(102, 205, 170) 32 | AQUAMARINE4 = RGB(69, 139, 116) 33 | AZURE1 = RGB(240, 255, 255) 34 | AZURE2 = RGB(224, 238, 238) 35 | AZURE3 = RGB(193, 205, 205) 36 | AZURE4 = RGB(131, 139, 139) 37 | BANANA = RGB(227, 207, 87) 38 | BEIGE = RGB(245, 245, 220) 39 | BISQUE1 = RGB(255, 228, 196) 40 | BISQUE2 = RGB(238, 213, 183) 41 | BISQUE3 = RGB(205, 183, 158) 42 | BISQUE4 = RGB(139, 125, 107) 43 | BLACK = RGB(0, 0, 0) 44 | BLANCHEDALMOND = RGB(255, 235, 205) 45 | BLUE = RGB(0, 0, 255) 46 | BLUE2 = RGB(0, 0, 238) 47 | BLUE3 = RGB(0, 0, 205) 48 | BLUE4 = RGB(0, 0, 139) 49 | BLUEVIOLET = RGB(138, 43, 226) 50 | BRICK = RGB(156, 102, 31) 51 | BROWN = RGB(165, 42, 42) 52 | BROWN1 = RGB(255, 64, 64) 53 | BROWN2 = RGB(238, 59, 59) 54 | BROWN3 = RGB(205, 51, 51) 55 | BROWN4 = RGB(139, 35, 35) 56 | BURLYWOOD = RGB(222, 184, 135) 57 | BURLYWOOD1 = RGB(255, 211, 155) 58 | BURLYWOOD2 = RGB(238, 197, 145) 59 | BURLYWOOD3 = RGB(205, 170, 125) 60 | BURLYWOOD4 = RGB(139, 115, 85) 61 | BURNTSIENNA = RGB(138, 54, 15) 62 | BURNTUMBER = RGB(138, 51, 36) 63 | CADETBLUE = RGB(95, 158, 160) 64 | CADETBLUE1 = RGB(152, 245, 255) 65 | CADETBLUE2 = RGB(142, 229, 238) 66 | CADETBLUE3 = RGB(122, 197, 205) 67 | CADETBLUE4 = RGB(83, 134, 139) 68 | CADMIUMORANGE = RGB(255, 97, 3) 69 | CADMIUMYELLOW = RGB(255, 153, 18) 70 | CARROT = RGB(237, 145, 33) 71 | CHARTREUSE1 = RGB(127, 255, 0) 72 | CHARTREUSE2 = RGB(118, 238, 0) 73 | CHARTREUSE3 = RGB(102, 205, 0) 74 | CHARTREUSE4 = RGB(69, 139, 0) 75 | CHOCOLATE = RGB(210, 105, 30) 76 | CHOCOLATE1 = RGB(255, 127, 36) 77 | CHOCOLATE2 = RGB(238, 118, 33) 78 | CHOCOLATE3 = RGB(205, 102, 29) 79 | CHOCOLATE4 = RGB(139, 69, 19) 80 | COBALT = RGB(61, 89, 171) 81 | COBALTGREEN = RGB(61, 145, 64) 82 | COLDGREY = RGB(128, 138, 135) 83 | CORAL = RGB(255, 127, 80) 84 | CORAL1 = RGB(255, 114, 86) 85 | CORAL2 = RGB(238, 106, 80) 86 | CORAL3 = RGB(205, 91, 69) 87 | CORAL4 = RGB(139, 62, 47) 88 | CORNFLOWERBLUE = RGB(100, 149, 237) 89 | CORNSILK1 = RGB(255, 248, 220) 90 | CORNSILK2 = RGB(238, 232, 205) 91 | CORNSILK3 = RGB(205, 200, 177) 92 | CORNSILK4 = RGB(139, 136, 120) 93 | CRIMSON = RGB(220, 20, 60) 94 | CYAN2 = RGB(0, 238, 238) 95 | CYAN3 = RGB(0, 205, 205) 96 | CYAN4 = RGB(0, 139, 139) 97 | DARKGOLDENROD = RGB(184, 134, 11) 98 | DARKGOLDENROD1 = RGB(255, 185, 15) 99 | DARKGOLDENROD2 = RGB(238, 173, 14) 100 | DARKGOLDENROD3 = RGB(205, 149, 12) 101 | DARKGOLDENROD4 = RGB(139, 101, 8) 102 | DARKGRAY = RGB(169, 169, 169) 103 | DARKGREEN = RGB(0, 100, 0) 104 | DARKKHAKI = RGB(189, 183, 107) 105 | DARKOLIVEGREEN = RGB(85, 107, 47) 106 | DARKOLIVEGREEN1 = RGB(202, 255, 112) 107 | DARKOLIVEGREEN2 = RGB(188, 238, 104) 108 | DARKOLIVEGREEN3 = RGB(162, 205, 90) 109 | DARKOLIVEGREEN4 = RGB(110, 139, 61) 110 | DARKORANGE = RGB(255, 140, 0) 111 | DARKORANGE1 = RGB(255, 127, 0) 112 | DARKORANGE2 = RGB(238, 118, 0) 113 | DARKORANGE3 = RGB(205, 102, 0) 114 | DARKORANGE4 = RGB(139, 69, 0) 115 | DARKORCHID = RGB(153, 50, 204) 116 | DARKORCHID1 = RGB(191, 62, 255) 117 | DARKORCHID2 = RGB(178, 58, 238) 118 | DARKORCHID3 = RGB(154, 50, 205) 119 | DARKORCHID4 = RGB(104, 34, 139) 120 | DARKSALMON = RGB(233, 150, 122) 121 | DARKSEAGREEN = RGB(143, 188, 143) 122 | DARKSEAGREEN1 = RGB(193, 255, 193) 123 | DARKSEAGREEN2 = RGB(180, 238, 180) 124 | DARKSEAGREEN3 = RGB(155, 205, 155) 125 | DARKSEAGREEN4 = RGB(105, 139, 105) 126 | DARKSLATEBLUE = RGB(72, 61, 139) 127 | DARKSLATEGRAY = RGB(47, 79, 79) 128 | DARKSLATEGRAY1 = RGB(151, 255, 255) 129 | DARKSLATEGRAY2 = RGB(141, 238, 238) 130 | DARKSLATEGRAY3 = RGB(121, 205, 205) 131 | DARKSLATEGRAY4 = RGB(82, 139, 139) 132 | DARKTURQUOISE = RGB(0, 206, 209) 133 | DARKVIOLET = RGB(148, 0, 211) 134 | DEEPPINK1 = RGB(255, 20, 147) 135 | DEEPPINK2 = RGB(238, 18, 137) 136 | DEEPPINK3 = RGB(205, 16, 118) 137 | DEEPPINK4 = RGB(139, 10, 80) 138 | DEEPSKYBLUE1 = RGB(0, 191, 255) 139 | DEEPSKYBLUE2 = RGB(0, 178, 238) 140 | DEEPSKYBLUE3 = RGB(0, 154, 205) 141 | DEEPSKYBLUE4 = RGB(0, 104, 139) 142 | DIMGRAY = RGB(105, 105, 105) 143 | DIMGRAY = RGB(105, 105, 105) 144 | DODGERBLUE1 = RGB(30, 144, 255) 145 | DODGERBLUE2 = RGB(28, 134, 238) 146 | DODGERBLUE3 = RGB(24, 116, 205) 147 | DODGERBLUE4 = RGB(16, 78, 139) 148 | EGGSHELL = RGB(252, 230, 201) 149 | EMERALDGREEN = RGB(0, 201, 87) 150 | FIREBRICK = RGB(178, 34, 34) 151 | FIREBRICK1 = RGB(255, 48, 48) 152 | FIREBRICK2 = RGB(238, 44, 44) 153 | FIREBRICK3 = RGB(205, 38, 38) 154 | FIREBRICK4 = RGB(139, 26, 26) 155 | FLESH = RGB(255, 125, 64) 156 | FLORALWHITE = RGB(255, 250, 240) 157 | FORESTGREEN = RGB(34, 139, 34) 158 | GAINSBORO = RGB(220, 220, 220) 159 | GHOSTWHITE = RGB(248, 248, 255) 160 | GOLD1 = RGB(255, 215, 0) 161 | GOLD2 = RGB(238, 201, 0) 162 | GOLD3 = RGB(205, 173, 0) 163 | GOLD4 = RGB(139, 117, 0) 164 | GOLDENROD = RGB(218, 165, 32) 165 | GOLDENROD1 = RGB(255, 193, 37) 166 | GOLDENROD2 = RGB(238, 180, 34) 167 | GOLDENROD3 = RGB(205, 155, 29) 168 | GOLDENROD4 = RGB(139, 105, 20) 169 | GRAY = RGB(128, 128, 128) 170 | GRAY1 = RGB(3, 3, 3) 171 | GRAY10 = RGB(26, 26, 26) 172 | GRAY11 = RGB(28, 28, 28) 173 | GRAY12 = RGB(31, 31, 31) 174 | GRAY13 = RGB(33, 33, 33) 175 | GRAY14 = RGB(36, 36, 36) 176 | GRAY15 = RGB(38, 38, 38) 177 | GRAY16 = RGB(41, 41, 41) 178 | GRAY17 = RGB(43, 43, 43) 179 | GRAY18 = RGB(46, 46, 46) 180 | GRAY19 = RGB(48, 48, 48) 181 | GRAY2 = RGB(5, 5, 5) 182 | GRAY20 = RGB(51, 51, 51) 183 | GRAY21 = RGB(54, 54, 54) 184 | GRAY22 = RGB(56, 56, 56) 185 | GRAY23 = RGB(59, 59, 59) 186 | GRAY24 = RGB(61, 61, 61) 187 | GRAY25 = RGB(64, 64, 64) 188 | GRAY26 = RGB(66, 66, 66) 189 | GRAY27 = RGB(69, 69, 69) 190 | GRAY28 = RGB(71, 71, 71) 191 | GRAY29 = RGB(74, 74, 74) 192 | GRAY3 = RGB(8, 8, 8) 193 | GRAY30 = RGB(77, 77, 77) 194 | GRAY31 = RGB(79, 79, 79) 195 | GRAY32 = RGB(82, 82, 82) 196 | GRAY33 = RGB(84, 84, 84) 197 | GRAY34 = RGB(87, 87, 87) 198 | GRAY35 = RGB(89, 89, 89) 199 | GRAY36 = RGB(92, 92, 92) 200 | GRAY37 = RGB(94, 94, 94) 201 | GRAY38 = RGB(97, 97, 97) 202 | GRAY39 = RGB(99, 99, 99) 203 | GRAY4 = RGB(10, 10, 10) 204 | GRAY40 = RGB(102, 102, 102) 205 | GRAY42 = RGB(107, 107, 107) 206 | GRAY43 = RGB(110, 110, 110) 207 | GRAY44 = RGB(112, 112, 112) 208 | GRAY45 = RGB(115, 115, 115) 209 | GRAY46 = RGB(117, 117, 117) 210 | GRAY47 = RGB(120, 120, 120) 211 | GRAY48 = RGB(122, 122, 122) 212 | GRAY49 = RGB(125, 125, 125) 213 | GRAY5 = RGB(13, 13, 13) 214 | GRAY50 = RGB(127, 127, 127) 215 | GRAY51 = RGB(130, 130, 130) 216 | GRAY52 = RGB(133, 133, 133) 217 | GRAY53 = RGB(135, 135, 135) 218 | GRAY54 = RGB(138, 138, 138) 219 | GRAY55 = RGB(140, 140, 140) 220 | GRAY56 = RGB(143, 143, 143) 221 | GRAY57 = RGB(145, 145, 145) 222 | GRAY58 = RGB(148, 148, 148) 223 | GRAY59 = RGB(150, 150, 150) 224 | GRAY6 = RGB(15, 15, 15) 225 | GRAY60 = RGB(153, 153, 153) 226 | GRAY61 = RGB(156, 156, 156) 227 | GRAY62 = RGB(158, 158, 158) 228 | GRAY63 = RGB(161, 161, 161) 229 | GRAY64 = RGB(163, 163, 163) 230 | GRAY65 = RGB(166, 166, 166) 231 | GRAY66 = RGB(168, 168, 168) 232 | GRAY67 = RGB(171, 171, 171) 233 | GRAY68 = RGB(173, 173, 173) 234 | GRAY69 = RGB(176, 176, 176) 235 | GRAY7 = RGB(18, 18, 18) 236 | GRAY70 = RGB(179, 179, 179) 237 | GRAY71 = RGB(181, 181, 181) 238 | GRAY72 = RGB(184, 184, 184) 239 | GRAY73 = RGB(186, 186, 186) 240 | GRAY74 = RGB(189, 189, 189) 241 | GRAY75 = RGB(191, 191, 191) 242 | GRAY76 = RGB(194, 194, 194) 243 | GRAY77 = RGB(196, 196, 196) 244 | GRAY78 = RGB(199, 199, 199) 245 | GRAY79 = RGB(201, 201, 201) 246 | GRAY8 = RGB(20, 20, 20) 247 | GRAY80 = RGB(204, 204, 204) 248 | GRAY81 = RGB(207, 207, 207) 249 | GRAY82 = RGB(209, 209, 209) 250 | GRAY83 = RGB(212, 212, 212) 251 | GRAY84 = RGB(214, 214, 214) 252 | GRAY85 = RGB(217, 217, 217) 253 | GRAY86 = RGB(219, 219, 219) 254 | GRAY87 = RGB(222, 222, 222) 255 | GRAY88 = RGB(224, 224, 224) 256 | GRAY89 = RGB(227, 227, 227) 257 | GRAY9 = RGB(23, 23, 23) 258 | GRAY90 = RGB(229, 229, 229) 259 | GRAY91 = RGB(232, 232, 232) 260 | GRAY92 = RGB(235, 235, 235) 261 | GRAY93 = RGB(237, 237, 237) 262 | GRAY94 = RGB(240, 240, 240) 263 | GRAY95 = RGB(242, 242, 242) 264 | GRAY97 = RGB(247, 247, 247) 265 | GRAY98 = RGB(250, 250, 250) 266 | GRAY99 = RGB(252, 252, 252) 267 | GREEN = RGB(0, 128, 0) 268 | GREEN1 = RGB(0, 255, 0) 269 | GREEN2 = RGB(0, 238, 0) 270 | GREEN3 = RGB(0, 205, 0) 271 | GREEN4 = RGB(0, 139, 0) 272 | GREENYELLOW = RGB(173, 255, 47) 273 | HONEYDEW1 = RGB(240, 255, 240) 274 | HONEYDEW2 = RGB(224, 238, 224) 275 | HONEYDEW3 = RGB(193, 205, 193) 276 | HONEYDEW4 = RGB(131, 139, 131) 277 | HOTPINK = RGB(255, 105, 180) 278 | HOTPINK1 = RGB(255, 110, 180) 279 | HOTPINK2 = RGB(238, 106, 167) 280 | HOTPINK3 = RGB(205, 96, 144) 281 | HOTPINK4 = RGB(139, 58, 98) 282 | INDIANRED = RGB(176, 23, 31) 283 | INDIANRED = RGB(205, 92, 92) 284 | INDIANRED1 = RGB(255, 106, 106) 285 | INDIANRED2 = RGB(238, 99, 99) 286 | INDIANRED3 = RGB(205, 85, 85) 287 | INDIANRED4 = RGB(139, 58, 58) 288 | INDIGO = RGB(75, 0, 130) 289 | IVORY1 = RGB(255, 255, 240) 290 | IVORY2 = RGB(238, 238, 224) 291 | IVORY3 = RGB(205, 205, 193) 292 | IVORY4 = RGB(139, 139, 131) 293 | IVORYBLACK = RGB(41, 36, 33) 294 | KHAKI = RGB(240, 230, 140) 295 | KHAKI1 = RGB(255, 246, 143) 296 | KHAKI2 = RGB(238, 230, 133) 297 | KHAKI3 = RGB(205, 198, 115) 298 | KHAKI4 = RGB(139, 134, 78) 299 | LAVENDER = RGB(230, 230, 250) 300 | LAVENDERBLUSH1 = RGB(255, 240, 245) 301 | LAVENDERBLUSH2 = RGB(238, 224, 229) 302 | LAVENDERBLUSH3 = RGB(205, 193, 197) 303 | LAVENDERBLUSH4 = RGB(139, 131, 134) 304 | LAWNGREEN = RGB(124, 252, 0) 305 | LEMONCHIFFON1 = RGB(255, 250, 205) 306 | LEMONCHIFFON2 = RGB(238, 233, 191) 307 | LEMONCHIFFON3 = RGB(205, 201, 165) 308 | LEMONCHIFFON4 = RGB(139, 137, 112) 309 | LIGHTBLUE = RGB(173, 216, 230) 310 | LIGHTBLUE1 = RGB(191, 239, 255) 311 | LIGHTBLUE2 = RGB(178, 223, 238) 312 | LIGHTBLUE3 = RGB(154, 192, 205) 313 | LIGHTBLUE4 = RGB(104, 131, 139) 314 | LIGHTCORAL = RGB(240, 128, 128) 315 | LIGHTCYAN1 = RGB(224, 255, 255) 316 | LIGHTCYAN2 = RGB(209, 238, 238) 317 | LIGHTCYAN3 = RGB(180, 205, 205) 318 | LIGHTCYAN4 = RGB(122, 139, 139) 319 | LIGHTGOLDENROD1 = RGB(255, 236, 139) 320 | LIGHTGOLDENROD2 = RGB(238, 220, 130) 321 | LIGHTGOLDENROD3 = RGB(205, 190, 112) 322 | LIGHTGOLDENROD4 = RGB(139, 129, 76) 323 | LIGHTGOLDENRODYELLOW = RGB(250, 250, 210) 324 | LIGHTGREY = RGB(211, 211, 211) 325 | LIGHTPINK = RGB(255, 182, 193) 326 | LIGHTPINK1 = RGB(255, 174, 185) 327 | LIGHTPINK2 = RGB(238, 162, 173) 328 | LIGHTPINK3 = RGB(205, 140, 149) 329 | LIGHTPINK4 = RGB(139, 95, 101) 330 | LIGHTSALMON1 = RGB(255, 160, 122) 331 | LIGHTSALMON2 = RGB(238, 149, 114) 332 | LIGHTSALMON3 = RGB(205, 129, 98) 333 | LIGHTSALMON4 = RGB(139, 87, 66) 334 | LIGHTSEAGREEN = RGB(32, 178, 170) 335 | LIGHTSKYBLUE = RGB(135, 206, 250) 336 | LIGHTSKYBLUE1 = RGB(176, 226, 255) 337 | LIGHTSKYBLUE2 = RGB(164, 211, 238) 338 | LIGHTSKYBLUE3 = RGB(141, 182, 205) 339 | LIGHTSKYBLUE4 = RGB(96, 123, 139) 340 | LIGHTSLATEBLUE = RGB(132, 112, 255) 341 | LIGHTSLATEGRAY = RGB(119, 136, 153) 342 | LIGHTSTEELBLUE = RGB(176, 196, 222) 343 | LIGHTSTEELBLUE1 = RGB(202, 225, 255) 344 | LIGHTSTEELBLUE2 = RGB(188, 210, 238) 345 | LIGHTSTEELBLUE3 = RGB(162, 181, 205) 346 | LIGHTSTEELBLUE4 = RGB(110, 123, 139) 347 | LIGHTYELLOW1 = RGB(255, 255, 224) 348 | LIGHTYELLOW2 = RGB(238, 238, 209) 349 | LIGHTYELLOW3 = RGB(205, 205, 180) 350 | LIGHTYELLOW4 = RGB(139, 139, 122) 351 | LIMEGREEN = RGB(50, 205, 50) 352 | LINEN = RGB(250, 240, 230) 353 | MAGENTA = RGB(255, 0, 255) 354 | MAGENTA2 = RGB(238, 0, 238) 355 | MAGENTA3 = RGB(205, 0, 205) 356 | MAGENTA4 = RGB(139, 0, 139) 357 | MANGANESEBLUE = RGB(3, 168, 158) 358 | MAROON = RGB(128, 0, 0) 359 | MAROON1 = RGB(255, 52, 179) 360 | MAROON2 = RGB(238, 48, 167) 361 | MAROON3 = RGB(205, 41, 144) 362 | MAROON4 = RGB(139, 28, 98) 363 | MEDIUMORCHID = RGB(186, 85, 211) 364 | MEDIUMORCHID1 = RGB(224, 102, 255) 365 | MEDIUMORCHID2 = RGB(209, 95, 238) 366 | MEDIUMORCHID3 = RGB(180, 82, 205) 367 | MEDIUMORCHID4 = RGB(122, 55, 139) 368 | MEDIUMPURPLE = RGB(147, 112, 219) 369 | MEDIUMPURPLE1 = RGB(171, 130, 255) 370 | MEDIUMPURPLE2 = RGB(159, 121, 238) 371 | MEDIUMPURPLE3 = RGB(137, 104, 205) 372 | MEDIUMPURPLE4 = RGB(93, 71, 139) 373 | MEDIUMSEAGREEN = RGB(60, 179, 113) 374 | MEDIUMSLATEBLUE = RGB(123, 104, 238) 375 | MEDIUMSPRINGGREEN = RGB(0, 250, 154) 376 | MEDIUMTURQUOISE = RGB(72, 209, 204) 377 | MEDIUMVIOLETRED = RGB(199, 21, 133) 378 | MELON = RGB(227, 168, 105) 379 | MIDNIGHTBLUE = RGB(25, 25, 112) 380 | MINT = RGB(189, 252, 201) 381 | MINTCREAM = RGB(245, 255, 250) 382 | MISTYROSE1 = RGB(255, 228, 225) 383 | MISTYROSE2 = RGB(238, 213, 210) 384 | MISTYROSE3 = RGB(205, 183, 181) 385 | MISTYROSE4 = RGB(139, 125, 123) 386 | MOCCASIN = RGB(255, 228, 181) 387 | NAVAJOWHITE1 = RGB(255, 222, 173) 388 | NAVAJOWHITE2 = RGB(238, 207, 161) 389 | NAVAJOWHITE3 = RGB(205, 179, 139) 390 | NAVAJOWHITE4 = RGB(139, 121, 94) 391 | NAVY = RGB(0, 0, 128) 392 | OLDLACE = RGB(253, 245, 230) 393 | OLIVE = RGB(128, 128, 0) 394 | OLIVEDRAB = RGB(107, 142, 35) 395 | OLIVEDRAB1 = RGB(192, 255, 62) 396 | OLIVEDRAB2 = RGB(179, 238, 58) 397 | OLIVEDRAB3 = RGB(154, 205, 50) 398 | OLIVEDRAB4 = RGB(105, 139, 34) 399 | ORANGE = RGB(255, 128, 0) 400 | ORANGE1 = RGB(255, 165, 0) 401 | ORANGE2 = RGB(238, 154, 0) 402 | ORANGE3 = RGB(205, 133, 0) 403 | ORANGE4 = RGB(139, 90, 0) 404 | ORANGERED1 = RGB(255, 69, 0) 405 | ORANGERED2 = RGB(238, 64, 0) 406 | ORANGERED3 = RGB(205, 55, 0) 407 | ORANGERED4 = RGB(139, 37, 0) 408 | ORCHID = RGB(218, 112, 214) 409 | ORCHID1 = RGB(255, 131, 250) 410 | ORCHID2 = RGB(238, 122, 233) 411 | ORCHID3 = RGB(205, 105, 201) 412 | ORCHID4 = RGB(139, 71, 137) 413 | PALEGOLDENROD = RGB(238, 232, 170) 414 | PALEGREEN = RGB(152, 251, 152) 415 | PALEGREEN1 = RGB(154, 255, 154) 416 | PALEGREEN2 = RGB(144, 238, 144) 417 | PALEGREEN3 = RGB(124, 205, 124) 418 | PALEGREEN4 = RGB(84, 139, 84) 419 | PALETURQUOISE1 = RGB(187, 255, 255) 420 | PALETURQUOISE2 = RGB(174, 238, 238) 421 | PALETURQUOISE3 = RGB(150, 205, 205) 422 | PALETURQUOISE4 = RGB(102, 139, 139) 423 | PALEVIOLETRED = RGB(219, 112, 147) 424 | PALEVIOLETRED1 = RGB(255, 130, 171) 425 | PALEVIOLETRED2 = RGB(238, 121, 159) 426 | PALEVIOLETRED3 = RGB(205, 104, 137) 427 | PALEVIOLETRED4 = RGB(139, 71, 93) 428 | PAPAYAWHIP = RGB(255, 239, 213) 429 | PEACHPUFF1 = RGB(255, 218, 185) 430 | PEACHPUFF2 = RGB(238, 203, 173) 431 | PEACHPUFF3 = RGB(205, 175, 149) 432 | PEACHPUFF4 = RGB(139, 119, 101) 433 | PEACOCK = RGB(51, 161, 201) 434 | PINK = RGB(255, 192, 203) 435 | PINK1 = RGB(255, 181, 197) 436 | PINK2 = RGB(238, 169, 184) 437 | PINK3 = RGB(205, 145, 158) 438 | PINK4 = RGB(139, 99, 108) 439 | PLUM = RGB(221, 160, 221) 440 | PLUM1 = RGB(255, 187, 255) 441 | PLUM2 = RGB(238, 174, 238) 442 | PLUM3 = RGB(205, 150, 205) 443 | PLUM4 = RGB(139, 102, 139) 444 | POWDERBLUE = RGB(176, 224, 230) 445 | PURPLE = RGB(128, 0, 128) 446 | PURPLE1 = RGB(155, 48, 255) 447 | PURPLE2 = RGB(145, 44, 238) 448 | PURPLE3 = RGB(125, 38, 205) 449 | PURPLE4 = RGB(85, 26, 139) 450 | RASPBERRY = RGB(135, 38, 87) 451 | RAWSIENNA = RGB(199, 97, 20) 452 | RED = RGB(255, 0, 0) 453 | RED1 = RGB(238, 0, 0) 454 | RED2 = RGB(205, 0, 0) 455 | RED3 = RGB(139, 0, 0) 456 | ROSYBROWN = RGB(188, 143, 143) 457 | ROSYBROWN1 = RGB(255, 193, 193) 458 | ROSYBROWN2 = RGB(238, 180, 180) 459 | ROSYBROWN3 = RGB(205, 155, 155) 460 | ROSYBROWN4 = RGB(139, 105, 105) 461 | ROYALBLUE = RGB(65, 105, 225) 462 | ROYALBLUE1 = RGB(72, 118, 255) 463 | ROYALBLUE2 = RGB(67, 110, 238) 464 | ROYALBLUE3 = RGB(58, 95, 205) 465 | ROYALBLUE4 = RGB(39, 64, 139) 466 | SALMON = RGB(250, 128, 114) 467 | SALMON1 = RGB(255, 140, 105) 468 | SALMON2 = RGB(238, 130, 98) 469 | SALMON3 = RGB(205, 112, 84) 470 | SALMON4 = RGB(139, 76, 57) 471 | SANDYBROWN = RGB(244, 164, 96) 472 | SAPGREEN = RGB(48, 128, 20) 473 | SEAGREEN1 = RGB(84, 255, 159) 474 | SEAGREEN2 = RGB(78, 238, 148) 475 | SEAGREEN3 = RGB(67, 205, 128) 476 | SEAGREEN4 = RGB(46, 139, 87) 477 | SEASHELL1 = RGB(255, 245, 238) 478 | SEASHELL2 = RGB(238, 229, 222) 479 | SEASHELL3 = RGB(205, 197, 191) 480 | SEASHELL4 = RGB(139, 134, 130) 481 | SEPIA = RGB(94, 38, 18) 482 | SGIBEET = RGB(142, 56, 142) 483 | SGIBRIGHTGRAY = RGB(197, 193, 170) 484 | SGICHARTREUSE = RGB(113, 198, 113) 485 | SGIDARKGRAY = RGB(85, 85, 85) 486 | SGIGRAY12 = RGB(30, 30, 30) 487 | SGIGRAY16 = RGB(40, 40, 40) 488 | SGIGRAY32 = RGB(81, 81, 81) 489 | SGIGRAY36 = RGB(91, 91, 91) 490 | SGIGRAY52 = RGB(132, 132, 132) 491 | SGIGRAY56 = RGB(142, 142, 142) 492 | SGIGRAY72 = RGB(183, 183, 183) 493 | SGIGRAY76 = RGB(193, 193, 193) 494 | SGIGRAY92 = RGB(234, 234, 234) 495 | SGIGRAY96 = RGB(244, 244, 244) 496 | SGILIGHTBLUE = RGB(125, 158, 192) 497 | SGILIGHTGRAY = RGB(170, 170, 170) 498 | SGIOLIVEDRAB = RGB(142, 142, 56) 499 | SGISALMON = RGB(198, 113, 113) 500 | SGISLATEBLUE = RGB(113, 113, 198) 501 | SGITEAL = RGB(56, 142, 142) 502 | SIENNA = RGB(160, 82, 45) 503 | SIENNA1 = RGB(255, 130, 71) 504 | SIENNA2 = RGB(238, 121, 66) 505 | SIENNA3 = RGB(205, 104, 57) 506 | SIENNA4 = RGB(139, 71, 38) 507 | SILVER = RGB(192, 192, 192) 508 | SKYBLUE = RGB(135, 206, 235) 509 | SKYBLUE1 = RGB(135, 206, 255) 510 | SKYBLUE2 = RGB(126, 192, 238) 511 | SKYBLUE3 = RGB(108, 166, 205) 512 | SKYBLUE4 = RGB(74, 112, 139) 513 | SLATEBLUE = RGB(106, 90, 205) 514 | SLATEBLUE1 = RGB(131, 111, 255) 515 | SLATEBLUE2 = RGB(122, 103, 238) 516 | SLATEBLUE3 = RGB(105, 89, 205) 517 | SLATEBLUE4 = RGB(71, 60, 139) 518 | SLATEGRAY = RGB(112, 128, 144) 519 | SLATEGRAY1 = RGB(198, 226, 255) 520 | SLATEGRAY2 = RGB(185, 211, 238) 521 | SLATEGRAY3 = RGB(159, 182, 205) 522 | SLATEGRAY4 = RGB(108, 123, 139) 523 | SNOW1 = RGB(255, 250, 250) 524 | SNOW2 = RGB(238, 233, 233) 525 | SNOW3 = RGB(205, 201, 201) 526 | SNOW4 = RGB(139, 137, 137) 527 | SPRINGGREEN = RGB(0, 255, 127) 528 | SPRINGGREEN1 = RGB(0, 238, 118) 529 | SPRINGGREEN2 = RGB(0, 205, 102) 530 | SPRINGGREEN3 = RGB(0, 139, 69) 531 | STEELBLUE = RGB(70, 130, 180) 532 | STEELBLUE1 = RGB(99, 184, 255) 533 | STEELBLUE2 = RGB(92, 172, 238) 534 | STEELBLUE3 = RGB(79, 148, 205) 535 | STEELBLUE4 = RGB(54, 100, 139) 536 | TAN = RGB(210, 180, 140) 537 | TAN1 = RGB(255, 165, 79) 538 | TAN2 = RGB(238, 154, 73) 539 | TAN3 = RGB(205, 133, 63) 540 | TAN4 = RGB(139, 90, 43) 541 | TEAL = RGB(0, 128, 128) 542 | THISTLE = RGB(216, 191, 216) 543 | THISTLE1 = RGB(255, 225, 255) 544 | THISTLE2 = RGB(238, 210, 238) 545 | THISTLE3 = RGB(205, 181, 205) 546 | THISTLE4 = RGB(139, 123, 139) 547 | TOMATO1 = RGB(255, 99, 71) 548 | TOMATO2 = RGB(238, 92, 66) 549 | TOMATO3 = RGB(205, 79, 57) 550 | TOMATO4 = RGB(139, 54, 38) 551 | TURQUOISE = RGB(64, 224, 208) 552 | TURQUOISE1 = RGB(0, 245, 255) 553 | TURQUOISE2 = RGB(0, 229, 238) 554 | TURQUOISE3 = RGB(0, 197, 205) 555 | TURQUOISE4 = RGB(0, 134, 139) 556 | TURQUOISEBLUE = RGB(0, 199, 140) 557 | VIOLET = RGB(238, 130, 238) 558 | VIOLETRED = RGB(208, 32, 144) 559 | VIOLETRED1 = RGB(255, 62, 150) 560 | VIOLETRED2 = RGB(238, 58, 140) 561 | VIOLETRED3 = RGB(205, 50, 120) 562 | VIOLETRED4 = RGB(139, 34, 82) 563 | WARMGREY = RGB(128, 128, 105) 564 | WHEAT = RGB(245, 222, 179) 565 | WHEAT1 = RGB(255, 231, 186) 566 | WHEAT2 = RGB(238, 216, 174) 567 | WHEAT3 = RGB(205, 186, 150) 568 | WHEAT4 = RGB(139, 126, 102) 569 | WHITE = RGB(255, 255, 255) 570 | WHITESMOKE = RGB(245, 245, 245) 571 | WHITESMOKE = RGB(245, 245, 245) 572 | YELLOW1 = RGB(255, 255, 0) 573 | YELLOW2 = RGB(238, 238, 0) 574 | YELLOW3 = RGB(205, 205, 0) 575 | YELLOW4 = RGB(139, 139, 0) 576 | 577 | #Add colors to colors dictionary 578 | colors['aliceblue'] = ALICEBLUE 579 | colors['antiquewhite'] = ANTIQUEWHITE 580 | colors['antiquewhite1'] = ANTIQUEWHITE1 581 | colors['antiquewhite2'] = ANTIQUEWHITE2 582 | colors['antiquewhite3'] = ANTIQUEWHITE3 583 | colors['antiquewhite4'] = ANTIQUEWHITE4 584 | colors['aqua'] = AQUA 585 | colors['aquamarine1'] = AQUAMARINE1 586 | colors['aquamarine2'] = AQUAMARINE2 587 | colors['aquamarine3'] = AQUAMARINE3 588 | colors['aquamarine4'] = AQUAMARINE4 589 | colors['azure1'] = AZURE1 590 | colors['azure2'] = AZURE2 591 | colors['azure3'] = AZURE3 592 | colors['azure4'] = AZURE4 593 | colors['banana'] = BANANA 594 | colors['beige'] = BEIGE 595 | colors['bisque1'] = BISQUE1 596 | colors['bisque2'] = BISQUE2 597 | colors['bisque3'] = BISQUE3 598 | colors['bisque4'] = BISQUE4 599 | colors['black'] = BLACK 600 | colors['blanchedalmond'] = BLANCHEDALMOND 601 | colors['blue'] = BLUE 602 | colors['blue2'] = BLUE2 603 | colors['blue3'] = BLUE3 604 | colors['blue4'] = BLUE4 605 | colors['blueviolet'] = BLUEVIOLET 606 | colors['brick'] = BRICK 607 | colors['brown'] = BROWN 608 | colors['brown1'] = BROWN1 609 | colors['brown2'] = BROWN2 610 | colors['brown3'] = BROWN3 611 | colors['brown4'] = BROWN4 612 | colors['burlywood'] = BURLYWOOD 613 | colors['burlywood1'] = BURLYWOOD1 614 | colors['burlywood2'] = BURLYWOOD2 615 | colors['burlywood3'] = BURLYWOOD3 616 | colors['burlywood4'] = BURLYWOOD4 617 | colors['burntsienna'] = BURNTSIENNA 618 | colors['burntumber'] = BURNTUMBER 619 | colors['cadetblue'] = CADETBLUE 620 | colors['cadetblue1'] = CADETBLUE1 621 | colors['cadetblue2'] = CADETBLUE2 622 | colors['cadetblue3'] = CADETBLUE3 623 | colors['cadetblue4'] = CADETBLUE4 624 | colors['cadmiumorange'] = CADMIUMORANGE 625 | colors['cadmiumyellow'] = CADMIUMYELLOW 626 | colors['carrot'] = CARROT 627 | colors['chartreuse1'] = CHARTREUSE1 628 | colors['chartreuse2'] = CHARTREUSE2 629 | colors['chartreuse3'] = CHARTREUSE3 630 | colors['chartreuse4'] = CHARTREUSE4 631 | colors['chocolate'] = CHOCOLATE 632 | colors['chocolate1'] = CHOCOLATE1 633 | colors['chocolate2'] = CHOCOLATE2 634 | colors['chocolate3'] = CHOCOLATE3 635 | colors['chocolate4'] = CHOCOLATE4 636 | colors['cobalt'] = COBALT 637 | colors['cobaltgreen'] = COBALTGREEN 638 | colors['coldgrey'] = COLDGREY 639 | colors['coral'] = CORAL 640 | colors['coral1'] = CORAL1 641 | colors['coral2'] = CORAL2 642 | colors['coral3'] = CORAL3 643 | colors['coral4'] = CORAL4 644 | colors['cornflowerblue'] = CORNFLOWERBLUE 645 | colors['cornsilk1'] = CORNSILK1 646 | colors['cornsilk2'] = CORNSILK2 647 | colors['cornsilk3'] = CORNSILK3 648 | colors['cornsilk4'] = CORNSILK4 649 | colors['crimson'] = CRIMSON 650 | colors['cyan2'] = CYAN2 651 | colors['cyan3'] = CYAN3 652 | colors['cyan4'] = CYAN4 653 | colors['darkgoldenrod'] = DARKGOLDENROD 654 | colors['darkgoldenrod1'] = DARKGOLDENROD1 655 | colors['darkgoldenrod2'] = DARKGOLDENROD2 656 | colors['darkgoldenrod3'] = DARKGOLDENROD3 657 | colors['darkgoldenrod4'] = DARKGOLDENROD4 658 | colors['darkgray'] = DARKGRAY 659 | colors['darkgreen'] = DARKGREEN 660 | colors['darkkhaki'] = DARKKHAKI 661 | colors['darkolivegreen'] = DARKOLIVEGREEN 662 | colors['darkolivegreen1'] = DARKOLIVEGREEN1 663 | colors['darkolivegreen2'] = DARKOLIVEGREEN2 664 | colors['darkolivegreen3'] = DARKOLIVEGREEN3 665 | colors['darkolivegreen4'] = DARKOLIVEGREEN4 666 | colors['darkorange'] = DARKORANGE 667 | colors['darkorange1'] = DARKORANGE1 668 | colors['darkorange2'] = DARKORANGE2 669 | colors['darkorange3'] = DARKORANGE3 670 | colors['darkorange4'] = DARKORANGE4 671 | colors['darkorchid'] = DARKORCHID 672 | colors['darkorchid1'] = DARKORCHID1 673 | colors['darkorchid2'] = DARKORCHID2 674 | colors['darkorchid3'] = DARKORCHID3 675 | colors['darkorchid4'] = DARKORCHID4 676 | colors['darksalmon'] = DARKSALMON 677 | colors['darkseagreen'] = DARKSEAGREEN 678 | colors['darkseagreen1'] = DARKSEAGREEN1 679 | colors['darkseagreen2'] = DARKSEAGREEN2 680 | colors['darkseagreen3'] = DARKSEAGREEN3 681 | colors['darkseagreen4'] = DARKSEAGREEN4 682 | colors['darkslateblue'] = DARKSLATEBLUE 683 | colors['darkslategray'] = DARKSLATEGRAY 684 | colors['darkslategray1'] = DARKSLATEGRAY1 685 | colors['darkslategray2'] = DARKSLATEGRAY2 686 | colors['darkslategray3'] = DARKSLATEGRAY3 687 | colors['darkslategray4'] = DARKSLATEGRAY4 688 | colors['darkturquoise'] = DARKTURQUOISE 689 | colors['darkviolet'] = DARKVIOLET 690 | colors['deeppink1'] = DEEPPINK1 691 | colors['deeppink2'] = DEEPPINK2 692 | colors['deeppink3'] = DEEPPINK3 693 | colors['deeppink4'] = DEEPPINK4 694 | colors['deepskyblue1'] = DEEPSKYBLUE1 695 | colors['deepskyblue2'] = DEEPSKYBLUE2 696 | colors['deepskyblue3'] = DEEPSKYBLUE3 697 | colors['deepskyblue4'] = DEEPSKYBLUE4 698 | colors['dimgray'] = DIMGRAY 699 | colors['dimgray'] = DIMGRAY 700 | colors['dodgerblue1'] = DODGERBLUE1 701 | colors['dodgerblue2'] = DODGERBLUE2 702 | colors['dodgerblue3'] = DODGERBLUE3 703 | colors['dodgerblue4'] = DODGERBLUE4 704 | colors['eggshell'] = EGGSHELL 705 | colors['emeraldgreen'] = EMERALDGREEN 706 | colors['firebrick'] = FIREBRICK 707 | colors['firebrick1'] = FIREBRICK1 708 | colors['firebrick2'] = FIREBRICK2 709 | colors['firebrick3'] = FIREBRICK3 710 | colors['firebrick4'] = FIREBRICK4 711 | colors['flesh'] = FLESH 712 | colors['floralwhite'] = FLORALWHITE 713 | colors['forestgreen'] = FORESTGREEN 714 | colors['gainsboro'] = GAINSBORO 715 | colors['ghostwhite'] = GHOSTWHITE 716 | colors['gold1'] = GOLD1 717 | colors['gold2'] = GOLD2 718 | colors['gold3'] = GOLD3 719 | colors['gold4'] = GOLD4 720 | colors['goldenrod'] = GOLDENROD 721 | colors['goldenrod1'] = GOLDENROD1 722 | colors['goldenrod2'] = GOLDENROD2 723 | colors['goldenrod3'] = GOLDENROD3 724 | colors['goldenrod4'] = GOLDENROD4 725 | colors['gray'] = GRAY 726 | colors['gray1'] = GRAY1 727 | colors['gray10'] = GRAY10 728 | colors['gray11'] = GRAY11 729 | colors['gray12'] = GRAY12 730 | colors['gray13'] = GRAY13 731 | colors['gray14'] = GRAY14 732 | colors['gray15'] = GRAY15 733 | colors['gray16'] = GRAY16 734 | colors['gray17'] = GRAY17 735 | colors['gray18'] = GRAY18 736 | colors['gray19'] = GRAY19 737 | colors['gray2'] = GRAY2 738 | colors['gray20'] = GRAY20 739 | colors['gray21'] = GRAY21 740 | colors['gray22'] = GRAY22 741 | colors['gray23'] = GRAY23 742 | colors['gray24'] = GRAY24 743 | colors['gray25'] = GRAY25 744 | colors['gray26'] = GRAY26 745 | colors['gray27'] = GRAY27 746 | colors['gray28'] = GRAY28 747 | colors['gray29'] = GRAY29 748 | colors['gray3'] = GRAY3 749 | colors['gray30'] = GRAY30 750 | colors['gray31'] = GRAY31 751 | colors['gray32'] = GRAY32 752 | colors['gray33'] = GRAY33 753 | colors['gray34'] = GRAY34 754 | colors['gray35'] = GRAY35 755 | colors['gray36'] = GRAY36 756 | colors['gray37'] = GRAY37 757 | colors['gray38'] = GRAY38 758 | colors['gray39'] = GRAY39 759 | colors['gray4'] = GRAY4 760 | colors['gray40'] = GRAY40 761 | colors['gray42'] = GRAY42 762 | colors['gray43'] = GRAY43 763 | colors['gray44'] = GRAY44 764 | colors['gray45'] = GRAY45 765 | colors['gray46'] = GRAY46 766 | colors['gray47'] = GRAY47 767 | colors['gray48'] = GRAY48 768 | colors['gray49'] = GRAY49 769 | colors['gray5'] = GRAY5 770 | colors['gray50'] = GRAY50 771 | colors['gray51'] = GRAY51 772 | colors['gray52'] = GRAY52 773 | colors['gray53'] = GRAY53 774 | colors['gray54'] = GRAY54 775 | colors['gray55'] = GRAY55 776 | colors['gray56'] = GRAY56 777 | colors['gray57'] = GRAY57 778 | colors['gray58'] = GRAY58 779 | colors['gray59'] = GRAY59 780 | colors['gray6'] = GRAY6 781 | colors['gray60'] = GRAY60 782 | colors['gray61'] = GRAY61 783 | colors['gray62'] = GRAY62 784 | colors['gray63'] = GRAY63 785 | colors['gray64'] = GRAY64 786 | colors['gray65'] = GRAY65 787 | colors['gray66'] = GRAY66 788 | colors['gray67'] = GRAY67 789 | colors['gray68'] = GRAY68 790 | colors['gray69'] = GRAY69 791 | colors['gray7'] = GRAY7 792 | colors['gray70'] = GRAY70 793 | colors['gray71'] = GRAY71 794 | colors['gray72'] = GRAY72 795 | colors['gray73'] = GRAY73 796 | colors['gray74'] = GRAY74 797 | colors['gray75'] = GRAY75 798 | colors['gray76'] = GRAY76 799 | colors['gray77'] = GRAY77 800 | colors['gray78'] = GRAY78 801 | colors['gray79'] = GRAY79 802 | colors['gray8'] = GRAY8 803 | colors['gray80'] = GRAY80 804 | colors['gray81'] = GRAY81 805 | colors['gray82'] = GRAY82 806 | colors['gray83'] = GRAY83 807 | colors['gray84'] = GRAY84 808 | colors['gray85'] = GRAY85 809 | colors['gray86'] = GRAY86 810 | colors['gray87'] = GRAY87 811 | colors['gray88'] = GRAY88 812 | colors['gray89'] = GRAY89 813 | colors['gray9'] = GRAY9 814 | colors['gray90'] = GRAY90 815 | colors['gray91'] = GRAY91 816 | colors['gray92'] = GRAY92 817 | colors['gray93'] = GRAY93 818 | colors['gray94'] = GRAY94 819 | colors['gray95'] = GRAY95 820 | colors['gray97'] = GRAY97 821 | colors['gray98'] = GRAY98 822 | colors['gray99'] = GRAY99 823 | colors['green'] = GREEN 824 | colors['green1'] = GREEN1 825 | colors['green2'] = GREEN2 826 | colors['green3'] = GREEN3 827 | colors['green4'] = GREEN4 828 | colors['greenyellow'] = GREENYELLOW 829 | colors['honeydew1'] = HONEYDEW1 830 | colors['honeydew2'] = HONEYDEW2 831 | colors['honeydew3'] = HONEYDEW3 832 | colors['honeydew4'] = HONEYDEW4 833 | colors['hotpink'] = HOTPINK 834 | colors['hotpink1'] = HOTPINK1 835 | colors['hotpink2'] = HOTPINK2 836 | colors['hotpink3'] = HOTPINK3 837 | colors['hotpink4'] = HOTPINK4 838 | colors['indianred'] = INDIANRED 839 | colors['indianred'] = INDIANRED 840 | colors['indianred1'] = INDIANRED1 841 | colors['indianred2'] = INDIANRED2 842 | colors['indianred3'] = INDIANRED3 843 | colors['indianred4'] = INDIANRED4 844 | colors['indigo'] = INDIGO 845 | colors['ivory1'] = IVORY1 846 | colors['ivory2'] = IVORY2 847 | colors['ivory3'] = IVORY3 848 | colors['ivory4'] = IVORY4 849 | colors['ivoryblack'] = IVORYBLACK 850 | colors['khaki'] = KHAKI 851 | colors['khaki1'] = KHAKI1 852 | colors['khaki2'] = KHAKI2 853 | colors['khaki3'] = KHAKI3 854 | colors['khaki4'] = KHAKI4 855 | colors['lavender'] = LAVENDER 856 | colors['lavenderblush1'] = LAVENDERBLUSH1 857 | colors['lavenderblush2'] = LAVENDERBLUSH2 858 | colors['lavenderblush3'] = LAVENDERBLUSH3 859 | colors['lavenderblush4'] = LAVENDERBLUSH4 860 | colors['lawngreen'] = LAWNGREEN 861 | colors['lemonchiffon1'] = LEMONCHIFFON1 862 | colors['lemonchiffon2'] = LEMONCHIFFON2 863 | colors['lemonchiffon3'] = LEMONCHIFFON3 864 | colors['lemonchiffon4'] = LEMONCHIFFON4 865 | colors['lightblue'] = LIGHTBLUE 866 | colors['lightblue1'] = LIGHTBLUE1 867 | colors['lightblue2'] = LIGHTBLUE2 868 | colors['lightblue3'] = LIGHTBLUE3 869 | colors['lightblue4'] = LIGHTBLUE4 870 | colors['lightcoral'] = LIGHTCORAL 871 | colors['lightcyan1'] = LIGHTCYAN1 872 | colors['lightcyan2'] = LIGHTCYAN2 873 | colors['lightcyan3'] = LIGHTCYAN3 874 | colors['lightcyan4'] = LIGHTCYAN4 875 | colors['lightgoldenrod1'] = LIGHTGOLDENROD1 876 | colors['lightgoldenrod2'] = LIGHTGOLDENROD2 877 | colors['lightgoldenrod3'] = LIGHTGOLDENROD3 878 | colors['lightgoldenrod4'] = LIGHTGOLDENROD4 879 | colors['lightgoldenrodyellow'] = LIGHTGOLDENRODYELLOW 880 | colors['lightgrey'] = LIGHTGREY 881 | colors['lightpink'] = LIGHTPINK 882 | colors['lightpink1'] = LIGHTPINK1 883 | colors['lightpink2'] = LIGHTPINK2 884 | colors['lightpink3'] = LIGHTPINK3 885 | colors['lightpink4'] = LIGHTPINK4 886 | colors['lightsalmon1'] = LIGHTSALMON1 887 | colors['lightsalmon2'] = LIGHTSALMON2 888 | colors['lightsalmon3'] = LIGHTSALMON3 889 | colors['lightsalmon4'] = LIGHTSALMON4 890 | colors['lightseagreen'] = LIGHTSEAGREEN 891 | colors['lightskyblue'] = LIGHTSKYBLUE 892 | colors['lightskyblue1'] = LIGHTSKYBLUE1 893 | colors['lightskyblue2'] = LIGHTSKYBLUE2 894 | colors['lightskyblue3'] = LIGHTSKYBLUE3 895 | colors['lightskyblue4'] = LIGHTSKYBLUE4 896 | colors['lightslateblue'] = LIGHTSLATEBLUE 897 | colors['lightslategray'] = LIGHTSLATEGRAY 898 | colors['lightsteelblue'] = LIGHTSTEELBLUE 899 | colors['lightsteelblue1'] = LIGHTSTEELBLUE1 900 | colors['lightsteelblue2'] = LIGHTSTEELBLUE2 901 | colors['lightsteelblue3'] = LIGHTSTEELBLUE3 902 | colors['lightsteelblue4'] = LIGHTSTEELBLUE4 903 | colors['lightyellow1'] = LIGHTYELLOW1 904 | colors['lightyellow2'] = LIGHTYELLOW2 905 | colors['lightyellow3'] = LIGHTYELLOW3 906 | colors['lightyellow4'] = LIGHTYELLOW4 907 | colors['limegreen'] = LIMEGREEN 908 | colors['linen'] = LINEN 909 | colors['magenta'] = MAGENTA 910 | colors['magenta2'] = MAGENTA2 911 | colors['magenta3'] = MAGENTA3 912 | colors['magenta4'] = MAGENTA4 913 | colors['manganeseblue'] = MANGANESEBLUE 914 | colors['maroon'] = MAROON 915 | colors['maroon1'] = MAROON1 916 | colors['maroon2'] = MAROON2 917 | colors['maroon3'] = MAROON3 918 | colors['maroon4'] = MAROON4 919 | colors['mediumorchid'] = MEDIUMORCHID 920 | colors['mediumorchid1'] = MEDIUMORCHID1 921 | colors['mediumorchid2'] = MEDIUMORCHID2 922 | colors['mediumorchid3'] = MEDIUMORCHID3 923 | colors['mediumorchid4'] = MEDIUMORCHID4 924 | colors['mediumpurple'] = MEDIUMPURPLE 925 | colors['mediumpurple1'] = MEDIUMPURPLE1 926 | colors['mediumpurple2'] = MEDIUMPURPLE2 927 | colors['mediumpurple3'] = MEDIUMPURPLE3 928 | colors['mediumpurple4'] = MEDIUMPURPLE4 929 | colors['mediumseagreen'] = MEDIUMSEAGREEN 930 | colors['mediumslateblue'] = MEDIUMSLATEBLUE 931 | colors['mediumspringgreen'] = MEDIUMSPRINGGREEN 932 | colors['mediumturquoise'] = MEDIUMTURQUOISE 933 | colors['mediumvioletred'] = MEDIUMVIOLETRED 934 | colors['melon'] = MELON 935 | colors['midnightblue'] = MIDNIGHTBLUE 936 | colors['mint'] = MINT 937 | colors['mintcream'] = MINTCREAM 938 | colors['mistyrose1'] = MISTYROSE1 939 | colors['mistyrose2'] = MISTYROSE2 940 | colors['mistyrose3'] = MISTYROSE3 941 | colors['mistyrose4'] = MISTYROSE4 942 | colors['moccasin'] = MOCCASIN 943 | colors['navajowhite1'] = NAVAJOWHITE1 944 | colors['navajowhite2'] = NAVAJOWHITE2 945 | colors['navajowhite3'] = NAVAJOWHITE3 946 | colors['navajowhite4'] = NAVAJOWHITE4 947 | colors['navy'] = NAVY 948 | colors['oldlace'] = OLDLACE 949 | colors['olive'] = OLIVE 950 | colors['olivedrab'] = OLIVEDRAB 951 | colors['olivedrab1'] = OLIVEDRAB1 952 | colors['olivedrab2'] = OLIVEDRAB2 953 | colors['olivedrab3'] = OLIVEDRAB3 954 | colors['olivedrab4'] = OLIVEDRAB4 955 | colors['orange'] = ORANGE 956 | colors['orange1'] = ORANGE1 957 | colors['orange2'] = ORANGE2 958 | colors['orange3'] = ORANGE3 959 | colors['orange4'] = ORANGE4 960 | colors['orangered1'] = ORANGERED1 961 | colors['orangered2'] = ORANGERED2 962 | colors['orangered3'] = ORANGERED3 963 | colors['orangered4'] = ORANGERED4 964 | colors['orchid'] = ORCHID 965 | colors['orchid1'] = ORCHID1 966 | colors['orchid2'] = ORCHID2 967 | colors['orchid3'] = ORCHID3 968 | colors['orchid4'] = ORCHID4 969 | colors['palegoldenrod'] = PALEGOLDENROD 970 | colors['palegreen'] = PALEGREEN 971 | colors['palegreen1'] = PALEGREEN1 972 | colors['palegreen2'] = PALEGREEN2 973 | colors['palegreen3'] = PALEGREEN3 974 | colors['palegreen4'] = PALEGREEN4 975 | colors['paleturquoise1'] = PALETURQUOISE1 976 | colors['paleturquoise2'] = PALETURQUOISE2 977 | colors['paleturquoise3'] = PALETURQUOISE3 978 | colors['paleturquoise4'] = PALETURQUOISE4 979 | colors['palevioletred'] = PALEVIOLETRED 980 | colors['palevioletred1'] = PALEVIOLETRED1 981 | colors['palevioletred2'] = PALEVIOLETRED2 982 | colors['palevioletred3'] = PALEVIOLETRED3 983 | colors['palevioletred4'] = PALEVIOLETRED4 984 | colors['papayawhip'] = PAPAYAWHIP 985 | colors['peachpuff1'] = PEACHPUFF1 986 | colors['peachpuff2'] = PEACHPUFF2 987 | colors['peachpuff3'] = PEACHPUFF3 988 | colors['peachpuff4'] = PEACHPUFF4 989 | colors['peacock'] = PEACOCK 990 | colors['pink'] = PINK 991 | colors['pink1'] = PINK1 992 | colors['pink2'] = PINK2 993 | colors['pink3'] = PINK3 994 | colors['pink4'] = PINK4 995 | colors['plum'] = PLUM 996 | colors['plum1'] = PLUM1 997 | colors['plum2'] = PLUM2 998 | colors['plum3'] = PLUM3 999 | colors['plum4'] = PLUM4 1000 | colors['powderblue'] = POWDERBLUE 1001 | colors['purple'] = PURPLE 1002 | colors['purple1'] = PURPLE1 1003 | colors['purple2'] = PURPLE2 1004 | colors['purple3'] = PURPLE3 1005 | colors['purple4'] = PURPLE4 1006 | colors['raspberry'] = RASPBERRY 1007 | colors['rawsienna'] = RAWSIENNA 1008 | colors['red'] = RED 1009 | colors['red1'] = RED1 1010 | colors['red2'] = RED2 1011 | colors['red3'] = RED3 1012 | colors['rosybrown'] = ROSYBROWN 1013 | colors['rosybrown1'] = ROSYBROWN1 1014 | colors['rosybrown2'] = ROSYBROWN2 1015 | colors['rosybrown3'] = ROSYBROWN3 1016 | colors['rosybrown4'] = ROSYBROWN4 1017 | colors['royalblue'] = ROYALBLUE 1018 | colors['royalblue1'] = ROYALBLUE1 1019 | colors['royalblue2'] = ROYALBLUE2 1020 | colors['royalblue3'] = ROYALBLUE3 1021 | colors['royalblue4'] = ROYALBLUE4 1022 | colors['salmon'] = SALMON 1023 | colors['salmon1'] = SALMON1 1024 | colors['salmon2'] = SALMON2 1025 | colors['salmon3'] = SALMON3 1026 | colors['salmon4'] = SALMON4 1027 | colors['sandybrown'] = SANDYBROWN 1028 | colors['sapgreen'] = SAPGREEN 1029 | colors['seagreen1'] = SEAGREEN1 1030 | colors['seagreen2'] = SEAGREEN2 1031 | colors['seagreen3'] = SEAGREEN3 1032 | colors['seagreen4'] = SEAGREEN4 1033 | colors['seashell1'] = SEASHELL1 1034 | colors['seashell2'] = SEASHELL2 1035 | colors['seashell3'] = SEASHELL3 1036 | colors['seashell4'] = SEASHELL4 1037 | colors['sepia'] = SEPIA 1038 | colors['sgibeet'] = SGIBEET 1039 | colors['sgibrightgray'] = SGIBRIGHTGRAY 1040 | colors['sgichartreuse'] = SGICHARTREUSE 1041 | colors['sgidarkgray'] = SGIDARKGRAY 1042 | colors['sgigray12'] = SGIGRAY12 1043 | colors['sgigray16'] = SGIGRAY16 1044 | colors['sgigray32'] = SGIGRAY32 1045 | colors['sgigray36'] = SGIGRAY36 1046 | colors['sgigray52'] = SGIGRAY52 1047 | colors['sgigray56'] = SGIGRAY56 1048 | colors['sgigray72'] = SGIGRAY72 1049 | colors['sgigray76'] = SGIGRAY76 1050 | colors['sgigray92'] = SGIGRAY92 1051 | colors['sgigray96'] = SGIGRAY96 1052 | colors['sgilightblue'] = SGILIGHTBLUE 1053 | colors['sgilightgray'] = SGILIGHTGRAY 1054 | colors['sgiolivedrab'] = SGIOLIVEDRAB 1055 | colors['sgisalmon'] = SGISALMON 1056 | colors['sgislateblue'] = SGISLATEBLUE 1057 | colors['sgiteal'] = SGITEAL 1058 | colors['sienna'] = SIENNA 1059 | colors['sienna1'] = SIENNA1 1060 | colors['sienna2'] = SIENNA2 1061 | colors['sienna3'] = SIENNA3 1062 | colors['sienna4'] = SIENNA4 1063 | colors['silver'] = SILVER 1064 | colors['skyblue'] = SKYBLUE 1065 | colors['skyblue1'] = SKYBLUE1 1066 | colors['skyblue2'] = SKYBLUE2 1067 | colors['skyblue3'] = SKYBLUE3 1068 | colors['skyblue4'] = SKYBLUE4 1069 | colors['slateblue'] = SLATEBLUE 1070 | colors['slateblue1'] = SLATEBLUE1 1071 | colors['slateblue2'] = SLATEBLUE2 1072 | colors['slateblue3'] = SLATEBLUE3 1073 | colors['slateblue4'] = SLATEBLUE4 1074 | colors['slategray'] = SLATEGRAY 1075 | colors['slategray1'] = SLATEGRAY1 1076 | colors['slategray2'] = SLATEGRAY2 1077 | colors['slategray3'] = SLATEGRAY3 1078 | colors['slategray4'] = SLATEGRAY4 1079 | colors['snow1'] = SNOW1 1080 | colors['snow2'] = SNOW2 1081 | colors['snow3'] = SNOW3 1082 | colors['snow4'] = SNOW4 1083 | colors['springgreen'] = SPRINGGREEN 1084 | colors['springgreen1'] = SPRINGGREEN1 1085 | colors['springgreen2'] = SPRINGGREEN2 1086 | colors['springgreen3'] = SPRINGGREEN3 1087 | colors['steelblue'] = STEELBLUE 1088 | colors['steelblue1'] = STEELBLUE1 1089 | colors['steelblue2'] = STEELBLUE2 1090 | colors['steelblue3'] = STEELBLUE3 1091 | colors['steelblue4'] = STEELBLUE4 1092 | colors['tan'] = TAN 1093 | colors['tan1'] = TAN1 1094 | colors['tan2'] = TAN2 1095 | colors['tan3'] = TAN3 1096 | colors['tan4'] = TAN4 1097 | colors['teal'] = TEAL 1098 | colors['thistle'] = THISTLE 1099 | colors['thistle1'] = THISTLE1 1100 | colors['thistle2'] = THISTLE2 1101 | colors['thistle3'] = THISTLE3 1102 | colors['thistle4'] = THISTLE4 1103 | colors['tomato1'] = TOMATO1 1104 | colors['tomato2'] = TOMATO2 1105 | colors['tomato3'] = TOMATO3 1106 | colors['tomato4'] = TOMATO4 1107 | colors['turquoise'] = TURQUOISE 1108 | colors['turquoise1'] = TURQUOISE1 1109 | colors['turquoise2'] = TURQUOISE2 1110 | colors['turquoise3'] = TURQUOISE3 1111 | colors['turquoise4'] = TURQUOISE4 1112 | colors['turquoiseblue'] = TURQUOISEBLUE 1113 | colors['violet'] = VIOLET 1114 | colors['violetred'] = VIOLETRED 1115 | colors['violetred1'] = VIOLETRED1 1116 | colors['violetred2'] = VIOLETRED2 1117 | colors['violetred3'] = VIOLETRED3 1118 | colors['violetred4'] = VIOLETRED4 1119 | colors['warmgrey'] = WARMGREY 1120 | colors['wheat'] = WHEAT 1121 | colors['wheat1'] = WHEAT1 1122 | colors['wheat2'] = WHEAT2 1123 | colors['wheat3'] = WHEAT3 1124 | colors['wheat4'] = WHEAT4 1125 | colors['white'] = WHITE 1126 | colors['whitesmoke'] = WHITESMOKE 1127 | colors['whitesmoke'] = WHITESMOKE 1128 | colors['yellow1'] = YELLOW1 1129 | colors['yellow2'] = YELLOW2 1130 | colors['yellow3'] = YELLOW3 1131 | colors['yellow4'] = YELLOW4 1132 | 1133 | colors = OrderedDict(sorted(colors.items(), key=lambda t: t[0])) 1134 | -------------------------------------------------------------------------------- /pipeline/libs/face_detector.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | 4 | 5 | class FaceDetector: 6 | def __init__(self, prototxt, model, confidence=0.5): 7 | self.confidence = confidence 8 | 9 | self.net = cv2.dnn.readNetFromCaffe(prototxt, model) 10 | 11 | def detect(self, images): 12 | # convert images into blob 13 | blob = self.preprocess(images) 14 | 15 | # pass the blob through the network and obtain the detections and predictions 16 | self.net.setInput(blob) 17 | detections = self.net.forward() 18 | # Prepare storage for faces for every image in the batch 19 | faces = dict(zip(range(len(images)), [[] for _ in range(len(images))])) 20 | 21 | # loop over the detections 22 | for i in range(0, detections.shape[2]): 23 | # extract the confidence (i.e., probability) associated with the prediction 24 | confidence = detections[0, 0, i, 2] 25 | 26 | # filter out weak detections by ensuring the `confidence` is 27 | # greater than the minimum confidence 28 | if confidence < self.confidence: 29 | continue 30 | 31 | # grab the image index 32 | image_idx = int(detections[0, 0, i, 0]) 33 | # grab the image dimensions 34 | (h, w) = images[image_idx].shape[:2] 35 | # compute the (x, y)-coordinates of the bounding box for the object 36 | box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) 37 | 38 | # Add result 39 | faces[image_idx].append((box, confidence)) 40 | 41 | return faces 42 | 43 | def preprocess(self, images): 44 | return cv2.dnn.blobFromImages(images, 1.0, (300, 300), (104.0, 177.0, 123.0)) 45 | -------------------------------------------------------------------------------- /pipeline/libs/text.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | 4 | def put_text(image, text, org, font_face=cv2.FONT_HERSHEY_SIMPLEX, font_scale=0.5, 5 | color=(0, 0, 0), bg_color=None, thickness=1, line_type=cv2.LINE_AA, 6 | org_pos="tl", padding=2): 7 | x, y = org 8 | ret, baseline = cv2.getTextSize(text, font_face, font_scale, thickness) 9 | 10 | # Calculate text and background box coordinates 11 | if org_pos == "tl": # top-left origin 12 | bg_rect_pt1 = (x, y + ret[1] + baseline + 2 * padding) 13 | bg_rect_pt2 = (x + ret[0] + 2 * padding, y) 14 | text_org = (x + padding, y + ret[1] + padding) 15 | elif org_pos == "tr": # top-right origin 16 | bg_rect_pt1 = (x - ret[0] - 2 * padding, y) 17 | bg_rect_pt2 = (x, y + ret[1] + baseline + 2 * padding) 18 | text_org = (x - ret[0] - padding, y + ret[1] + baseline + padding) 19 | elif org_pos == "bl": # bottom-left origin 20 | bg_rect_pt1 = (x, y - ret[1] - baseline - 2 * padding) 21 | bg_rect_pt2 = (x + ret[0] + 2 * padding, y) 22 | text_org = (x + padding, y - baseline - padding) 23 | elif org_pos == "br": # bottom-right origin 24 | bg_rect_pt1 = (x, y - ret[1] - baseline - 2 * padding) 25 | bg_rect_pt2 = (x - ret[0] - 2 * padding, y) 26 | text_org = (x - ret[0] - padding, y - baseline - padding) 27 | 28 | if bg_color: 29 | # Draw background box 30 | cv2.rectangle(image, bg_rect_pt1, bg_rect_pt2, bg_color, -1) 31 | 32 | cv2.putText(image, 33 | text=text, 34 | org=text_org, 35 | fontFace=font_face, 36 | fontScale=font_scale, 37 | color=color, 38 | thickness=thickness, 39 | lineType=line_type) 40 | -------------------------------------------------------------------------------- /pipeline/libs/timeme.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def timeme(method): 5 | def wrapper(*args, **kw): 6 | start_time = time.time() 7 | result = method(*args, **kw) 8 | end_time = time.time() 9 | 10 | print(int(round((end_time - start_time) * 1000)), 'ms') 11 | return result 12 | 13 | return wrapper 14 | -------------------------------------------------------------------------------- /pipeline/libs/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def walk_to_level(path, level=None): 5 | if level is None: 6 | yield from os.walk(path) 7 | return 8 | 9 | path = path.rstrip(os.path.sep) 10 | num_sep = path.count(os.path.sep) 11 | for root, dirs, files in os.walk(path): 12 | yield root, dirs, files 13 | num_sep_this = root.count(os.path.sep) 14 | if num_sep + level <= num_sep_this: 15 | # When some directory on or below the desired level is found, all 16 | # of its subdirs are removed from the list of subdirs to search next. 17 | # So they won't be walked. 18 | del dirs[:] 19 | 20 | 21 | def list_files(path, valid_exts=None, level=None): 22 | # Loop over the input directory structure 23 | for (root_dir, dir_names, filenames) in walk_to_level(path, level): 24 | for filename in sorted(filenames): 25 | # Determine the file extension of the current file 26 | ext = filename[filename.rfind("."):].lower() 27 | if valid_exts and ext.endswith(valid_exts): 28 | # Construct the path to the file and yield it 29 | file = os.path.join(root_dir, filename) 30 | yield file 31 | -------------------------------------------------------------------------------- /pipeline/pipeline.py: -------------------------------------------------------------------------------- 1 | class Pipeline(object): 2 | """Common pipeline class fo all pipeline tasks.""" 3 | 4 | def __init__(self, source=None): 5 | self.source = source 6 | 7 | def __iter__(self): 8 | return self.generator() 9 | 10 | def generator(self): 11 | """Yields the pipeline data.""" 12 | 13 | while self.has_next(): 14 | try: 15 | data = next(self.source) if self.source else {} 16 | if self.filter(data): 17 | yield self.map(data) 18 | except StopIteration: 19 | return 20 | 21 | def __or__(self, other): 22 | """Allows to connect the pipeline task using | operator.""" 23 | 24 | if other is not None: 25 | other.source = self.generator() 26 | return other 27 | else: 28 | return self 29 | 30 | def filter(self, data): 31 | """Overwrite to filter out the pipeline data.""" 32 | 33 | return True 34 | 35 | def map(self, data): 36 | """Overwrite to map the pipeline data.""" 37 | 38 | return data 39 | 40 | def has_next(self): 41 | """Overwrite to stop the generator in certain conditions.""" 42 | 43 | return True 44 | 45 | -------------------------------------------------------------------------------- /pipeline/save_faces.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | 4 | from pipeline.pipeline import Pipeline 5 | 6 | 7 | class SaveFaces(Pipeline): 8 | """Pipeline task to save detected faces.""" 9 | 10 | def __init__(self, path, image_ext="jpg"): 11 | self.path = path 12 | self.image_ext = image_ext 13 | 14 | super(SaveFaces, self).__init__() 15 | 16 | def map(self, data): 17 | image_id = data["image_id"] 18 | image = data["image"] 19 | faces = data["faces"] 20 | data["face_files"] = [] 21 | 22 | # Loop over all detected faces 23 | for i, face in enumerate(faces): 24 | box, confidence = face 25 | (x1, y1, x2, y2) = box.astype("int") 26 | # Crop the face from the image 27 | face = image[y1:y2, x1:x2] 28 | 29 | # Prepare output directory for faces 30 | output = os.path.join(*(image_id.split(os.path.sep))) 31 | output = os.path.join(self.path, output) 32 | os.makedirs(output, exist_ok=True) 33 | 34 | # Save faces 35 | face_file = os.path.join(output, f"{i:05d}.{self.image_ext}") 36 | data["face_files"].append(face_file) 37 | cv2.imwrite(face_file, face) 38 | 39 | return data 40 | -------------------------------------------------------------------------------- /pipeline/save_image.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | 4 | from pipeline.pipeline import Pipeline 5 | 6 | 7 | class SaveImage(Pipeline): 8 | """Pipeline task to save images.""" 9 | 10 | def __init__(self, src, path, image_ext="jpg", jpg_quality=None, png_compression=None): 11 | self.src = src 12 | self.path = path 13 | self.image_ext = image_ext 14 | self.jpg_quality = jpg_quality # 0 - 100 (higher means better). Default is 95. 15 | self.png_compression = png_compression # 0 - 9 (higher means a smaller size and longer compression time). Default is 3. 16 | 17 | super(SaveImage, self).__init__() 18 | 19 | def map(self, data): 20 | image = data[self.src] 21 | image_id = data["image_id"] 22 | 23 | # Prepare output for image based on image_id 24 | output = image_id.split(os.path.sep) 25 | dirname = output[:-1] 26 | if len(dirname) > 0: 27 | dirname = os.path.join(*dirname) 28 | dirname = os.path.join(self.path, dirname) 29 | os.makedirs(dirname, exist_ok=True) 30 | else: 31 | dirname = self.path 32 | filename = f"{output[-1].rsplit('.', 1)[0]}.{self.image_ext}" 33 | path = os.path.join(dirname, filename) 34 | 35 | if self.image_ext == "jpg": 36 | cv2.imwrite(path, image, 37 | (cv2.IMWRITE_JPEG_QUALITY, self.jpg_quality) if self.jpg_quality else None) 38 | elif self.image_ext == "png": 39 | cv2.imwrite(path, image, 40 | (cv2.IMWRITE_PNG_COMPRESSION, self.png_compression) if self.png_compression else None) 41 | else: 42 | raise Exception("Unsupported image format") 43 | 44 | return data 45 | -------------------------------------------------------------------------------- /pipeline/save_summary.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import numpy as np 4 | 5 | from pipeline.pipeline import Pipeline 6 | 7 | 8 | class SaveSummary(Pipeline): 9 | """Pipeline task to save processing summary.""" 10 | 11 | def __init__(self, filename): 12 | self.filename = filename 13 | 14 | self.summary = {} 15 | super(SaveSummary, self).__init__() 16 | 17 | def map(self, data): 18 | image_id = data["image_id"] 19 | face_files = data["face_files"] 20 | faces = data["faces"] 21 | 22 | # Loop over all detected faces and buffer summary results 23 | self.summary[image_id] = {} 24 | for i, face in enumerate(faces): 25 | box, confidence = face 26 | (x1, y1, x2, y2) = box.astype("int") 27 | face_file = face_files[i] 28 | self.summary[image_id][face_file] = { 29 | "box": np.array([x1, y1, x2, y2], dtype=int).tolist(), 30 | "confidence": confidence.item() 31 | } 32 | 33 | return data 34 | 35 | def write(self): 36 | dirname = os.path.dirname(os.path.abspath(self.filename)) 37 | os.makedirs(dirname, exist_ok=True) 38 | 39 | with open(self.filename, 'w') as json_file: 40 | json_file.write(json.dumps(self.summary)) 41 | -------------------------------------------------------------------------------- /pipeline/save_video.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | from pipeline.pipeline import Pipeline 4 | 5 | 6 | class SaveVideo(Pipeline): 7 | """Pipeline task to save video.""" 8 | 9 | def __init__(self, src, filename, fps=30, fourcc='MJPG'): 10 | self.src = src 11 | self.filename = filename 12 | self.fps = fps 13 | self.writer = None 14 | self.fourcc = cv2.VideoWriter_fourcc(*fourcc) 15 | 16 | super(SaveVideo, self).__init__() 17 | 18 | def map(self, data): 19 | image = data[self.src] 20 | h, w = image.shape[:2] 21 | 22 | if self.writer is None: 23 | self.writer = cv2.VideoWriter(self.filename, self.fourcc, self.fps, (w, h), image.ndim == 3) 24 | self.writer.write(image) 25 | 26 | return data 27 | 28 | def cleanup(self): 29 | if self.writer: 30 | self.writer.release() 31 | -------------------------------------------------------------------------------- /process_images.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import os 3 | import json 4 | import numpy as np 5 | 6 | 7 | def parse_args(): 8 | import argparse 9 | 10 | # Parse command line arguments 11 | ap = argparse.ArgumentParser(description="Image processing pipeline") 12 | ap.add_argument("-i", "--input", required=True, 13 | help="path to input image files") 14 | ap.add_argument("-o", "--output", default="output", 15 | help="path to output directory") 16 | ap.add_argument("-os", "--out-summary", default=None, 17 | help="output JSON summary file name") 18 | ap.add_argument("-c", "--classifier", default="models/haarcascade/haarcascade_frontalface_default.xml", 19 | help="path to where the face cascade resides") 20 | 21 | return ap.parse_args() 22 | 23 | 24 | def list_images(path, valid_exts=None): 25 | image_files = [] 26 | # Loop over the input directory structure 27 | for (root_dir, dir_names, filenames) in os.walk(path): 28 | for filename in sorted(filenames): 29 | # Determine the file extension of the current file 30 | ext = filename[filename.rfind("."):].lower() 31 | if valid_exts and ext.endswith(valid_exts): 32 | # Construct the path to the file 33 | file = os.path.join(root_dir, filename) 34 | image_files.append(file) 35 | 36 | return image_files 37 | 38 | 39 | def main(args): 40 | os.makedirs(args.output, exist_ok=True) 41 | 42 | # load the face detector 43 | detector = cv2.CascadeClassifier(args.classifier) 44 | 45 | # list images from input directory 46 | input_image_files = list_images(args.input, (".jpg", ".png")) 47 | 48 | # Storage for JSON summary 49 | summary = {} 50 | 51 | # Loop over the image paths 52 | for image_file in input_image_files: 53 | # Load the image and convert it to grayscale 54 | image = cv2.imread(image_file) 55 | gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 56 | 57 | # Detect faces 58 | face_rects = detector.detectMultiScale(gray, scaleFactor=1.05, minNeighbors=5, 59 | minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE) 60 | summary[image_file] = {} 61 | # Loop over all detected faces 62 | for i, (x, y, w, h) in enumerate(face_rects): 63 | face = image[y:y+w, x:x+h] 64 | 65 | # Prepare output directory for faces 66 | output = os.path.join(*(image_file.split(os.path.sep)[1:])) 67 | output = os.path.join(args.output, output) 68 | os.makedirs(output, exist_ok=True) 69 | 70 | # Save faces 71 | face_file = os.path.join(output, f"{i:05d}.jpg") 72 | cv2.imwrite(face_file, face) 73 | 74 | # Store summary data 75 | summary[image_file][face_file] = np.array([x, y, w, h], dtype=int).tolist() 76 | 77 | # Display summary 78 | print(f"[INFO] {image_file}: face detections {len(face_rects)}") 79 | 80 | # Save summary data 81 | if args.out_summary: 82 | summary_file = os.path.join(args.output, args.out_summary) 83 | print(f"[INFO] Saving summary to {summary_file}...") 84 | with open(summary_file, 'w') as json_file: 85 | json_file.write(json.dumps(summary)) 86 | 87 | 88 | if __name__ == "__main__": 89 | args = parse_args() 90 | main(args) 91 | -------------------------------------------------------------------------------- /process_images_pipeline.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from pipeline.capture_images import CaptureImages 4 | from pipeline.detect_faces import DetectFaces 5 | from pipeline.save_faces import SaveFaces 6 | from pipeline.save_summary import SaveSummary 7 | from pipeline.display_summary import DisplaySummary 8 | 9 | 10 | def parse_args(): 11 | import argparse 12 | 13 | # Parse command line arguments 14 | ap = argparse.ArgumentParser(description="Image processing pipeline") 15 | ap.add_argument("-i", "--input", required=True, 16 | help="path to input image files") 17 | ap.add_argument("-o", "--output", default="output", 18 | help="path to output directory") 19 | ap.add_argument("-os", "--out-summary", default="summary.json", 20 | help="output JSON summary file name") 21 | ap.add_argument("--prototxt", default="./models/face_detector/deploy.prototxt.txt", 22 | help="path to Caffe 'deploy' prototxt file") 23 | ap.add_argument("--model", default="./models/face_detector/res10_300x300_ssd_iter_140000.caffemodel", 24 | help="path to Caffe pre-trained model") 25 | ap.add_argument("--confidence", type=float, default=0.5, 26 | help="minimum probability to filter weak face detections") 27 | ap.add_argument("--batch-size", type=int, default=1, 28 | help="face detection batch size") 29 | 30 | return ap.parse_args() 31 | 32 | 33 | def main(args): 34 | # Create pipeline steps 35 | capture_images = CaptureImages(args.input) 36 | 37 | detect_faces = DetectFaces(prototxt=args.prototxt, model=args.model, 38 | confidence=args.confidence, batch_size=args.batch_size) 39 | 40 | save_faces = SaveFaces(args.output) 41 | 42 | summary_file = os.path.join(args.output, args.out_summary) 43 | save_summary = SaveSummary(summary_file) 44 | 45 | display_summary = DisplaySummary() 46 | 47 | # Create image processing pipeline 48 | pipeline = (capture_images | 49 | detect_faces | 50 | save_faces | 51 | save_summary | 52 | display_summary) 53 | 54 | try: 55 | # Iterate through pipeline 56 | for _ in pipeline: 57 | pass 58 | except StopIteration: 59 | return 60 | except KeyboardInterrupt: 61 | return 62 | finally: 63 | print(f"[INFO] Saving summary to {summary_file}...") 64 | save_summary.write() 65 | 66 | 67 | if __name__ == "__main__": 68 | args = parse_args() 69 | main(args) 70 | -------------------------------------------------------------------------------- /process_video_pipeline.py: -------------------------------------------------------------------------------- 1 | import os 2 | from tqdm import tqdm 3 | 4 | from pipeline.capture_video import CaptureVideo 5 | from pipeline.detect_faces import DetectFaces 6 | from pipeline.save_faces import SaveFaces 7 | from pipeline.save_summary import SaveSummary 8 | from pipeline.annotate_image import AnnotateImage 9 | from pipeline.display_video import DisplayVideo 10 | from pipeline.save_video import SaveVideo 11 | 12 | 13 | def parse_args(): 14 | import argparse 15 | 16 | # Parse command line arguments 17 | ap = argparse.ArgumentParser(description="Video processing pipeline") 18 | ap.add_argument("-i", "--input", default="0", 19 | help="path to input video file or camera identifier") 20 | ap.add_argument("-o", "--output", default="output", 21 | help="path to output directory") 22 | ap.add_argument("-ov", "--out-video", default=None, 23 | help="output video file name") 24 | ap.add_argument("-os", "--out-summary", default="summary.json", 25 | help="output JSON summary file name") 26 | ap.add_argument("-p", "--progress", action="store_true", help="display progress") 27 | ap.add_argument("-d", "--display", action="store_true", help="display video result") 28 | ap.add_argument("--prototxt", default="./models/face_detector/deploy.prototxt.txt", 29 | help="path to Caffe 'deploy' prototxt file") 30 | ap.add_argument("--model", default="./models/face_detector/res10_300x300_ssd_iter_140000.caffemodel", 31 | help="path to Caffe pre-trained model") 32 | ap.add_argument("--confidence", type=float, default=0.5, 33 | help="minimum probability to filter weak face detections") 34 | ap.add_argument("--batch-size", type=int, default=1, 35 | help="face detection batch size") 36 | 37 | return ap.parse_args() 38 | 39 | 40 | def main(args): 41 | # Create pipeline steps 42 | capture_video = CaptureVideo(int(args.input) if args.input.isdigit() else args.input) 43 | 44 | detect_faces = DetectFaces(prototxt=args.prototxt, model=args.model, 45 | confidence=args.confidence, batch_size=args.batch_size) 46 | 47 | save_faces = SaveFaces(args.output) 48 | 49 | summary_file = os.path.join(args.output, args.out_summary) 50 | save_summary = SaveSummary(summary_file) 51 | 52 | annotate_image = AnnotateImage("annotated_image") \ 53 | if args.display or args.out_video else None 54 | 55 | display_video = DisplayVideo("annotated_image") \ 56 | if args.display else None 57 | 58 | save_video = SaveVideo("annotated_image", os.path.join(args.output, args.out_video), fps=capture_video.fps) \ 59 | if args.out_video else None 60 | 61 | # Create image processing pipeline 62 | pipeline = (capture_video | 63 | detect_faces | 64 | save_faces | 65 | annotate_image | 66 | display_video | 67 | save_video | 68 | save_summary) 69 | 70 | # Iterate through pipeline 71 | progress = tqdm(total=capture_video.frame_count if capture_video.frame_count > 0 else None, 72 | disable=not args.progress) 73 | try: 74 | for _ in pipeline: 75 | progress.update(1) 76 | except StopIteration: 77 | return 78 | except KeyboardInterrupt: 79 | return 80 | finally: 81 | progress.close() 82 | 83 | # Pipeline cleanup 84 | capture_video.cleanup() 85 | if display_video: 86 | display_video.cleanup() 87 | if save_video: 88 | save_video.cleanup() 89 | 90 | print(f"[INFO] Saving summary to {summary_file}...") 91 | save_summary.write() 92 | 93 | 94 | if __name__ == "__main__": 95 | args = parse_args() 96 | main(args) 97 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths = tests -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/tests/__init__.py -------------------------------------------------------------------------------- /tests/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | MAIN_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..") 4 | 5 | ASSETS_DIR = os.path.join(MAIN_DIR, "assets") 6 | ASSETS_IMAGES_DIR = os.path.join(ASSETS_DIR, "images") 7 | ASSETS_VIDEOS_DIR = os.path.join(ASSETS_DIR, "videos") 8 | 9 | MODELS_DIR = os.path.join(MAIN_DIR, "models") 10 | MODELS_FACE_DETECTOR_DIR = os.path.join(MODELS_DIR, "face_detector") 11 | -------------------------------------------------------------------------------- /tests/pipeline/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/tests/pipeline/__init__.py -------------------------------------------------------------------------------- /tests/pipeline/libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagin/image-processing-pipeline/7df1963247caa01b503980fe152138b88df6c526/tests/pipeline/libs/__init__.py -------------------------------------------------------------------------------- /tests/pipeline/libs/test_face_detector.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cv2 3 | 4 | from pipeline.libs.face_detector import FaceDetector 5 | import tests.config as config 6 | 7 | 8 | class TestFaceDetector: 9 | def test_face_detector_with_sample_image(self): 10 | prototxt = os.path.join(config.MODELS_FACE_DETECTOR_DIR, "deploy.prototxt.txt") 11 | model = os.path.join(config.MODELS_FACE_DETECTOR_DIR, "res10_300x300_ssd_iter_140000.caffemodel") 12 | detector = FaceDetector(prototxt, model) 13 | 14 | test_image = cv2.imread(os.path.join(config.ASSETS_IMAGES_DIR, "friends", "friends_01.jpg")) 15 | faces = detector.detect([test_image]) 16 | 17 | assert len(faces) == 1 18 | assert len(faces[0]) == 3 # Should recognize 3 faces from friends_01.jpg 19 | 20 | def test_face_detector_with_batch_images(self): 21 | prototxt = os.path.join(config.MODELS_FACE_DETECTOR_DIR, "deploy.prototxt.txt") 22 | model = os.path.join(config.MODELS_FACE_DETECTOR_DIR, "res10_300x300_ssd_iter_140000.caffemodel") 23 | detector = FaceDetector(prototxt, model) 24 | 25 | test_image_1 = cv2.imread(os.path.join(config.ASSETS_IMAGES_DIR, "friends", "friends_01.jpg")) 26 | test_image_2 = cv2.imread(os.path.join(config.ASSETS_IMAGES_DIR, "friends", "friends_02.jpg")) 27 | test_image_3 = cv2.imread(os.path.join(config.ASSETS_IMAGES_DIR, "friends", "friends_03.jpg")) 28 | test_image_4 = cv2.imread(os.path.join(config.ASSETS_IMAGES_DIR, "friends", "friends_04.jpg")) 29 | faces = detector.detect([test_image_1, test_image_2, test_image_3, test_image_4]) 30 | 31 | assert len(faces) == 4 32 | assert len(faces[0]) 33 | assert len(faces[1]) 34 | assert len(faces[2]) 35 | assert len(faces[3]) 36 | -------------------------------------------------------------------------------- /tests/pipeline/test_capture_images.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from pipeline.capture_images import CaptureImages 4 | import tests.config as config 5 | 6 | 7 | class TestCaptureImages: 8 | def test_capture_images(self): 9 | capture_images = CaptureImages(config.ASSETS_IMAGES_DIR) 10 | 11 | images = list(capture_images) 12 | 13 | assert len(images) == 6 14 | assert isinstance(images[0]["image"], np.ndarray) 15 | -------------------------------------------------------------------------------- /tests/pipeline/test_detect_faces.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from pipeline.capture_images import CaptureImages 4 | from pipeline.detect_faces import DetectFaces 5 | import tests.config as config 6 | 7 | 8 | class TestDetectFaces: 9 | def test_detect_faces(self): 10 | capture_images = CaptureImages(os.path.join(config.ASSETS_IMAGES_DIR, "friends")) 11 | prototxt = os.path.join(config.MODELS_FACE_DETECTOR_DIR, "deploy.prototxt.txt") 12 | model = os.path.join(config.MODELS_FACE_DETECTOR_DIR, "res10_300x300_ssd_iter_140000.caffemodel") 13 | detect_faces = DetectFaces(prototxt=prototxt, model=model) 14 | 15 | pipeline = iter(capture_images | 16 | detect_faces) 17 | 18 | data = next(pipeline) 19 | assert len(data["faces"]) 20 | 21 | data = next(pipeline) 22 | assert len(data["faces"]) 23 | 24 | data = next(pipeline) 25 | assert len(data["faces"]) 26 | 27 | data = next(pipeline) 28 | assert len(data["faces"]) 29 | 30 | def test_detect_faces_in_batch(self): 31 | capture_images = CaptureImages(os.path.join(config.ASSETS_IMAGES_DIR, "friends")) 32 | prototxt = os.path.join(config.MODELS_FACE_DETECTOR_DIR, "deploy.prototxt.txt") 33 | model = os.path.join(config.MODELS_FACE_DETECTOR_DIR, "res10_300x300_ssd_iter_140000.caffemodel") 34 | detect_faces = DetectFaces(prototxt=prototxt, model=model, batch_size=2) 35 | 36 | pipeline = iter(capture_images | 37 | detect_faces) 38 | 39 | data = next(pipeline) 40 | assert len(data["faces"]) 41 | 42 | data = next(pipeline) 43 | assert len(data["faces"]) 44 | 45 | data = next(pipeline) 46 | assert len(data["faces"]) 47 | 48 | data = next(pipeline) 49 | assert len(data["faces"]) 50 | -------------------------------------------------------------------------------- /tests/pipeline/test_pipeline.py: -------------------------------------------------------------------------------- 1 | from pipeline.pipeline import Pipeline 2 | 3 | 4 | class AllNumbers(Pipeline): 5 | """Generate integer numbers""" 6 | 7 | def generator(self): 8 | value = 0 9 | while True: 10 | yield value 11 | value += 1 12 | 13 | 14 | class Evens(Pipeline): 15 | """Filter even numbers only""" 16 | 17 | def filter(self, value): 18 | return value % 2 == 0 19 | 20 | 21 | class MultipleOf(Pipeline): 22 | """Filter numbers which are multiplied by given factor""" 23 | 24 | def __init__(self, factor=1): 25 | self.factor = factor 26 | super(MultipleOf, self).__init__() 27 | 28 | def filter(self, value): 29 | return value % self.factor == 0 30 | 31 | 32 | class First(Pipeline): 33 | """Get first 'total' numbers""" 34 | 35 | def __init__(self, total=10): 36 | self.total = total 37 | self.count = 0 38 | super(First, self).__init__() 39 | 40 | def map(self, value): 41 | self.count += 1 42 | return value 43 | 44 | def has_next(self): 45 | return self.count < self.total 46 | 47 | 48 | class Printer(Pipeline): 49 | """Print result""" 50 | 51 | def map(self, value): 52 | print(value) 53 | return value 54 | 55 | 56 | class TestPipeline: 57 | def test_pipeline(self, capsys): 58 | # Create pipeline modules 59 | all_numbers = AllNumbers() 60 | evens = Evens() 61 | multiple_of_3 = MultipleOf(3) 62 | printer = Printer() 63 | first_10 = First(10) 64 | 65 | # Create pipeline 66 | pipeline = all_numbers | evens | multiple_of_3 | first_10 | printer 67 | 68 | # Iterate through pipeline 69 | for _ in pipeline: 70 | pass 71 | captured = capsys.readouterr() 72 | 73 | assert captured.out == "0\n6\n12\n18\n24\n30\n36\n42\n48\n54\n" 74 | -------------------------------------------------------------------------------- /tests/test_environment.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | def test_opencv_version(): 4 | assert cv2.__version__ >= '4.0' --------------------------------------------------------------------------------