diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..4dabdbe --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,66 @@ +name: Tests and Linting + +on: + push: + paths: + - '.github/workflows/tests.yml' + - 'retinaface/**' + - 'tests/**' + - 'requirements.txt' + - '.gitignore' + - 'setup.py' + pull_request: + paths: + - '.github/workflows/tests.yml' + - 'retinaface/**' + - 'tests/**' + - 'requirements.txt' + - '.gitignore' + - 'setup.py' + +jobs: + unit-tests: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.8] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + pip install . + + - name: Test with pytest + run: | + python -m pytest tests/ -s --disable-warnings + linting: + needs: unit-tests + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.8] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + pip install black + pip install . + + - name: Lint with pylint + run: | + python -m pylint retinaface/ --fail-under=10 \ No newline at end of file diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..8c89d8a --- /dev/null +++ b/.pylintrc @@ -0,0 +1,645 @@ +[MAIN] + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Load and enable all available extensions. Use --list-extensions to see a list +# all available extensions. +#enable-all-extensions= + +# In error mode, messages with a category besides ERROR or FATAL are +# suppressed, and no reports are done by default. Error mode is compatible with +# disabling specific errors. +#errors-only= + +# Always return a 0 (non-error) status code, even if lint errors are found. +# This is primarily useful in continuous integration scripts. +#exit-zero= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-allow-list= + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist= + +# Return non-zero exit code if any of these messages/categories are detected, +# even if score is above --fail-under value. Syntax same as enable. Messages +# specified are enabled, while categories only check already-enabled messages. +fail-on= + +# Specify a score threshold under which the program will exit with error. +fail-under=10 + +# Interpret the stdin as a python script, whose filename needs to be passed as +# the module_or_package argument. +#from-stdin= + +# Files or directories to be skipped. They should be base names, not paths. +ignore=CVS + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\' represents the directory delimiter on Windows systems, it +# can't be used as an escape character. +ignore-paths= + +# Files or directories matching the regular expression patterns are skipped. +# The regex matches against base names, not paths. The default value ignores +# Emacs file locks +ignore-patterns=^\.# + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs=1 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Minimum Python version to use for version dependent checks. Will default to +# the version used to run pylint. +py-version=3.9 + +# Discover python modules and packages in the file system subtree. +recursive=no + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# In verbose mode, extra non-checker-related info will be displayed. +#verbose= + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. If left empty, argument names will be checked with the set +# naming style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. If left empty, attribute names will be checked with the set naming +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Bad variable names regexes, separated by a comma. If names match any regex, +# they will always be refused +bad-names-rgxs= + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. If left empty, class attribute names will be checked +# with the set naming style. +#class-attribute-rgx= + +# Naming style matching correct class constant names. +class-const-naming-style=UPPER_CASE + +# Regular expression matching correct class constant names. Overrides class- +# const-naming-style. If left empty, class constant names will be checked with +# the set naming style. +#class-const-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. If left empty, class names will be checked with the set naming style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. If left empty, constant names will be checked with the set naming +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. If left empty, function names will be checked with the set +# naming style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + x1, + x2, + y1, + y2, + ex, + Run, + _ + +# Good variable names regexes, separated by a comma. If names match any regex, +# they will always be accepted +good-names-rgxs= + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. If left empty, inline iteration names will be checked +# with the set naming style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. If left empty, method names will be checked with the set naming style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. If left empty, module names will be checked with the set naming style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Regular expression matching correct type variable names. If left empty, type +# variable names will be checked with the set naming style. +#typevar-rgx= + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. If left empty, variable names will be checked with the set +# naming style. +#variable-rgx= + + +[CLASSES] + +# Warn about protected attribute access inside special methods +check-protected-access-in-special-methods=no + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[DESIGN] + +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +exclude-too-few-public-methods= + +# List of qualified class names to ignore when counting class parents (see +# R0901) +ignored-parents= + +# Maximum number of arguments for function / method. +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when caught. +overgeneral-exceptions=BaseException, + Exception + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module. +max-module-lines=1000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules= + +# Output a graph (.gv or any supported image format) of external dependencies +# to the given file (report RP0402 must not be disabled). +ext-import-graph= + +# Output a graph (.gv or any supported image format) of all (i.e. internal and +# external) dependencies to the given file (report RP0402 must not be +# disabled). +import-graph= + +# Output a graph (.gv or any supported image format) of internal dependencies +# to the given file (report RP0402 must not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, +# UNDEFINED. +confidence=HIGH, + CONTROL_FLOW, + INFERENCE, + INFERENCE_FAILURE, + UNDEFINED + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then re-enable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable=raw-checker-failed, + bad-inline-option, + locally-disabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + use-symbolic-message-instead, + import-error, + invalid-name, + missing-module-docstring, + missing-function-docstring, + missing-class-docstring, + too-many-arguments, + too-many-locals, + too-many-branches, + too-many-statements, + global-variable-undefined, + import-outside-toplevel, + singleton-comparison, + too-many-lines, + duplicate-code, + bare-except, + cyclic-import, + global-statement, + no-member, + no-name-in-module, + unrecognized-option, + consider-using-dict-items, + consider-iterating-dictionary, + unexpected-keyword-arg + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[METHOD_ARGS] + +# List of qualified names (i.e., library.method) which require a timeout +# parameter e.g. 'requests.api.get,requests.api.post' +timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + +# Regular expression of note tags to take in consideration. +notes-rgx= + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit,argparse.parse_error + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'fatal', 'error', 'warning', 'refactor', +# 'convention', and 'info' which contain the number of messages in each +# category, as well as 'statement' which is the total number of statements +# analyzed. This score is used by the global evaluation report (RP0004). +evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +#output-format= + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=yes + +# Signatures are removed from the similarity computation +ignore-signatures=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the 'python-enchant' package. +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear at the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of symbolic message names to ignore for Mixin members. +ignored-checks-for-mixins=no-member, + not-async-context-manager, + not-context-manager, + attribute-defined-outside-init + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# Regex pattern to define which classes are considered mixins. +mixin-class-rgx=.*[Mm]ixin + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d1857aa --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +test: + python -m pytest tests/ -s --disable-warnings + +lint: + python -m pylint retinaface/ --fail-under=10 \ No newline at end of file diff --git a/README.md b/README.md index fb4641c..b961e8e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
-[![PyPI Downloads](https://static.pepy.tech/personalized-badge/retina-face?period=total&units=international_system&left_color=grey&right_color=blue&left_text=pypi%20downloads)](https://pepy.tech/project/retina-face) +[![PyPI Downloads](https://static.pepy.tech/personalized-badge/retina-face?period=total&units=international_system&left_color=grey&right_color=blue&left_text=pip%20downloads)](https://pepy.tech/project/retina-face) [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/retina-face?color=green&label=conda%20downloads)](https://anaconda.org/conda-forge/retina-face) [![Stars](https://img.shields.io/github/stars/serengil/retinaface?color=yellow)](https://github.com/serengil/retinaface) [![License](http://img.shields.io/:license-MIT-green.svg?style=flat)](https://github.com/serengil/retinaface/blob/master/LICENSE) @@ -102,6 +102,10 @@ print(obj["verified"]) Notice that ArcFace got 99.40% accuracy on [LFW data set](https://sefiks.com/2020/08/27/labeled-faces-in-the-wild-for-face-recognition/) whereas human beings just have 97.53% confidence. +## Contribution [![Tests](https://github.com/serengil/retinaface/actions/workflows/tests.yml/badge.svg)](https://github.com/serengil/retinaface/actions/workflows/tests.yml) + +Pull requests are more than welcome! You should run the unit tests and linting locally before creating a PR. Commands `make test` and `make lint` will help you to run it locally. Once a PR created, GitHub test workflow will be run automatically and unit test results will be available in [GitHub actions](https://github.com/serengil/retinaface/actions) before approval. + ## Support There are many ways to support a project. Starring⭐️ the repo is just one 🙏 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e5ee965 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +numpy>=1.14.0 +gdown>=3.10.1 +Pillow>=5.2.0 +opencv-python>=3.4.4 +tensorflow>=1.9.0 \ No newline at end of file diff --git a/retinaface/RetinaFace.py b/retinaface/RetinaFace.py index d9f1af1..ffd8eb7 100644 --- a/retinaface/RetinaFace.py +++ b/retinaface/RetinaFace.py @@ -1,69 +1,82 @@ -import warnings -warnings.filterwarnings("ignore") - import os -os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' - -#--------------------------- +import warnings +import logging +from typing import Union, Any, Optional, Dict import numpy as np import tensorflow as tf -import cv2 from retinaface.model import retinaface_model from retinaface.commons import preprocess, postprocess -#--------------------------- +# pylint: disable=global-variable-undefined, no-name-in-module, unused-import, too-many-locals, redefined-outer-name, too-many-statements, too-many-arguments -import tensorflow as tf +# --------------------------- -#Limit the amount of reserved VRAM so that other scripts can be run in the same GPU as well -os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true' +# configurations +warnings.filterwarnings("ignore") +os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" +# Limit the amount of reserved VRAM so that other scripts can be run in the same GPU as well +os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true" -tf_version = int(tf.__version__.split(".")[0]) +tf_version = int(tf.__version__.split(".", maxsplit=1)[0]) if tf_version == 2: - import logging tf.get_logger().setLevel(logging.ERROR) + from tensorflow.keras.models import Model +else: + from keras.models import Model # --------------------------- -def build_model(): - +def build_model() -> Any: + """ + Builds retinaface model once and store it into memory + """ + # pylint: disable=invalid-name global model # singleton design pattern if not "model" in globals(): - model = tf.function( retinaface_model.build_model(), - input_signature=(tf.TensorSpec(shape=[None, None, None, 3], dtype=np.float32),) + input_signature=(tf.TensorSpec(shape=[None, None, None, 3], dtype=np.float32),), ) return model -def get_image(img_path): - if type(img_path) == str: # Load from file path - if not os.path.isfile(img_path): - raise ValueError("Input image file path (", img_path, ") does not exist.") - img = cv2.imread(img_path) - - elif isinstance(img_path, np.ndarray): # Use given NumPy array - img = img_path.copy() - - else: - raise ValueError("Invalid image input. Only file paths or a NumPy array accepted.") - - # Validate image shape - if len(img.shape) != 3 or np.prod(img.shape) == 0: - raise ValueError("Input image needs to have 3 channels at must not be empty.") - - return img - -def detect_faces(img_path, threshold=0.9, model=None, allow_upscaling=True): +def detect_faces( + img_path: Union[str, np.ndarray], + threshold: float = 0.9, + model: Optional[Model] = None, + allow_upscaling: bool = True, +) -> Dict[str, Any]: + """ + Detect the facial area for a given image + Args: + img_path (str or numpy array): given image + threshold (float): threshold for detection + model (Model): pre-trained model can be given + allow_upscaling (bool): allowing up-scaling + Returns: + detected faces as: + { + "face_1": { + "score": 0.9993440508842468, + "facial_area": [155, 81, 434, 443], + "landmarks": { + "right_eye": [257.82974, 209.64787], + "left_eye": [374.93427, 251.78687], + "nose": [303.4773, 299.91144], + "mouth_right": [228.37329, 338.73193], + "mouth_left": [320.21982, 374.58798] + } + } + } + """ resp = {} - img = get_image(img_path) + img = preprocess.get_image(img_path) # --------------------------- @@ -78,12 +91,16 @@ def detect_faces(img_path, threshold=0.9, model=None, allow_upscaling=True): _feat_stride_fpn = [32, 16, 8] _anchors_fpn = { - 'stride32': np.array([[-248., -248., 263., 263.], [-120., -120., 135., 135.]], dtype=np.float32), - 'stride16': np.array([[-56., -56., 71., 71.], [-24., -24., 39., 39.]], dtype=np.float32), - 'stride8': np.array([[-8., -8., 23., 23.], [0., 0., 15., 15.]], dtype=np.float32) + "stride32": np.array( + [[-248.0, -248.0, 263.0, 263.0], [-120.0, -120.0, 135.0, 135.0]], dtype=np.float32 + ), + "stride16": np.array( + [[-56.0, -56.0, 71.0, 71.0], [-24.0, -24.0, 39.0, 39.0]], dtype=np.float32 + ), + "stride8": np.array([[-8.0, -8.0, 23.0, 23.0], [0.0, 0.0, 15.0, 15.0]], dtype=np.float32), } - _num_anchors = {'stride32': 2, 'stride16': 2, 'stride8': 2} + _num_anchors = {"stride32": 2, "stride16": 2, "stride8": 2} # --------------------------- @@ -95,24 +112,23 @@ def detect_faces(img_path, threshold=0.9, model=None, allow_upscaling=True): net_out = [elt.numpy() for elt in net_out] sym_idx = 0 - for _idx, s in enumerate(_feat_stride_fpn): - _key = 'stride%s' % s + for _, s in enumerate(_feat_stride_fpn): + # _key = f"stride{s}" scores = net_out[sym_idx] - scores = scores[:, :, :, _num_anchors['stride%s' % s]:] + scores = scores[:, :, :, _num_anchors[f"stride{s}"] :] bbox_deltas = net_out[sym_idx + 1] height, width = bbox_deltas.shape[1], bbox_deltas.shape[2] - A = _num_anchors['stride%s' % s] + A = _num_anchors[f"stride{s}"] K = height * width - anchors_fpn = _anchors_fpn['stride%s' % s] + anchors_fpn = _anchors_fpn[f"stride{s}"] anchors = postprocess.anchors_plane(height, width, s, anchors_fpn) anchors = anchors.reshape((K * A, 4)) scores = scores.reshape((-1, 1)) bbox_stds = [1.0, 1.0, 1.0, 1.0] - bbox_deltas = bbox_deltas - bbox_pred_len = bbox_deltas.shape[3]//A + bbox_pred_len = bbox_deltas.shape[3] // A bbox_deltas = bbox_deltas.reshape((-1, bbox_pred_len)) bbox_deltas[:, 0::4] = bbox_deltas[:, 0::4] * bbox_stds[0] bbox_deltas[:, 1::4] = bbox_deltas[:, 1::4] * bbox_stds[1] @@ -135,8 +151,8 @@ def detect_faces(img_path, threshold=0.9, model=None, allow_upscaling=True): scores_list.append(scores) landmark_deltas = net_out[sym_idx + 2] - landmark_pred_len = landmark_deltas.shape[3]//A - landmark_deltas = landmark_deltas.reshape((-1, 5, landmark_pred_len//5)) + landmark_pred_len = landmark_deltas.shape[3] // A + landmark_deltas = landmark_deltas.reshape((-1, 5, landmark_pred_len // 5)) landmarks = postprocess.landmark_pred(anchors, landmark_deltas) landmarks = landmarks[order, :] @@ -145,8 +161,8 @@ def detect_faces(img_path, threshold=0.9, model=None, allow_upscaling=True): sym_idx += 3 proposals = np.vstack(proposals_list) - - if proposals.shape[0]==0: + + if proposals.shape[0] == 0: return resp scores = np.vstack(scores_list) @@ -160,8 +176,8 @@ def detect_faces(img_path, threshold=0.9, model=None, allow_upscaling=True): pre_det = np.hstack((proposals[:, 0:4], scores)).astype(np.float32, copy=False) - #nms = cpu_nms_wrapper(nms_threshold) - #keep = nms(pre_det) + # nms = cpu_nms_wrapper(nms_threshold) + # keep = nms(pre_det) keep = postprocess.cpu_nms(pre_det, nms_threshold) det = np.hstack((pre_det, proposals[:, 4:])) @@ -169,8 +185,7 @@ def detect_faces(img_path, threshold=0.9, model=None, allow_upscaling=True): landmarks = landmarks[keep] for idx, face in enumerate(det): - - label = 'face_'+str(idx+1) + label = "face_" + str(idx + 1) resp[label] = {} resp[label]["score"] = face[4] @@ -186,41 +201,57 @@ def detect_faces(img_path, threshold=0.9, model=None, allow_upscaling=True): return resp -def extract_faces(img_path, threshold=0.9, model=None, align=True, allow_upscaling=True, expand_face_area: int = 0): - +def extract_faces( + img_path: Union[str, np.ndarray], + threshold: float = 0.9, + model: Optional[Model] = None, + align: bool = True, + allow_upscaling: bool = True, + expand_face_area: int = 0, +) -> list: + """ + Extract detected and aligned faces + Args: + img_path (str or numpy): given image + threshold (float): detection threshold + model (Model): pre-trained model can be passed to the function + align (bool): enable or disable alignment + allow_upscaling (bool) + expand_face_area (int): set this to something to expand facial area with given pixels + """ resp = [] # --------------------------- - img = get_image(img_path) + img = preprocess.get_image(img_path) # --------------------------- - obj = detect_faces(img_path=img, threshold=threshold, model=model, allow_upscaling=allow_upscaling) - - if type(obj) == dict: - for key in obj: - identity = obj[key] + obj = detect_faces( + img_path=img, threshold=threshold, model=model, allow_upscaling=allow_upscaling + ) + if isinstance(obj, dict): + for _, identity in obj.items(): facial_area = identity["facial_area"] + # expand the facial area to be extracted and stay within img.shape limits x1 = max(0, facial_area[0] - expand_face_area) # expand left y1 = max(0, facial_area[1] - expand_face_area) # expand top x2 = min(img.shape[1], facial_area[2] + expand_face_area) # expand right y2 = min(img.shape[0], facial_area[3] + expand_face_area) # expand bottom - facial_img = img[y1: y2, x1: x2] + facial_img = img[y1:y2, x1:x2] - if align == True: + if align is True: landmarks = identity["landmarks"] left_eye = landmarks["left_eye"] right_eye = landmarks["right_eye"] nose = landmarks["nose"] - mouth_right = landmarks["mouth_right"] - mouth_left = landmarks["mouth_left"] + # mouth_right = landmarks["mouth_right"] + # mouth_left = landmarks["mouth_left"] facial_img = postprocess.alignment_procedure(facial_img, right_eye, left_eye, nose) resp.append(facial_img[:, :, ::-1]) - # elif type(obj) == tuple: return resp diff --git a/retinaface/commons/logger.py b/retinaface/commons/logger.py new file mode 100644 index 0000000..4fd7976 --- /dev/null +++ b/retinaface/commons/logger.py @@ -0,0 +1,41 @@ +import os +import logging +from datetime import datetime + +# pylint: disable=broad-except +class Logger: + def __init__(self, module=None): + self.module = module + log_level = os.environ.get("RETINAFACE_LOG_LEVEL", str(logging.INFO)) + try: + self.log_level = int(log_level) + except Exception as err: + self.dump_log( + f"Exception while parsing $RETINAFACE_LOG_LEVEL." + f"Expected int but it is {log_level} ({str(err)})." + "Setting app log level to info." + ) + self.log_level = logging.INFO + + def info(self, message): + if self.log_level <= logging.INFO: + self.dump_log(f"{message}") + + def debug(self, message): + if self.log_level <= logging.DEBUG: + self.dump_log(f"🕷️ {message}") + + def warn(self, message): + if self.log_level <= logging.WARNING: + self.dump_log(f"⚠️ {message}") + + def error(self, message): + if self.log_level <= logging.ERROR: + self.dump_log(f"🔴 {message}") + + def critical(self, message): + if self.log_level <= logging.CRITICAL: + self.dump_log(f"💥 {message}") + + def dump_log(self, message): + print(f"{str(datetime.now())[2:-7]} - {message}") diff --git a/retinaface/commons/postprocess.py b/retinaface/commons/postprocess.py index dbcfb44..12e819f 100644 --- a/retinaface/commons/postprocess.py +++ b/retinaface/commons/postprocess.py @@ -1,72 +1,77 @@ +import math import numpy as np from PIL import Image -import math -import cv2 -def findEuclideanDistance(source_representation, test_representation): + +# pylint: disable=unused-argument + + +def findEuclideanDistance( + source_representation: np.ndarray, test_representation: np.ndarray +) -> float: + """ + Find euclidean distance between 2 vectors + Args: + source_representation (numpy array) + test_representation (numpy array) + Returns + distance + """ euclidean_distance = source_representation - test_representation euclidean_distance = np.sum(np.multiply(euclidean_distance, euclidean_distance)) euclidean_distance = np.sqrt(euclidean_distance) return euclidean_distance -#this function copied from the deepface repository: https://github.com/serengil/deepface/blob/master/deepface/commons/functions.py -def alignment_procedure(img, left_eye, right_eye, nose): - #this function aligns given face in img based on left and right eye coordinates - - #left eye is the eye appearing on the left (right eye of the person) - #left top point is (0, 0) +def alignment_procedure(img: np.ndarray, left_eye: tuple, right_eye: tuple, nose: tuple): + """ + Alignma given face with respect to the left and right eye coordinates. + Left eye is the eye appearing on the left (right eye of the person). Left top point is (0, 0) + Args: + img (numpy array): given image + left_eye (tuple): left eye coordinates. + Left eye is appearing on the left of image (right eye of the person) + right_eye (tuple): right eye coordinates. + Right eye is appearing on the right of image (left eye of the person) + nose (tuple): coordinates of nose + """ left_eye_x, left_eye_y = left_eye right_eye_x, right_eye_y = right_eye - #----------------------- - #decide the image is inverse - - center_eyes = (int((left_eye_x + right_eye_x) / 2), int((left_eye_y + right_eye_y) / 2)) - - if False: - - img = cv2.circle(img, (int(left_eye[0]), int(left_eye[1])), 2, (0, 255, 255), 2) - img = cv2.circle(img, (int(right_eye[0]), int(right_eye[1])), 2, (255, 0, 0), 2) - img = cv2.circle(img, center_eyes, 2, (0, 0, 255), 2) - img = cv2.circle(img, (int(nose[0]), int(nose[1])), 2, (255, 255, 255), 2) - - #----------------------- - #find rotation direction - + # ----------------------- + # find rotation direction if left_eye_y > right_eye_y: point_3rd = (right_eye_x, left_eye_y) - direction = -1 #rotate same direction to clock + direction = -1 # rotate same direction to clock else: point_3rd = (left_eye_x, right_eye_y) - direction = 1 #rotate inverse direction of clock + direction = 1 # rotate inverse direction of clock - #----------------------- - #find length of triangle edges + # ----------------------- + # find length of triangle edges a = findEuclideanDistance(np.array(left_eye), np.array(point_3rd)) b = findEuclideanDistance(np.array(right_eye), np.array(point_3rd)) c = findEuclideanDistance(np.array(right_eye), np.array(left_eye)) - #----------------------- - - #apply cosine rule + # ----------------------- + # apply cosine rule + if b != 0 and c != 0: # this multiplication causes division by zero in cos_a calculation - if b != 0 and c != 0: #this multiplication causes division by zero in cos_a calculation + cos_a = (b * b + c * c - a * a) / (2 * b * c) - cos_a = (b*b + c*c - a*a)/(2*b*c) - - #PR15: While mathematically cos_a must be within the closed range [-1.0, 1.0], floating point errors would produce cases violating this - #In fact, we did come across a case where cos_a took the value 1.0000000169176173, which lead to a NaN from the following np.arccos step + # PR15: While mathematically cos_a must be within the closed range [-1.0, 1.0], + # floating point errors would produce cases violating this + # In fact, we did come across a case where cos_a took the value 1.0000000169176173 + # which lead to a NaN from the following np.arccos step cos_a = min(1.0, max(-1.0, cos_a)) - - - angle = np.arccos(cos_a) #angle in radian - angle = (angle * 180) / math.pi #radian to degree - #----------------------- - #rotate base image + angle = np.arccos(cos_a) # angle in radian + angle = (angle * 180) / math.pi # radian to degree + + # ----------------------- + # rotate base image if direction == -1: angle = 90 - angle @@ -74,12 +79,16 @@ def alignment_procedure(img, left_eye, right_eye, nose): img = Image.fromarray(img) img = np.array(img.rotate(direction * angle)) - #----------------------- + # ----------------------- + + return img - return img #return img anyway -#this function is copied from the following code snippet: https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/retinaface.py def bbox_pred(boxes, box_deltas): + """ + This function is copied from the following code snippet: + https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/retinaface.py + """ if boxes.shape[0] == 0: return np.zeros((0, box_deltas.shape[1])) @@ -109,15 +118,19 @@ def bbox_pred(boxes, box_deltas): # y2 pred_boxes[:, 3:4] = pred_ctr_y + 0.5 * (pred_h - 1.0) - if box_deltas.shape[1]>4: - pred_boxes[:,4:] = box_deltas[:,4:] + if box_deltas.shape[1] > 4: + pred_boxes[:, 4:] = box_deltas[:, 4:] return pred_boxes -# This function copied from the following code snippet: https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/retinaface.py + def landmark_pred(boxes, landmark_deltas): + """ + This function copied from the following code snippet + https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/retinaface.py + """ if boxes.shape[0] == 0: - return np.zeros((0, landmark_deltas.shape[1])) + return np.zeros((0, landmark_deltas.shape[1])) boxes = boxes.astype(float, copy=False) widths = boxes[:, 2] - boxes[:, 0] + 1.0 heights = boxes[:, 3] - boxes[:, 1] + 1.0 @@ -125,12 +138,16 @@ def landmark_pred(boxes, landmark_deltas): ctr_y = boxes[:, 1] + 0.5 * (heights - 1.0) pred = landmark_deltas.copy() for i in range(5): - pred[:,i,0] = landmark_deltas[:,i,0]*widths + ctr_x - pred[:,i,1] = landmark_deltas[:,i,1]*heights + ctr_y + pred[:, i, 0] = landmark_deltas[:, i, 0] * widths + ctr_x + pred[:, i, 1] = landmark_deltas[:, i, 1] * heights + ctr_y return pred -# This function copied from rcnn module of retinaface-tf2 project: https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/rcnn/processing/bbox_transform.py + def clip_boxes(boxes, im_shape): + """ + This function copied from rcnn module of retinaface-tf2 project + https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/rcnn/processing/bbox_transform.py + """ # x1 >= 0 boxes[:, 0::4] = np.maximum(np.minimum(boxes[:, 0::4], im_shape[1] - 1), 0) # y1 >= 0 @@ -141,17 +158,27 @@ def clip_boxes(boxes, im_shape): boxes[:, 3::4] = np.maximum(np.minimum(boxes[:, 3::4], im_shape[0] - 1), 0) return boxes -#this function is mainly based on the following code snippet: https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/rcnn/cython/anchors.pyx + def anchors_plane(height, width, stride, base_anchors): + """ + This function is mainly based on the following code snippet + https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/rcnn/cython/anchors.pyx + """ A = base_anchors.shape[0] c_0_2 = np.tile(np.arange(0, width)[np.newaxis, :, np.newaxis, np.newaxis], (height, 1, A, 1)) c_1_3 = np.tile(np.arange(0, height)[:, np.newaxis, np.newaxis, np.newaxis], (1, width, A, 1)) - all_anchors = np.concatenate([c_0_2, c_1_3, c_0_2, c_1_3], axis=-1) * stride + np.tile(base_anchors[np.newaxis, np.newaxis, :, :], (height, width, 1, 1)) + all_anchors = np.concatenate([c_0_2, c_1_3, c_0_2, c_1_3], axis=-1) * stride + np.tile( + base_anchors[np.newaxis, np.newaxis, :, :], (height, width, 1, 1) + ) return all_anchors -#this function is mainly based on the following code snippet: https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/rcnn/cython/cpu_nms.pyx -#Fast R-CNN by Ross Girshick + def cpu_nms(dets, threshold): + """ + This function is mainly based on the following code snippet + https://github.com/StanislasBertrand/RetinaFace-tf2/blob/master/rcnn/cython/cpu_nms.pyx + Fast R-CNN by Ross Girshick + """ x1 = dets[:, 0] y1 = dets[:, 1] x2 = dets[:, 2] @@ -170,14 +197,21 @@ def cpu_nms(dets, threshold): if suppressed[i] == 1: continue keep.append(i) - ix1 = x1[i]; iy1 = y1[i]; ix2 = x2[i]; iy2 = y2[i] + ix1 = x1[i] + iy1 = y1[i] + ix2 = x2[i] + iy2 = y2[i] iarea = areas[i] for _j in range(_i + 1, ndets): j = order[_j] if suppressed[j] == 1: continue - xx1 = max(ix1, x1[j]); yy1 = max(iy1, y1[j]); xx2 = min(ix2, x2[j]); yy2 = min(iy2, y2[j]) - w = max(0.0, xx2 - xx1 + 1); h = max(0.0, yy2 - yy1 + 1) + xx1 = max(ix1, x1[j]) + yy1 = max(iy1, y1[j]) + xx2 = min(ix2, x2[j]) + yy2 = min(iy2, y2[j]) + w = max(0.0, xx2 - xx1 + 1) + h = max(0.0, yy2 - yy1 + 1) inter = w * h ovr = inter / (iarea + areas[j] - inter) if ovr >= threshold: diff --git a/retinaface/commons/preprocess.py b/retinaface/commons/preprocess.py index ec96902..b3f471f 100644 --- a/retinaface/commons/preprocess.py +++ b/retinaface/commons/preprocess.py @@ -1,10 +1,89 @@ +import os +import base64 +from pathlib import Path +from typing import Union +import requests +from PIL import Image import numpy as np import cv2 -# This function is modified from the following code snippet: -# https://github.com/StanislasBertrand/RetinaFace-tf2/blob/5f68ce8130889384cb8aca937a270cea4ef2d020/retinaface.py#L49-L74 -def resize_image(img, scales, allow_upscaling): +def get_image(img_uri: Union[str, np.ndarray]) -> np.ndarray: + """ + Load the given image + Args: + img_path (str or numpy array): exact image path, pre-loaded numpy array (BGR format) + , base64 encoded string and urls are welcome + Returns: + image itself + """ + # if it is pre-loaded numpy array + if isinstance(img_uri, np.ndarray): # Use given NumPy array + img = img_uri.copy() + + # if it is base64 encoded string + elif isinstance(img_uri, str) and img_uri.startswith("data:image/"): + img = load_base64_img(img_uri) + + # if it is an external url + elif isinstance(img_uri, str) and img_uri.startswith("http"): + img = np.array( + Image.open(requests.get(img_uri, stream=True, timeout=60).raw).convert("BGR") + ) + + # then it has to be a path on filesystem + elif isinstance(img_uri, str): + if isinstance(img_uri, Path): + img_uri = str(img_uri) + + if not os.path.isfile(img_uri): + raise ValueError(f"Input image file path ({img_uri}) does not exist.") + + # pylint: disable=no-member + img = cv2.imread(img_uri) + + else: + raise ValueError( + f"Invalid image input - {img_uri}." + "Exact paths, pre-loaded numpy arrays, base64 encoded " + "strings and urls are welcome." + ) + + # Validate image shape + if len(img.shape) != 3 or np.prod(img.shape) == 0: + raise ValueError("Input image needs to have 3 channels at must not be empty.") + + return img + + +def load_base64_img(uri) -> np.ndarray: + """Load image from base64 string. + + Args: + uri: a base64 string. + + Returns: + numpy array: the loaded image. + """ + encoded_data = uri.split(",")[1] + nparr = np.fromstring(base64.b64decode(encoded_data), np.uint8) + img_bgr = cv2.imdecode(nparr, cv2.IMREAD_COLOR) + # img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) + return img_bgr + + +def resize_image(img: np.ndarray, scales: list, allow_upscaling: bool) -> tuple: + """ + This function is modified from the following code snippet: + https://github.com/StanislasBertrand/RetinaFace-tf2/blob/5f68ce8130889384cb8aca937a270cea4ef2d020/retinaface.py#L49-L74 + + Args: + img (numpy array): given image + scales (list) + allow_upscaling (bool) + Returns + resized image, im_scale + """ img_h, img_w = img.shape[0:2] target_size = scales[0] max_size = scales[1] @@ -22,21 +101,21 @@ def resize_image(img, scales, allow_upscaling): im_scale = max_size / float(im_size_max) if im_scale != 1.0: - img = cv2.resize( - img, - None, - None, - fx=im_scale, - fy=im_scale, - interpolation=cv2.INTER_LINEAR - ) + img = cv2.resize(img, None, None, fx=im_scale, fy=im_scale, interpolation=cv2.INTER_LINEAR) return img, im_scale -# This function is modified from the following code snippet: -# https://github.com/StanislasBertrand/RetinaFace-tf2/blob/5f68ce8130889384cb8aca937a270cea4ef2d020/retinaface.py#L76-L96 -def preprocess_image(img, allow_upscaling): +def preprocess_image(img: np.ndarray, allow_upscaling: bool) -> tuple: + """ + This function is modified from the following code snippet: + https://github.com/StanislasBertrand/RetinaFace-tf2/blob/5f68ce8130889384cb8aca937a270cea4ef2d020/retinaface.py#L76-L96 + Args: + img (numpy array): given image + allow_upscaling (bool) + Returns: + tensor, image shape, im_scale + """ pixel_means = np.array([0.0, 0.0, 0.0], dtype=np.float32) pixel_stds = np.array([1.0, 1.0, 1.0], dtype=np.float32) pixel_scale = float(1.0) @@ -48,6 +127,8 @@ def preprocess_image(img, allow_upscaling): # Make image scaling + BGR2RGB conversion + transpose (N,H,W,C) to (N,C,H,W) for i in range(3): - im_tensor[0, :, :, i] = (img[:, :, 2 - i] / pixel_scale - pixel_means[2 - i]) / pixel_stds[2 - i] + im_tensor[0, :, :, i] = (img[:, :, 2 - i] / pixel_scale - pixel_means[2 - i]) / pixel_stds[ + 2 - i + ] return im_tensor, img.shape[0:2], im_scale diff --git a/retinaface/model/retinaface_model.py b/retinaface/model/retinaface_model.py index a2cc022..881a41a 100644 --- a/retinaface/model/retinaface_model.py +++ b/retinaface/model/retinaface_model.py @@ -1,468 +1,1028 @@ -import tensorflow as tf -import gdown -from pathlib import Path import os +from pathlib import Path +import gdown +import tensorflow as tf +from retinaface.commons.logger import Logger + +logger = Logger(module="retinaface/model/retinaface_model.py") -tf_version = int(tf.__version__.split(".")[0]) +# pylint: disable=too-many-statements, no-name-in-module + +# configurations + +tf_version = int(tf.__version__.split(".", maxsplit=1)[0]) if tf_version == 1: from keras.models import Model - from keras.layers import Input, BatchNormalization, ZeroPadding2D, Conv2D, ReLU, MaxPool2D, Add, UpSampling2D, concatenate, Softmax + from keras.layers import ( + Input, + BatchNormalization, + ZeroPadding2D, + Conv2D, + ReLU, + MaxPool2D, + Add, + UpSampling2D, + concatenate, + Softmax, + ) else: from tensorflow.keras.models import Model - from tensorflow.keras.layers import Input, BatchNormalization, ZeroPadding2D, Conv2D, ReLU, MaxPool2D, Add, UpSampling2D, concatenate, Softmax - -def load_weights(model): - - home = str(os.getenv('DEEPFACE_HOME', default=Path.home())) - - exact_file = home+'/.deepface/weights/retinaface.h5' - #url = 'https://drive.google.com/file/d/1K3Eq2k1b9dpKkucZjPAiCCnNzfCMosK4' - #url = 'https://drive.google.com/uc?id=1K3Eq2k1b9dpKkucZjPAiCCnNzfCMosK4' - url = 'https://github.com/serengil/deepface_models/releases/download/v1.0/retinaface.h5' - - #----------------------------- - - if not os.path.exists(home+"/.deepface"): - os.mkdir(home+"/.deepface") - print("Directory ",home,"/.deepface created") - - if not os.path.exists(home+"/.deepface/weights"): - os.mkdir(home+"/.deepface/weights") - print("Directory ",home,"/.deepface/weights created") - - #----------------------------- - - if os.path.isfile(exact_file) != True: - print("retinaface.h5 will be downloaded from the url "+url) + from tensorflow.keras.layers import ( + Input, + BatchNormalization, + ZeroPadding2D, + Conv2D, + ReLU, + MaxPool2D, + Add, + UpSampling2D, + concatenate, + Softmax, + ) + + +def load_weights(model: Model): + """ + Loading pre-trained weights for the RetinaFace model + Args: + model (Model): retinaface model structure with randon weights + Returns: + model (Model): retinaface model with its structure and pre-trained weights + + """ + home = str(os.getenv("DEEPFACE_HOME", default=str(Path.home()))) + + exact_file = home + "/.deepface/weights/retinaface.h5" + url = "https://github.com/serengil/deepface_models/releases/download/v1.0/retinaface.h5" + + # ----------------------------- + + if not os.path.exists(home + "/.deepface"): + os.mkdir(home + "/.deepface") + logger.info(f"Directory {home}/.deepface created") + + if not os.path.exists(home + "/.deepface/weights"): + os.mkdir(home + "/.deepface/weights") + logger.info(f"Directory {home}/.deepface/weights created") + + # ----------------------------- + + if os.path.isfile(exact_file) is not True: + logger.info(f"retinaface.h5 will be downloaded from the url {url}") gdown.download(url, exact_file, quiet=False) - #----------------------------- + # ----------------------------- - #gdown should download the pretrained weights here. If it does not still exist, then throw an exception. - if os.path.isfile(exact_file) != True: - raise ValueError("Pre-trained weight could not be loaded!" - +" You might try to download the pre-trained weights from the url "+ url - + " and copy it to the ", exact_file, "manually.") + # gdown should download the pretrained weights here. + # If it does not still exist, then throw an exception. + if os.path.isfile(exact_file) is not True: + raise ValueError( + "Pre-trained weight could not be loaded!" + + " You might try to download the pre-trained weights from the url " + + url + + " and copy it to the ", + exact_file, + "manually.", + ) model.load_weights(exact_file) return model -def build_model(): - data = Input(dtype=tf.float32, shape=(None, None, 3), name='data') +def build_model() -> Model: + """ + Build RetinaFace model + """ + data = Input(dtype=tf.float32, shape=(None, None, 3), name="data") - bn_data = BatchNormalization(epsilon=1.9999999494757503e-05, name='bn_data', trainable=False)(data) + bn_data = BatchNormalization(epsilon=1.9999999494757503e-05, name="bn_data", trainable=False)( + data + ) conv0_pad = ZeroPadding2D(padding=tuple([3, 3]))(bn_data) - conv0 = Conv2D(filters = 64, kernel_size = (7, 7), name = 'conv0', strides = [2, 2], padding = 'VALID', use_bias = False)(conv0_pad) + conv0 = Conv2D( + filters=64, + kernel_size=(7, 7), + name="conv0", + strides=[2, 2], + padding="VALID", + use_bias=False, + )(conv0_pad) - bn0 = BatchNormalization(epsilon=1.9999999494757503e-05, name='bn0', trainable=False)(conv0) + bn0 = BatchNormalization(epsilon=1.9999999494757503e-05, name="bn0", trainable=False)(conv0) - relu0 = ReLU(name='relu0')(bn0) + relu0 = ReLU(name="relu0")(bn0) pooling0_pad = ZeroPadding2D(padding=tuple([1, 1]))(relu0) - pooling0 = MaxPool2D((3, 3), (2, 2), padding='VALID', name='pooling0')(pooling0_pad) + pooling0 = MaxPool2D((3, 3), (2, 2), padding="VALID", name="pooling0")(pooling0_pad) - stage1_unit1_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage1_unit1_bn1', trainable=False)(pooling0) + stage1_unit1_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage1_unit1_bn1", trainable=False + )(pooling0) - stage1_unit1_relu1 = ReLU(name='stage1_unit1_relu1')(stage1_unit1_bn1) + stage1_unit1_relu1 = ReLU(name="stage1_unit1_relu1")(stage1_unit1_bn1) - stage1_unit1_conv1 = Conv2D(filters = 64, kernel_size = (1, 1), name = 'stage1_unit1_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit1_relu1) + stage1_unit1_conv1 = Conv2D( + filters=64, + kernel_size=(1, 1), + name="stage1_unit1_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit1_relu1) - stage1_unit1_sc = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage1_unit1_sc', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit1_relu1) + stage1_unit1_sc = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage1_unit1_sc", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit1_relu1) - stage1_unit1_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage1_unit1_bn2', trainable=False)(stage1_unit1_conv1) + stage1_unit1_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage1_unit1_bn2", trainable=False + )(stage1_unit1_conv1) - stage1_unit1_relu2 = ReLU(name='stage1_unit1_relu2')(stage1_unit1_bn2) + stage1_unit1_relu2 = ReLU(name="stage1_unit1_relu2")(stage1_unit1_bn2) stage1_unit1_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage1_unit1_relu2) - stage1_unit1_conv2 = Conv2D(filters = 64, kernel_size = (3, 3), name = 'stage1_unit1_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit1_conv2_pad) - - stage1_unit1_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage1_unit1_bn3', trainable=False)(stage1_unit1_conv2) - - stage1_unit1_relu3 = ReLU(name='stage1_unit1_relu3')(stage1_unit1_bn3) - - stage1_unit1_conv3 = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage1_unit1_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit1_relu3) - - plus0_v1 = Add()([stage1_unit1_conv3 , stage1_unit1_sc]) - - stage1_unit2_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage1_unit2_bn1', trainable=False)(plus0_v1) - - stage1_unit2_relu1 = ReLU(name='stage1_unit2_relu1')(stage1_unit2_bn1) - - stage1_unit2_conv1 = Conv2D(filters = 64, kernel_size = (1, 1), name = 'stage1_unit2_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit2_relu1) - - stage1_unit2_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage1_unit2_bn2', trainable=False)(stage1_unit2_conv1) - - stage1_unit2_relu2 = ReLU(name='stage1_unit2_relu2')(stage1_unit2_bn2) + stage1_unit1_conv2 = Conv2D( + filters=64, + kernel_size=(3, 3), + name="stage1_unit1_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit1_conv2_pad) + + stage1_unit1_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage1_unit1_bn3", trainable=False + )(stage1_unit1_conv2) + + stage1_unit1_relu3 = ReLU(name="stage1_unit1_relu3")(stage1_unit1_bn3) + + stage1_unit1_conv3 = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage1_unit1_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit1_relu3) + + plus0_v1 = Add()([stage1_unit1_conv3, stage1_unit1_sc]) + + stage1_unit2_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage1_unit2_bn1", trainable=False + )(plus0_v1) + + stage1_unit2_relu1 = ReLU(name="stage1_unit2_relu1")(stage1_unit2_bn1) + + stage1_unit2_conv1 = Conv2D( + filters=64, + kernel_size=(1, 1), + name="stage1_unit2_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit2_relu1) + + stage1_unit2_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage1_unit2_bn2", trainable=False + )(stage1_unit2_conv1) + + stage1_unit2_relu2 = ReLU(name="stage1_unit2_relu2")(stage1_unit2_bn2) stage1_unit2_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage1_unit2_relu2) - stage1_unit2_conv2 = Conv2D(filters = 64, kernel_size = (3, 3), name = 'stage1_unit2_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit2_conv2_pad) - - stage1_unit2_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage1_unit2_bn3', trainable=False)(stage1_unit2_conv2) - - stage1_unit2_relu3 = ReLU(name='stage1_unit2_relu3')(stage1_unit2_bn3) - - stage1_unit2_conv3 = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage1_unit2_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit2_relu3) - - plus1_v2 = Add()([stage1_unit2_conv3 , plus0_v1]) - - stage1_unit3_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage1_unit3_bn1', trainable=False)(plus1_v2) - - stage1_unit3_relu1 = ReLU(name='stage1_unit3_relu1')(stage1_unit3_bn1) - - stage1_unit3_conv1 = Conv2D(filters = 64, kernel_size = (1, 1), name = 'stage1_unit3_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit3_relu1) - - stage1_unit3_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage1_unit3_bn2', trainable=False)(stage1_unit3_conv1) - - stage1_unit3_relu2 = ReLU(name='stage1_unit3_relu2')(stage1_unit3_bn2) + stage1_unit2_conv2 = Conv2D( + filters=64, + kernel_size=(3, 3), + name="stage1_unit2_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit2_conv2_pad) + + stage1_unit2_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage1_unit2_bn3", trainable=False + )(stage1_unit2_conv2) + + stage1_unit2_relu3 = ReLU(name="stage1_unit2_relu3")(stage1_unit2_bn3) + + stage1_unit2_conv3 = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage1_unit2_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit2_relu3) + + plus1_v2 = Add()([stage1_unit2_conv3, plus0_v1]) + + stage1_unit3_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage1_unit3_bn1", trainable=False + )(plus1_v2) + + stage1_unit3_relu1 = ReLU(name="stage1_unit3_relu1")(stage1_unit3_bn1) + + stage1_unit3_conv1 = Conv2D( + filters=64, + kernel_size=(1, 1), + name="stage1_unit3_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit3_relu1) + + stage1_unit3_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage1_unit3_bn2", trainable=False + )(stage1_unit3_conv1) + + stage1_unit3_relu2 = ReLU(name="stage1_unit3_relu2")(stage1_unit3_bn2) stage1_unit3_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage1_unit3_relu2) - stage1_unit3_conv2 = Conv2D(filters = 64, kernel_size = (3, 3), name = 'stage1_unit3_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit3_conv2_pad) - - stage1_unit3_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage1_unit3_bn3', trainable=False)(stage1_unit3_conv2) - - stage1_unit3_relu3 = ReLU(name='stage1_unit3_relu3')(stage1_unit3_bn3) - - stage1_unit3_conv3 = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage1_unit3_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage1_unit3_relu3) - - plus2 = Add()([stage1_unit3_conv3 , plus1_v2]) - - stage2_unit1_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit1_bn1', trainable=False)(plus2) - - stage2_unit1_relu1 = ReLU(name='stage2_unit1_relu1')(stage2_unit1_bn1) - - stage2_unit1_conv1 = Conv2D(filters = 128, kernel_size = (1, 1), name = 'stage2_unit1_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit1_relu1) - - stage2_unit1_sc = Conv2D(filters = 512, kernel_size = (1, 1), name = 'stage2_unit1_sc', strides = [2, 2], padding = 'VALID', use_bias = False)(stage2_unit1_relu1) - - stage2_unit1_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit1_bn2', trainable=False)(stage2_unit1_conv1) - - stage2_unit1_relu2 = ReLU(name='stage2_unit1_relu2')(stage2_unit1_bn2) + stage1_unit3_conv2 = Conv2D( + filters=64, + kernel_size=(3, 3), + name="stage1_unit3_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit3_conv2_pad) + + stage1_unit3_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage1_unit3_bn3", trainable=False + )(stage1_unit3_conv2) + + stage1_unit3_relu3 = ReLU(name="stage1_unit3_relu3")(stage1_unit3_bn3) + + stage1_unit3_conv3 = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage1_unit3_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage1_unit3_relu3) + + plus2 = Add()([stage1_unit3_conv3, plus1_v2]) + + stage2_unit1_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit1_bn1", trainable=False + )(plus2) + + stage2_unit1_relu1 = ReLU(name="stage2_unit1_relu1")(stage2_unit1_bn1) + + stage2_unit1_conv1 = Conv2D( + filters=128, + kernel_size=(1, 1), + name="stage2_unit1_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit1_relu1) + + stage2_unit1_sc = Conv2D( + filters=512, + kernel_size=(1, 1), + name="stage2_unit1_sc", + strides=[2, 2], + padding="VALID", + use_bias=False, + )(stage2_unit1_relu1) + + stage2_unit1_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit1_bn2", trainable=False + )(stage2_unit1_conv1) + + stage2_unit1_relu2 = ReLU(name="stage2_unit1_relu2")(stage2_unit1_bn2) stage2_unit1_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage2_unit1_relu2) - stage2_unit1_conv2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'stage2_unit1_conv2', strides = [2, 2], padding = 'VALID', use_bias = False)(stage2_unit1_conv2_pad) - - stage2_unit1_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit1_bn3', trainable=False)(stage2_unit1_conv2) - - stage2_unit1_relu3 = ReLU(name='stage2_unit1_relu3')(stage2_unit1_bn3) - - stage2_unit1_conv3 = Conv2D(filters = 512, kernel_size = (1, 1), name = 'stage2_unit1_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit1_relu3) - - plus3 = Add()([stage2_unit1_conv3 , stage2_unit1_sc]) - - stage2_unit2_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit2_bn1', trainable=False)(plus3) - - stage2_unit2_relu1 = ReLU(name='stage2_unit2_relu1')(stage2_unit2_bn1) - - stage2_unit2_conv1 = Conv2D(filters = 128, kernel_size = (1, 1), name = 'stage2_unit2_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit2_relu1) - - stage2_unit2_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit2_bn2', trainable=False)(stage2_unit2_conv1) - - stage2_unit2_relu2 = ReLU(name='stage2_unit2_relu2')(stage2_unit2_bn2) + stage2_unit1_conv2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="stage2_unit1_conv2", + strides=[2, 2], + padding="VALID", + use_bias=False, + )(stage2_unit1_conv2_pad) + + stage2_unit1_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit1_bn3", trainable=False + )(stage2_unit1_conv2) + + stage2_unit1_relu3 = ReLU(name="stage2_unit1_relu3")(stage2_unit1_bn3) + + stage2_unit1_conv3 = Conv2D( + filters=512, + kernel_size=(1, 1), + name="stage2_unit1_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit1_relu3) + + plus3 = Add()([stage2_unit1_conv3, stage2_unit1_sc]) + + stage2_unit2_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit2_bn1", trainable=False + )(plus3) + + stage2_unit2_relu1 = ReLU(name="stage2_unit2_relu1")(stage2_unit2_bn1) + + stage2_unit2_conv1 = Conv2D( + filters=128, + kernel_size=(1, 1), + name="stage2_unit2_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit2_relu1) + + stage2_unit2_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit2_bn2", trainable=False + )(stage2_unit2_conv1) + + stage2_unit2_relu2 = ReLU(name="stage2_unit2_relu2")(stage2_unit2_bn2) stage2_unit2_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage2_unit2_relu2) - stage2_unit2_conv2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'stage2_unit2_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit2_conv2_pad) - - stage2_unit2_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit2_bn3', trainable=False)(stage2_unit2_conv2) - - stage2_unit2_relu3 = ReLU(name='stage2_unit2_relu3')(stage2_unit2_bn3) - - stage2_unit2_conv3 = Conv2D(filters = 512, kernel_size = (1, 1), name = 'stage2_unit2_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit2_relu3) - - plus4 = Add()([stage2_unit2_conv3 , plus3]) - - stage2_unit3_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit3_bn1', trainable=False)(plus4) - - stage2_unit3_relu1 = ReLU(name='stage2_unit3_relu1')(stage2_unit3_bn1) - - stage2_unit3_conv1 = Conv2D(filters = 128, kernel_size = (1, 1), name = 'stage2_unit3_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit3_relu1) - - stage2_unit3_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit3_bn2', trainable=False)(stage2_unit3_conv1) - - stage2_unit3_relu2 = ReLU(name='stage2_unit3_relu2')(stage2_unit3_bn2) + stage2_unit2_conv2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="stage2_unit2_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit2_conv2_pad) + + stage2_unit2_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit2_bn3", trainable=False + )(stage2_unit2_conv2) + + stage2_unit2_relu3 = ReLU(name="stage2_unit2_relu3")(stage2_unit2_bn3) + + stage2_unit2_conv3 = Conv2D( + filters=512, + kernel_size=(1, 1), + name="stage2_unit2_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit2_relu3) + + plus4 = Add()([stage2_unit2_conv3, plus3]) + + stage2_unit3_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit3_bn1", trainable=False + )(plus4) + + stage2_unit3_relu1 = ReLU(name="stage2_unit3_relu1")(stage2_unit3_bn1) + + stage2_unit3_conv1 = Conv2D( + filters=128, + kernel_size=(1, 1), + name="stage2_unit3_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit3_relu1) + + stage2_unit3_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit3_bn2", trainable=False + )(stage2_unit3_conv1) + + stage2_unit3_relu2 = ReLU(name="stage2_unit3_relu2")(stage2_unit3_bn2) stage2_unit3_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage2_unit3_relu2) - stage2_unit3_conv2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'stage2_unit3_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit3_conv2_pad) - - stage2_unit3_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit3_bn3', trainable=False)(stage2_unit3_conv2) - - stage2_unit3_relu3 = ReLU(name='stage2_unit3_relu3')(stage2_unit3_bn3) - - stage2_unit3_conv3 = Conv2D(filters = 512, kernel_size = (1, 1), name = 'stage2_unit3_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit3_relu3) - - plus5 = Add()([stage2_unit3_conv3 , plus4]) - - stage2_unit4_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit4_bn1', trainable=False)(plus5) - - stage2_unit4_relu1 = ReLU(name='stage2_unit4_relu1')(stage2_unit4_bn1) - - stage2_unit4_conv1 = Conv2D(filters = 128, kernel_size = (1, 1), name = 'stage2_unit4_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit4_relu1) - - stage2_unit4_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit4_bn2', trainable=False)(stage2_unit4_conv1) - - stage2_unit4_relu2 = ReLU(name='stage2_unit4_relu2')(stage2_unit4_bn2) + stage2_unit3_conv2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="stage2_unit3_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit3_conv2_pad) + + stage2_unit3_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit3_bn3", trainable=False + )(stage2_unit3_conv2) + + stage2_unit3_relu3 = ReLU(name="stage2_unit3_relu3")(stage2_unit3_bn3) + + stage2_unit3_conv3 = Conv2D( + filters=512, + kernel_size=(1, 1), + name="stage2_unit3_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit3_relu3) + + plus5 = Add()([stage2_unit3_conv3, plus4]) + + stage2_unit4_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit4_bn1", trainable=False + )(plus5) + + stage2_unit4_relu1 = ReLU(name="stage2_unit4_relu1")(stage2_unit4_bn1) + + stage2_unit4_conv1 = Conv2D( + filters=128, + kernel_size=(1, 1), + name="stage2_unit4_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit4_relu1) + + stage2_unit4_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit4_bn2", trainable=False + )(stage2_unit4_conv1) + + stage2_unit4_relu2 = ReLU(name="stage2_unit4_relu2")(stage2_unit4_bn2) stage2_unit4_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage2_unit4_relu2) - stage2_unit4_conv2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'stage2_unit4_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit4_conv2_pad) - - stage2_unit4_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage2_unit4_bn3', trainable=False)(stage2_unit4_conv2) - - stage2_unit4_relu3 = ReLU(name='stage2_unit4_relu3')(stage2_unit4_bn3) - - stage2_unit4_conv3 = Conv2D(filters = 512, kernel_size = (1, 1), name = 'stage2_unit4_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage2_unit4_relu3) - - plus6 = Add()([stage2_unit4_conv3 , plus5]) - - stage3_unit1_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit1_bn1', trainable=False)(plus6) - - stage3_unit1_relu1 = ReLU(name='stage3_unit1_relu1')(stage3_unit1_bn1) - - stage3_unit1_conv1 = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage3_unit1_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit1_relu1) - - stage3_unit1_sc = Conv2D(filters = 1024, kernel_size = (1, 1), name = 'stage3_unit1_sc', strides = [2, 2], padding = 'VALID', use_bias = False)(stage3_unit1_relu1) - - stage3_unit1_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit1_bn2', trainable=False)(stage3_unit1_conv1) - - stage3_unit1_relu2 = ReLU(name='stage3_unit1_relu2')(stage3_unit1_bn2) + stage2_unit4_conv2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="stage2_unit4_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit4_conv2_pad) + + stage2_unit4_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage2_unit4_bn3", trainable=False + )(stage2_unit4_conv2) + + stage2_unit4_relu3 = ReLU(name="stage2_unit4_relu3")(stage2_unit4_bn3) + + stage2_unit4_conv3 = Conv2D( + filters=512, + kernel_size=(1, 1), + name="stage2_unit4_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage2_unit4_relu3) + + plus6 = Add()([stage2_unit4_conv3, plus5]) + + stage3_unit1_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit1_bn1", trainable=False + )(plus6) + + stage3_unit1_relu1 = ReLU(name="stage3_unit1_relu1")(stage3_unit1_bn1) + + stage3_unit1_conv1 = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage3_unit1_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit1_relu1) + + stage3_unit1_sc = Conv2D( + filters=1024, + kernel_size=(1, 1), + name="stage3_unit1_sc", + strides=[2, 2], + padding="VALID", + use_bias=False, + )(stage3_unit1_relu1) + + stage3_unit1_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit1_bn2", trainable=False + )(stage3_unit1_conv1) + + stage3_unit1_relu2 = ReLU(name="stage3_unit1_relu2")(stage3_unit1_bn2) stage3_unit1_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage3_unit1_relu2) - stage3_unit1_conv2 = Conv2D(filters = 256, kernel_size = (3, 3), name = 'stage3_unit1_conv2', strides = [2, 2], padding = 'VALID', use_bias = False)(stage3_unit1_conv2_pad) - - ssh_m1_red_conv = Conv2D(filters = 256, kernel_size = (1, 1), name = 'ssh_m1_red_conv', strides = [1, 1], padding = 'VALID', use_bias = True)(stage3_unit1_relu2) - - stage3_unit1_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit1_bn3', trainable=False)(stage3_unit1_conv2) - - ssh_m1_red_conv_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m1_red_conv_bn', trainable=False)(ssh_m1_red_conv) - - stage3_unit1_relu3 = ReLU(name='stage3_unit1_relu3')(stage3_unit1_bn3) - - ssh_m1_red_conv_relu = ReLU(name='ssh_m1_red_conv_relu')(ssh_m1_red_conv_bn) - - stage3_unit1_conv3 = Conv2D(filters = 1024, kernel_size = (1, 1), name = 'stage3_unit1_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit1_relu3) - - plus7 = Add()([stage3_unit1_conv3 , stage3_unit1_sc]) - - stage3_unit2_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit2_bn1', trainable=False)(plus7) - - stage3_unit2_relu1 = ReLU(name='stage3_unit2_relu1')(stage3_unit2_bn1) - - stage3_unit2_conv1 = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage3_unit2_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit2_relu1) - - stage3_unit2_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit2_bn2', trainable=False)(stage3_unit2_conv1) - - stage3_unit2_relu2 = ReLU(name='stage3_unit2_relu2')(stage3_unit2_bn2) + stage3_unit1_conv2 = Conv2D( + filters=256, + kernel_size=(3, 3), + name="stage3_unit1_conv2", + strides=[2, 2], + padding="VALID", + use_bias=False, + )(stage3_unit1_conv2_pad) + + ssh_m1_red_conv = Conv2D( + filters=256, + kernel_size=(1, 1), + name="ssh_m1_red_conv", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(stage3_unit1_relu2) + + stage3_unit1_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit1_bn3", trainable=False + )(stage3_unit1_conv2) + + ssh_m1_red_conv_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m1_red_conv_bn", trainable=False + )(ssh_m1_red_conv) + + stage3_unit1_relu3 = ReLU(name="stage3_unit1_relu3")(stage3_unit1_bn3) + + ssh_m1_red_conv_relu = ReLU(name="ssh_m1_red_conv_relu")(ssh_m1_red_conv_bn) + + stage3_unit1_conv3 = Conv2D( + filters=1024, + kernel_size=(1, 1), + name="stage3_unit1_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit1_relu3) + + plus7 = Add()([stage3_unit1_conv3, stage3_unit1_sc]) + + stage3_unit2_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit2_bn1", trainable=False + )(plus7) + + stage3_unit2_relu1 = ReLU(name="stage3_unit2_relu1")(stage3_unit2_bn1) + + stage3_unit2_conv1 = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage3_unit2_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit2_relu1) + + stage3_unit2_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit2_bn2", trainable=False + )(stage3_unit2_conv1) + + stage3_unit2_relu2 = ReLU(name="stage3_unit2_relu2")(stage3_unit2_bn2) stage3_unit2_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage3_unit2_relu2) - stage3_unit2_conv2 = Conv2D(filters = 256, kernel_size = (3, 3), name = 'stage3_unit2_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit2_conv2_pad) - - stage3_unit2_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit2_bn3', trainable=False)(stage3_unit2_conv2) - - stage3_unit2_relu3 = ReLU(name='stage3_unit2_relu3')(stage3_unit2_bn3) - - stage3_unit2_conv3 = Conv2D(filters = 1024, kernel_size = (1, 1), name = 'stage3_unit2_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit2_relu3) - - plus8 = Add()([stage3_unit2_conv3 , plus7]) - - stage3_unit3_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit3_bn1', trainable=False)(plus8) - - stage3_unit3_relu1 = ReLU(name='stage3_unit3_relu1')(stage3_unit3_bn1) - - stage3_unit3_conv1 = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage3_unit3_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit3_relu1) - - stage3_unit3_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit3_bn2', trainable=False)(stage3_unit3_conv1) - - stage3_unit3_relu2 = ReLU(name='stage3_unit3_relu2')(stage3_unit3_bn2) + stage3_unit2_conv2 = Conv2D( + filters=256, + kernel_size=(3, 3), + name="stage3_unit2_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit2_conv2_pad) + + stage3_unit2_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit2_bn3", trainable=False + )(stage3_unit2_conv2) + + stage3_unit2_relu3 = ReLU(name="stage3_unit2_relu3")(stage3_unit2_bn3) + + stage3_unit2_conv3 = Conv2D( + filters=1024, + kernel_size=(1, 1), + name="stage3_unit2_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit2_relu3) + + plus8 = Add()([stage3_unit2_conv3, plus7]) + + stage3_unit3_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit3_bn1", trainable=False + )(plus8) + + stage3_unit3_relu1 = ReLU(name="stage3_unit3_relu1")(stage3_unit3_bn1) + + stage3_unit3_conv1 = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage3_unit3_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit3_relu1) + + stage3_unit3_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit3_bn2", trainable=False + )(stage3_unit3_conv1) + + stage3_unit3_relu2 = ReLU(name="stage3_unit3_relu2")(stage3_unit3_bn2) stage3_unit3_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage3_unit3_relu2) - stage3_unit3_conv2 = Conv2D(filters = 256, kernel_size = (3, 3), name = 'stage3_unit3_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit3_conv2_pad) - - stage3_unit3_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit3_bn3', trainable=False)(stage3_unit3_conv2) - - stage3_unit3_relu3 = ReLU(name='stage3_unit3_relu3')(stage3_unit3_bn3) - - stage3_unit3_conv3 = Conv2D(filters = 1024, kernel_size = (1, 1), name = 'stage3_unit3_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit3_relu3) - - plus9 = Add()([stage3_unit3_conv3 , plus8]) - - stage3_unit4_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit4_bn1', trainable=False)(plus9) - - stage3_unit4_relu1 = ReLU(name='stage3_unit4_relu1')(stage3_unit4_bn1) - - stage3_unit4_conv1 = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage3_unit4_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit4_relu1) - - stage3_unit4_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit4_bn2', trainable=False)(stage3_unit4_conv1) - - stage3_unit4_relu2 = ReLU(name='stage3_unit4_relu2')(stage3_unit4_bn2) + stage3_unit3_conv2 = Conv2D( + filters=256, + kernel_size=(3, 3), + name="stage3_unit3_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit3_conv2_pad) + + stage3_unit3_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit3_bn3", trainable=False + )(stage3_unit3_conv2) + + stage3_unit3_relu3 = ReLU(name="stage3_unit3_relu3")(stage3_unit3_bn3) + + stage3_unit3_conv3 = Conv2D( + filters=1024, + kernel_size=(1, 1), + name="stage3_unit3_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit3_relu3) + + plus9 = Add()([stage3_unit3_conv3, plus8]) + + stage3_unit4_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit4_bn1", trainable=False + )(plus9) + + stage3_unit4_relu1 = ReLU(name="stage3_unit4_relu1")(stage3_unit4_bn1) + + stage3_unit4_conv1 = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage3_unit4_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit4_relu1) + + stage3_unit4_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit4_bn2", trainable=False + )(stage3_unit4_conv1) + + stage3_unit4_relu2 = ReLU(name="stage3_unit4_relu2")(stage3_unit4_bn2) stage3_unit4_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage3_unit4_relu2) - stage3_unit4_conv2 = Conv2D(filters = 256, kernel_size = (3, 3), name = 'stage3_unit4_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit4_conv2_pad) - - stage3_unit4_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit4_bn3', trainable=False)(stage3_unit4_conv2) - - stage3_unit4_relu3 = ReLU(name='stage3_unit4_relu3')(stage3_unit4_bn3) - - stage3_unit4_conv3 = Conv2D(filters = 1024, kernel_size = (1, 1), name = 'stage3_unit4_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit4_relu3) - - plus10 = Add()([stage3_unit4_conv3 , plus9]) - - stage3_unit5_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit5_bn1', trainable=False)(plus10) - - stage3_unit5_relu1 = ReLU(name='stage3_unit5_relu1')(stage3_unit5_bn1) - - stage3_unit5_conv1 = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage3_unit5_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit5_relu1) - - stage3_unit5_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit5_bn2', trainable=False)(stage3_unit5_conv1) - - stage3_unit5_relu2 = ReLU(name='stage3_unit5_relu2')(stage3_unit5_bn2) + stage3_unit4_conv2 = Conv2D( + filters=256, + kernel_size=(3, 3), + name="stage3_unit4_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit4_conv2_pad) + + stage3_unit4_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit4_bn3", trainable=False + )(stage3_unit4_conv2) + + stage3_unit4_relu3 = ReLU(name="stage3_unit4_relu3")(stage3_unit4_bn3) + + stage3_unit4_conv3 = Conv2D( + filters=1024, + kernel_size=(1, 1), + name="stage3_unit4_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit4_relu3) + + plus10 = Add()([stage3_unit4_conv3, plus9]) + + stage3_unit5_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit5_bn1", trainable=False + )(plus10) + + stage3_unit5_relu1 = ReLU(name="stage3_unit5_relu1")(stage3_unit5_bn1) + + stage3_unit5_conv1 = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage3_unit5_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit5_relu1) + + stage3_unit5_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit5_bn2", trainable=False + )(stage3_unit5_conv1) + + stage3_unit5_relu2 = ReLU(name="stage3_unit5_relu2")(stage3_unit5_bn2) stage3_unit5_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage3_unit5_relu2) - stage3_unit5_conv2 = Conv2D(filters = 256, kernel_size = (3, 3), name = 'stage3_unit5_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit5_conv2_pad) - - stage3_unit5_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit5_bn3', trainable=False)(stage3_unit5_conv2) - - stage3_unit5_relu3 = ReLU(name='stage3_unit5_relu3')(stage3_unit5_bn3) - - stage3_unit5_conv3 = Conv2D(filters = 1024, kernel_size = (1, 1), name = 'stage3_unit5_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit5_relu3) - - plus11 = Add()([stage3_unit5_conv3 , plus10]) - - stage3_unit6_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit6_bn1', trainable=False)(plus11) - - stage3_unit6_relu1 = ReLU(name='stage3_unit6_relu1')(stage3_unit6_bn1) - - stage3_unit6_conv1 = Conv2D(filters = 256, kernel_size = (1, 1), name = 'stage3_unit6_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit6_relu1) - - stage3_unit6_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit6_bn2', trainable=False)(stage3_unit6_conv1) - - stage3_unit6_relu2 = ReLU(name='stage3_unit6_relu2')(stage3_unit6_bn2) + stage3_unit5_conv2 = Conv2D( + filters=256, + kernel_size=(3, 3), + name="stage3_unit5_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit5_conv2_pad) + + stage3_unit5_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit5_bn3", trainable=False + )(stage3_unit5_conv2) + + stage3_unit5_relu3 = ReLU(name="stage3_unit5_relu3")(stage3_unit5_bn3) + + stage3_unit5_conv3 = Conv2D( + filters=1024, + kernel_size=(1, 1), + name="stage3_unit5_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit5_relu3) + + plus11 = Add()([stage3_unit5_conv3, plus10]) + + stage3_unit6_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit6_bn1", trainable=False + )(plus11) + + stage3_unit6_relu1 = ReLU(name="stage3_unit6_relu1")(stage3_unit6_bn1) + + stage3_unit6_conv1 = Conv2D( + filters=256, + kernel_size=(1, 1), + name="stage3_unit6_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit6_relu1) + + stage3_unit6_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit6_bn2", trainable=False + )(stage3_unit6_conv1) + + stage3_unit6_relu2 = ReLU(name="stage3_unit6_relu2")(stage3_unit6_bn2) stage3_unit6_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage3_unit6_relu2) - stage3_unit6_conv2 = Conv2D(filters = 256, kernel_size = (3, 3), name = 'stage3_unit6_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit6_conv2_pad) - - stage3_unit6_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage3_unit6_bn3', trainable=False)(stage3_unit6_conv2) - - stage3_unit6_relu3 = ReLU(name='stage3_unit6_relu3')(stage3_unit6_bn3) - - stage3_unit6_conv3 = Conv2D(filters = 1024, kernel_size = (1, 1), name = 'stage3_unit6_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage3_unit6_relu3) - - plus12 = Add()([stage3_unit6_conv3 , plus11]) - - stage4_unit1_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage4_unit1_bn1', trainable=False)(plus12) - - stage4_unit1_relu1 = ReLU(name='stage4_unit1_relu1')(stage4_unit1_bn1) - - stage4_unit1_conv1 = Conv2D(filters = 512, kernel_size = (1, 1), name = 'stage4_unit1_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage4_unit1_relu1) - - stage4_unit1_sc = Conv2D(filters = 2048, kernel_size = (1, 1), name = 'stage4_unit1_sc', strides = [2, 2], padding = 'VALID', use_bias = False)(stage4_unit1_relu1) - - stage4_unit1_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage4_unit1_bn2', trainable=False)(stage4_unit1_conv1) - - stage4_unit1_relu2 = ReLU(name='stage4_unit1_relu2')(stage4_unit1_bn2) + stage3_unit6_conv2 = Conv2D( + filters=256, + kernel_size=(3, 3), + name="stage3_unit6_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit6_conv2_pad) + + stage3_unit6_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage3_unit6_bn3", trainable=False + )(stage3_unit6_conv2) + + stage3_unit6_relu3 = ReLU(name="stage3_unit6_relu3")(stage3_unit6_bn3) + + stage3_unit6_conv3 = Conv2D( + filters=1024, + kernel_size=(1, 1), + name="stage3_unit6_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage3_unit6_relu3) + + plus12 = Add()([stage3_unit6_conv3, plus11]) + + stage4_unit1_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage4_unit1_bn1", trainable=False + )(plus12) + + stage4_unit1_relu1 = ReLU(name="stage4_unit1_relu1")(stage4_unit1_bn1) + + stage4_unit1_conv1 = Conv2D( + filters=512, + kernel_size=(1, 1), + name="stage4_unit1_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage4_unit1_relu1) + + stage4_unit1_sc = Conv2D( + filters=2048, + kernel_size=(1, 1), + name="stage4_unit1_sc", + strides=[2, 2], + padding="VALID", + use_bias=False, + )(stage4_unit1_relu1) + + stage4_unit1_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage4_unit1_bn2", trainable=False + )(stage4_unit1_conv1) + + stage4_unit1_relu2 = ReLU(name="stage4_unit1_relu2")(stage4_unit1_bn2) stage4_unit1_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage4_unit1_relu2) - stage4_unit1_conv2 = Conv2D(filters = 512, kernel_size = (3, 3), name = 'stage4_unit1_conv2', strides = [2, 2], padding = 'VALID', use_bias = False)(stage4_unit1_conv2_pad) - - ssh_c2_lateral = Conv2D(filters = 256, kernel_size = (1, 1), name = 'ssh_c2_lateral', strides = [1, 1], padding = 'VALID', use_bias = True)(stage4_unit1_relu2) - - stage4_unit1_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage4_unit1_bn3', trainable=False)(stage4_unit1_conv2) - - ssh_c2_lateral_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_c2_lateral_bn', trainable=False)(ssh_c2_lateral) - - stage4_unit1_relu3 = ReLU(name='stage4_unit1_relu3')(stage4_unit1_bn3) - - ssh_c2_lateral_relu = ReLU(name='ssh_c2_lateral_relu')(ssh_c2_lateral_bn) - - stage4_unit1_conv3 = Conv2D(filters = 2048, kernel_size = (1, 1), name = 'stage4_unit1_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage4_unit1_relu3) - - plus13 = Add()([stage4_unit1_conv3 , stage4_unit1_sc]) - - stage4_unit2_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage4_unit2_bn1', trainable=False)(plus13) - - stage4_unit2_relu1 = ReLU(name='stage4_unit2_relu1')(stage4_unit2_bn1) - - stage4_unit2_conv1 = Conv2D(filters = 512, kernel_size = (1, 1), name = 'stage4_unit2_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage4_unit2_relu1) - - stage4_unit2_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage4_unit2_bn2', trainable=False)(stage4_unit2_conv1) - - stage4_unit2_relu2 = ReLU(name='stage4_unit2_relu2')(stage4_unit2_bn2) + stage4_unit1_conv2 = Conv2D( + filters=512, + kernel_size=(3, 3), + name="stage4_unit1_conv2", + strides=[2, 2], + padding="VALID", + use_bias=False, + )(stage4_unit1_conv2_pad) + + ssh_c2_lateral = Conv2D( + filters=256, + kernel_size=(1, 1), + name="ssh_c2_lateral", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(stage4_unit1_relu2) + + stage4_unit1_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage4_unit1_bn3", trainable=False + )(stage4_unit1_conv2) + + ssh_c2_lateral_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_c2_lateral_bn", trainable=False + )(ssh_c2_lateral) + + stage4_unit1_relu3 = ReLU(name="stage4_unit1_relu3")(stage4_unit1_bn3) + + ssh_c2_lateral_relu = ReLU(name="ssh_c2_lateral_relu")(ssh_c2_lateral_bn) + + stage4_unit1_conv3 = Conv2D( + filters=2048, + kernel_size=(1, 1), + name="stage4_unit1_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage4_unit1_relu3) + + plus13 = Add()([stage4_unit1_conv3, stage4_unit1_sc]) + + stage4_unit2_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage4_unit2_bn1", trainable=False + )(plus13) + + stage4_unit2_relu1 = ReLU(name="stage4_unit2_relu1")(stage4_unit2_bn1) + + stage4_unit2_conv1 = Conv2D( + filters=512, + kernel_size=(1, 1), + name="stage4_unit2_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage4_unit2_relu1) + + stage4_unit2_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage4_unit2_bn2", trainable=False + )(stage4_unit2_conv1) + + stage4_unit2_relu2 = ReLU(name="stage4_unit2_relu2")(stage4_unit2_bn2) stage4_unit2_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage4_unit2_relu2) - stage4_unit2_conv2 = Conv2D(filters = 512, kernel_size = (3, 3), name = 'stage4_unit2_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage4_unit2_conv2_pad) - - stage4_unit2_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage4_unit2_bn3', trainable=False)(stage4_unit2_conv2) - - stage4_unit2_relu3 = ReLU(name='stage4_unit2_relu3')(stage4_unit2_bn3) - - stage4_unit2_conv3 = Conv2D(filters = 2048, kernel_size = (1, 1), name = 'stage4_unit2_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage4_unit2_relu3) - - plus14 = Add()([stage4_unit2_conv3 , plus13]) - - stage4_unit3_bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage4_unit3_bn1', trainable=False)(plus14) - - stage4_unit3_relu1 = ReLU(name='stage4_unit3_relu1')(stage4_unit3_bn1) - - stage4_unit3_conv1 = Conv2D(filters = 512, kernel_size = (1, 1), name = 'stage4_unit3_conv1', strides = [1, 1], padding = 'VALID', use_bias = False)(stage4_unit3_relu1) - - stage4_unit3_bn2 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage4_unit3_bn2', trainable=False)(stage4_unit3_conv1) - - stage4_unit3_relu2 = ReLU(name='stage4_unit3_relu2')(stage4_unit3_bn2) + stage4_unit2_conv2 = Conv2D( + filters=512, + kernel_size=(3, 3), + name="stage4_unit2_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage4_unit2_conv2_pad) + + stage4_unit2_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage4_unit2_bn3", trainable=False + )(stage4_unit2_conv2) + + stage4_unit2_relu3 = ReLU(name="stage4_unit2_relu3")(stage4_unit2_bn3) + + stage4_unit2_conv3 = Conv2D( + filters=2048, + kernel_size=(1, 1), + name="stage4_unit2_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage4_unit2_relu3) + + plus14 = Add()([stage4_unit2_conv3, plus13]) + + stage4_unit3_bn1 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage4_unit3_bn1", trainable=False + )(plus14) + + stage4_unit3_relu1 = ReLU(name="stage4_unit3_relu1")(stage4_unit3_bn1) + + stage4_unit3_conv1 = Conv2D( + filters=512, + kernel_size=(1, 1), + name="stage4_unit3_conv1", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage4_unit3_relu1) + + stage4_unit3_bn2 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage4_unit3_bn2", trainable=False + )(stage4_unit3_conv1) + + stage4_unit3_relu2 = ReLU(name="stage4_unit3_relu2")(stage4_unit3_bn2) stage4_unit3_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(stage4_unit3_relu2) - stage4_unit3_conv2 = Conv2D(filters = 512, kernel_size = (3, 3), name = 'stage4_unit3_conv2', strides = [1, 1], padding = 'VALID', use_bias = False)(stage4_unit3_conv2_pad) + stage4_unit3_conv2 = Conv2D( + filters=512, + kernel_size=(3, 3), + name="stage4_unit3_conv2", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage4_unit3_conv2_pad) - stage4_unit3_bn3 = BatchNormalization(epsilon=1.9999999494757503e-05, name='stage4_unit3_bn3', trainable=False)(stage4_unit3_conv2) + stage4_unit3_bn3 = BatchNormalization( + epsilon=1.9999999494757503e-05, name="stage4_unit3_bn3", trainable=False + )(stage4_unit3_conv2) - stage4_unit3_relu3 = ReLU(name='stage4_unit3_relu3')(stage4_unit3_bn3) + stage4_unit3_relu3 = ReLU(name="stage4_unit3_relu3")(stage4_unit3_bn3) - stage4_unit3_conv3 = Conv2D(filters = 2048, kernel_size = (1, 1), name = 'stage4_unit3_conv3', strides = [1, 1], padding = 'VALID', use_bias = False)(stage4_unit3_relu3) + stage4_unit3_conv3 = Conv2D( + filters=2048, + kernel_size=(1, 1), + name="stage4_unit3_conv3", + strides=[1, 1], + padding="VALID", + use_bias=False, + )(stage4_unit3_relu3) - plus15 = Add()([stage4_unit3_conv3 , plus14]) + plus15 = Add()([stage4_unit3_conv3, plus14]) - bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name='bn1', trainable=False)(plus15) + bn1 = BatchNormalization(epsilon=1.9999999494757503e-05, name="bn1", trainable=False)(plus15) - relu1 = ReLU(name='relu1')(bn1) + relu1 = ReLU(name="relu1")(bn1) - ssh_c3_lateral = Conv2D(filters = 256, kernel_size = (1, 1), name = 'ssh_c3_lateral', strides = [1, 1], padding = 'VALID', use_bias = True)(relu1) + ssh_c3_lateral = Conv2D( + filters=256, + kernel_size=(1, 1), + name="ssh_c3_lateral", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(relu1) - ssh_c3_lateral_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_c3_lateral_bn', trainable=False)(ssh_c3_lateral) + ssh_c3_lateral_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_c3_lateral_bn", trainable=False + )(ssh_c3_lateral) - ssh_c3_lateral_relu = ReLU(name='ssh_c3_lateral_relu')(ssh_c3_lateral_bn) + ssh_c3_lateral_relu = ReLU(name="ssh_c3_lateral_relu")(ssh_c3_lateral_bn) ssh_m3_det_conv1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_c3_lateral_relu) - ssh_m3_det_conv1 = Conv2D(filters = 256, kernel_size = (3, 3), name = 'ssh_m3_det_conv1', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m3_det_conv1_pad) + ssh_m3_det_conv1 = Conv2D( + filters=256, + kernel_size=(3, 3), + name="ssh_m3_det_conv1", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m3_det_conv1_pad) ssh_m3_det_context_conv1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_c3_lateral_relu) - ssh_m3_det_context_conv1 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m3_det_context_conv1', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m3_det_context_conv1_pad) + ssh_m3_det_context_conv1 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m3_det_context_conv1", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m3_det_context_conv1_pad) - ssh_c3_up = UpSampling2D(size=(2, 2), interpolation="nearest", name="ssh_c3_up")(ssh_c3_lateral_relu) + ssh_c3_up = UpSampling2D(size=(2, 2), interpolation="nearest", name="ssh_c3_up")( + ssh_c3_lateral_relu + ) - ssh_m3_det_conv1_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m3_det_conv1_bn', trainable=False)(ssh_m3_det_conv1) + ssh_m3_det_conv1_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m3_det_conv1_bn", trainable=False + )(ssh_m3_det_conv1) - ssh_m3_det_context_conv1_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m3_det_context_conv1_bn', trainable=False)(ssh_m3_det_context_conv1) + ssh_m3_det_context_conv1_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m3_det_context_conv1_bn", trainable=False + )(ssh_m3_det_context_conv1) x1_shape = tf.shape(ssh_c3_up) x2_shape = tf.shape(ssh_c2_lateral_relu) @@ -470,51 +1030,117 @@ def build_model(): size = [-1, x2_shape[1], x2_shape[2], -1] crop0 = tf.slice(ssh_c3_up, offsets, size, "crop0") - ssh_m3_det_context_conv1_relu = ReLU(name='ssh_m3_det_context_conv1_relu')(ssh_m3_det_context_conv1_bn) - - plus0_v2 = Add()([ssh_c2_lateral_relu , crop0]) - - ssh_m3_det_context_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_m3_det_context_conv1_relu) - - ssh_m3_det_context_conv2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m3_det_context_conv2', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m3_det_context_conv2_pad) - - ssh_m3_det_context_conv3_1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_m3_det_context_conv1_relu) - - ssh_m3_det_context_conv3_1 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m3_det_context_conv3_1', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m3_det_context_conv3_1_pad) + ssh_m3_det_context_conv1_relu = ReLU(name="ssh_m3_det_context_conv1_relu")( + ssh_m3_det_context_conv1_bn + ) + + plus0_v2 = Add()([ssh_c2_lateral_relu, crop0]) + + ssh_m3_det_context_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))( + ssh_m3_det_context_conv1_relu + ) + + ssh_m3_det_context_conv2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m3_det_context_conv2", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m3_det_context_conv2_pad) + + ssh_m3_det_context_conv3_1_pad = ZeroPadding2D(padding=tuple([1, 1]))( + ssh_m3_det_context_conv1_relu + ) + + ssh_m3_det_context_conv3_1 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m3_det_context_conv3_1", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m3_det_context_conv3_1_pad) ssh_c2_aggr_pad = ZeroPadding2D(padding=tuple([1, 1]))(plus0_v2) - ssh_c2_aggr = Conv2D(filters = 256, kernel_size = (3, 3), name = 'ssh_c2_aggr', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_c2_aggr_pad) - - ssh_m3_det_context_conv2_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m3_det_context_conv2_bn', trainable=False)(ssh_m3_det_context_conv2) - - ssh_m3_det_context_conv3_1_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m3_det_context_conv3_1_bn', trainable=False)(ssh_m3_det_context_conv3_1) - - ssh_c2_aggr_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_c2_aggr_bn', trainable=False)(ssh_c2_aggr) - - ssh_m3_det_context_conv3_1_relu = ReLU(name='ssh_m3_det_context_conv3_1_relu')(ssh_m3_det_context_conv3_1_bn) - - ssh_c2_aggr_relu = ReLU(name='ssh_c2_aggr_relu')(ssh_c2_aggr_bn) - - ssh_m3_det_context_conv3_2_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_m3_det_context_conv3_1_relu) - - ssh_m3_det_context_conv3_2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m3_det_context_conv3_2', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m3_det_context_conv3_2_pad) + ssh_c2_aggr = Conv2D( + filters=256, + kernel_size=(3, 3), + name="ssh_c2_aggr", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_c2_aggr_pad) + + ssh_m3_det_context_conv2_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m3_det_context_conv2_bn", trainable=False + )(ssh_m3_det_context_conv2) + + ssh_m3_det_context_conv3_1_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m3_det_context_conv3_1_bn", trainable=False + )(ssh_m3_det_context_conv3_1) + + ssh_c2_aggr_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_c2_aggr_bn", trainable=False + )(ssh_c2_aggr) + + ssh_m3_det_context_conv3_1_relu = ReLU(name="ssh_m3_det_context_conv3_1_relu")( + ssh_m3_det_context_conv3_1_bn + ) + + ssh_c2_aggr_relu = ReLU(name="ssh_c2_aggr_relu")(ssh_c2_aggr_bn) + + ssh_m3_det_context_conv3_2_pad = ZeroPadding2D(padding=tuple([1, 1]))( + ssh_m3_det_context_conv3_1_relu + ) + + ssh_m3_det_context_conv3_2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m3_det_context_conv3_2", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m3_det_context_conv3_2_pad) ssh_m2_det_conv1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_c2_aggr_relu) - ssh_m2_det_conv1 = Conv2D(filters = 256, kernel_size = (3, 3), name = 'ssh_m2_det_conv1', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m2_det_conv1_pad) + ssh_m2_det_conv1 = Conv2D( + filters=256, + kernel_size=(3, 3), + name="ssh_m2_det_conv1", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m2_det_conv1_pad) ssh_m2_det_context_conv1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_c2_aggr_relu) - ssh_m2_det_context_conv1 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m2_det_context_conv1', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m2_det_context_conv1_pad) + ssh_m2_det_context_conv1 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m2_det_context_conv1", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m2_det_context_conv1_pad) - ssh_m2_red_up = UpSampling2D(size=(2, 2), interpolation="nearest", name="ssh_m2_red_up")(ssh_c2_aggr_relu) + ssh_m2_red_up = UpSampling2D(size=(2, 2), interpolation="nearest", name="ssh_m2_red_up")( + ssh_c2_aggr_relu + ) - ssh_m3_det_context_conv3_2_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m3_det_context_conv3_2_bn', trainable=False)(ssh_m3_det_context_conv3_2) + ssh_m3_det_context_conv3_2_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m3_det_context_conv3_2_bn", trainable=False + )(ssh_m3_det_context_conv3_2) - ssh_m2_det_conv1_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m2_det_conv1_bn', trainable=False)(ssh_m2_det_conv1) + ssh_m2_det_conv1_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m2_det_conv1_bn", trainable=False + )(ssh_m2_det_conv1) - ssh_m2_det_context_conv1_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m2_det_context_conv1_bn', trainable=False)(ssh_m2_det_context_conv1) + ssh_m2_det_context_conv1_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m2_det_context_conv1_bn", trainable=False + )(ssh_m2_det_context_conv1) x1_shape = tf.shape(ssh_m2_red_up) x2_shape = tf.shape(ssh_m1_red_conv_relu) @@ -522,48 +1148,116 @@ def build_model(): size = [-1, x2_shape[1], x2_shape[2], -1] crop1 = tf.slice(ssh_m2_red_up, offsets, size, "crop1") - ssh_m3_det_concat = concatenate([ssh_m3_det_conv1_bn, ssh_m3_det_context_conv2_bn, ssh_m3_det_context_conv3_2_bn], 3, name='ssh_m3_det_concat') - - ssh_m2_det_context_conv1_relu = ReLU(name='ssh_m2_det_context_conv1_relu')(ssh_m2_det_context_conv1_bn) - - plus1_v1 = Add()([ssh_m1_red_conv_relu , crop1]) - - ssh_m3_det_concat_relu = ReLU(name='ssh_m3_det_concat_relu')(ssh_m3_det_concat) - - ssh_m2_det_context_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_m2_det_context_conv1_relu) - - ssh_m2_det_context_conv2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m2_det_context_conv2', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m2_det_context_conv2_pad) - - ssh_m2_det_context_conv3_1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_m2_det_context_conv1_relu) - - ssh_m2_det_context_conv3_1 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m2_det_context_conv3_1', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m2_det_context_conv3_1_pad) + ssh_m3_det_concat = concatenate( + [ssh_m3_det_conv1_bn, ssh_m3_det_context_conv2_bn, ssh_m3_det_context_conv3_2_bn], + 3, + name="ssh_m3_det_concat", + ) + + ssh_m2_det_context_conv1_relu = ReLU(name="ssh_m2_det_context_conv1_relu")( + ssh_m2_det_context_conv1_bn + ) + + plus1_v1 = Add()([ssh_m1_red_conv_relu, crop1]) + + ssh_m3_det_concat_relu = ReLU(name="ssh_m3_det_concat_relu")(ssh_m3_det_concat) + + ssh_m2_det_context_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))( + ssh_m2_det_context_conv1_relu + ) + + ssh_m2_det_context_conv2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m2_det_context_conv2", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m2_det_context_conv2_pad) + + ssh_m2_det_context_conv3_1_pad = ZeroPadding2D(padding=tuple([1, 1]))( + ssh_m2_det_context_conv1_relu + ) + + ssh_m2_det_context_conv3_1 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m2_det_context_conv3_1", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m2_det_context_conv3_1_pad) ssh_c1_aggr_pad = ZeroPadding2D(padding=tuple([1, 1]))(plus1_v1) - ssh_c1_aggr = Conv2D(filters = 256, kernel_size = (3, 3), name = 'ssh_c1_aggr', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_c1_aggr_pad) - - face_rpn_cls_score_stride32 = Conv2D(filters = 4, kernel_size = (1, 1), name = 'face_rpn_cls_score_stride32', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m3_det_concat_relu) - - inter_1 = concatenate([face_rpn_cls_score_stride32[:, :, :, 0], face_rpn_cls_score_stride32[:, :, :, 1]], axis=1) - inter_2 = concatenate([face_rpn_cls_score_stride32[:, :, :, 2], face_rpn_cls_score_stride32[:, :, :, 3]], axis=1) + ssh_c1_aggr = Conv2D( + filters=256, + kernel_size=(3, 3), + name="ssh_c1_aggr", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_c1_aggr_pad) + + face_rpn_cls_score_stride32 = Conv2D( + filters=4, + kernel_size=(1, 1), + name="face_rpn_cls_score_stride32", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m3_det_concat_relu) + + inter_1 = concatenate( + [face_rpn_cls_score_stride32[:, :, :, 0], face_rpn_cls_score_stride32[:, :, :, 1]], axis=1 + ) + inter_2 = concatenate( + [face_rpn_cls_score_stride32[:, :, :, 2], face_rpn_cls_score_stride32[:, :, :, 3]], axis=1 + ) final = tf.stack([inter_1, inter_2]) - face_rpn_cls_score_reshape_stride32 = tf.transpose(final, (1, 2, 3, 0), name="face_rpn_cls_score_reshape_stride32") - - face_rpn_bbox_pred_stride32 = Conv2D(filters = 8, kernel_size = (1, 1), name = 'face_rpn_bbox_pred_stride32', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m3_det_concat_relu) - - face_rpn_landmark_pred_stride32 = Conv2D(filters = 20, kernel_size = (1, 1), name = 'face_rpn_landmark_pred_stride32', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m3_det_concat_relu) - - ssh_m2_det_context_conv2_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m2_det_context_conv2_bn', trainable=False)(ssh_m2_det_context_conv2) - - ssh_m2_det_context_conv3_1_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m2_det_context_conv3_1_bn', trainable=False)(ssh_m2_det_context_conv3_1) - - ssh_c1_aggr_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_c1_aggr_bn', trainable=False)(ssh_c1_aggr) - - ssh_m2_det_context_conv3_1_relu = ReLU(name='ssh_m2_det_context_conv3_1_relu')(ssh_m2_det_context_conv3_1_bn) - - ssh_c1_aggr_relu = ReLU(name='ssh_c1_aggr_relu')(ssh_c1_aggr_bn) - - face_rpn_cls_prob_stride32 = Softmax(name = 'face_rpn_cls_prob_stride32')(face_rpn_cls_score_reshape_stride32) + face_rpn_cls_score_reshape_stride32 = tf.transpose( + final, (1, 2, 3, 0), name="face_rpn_cls_score_reshape_stride32" + ) + + face_rpn_bbox_pred_stride32 = Conv2D( + filters=8, + kernel_size=(1, 1), + name="face_rpn_bbox_pred_stride32", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m3_det_concat_relu) + + face_rpn_landmark_pred_stride32 = Conv2D( + filters=20, + kernel_size=(1, 1), + name="face_rpn_landmark_pred_stride32", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m3_det_concat_relu) + + ssh_m2_det_context_conv2_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m2_det_context_conv2_bn", trainable=False + )(ssh_m2_det_context_conv2) + + ssh_m2_det_context_conv3_1_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m2_det_context_conv3_1_bn", trainable=False + )(ssh_m2_det_context_conv3_1) + + ssh_c1_aggr_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_c1_aggr_bn", trainable=False + )(ssh_c1_aggr) + + ssh_m2_det_context_conv3_1_relu = ReLU(name="ssh_m2_det_context_conv3_1_relu")( + ssh_m2_det_context_conv3_1_bn + ) + + ssh_c1_aggr_relu = ReLU(name="ssh_c1_aggr_relu")(ssh_c1_aggr_bn) + + face_rpn_cls_prob_stride32 = Softmax(name="face_rpn_cls_prob_stride32")( + face_rpn_cls_score_reshape_stride32 + ) input_shape = [tf.shape(face_rpn_cls_prob_stride32)[k] for k in range(4)] sz = tf.dtypes.cast(input_shape[1] / 2, dtype=tf.int32) @@ -572,58 +1266,148 @@ def build_model(): inter_3 = face_rpn_cls_prob_stride32[:, sz:, :, 0] inter_4 = face_rpn_cls_prob_stride32[:, sz:, :, 1] final = tf.stack([inter_1, inter_3, inter_2, inter_4]) - face_rpn_cls_prob_reshape_stride32 = tf.transpose(final, (1, 2, 3, 0), name="face_rpn_cls_prob_reshape_stride32") - - ssh_m2_det_context_conv3_2_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_m2_det_context_conv3_1_relu) - - ssh_m2_det_context_conv3_2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m2_det_context_conv3_2', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m2_det_context_conv3_2_pad) - - ssh_m1_det_conv1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_c1_aggr_relu) - - ssh_m1_det_conv1 = Conv2D(filters = 256, kernel_size = (3, 3), name = 'ssh_m1_det_conv1', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m1_det_conv1_pad) - - ssh_m1_det_context_conv1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_c1_aggr_relu) - - ssh_m1_det_context_conv1 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m1_det_context_conv1', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m1_det_context_conv1_pad) - - ssh_m2_det_context_conv3_2_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m2_det_context_conv3_2_bn', trainable=False)(ssh_m2_det_context_conv3_2) - - ssh_m1_det_conv1_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m1_det_conv1_bn', trainable=False)(ssh_m1_det_conv1) - - ssh_m1_det_context_conv1_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m1_det_context_conv1_bn', trainable=False)(ssh_m1_det_context_conv1) - - ssh_m2_det_concat = concatenate([ssh_m2_det_conv1_bn, ssh_m2_det_context_conv2_bn, ssh_m2_det_context_conv3_2_bn], 3, name='ssh_m2_det_concat') - - ssh_m1_det_context_conv1_relu = ReLU(name='ssh_m1_det_context_conv1_relu')(ssh_m1_det_context_conv1_bn) - - ssh_m2_det_concat_relu = ReLU(name='ssh_m2_det_concat_relu')(ssh_m2_det_concat) - - ssh_m1_det_context_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_m1_det_context_conv1_relu) - - ssh_m1_det_context_conv2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m1_det_context_conv2', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m1_det_context_conv2_pad) - - ssh_m1_det_context_conv3_1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_m1_det_context_conv1_relu) - - ssh_m1_det_context_conv3_1 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m1_det_context_conv3_1', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m1_det_context_conv3_1_pad) - - face_rpn_cls_score_stride16 = Conv2D(filters = 4, kernel_size = (1, 1), name = 'face_rpn_cls_score_stride16', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m2_det_concat_relu) - - inter_1 = concatenate([face_rpn_cls_score_stride16[:, :, :, 0], face_rpn_cls_score_stride16[:, :, :, 1]], axis=1) - inter_2 = concatenate([face_rpn_cls_score_stride16[:, :, :, 2], face_rpn_cls_score_stride16[:, :, :, 3]], axis=1) + face_rpn_cls_prob_reshape_stride32 = tf.transpose( + final, (1, 2, 3, 0), name="face_rpn_cls_prob_reshape_stride32" + ) + + ssh_m2_det_context_conv3_2_pad = ZeroPadding2D(padding=tuple([1, 1]))( + ssh_m2_det_context_conv3_1_relu + ) + + ssh_m2_det_context_conv3_2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m2_det_context_conv3_2", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m2_det_context_conv3_2_pad) + + ssh_m1_det_conv1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_c1_aggr_relu) + + ssh_m1_det_conv1 = Conv2D( + filters=256, + kernel_size=(3, 3), + name="ssh_m1_det_conv1", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m1_det_conv1_pad) + + ssh_m1_det_context_conv1_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_c1_aggr_relu) + + ssh_m1_det_context_conv1 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m1_det_context_conv1", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m1_det_context_conv1_pad) + + ssh_m2_det_context_conv3_2_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m2_det_context_conv3_2_bn", trainable=False + )(ssh_m2_det_context_conv3_2) + + ssh_m1_det_conv1_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m1_det_conv1_bn", trainable=False + )(ssh_m1_det_conv1) + + ssh_m1_det_context_conv1_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m1_det_context_conv1_bn", trainable=False + )(ssh_m1_det_context_conv1) + + ssh_m2_det_concat = concatenate( + [ssh_m2_det_conv1_bn, ssh_m2_det_context_conv2_bn, ssh_m2_det_context_conv3_2_bn], + 3, + name="ssh_m2_det_concat", + ) + + ssh_m1_det_context_conv1_relu = ReLU(name="ssh_m1_det_context_conv1_relu")( + ssh_m1_det_context_conv1_bn + ) + + ssh_m2_det_concat_relu = ReLU(name="ssh_m2_det_concat_relu")(ssh_m2_det_concat) + + ssh_m1_det_context_conv2_pad = ZeroPadding2D(padding=tuple([1, 1]))( + ssh_m1_det_context_conv1_relu + ) + + ssh_m1_det_context_conv2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m1_det_context_conv2", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m1_det_context_conv2_pad) + + ssh_m1_det_context_conv3_1_pad = ZeroPadding2D(padding=tuple([1, 1]))( + ssh_m1_det_context_conv1_relu + ) + + ssh_m1_det_context_conv3_1 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m1_det_context_conv3_1", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m1_det_context_conv3_1_pad) + + face_rpn_cls_score_stride16 = Conv2D( + filters=4, + kernel_size=(1, 1), + name="face_rpn_cls_score_stride16", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m2_det_concat_relu) + + inter_1 = concatenate( + [face_rpn_cls_score_stride16[:, :, :, 0], face_rpn_cls_score_stride16[:, :, :, 1]], axis=1 + ) + inter_2 = concatenate( + [face_rpn_cls_score_stride16[:, :, :, 2], face_rpn_cls_score_stride16[:, :, :, 3]], axis=1 + ) final = tf.stack([inter_1, inter_2]) - face_rpn_cls_score_reshape_stride16 = tf.transpose(final, (1, 2, 3, 0), name="face_rpn_cls_score_reshape_stride16") - - face_rpn_bbox_pred_stride16 = Conv2D(filters = 8, kernel_size = (1, 1), name = 'face_rpn_bbox_pred_stride16', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m2_det_concat_relu) - - face_rpn_landmark_pred_stride16 = Conv2D(filters = 20, kernel_size = (1, 1), name = 'face_rpn_landmark_pred_stride16', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m2_det_concat_relu) - - ssh_m1_det_context_conv2_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m1_det_context_conv2_bn', trainable=False)(ssh_m1_det_context_conv2) - - ssh_m1_det_context_conv3_1_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m1_det_context_conv3_1_bn', trainable=False)(ssh_m1_det_context_conv3_1) - - ssh_m1_det_context_conv3_1_relu = ReLU(name='ssh_m1_det_context_conv3_1_relu')(ssh_m1_det_context_conv3_1_bn) - - face_rpn_cls_prob_stride16 = Softmax(name = 'face_rpn_cls_prob_stride16')(face_rpn_cls_score_reshape_stride16) + face_rpn_cls_score_reshape_stride16 = tf.transpose( + final, (1, 2, 3, 0), name="face_rpn_cls_score_reshape_stride16" + ) + + face_rpn_bbox_pred_stride16 = Conv2D( + filters=8, + kernel_size=(1, 1), + name="face_rpn_bbox_pred_stride16", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m2_det_concat_relu) + + face_rpn_landmark_pred_stride16 = Conv2D( + filters=20, + kernel_size=(1, 1), + name="face_rpn_landmark_pred_stride16", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m2_det_concat_relu) + + ssh_m1_det_context_conv2_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m1_det_context_conv2_bn", trainable=False + )(ssh_m1_det_context_conv2) + + ssh_m1_det_context_conv3_1_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m1_det_context_conv3_1_bn", trainable=False + )(ssh_m1_det_context_conv3_1) + + ssh_m1_det_context_conv3_1_relu = ReLU(name="ssh_m1_det_context_conv3_1_relu")( + ssh_m1_det_context_conv3_1_bn + ) + + face_rpn_cls_prob_stride16 = Softmax(name="face_rpn_cls_prob_stride16")( + face_rpn_cls_score_reshape_stride16 + ) input_shape = [tf.shape(face_rpn_cls_prob_stride16)[k] for k in range(4)] sz = tf.dtypes.cast(input_shape[1] / 2, dtype=tf.int32) @@ -632,29 +1416,75 @@ def build_model(): inter_3 = face_rpn_cls_prob_stride16[:, sz:, :, 0] inter_4 = face_rpn_cls_prob_stride16[:, sz:, :, 1] final = tf.stack([inter_1, inter_3, inter_2, inter_4]) - face_rpn_cls_prob_reshape_stride16 = tf.transpose(final, (1, 2, 3, 0), name="face_rpn_cls_prob_reshape_stride16") - - ssh_m1_det_context_conv3_2_pad = ZeroPadding2D(padding=tuple([1, 1]))(ssh_m1_det_context_conv3_1_relu) - - ssh_m1_det_context_conv3_2 = Conv2D(filters = 128, kernel_size = (3, 3), name = 'ssh_m1_det_context_conv3_2', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m1_det_context_conv3_2_pad) - - ssh_m1_det_context_conv3_2_bn = BatchNormalization(epsilon=1.9999999494757503e-05, name='ssh_m1_det_context_conv3_2_bn', trainable=False)(ssh_m1_det_context_conv3_2) - - ssh_m1_det_concat = concatenate([ssh_m1_det_conv1_bn, ssh_m1_det_context_conv2_bn, ssh_m1_det_context_conv3_2_bn], 3, name='ssh_m1_det_concat') - - ssh_m1_det_concat_relu = ReLU(name='ssh_m1_det_concat_relu')(ssh_m1_det_concat) - face_rpn_cls_score_stride8 = Conv2D(filters = 4, kernel_size = (1, 1), name = 'face_rpn_cls_score_stride8', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m1_det_concat_relu) - - inter_1 = concatenate([face_rpn_cls_score_stride8[:, :, :, 0], face_rpn_cls_score_stride8[:, :, :, 1]], axis=1) - inter_2 = concatenate([face_rpn_cls_score_stride8[:, :, :, 2], face_rpn_cls_score_stride8[:, :, :, 3]], axis=1) + face_rpn_cls_prob_reshape_stride16 = tf.transpose( + final, (1, 2, 3, 0), name="face_rpn_cls_prob_reshape_stride16" + ) + + ssh_m1_det_context_conv3_2_pad = ZeroPadding2D(padding=tuple([1, 1]))( + ssh_m1_det_context_conv3_1_relu + ) + + ssh_m1_det_context_conv3_2 = Conv2D( + filters=128, + kernel_size=(3, 3), + name="ssh_m1_det_context_conv3_2", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m1_det_context_conv3_2_pad) + + ssh_m1_det_context_conv3_2_bn = BatchNormalization( + epsilon=1.9999999494757503e-05, name="ssh_m1_det_context_conv3_2_bn", trainable=False + )(ssh_m1_det_context_conv3_2) + + ssh_m1_det_concat = concatenate( + [ssh_m1_det_conv1_bn, ssh_m1_det_context_conv2_bn, ssh_m1_det_context_conv3_2_bn], + 3, + name="ssh_m1_det_concat", + ) + + ssh_m1_det_concat_relu = ReLU(name="ssh_m1_det_concat_relu")(ssh_m1_det_concat) + face_rpn_cls_score_stride8 = Conv2D( + filters=4, + kernel_size=(1, 1), + name="face_rpn_cls_score_stride8", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m1_det_concat_relu) + + inter_1 = concatenate( + [face_rpn_cls_score_stride8[:, :, :, 0], face_rpn_cls_score_stride8[:, :, :, 1]], axis=1 + ) + inter_2 = concatenate( + [face_rpn_cls_score_stride8[:, :, :, 2], face_rpn_cls_score_stride8[:, :, :, 3]], axis=1 + ) final = tf.stack([inter_1, inter_2]) - face_rpn_cls_score_reshape_stride8 = tf.transpose(final, (1, 2, 3, 0), name="face_rpn_cls_score_reshape_stride8") - - face_rpn_bbox_pred_stride8 = Conv2D(filters = 8, kernel_size = (1, 1), name = 'face_rpn_bbox_pred_stride8', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m1_det_concat_relu) - - face_rpn_landmark_pred_stride8 = Conv2D(filters = 20, kernel_size = (1, 1), name = 'face_rpn_landmark_pred_stride8', strides = [1, 1], padding = 'VALID', use_bias = True)(ssh_m1_det_concat_relu) - - face_rpn_cls_prob_stride8 = Softmax(name = 'face_rpn_cls_prob_stride8')(face_rpn_cls_score_reshape_stride8) + face_rpn_cls_score_reshape_stride8 = tf.transpose( + final, (1, 2, 3, 0), name="face_rpn_cls_score_reshape_stride8" + ) + + face_rpn_bbox_pred_stride8 = Conv2D( + filters=8, + kernel_size=(1, 1), + name="face_rpn_bbox_pred_stride8", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m1_det_concat_relu) + + face_rpn_landmark_pred_stride8 = Conv2D( + filters=20, + kernel_size=(1, 1), + name="face_rpn_landmark_pred_stride8", + strides=[1, 1], + padding="VALID", + use_bias=True, + )(ssh_m1_det_concat_relu) + + face_rpn_cls_prob_stride8 = Softmax(name="face_rpn_cls_prob_stride8")( + face_rpn_cls_score_reshape_stride8 + ) input_shape = [tf.shape(face_rpn_cls_prob_stride8)[k] for k in range(4)] sz = tf.dtypes.cast(input_shape[1] / 2, dtype=tf.int32) @@ -663,19 +1493,24 @@ def build_model(): inter_3 = face_rpn_cls_prob_stride8[:, sz:, :, 0] inter_4 = face_rpn_cls_prob_stride8[:, sz:, :, 1] final = tf.stack([inter_1, inter_3, inter_2, inter_4]) - face_rpn_cls_prob_reshape_stride8 = tf.transpose(final, (1, 2, 3, 0), name="face_rpn_cls_prob_reshape_stride8") - - model = Model(inputs=data, - outputs=[face_rpn_cls_prob_reshape_stride32, - face_rpn_bbox_pred_stride32, - face_rpn_landmark_pred_stride32, - face_rpn_cls_prob_reshape_stride16, - face_rpn_bbox_pred_stride16, - face_rpn_landmark_pred_stride16, - face_rpn_cls_prob_reshape_stride8, - face_rpn_bbox_pred_stride8, - face_rpn_landmark_pred_stride8 - ]) + face_rpn_cls_prob_reshape_stride8 = tf.transpose( + final, (1, 2, 3, 0), name="face_rpn_cls_prob_reshape_stride8" + ) + + model = Model( + inputs=data, + outputs=[ + face_rpn_cls_prob_reshape_stride32, + face_rpn_bbox_pred_stride32, + face_rpn_landmark_pred_stride32, + face_rpn_cls_prob_reshape_stride16, + face_rpn_bbox_pred_stride16, + face_rpn_landmark_pred_stride16, + face_rpn_cls_prob_reshape_stride8, + face_rpn_bbox_pred_stride8, + face_rpn_landmark_pred_stride8, + ], + ) model = load_weights(model) return model diff --git a/scripts/push-release.sh b/scripts/push-release.sh new file mode 100644 index 0000000..5b3e6fa --- /dev/null +++ b/scripts/push-release.sh @@ -0,0 +1,11 @@ +cd .. + +echo "deleting existing release related files" +rm -rf dist/* +rm -rf build/* + +echo "creating a package for current release - pypi compatible" +python setup.py sdist bdist_wheel + +echo "pushing the release to pypi" +python -m twine upload dist/* \ No newline at end of file diff --git a/setup.py b/setup.py index 2dd8e63..8f5accb 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,11 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() +with open("requirements.txt", "r", encoding="utf-8") as f: + requirements = f.read().split("\n") + setuptools.setup( - name="retina-face", #pip install retina-face + name="retina-face", # pip install retina-face version="0.0.14", author="Sefik Ilkin Serengil", author_email="serengil@gmail.com", @@ -16,8 +19,8 @@ classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent" + "Operating System :: OS Independent", ], - python_requires='>=3.5.5', - install_requires=["numpy>=1.14.0", "gdown>=3.10.1", "Pillow>=5.2.0", "opencv-python>=3.4.4", "tensorflow>=1.9.0"] + python_requires=">=3.5.5", + install_requires=requirements, ) diff --git a/tests/dataset/img1.jpg b/tests/dataset/img1.jpg deleted file mode 100644 index c10b680..0000000 Binary files a/tests/dataset/img1.jpg and /dev/null differ diff --git a/tests/dataset/img2.jpg b/tests/dataset/img2.jpg deleted file mode 100644 index 06c2b0f..0000000 Binary files a/tests/dataset/img2.jpg and /dev/null differ diff --git a/tests/dataset/img4.jpg b/tests/dataset/img4.jpg deleted file mode 100644 index 32bd655..0000000 Binary files a/tests/dataset/img4.jpg and /dev/null differ diff --git a/tests/dataset/img5.jpg b/tests/dataset/img5.jpg deleted file mode 100644 index 7df0558..0000000 Binary files a/tests/dataset/img5.jpg and /dev/null differ diff --git a/tests/dataset/img6.jpg b/tests/dataset/img6.jpg deleted file mode 100644 index eb7a310..0000000 Binary files a/tests/dataset/img6.jpg and /dev/null differ diff --git a/tests/dataset/img7.jpg b/tests/dataset/img7.jpg deleted file mode 100644 index 82bf87b..0000000 Binary files a/tests/dataset/img7.jpg and /dev/null differ diff --git a/tests/dataset/img8.jpg b/tests/dataset/img8.jpg deleted file mode 100644 index 1db4f69..0000000 Binary files a/tests/dataset/img8.jpg and /dev/null differ diff --git a/tests/test_actions.py b/tests/test_actions.py new file mode 100644 index 0000000..9921a7c --- /dev/null +++ b/tests/test_actions.py @@ -0,0 +1,95 @@ +import cv2 +import numpy as np +from retinaface import RetinaFace +from retinaface.commons.logger import Logger + +logger = Logger("tests/test_actions.py") + +do_plotting = False + +if do_plotting is True: + import matplotlib.pyplot as plt + + +def int_tuple(t): + return tuple(int(x) for x in t) + + +def test_analyze_crowded_photo(): + img_path = "tests/dataset/img3.jpg" + img = cv2.imread(img_path) + resp = RetinaFace.detect_faces(img_path, threshold=0.1) + + # it has to find 239 faces with threshold 0.1 + assert len(resp.keys()) > 200 + + for idx, identity in resp.items(): + confidence = identity["score"] + logger.debug(f"Confidence score of {idx} is {confidence}") + + rectangle_color = (255, 255, 255) + + landmarks = identity["landmarks"] + diameter = 1 + + cv2.circle(img, int_tuple(landmarks["left_eye"]), diameter, (0, 0, 255), -1) + cv2.circle(img, int_tuple(landmarks["right_eye"]), diameter, (0, 0, 255), -1) + cv2.circle(img, int_tuple(landmarks["nose"]), diameter, (0, 0, 255), -1) + cv2.circle(img, int_tuple(landmarks["mouth_left"]), diameter, (0, 0, 255), -1) + cv2.circle(img, int_tuple(landmarks["mouth_right"]), diameter, (0, 0, 255), -1) + + facial_area = identity["facial_area"] + + cv2.rectangle( + img, + (facial_area[2], facial_area[3]), + (facial_area[0], facial_area[1]), + rectangle_color, + 1, + ) + + if do_plotting is True: + plt.imshow(img[:, :, ::-1]) + plt.axis("off") + plt.show() + cv2.imwrite("outputs/" + img_path.split("/")[1], img) + + logger.info("✅ Crowded photo analysis test done") + + +def test_alignment_for_inverse_clock_way(): + # img11.jpg is required to rotate inverse direction of clock + img_path = "tests/dataset/img11.jpg" + img = cv2.imread(img_path) + do_alignment_checks(img, expected_faces=1) + logger.info("✅ Alignment for inverse clock way test done") + + +def test_alignment_for_clock_way(): + # img11.jpg is required to rotate inverse direction of clock + img_path = "tests/dataset/img11.jpg" + img = cv2.imread(img_path) + mirror_img = cv2.flip(img, 1) + do_alignment_checks(mirror_img, expected_faces=1) + logger.info("✅ Alignment for clock way test done") + + +def do_alignment_checks(img: np.ndarray, expected_faces: int) -> None: + faces = RetinaFace.extract_faces(img_path=img, align=True, expand_face_area=100) + + # it has one clear face + assert len(faces) == expected_faces + + for face in faces: + if do_plotting is True: + plt.imshow(face) + plt.axis("off") + plt.show() + + obj = RetinaFace.detect_faces(face, threshold=0.1) + landmarks = obj["face_1"]["landmarks"] + right_eye = landmarks["right_eye"] + left_eye = landmarks["left_eye"] + + # check eyes are on same horizantal + assert abs(right_eye[1] - left_eye[1]) < 10 diff --git a/tests/unit-tests.py b/tests/unit-tests.py deleted file mode 100644 index fec1262..0000000 --- a/tests/unit-tests.py +++ /dev/null @@ -1,67 +0,0 @@ -from retinaface import RetinaFace -import matplotlib.pyplot as plt -import cv2 -from PIL import Image -import numpy as np - -img_path = "dataset/img3.jpg" -img = cv2.imread(img_path) - -resp = RetinaFace.detect_faces(img_path, threshold = 0.1) -#print(resp) - -def int_tuple(t): - return tuple(int(x) for x in t) - -for key in resp: - identity = resp[key] - - #--------------------- - confidence = identity["score"] - - rectangle_color = (255, 255, 255) - - landmarks = identity["landmarks"] - diameter = 1 - cv2.circle(img, int_tuple(landmarks["left_eye"]), diameter, (0, 0, 255), -1) - cv2.circle(img, int_tuple(landmarks["right_eye"]), diameter, (0, 0, 255), -1) - cv2.circle(img, int_tuple(landmarks["nose"]), diameter, (0, 0, 255), -1) - cv2.circle(img, int_tuple(landmarks["mouth_left"]), diameter, (0, 0, 255), -1) - cv2.circle(img, int_tuple(landmarks["mouth_right"]), diameter, (0, 0, 255), -1) - - facial_area = identity["facial_area"] - - cv2.rectangle(img, (facial_area[2], facial_area[3]), (facial_area[0], facial_area[1]), rectangle_color, 1) - #facial_img = img[facial_area[1]: facial_area[3], facial_area[0]: facial_area[2]] - #plt.imshow(facial_img[:, :, ::-1]) - -plt.imshow(img[:, :, ::-1]) -plt.axis('off') -plt.show() -cv2.imwrite('outputs/'+img_path.split("/")[1], img) - -#------------------------------ -#extract face with alignment - -img_paths = ["dataset/img11.jpg", "dataset/img6.jpg"] -rotate_angles = [0, 30, 45, 60, 90, -30, -45, -60, -90] - -for img_path in img_paths: - - #resp = RetinaFace.extract_faces(img_path = img_path, align = True) - img = cv2.imread(img_path) - img_base = img.copy() - - for angle in rotate_angles: - print(f"rotating {img_path} to {angle} degrees") - img = img_base.copy() - img = Image.fromarray(img) - img = np.array(img.rotate(angle)) - - faces = RetinaFace.extract_faces(img_path = img, align = True) - - for face in faces: - plt.imshow(face) - plt.axis('off') - plt.show() - #cv2.imwrite('outputs/'+img_path.split("/")[1], face[:, :, ::-1])