Compare commits
	
		
			2 Commits
		
	
	
		
			v0.6.0
			...
			feat/depth
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 32eb2f618f | |||
| 2c9c7d9078 | 
@@ -1,6 +1,2 @@
 | 
			
		||||
venv
 | 
			
		||||
dist/*
 | 
			
		||||
build-docker.sh
 | 
			
		||||
Dockerfile
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -3,6 +3,3 @@
 | 
			
		||||
*.egg-info
 | 
			
		||||
.idea
 | 
			
		||||
*/__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
 | 
			
		||||
RUN echo -n "[global]\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 echo -n "[global]\nextra-index-url=https://www.piwheels.org/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
 | 
			
		||||
 | 
			
		||||
#################
 | 
			
		||||
FROM base as model-builder
 | 
			
		||||
RUN pip3 install numpy
 | 
			
		||||
 | 
			
		||||
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 ""
 | 
			
		||||
 | 
			
		||||
#################
 | 
			
		||||
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 events events
 | 
			
		||||
ADD camera camera
 | 
			
		||||
ADD README.md .
 | 
			
		||||
ADD setup.cfg setup.cfg
 | 
			
		||||
ADD setup.py setup.py
 | 
			
		||||
 | 
			
		||||
# Poetry expect to found a git project
 | 
			
		||||
ADD .git .git
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
ENV PYTHON_EGG_CACHE=/tmp/cache
 | 
			
		||||
RUN python3 setup.py install
 | 
			
		||||
 | 
			
		||||
WORKDIR /tmp
 | 
			
		||||
USER 1234
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										133
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								README.md
									
									
									
									
									
								
							@@ -9,136 +9,3 @@ To build images, run script:
 | 
			
		||||
```bash 
 | 
			
		||||
./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
 | 
			
		||||
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,linux/arm/v7"
 | 
			
		||||
 | 
			
		||||
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}"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										379
									
								
								camera/cli.py
									
									
									
									
									
								
							
							
						
						
									
										379
									
								
								camera/cli.py
									
									
									
									
									
								
							@@ -1,365 +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 os
 | 
			
		||||
import signal
 | 
			
		||||
import types
 | 
			
		||||
import typing
 | 
			
		||||
from typing import List
 | 
			
		||||
 | 
			
		||||
import depthai as dai
 | 
			
		||||
from . import depthai as cam
 | 
			
		||||
from docopt import docopt
 | 
			
		||||
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__)
 | 
			
		||||
logging.basicConfig(level=logging.INFO)
 | 
			
		||||
 | 
			
		||||
_DEFAULT_CLIENT_ID = "robocar-depthai"
 | 
			
		||||
default_client_id = "robocar-depthai"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _parse_args_cli() -> argparse.Namespace:
 | 
			
		||||
    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("--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:
 | 
			
		||||
def init_mqtt_client(broker_host: str, user: str, password: str, client_id: str) -> mqtt.Client:
 | 
			
		||||
    logger.info("Start part.py-robocar-oak-camera")
 | 
			
		||||
    client = mqtt.Client(client_id=client_id, clean_session=True, userdata=None, protocol=mqtt.MQTTv311)
 | 
			
		||||
 | 
			
		||||
    client.username_pw_set(user, password)
 | 
			
		||||
    logger.info("Connect to mqtt broker %s", broker_host)
 | 
			
		||||
    client.connect(host=broker_host, port=broker_port, keepalive=60)
 | 
			
		||||
    logger.info("Connect to mqtt broker "+ broker_host)
 | 
			
		||||
    client.connect(host=broker_host, port=1883, keepalive=60)
 | 
			
		||||
    logger.info("Connected to mqtt broker")
 | 
			
		||||
    return client
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def execute_from_command_line() -> None:
 | 
			
		||||
    """
 | 
			
		||||
    Cli entrypoint
 | 
			
		||||
    :return:
 | 
			
		||||
    """
 | 
			
		||||
def execute_from_command_line():
 | 
			
		||||
    logging.basicConfig(level=logging.INFO)
 | 
			
		||||
 | 
			
		||||
    args = _parse_args_cli()
 | 
			
		||||
    if args.log == "info":
 | 
			
		||||
        logging.basicConfig(level=logging.INFO)
 | 
			
		||||
    elif args.log == "debug":
 | 
			
		||||
        logging.basicConfig(level=logging.DEBUG)
 | 
			
		||||
    args = docopt(__doc__)
 | 
			
		||||
 | 
			
		||||
    client = _init_mqtt_client(broker_host=args.mqtt_broker_host,
 | 
			
		||||
                               broker_port=args.mqtt_broker_port,
 | 
			
		||||
                               user=args.mqtt_username,
 | 
			
		||||
                               password=args.mqtt_password,
 | 
			
		||||
                               client_id=args.mqtt_client_id,
 | 
			
		||||
                               )
 | 
			
		||||
    frame_processor = cam.FrameProcessor(mqtt_client=client, frame_topic=args.mqtt_topic_robocar_oak_camera)
 | 
			
		||||
    object_processor = cam.ObjectProcessor(mqtt_client=client,
 | 
			
		||||
                                           objects_topic=args.mqtt_topic_robocar_objects,
 | 
			
		||||
                                           objects_threshold=args.objects_threshold)
 | 
			
		||||
    disparity_processor = cam.DisparityProcessor(mqtt_client=client, disparity_topic=args.mqtt_topic_robocar_disparity)
 | 
			
		||||
    client = init_mqtt_client(broker_host=get_default_value(args["--mqtt-broker"], "MQTT_BROKER", "localhost"),
 | 
			
		||||
                              user=get_default_value(args["--mqtt-username"], "MQTT_USERNAME", ""),
 | 
			
		||||
                              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_topic = get_default_value(args["--mqtt-topic-robocar-oak-camera"], "MQTT_TOPIC_CAMERA", "/oak/camera_rgb")
 | 
			
		||||
 | 
			
		||||
    pipeline = dai.Pipeline()
 | 
			
		||||
    if args.camera_tuning_exposition == CAMERA_EXPOSITION_500US:
 | 
			
		||||
        pipeline.setCameraTuningBlobPath('/camera_tuning/tuning_exp_limit_500us.bin')
 | 
			
		||||
    elif args.camera_tuning_exposition == CAMERA_EXPOSITION_8300US:
 | 
			
		||||
        pipeline.setCameraTuningBlobPath('/camera_tuning/tuning_exp_limit_8300us.bin')
 | 
			
		||||
 | 
			
		||||
    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=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=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()
 | 
			
		||||
    frame_processor = cam.FramePublisher(mqtt_client=client,
 | 
			
		||||
                                         frame_topic=frame_topic,
 | 
			
		||||
                                         img_width=int(get_default_value(args["--image-width"], "IMAGE_WIDTH", 160)),
 | 
			
		||||
                                         img_height=int(get_default_value(args["--image-height"], "IMAGE_HEIGHT", 120)))
 | 
			
		||||
    frame_processor.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:
 | 
			
		||||
        return os.environ[env_var]
 | 
			
		||||
    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,662 +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)
 | 
			
		||||
            q_disparity = dev.getOutputQueue(name=self._depth_source.get_stream_name(), maxSize=queue_size,  # type: ignore
 | 
			
		||||
                                             blocking=False)
 | 
			
		||||
 | 
			
		||||
            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")
 | 
			
		||||
        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'])),
 | 
			
		||||
      )
 | 
			
		||||
		Reference in New Issue
	
	Block a user