Compare commits
2 Commits
master
...
feat/depth
Author | SHA1 | Date | |
---|---|---|---|
32eb2f618f | |||
2c9c7d9078 |
@ -1,6 +1,2 @@
|
|||||||
venv
|
venv
|
||||||
dist/*
|
|
||||||
build-docker.sh
|
|
||||||
Dockerfile
|
|
||||||
|
|
||||||
|
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,6 +3,3 @@
|
|||||||
*.egg-info
|
*.egg-info
|
||||||
.idea
|
.idea
|
||||||
*/__pycache__/
|
*/__pycache__/
|
||||||
/dist/
|
|
||||||
build
|
|
||||||
__pycache__
|
|
||||||
|
618
.pylintrc
618
.pylintrc
@ -1,618 +0,0 @@
|
|||||||
[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=depthai,node,cv2,events.*
|
|
||||||
|
|
||||||
# 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=^\.#,test_.*?py
|
|
||||||
|
|
||||||
# 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.10
|
|
||||||
|
|
||||||
# Discover python modules and packages in the file system subtree.
|
|
||||||
recursive=yes
|
|
||||||
|
|
||||||
# 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=
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[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=
|
|
||||||
|
|
||||||
|
|
||||||
[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=cv2,events.events_pb2,depthai.*,dai.*
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
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=
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[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*(# )?<?https?://\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=120
|
|
||||||
|
|
||||||
# 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=
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[EXCEPTIONS]
|
|
||||||
|
|
||||||
# Exceptions that will emit a warning when caught.
|
|
||||||
overgeneral-exceptions=BaseException,
|
|
||||||
Exception
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[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=1
|
|
||||||
|
|
||||||
|
|
||||||
[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
|
|
||||||
|
|
||||||
|
|
||||||
[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,
|
|
||||||
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=
|
|
46
Dockerfile
46
Dockerfile
@ -1,47 +1,23 @@
|
|||||||
FROM docker.io/library/python:3.12-slim as base
|
FROM docker.io/library/python:3.9-slim
|
||||||
|
|
||||||
# Configure piwheels repo to use pre-compiled numpy wheels for arm
|
# Configure piwheels repo to use pre-compiled numpy wheels for arm
|
||||||
RUN echo -n "[global]\n" > /etc/pip.conf &&\
|
RUN echo -n "[global]\nextra-index-url=https://www.piwheels.org/simple\n" >> /etc/pip.conf
|
||||||
echo -n "extra-index-url = https://www.piwheels.org/simple https://git.cyrilix.bzh/api/packages/robocars/pypi/simple \n" >> /etc/pip.conf
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y libgl1 libglib2.0-0 procps cmake g++ gcc
|
RUN apt-get update && apt-get install -y libgl1 libglib2.0-0
|
||||||
|
|
||||||
#################
|
RUN pip3 install numpy
|
||||||
FROM base as model-builder
|
|
||||||
|
|
||||||
RUN python3 -m pip install blobconverter
|
ADD requirements.txt requirements.txt
|
||||||
|
|
||||||
RUN mkdir -p /models
|
RUN pip3 install -r requirements.txt
|
||||||
|
|
||||||
RUN blobconverter --zoo-name mobile_object_localizer_192x192 --zoo-type depthai --shaves 6 --version 2021.4 --output-dir /models || echo ""
|
ADD events events
|
||||||
|
|
||||||
#################
|
|
||||||
FROM base as builder
|
|
||||||
|
|
||||||
RUN apt-get install -y git && \
|
|
||||||
pip3 install poetry && \
|
|
||||||
poetry self add "poetry-dynamic-versioning[plugin]"
|
|
||||||
|
|
||||||
ADD poetry.lock .
|
|
||||||
ADD pyproject.toml .
|
|
||||||
ADD camera camera
|
ADD camera camera
|
||||||
ADD README.md .
|
ADD setup.cfg setup.cfg
|
||||||
|
ADD setup.py setup.py
|
||||||
|
|
||||||
# Poetry expect to found a git project
|
ENV PYTHON_EGG_CACHE=/tmp/cache
|
||||||
ADD .git .git
|
RUN python3 setup.py install
|
||||||
|
|
||||||
RUN poetry build
|
|
||||||
|
|
||||||
#################
|
|
||||||
FROM base
|
|
||||||
|
|
||||||
COPY camera_tunning /camera_tuning
|
|
||||||
|
|
||||||
RUN mkdir /models
|
|
||||||
COPY --from=model-builder /models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob /models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob
|
|
||||||
|
|
||||||
COPY --from=builder dist/*.whl /tmp/
|
|
||||||
RUN pip3 install /tmp/*.whl
|
|
||||||
|
|
||||||
WORKDIR /tmp
|
WORKDIR /tmp
|
||||||
USER 1234
|
USER 1234
|
||||||
|
133
README.md
133
README.md
@ -9,136 +9,3 @@ To build images, run script:
|
|||||||
```bash
|
```bash
|
||||||
./build-docker.sh
|
./build-docker.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```shell
|
|
||||||
usage: cli.py [-h] [-u MQTT_USERNAME] [-p MQTT_PASSWORD] [-b MQTT_BROKER_HOST]
|
|
||||||
[-P MQTT_BROKER_PORT] [-C MQTT_CLIENT_ID]
|
|
||||||
[-c MQTT_TOPIC_ROBOCAR_OAK_CAMERA]
|
|
||||||
[-o MQTT_TOPIC_ROBOCAR_OBJECTS] [-t OBJECTS_THRESHOLD]
|
|
||||||
[-d MQTT_TOPIC_ROBOCAR_DISPARITY] [-f CAMERA_FPS]
|
|
||||||
[--camera-tuning-exposition {default,500us,8300us}]
|
|
||||||
[-H IMAGE_HEIGHT] [-W IMAGE_WIDTH] [--log {info,debug}]
|
|
||||||
[--stereo-mode-lr-check] [--stereo-mode-extended-disparity]
|
|
||||||
[--stereo-mode-subpixel]
|
|
||||||
[--stereo-post-processing-median-filter]
|
|
||||||
[--stereo-post-processing-median-value {MEDIAN_OFF,KERNEL_3x3,KERNEL_5x5,KERNEL_7x7}]
|
|
||||||
[--stereo-post-processing-speckle-filter]
|
|
||||||
[--stereo-post-processing-speckle-enable STEREO_POST_PROCESSING_SPECKLE_ENABLE]
|
|
||||||
[--stereo-post-processing-speckle-range STEREO_POST_PROCESSING_SPECKLE_RANGE]
|
|
||||||
[--stereo-post-processing-temporal-filter]
|
|
||||||
[--stereo-post-processing-temporal-persistency-mode {PERSISTENCY_OFF,VALID_8_OUT_OF_8,VALID_2_IN_LAST_3,VALID_2_IN_LAST_4,VALID_2_OUT_OF_8,VALID_1_IN_LAST_2,VALID_1_IN_LAST_5,VALID_1_IN_LAST_8,PERSISTENCY_INDEFINITELY}]
|
|
||||||
[--stereo-post-processing-temporal-alpha STEREO_POST_PROCESSING_TEMPORAL_ALPHA]
|
|
||||||
[--stereo-post-processing-temporal-delta STEREO_POST_PROCESSING_TEMPORAL_DELTA]
|
|
||||||
[--stereo-post-processing-spatial-filter]
|
|
||||||
[--stereo-post-processing-spatial-enable STEREO_POST_PROCESSING_SPATIAL_ENABLE]
|
|
||||||
[--stereo-post-processing-spatial-hole-filling-radius STEREO_POST_PROCESSING_SPATIAL_HOLE_FILLING_RADIUS]
|
|
||||||
[--stereo-post-processing-spatial-alpha STEREO_POST_PROCESSING_SPATIAL_ALPHA]
|
|
||||||
[--stereo-post-processing-spatial-delta STEREO_POST_PROCESSING_SPATIAL_DELTA]
|
|
||||||
[--stereo-post-processing-spatial-num-iterations STEREO_POST_PROCESSING_SPATIAL_NUM_ITERATIONS]
|
|
||||||
[--stereo-post-processing-threshold-filter]
|
|
||||||
[--stereo-post-processing-threshold-min-range STEREO_POST_PROCESSING_THRESHOLD_MIN_RANGE]
|
|
||||||
[--stereo-post-processing-threshold-max-range STEREO_POST_PROCESSING_THRESHOLD_MAX_RANGE]
|
|
||||||
[--stereo-post-processing-decimation-filter]
|
|
||||||
[--stereo-post-processing-decimation-decimal-factor {1,2,3,4}]
|
|
||||||
[--stereo-post-processing-decimation-mode {PIXEL_SKIPPING,NON_ZERO_MEDIAN,NON_ZERO_MEAN}]
|
|
||||||
|
|
||||||
options:
|
|
||||||
-h, --help show this help message and exit
|
|
||||||
-u MQTT_USERNAME, --mqtt-username MQTT_USERNAME
|
|
||||||
MQTT user
|
|
||||||
-p MQTT_PASSWORD, --mqtt-password MQTT_PASSWORD
|
|
||||||
MQTT password
|
|
||||||
-b MQTT_BROKER_HOST, --mqtt-broker-host MQTT_BROKER_HOST
|
|
||||||
MQTT broker host
|
|
||||||
-P MQTT_BROKER_PORT, --mqtt-broker-port MQTT_BROKER_PORT
|
|
||||||
MQTT broker port
|
|
||||||
-C MQTT_CLIENT_ID, --mqtt-client-id MQTT_CLIENT_ID
|
|
||||||
MQTT client id
|
|
||||||
-c MQTT_TOPIC_ROBOCAR_OAK_CAMERA, --mqtt-topic-robocar-oak-camera MQTT_TOPIC_ROBOCAR_OAK_CAMERA
|
|
||||||
MQTT topic where to publish robocar-oak-camera frames
|
|
||||||
-o MQTT_TOPIC_ROBOCAR_OBJECTS, ---mqtt-topic-robocar-objects MQTT_TOPIC_ROBOCAR_OBJECTS
|
|
||||||
MQTT topic where to publish objects detection results
|
|
||||||
-t OBJECTS_THRESHOLD, --objects-threshold OBJECTS_THRESHOLD
|
|
||||||
threshold to filter detected objects
|
|
||||||
-d MQTT_TOPIC_ROBOCAR_DISPARITY, ---mqtt-topic-robocar-disparity MQTT_TOPIC_ROBOCAR_DISPARITY
|
|
||||||
MQTT topic where to publish disparity results
|
|
||||||
-f CAMERA_FPS, --camera-fps CAMERA_FPS
|
|
||||||
set rate at which camera should produce frames
|
|
||||||
--camera-tuning-exposition {default,500us,8300us}
|
|
||||||
override camera exposition configuration
|
|
||||||
-H IMAGE_HEIGHT, --image-height IMAGE_HEIGHT
|
|
||||||
image height
|
|
||||||
-W IMAGE_WIDTH, --image-width IMAGE_WIDTH
|
|
||||||
image width
|
|
||||||
--log {info,debug} Log level
|
|
||||||
--stereo-mode-lr-check
|
|
||||||
remove incorrectly calculated disparity pixels due to
|
|
||||||
occlusions at object borders
|
|
||||||
--stereo-mode-extended-disparity
|
|
||||||
allows detecting closer distance objects for the given
|
|
||||||
baseline. This increases the maximum disparity search
|
|
||||||
from 96 to 191, meaning the range is now: [0..190]
|
|
||||||
--stereo-mode-subpixel
|
|
||||||
iimproves the precision and is especially useful for
|
|
||||||
long range measurements
|
|
||||||
--stereo-post-processing-median-filter
|
|
||||||
enable post-processing median filter
|
|
||||||
--stereo-post-processing-median-value {MEDIAN_OFF,KERNEL_3x3,KERNEL_5x5,KERNEL_7x7}
|
|
||||||
Median filter config
|
|
||||||
--stereo-post-processing-speckle-filter
|
|
||||||
enable post-processing speckle filter
|
|
||||||
--stereo-post-processing-speckle-enable STEREO_POST_PROCESSING_SPECKLE_ENABLE
|
|
||||||
enable post-processing speckle filter
|
|
||||||
--stereo-post-processing-speckle-range STEREO_POST_PROCESSING_SPECKLE_RANGE
|
|
||||||
Speckle search range
|
|
||||||
--stereo-post-processing-temporal-filter
|
|
||||||
enable post-processing temporal filter
|
|
||||||
--stereo-post-processing-temporal-persistency-mode {PERSISTENCY_OFF,VALID_8_OUT_OF_8,VALID_2_IN_LAST_3,VALID_2_IN_LAST_4,VALID_2_OUT_OF_8,VALID_1_IN_LAST_2,VALID_1_IN_LAST_5,VALID_1_IN_LAST_8,PERSISTENCY_INDEFINITELY}
|
|
||||||
Persistency mode.
|
|
||||||
--stereo-post-processing-temporal-alpha STEREO_POST_PROCESSING_TEMPORAL_ALPHA
|
|
||||||
The Alpha factor in an exponential moving average with
|
|
||||||
Alpha=1 - no filter. Alpha = 0 - infinite filter.
|
|
||||||
Determines the extent of the temporal history that
|
|
||||||
should be averaged.
|
|
||||||
--stereo-post-processing-temporal-delta STEREO_POST_PROCESSING_TEMPORAL_DELTA
|
|
||||||
Step-size boundary. Establishes the threshold used to
|
|
||||||
preserve surfaces (edges). If the disparity value
|
|
||||||
between neighboring pixels exceed the disparity
|
|
||||||
threshold set by this delta parameter, then filtering
|
|
||||||
will be temporarily disabled. Default value 0 means
|
|
||||||
auto: 3 disparity integer levels. In case of subpixel
|
|
||||||
mode it’s 3*number of subpixel levels.
|
|
||||||
--stereo-post-processing-spatial-filter
|
|
||||||
enable post-processing spatial filter
|
|
||||||
--stereo-post-processing-spatial-enable STEREO_POST_PROCESSING_SPATIAL_ENABLE
|
|
||||||
Whether to enable or disable the filter
|
|
||||||
--stereo-post-processing-spatial-hole-filling-radius STEREO_POST_PROCESSING_SPATIAL_HOLE_FILLING_RADIUS
|
|
||||||
An in-place heuristic symmetric hole-filling mode
|
|
||||||
applied horizontally during the filter passes
|
|
||||||
--stereo-post-processing-spatial-alpha STEREO_POST_PROCESSING_SPATIAL_ALPHA
|
|
||||||
The Alpha factor in an exponential moving average with
|
|
||||||
Alpha=1 - no filter. Alpha = 0 - infinite filter
|
|
||||||
--stereo-post-processing-spatial-delta STEREO_POST_PROCESSING_SPATIAL_DELTA
|
|
||||||
Step-size boundary. Establishes the threshold used to
|
|
||||||
preserve edges
|
|
||||||
--stereo-post-processing-spatial-num-iterations STEREO_POST_PROCESSING_SPATIAL_NUM_ITERATIONS
|
|
||||||
Number of iterations over the image in both horizontal
|
|
||||||
and vertical direction
|
|
||||||
--stereo-post-processing-threshold-filter
|
|
||||||
enable post-processing threshold filter
|
|
||||||
--stereo-post-processing-threshold-min-range STEREO_POST_PROCESSING_THRESHOLD_MIN_RANGE
|
|
||||||
Minimum range in depth units. Depth values under this
|
|
||||||
value are invalidated
|
|
||||||
--stereo-post-processing-threshold-max-range STEREO_POST_PROCESSING_THRESHOLD_MAX_RANGE
|
|
||||||
Maximum range in depth units. Depth values over this
|
|
||||||
value are invalidated.
|
|
||||||
--stereo-post-processing-decimation-filter
|
|
||||||
enable post-processing decimation filter
|
|
||||||
--stereo-post-processing-decimation-decimal-factor {1,2,3,4}
|
|
||||||
Decimation factor
|
|
||||||
--stereo-post-processing-decimation-mode {PIXEL_SKIPPING,NON_ZERO_MEDIAN,NON_ZERO_MEAN}
|
|
||||||
Decimation algorithm type
|
|
||||||
|
|
||||||
```
|
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
IMAGE_NAME=robocar-oak-camera
|
IMAGE_NAME=robocar-oak-camera
|
||||||
TAG=$(git describe)
|
TAG=$(git describe)
|
||||||
FULL_IMAGE_NAME=git.cyrilix.bzh/robocars/${IMAGE_NAME}:${TAG}
|
FULL_IMAGE_NAME=docker.io/cyrilix/${IMAGE_NAME}:${TAG}
|
||||||
PLATFORM="linux/amd64,linux/arm64"
|
PLATFORM="linux/amd64,linux/arm64"
|
||||||
#PLATFORM="linux/amd64,linux/arm64,linux/arm/v7"
|
#PLATFORM="linux/amd64,linux/arm64,linux/arm/v7"
|
||||||
|
|
||||||
podman build . --platform "${PLATFORM}" --manifest "${IMAGE_NAME}:${TAG}"
|
podman build . --platform "${PLATFORM}" --manifest "${IMAGE_NAME}:${TAG}"
|
||||||
podman manifest push --all "localhost/${IMAGE_NAME}:${TAG}" "docker://${FULL_IMAGE_NAME}"
|
podman manifest push --format v2s2 "localhost/${IMAGE_NAME}:${TAG}" "docker://${FULL_IMAGE_NAME}"
|
||||||
|
|
||||||
printf "\nImage %s published" "docker://${FULL_IMAGE_NAME}"
|
printf "\nImage %s published" "docker://${FULL_IMAGE_NAME}"
|
||||||
|
385
camera/cli.py
385
camera/cli.py
@ -1,373 +1,66 @@
|
|||||||
"""
|
"""
|
||||||
Mqtt gateway for oak-lite device
|
Publish data from oak-lite device
|
||||||
|
|
||||||
|
Usage: rc-oak-camera [-u USERNAME | --mqtt-username=USERNAME] [--mqtt-password=PASSWORD] [--mqtt-broker=HOSTNAME] \
|
||||||
|
[--mqtt-topic-robocar-oak-camera="TOPIC_CAMERA"] [--mqtt-client-id=CLIENT_ID] \
|
||||||
|
[-H IMG_HEIGHT | --image-height=IMG_HEIGHT] [-W IMG_WIDTH | --image-width=IMG_width]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h --help Show this screen.
|
||||||
|
-u USERID --mqtt-username=USERNAME MQTT user
|
||||||
|
-p PASSWORD --mqtt-password=PASSWORD MQTT password
|
||||||
|
-b HOSTNAME --mqtt-broker=HOSTNAME MQTT broker host
|
||||||
|
-C CLIENT_ID --mqtt-client-id=CLIENT_ID MQTT client id
|
||||||
|
-c TOPIC_CAMERA --mqtt-topic-robocar-oak-camera=TOPIC_CAMERA MQTT topic where to publish robocar-oak-camera frames
|
||||||
|
-H IMG_HEIGHT --image-height=IMG_HEIGHT IMG_HEIGHT image height
|
||||||
|
-W IMG_WIDTH --image-width=IMG_width IMG_WIDTH image width
|
||||||
"""
|
"""
|
||||||
import argparse
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import signal
|
from . import depthai as cam
|
||||||
import types
|
from docopt import docopt
|
||||||
import typing
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import depthai as dai
|
|
||||||
import paho.mqtt.client as mqtt
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
from camera import oak_pipeline as cam
|
|
||||||
from camera.oak_pipeline import StereoDepthPostFilter, MedianFilter, SpeckleFilter, TemporalFilter, SpatialFilter, \
|
|
||||||
ThresholdFilter, DecimationFilter
|
|
||||||
|
|
||||||
CAMERA_EXPOSITION_DEFAULT = "default"
|
|
||||||
CAMERA_EXPOSITION_8300US = "8300us"
|
|
||||||
CAMERA_EXPOSITION_500US = "500us"
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
_DEFAULT_CLIENT_ID = "robocar-depthai"
|
default_client_id = "robocar-depthai"
|
||||||
|
|
||||||
|
|
||||||
def _parse_args_cli() -> argparse.Namespace:
|
def init_mqtt_client(broker_host: str, user: str, password: str, client_id: str) -> mqtt.Client:
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("-u", "--mqtt-username",
|
|
||||||
help="MQTT user",
|
|
||||||
default=_get_env_value("MQTT_USERNAME", ""))
|
|
||||||
parser.add_argument("-p", "--mqtt-password",
|
|
||||||
help="MQTT password",
|
|
||||||
default=_get_env_value("MQTT_PASSWORD", ""))
|
|
||||||
parser.add_argument("-b", "--mqtt-broker-host",
|
|
||||||
help="MQTT broker host",
|
|
||||||
default=_get_env_value("MQTT_BROKER_HOST", "localhost"))
|
|
||||||
parser.add_argument("-P", "--mqtt-broker-port",
|
|
||||||
help="MQTT broker port",
|
|
||||||
type=int,
|
|
||||||
default=_get_env_int_value("MQTT_BROKER_PORT", 1883))
|
|
||||||
parser.add_argument("-C", "--mqtt-client-id",
|
|
||||||
help="MQTT client id",
|
|
||||||
default=_get_env_value("MQTT_CLIENT_ID", _DEFAULT_CLIENT_ID))
|
|
||||||
parser.add_argument("-c", "--mqtt-topic-robocar-oak-camera",
|
|
||||||
help="MQTT topic where to publish robocar-oak-camera frames",
|
|
||||||
default=_get_env_value("MQTT_TOPIC_CAMERA", "/oak/camera_rgb"))
|
|
||||||
parser.add_argument("-o", "---mqtt-topic-robocar-objects",
|
|
||||||
help="MQTT topic where to publish objects detection results",
|
|
||||||
default=_get_env_value("MQTT_TOPIC_OBJECTS", "/objects"))
|
|
||||||
parser.add_argument("-t", "--objects-threshold",
|
|
||||||
help="threshold to filter detected objects",
|
|
||||||
type=float,
|
|
||||||
default=_get_env_float_value("OBJECTS_THRESHOLD", 0.2))
|
|
||||||
parser.add_argument("-d", "---mqtt-topic-robocar-disparity",
|
|
||||||
help="MQTT topic where to publish disparity results",
|
|
||||||
default=_get_env_value("MQTT_TOPIC_DISPARITY", "/disparity"))
|
|
||||||
parser.add_argument("-f", "--camera-fps",
|
|
||||||
help="set rate at which camera should produce frames",
|
|
||||||
type=int,
|
|
||||||
default=30)
|
|
||||||
parser.add_argument("--camera-tuning-exposition", type=str,
|
|
||||||
default=CAMERA_EXPOSITION_DEFAULT,
|
|
||||||
help="override camera exposition configuration",
|
|
||||||
choices=[CAMERA_EXPOSITION_DEFAULT, CAMERA_EXPOSITION_500US, CAMERA_EXPOSITION_8300US])
|
|
||||||
parser.add_argument("-H", "--image-height", help="image height",
|
|
||||||
type=int,
|
|
||||||
default=_get_env_int_value("IMAGE_HEIGHT", 120))
|
|
||||||
parser.add_argument("-W", "--image-width", help="image width",
|
|
||||||
type=int,
|
|
||||||
default=_get_env_int_value("IMAGE_WIDTH", 126))
|
|
||||||
parser.add_argument("--log", help="Log level",
|
|
||||||
type=str,
|
|
||||||
default="info",
|
|
||||||
choices=["info", "debug"])
|
|
||||||
|
|
||||||
parser.add_argument("--disable-disparity", action="store_true",
|
|
||||||
help="enable disparity frame",
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-mode-lr-check",
|
|
||||||
help="remove incorrectly calculated disparity pixels due to occlusions at object borders",
|
|
||||||
default=False, action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-mode-extended-disparity",
|
|
||||||
help="allows detecting closer distance objects for the given baseline. This increases the maximum disparity search from 96 to 191, meaning the range is now: [0..190]",
|
|
||||||
default=False, action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-mode-subpixel",
|
|
||||||
help="iimproves the precision and is especially useful for long range measurements",
|
|
||||||
default=False, action="store_true"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
parser.add_argument("--stereo-post-processing-median-filter",
|
|
||||||
help="enable post-processing median filter",
|
|
||||||
default=False, action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-median-value",
|
|
||||||
help="Median filter config ",
|
|
||||||
type=str,
|
|
||||||
choices=["MEDIAN_OFF", "KERNEL_3x3", "KERNEL_5x5", "KERNEL_7x7"],
|
|
||||||
default="KERNEL_7x7",
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-speckle-filter",
|
|
||||||
help="enable post-processing speckle filter",
|
|
||||||
default=False, action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-speckle-enable",
|
|
||||||
help="enable post-processing speckle filter",
|
|
||||||
type=bool, default=False
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-speckle-range",
|
|
||||||
help="Speckle search range",
|
|
||||||
type=int, default=50
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument("--stereo-post-processing-temporal-filter",
|
|
||||||
help="enable post-processing temporal filter",
|
|
||||||
default=False, action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-temporal-persistency-mode",
|
|
||||||
help="Persistency mode.",
|
|
||||||
type=str, default="VALID_2_IN_LAST_4",
|
|
||||||
choices=["PERSISTENCY_OFF", "VALID_8_OUT_OF_8", "VALID_2_IN_LAST_3", "VALID_2_IN_LAST_4",
|
|
||||||
"VALID_2_OUT_OF_8", "VALID_1_IN_LAST_2", "VALID_1_IN_LAST_5", "VALID_1_IN_LAST_8",
|
|
||||||
"PERSISTENCY_INDEFINITELY"]
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-temporal-alpha",
|
|
||||||
help="The Alpha factor in an exponential moving average with Alpha=1 - no filter. "
|
|
||||||
"Alpha = 0 - infinite filter. Determines the extent of the temporal history that should be "
|
|
||||||
"averaged. ",
|
|
||||||
type=float, default=0.4,
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-temporal-delta",
|
|
||||||
help="Step-size boundary. Establishes the threshold used to preserve surfaces (edges). "
|
|
||||||
"If the disparity value between neighboring pixels exceed the disparity threshold set by "
|
|
||||||
"this delta parameter, then filtering will be temporarily disabled. Default value 0 means "
|
|
||||||
"auto: 3 disparity integer levels. In case of subpixel mode it’s 3*number of subpixel "
|
|
||||||
"levels.",
|
|
||||||
type=int, default=0,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument("--stereo-post-processing-spatial-filter",
|
|
||||||
help="enable post-processing spatial filter",
|
|
||||||
default=False, action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-spatial-enable",
|
|
||||||
help="Whether to enable or disable the filter",
|
|
||||||
type=bool, default=False,
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-spatial-hole-filling-radius",
|
|
||||||
help="An in-place heuristic symmetric hole-filling mode applied horizontally during the filter passes",
|
|
||||||
type=int, default=2,
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-spatial-alpha",
|
|
||||||
help="The Alpha factor in an exponential moving average with Alpha=1 - no filter. Alpha = 0 - infinite filter",
|
|
||||||
type=float, default=0.5,
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-spatial-delta",
|
|
||||||
help="Step-size boundary. Establishes the threshold used to preserve edges",
|
|
||||||
type=int, default=0,
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-spatial-num-iterations",
|
|
||||||
help="Number of iterations over the image in both horizontal and vertical direction",
|
|
||||||
type=int, default=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument("--stereo-post-processing-threshold-filter",
|
|
||||||
help="enable post-processing threshold filter",
|
|
||||||
default=False, action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-threshold-min-range",
|
|
||||||
help="Minimum range in depth units. Depth values under this value are invalidated",
|
|
||||||
type=int, default=500,
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-threshold-max-range",
|
|
||||||
help="Maximum range in depth units. Depth values over this value are invalidated.",
|
|
||||||
type=int, default=15000,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument("--stereo-post-processing-decimation-filter",
|
|
||||||
help="enable post-processing decimation filter",
|
|
||||||
default=False, action="store_true"
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-decimation-decimal-factor",
|
|
||||||
help="Decimation factor",
|
|
||||||
type=int, default=1, choices=[1, 2, 3, 4]
|
|
||||||
)
|
|
||||||
parser.add_argument("--stereo-post-processing-decimation-mode",
|
|
||||||
help="Decimation algorithm type",
|
|
||||||
type=str, default="PIXEL_SKIPPING",
|
|
||||||
choices=["PIXEL_SKIPPING", "NON_ZERO_MEDIAN", "NON_ZERO_MEAN"]
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
return args
|
|
||||||
|
|
||||||
|
|
||||||
def _init_mqtt_client(broker_host: str, broker_port: int, user: str, password: str, client_id: str) -> mqtt.Client:
|
|
||||||
logger.info("Start part.py-robocar-oak-camera")
|
logger.info("Start part.py-robocar-oak-camera")
|
||||||
client = mqtt.Client(client_id=client_id, clean_session=True, userdata=None, protocol=mqtt.MQTTv311)
|
client = mqtt.Client(client_id=client_id, clean_session=True, userdata=None, protocol=mqtt.MQTTv311)
|
||||||
|
|
||||||
client.username_pw_set(user, password)
|
client.username_pw_set(user, password)
|
||||||
logger.info("Connect to mqtt broker %s", broker_host)
|
logger.info("Connect to mqtt broker "+ broker_host)
|
||||||
client.connect(host=broker_host, port=broker_port, keepalive=60)
|
client.connect(host=broker_host, port=1883, keepalive=60)
|
||||||
logger.info("Connected to mqtt broker")
|
logger.info("Connected to mqtt broker")
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
def execute_from_command_line() -> None:
|
def execute_from_command_line():
|
||||||
"""
|
|
||||||
Cli entrypoint
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
args = _parse_args_cli()
|
|
||||||
if args.log == "info":
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
elif args.log == "debug":
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
|
|
||||||
client = _init_mqtt_client(broker_host=args.mqtt_broker_host,
|
args = docopt(__doc__)
|
||||||
broker_port=args.mqtt_broker_port,
|
|
||||||
user=args.mqtt_username,
|
client = init_mqtt_client(broker_host=get_default_value(args["--mqtt-broker"], "MQTT_BROKER", "localhost"),
|
||||||
password=args.mqtt_password,
|
user=get_default_value(args["--mqtt-username"], "MQTT_USERNAME", ""),
|
||||||
client_id=args.mqtt_client_id,
|
password=get_default_value(args["--mqtt-password"], "MQTT_PASSWORD", ""),
|
||||||
|
client_id=get_default_value(args["--mqtt-client-id"], "MQTT_CLIENT_ID",
|
||||||
|
default_client_id),
|
||||||
)
|
)
|
||||||
frame_processor = cam.FrameProcessor(mqtt_client=client, frame_topic=args.mqtt_topic_robocar_oak_camera)
|
frame_topic = get_default_value(args["--mqtt-topic-robocar-oak-camera"], "MQTT_TOPIC_CAMERA", "/oak/camera_rgb")
|
||||||
object_processor = cam.ObjectProcessor(mqtt_client=client,
|
|
||||||
objects_topic=args.mqtt_topic_robocar_objects,
|
|
||||||
objects_threshold=args.objects_threshold)
|
|
||||||
if args.disable_disparity == False:
|
|
||||||
depth_source = cam.DepthSource(pipeline=pipeline,
|
|
||||||
extended_disparity=args.stereo_mode_extended_disparity,
|
|
||||||
subpixel=args.stereo_mode_subpixel,
|
|
||||||
lr_check=args.stereo_mode_lr_check,
|
|
||||||
stereo_filters=stereo_filters),
|
|
||||||
disparity_processor = cam.DisparityProcessor(mqtt_client=client, disparity_topic=args.mqtt_topic_robocar_disparity)
|
|
||||||
else:
|
|
||||||
disparity_processor = None
|
|
||||||
depth_source = None
|
|
||||||
|
|
||||||
pipeline = dai.Pipeline()
|
frame_processor = cam.FramePublisher(mqtt_client=client,
|
||||||
if args.camera_tuning_exposition == CAMERA_EXPOSITION_500US:
|
frame_topic=frame_topic,
|
||||||
pipeline.setCameraTuningBlobPath('/camera_tuning/tuning_exp_limit_500us.bin')
|
img_width=int(get_default_value(args["--image-width"], "IMAGE_WIDTH", 160)),
|
||||||
elif args.camera_tuning_exposition == CAMERA_EXPOSITION_8300US:
|
img_height=int(get_default_value(args["--image-height"], "IMAGE_HEIGHT", 120)))
|
||||||
pipeline.setCameraTuningBlobPath('/camera_tuning/tuning_exp_limit_8300us.bin')
|
frame_processor.run()
|
||||||
|
|
||||||
stereo_filters = _get_stereo_filters(args)
|
|
||||||
|
|
||||||
pipeline_controller = cam.PipelineController(pipeline=pipeline,
|
|
||||||
frame_processor=frame_processor,
|
|
||||||
object_processor=object_processor,
|
|
||||||
object_node=cam.ObjectDetectionNN(pipeline=pipeline),
|
|
||||||
camera=cam.CameraSource(pipeline=pipeline,
|
|
||||||
img_width=args.image_width,
|
|
||||||
img_height=args.image_height,
|
|
||||||
fps=args.camera_fps,
|
|
||||||
),
|
|
||||||
depth_source=depth_source,
|
|
||||||
disparity_processor=disparity_processor)
|
|
||||||
|
|
||||||
def sigterm_handler(signum: int, frame: typing.Optional[
|
|
||||||
types.FrameType]) -> None: # pylint: disable=unused-argument # need to implement handler signature
|
|
||||||
logger.info("exit on SIGTERM")
|
|
||||||
pipeline_controller.stop()
|
|
||||||
|
|
||||||
signal.signal(signal.SIGTERM, sigterm_handler)
|
|
||||||
pipeline_controller.run()
|
|
||||||
|
|
||||||
|
|
||||||
def _get_env_value(env_var: str, default_value: str) -> str:
|
def get_default_value(value, env_var: str, default_value) -> str:
|
||||||
|
if value:
|
||||||
|
return value
|
||||||
if env_var in os.environ:
|
if env_var in os.environ:
|
||||||
return os.environ[env_var]
|
return os.environ[env_var]
|
||||||
return default_value
|
return default_value
|
||||||
|
|
||||||
|
|
||||||
def _get_env_int_value(env_var: str, default_value: int) -> int:
|
|
||||||
value = _get_env_value(env_var, str(default_value))
|
|
||||||
return int(value)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_env_float_value(env_var: str, default_value: float) -> float:
|
|
||||||
value = _get_env_value(env_var, str(default_value))
|
|
||||||
return float(value)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_stereo_filters(args: argparse.Namespace) -> List[StereoDepthPostFilter]:
|
|
||||||
filters = []
|
|
||||||
|
|
||||||
if args.stereo_post_processing_median_filter:
|
|
||||||
if args.stereo_post_processing_median_value == "MEDIAN_OFF":
|
|
||||||
value = dai.MedianFilter.MEDIAN_OFF
|
|
||||||
elif args.stereo_post_processing_median_value == "KERNEL_3x3":
|
|
||||||
value = dai.MedianFilter.KERNEL_3x3
|
|
||||||
elif args.stereo_post_processing_median_value == "KERNEL_5x5":
|
|
||||||
value = dai.MedianFilter.KERNEL_5x5
|
|
||||||
elif args.stereo_post_processing_median_value == "KERNEL_7x7":
|
|
||||||
value = dai.MedianFilter.KERNEL_7x7
|
|
||||||
else:
|
|
||||||
value = dai.MedianFilter.KERNEL_7x7
|
|
||||||
|
|
||||||
filters.append(MedianFilter(value=value))
|
|
||||||
|
|
||||||
if args.stereo_post_processing_speckle_filter:
|
|
||||||
filters.append(SpeckleFilter(enable=args.stereo_post_processing_speckle_enable,
|
|
||||||
speckle_range=args.stereo_post_processing_speckle_range))
|
|
||||||
|
|
||||||
if args.stereo_post_processing_temporal_filter:
|
|
||||||
if args.stereo_post_processing_temporal_persistency-mode == "PERSISTENCY_OFF":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.PERSISTENCY_OFF
|
|
||||||
elif args.stereo_post_processing_temporal_persistency-mode == "VALID_8_OUT_OF_8":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.VALID_8_OUT_OF_8
|
|
||||||
elif args.stereo_post_processing_temporal_persistency-mode == "VALID_2_IN_LAST_3":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.VALID_2_IN_LAST_3
|
|
||||||
elif args.stereo_post_processing_temporal_persistency-mode == "VALID_2_IN_LAST_4":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.VALID_2_IN_LAST_4
|
|
||||||
elif args.stereo_post_processing_temporal_persistency-mode == "VALID_2_OUT_OF_8":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.VALID_2_OUT_OF_8
|
|
||||||
elif args.stereo_post_processing_temporal_persistency-mode == "VALID_1_IN_LAST_2":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.VALID_1_IN_LAST_2
|
|
||||||
elif args.stereo_post_processing_temporal_persistency-mode == "VALID_1_IN_LAST_5":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.VALID_1_IN_LAST_5
|
|
||||||
elif args.stereo_post_processing_temporal_persistency-mode == "VALID_1_IN_LAST_8":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.VALID_1_IN_LAST_8
|
|
||||||
elif args.stereo_post_processing_temporal_persistency-mode == "PERSISTENCY_INDEFINITELY":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.PERSISTENCY_INDEFINITELY
|
|
||||||
else:
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.VALID_2_IN_LAST_4
|
|
||||||
|
|
||||||
filters.append(TemporalFilter(
|
|
||||||
enable=args.stereo_post_processing_temporal_enable,
|
|
||||||
persistencyMode=mode,
|
|
||||||
alpha=args.stereo_post_processing_temporal_alpha,
|
|
||||||
delta=args.stereo_post_processing_temporal_delta
|
|
||||||
))
|
|
||||||
|
|
||||||
if args.stereo_post_processing_spatial_filter:
|
|
||||||
filters.append(SpatialFilter(enable=args.stereo_post_processing_spatial_enable,
|
|
||||||
hole_filling_radius=args.stereo_post_processing_spatial_hole_filling_radius,
|
|
||||||
alpha=args.stereo_post_processing_spatial_alpha,
|
|
||||||
delta=args.stereo_post_processing_spatial_delta,
|
|
||||||
num_iterations=args.stereo_post_processing_spatial_num_iterations,
|
|
||||||
))
|
|
||||||
|
|
||||||
if args.stereo_post_processing_threshold_filter:
|
|
||||||
filters.append(ThresholdFilter(
|
|
||||||
min_range=args.stereo_post_processing_threshold_min_range,
|
|
||||||
max_range=args.stereo_post_processing_threshold_max_range,
|
|
||||||
))
|
|
||||||
|
|
||||||
if args.stereo_post_processing_decimation_filter:
|
|
||||||
|
|
||||||
if args.stereo_post_processing_decimation_mode == "PIXEL_SKIPPING":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.DecimationFilter.DecimationMode.PIXEL_SKIPPING
|
|
||||||
if args.stereo_post_processing_decimation_mode == "NON_ZERO_MEDIAN":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.DecimationFilter.DecimationMode.NON_ZERO_MEDIAN
|
|
||||||
if args.stereo_post_processing_decimation_mode == "NON_ZERO_MEAN":
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.DecimationFilter.DecimationMode.NON_ZERO_MEAN
|
|
||||||
else:
|
|
||||||
mode=dai.RawStereoDepthConfig.PostProcessing.DecimationFilter.DecimationMode.PIXEL_SKIPPING
|
|
||||||
|
|
||||||
filters.append(DecimationFilter(
|
|
||||||
decimation_factor=args.stereo_post_processing_decimation_decimal_factor,
|
|
||||||
mode=mode
|
|
||||||
))
|
|
||||||
|
|
||||||
return filters
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
execute_from_command_line()
|
|
137
camera/depthai.py
Normal file
137
camera/depthai.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
|
import events.events_pb2
|
||||||
|
|
||||||
|
import depthai as dai
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Closer-in minimum depth, disparity range is doubled (from 95 to 190):
|
||||||
|
extended_disparity = False
|
||||||
|
# Better accuracy for longer distance, fractional disparity 32-levels:
|
||||||
|
subpixel = True
|
||||||
|
# Better handling for occlusions:
|
||||||
|
lr_check = True
|
||||||
|
|
||||||
|
class FramePublisher:
|
||||||
|
def __init__(self, mqtt_client: mqtt.Client, frame_topic: str, img_width: int, img_height: int):
|
||||||
|
self._mqtt_client = mqtt_client
|
||||||
|
self._frame_topic = frame_topic
|
||||||
|
self._img_width = img_width
|
||||||
|
self._img_height = img_height
|
||||||
|
self._depth = None
|
||||||
|
self._pipeline = self._configure_pipeline()
|
||||||
|
|
||||||
|
def _configure_pipeline(self) -> dai.Pipeline:
|
||||||
|
logger.info("configure pipeline")
|
||||||
|
pipeline = dai.Pipeline()
|
||||||
|
|
||||||
|
cam_rgb = pipeline.create(dai.node.ColorCamera)
|
||||||
|
xout_rgb = pipeline.create(dai.node.XLinkOut)
|
||||||
|
|
||||||
|
xout_rgb.setStreamName("rgb")
|
||||||
|
|
||||||
|
monoLeft = pipeline.create(dai.node.MonoCamera)
|
||||||
|
monoRight = pipeline.create(dai.node.MonoCamera)
|
||||||
|
depth = pipeline.create(dai.node.StereoDepth)
|
||||||
|
xout = pipeline.create(dai.node.XLinkOut)
|
||||||
|
self._depth = depth
|
||||||
|
|
||||||
|
xout.setStreamName("disparity")
|
||||||
|
|
||||||
|
# Properties
|
||||||
|
monoLeft.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
|
||||||
|
monoLeft.setBoardSocket(dai.CameraBoardSocket.LEFT)
|
||||||
|
monoRight.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
|
||||||
|
monoRight.setBoardSocket(dai.CameraBoardSocket.RIGHT)
|
||||||
|
|
||||||
|
# Create a node that will produce the depth map (using disparity output as it's easier to visualize depth this way)
|
||||||
|
depth.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.HIGH_DENSITY)
|
||||||
|
# Options: MEDIAN_OFF, KERNEL_3x3, KERNEL_5x5, KERNEL_7x7 (default)
|
||||||
|
depth.initialConfig.setMedianFilter(dai.MedianFilter.KERNEL_7x7)
|
||||||
|
depth.setLeftRightCheck(lr_check)
|
||||||
|
depth.setExtendedDisparity(extended_disparity)
|
||||||
|
depth.setSubpixel(subpixel)
|
||||||
|
|
||||||
|
config = depth.initialConfig.get()
|
||||||
|
config.postProcessing.speckleFilter.enable = True
|
||||||
|
config.postProcessing.speckleFilter.speckleRange = 50
|
||||||
|
config.postProcessing.temporalFilter.enable = False
|
||||||
|
config.postProcessing.spatialFilter.enable = False
|
||||||
|
config.postProcessing.spatialFilter.holeFillingRadius = 2
|
||||||
|
config.postProcessing.spatialFilter.numIterations = 1
|
||||||
|
#config.postProcessing.thresholdFilter.minRange = 400
|
||||||
|
#config.postProcessing.thresholdFilter.maxRange = 15000
|
||||||
|
config.postProcessing.decimationFilter.decimationFactor = 2
|
||||||
|
depth.initialConfig.set(config)
|
||||||
|
|
||||||
|
# Linking
|
||||||
|
monoLeft.out.link(depth.left)
|
||||||
|
monoRight.out.link(depth.right)
|
||||||
|
depth.disparity.link(xout.input)
|
||||||
|
|
||||||
|
# Properties
|
||||||
|
cam_rgb.setBoardSocket(dai.CameraBoardSocket.RGB)
|
||||||
|
cam_rgb.setPreviewSize(width=self._img_width, height=self._img_height)
|
||||||
|
cam_rgb.setInterleaved(False)
|
||||||
|
cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.RGB)
|
||||||
|
cam_rgb.setFps(30)
|
||||||
|
|
||||||
|
# Linking
|
||||||
|
cam_rgb.preview.link(xout_rgb.input)
|
||||||
|
logger.info("pipeline configured")
|
||||||
|
return pipeline
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# Connect to device and start pipeline
|
||||||
|
with dai.Device(self._pipeline) as device:
|
||||||
|
logger.info('MxId: %s', device.getDeviceInfo().getMxId())
|
||||||
|
logger.info('USB speed: %s', device.getUsbSpeed())
|
||||||
|
logger.info('Connected cameras: %s', device.getConnectedCameras())
|
||||||
|
|
||||||
|
logger.info("output queues found: %s", device.getOutputQueueNames())
|
||||||
|
|
||||||
|
device.startPipeline()
|
||||||
|
# Queues
|
||||||
|
queue_size = 4
|
||||||
|
q_rgb = device.getOutputQueue("rgb", maxSize=queue_size, blocking=False)
|
||||||
|
|
||||||
|
# Output queue will be used to get the disparity frames from the outputs defined above
|
||||||
|
q_disparity = device.getOutputQueue(name="disparity", maxSize=4, blocking=False)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
logger.debug("wait for new frame")
|
||||||
|
inRgb = q_rgb.get() # blocking call, will wait until a new data has arrived
|
||||||
|
inDisparity = q_disparity.get()
|
||||||
|
# im_resize = inRgb.getCvFrame()
|
||||||
|
im_resize = inDisparity.getCvFrame()
|
||||||
|
|
||||||
|
# Normalization for better visualization
|
||||||
|
im_resize = (im_resize * (255 / self._depth.initialConfig.getMaxDisparity())).astype(np.uint8)
|
||||||
|
|
||||||
|
# Available color maps: https://docs.opencv.org/3.4/d3/d50/group__imgproc__colormap.html
|
||||||
|
# im_resize = cv2.applyColorMap(im_resize, cv2.COLORMAP_JET)
|
||||||
|
|
||||||
|
is_success, im_buf_arr = cv2.imencode(".jpg", im_resize)
|
||||||
|
byte_im = im_buf_arr.tobytes()
|
||||||
|
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
frame_msg = events.events_pb2.FrameMessage()
|
||||||
|
frame_msg.id.name = "robocar-oak-camera-oak"
|
||||||
|
frame_msg.id.id = str(int(now.timestamp() * 1000))
|
||||||
|
frame_msg.id.created_at.FromDatetime(now)
|
||||||
|
frame_msg.frame = byte_im
|
||||||
|
|
||||||
|
logger.debug("publish frame event to %s", self._frame_topic)
|
||||||
|
self._mqtt_client.publish(topic=self._frame_topic,
|
||||||
|
payload=frame_msg.SerializeToString(),
|
||||||
|
qos=0,
|
||||||
|
retain=False)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("unexpected error: %s", str(e))
|
@ -1,666 +0,0 @@
|
|||||||
"""
|
|
||||||
Camera event loop
|
|
||||||
"""
|
|
||||||
import abc
|
|
||||||
import datetime
|
|
||||||
import logging
|
|
||||||
import pathlib
|
|
||||||
import time
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import List, Any
|
|
||||||
|
|
||||||
import cv2
|
|
||||||
import depthai as dai
|
|
||||||
import events.events_pb2 as evt
|
|
||||||
import numpy as np
|
|
||||||
import numpy.typing as npt
|
|
||||||
import paho.mqtt.client as mqtt
|
|
||||||
from depthai import Device
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
_NN_PATH = "/models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob"
|
|
||||||
_NN_WIDTH = 192
|
|
||||||
_NN_HEIGHT = 192
|
|
||||||
|
|
||||||
_PREVIEW_WIDTH = 640
|
|
||||||
_PREVIEW_HEIGHT = 480
|
|
||||||
|
|
||||||
_CAMERA_BASELINE_IN_MM = 75
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectProcessor:
|
|
||||||
"""
|
|
||||||
Processor for Object detection
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, mqtt_client: mqtt.Client, objects_topic: str, objects_threshold: float):
|
|
||||||
self._mqtt_client = mqtt_client
|
|
||||||
self._objects_topic = objects_topic
|
|
||||||
self._objects_threshold = objects_threshold
|
|
||||||
|
|
||||||
def process(self, in_nn: dai.NNData, frame_ref: evt.FrameRef) -> None:
|
|
||||||
"""
|
|
||||||
Parse and publish result of NeuralNetwork result
|
|
||||||
:param in_nn: NeuralNetwork result read from device
|
|
||||||
:param frame_ref: Id of the frame where objects are been detected
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
detection_boxes = np.array(in_nn.getLayerFp16("ExpandDims")).reshape((100, 4))
|
|
||||||
detection_scores = np.array(in_nn.getLayerFp16("ExpandDims_2")).reshape((100,))
|
|
||||||
# keep boxes bigger than threshold
|
|
||||||
mask = detection_scores >= self._objects_threshold
|
|
||||||
boxes = detection_boxes[mask]
|
|
||||||
scores = detection_scores[mask]
|
|
||||||
|
|
||||||
if boxes.shape[0] > 0:
|
|
||||||
self._publish_objects(boxes, frame_ref, scores)
|
|
||||||
|
|
||||||
def _publish_objects(self, boxes: npt.NDArray[np.float64], frame_ref: evt.FrameRef, scores: npt.NDArray[np.float64]) -> None:
|
|
||||||
objects_msg = evt.ObjectsMessage()
|
|
||||||
objs = []
|
|
||||||
for i in range(boxes.shape[0]):
|
|
||||||
logger.debug("new object detected: %s", str(boxes[i]))
|
|
||||||
objs.append(_bbox_to_object(boxes[i], scores[i].astype(float)))
|
|
||||||
objects_msg.objects.extend(objs)
|
|
||||||
objects_msg.frame_ref.name = frame_ref.name
|
|
||||||
objects_msg.frame_ref.id = frame_ref.id
|
|
||||||
objects_msg.frame_ref.created_at.FromDatetime(frame_ref.created_at.ToDatetime())
|
|
||||||
logger.debug("publish object event to %s", self._objects_topic)
|
|
||||||
self._mqtt_client.publish(topic=self._objects_topic,
|
|
||||||
payload=objects_msg.SerializeToString(),
|
|
||||||
qos=0,
|
|
||||||
retain=False)
|
|
||||||
|
|
||||||
|
|
||||||
class FrameProcessError(Exception):
|
|
||||||
"""
|
|
||||||
Error base for invalid frame processing
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
message -- explanation of the error
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, message: str):
|
|
||||||
"""
|
|
||||||
:param message: explanation of the error
|
|
||||||
"""
|
|
||||||
self.message = message
|
|
||||||
|
|
||||||
|
|
||||||
class FrameProcessor:
|
|
||||||
"""
|
|
||||||
Processor for camera frames
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, mqtt_client: mqtt.Client, frame_topic: str):
|
|
||||||
self._mqtt_client = mqtt_client
|
|
||||||
self._frame_topic = frame_topic
|
|
||||||
|
|
||||||
def process(self, img: dai.ImgFrame) -> Any:
|
|
||||||
"""
|
|
||||||
Publish camera frames
|
|
||||||
:param img: image read from camera
|
|
||||||
:return:
|
|
||||||
id frame reference
|
|
||||||
:raise:
|
|
||||||
FrameProcessError if frame can't be processed
|
|
||||||
"""
|
|
||||||
im_resize = img.getCvFrame()
|
|
||||||
is_success, im_buf_arr = cv2.imencode(".jpg", im_resize)
|
|
||||||
if not is_success:
|
|
||||||
raise FrameProcessError("unable to process to encode frame to jpg")
|
|
||||||
byte_im = im_buf_arr.tobytes()
|
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
frame_msg = evt.FrameMessage()
|
|
||||||
frame_msg.id.name = "robocar-oak-camera-oak"
|
|
||||||
frame_msg.id.id = str(int(now.timestamp() * 1000))
|
|
||||||
frame_msg.id.created_at.FromDatetime(now)
|
|
||||||
frame_msg.frame = byte_im
|
|
||||||
logger.debug("publish frame event to %s", self._frame_topic)
|
|
||||||
self._mqtt_client.publish(topic=self._frame_topic,
|
|
||||||
payload=frame_msg.SerializeToString(),
|
|
||||||
qos=0,
|
|
||||||
retain=False)
|
|
||||||
return frame_msg.id
|
|
||||||
|
|
||||||
|
|
||||||
class DisparityProcessor:
|
|
||||||
"""
|
|
||||||
Processor for camera frames
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, mqtt_client: mqtt.Client, disparity_topic: str):
|
|
||||||
self._mqtt_client = mqtt_client
|
|
||||||
self._disparity_topic = disparity_topic
|
|
||||||
|
|
||||||
def process(self, img: dai.ImgFrame, frame_ref: evt.FrameRef, focal_length_in_pixels: float,
|
|
||||||
baseline_mm: float = _CAMERA_BASELINE_IN_MM) -> None:
|
|
||||||
im_frame = img.getCvFrame()
|
|
||||||
is_success, im_buf_arr = cv2.imencode(".jpg", im_frame)
|
|
||||||
if not is_success:
|
|
||||||
raise FrameProcessError("unable to process to encode frame to jpg")
|
|
||||||
byte_im = im_buf_arr.tobytes()
|
|
||||||
|
|
||||||
disparity_msg = evt.DisparityMessage()
|
|
||||||
disparity_msg.disparity = byte_im
|
|
||||||
disparity_msg.frame_ref.name = frame_ref.name
|
|
||||||
disparity_msg.frame_ref.id = frame_ref.id
|
|
||||||
disparity_msg.frame_ref.created_at.FromDatetime(frame_ref.created_at.ToDatetime())
|
|
||||||
disparity_msg.focal_length_in_pixels = focal_length_in_pixels
|
|
||||||
disparity_msg.baseline_in_mm = baseline_mm
|
|
||||||
|
|
||||||
self._mqtt_client.publish(topic=self._disparity_topic,
|
|
||||||
payload=disparity_msg.SerializeToString(),
|
|
||||||
qos=0,
|
|
||||||
retain=False)
|
|
||||||
|
|
||||||
|
|
||||||
class Source(abc.ABC):
|
|
||||||
"""Base class for image source"""
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def get_stream_name(self) -> str:
|
|
||||||
"""
|
|
||||||
Queue/stream name to use to get data
|
|
||||||
|
|
||||||
:return: steam name
|
|
||||||
"""
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def link(self, input_node: dai.Node.Input) -> None:
|
|
||||||
"""
|
|
||||||
Link this source to the input node
|
|
||||||
|
|
||||||
:param: input_node: input node to link
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectDetectionNN:
|
|
||||||
"""
|
|
||||||
Node to detect objects into image
|
|
||||||
|
|
||||||
Read image as input and apply resize transformation before to run NN on it
|
|
||||||
Result is available with 'get_stream_name()' stream
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, pipeline: dai.Pipeline):
|
|
||||||
# Define a neural network that will make predictions based on the source frames
|
|
||||||
detection_nn = pipeline.createNeuralNetwork()
|
|
||||||
detection_nn.setBlobPath(pathlib.Path(_NN_PATH))
|
|
||||||
detection_nn.setNumPoolFrames(4)
|
|
||||||
detection_nn.input.setBlocking(False)
|
|
||||||
detection_nn.setNumInferenceThreads(2)
|
|
||||||
self._detection_nn = detection_nn
|
|
||||||
self._xout = self._configure_xout_nn(pipeline)
|
|
||||||
self._detection_nn.out.link(self._xout.input)
|
|
||||||
self._manip_image = self._configure_manip(pipeline)
|
|
||||||
self._manip_image.out.link(self._detection_nn.input)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _configure_manip(pipeline: dai.Pipeline) -> dai.node.ImageManip:
|
|
||||||
# Resize image
|
|
||||||
manip = pipeline.createImageManip()
|
|
||||||
manip.initialConfig.setResize(_NN_WIDTH, _NN_HEIGHT)
|
|
||||||
manip.initialConfig.setFrameType(dai.ImgFrame.Type.RGB888p)
|
|
||||||
manip.initialConfig.setKeepAspectRatio(False)
|
|
||||||
return manip
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _configure_xout_nn(pipeline: dai.Pipeline) -> dai.node.XLinkOut:
|
|
||||||
xout_nn = pipeline.createXLinkOut()
|
|
||||||
xout_nn.setStreamName("nn")
|
|
||||||
xout_nn.input.setBlocking(False)
|
|
||||||
return xout_nn
|
|
||||||
|
|
||||||
def get_stream_name(self) -> str:
|
|
||||||
"""
|
|
||||||
Queue/stream name to use to get data
|
|
||||||
|
|
||||||
:return: stream name
|
|
||||||
"""
|
|
||||||
return self._xout.getStreamName()
|
|
||||||
|
|
||||||
def get_input(self) -> dai.Node.Input:
|
|
||||||
"""
|
|
||||||
Get input node to use to link with source node
|
|
||||||
:return: input to link with source output, see Source.link()
|
|
||||||
"""
|
|
||||||
return self._manip_image.inputImage
|
|
||||||
|
|
||||||
|
|
||||||
class CameraSource(Source):
|
|
||||||
"""Image source based on camera preview"""
|
|
||||||
|
|
||||||
def __init__(self, pipeline: dai.Pipeline, img_width: int, img_height: int, fps: int):
|
|
||||||
self._cam_rgb = pipeline.createColorCamera()
|
|
||||||
self._xout_rgb = pipeline.createXLinkOut()
|
|
||||||
self._xout_rgb.setStreamName("rgb")
|
|
||||||
|
|
||||||
# Properties
|
|
||||||
self._cam_rgb.setBoardSocket(dai.CameraBoardSocket.RGB)
|
|
||||||
self._cam_rgb.setPreviewSize(width=_PREVIEW_WIDTH, height=_PREVIEW_HEIGHT)
|
|
||||||
self._cam_rgb.setInterleaved(False)
|
|
||||||
self._cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.RGB)
|
|
||||||
self._cam_rgb.setFps(fps)
|
|
||||||
self._resize_manip = self._configure_manip(pipeline=pipeline, img_width=img_width, img_height=img_height)
|
|
||||||
|
|
||||||
# link camera preview to output
|
|
||||||
self._cam_rgb.preview.link(self._resize_manip.inputImage)
|
|
||||||
self._resize_manip.out.link(self._xout_rgb.input)
|
|
||||||
|
|
||||||
def link(self, input_node: dai.Node.Input) -> None:
|
|
||||||
self._cam_rgb.preview.link(input_node)
|
|
||||||
|
|
||||||
def get_stream_name(self) -> str:
|
|
||||||
return self._xout_rgb.getStreamName()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _configure_manip(pipeline: dai.Pipeline, img_width: int, img_height: int) -> dai.node.ImageManip:
|
|
||||||
# Resize image
|
|
||||||
manip = pipeline.createImageManip()
|
|
||||||
manip.initialConfig.setResize(img_width, img_height)
|
|
||||||
manip.initialConfig.setFrameType(dai.ImgFrame.Type.RGB888p)
|
|
||||||
manip.initialConfig.setKeepAspectRatio(False)
|
|
||||||
return manip
|
|
||||||
|
|
||||||
|
|
||||||
class StereoDepthPostFilter(abc.ABC):
|
|
||||||
@abc.abstractmethod
|
|
||||||
def apply(self, config: dai.RawStereoDepthConfig) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MedianFilter(StereoDepthPostFilter):
|
|
||||||
"""
|
|
||||||
This is a non-edge preserving Median filter, which can be used to reduce noise and smoothen the depth map.
|
|
||||||
Median filter is implemented in hardware, so it’s the fastest filter.
|
|
||||||
"""
|
|
||||||
def __init__(self, value: dai.MedianFilter = dai.MedianFilter.KERNEL_7x7) -> None:
|
|
||||||
self._value = value
|
|
||||||
|
|
||||||
def apply(self, config: dai.RawStereoDepthConfig) -> None:
|
|
||||||
config.postProcessing.median.value = self._value
|
|
||||||
|
|
||||||
|
|
||||||
class SpeckleFilter(StereoDepthPostFilter):
|
|
||||||
"""
|
|
||||||
Speckle Filter is used to reduce the speckle noise. Speckle noise is a region with huge variance between
|
|
||||||
neighboring disparity/depth pixels, and speckle filter tries to filter this region.
|
|
||||||
"""
|
|
||||||
def __init__(self, enable: bool = True, speckle_range: int = 50) -> None:
|
|
||||||
"""
|
|
||||||
:param enable: Whether to enable or disable the filter.
|
|
||||||
:param speckle_range: Speckle search range.
|
|
||||||
"""
|
|
||||||
self._enable = enable
|
|
||||||
self._speckle_range = speckle_range
|
|
||||||
|
|
||||||
def apply(self, config: dai.RawStereoDepthConfig) -> None:
|
|
||||||
config.postProcessing.speckleFilter.enable = self._enable
|
|
||||||
config.postProcessing.speckleFilter.speckleRange = self._speckle_range
|
|
||||||
|
|
||||||
|
|
||||||
class TemporalFilter(StereoDepthPostFilter):
|
|
||||||
"""
|
|
||||||
Temporal Filter is intended to improve the depth data persistency by manipulating per-pixel values based on
|
|
||||||
previous frames. The filter performs a single pass on the data, adjusting the depth values while also updating the
|
|
||||||
tracking history. In cases where the pixel data is missing or invalid, the filter uses a user-defined persistency
|
|
||||||
mode to decide whether the missing value should be rectified with stored data. Note that due to its reliance on
|
|
||||||
historic data the filter may introduce visible blurring/smearing artifacts, and therefore is best-suited for
|
|
||||||
static scenes.
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
enable: bool = True,
|
|
||||||
persistencyMode: dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode=dai.RawStereoDepthConfig.PostProcessing.TemporalFilter.PersistencyMode.VALID_2_IN_LAST_4,
|
|
||||||
alpha: float = 0.4,
|
|
||||||
delta: int = 0):
|
|
||||||
"""
|
|
||||||
:param enable: Whether to enable or disable the filter.
|
|
||||||
:param persistencyMode: Persistency mode. If the current disparity/depth value is invalid, it will be replaced
|
|
||||||
by an older value, based on persistency mode.
|
|
||||||
:param alpha: The Alpha factor in an exponential moving average with Alpha=1 - no filter.
|
|
||||||
Alpha = 0 - infinite filter. Determines the extent of the temporal history that should be averaged.
|
|
||||||
:param delta: Step-size boundary. Establishes the threshold used to preserve surfaces (edges).
|
|
||||||
If the disparity value between neighboring pixels exceed the disparity threshold set by this delta parameter,
|
|
||||||
then filtering will be temporarily disabled. Default value 0 means auto: 3 disparity integer levels.
|
|
||||||
In case of subpixel mode it’s 3*number of subpixel levels.
|
|
||||||
"""
|
|
||||||
self._enable = enable
|
|
||||||
self._persistencyMode = persistencyMode
|
|
||||||
self._alpha = alpha
|
|
||||||
self._delta = delta
|
|
||||||
|
|
||||||
def apply(self, config: dai.RawStereoDepthConfig) -> None:
|
|
||||||
config.postProcessing.temporalFilter.enable = self._enable
|
|
||||||
config.postProcessing.temporalFilter.persistencyMode = self._persistencyMode
|
|
||||||
config.postProcessing.temporalFilter.alpha = self._alpha
|
|
||||||
config.postProcessing.temporalFilter.delta = self._delta
|
|
||||||
|
|
||||||
|
|
||||||
class SpatialFilter(StereoDepthPostFilter):
|
|
||||||
"""
|
|
||||||
Spatial Edge-Preserving Filter will fill invalid depth pixels with valid neighboring depth pixels. It performs a
|
|
||||||
series of 1D horizontal and vertical passes or iterations, to enhance the smoothness of the reconstructed data.
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
enable: bool = True,
|
|
||||||
hole_filling_radius: int = 2,
|
|
||||||
alpha: float = 0.5,
|
|
||||||
delta: int = 0,
|
|
||||||
num_iterations: int = 1):
|
|
||||||
"""
|
|
||||||
:param enable: Whether to enable or disable the filter.
|
|
||||||
:param hole_filling_radius: An in-place heuristic symmetric hole-filling mode applied horizontally during
|
|
||||||
the filter passes. Intended to rectify minor artefacts with minimal performance impact. Search radius for
|
|
||||||
hole filling.
|
|
||||||
:param alpha: The Alpha factor in an exponential moving average with Alpha=1 - no filter.
|
|
||||||
Alpha = 0 - infinite filter. Determines the amount of smoothing.
|
|
||||||
:param delta: Step-size boundary. Establishes the threshold used to preserve “edges”. If the disparity value
|
|
||||||
between neighboring pixels exceed the disparity threshold set by this delta parameter, then filtering will be
|
|
||||||
temporarily disabled. Default value 0 means auto: 3 disparity integer levels. In case of subpixel mode it’s
|
|
||||||
3*number of subpixel levels.
|
|
||||||
:param num_iterations: Number of iterations over the image in both horizontal and vertical direction.
|
|
||||||
"""
|
|
||||||
self._enable = enable
|
|
||||||
self._hole_filling_radius = hole_filling_radius
|
|
||||||
self._alpha = alpha
|
|
||||||
self._delta = delta
|
|
||||||
self._num_iterations = num_iterations
|
|
||||||
|
|
||||||
def apply(self, config: dai.RawStereoDepthConfig) -> None:
|
|
||||||
config.postProcessing.spatialFilter.enable = self._enable
|
|
||||||
config.postProcessing.spatialFilter.holeFillingRadius = self._hole_filling_radius
|
|
||||||
config.postProcessing.spatialFilter.alpha = self._alpha
|
|
||||||
config.postProcessing.spatialFilter.delta = self._delta
|
|
||||||
config.postProcessing.spatialFilter.numIterations = self._num_iterations
|
|
||||||
|
|
||||||
|
|
||||||
class ThresholdFilter(StereoDepthPostFilter):
|
|
||||||
"""
|
|
||||||
Threshold Filter filters out all disparity/depth pixels outside the configured min/max threshold values.
|
|
||||||
"""
|
|
||||||
def __init__(self, min_range: int = 400, max_range: int = 15000):
|
|
||||||
"""
|
|
||||||
:param min_range: Minimum range in depth units. Depth values under this value are invalidated.
|
|
||||||
:param max_range: Maximum range in depth units. Depth values over this value are invalidated.
|
|
||||||
"""
|
|
||||||
self._min_range = min_range
|
|
||||||
self._max_range = max_range
|
|
||||||
|
|
||||||
def apply(self, config: dai.RawStereoDepthConfig) -> None:
|
|
||||||
config.postProcessing.thresholdFilter.minRange = self._min_range
|
|
||||||
config.postProcessing.thresholdFilter.maxRange = self._max_range
|
|
||||||
|
|
||||||
|
|
||||||
class DecimationFilter(StereoDepthPostFilter):
|
|
||||||
"""
|
|
||||||
Decimation Filter will sub-samples the depth map, which means it reduces the depth scene complexity and allows
|
|
||||||
other filters to run faster. Setting decimationFactor to 2 will downscale 1280x800 depth map to 640x400.
|
|
||||||
"""
|
|
||||||
def __init__(self,
|
|
||||||
decimation_factor: int = 1,
|
|
||||||
decimation_mode: dai.RawStereoDepthConfig.PostProcessing.DecimationFilter.DecimationMode = dai.RawStereoDepthConfig.PostProcessing.DecimationFilter.DecimationMode.PIXEL_SKIPPING
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
:param decimation_factor: Decimation factor. Valid values are 1,2,3,4. Disparity/depth map x/y resolution will
|
|
||||||
be decimated with this value.
|
|
||||||
:param decimation_mode: Decimation algorithm type.
|
|
||||||
"""
|
|
||||||
self._decimation_factor = decimation_factor
|
|
||||||
self._mode = decimation_mode
|
|
||||||
|
|
||||||
def apply(self, config: dai.RawStereoDepthConfig) -> None:
|
|
||||||
config.postProcessing.decimationFilter.decimationFactor = self._decimation_factor
|
|
||||||
config.postProcessing.decimationFilter.decimationMode = self._mode
|
|
||||||
|
|
||||||
|
|
||||||
class DepthSource(Source):
|
|
||||||
def __init__(self, pipeline: dai.Pipeline,
|
|
||||||
extended_disparity: bool = False,
|
|
||||||
subpixel: bool = False,
|
|
||||||
lr_check: bool = True,
|
|
||||||
stereo_filters: List[StereoDepthPostFilter] = []
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
# Closer-in minimum depth, disparity range is doubled (from 95 to 190):
|
|
||||||
extended_disparity = False
|
|
||||||
# Better accuracy for longer distance, fractional disparity 32-levels:
|
|
||||||
subpixel = False
|
|
||||||
# Better handling for occlusions:
|
|
||||||
lr_check = True
|
|
||||||
"""
|
|
||||||
self._monoLeft = pipeline.create(dai.node.MonoCamera)
|
|
||||||
self._monoRight = pipeline.create(dai.node.MonoCamera)
|
|
||||||
self._depth = pipeline.create(dai.node.StereoDepth)
|
|
||||||
self._xout_disparity = pipeline.create(dai.node.XLinkOut)
|
|
||||||
|
|
||||||
self._xout_disparity.setStreamName("disparity")
|
|
||||||
|
|
||||||
# Properties
|
|
||||||
self._monoLeft.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
|
|
||||||
self._monoLeft.setCamera("left")
|
|
||||||
self._monoLeft.out.link(self._depth.left)
|
|
||||||
self._monoRight.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
|
|
||||||
self._monoRight.setCamera("right")
|
|
||||||
self._monoRight.out.link(self._depth.right)
|
|
||||||
|
|
||||||
# Create a node that will produce the depth map
|
|
||||||
# (using disparity output as it's easier to visualize depth this way)
|
|
||||||
self._depth.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.HIGH_DENSITY)
|
|
||||||
# Options: MEDIAN_OFF, KERNEL_3x3, KERNEL_5x5, KERNEL_7x7 (default)
|
|
||||||
self._depth.initialConfig.setMedianFilter(dai.MedianFilter.KERNEL_7x7)
|
|
||||||
self._depth.setLeftRightCheck(lr_check)
|
|
||||||
self._depth.setExtendedDisparity(extended_disparity)
|
|
||||||
self._depth.setSubpixel(subpixel)
|
|
||||||
self._depth.disparity.link(self._xout_disparity.input)
|
|
||||||
|
|
||||||
if len(stereo_filters) > 0:
|
|
||||||
# Configure post-processing filters
|
|
||||||
config = self._depth.initialConfig.get()
|
|
||||||
for filter in stereo_filters:
|
|
||||||
filter.apply(config)
|
|
||||||
self._depth.initialConfig.set(config)
|
|
||||||
|
|
||||||
def get_stream_name(self) -> str:
|
|
||||||
return self._xout_disparity.getStreamName()
|
|
||||||
|
|
||||||
def link(self, input_node: dai.Node.Input) -> None:
|
|
||||||
self._depth.disparity.link(input_node)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MqttConfig:
|
|
||||||
"""MQTT configuration"""
|
|
||||||
host: str
|
|
||||||
topic: str
|
|
||||||
port: int = 1883
|
|
||||||
qos: int = 0
|
|
||||||
|
|
||||||
|
|
||||||
class MqttSource(Source):
|
|
||||||
"""Image source based onto mqtt stream"""
|
|
||||||
|
|
||||||
def __init__(self, device: Device, pipeline: dai.Pipeline, mqtt_config: MqttConfig):
|
|
||||||
self._mqtt_config = mqtt_config
|
|
||||||
self._client = mqtt.Client()
|
|
||||||
self._client.user_data_set(mqtt_config)
|
|
||||||
self._client.on_connect = self._on_connect
|
|
||||||
self._client.on_message = self._on_message
|
|
||||||
|
|
||||||
self._img_in = pipeline.createXLinkIn()
|
|
||||||
self._img_in.setStreamName("img_input")
|
|
||||||
self._img_out = pipeline.createXLinkOut()
|
|
||||||
self._img_out.setStreamName("img_output")
|
|
||||||
self._img_in.out.link(self._img_out.input)
|
|
||||||
|
|
||||||
self._img_in_queue = device.getInputQueue(self._img_in.getStreamName())
|
|
||||||
|
|
||||||
def run(self) -> None:
|
|
||||||
""" Connect and start mqtt loop """
|
|
||||||
self._client.connect(host=self._mqtt_config.host, port=self._mqtt_config.port)
|
|
||||||
self._client.loop_start()
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
|
||||||
"""Stop and disconnect mqtt loop"""
|
|
||||||
self._client.loop_stop()
|
|
||||||
self._client.disconnect()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def _on_connect(client: mqtt.Client, userdata: MqttConfig, flags: Any,
|
|
||||||
result_connection: Any) -> None:
|
|
||||||
# if we lose the connection and reconnect then subscriptions will be renewed.
|
|
||||||
client.subscribe(topic=userdata.topic, qos=userdata.qos)
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
def _on_message(self, _: mqtt.Client, user_data: MqttConfig, msg: mqtt.MQTTMessage) -> None:
|
|
||||||
frame_msg = evt.FrameMessage()
|
|
||||||
frame_msg.ParseFromString(msg.payload)
|
|
||||||
|
|
||||||
frame = np.asarray(frame_msg.frame, dtype="uint8")
|
|
||||||
frame = cv2.imdecode(frame, cv2.IMREAD_COLOR)
|
|
||||||
nn_data = dai.NNData()
|
|
||||||
nn_data.setLayer("data", _to_planar(frame, (300, 300)))
|
|
||||||
self._img_in_queue.send(nn_data)
|
|
||||||
|
|
||||||
def get_stream_name(self) -> str:
|
|
||||||
return self._img_out.getStreamName()
|
|
||||||
|
|
||||||
def link(self, input_node: dai.Node.Input) -> None:
|
|
||||||
self._img_in.out.link(input_node)
|
|
||||||
|
|
||||||
|
|
||||||
def _to_planar(arr: npt.NDArray[np.uint8], shape: tuple[int, int]) -> list[int]:
|
|
||||||
return [val for channel in cv2.resize(arr, shape).transpose(2, 0, 1) for y_col in channel for val in y_col]
|
|
||||||
|
|
||||||
|
|
||||||
class PipelineController:
|
|
||||||
"""
|
|
||||||
Pipeline controller that drive camera device
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, frame_processor: FrameProcessor,
|
|
||||||
object_processor: ObjectProcessor, disparity_processor: DisparityProcessor,
|
|
||||||
camera: Source, depth_source: Source, object_node: ObjectDetectionNN,
|
|
||||||
pipeline: dai.Pipeline):
|
|
||||||
self._frame_processor = frame_processor
|
|
||||||
self._object_processor = object_processor
|
|
||||||
self._disparity_processor = disparity_processor
|
|
||||||
self._camera = camera
|
|
||||||
self._depth_source = depth_source
|
|
||||||
self._object_node = object_node
|
|
||||||
self._stop = False
|
|
||||||
self._pipeline = pipeline
|
|
||||||
self._configure_pipeline()
|
|
||||||
self._focal_length_in_pixels: float | None = None
|
|
||||||
|
|
||||||
def _configure_pipeline(self) -> None:
|
|
||||||
logger.info("configure pipeline")
|
|
||||||
|
|
||||||
self._pipeline.setOpenVINOVersion(version=dai.OpenVINO.VERSION_2021_4)
|
|
||||||
|
|
||||||
# Link preview to manip and manip to nn
|
|
||||||
self._camera.link(self._object_node.get_input())
|
|
||||||
|
|
||||||
logger.info("pipeline configured")
|
|
||||||
|
|
||||||
def run(self) -> None:
|
|
||||||
"""
|
|
||||||
Start event loop
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
# Connect to device and start pipeline
|
|
||||||
with Device(pipeline=self._pipeline) as dev:
|
|
||||||
logger.info('MxId: %s', dev.getDeviceInfo().getMxId())
|
|
||||||
logger.info('USB speed: %s', dev.getUsbSpeed())
|
|
||||||
logger.info('Connected cameras: %s', str(dev.getConnectedCameras()))
|
|
||||||
logger.info("output queues found: %s", str(''.join(dev.getOutputQueueNames()))) # type: ignore
|
|
||||||
|
|
||||||
calib_data = dev.readCalibration()
|
|
||||||
intrinsics = calib_data.getCameraIntrinsics(dai.CameraBoardSocket.CAM_C)
|
|
||||||
self._focal_length_in_pixels = intrinsics[0][0]
|
|
||||||
logger.info('Right mono camera focal length in pixels: %s', self._focal_length_in_pixels)
|
|
||||||
|
|
||||||
dev.startPipeline()
|
|
||||||
# Queues
|
|
||||||
queue_size = 4
|
|
||||||
q_rgb = dev.getOutputQueue(name=self._camera.get_stream_name(), maxSize=queue_size, # type: ignore
|
|
||||||
blocking=False)
|
|
||||||
q_nn = dev.getOutputQueue(name=self._object_node.get_stream_name(), maxSize=queue_size, # type: ignore
|
|
||||||
blocking=False)
|
|
||||||
if self._disparity_processor is not None:
|
|
||||||
q_disparity = dev.getOutputQueue(name=self._depth_source.get_stream_name(), maxSize=queue_size, # type: ignore
|
|
||||||
blocking=False)
|
|
||||||
else:
|
|
||||||
q_disparity = None
|
|
||||||
|
|
||||||
start_time = time.time()
|
|
||||||
counter = 0
|
|
||||||
fps = 0
|
|
||||||
display_time = time.time()
|
|
||||||
self._stop = False
|
|
||||||
while True:
|
|
||||||
if self._stop:
|
|
||||||
logger.info("stop loop event")
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
self._loop_on_camera_events(q_nn, q_rgb, q_disparity)
|
|
||||||
# pylint: disable=broad-except # bad frame or event must not stop loop
|
|
||||||
except Exception as ex:
|
|
||||||
logger.exception("unexpected error: %s", str(ex))
|
|
||||||
|
|
||||||
counter += 1
|
|
||||||
if (time.time() - start_time) > 1:
|
|
||||||
fps = counter / (time.time() - start_time)
|
|
||||||
counter = 0
|
|
||||||
start_time = time.time()
|
|
||||||
if (time.time() - display_time) >= 10:
|
|
||||||
display_time = time.time()
|
|
||||||
logger.info("fps: %s", fps)
|
|
||||||
|
|
||||||
def _loop_on_camera_events(self, q_nn: dai.DataOutputQueue, q_rgb: dai.DataOutputQueue, q_disparity: dai.DataOutputQueue) -> None:
|
|
||||||
logger.debug("wait for new frame")
|
|
||||||
|
|
||||||
# Wait for frame
|
|
||||||
in_rgb: dai.ImgFrame = q_rgb.get() # type: ignore # blocking call, will wait until a new data has arrived
|
|
||||||
try:
|
|
||||||
logger.debug("process frame")
|
|
||||||
frame_ref = self._frame_processor.process(in_rgb)
|
|
||||||
except FrameProcessError as ex:
|
|
||||||
logger.error("unable to process frame: %s", str(ex))
|
|
||||||
return
|
|
||||||
logger.debug("frame processed")
|
|
||||||
|
|
||||||
logger.debug("wait for nn response")
|
|
||||||
# Read NN result
|
|
||||||
in_nn: dai.NNData = q_nn.get() # type: ignore
|
|
||||||
logger.debug("process objects")
|
|
||||||
self._object_processor.process(in_nn, frame_ref)
|
|
||||||
logger.debug("objects processed")
|
|
||||||
|
|
||||||
logger.debug("process disparity")
|
|
||||||
if self._disparity_processor is not None:
|
|
||||||
in_disparity: dai.ImgFrame = q_disparity.get() # type: ignore
|
|
||||||
self._disparity_processor.process(in_disparity, frame_ref=frame_ref,
|
|
||||||
focal_length_in_pixels=self._focal_length_in_pixels)
|
|
||||||
logger.debug("disparity processed")
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
|
||||||
"""
|
|
||||||
Stop event loop, if loop is not running, do nothing
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
self._stop = True
|
|
||||||
|
|
||||||
|
|
||||||
def _bbox_to_object(bbox: npt.NDArray[np.float64], score: float) -> evt.Object:
|
|
||||||
obj = evt.Object()
|
|
||||||
obj.type = evt.TypeObject.ANY
|
|
||||||
obj.top = bbox[0].astype(float)
|
|
||||||
obj.right = bbox[3].astype(float)
|
|
||||||
obj.bottom = bbox[2].astype(float)
|
|
||||||
obj.left = bbox[1].astype(float)
|
|
||||||
obj.confidence = score
|
|
||||||
return obj
|
|
@ -1,201 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import typing
|
|
||||||
import unittest.mock
|
|
||||||
|
|
||||||
import depthai as dai
|
|
||||||
import events.events_pb2
|
|
||||||
import numpy as np
|
|
||||||
import numpy.typing as npt
|
|
||||||
import paho.mqtt.client as mqtt
|
|
||||||
import pytest
|
|
||||||
import pytest_mock
|
|
||||||
|
|
||||||
from camera.oak_pipeline import DisparityProcessor, ObjectProcessor, FrameProcessor, FrameProcessError
|
|
||||||
|
|
||||||
Object = dict[str, float]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mqtt_client(mocker: pytest_mock.MockerFixture) -> mqtt.Client:
|
|
||||||
return mocker.MagicMock() # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
class TestObjectProcessor:
|
|
||||||
@pytest.fixture
|
|
||||||
def frame_ref(self) -> events.events_pb2.FrameRef:
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
frame_msg = events.events_pb2.FrameMessage()
|
|
||||||
frame_msg.id.name = "robocar-oak-camera-oak"
|
|
||||||
frame_msg.id.id = str(int(now.timestamp() * 1000))
|
|
||||||
frame_msg.id.created_at.FromDatetime(now)
|
|
||||||
return frame_msg.id
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def object1(self) -> Object:
|
|
||||||
return {
|
|
||||||
"left": 0.3,
|
|
||||||
"right": 0.7,
|
|
||||||
"top": 0.1,
|
|
||||||
"bottom": 0.6,
|
|
||||||
"score": 0.8,
|
|
||||||
}
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def raw_objects_empty(self, mocker: pytest_mock.MockerFixture) -> dai.NNData:
|
|
||||||
raw_objects = mocker.MagicMock()
|
|
||||||
|
|
||||||
def mock_return(name: str) -> typing.List[typing.Union[int, typing.List[int]]]:
|
|
||||||
if name == "ExpandDims":
|
|
||||||
return [[0] * 4] * 100
|
|
||||||
elif name == "ExpandDims_2":
|
|
||||||
return [0] * 100
|
|
||||||
else:
|
|
||||||
raise ValueError(f"{name} is not a valid arg")
|
|
||||||
|
|
||||||
m = mocker.patch(target='depthai.NNData.getLayerFp16', autospec=True)
|
|
||||||
m.getLayerFp16 = mock_return
|
|
||||||
return m
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def raw_objects_one(self, mocker: pytest_mock.MockerFixture, object1: Object) -> dai.NNData:
|
|
||||||
def mock_return(name: str) -> typing.Union[npt.NDArray[np.int64], typing.List[float]]:
|
|
||||||
if name == "ExpandDims": # Detection boxes
|
|
||||||
boxes: list[list[float]] = [[0.] * 4] * 100
|
|
||||||
boxes[0] = [object1["top"], object1["left"], object1["bottom"], object1["right"]]
|
|
||||||
return np.array(boxes)
|
|
||||||
|
|
||||||
elif name == "ExpandDims_2": # Detection scores
|
|
||||||
scores: list[float] = [0.] * 100
|
|
||||||
scores[0] = object1["score"]
|
|
||||||
return scores
|
|
||||||
else:
|
|
||||||
raise ValueError(f"{name} is not a valid arg")
|
|
||||||
|
|
||||||
m = mocker.patch(target='depthai.NNData.getLayerFp16', autospec=True)
|
|
||||||
m.getLayerFp16 = mock_return
|
|
||||||
return m
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def object_processor(self, mqtt_client: mqtt.Client) -> ObjectProcessor:
|
|
||||||
return ObjectProcessor(mqtt_client, "topic/object", 0.2)
|
|
||||||
|
|
||||||
def test_process_without_object(self, object_processor: ObjectProcessor, mqtt_client: mqtt.Client,
|
|
||||||
raw_objects_empty: dai.NNData, frame_ref: events.events_pb2.FrameRef) -> None:
|
|
||||||
object_processor.process(raw_objects_empty, frame_ref)
|
|
||||||
publish_mock: unittest.mock.MagicMock = mqtt_client.publish # type: ignore
|
|
||||||
publish_mock.assert_not_called()
|
|
||||||
|
|
||||||
def test_process_with_object_with_low_score(self, object_processor: ObjectProcessor,
|
|
||||||
mqtt_client: mqtt.Client, raw_objects_one: dai.NNData,
|
|
||||||
frame_ref: events.events_pb2.FrameRef) -> None:
|
|
||||||
object_processor._objects_threshold = 0.9
|
|
||||||
object_processor.process(raw_objects_one, frame_ref)
|
|
||||||
publish_mock: unittest.mock.MagicMock = mqtt_client.publish # type: ignore
|
|
||||||
publish_mock.assert_not_called()
|
|
||||||
|
|
||||||
def test_process_with_one_object(self,
|
|
||||||
object_processor: ObjectProcessor, mqtt_client: mqtt.Client,
|
|
||||||
raw_objects_one: dai.NNData, frame_ref: events.events_pb2.FrameRef,
|
|
||||||
object1: Object) -> None:
|
|
||||||
object_processor.process(raw_objects_one, frame_ref)
|
|
||||||
left = object1["left"]
|
|
||||||
right = object1["right"]
|
|
||||||
top = object1["top"]
|
|
||||||
bottom = object1["bottom"]
|
|
||||||
score = object1["score"]
|
|
||||||
|
|
||||||
pub_mock: unittest.mock.MagicMock = mqtt_client.publish # type: ignore
|
|
||||||
pub_mock.assert_called_once_with(payload=unittest.mock.ANY, qos=0, retain=False, topic="topic/object")
|
|
||||||
payload = pub_mock.call_args.kwargs['payload']
|
|
||||||
objects_msg = events.events_pb2.ObjectsMessage()
|
|
||||||
objects_msg.ParseFromString(payload)
|
|
||||||
assert len(objects_msg.objects) == 1
|
|
||||||
assert left - 0.0001 < objects_msg.objects[0].left < left + 0.0001
|
|
||||||
assert right - 0.0001 < objects_msg.objects[0].right < right + 0.0001
|
|
||||||
assert top - 0.0001 < objects_msg.objects[0].top < top + 0.0001
|
|
||||||
assert bottom - 0.0001 < objects_msg.objects[0].bottom < bottom + 0.0001
|
|
||||||
assert score - 0.0001 < objects_msg.objects[0].confidence < score + 0.0001
|
|
||||||
assert objects_msg.frame_ref == frame_ref
|
|
||||||
|
|
||||||
|
|
||||||
class TestFrameProcessor:
|
|
||||||
@pytest.fixture
|
|
||||||
def frame_processor(self, mqtt_client: mqtt.Client) -> FrameProcessor:
|
|
||||||
return FrameProcessor(mqtt_client, "topic/frame")
|
|
||||||
|
|
||||||
def test_process(self, frame_processor: FrameProcessor, mocker: pytest_mock.MockerFixture,
|
|
||||||
mqtt_client: mqtt.Client) -> None:
|
|
||||||
img: dai.ImgFrame = mocker.MagicMock()
|
|
||||||
mocker.patch(target="cv2.imencode").return_value = (True, np.array(b"img content"))
|
|
||||||
|
|
||||||
frame_ref = frame_processor.process(img)
|
|
||||||
|
|
||||||
pub_mock: unittest.mock.MagicMock = mqtt_client.publish # type: ignore
|
|
||||||
pub_mock.assert_called_once_with(payload=unittest.mock.ANY, qos=0, retain=False, topic="topic/frame")
|
|
||||||
payload = pub_mock.call_args.kwargs['payload']
|
|
||||||
frame_msg = events.events_pb2.FrameMessage()
|
|
||||||
frame_msg.ParseFromString(payload)
|
|
||||||
|
|
||||||
assert frame_msg.id == frame_ref
|
|
||||||
assert frame_msg.frame == b"img content"
|
|
||||||
|
|
||||||
assert frame_msg.id.name == "robocar-oak-camera-oak"
|
|
||||||
assert len(frame_msg.id.id) is 13
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
assert now - datetime.timedelta(
|
|
||||||
milliseconds=10) < frame_msg.id.created_at.ToDatetime() < now + datetime.timedelta(milliseconds=10)
|
|
||||||
|
|
||||||
def test_process_error(self, frame_processor: FrameProcessor, mocker: pytest_mock.MockerFixture,
|
|
||||||
mqtt_client: mqtt.Client) -> None:
|
|
||||||
img: dai.ImgFrame = mocker.MagicMock()
|
|
||||||
mocker.patch(target="cv2.imencode").return_value = (False, None)
|
|
||||||
|
|
||||||
with pytest.raises(FrameProcessError) as ex:
|
|
||||||
_ = frame_processor.process(img)
|
|
||||||
exception_raised = ex.value
|
|
||||||
assert exception_raised.message == "unable to process to encode frame to jpg"
|
|
||||||
|
|
||||||
|
|
||||||
class TestDisparityProcessor:
|
|
||||||
@pytest.fixture
|
|
||||||
def frame_ref(self) -> events.events_pb2.FrameRef:
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
frame_msg = events.events_pb2.FrameMessage()
|
|
||||||
frame_msg.id.name = "robocar-oak-camera-oak"
|
|
||||||
frame_msg.id.id = str(int(now.timestamp() * 1000))
|
|
||||||
frame_msg.id.created_at.FromDatetime(now)
|
|
||||||
return frame_msg.id
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def disparity_processor(self, mqtt_client: mqtt.Client) -> DisparityProcessor:
|
|
||||||
return DisparityProcessor(mqtt_client, "topic/disparity")
|
|
||||||
|
|
||||||
def test_process(self, disparity_processor: DisparityProcessor, mocker: pytest_mock.MockerFixture,
|
|
||||||
frame_ref: events.events_pb2.FrameRef,
|
|
||||||
mqtt_client: mqtt.Client) -> None:
|
|
||||||
img: dai.ImgFrame = mocker.MagicMock()
|
|
||||||
mocker.patch(target="cv2.imencode").return_value = (True, np.array(b"img content"))
|
|
||||||
|
|
||||||
disparity_processor.process(img, frame_ref, 42)
|
|
||||||
|
|
||||||
pub_mock: unittest.mock.MagicMock = mqtt_client.publish # type: ignore
|
|
||||||
pub_mock.assert_called_once_with(payload=unittest.mock.ANY, qos=0, retain=False, topic="topic/disparity")
|
|
||||||
payload = pub_mock.call_args.kwargs['payload']
|
|
||||||
disparity_msg = events.events_pb2.DisparityMessage()
|
|
||||||
disparity_msg.ParseFromString(payload)
|
|
||||||
|
|
||||||
assert disparity_msg.frame_ref == frame_ref
|
|
||||||
assert disparity_msg.disparity == b"img content"
|
|
||||||
assert disparity_msg.focal_length_in_pixels == 42
|
|
||||||
assert disparity_msg.baseline_in_mm == 75
|
|
||||||
|
|
||||||
def test_process_error(self, disparity_processor: DisparityProcessor, mocker: pytest_mock.MockerFixture,
|
|
||||||
frame_ref: events.events_pb2.FrameRef,
|
|
||||||
mqtt_client: mqtt.Client) -> None:
|
|
||||||
img: dai.ImgFrame = mocker.MagicMock()
|
|
||||||
mocker.patch(target="cv2.imencode").return_value = (False, None)
|
|
||||||
|
|
||||||
with pytest.raises(FrameProcessError) as ex:
|
|
||||||
disparity_processor.process(img, frame_ref, 42)
|
|
||||||
exception_raised = ex.value
|
|
||||||
assert exception_raised.message == "unable to process to encode frame to jpg"
|
|
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
53
events/events_pb2.py
Normal file
53
events/events_pb2.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
|
# source: events/events.proto
|
||||||
|
"""Generated protocol buffer code."""
|
||||||
|
from google.protobuf.internal import builder as _builder
|
||||||
|
from google.protobuf import descriptor as _descriptor
|
||||||
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
|
from google.protobuf import symbol_database as _symbol_database
|
||||||
|
# @@protoc_insertion_point(imports)
|
||||||
|
|
||||||
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
|
from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13\x65vents/events.proto\x12\x0erobocar.events\x1a\x1fgoogle/protobuf/timestamp.proto\"T\n\x08\x46rameRef\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\x12.\n\ncreated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"C\n\x0c\x46rameMessage\x12$\n\x02id\x18\x01 \x01(\x0b\x32\x18.robocar.events.FrameRef\x12\r\n\x05\x66rame\x18\x02 \x01(\x0c\"d\n\x0fSteeringMessage\x12\x10\n\x08steering\x18\x01 \x01(\x02\x12\x12\n\nconfidence\x18\x02 \x01(\x02\x12+\n\tframe_ref\x18\x03 \x01(\x0b\x32\x18.robocar.events.FrameRef\"d\n\x0fThrottleMessage\x12\x10\n\x08throttle\x18\x01 \x01(\x02\x12\x12\n\nconfidence\x18\x02 \x01(\x02\x12+\n\tframe_ref\x18\x03 \x01(\x0b\x32\x18.robocar.events.FrameRef\"A\n\x10\x44riveModeMessage\x12-\n\ndrive_mode\x18\x01 \x01(\x0e\x32\x19.robocar.events.DriveMode\"f\n\x0eObjectsMessage\x12\'\n\x07objects\x18\x01 \x03(\x0b\x32\x16.robocar.events.Object\x12+\n\tframe_ref\x18\x02 \x01(\x0b\x32\x18.robocar.events.FrameRef\"\x80\x01\n\x06Object\x12(\n\x04type\x18\x01 \x01(\x0e\x32\x1a.robocar.events.TypeObject\x12\x0c\n\x04left\x18\x02 \x01(\x05\x12\x0b\n\x03top\x18\x03 \x01(\x05\x12\r\n\x05right\x18\x04 \x01(\x05\x12\x0e\n\x06\x62ottom\x18\x05 \x01(\x05\x12\x12\n\nconfidence\x18\x06 \x01(\x02\"&\n\x13SwitchRecordMessage\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\"\x8c\x01\n\x0bRoadMessage\x12&\n\x07\x63ontour\x18\x01 \x03(\x0b\x32\x15.robocar.events.Point\x12(\n\x07\x65llipse\x18\x02 \x01(\x0b\x32\x17.robocar.events.Ellipse\x12+\n\tframe_ref\x18\x03 \x01(\x0b\x32\x18.robocar.events.FrameRef\"\x1d\n\x05Point\x12\t\n\x01x\x18\x01 \x01(\x05\x12\t\n\x01y\x18\x02 \x01(\x05\"r\n\x07\x45llipse\x12%\n\x06\x63\x65nter\x18\x01 \x01(\x0b\x32\x15.robocar.events.Point\x12\r\n\x05width\x18\x02 \x01(\x05\x12\x0e\n\x06height\x18\x03 \x01(\x05\x12\r\n\x05\x61ngle\x18\x04 \x01(\x02\x12\x12\n\nconfidence\x18\x05 \x01(\x02\"\x82\x01\n\rRecordMessage\x12+\n\x05\x66rame\x18\x01 \x01(\x0b\x32\x1c.robocar.events.FrameMessage\x12\x31\n\x08steering\x18\x02 \x01(\x0b\x32\x1f.robocar.events.SteeringMessage\x12\x11\n\trecordSet\x18\x03 \x01(\t*-\n\tDriveMode\x12\x0b\n\x07INVALID\x10\x00\x12\x08\n\x04USER\x10\x01\x12\t\n\x05PILOT\x10\x02*2\n\nTypeObject\x12\x07\n\x03\x41NY\x10\x00\x12\x07\n\x03\x43\x41R\x10\x01\x12\x08\n\x04\x42UMP\x10\x02\x12\x08\n\x04PLOT\x10\x03\x42\nZ\x08./eventsb\x06proto3')
|
||||||
|
|
||||||
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||||
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'events.events_pb2', globals())
|
||||||
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
|
||||||
|
DESCRIPTOR._options = None
|
||||||
|
DESCRIPTOR._serialized_options = b'Z\010./events'
|
||||||
|
_DRIVEMODE._serialized_start=1196
|
||||||
|
_DRIVEMODE._serialized_end=1241
|
||||||
|
_TYPEOBJECT._serialized_start=1243
|
||||||
|
_TYPEOBJECT._serialized_end=1293
|
||||||
|
_FRAMEREF._serialized_start=72
|
||||||
|
_FRAMEREF._serialized_end=156
|
||||||
|
_FRAMEMESSAGE._serialized_start=158
|
||||||
|
_FRAMEMESSAGE._serialized_end=225
|
||||||
|
_STEERINGMESSAGE._serialized_start=227
|
||||||
|
_STEERINGMESSAGE._serialized_end=327
|
||||||
|
_THROTTLEMESSAGE._serialized_start=329
|
||||||
|
_THROTTLEMESSAGE._serialized_end=429
|
||||||
|
_DRIVEMODEMESSAGE._serialized_start=431
|
||||||
|
_DRIVEMODEMESSAGE._serialized_end=496
|
||||||
|
_OBJECTSMESSAGE._serialized_start=498
|
||||||
|
_OBJECTSMESSAGE._serialized_end=600
|
||||||
|
_OBJECT._serialized_start=603
|
||||||
|
_OBJECT._serialized_end=731
|
||||||
|
_SWITCHRECORDMESSAGE._serialized_start=733
|
||||||
|
_SWITCHRECORDMESSAGE._serialized_end=771
|
||||||
|
_ROADMESSAGE._serialized_start=774
|
||||||
|
_ROADMESSAGE._serialized_end=914
|
||||||
|
_POINT._serialized_start=916
|
||||||
|
_POINT._serialized_end=945
|
||||||
|
_ELLIPSE._serialized_start=947
|
||||||
|
_ELLIPSE._serialized_end=1061
|
||||||
|
_RECORDMESSAGE._serialized_start=1064
|
||||||
|
_RECORDMESSAGE._serialized_end=1194
|
||||||
|
# @@protoc_insertion_point(module_scope)
|
631
poetry.lock
generated
631
poetry.lock
generated
@ -1,631 +0,0 @@
|
|||||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "astroid"
|
|
||||||
version = "2.15.8"
|
|
||||||
description = "An abstract syntax tree for Python with inference support."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7.2"
|
|
||||||
files = [
|
|
||||||
{file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"},
|
|
||||||
{file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
lazy-object-proxy = ">=1.4.0"
|
|
||||||
wrapt = {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "beautifulsoup4"
|
|
||||||
version = "4.12.3"
|
|
||||||
description = "Screen-scraping library"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6.0"
|
|
||||||
files = [
|
|
||||||
{file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"},
|
|
||||||
{file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
soupsieve = ">1.2"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
cchardet = ["cchardet"]
|
|
||||||
chardet = ["chardet"]
|
|
||||||
charset-normalizer = ["charset-normalizer"]
|
|
||||||
html5lib = ["html5lib"]
|
|
||||||
lxml = ["lxml"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "colorama"
|
|
||||||
version = "0.4.6"
|
|
||||||
description = "Cross-platform colored terminal text."
|
|
||||||
optional = false
|
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
|
||||||
files = [
|
|
||||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
|
||||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "depthai"
|
|
||||||
version = "2.25.0.0"
|
|
||||||
description = "DepthAI Python Library"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
files = [
|
|
||||||
{file = "depthai-2.25.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fcb825cc61ae9390dc490246ea057c4ecbd5bc6e2367bec7a356b0b97c2c06d4"},
|
|
||||||
{file = "depthai-2.25.0.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:cc12b461dc3a46a046455236e137b47506f6f7792114c66a48da3229a73e7187"},
|
|
||||||
{file = "depthai-2.25.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8be92a3c4e38f12df22f08798e3f9e77ccd1c6d00986718391c2aa4b6947ebac"},
|
|
||||||
{file = "depthai-2.25.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:883047d8e90da730e462260aba6791305523f6e011f7348c91684252f745ff45"},
|
|
||||||
{file = "depthai-2.25.0.0-cp310-cp310-win32.whl", hash = "sha256:e10524a9907c78fbb4461411202580c024585114de3b1f6b16c9234fc6cb521c"},
|
|
||||||
{file = "depthai-2.25.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:7f863893c8f2351ccaa5ce4835c49b37fd9976700b66fd75d2233f2d4a2049b2"},
|
|
||||||
{file = "depthai-2.25.0.0-cp311-cp311-linux_armv6l.linux_armv7l.whl", hash = "sha256:033d92de7743d6a9477a12799f3650dc48b66cd7167a6bdbe9313d9d2bd0f79e"},
|
|
||||||
{file = "depthai-2.25.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7299e600a84fa13ec6789451c11f8ce437458a09969e6e59fb1f71e0a7148e9f"},
|
|
||||||
{file = "depthai-2.25.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1d4a1b94cd7559a2becf19b3ccd963f372edde64d4a235cb8eb5fc53718529c3"},
|
|
||||||
{file = "depthai-2.25.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e29a8d8dcd35a50b73264b64e7c3e1f542dcf2aa20fed862016381d9dc749f"},
|
|
||||||
{file = "depthai-2.25.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5d67ffa8f90563180c45fccd3a1910813bc384644add1e12b1070c011ee21dc"},
|
|
||||||
{file = "depthai-2.25.0.0-cp311-cp311-win32.whl", hash = "sha256:2b592e0a55c8cf88673982c49308f8d970b91c7e27a8337a9fe9772337537243"},
|
|
||||||
{file = "depthai-2.25.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a8ea5f7be54ff82447d52a73034c2d29707903272448c4acb2bd32da398acb7"},
|
|
||||||
{file = "depthai-2.25.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9e065a5aa1da83a050ec33f2a5016d880127c0c3eaae8a95d2988d525ee3d058"},
|
|
||||||
{file = "depthai-2.25.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:15840f90c12bfc4c75110e352c8b82c5663f88afbe3b08f161f65c88066eba36"},
|
|
||||||
{file = "depthai-2.25.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:861fb8f12c29e8a288488759f25dbbff2373ddd23844b1c2ce2e63941b16f786"},
|
|
||||||
{file = "depthai-2.25.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f48dd45e0f2cb94f0ad94e3123102afff33996510dcb41dca9ffda0e73b7e"},
|
|
||||||
{file = "depthai-2.25.0.0-cp312-cp312-win32.whl", hash = "sha256:9fece6c03321b17cfdf5d804e6fa6dcc7618ba827433bfd192c51351e709875e"},
|
|
||||||
{file = "depthai-2.25.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7a77b63ec35750d20c38fa4254a5eb3c7b5fe7e01a0966da77c7c6203cef2e4"},
|
|
||||||
{file = "depthai-2.25.0.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a1ceb287a446350469970cc9ed90eb42bd7891f8d31e13bb6420e34a4f326dca"},
|
|
||||||
{file = "depthai-2.25.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cabec2946595474b25e8887c23d59b08abf77799424e363be1c691a64e4c83a"},
|
|
||||||
{file = "depthai-2.25.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8da730dd5653fb05a3f5b9b78e532a1abfd46495d1874df88ceb2bece810bda"},
|
|
||||||
{file = "depthai-2.25.0.0-cp36-cp36m-win32.whl", hash = "sha256:7e7a9ec66677b0c7e19e38a8d13c9aac51be52373d11d862027b346688e56e0c"},
|
|
||||||
{file = "depthai-2.25.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:aefde0d9dffe53b50a3bdce6a73d6fd867e0e39e69a76cd07f9ad6bfb23d2705"},
|
|
||||||
{file = "depthai-2.25.0.0-cp37-cp37m-linux_armv6l.linux_armv7l.whl", hash = "sha256:192f4830ad5f98b4befb1dccb2de3fdaeeea2604dbb16698a90012971ab91abe"},
|
|
||||||
{file = "depthai-2.25.0.0-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:b6d7ed50fac7ed480c69be394e3fb6e4c3985ab7cb42440697f2f89b9972e63b"},
|
|
||||||
{file = "depthai-2.25.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f6ce654d1bd36b538e7266fa919b133950034778506260e1fcbeb8e8ea02c9"},
|
|
||||||
{file = "depthai-2.25.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f1e67f59594b503189053efb134190ecf0780719fee25ebe79a2643f7cb388"},
|
|
||||||
{file = "depthai-2.25.0.0-cp37-cp37m-win32.whl", hash = "sha256:02c64db28871d7185d021b1015d283ced7f613e38f8126b0f3ec4e778cb67899"},
|
|
||||||
{file = "depthai-2.25.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3ccdc2bb5277c21682d56022a6a5af2af2bbcbe36e93901d62363b278298230e"},
|
|
||||||
{file = "depthai-2.25.0.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:b9f6655a5c7e57c052af5be95e86bb5ba91e704648b7633bf5bc6e67bd6096c9"},
|
|
||||||
{file = "depthai-2.25.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b6b0ca46f5c1b7adb339c7dde1ea22875b455393d8f378c751efc8cabe75810"},
|
|
||||||
{file = "depthai-2.25.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f43690411da216de2975680b8fdf4f0f0e0ce34a1575aea6a4260ea367d25ac0"},
|
|
||||||
{file = "depthai-2.25.0.0-cp38-cp38-win32.whl", hash = "sha256:c528268023f87269f32f5dcb89530a26d6d39cfea6ec262e88c545bc34619045"},
|
|
||||||
{file = "depthai-2.25.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:cf376c512e568e6d648d372514ae9cdbfba12add74e3ecdbf8877efea9a4ca0b"},
|
|
||||||
{file = "depthai-2.25.0.0-cp39-cp39-linux_armv6l.linux_armv7l.whl", hash = "sha256:a1a077619a35b663d8e40f2cb8e13689d51e8e05caa6a0f34431199e3bdf5dde"},
|
|
||||||
{file = "depthai-2.25.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:106c0766f78fe5edd902002361cc80dde8240d781dfe2058938b459d9e2198bf"},
|
|
||||||
{file = "depthai-2.25.0.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:5792c4c99fd8b0708ad0f5a7520cdbb7f524475c6f0851fc1dac520be64a4d87"},
|
|
||||||
{file = "depthai-2.25.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:237f6a61bb529d8ed58aba36e9f1be13a612278c3425ecf43e7a78d8464c047b"},
|
|
||||||
{file = "depthai-2.25.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30eb3ef763f12eaaa03e41aad8787cc69887dfbf2e96eb1a68d583ef16984a2"},
|
|
||||||
{file = "depthai-2.25.0.0-cp39-cp39-win32.whl", hash = "sha256:267ad298bd958637f11a86a688f65689c06752db863f0772ae1e273ada9d1a14"},
|
|
||||||
{file = "depthai-2.25.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:74fb00af51c1ba5d4bd88b7ea520a0dc87e505ae9c5c3786a97dd613a5a89e25"},
|
|
||||||
{file = "depthai-2.25.0.0.tar.gz", hash = "sha256:0d551432d5622683ce935cf9ce8e398645bde4d854c957c1c88f1f3b9e8dcb27"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dill"
|
|
||||||
version = "0.3.8"
|
|
||||||
description = "serialize all of Python"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"},
|
|
||||||
{file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
graph = ["objgraph (>=1.7.2)"]
|
|
||||||
profile = ["gprof2dot (>=2022.7.29)"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "google"
|
|
||||||
version = "3.0.0"
|
|
||||||
description = "Python bindings to the Google search engine."
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
files = [
|
|
||||||
{file = "google-3.0.0-py2.py3-none-any.whl", hash = "sha256:889cf695f84e4ae2c55fbc0cfdaf4c1e729417fa52ab1db0485202ba173e4935"},
|
|
||||||
{file = "google-3.0.0.tar.gz", hash = "sha256:143530122ee5130509ad5e989f0512f7cb218b2d4eddbafbad40fd10e8d8ccbe"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
beautifulsoup4 = "*"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iniconfig"
|
|
||||||
version = "2.0.0"
|
|
||||||
description = "brain-dead simple config-ini parsing"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
|
||||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "isort"
|
|
||||||
version = "5.13.2"
|
|
||||||
description = "A Python utility / library to sort Python imports."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8.0"
|
|
||||||
files = [
|
|
||||||
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
|
|
||||||
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
colors = ["colorama (>=0.4.6)"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy-object-proxy"
|
|
||||||
version = "1.10.0"
|
|
||||||
description = "A fast and thorough lazy object proxy."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"},
|
|
||||||
{file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mccabe"
|
|
||||||
version = "0.7.0"
|
|
||||||
description = "McCabe checker, plugin for flake8"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
files = [
|
|
||||||
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
|
||||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mypy"
|
|
||||||
version = "0.982"
|
|
||||||
description = "Optional static typing for Python"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"},
|
|
||||||
{file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"},
|
|
||||||
{file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"},
|
|
||||||
{file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"},
|
|
||||||
{file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"},
|
|
||||||
{file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"},
|
|
||||||
{file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"},
|
|
||||||
{file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"},
|
|
||||||
{file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"},
|
|
||||||
{file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"},
|
|
||||||
{file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"},
|
|
||||||
{file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"},
|
|
||||||
{file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"},
|
|
||||||
{file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"},
|
|
||||||
{file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"},
|
|
||||||
{file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"},
|
|
||||||
{file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"},
|
|
||||||
{file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"},
|
|
||||||
{file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"},
|
|
||||||
{file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"},
|
|
||||||
{file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"},
|
|
||||||
{file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"},
|
|
||||||
{file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"},
|
|
||||||
{file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
mypy-extensions = ">=0.4.3"
|
|
||||||
typing-extensions = ">=3.10"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
dmypy = ["psutil (>=4.0)"]
|
|
||||||
python2 = ["typed-ast (>=1.4.0,<2)"]
|
|
||||||
reports = ["lxml"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mypy-extensions"
|
|
||||||
version = "1.0.0"
|
|
||||||
description = "Type system extensions for programs checked with the mypy type checker."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.5"
|
|
||||||
files = [
|
|
||||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
|
||||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "numpy"
|
|
||||||
version = "1.26.4"
|
|
||||||
description = "Fundamental package for array computing in Python"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.9"
|
|
||||||
files = [
|
|
||||||
{file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
|
|
||||||
{file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
|
|
||||||
{file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"},
|
|
||||||
{file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"},
|
|
||||||
{file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"},
|
|
||||||
{file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"},
|
|
||||||
{file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"},
|
|
||||||
{file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"},
|
|
||||||
{file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"},
|
|
||||||
{file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"},
|
|
||||||
{file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"},
|
|
||||||
{file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"},
|
|
||||||
{file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"},
|
|
||||||
{file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"},
|
|
||||||
{file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"},
|
|
||||||
{file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"},
|
|
||||||
{file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
|
|
||||||
{file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
|
|
||||||
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"},
|
|
||||||
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"},
|
|
||||||
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"},
|
|
||||||
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"},
|
|
||||||
{file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"},
|
|
||||||
{file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"},
|
|
||||||
{file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"},
|
|
||||||
{file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"},
|
|
||||||
{file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"},
|
|
||||||
{file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"},
|
|
||||||
{file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"},
|
|
||||||
{file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"},
|
|
||||||
{file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"},
|
|
||||||
{file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"},
|
|
||||||
{file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"},
|
|
||||||
{file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"},
|
|
||||||
{file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"},
|
|
||||||
{file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "opencv-python-headless"
|
|
||||||
version = "4.9.0.80"
|
|
||||||
description = "Wrapper package for OpenCV python bindings."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
files = [
|
|
||||||
{file = "opencv-python-headless-4.9.0.80.tar.gz", hash = "sha256:71a4cd8cf7c37122901d8e81295db7fb188730e33a0e40039a4e59c1030b0958"},
|
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-macosx_10_16_x86_64.whl", hash = "sha256:2ea8a2edc4db87841991b2fbab55fc07b97ecb602e0f47d5d485bd75cee17c1a"},
|
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e0ee54e27be493e8f7850847edae3128e18b540dac1d7b2e4001b8944e11e1c6"},
|
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57ce2865e8fec431c6f97a81e9faaf23fa5be61011d0a75ccf47a3c0d65fa73d"},
|
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:976656362d68d9f40a5c66f83901430538002465f7db59142784f3893918f3df"},
|
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-win32.whl", hash = "sha256:11e3849d83e6651d4e7699aadda9ec7ed7c38957cbbcb99db074f2a2d2de9670"},
|
|
||||||
{file = "opencv_python_headless-4.9.0.80-cp37-abi3-win_amd64.whl", hash = "sha256:a8056c2cb37cd65dfcdf4153ca16f7362afcf3a50d600d6bb69c660fc61ee29c"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
numpy = {version = ">=1.26.0", markers = "python_version >= \"3.12\""}
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "packaging"
|
|
||||||
version = "24.0"
|
|
||||||
description = "Core utilities for Python packages"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
|
||||||
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "paho-mqtt"
|
|
||||||
version = "1.6.1"
|
|
||||||
description = "MQTT version 5.0/3.1.1 client class"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
files = [
|
|
||||||
{file = "paho-mqtt-1.6.1.tar.gz", hash = "sha256:2a8291c81623aec00372b5a85558a372c747cbca8e9934dfe218638b8eefc26f"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
proxy = ["PySocks"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "platformdirs"
|
|
||||||
version = "4.2.0"
|
|
||||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
|
|
||||||
{file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
|
||||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pluggy"
|
|
||||||
version = "1.4.0"
|
|
||||||
description = "plugin and hook calling mechanisms for python"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
|
|
||||||
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
dev = ["pre-commit", "tox"]
|
|
||||||
testing = ["pytest", "pytest-benchmark"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "protobuf"
|
|
||||||
version = "4.25.3"
|
|
||||||
description = ""
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"},
|
|
||||||
{file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"},
|
|
||||||
{file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"},
|
|
||||||
{file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"},
|
|
||||||
{file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"},
|
|
||||||
{file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"},
|
|
||||||
{file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"},
|
|
||||||
{file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"},
|
|
||||||
{file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"},
|
|
||||||
{file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"},
|
|
||||||
{file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "protobuf3"
|
|
||||||
version = "0.2.1"
|
|
||||||
description = "Protocol buffers library for Python 3"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
files = [
|
|
||||||
{file = "protobuf3-0.2.1.tar.gz", hash = "sha256:ddd878b3f991beff566ab384d3588cf8e89758e3a16a78f4099dbe70de3c41a2"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pylint"
|
|
||||||
version = "2.17.7"
|
|
||||||
description = "python code static checker"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7.2"
|
|
||||||
files = [
|
|
||||||
{file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"},
|
|
||||||
{file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
astroid = ">=2.15.8,<=2.17.0-dev0"
|
|
||||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
|
||||||
dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""}
|
|
||||||
isort = ">=4.2.5,<6"
|
|
||||||
mccabe = ">=0.6,<0.8"
|
|
||||||
platformdirs = ">=2.2.0"
|
|
||||||
tomlkit = ">=0.10.1"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
spelling = ["pyenchant (>=3.2,<4.0)"]
|
|
||||||
testutils = ["gitpython (>3)"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pytest"
|
|
||||||
version = "7.4.4"
|
|
||||||
description = "pytest: simple powerful testing with Python"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
|
|
||||||
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
|
||||||
iniconfig = "*"
|
|
||||||
packaging = "*"
|
|
||||||
pluggy = ">=0.12,<2.0"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pytest-mock"
|
|
||||||
version = "3.12.0"
|
|
||||||
description = "Thin-wrapper around the mock package for easier use with pytest"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"},
|
|
||||||
{file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
pytest = ">=5.0"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
dev = ["pre-commit", "pytest-asyncio", "tox"]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "robocar-protobuf"
|
|
||||||
version = "1.6.0"
|
|
||||||
description = "Protobuf message definitions for robocar"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.10,<4.0"
|
|
||||||
files = [
|
|
||||||
{file = "robocar_protobuf-1.6.0-py3-none-any.whl", hash = "sha256:d4c361a29a3aeefba6c8e449e26bd48ecf33f1dabd14b10cb73706cb293c9071"},
|
|
||||||
{file = "robocar_protobuf-1.6.0.tar.gz", hash = "sha256:5d32e89aacdad951414fe3286fbd20c2ab5013c08980f52b3abfb3e08629c106"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
protobuf = ">=4.21.8,<5.0.0"
|
|
||||||
|
|
||||||
[package.source]
|
|
||||||
type = "legacy"
|
|
||||||
url = "https://git.cyrilix.bzh/api/packages/robocars/pypi/simple"
|
|
||||||
reference = "robocar"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "soupsieve"
|
|
||||||
version = "2.5"
|
|
||||||
description = "A modern CSS selector implementation for Beautiful Soup."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
|
|
||||||
{file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tomlkit"
|
|
||||||
version = "0.12.4"
|
|
||||||
description = "Style preserving TOML library"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.7"
|
|
||||||
files = [
|
|
||||||
{file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"},
|
|
||||||
{file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "types-paho-mqtt"
|
|
||||||
version = "1.6.0.20240106"
|
|
||||||
description = "Typing stubs for paho-mqtt"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "types-paho-mqtt-1.6.0.20240106.tar.gz", hash = "sha256:6b486b9e4438c856cc0ac8312bf6a81c2bca1697bf36cde6d2ecddb44513550e"},
|
|
||||||
{file = "types_paho_mqtt-1.6.0.20240106-py3-none-any.whl", hash = "sha256:1d233c2c017a512ebbec24d6a90d94302767c75a33a7c2584a660eac7fade248"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "types-protobuf"
|
|
||||||
version = "3.20.4.6"
|
|
||||||
description = "Typing stubs for protobuf"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
files = [
|
|
||||||
{file = "types-protobuf-3.20.4.6.tar.gz", hash = "sha256:ba27443c592bbec1629dd69494a24c84461c63f0d3b7d648ce258aaae9680965"},
|
|
||||||
{file = "types_protobuf-3.20.4.6-py3-none-any.whl", hash = "sha256:ab2d315ba82246b83d28f8797c98dc0fe1dd5cfd187909e56faf87239aedaae3"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typing-extensions"
|
|
||||||
version = "4.10.0"
|
|
||||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.8"
|
|
||||||
files = [
|
|
||||||
{file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
|
|
||||||
{file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wrapt"
|
|
||||||
version = "1.16.0"
|
|
||||||
description = "Module for decorators, wrappers and monkey patching."
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6"
|
|
||||||
files = [
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
|
|
||||||
{file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
|
|
||||||
{file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
|
|
||||||
{file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
|
|
||||||
{file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
|
|
||||||
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
|
|
||||||
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
|
|
||||||
{file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
|
|
||||||
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
|
|
||||||
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
|
|
||||||
{file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
|
|
||||||
{file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
|
|
||||||
{file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
|
|
||||||
{file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
|
|
||||||
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
|
|
||||||
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
|
|
||||||
{file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
|
|
||||||
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
|
|
||||||
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
|
|
||||||
{file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
|
|
||||||
{file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
|
|
||||||
{file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
|
|
||||||
{file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
|
|
||||||
{file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
|
|
||||||
{file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
|
|
||||||
{file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[metadata]
|
|
||||||
lock-version = "2.0"
|
|
||||||
python-versions = "^3.12"
|
|
||||||
content-hash = "c40d76f962d6502e0946e1cbacedab80dd035d070de7e71e3ae68dc346b69b22"
|
|
@ -1,56 +0,0 @@
|
|||||||
[tool.poetry]
|
|
||||||
name = "robocar-oak-camera"
|
|
||||||
version = "0.0.0"
|
|
||||||
description = "Mqtt gateway for oak-lite device"
|
|
||||||
authors = ["Cyrille Nofficial <cynoffic@cyrilix.fr>"]
|
|
||||||
readme = "README.md"
|
|
||||||
packages = [
|
|
||||||
{ include = "camera" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
|
||||||
python = "^3.12"
|
|
||||||
paho-mqtt = "^1.6"
|
|
||||||
depthai = "^2"
|
|
||||||
protobuf3 = "^0.2.1"
|
|
||||||
google = "^3.0.0"
|
|
||||||
protobuf = "^4.21"
|
|
||||||
opencv-python-headless = "^4.6.0"
|
|
||||||
robocar-protobuf = {version = "^1.6", source = "robocar"}
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.test.dependencies]
|
|
||||||
pytest = "^7.1.3"
|
|
||||||
pytest-mock = "^3.10.0"
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
|
||||||
pylint = "^2.15.4"
|
|
||||||
mypy = "^0.982"
|
|
||||||
types-paho-mqtt = "^1.6.0.1"
|
|
||||||
types-protobuf = "^3.20.4.2"
|
|
||||||
|
|
||||||
|
|
||||||
[[tool.poetry.source]]
|
|
||||||
name = "robocar"
|
|
||||||
url = "https://git.cyrilix.bzh/api/packages/robocars/pypi/simple"
|
|
||||||
priority = "explicit"
|
|
||||||
|
|
||||||
[build-system]
|
|
||||||
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]
|
|
||||||
build-backend = "poetry_dynamic_versioning.backend"
|
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
|
||||||
rc-oak-camera = 'camera.cli:execute_from_command_line'
|
|
||||||
|
|
||||||
[tool.poetry-dynamic-versioning]
|
|
||||||
enable = true
|
|
||||||
style = 'semver'
|
|
||||||
vcs = 'git'
|
|
||||||
dirty = true
|
|
||||||
bump = true
|
|
||||||
|
|
||||||
[tool.mypy]
|
|
||||||
strict = true
|
|
||||||
warn_unused_configs = true
|
|
||||||
plugins = 'numpy.typing.mypy_plugin'
|
|
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
paho-mqtt~=1.6.1
|
||||||
|
docopt~=0.6.2
|
||||||
|
depthai==2.14.1.0
|
||||||
|
opencv-python~=4.5.5.62
|
||||||
|
google~=3.0.0
|
||||||
|
google-api-core~=2.4.0
|
||||||
|
setuptools==60.5.0
|
||||||
|
protobuf3
|
5
setup.cfg
Normal file
5
setup.cfg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[metadata]
|
||||||
|
description-file = README.md
|
||||||
|
|
||||||
|
[aliases]
|
||||||
|
test = pytest
|
68
setup.py
Normal file
68
setup.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
|
# include the non python files
|
||||||
|
def package_files(directory, strip_leading):
|
||||||
|
paths = []
|
||||||
|
for (path, directories, filenames) in os.walk(directory):
|
||||||
|
for filename in filenames:
|
||||||
|
package_file = os.path.join(path, filename)
|
||||||
|
paths.append(package_file[len(strip_leading):])
|
||||||
|
return paths
|
||||||
|
|
||||||
|
|
||||||
|
tests_require = ['pytest',
|
||||||
|
]
|
||||||
|
|
||||||
|
setup(name='robocar-oak-camera',
|
||||||
|
version='0.1',
|
||||||
|
description='Mqtt gateway for oak-lite device.',
|
||||||
|
url='https://github.com/cyrilix/robocar-oak-camera',
|
||||||
|
license='Apache2',
|
||||||
|
entry_points={
|
||||||
|
'console_scripts': [
|
||||||
|
'rc-oak-camera=camera.cli:execute_from_command_line',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
setup_requires=['pytest-runner'],
|
||||||
|
install_requires=['depthai',
|
||||||
|
'docopt',
|
||||||
|
'paho-mqtt',
|
||||||
|
'protobuf3',
|
||||||
|
'google',
|
||||||
|
'numpy',
|
||||||
|
'opencv-python',
|
||||||
|
'blobconverter',
|
||||||
|
],
|
||||||
|
tests_require=tests_require,
|
||||||
|
extras_require={
|
||||||
|
'tests': tests_require
|
||||||
|
},
|
||||||
|
|
||||||
|
include_package_data=True,
|
||||||
|
|
||||||
|
classifiers=[
|
||||||
|
# How mature is this project? Common values are
|
||||||
|
# 3 - Alpha
|
||||||
|
# 4 - Beta
|
||||||
|
# 5 - Production/Stable
|
||||||
|
'Development Status :: 3 - Alpha',
|
||||||
|
|
||||||
|
# Indicate who your project is intended for
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'Topic :: Scientific/Engineering :: Artificial Intelligence',
|
||||||
|
|
||||||
|
# Pick your license as you wish (should match "license" above)
|
||||||
|
'License :: OSI Approved :: Apache 2 License',
|
||||||
|
|
||||||
|
# Specify the Python versions you support here. In particular, ensure
|
||||||
|
# that you indicate whether you support Python 2, Python 3 or both.
|
||||||
|
|
||||||
|
'Programming Language :: Python :: 3.7',
|
||||||
|
],
|
||||||
|
keywords='selfdriving cars drive',
|
||||||
|
|
||||||
|
packages=find_packages(exclude=(['tests', 'env'])),
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user