Compare commits
	
		
			2 Commits
		
	
	
		
			build/poet
			...
			feat/text_
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0467ab780c | |||
| 2c9c7d9078 | 
@@ -1,6 +1,2 @@
 | 
				
			|||||||
venv
 | 
					venv
 | 
				
			||||||
dist/
 | 
					 | 
				
			||||||
build-docker.sh
 | 
					 | 
				
			||||||
Dockerfile
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -3,6 +3,3 @@
 | 
				
			|||||||
*.egg-info
 | 
					*.egg-info
 | 
				
			||||||
.idea
 | 
					.idea
 | 
				
			||||||
*/__pycache__/
 | 
					*/__pycache__/
 | 
				
			||||||
/dist/
 | 
					 | 
				
			||||||
build
 | 
					 | 
				
			||||||
__pycache__
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										618
									
								
								.pylintrc
									
									
									
									
									
								
							
							
						
						
									
										618
									
								
								.pylintrc
									
									
									
									
									
								
							@@ -1,618 +0,0 @@
 | 
				
			|||||||
[MAIN]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
 | 
					 | 
				
			||||||
# 3 compatible code, which means that the block might have code that exists
 | 
					 | 
				
			||||||
# only in one or another interpreter, leading to false positives when analysed.
 | 
					 | 
				
			||||||
analyse-fallback-blocks=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Load and enable all available extensions. Use --list-extensions to see a list
 | 
					 | 
				
			||||||
# all available extensions.
 | 
					 | 
				
			||||||
#enable-all-extensions=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# In error mode, messages with a category besides ERROR or FATAL are
 | 
					 | 
				
			||||||
# suppressed, and no reports are done by default. Error mode is compatible with
 | 
					 | 
				
			||||||
# disabling specific errors.
 | 
					 | 
				
			||||||
#errors-only=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Always return a 0 (non-error) status code, even if lint errors are found.
 | 
					 | 
				
			||||||
# This is primarily useful in continuous integration scripts.
 | 
					 | 
				
			||||||
#exit-zero=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# A comma-separated list of package or module names from where C extensions may
 | 
					 | 
				
			||||||
# be loaded. Extensions are loading into the active Python interpreter and may
 | 
					 | 
				
			||||||
# run arbitrary code.
 | 
					 | 
				
			||||||
extension-pkg-allow-list=depthai,node,cv2,events.*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# A comma-separated list of package or module names from where C extensions may
 | 
					 | 
				
			||||||
# be loaded. Extensions are loading into the active Python interpreter and may
 | 
					 | 
				
			||||||
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
 | 
					 | 
				
			||||||
# for backward compatibility.)
 | 
					 | 
				
			||||||
extension-pkg-whitelist=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Return non-zero exit code if any of these messages/categories are detected,
 | 
					 | 
				
			||||||
# even if score is above --fail-under value. Syntax same as enable. Messages
 | 
					 | 
				
			||||||
# specified are enabled, while categories only check already-enabled messages.
 | 
					 | 
				
			||||||
fail-on=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Specify a score threshold under which the program will exit with error.
 | 
					 | 
				
			||||||
fail-under=10
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Interpret the stdin as a python script, whose filename needs to be passed as
 | 
					 | 
				
			||||||
# the module_or_package argument.
 | 
					 | 
				
			||||||
#from-stdin=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Files or directories to be skipped. They should be base names, not paths.
 | 
					 | 
				
			||||||
ignore=CVS
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Add files or directories matching the regular expressions patterns to the
 | 
					 | 
				
			||||||
# ignore-list. The regex matches against paths and can be in Posix or Windows
 | 
					 | 
				
			||||||
# format. Because '\' represents the directory delimiter on Windows systems, it
 | 
					 | 
				
			||||||
# can't be used as an escape character.
 | 
					 | 
				
			||||||
ignore-paths=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Files or directories matching the regular expression patterns are skipped.
 | 
					 | 
				
			||||||
# The regex matches against base names, not paths. The default value ignores
 | 
					 | 
				
			||||||
# Emacs file locks
 | 
					 | 
				
			||||||
ignore-patterns=^\.#,test_.*?py
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of module names for which member attributes should not be checked
 | 
					 | 
				
			||||||
# (useful for modules/projects where namespaces are manipulated during runtime
 | 
					 | 
				
			||||||
# and thus existing member attributes cannot be deduced by static analysis). It
 | 
					 | 
				
			||||||
# supports qualified module names, as well as Unix pattern matching.
 | 
					 | 
				
			||||||
ignored-modules=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Python code to execute, usually for sys.path manipulation such as
 | 
					 | 
				
			||||||
# pygtk.require().
 | 
					 | 
				
			||||||
#init-hook=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
 | 
					 | 
				
			||||||
# number of processors available to use, and will cap the count on Windows to
 | 
					 | 
				
			||||||
# avoid hangs.
 | 
					 | 
				
			||||||
jobs=1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Control the amount of potential inferred values when inferring a single
 | 
					 | 
				
			||||||
# object. This can help the performance when dealing with large functions or
 | 
					 | 
				
			||||||
# complex, nested conditions.
 | 
					 | 
				
			||||||
limit-inference-results=100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of plugins (as comma separated values of python module names) to load,
 | 
					 | 
				
			||||||
# usually to register additional checkers.
 | 
					 | 
				
			||||||
load-plugins=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Pickle collected data for later comparisons.
 | 
					 | 
				
			||||||
persistent=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Minimum Python version to use for version dependent checks. Will default to
 | 
					 | 
				
			||||||
# the version used to run pylint.
 | 
					 | 
				
			||||||
py-version=3.10
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Discover python modules and packages in the file system subtree.
 | 
					 | 
				
			||||||
recursive=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
 | 
					 | 
				
			||||||
# user-friendly hints instead of false-positive error messages.
 | 
					 | 
				
			||||||
suggestion-mode=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
 | 
					 | 
				
			||||||
# active Python interpreter and may run arbitrary code.
 | 
					 | 
				
			||||||
unsafe-load-any-extension=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# In verbose mode, extra non-checker-related info will be displayed.
 | 
					 | 
				
			||||||
#verbose=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[REPORTS]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Python expression which should return a score less than or equal to 10. You
 | 
					 | 
				
			||||||
# have access to the variables 'fatal', 'error', 'warning', 'refactor',
 | 
					 | 
				
			||||||
# 'convention', and 'info' which contain the number of messages in each
 | 
					 | 
				
			||||||
# category, as well as 'statement' which is the total number of statements
 | 
					 | 
				
			||||||
# analyzed. This score is used by the global evaluation report (RP0004).
 | 
					 | 
				
			||||||
evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Template used to display messages. This is a python new-style format string
 | 
					 | 
				
			||||||
# used to format the message information. See doc for all details.
 | 
					 | 
				
			||||||
msg-template=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Set the output format. Available formats are text, parseable, colorized, json
 | 
					 | 
				
			||||||
# and msvs (visual studio). You can also give a reporter class, e.g.
 | 
					 | 
				
			||||||
# mypackage.mymodule.MyReporterClass.
 | 
					 | 
				
			||||||
#output-format=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Tells whether to display a full report or only the messages.
 | 
					 | 
				
			||||||
reports=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Activate the evaluation score.
 | 
					 | 
				
			||||||
score=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[MESSAGES CONTROL]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Only show warnings with the listed confidence levels. Leave empty to show
 | 
					 | 
				
			||||||
# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
 | 
					 | 
				
			||||||
# UNDEFINED.
 | 
					 | 
				
			||||||
confidence=HIGH,
 | 
					 | 
				
			||||||
           CONTROL_FLOW,
 | 
					 | 
				
			||||||
           INFERENCE,
 | 
					 | 
				
			||||||
           INFERENCE_FAILURE,
 | 
					 | 
				
			||||||
           UNDEFINED
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Disable the message, report, category or checker with the given id(s). You
 | 
					 | 
				
			||||||
# can either give multiple identifiers separated by comma (,) or put this
 | 
					 | 
				
			||||||
# option multiple times (only on the command line, not in the configuration
 | 
					 | 
				
			||||||
# file where it should appear only once). You can also use "--disable=all" to
 | 
					 | 
				
			||||||
# disable everything first and then re-enable specific checks. For example, if
 | 
					 | 
				
			||||||
# you want to run only the similarities checker, you can use "--disable=all
 | 
					 | 
				
			||||||
# --enable=similarities". If you want to run only the classes checker, but have
 | 
					 | 
				
			||||||
# no Warning level messages displayed, use "--disable=all --enable=classes
 | 
					 | 
				
			||||||
# --disable=W".
 | 
					 | 
				
			||||||
disable=raw-checker-failed,
 | 
					 | 
				
			||||||
        bad-inline-option,
 | 
					 | 
				
			||||||
        locally-disabled,
 | 
					 | 
				
			||||||
        file-ignored,
 | 
					 | 
				
			||||||
        suppressed-message,
 | 
					 | 
				
			||||||
        useless-suppression,
 | 
					 | 
				
			||||||
        deprecated-pragma,
 | 
					 | 
				
			||||||
        use-symbolic-message-instead
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Enable the message, report, category or checker with the given id(s). You can
 | 
					 | 
				
			||||||
# either give multiple identifier separated by comma (,) or put this option
 | 
					 | 
				
			||||||
# multiple time (only on the command line, not in the configuration file where
 | 
					 | 
				
			||||||
# it should appear only once). See also the "--disable" option for examples.
 | 
					 | 
				
			||||||
enable=c-extension-no-member
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[LOGGING]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# The type of string formatting that logging methods do. `old` means using %
 | 
					 | 
				
			||||||
# formatting, `new` is for `{}` formatting.
 | 
					 | 
				
			||||||
logging-format-style=old
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Logging modules to check that the string format arguments are in logging
 | 
					 | 
				
			||||||
# function parameter format.
 | 
					 | 
				
			||||||
logging-modules=logging
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[SPELLING]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Limits count of emitted suggestions for spelling mistakes.
 | 
					 | 
				
			||||||
max-spelling-suggestions=4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Spelling dictionary name. Available dictionaries: none. To make it work,
 | 
					 | 
				
			||||||
# install the 'python-enchant' package.
 | 
					 | 
				
			||||||
spelling-dict=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of comma separated words that should be considered directives if they
 | 
					 | 
				
			||||||
# appear at the beginning of a comment and should not be checked.
 | 
					 | 
				
			||||||
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of comma separated words that should not be checked.
 | 
					 | 
				
			||||||
spelling-ignore-words=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# A path to a file that contains the private dictionary; one word per line.
 | 
					 | 
				
			||||||
spelling-private-dict-file=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Tells whether to store unknown words to the private dictionary (see the
 | 
					 | 
				
			||||||
# --spelling-private-dict-file option) instead of raising a message.
 | 
					 | 
				
			||||||
spelling-store-unknown-words=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[MISCELLANEOUS]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of note tags to take in consideration, separated by a comma.
 | 
					 | 
				
			||||||
notes=FIXME,
 | 
					 | 
				
			||||||
      XXX,
 | 
					 | 
				
			||||||
      TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression of note tags to take in consideration.
 | 
					 | 
				
			||||||
notes-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[TYPECHECK]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of decorators that produce context managers, such as
 | 
					 | 
				
			||||||
# contextlib.contextmanager. Add to this list to register other decorators that
 | 
					 | 
				
			||||||
# produce valid context managers.
 | 
					 | 
				
			||||||
contextmanager-decorators=contextlib.contextmanager
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of members which are set dynamically and missed by pylint inference
 | 
					 | 
				
			||||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
 | 
					 | 
				
			||||||
# expressions are accepted.
 | 
					 | 
				
			||||||
generated-members=cv2,events.events_pb2,depthai.*,dai.*
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Tells whether to warn about missing members when the owner of the attribute
 | 
					 | 
				
			||||||
# is inferred to be None.
 | 
					 | 
				
			||||||
ignore-none=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This flag controls whether pylint should warn about no-member and similar
 | 
					 | 
				
			||||||
# checks whenever an opaque object is returned when inferring. The inference
 | 
					 | 
				
			||||||
# can return multiple potential results while evaluating a Python object, but
 | 
					 | 
				
			||||||
# some branches might not be evaluated, which results in partial inference. In
 | 
					 | 
				
			||||||
# that case, it might be useful to still emit no-member and other checks for
 | 
					 | 
				
			||||||
# the rest of the inferred objects.
 | 
					 | 
				
			||||||
ignore-on-opaque-inference=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of symbolic message names to ignore for Mixin members.
 | 
					 | 
				
			||||||
ignored-checks-for-mixins=no-member,
 | 
					 | 
				
			||||||
                          not-async-context-manager,
 | 
					 | 
				
			||||||
                          not-context-manager,
 | 
					 | 
				
			||||||
                          attribute-defined-outside-init
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of class names for which member attributes should not be checked (useful
 | 
					 | 
				
			||||||
# for classes with dynamically set attributes). This supports the use of
 | 
					 | 
				
			||||||
# qualified names.
 | 
					 | 
				
			||||||
ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Show a hint with possible names when a member name was not found. The aspect
 | 
					 | 
				
			||||||
# of finding the hint is based on edit distance.
 | 
					 | 
				
			||||||
missing-member-hint=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# The minimum edit distance a name should have in order to be considered a
 | 
					 | 
				
			||||||
# similar match for a missing member name.
 | 
					 | 
				
			||||||
missing-member-hint-distance=1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# The total number of similar names that should be taken in consideration when
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
missing-member-max-choices=1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regex pattern to define which classes are considered mixins.
 | 
					 | 
				
			||||||
mixin-class-rgx=.*[Mm]ixin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of decorators that change the signature of a decorated function.
 | 
					 | 
				
			||||||
signature-mutators=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[CLASSES]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Warn about protected attribute access inside special methods
 | 
					 | 
				
			||||||
check-protected-access-in-special-methods=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of method names used to declare (i.e. assign) instance attributes.
 | 
					 | 
				
			||||||
defining-attr-methods=__init__,
 | 
					 | 
				
			||||||
                      __new__,
 | 
					 | 
				
			||||||
                      setUp,
 | 
					 | 
				
			||||||
                      __post_init__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of member names, which should be excluded from the protected access
 | 
					 | 
				
			||||||
# warning.
 | 
					 | 
				
			||||||
exclude-protected=_asdict,
 | 
					 | 
				
			||||||
                  _fields,
 | 
					 | 
				
			||||||
                  _replace,
 | 
					 | 
				
			||||||
                  _source,
 | 
					 | 
				
			||||||
                  _make
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of valid names for the first argument in a class method.
 | 
					 | 
				
			||||||
valid-classmethod-first-arg=cls
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of valid names for the first argument in a metaclass class method.
 | 
					 | 
				
			||||||
valid-metaclass-classmethod-first-arg=cls
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[VARIABLES]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of additional names supposed to be defined in builtins. Remember that
 | 
					 | 
				
			||||||
# you should avoid defining new builtins when possible.
 | 
					 | 
				
			||||||
additional-builtins=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Tells whether unused global variables should be treated as a violation.
 | 
					 | 
				
			||||||
allow-global-unused-variables=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of names allowed to shadow builtins
 | 
					 | 
				
			||||||
allowed-redefined-builtins=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of strings which can identify a callback function by name. A callback
 | 
					 | 
				
			||||||
# name must start or end with one of those strings.
 | 
					 | 
				
			||||||
callbacks=cb_,
 | 
					 | 
				
			||||||
          _cb
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# A regular expression matching the name of dummy variables (i.e. expected to
 | 
					 | 
				
			||||||
# not be used).
 | 
					 | 
				
			||||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Argument names that match this expression will be ignored.
 | 
					 | 
				
			||||||
ignored-argument-names=_.*|^ignored_|^unused_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Tells whether we should check for unused import in __init__ files.
 | 
					 | 
				
			||||||
init-import=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of qualified module names which can have objects that can redefine
 | 
					 | 
				
			||||||
# builtins.
 | 
					 | 
				
			||||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[FORMAT]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
 | 
					 | 
				
			||||||
expected-line-ending-format=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regexp for a line that is allowed to be longer than the limit.
 | 
					 | 
				
			||||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Number of spaces of indent required inside a hanging or continued line.
 | 
					 | 
				
			||||||
indent-after-paren=4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1
 | 
					 | 
				
			||||||
# tab).
 | 
					 | 
				
			||||||
indent-string='    '
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of characters on a single line.
 | 
					 | 
				
			||||||
max-line-length=120
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of lines in a module.
 | 
					 | 
				
			||||||
max-module-lines=1000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Allow the body of a class to be on the same line as the declaration if body
 | 
					 | 
				
			||||||
# contains single statement.
 | 
					 | 
				
			||||||
single-line-class-stmt=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Allow the body of an if to be on the same line as the test if there is no
 | 
					 | 
				
			||||||
# else.
 | 
					 | 
				
			||||||
single-line-if-stmt=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[IMPORTS]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of modules that can be imported at any level, not just the top level
 | 
					 | 
				
			||||||
# one.
 | 
					 | 
				
			||||||
allow-any-import-level=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Allow wildcard imports from modules that define __all__.
 | 
					 | 
				
			||||||
allow-wildcard-with-all=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Deprecated modules which should not be used, separated by a comma.
 | 
					 | 
				
			||||||
deprecated-modules=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Output a graph (.gv or any supported image format) of external dependencies
 | 
					 | 
				
			||||||
# to the given file (report RP0402 must not be disabled).
 | 
					 | 
				
			||||||
ext-import-graph=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Output a graph (.gv or any supported image format) of all (i.e. internal and
 | 
					 | 
				
			||||||
# external) dependencies to the given file (report RP0402 must not be
 | 
					 | 
				
			||||||
# disabled).
 | 
					 | 
				
			||||||
import-graph=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Output a graph (.gv or any supported image format) of internal dependencies
 | 
					 | 
				
			||||||
# to the given file (report RP0402 must not be disabled).
 | 
					 | 
				
			||||||
int-import-graph=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Force import order to recognize a module as part of the standard
 | 
					 | 
				
			||||||
# compatibility libraries.
 | 
					 | 
				
			||||||
known-standard-library=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Force import order to recognize a module as part of a third party library.
 | 
					 | 
				
			||||||
known-third-party=enchant
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Couples of modules and preferred modules, separated by a comma.
 | 
					 | 
				
			||||||
preferred-modules=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[METHOD_ARGS]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of qualified names (i.e., library.method) which require a timeout
 | 
					 | 
				
			||||||
# parameter e.g. 'requests.api.get,requests.api.post'
 | 
					 | 
				
			||||||
timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[EXCEPTIONS]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Exceptions that will emit a warning when caught.
 | 
					 | 
				
			||||||
overgeneral-exceptions=BaseException,
 | 
					 | 
				
			||||||
                       Exception
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[REFACTORING]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of nested blocks for function / method body
 | 
					 | 
				
			||||||
max-nested-blocks=5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Complete name of functions that never returns. When checking for
 | 
					 | 
				
			||||||
# inconsistent-return-statements if a never returning function is called then
 | 
					 | 
				
			||||||
# it will be considered as an explicit return statement and no message will be
 | 
					 | 
				
			||||||
# printed.
 | 
					 | 
				
			||||||
never-returning-functions=sys.exit,argparse.parse_error
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[SIMILARITIES]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Comments are removed from the similarity computation
 | 
					 | 
				
			||||||
ignore-comments=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Docstrings are removed from the similarity computation
 | 
					 | 
				
			||||||
ignore-docstrings=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Imports are removed from the similarity computation
 | 
					 | 
				
			||||||
ignore-imports=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Signatures are removed from the similarity computation
 | 
					 | 
				
			||||||
ignore-signatures=yes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Minimum lines number of a similarity.
 | 
					 | 
				
			||||||
min-similarity-lines=4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[DESIGN]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of regular expressions of class ancestor names to ignore when counting
 | 
					 | 
				
			||||||
# public methods (see R0903)
 | 
					 | 
				
			||||||
exclude-too-few-public-methods=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of qualified class names to ignore when counting class parents (see
 | 
					 | 
				
			||||||
# R0901)
 | 
					 | 
				
			||||||
ignored-parents=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of arguments for function / method.
 | 
					 | 
				
			||||||
max-args=5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of attributes for a class (see R0902).
 | 
					 | 
				
			||||||
max-attributes=7
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of boolean expressions in an if statement (see R0916).
 | 
					 | 
				
			||||||
max-bool-expr=5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of branch for function / method body.
 | 
					 | 
				
			||||||
max-branches=12
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of locals for function / method body.
 | 
					 | 
				
			||||||
max-locals=15
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of parents for a class (see R0901).
 | 
					 | 
				
			||||||
max-parents=7
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of public methods for a class (see R0904).
 | 
					 | 
				
			||||||
max-public-methods=20
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of return / yield for function / method body.
 | 
					 | 
				
			||||||
max-returns=6
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Maximum number of statements in function / method body.
 | 
					 | 
				
			||||||
max-statements=50
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Minimum number of public methods for a class (see R0903).
 | 
					 | 
				
			||||||
min-public-methods=1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[STRING]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This flag controls whether inconsistent-quotes generates a warning when the
 | 
					 | 
				
			||||||
# character used as a quote delimiter is used inconsistently within a module.
 | 
					 | 
				
			||||||
check-quote-consistency=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# This flag controls whether the implicit-str-concat should generate a warning
 | 
					 | 
				
			||||||
# on implicit string concatenation in sequences defined over several lines.
 | 
					 | 
				
			||||||
check-str-concat-over-line-jumps=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[BASIC]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct argument names.
 | 
					 | 
				
			||||||
argument-naming-style=snake_case
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct argument names. Overrides argument-
 | 
					 | 
				
			||||||
# naming-style. If left empty, argument names will be checked with the set
 | 
					 | 
				
			||||||
# naming style.
 | 
					 | 
				
			||||||
#argument-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct attribute names.
 | 
					 | 
				
			||||||
attr-naming-style=snake_case
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct attribute names. Overrides attr-naming-
 | 
					 | 
				
			||||||
# style. If left empty, attribute names will be checked with the set naming
 | 
					 | 
				
			||||||
# style.
 | 
					 | 
				
			||||||
#attr-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Bad variable names which should always be refused, separated by a comma.
 | 
					 | 
				
			||||||
bad-names=foo,
 | 
					 | 
				
			||||||
          bar,
 | 
					 | 
				
			||||||
          baz,
 | 
					 | 
				
			||||||
          toto,
 | 
					 | 
				
			||||||
          tutu,
 | 
					 | 
				
			||||||
          tata
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Bad variable names regexes, separated by a comma. If names match any regex,
 | 
					 | 
				
			||||||
# they will always be refused
 | 
					 | 
				
			||||||
bad-names-rgxs=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct class attribute names.
 | 
					 | 
				
			||||||
class-attribute-naming-style=any
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct class attribute names. Overrides class-
 | 
					 | 
				
			||||||
# attribute-naming-style. If left empty, class attribute names will be checked
 | 
					 | 
				
			||||||
# with the set naming style.
 | 
					 | 
				
			||||||
#class-attribute-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct class constant names.
 | 
					 | 
				
			||||||
class-const-naming-style=UPPER_CASE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct class constant names. Overrides class-
 | 
					 | 
				
			||||||
# const-naming-style. If left empty, class constant names will be checked with
 | 
					 | 
				
			||||||
# the set naming style.
 | 
					 | 
				
			||||||
#class-const-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct class names.
 | 
					 | 
				
			||||||
class-naming-style=PascalCase
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct class names. Overrides class-naming-
 | 
					 | 
				
			||||||
# style. If left empty, class names will be checked with the set naming style.
 | 
					 | 
				
			||||||
#class-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct constant names.
 | 
					 | 
				
			||||||
const-naming-style=UPPER_CASE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct constant names. Overrides const-naming-
 | 
					 | 
				
			||||||
# style. If left empty, constant names will be checked with the set naming
 | 
					 | 
				
			||||||
# style.
 | 
					 | 
				
			||||||
#const-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Minimum line length for functions/classes that require docstrings, shorter
 | 
					 | 
				
			||||||
# ones are exempt.
 | 
					 | 
				
			||||||
docstring-min-length=-1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct function names.
 | 
					 | 
				
			||||||
function-naming-style=snake_case
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct function names. Overrides function-
 | 
					 | 
				
			||||||
# naming-style. If left empty, function names will be checked with the set
 | 
					 | 
				
			||||||
# naming style.
 | 
					 | 
				
			||||||
#function-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Good variable names which should always be accepted, separated by a comma.
 | 
					 | 
				
			||||||
good-names=i,
 | 
					 | 
				
			||||||
           j,
 | 
					 | 
				
			||||||
           k,
 | 
					 | 
				
			||||||
           ex,
 | 
					 | 
				
			||||||
           Run,
 | 
					 | 
				
			||||||
           _
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Good variable names regexes, separated by a comma. If names match any regex,
 | 
					 | 
				
			||||||
# they will always be accepted
 | 
					 | 
				
			||||||
good-names-rgxs=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Include a hint for the correct naming format with invalid-name.
 | 
					 | 
				
			||||||
include-naming-hint=no
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct inline iteration names.
 | 
					 | 
				
			||||||
inlinevar-naming-style=any
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct inline iteration names. Overrides
 | 
					 | 
				
			||||||
# inlinevar-naming-style. If left empty, inline iteration names will be checked
 | 
					 | 
				
			||||||
# with the set naming style.
 | 
					 | 
				
			||||||
#inlinevar-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct method names.
 | 
					 | 
				
			||||||
method-naming-style=snake_case
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct method names. Overrides method-naming-
 | 
					 | 
				
			||||||
# style. If left empty, method names will be checked with the set naming style.
 | 
					 | 
				
			||||||
#method-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct module names.
 | 
					 | 
				
			||||||
module-naming-style=snake_case
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct module names. Overrides module-naming-
 | 
					 | 
				
			||||||
# style. If left empty, module names will be checked with the set naming style.
 | 
					 | 
				
			||||||
#module-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Colon-delimited sets of names that determine each other's naming style when
 | 
					 | 
				
			||||||
# the name regexes allow several styles.
 | 
					 | 
				
			||||||
name-group=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression which should only match function or class names that do
 | 
					 | 
				
			||||||
# not require a docstring.
 | 
					 | 
				
			||||||
no-docstring-rgx=^_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
 | 
					 | 
				
			||||||
# to this list to register other decorators that produce valid properties.
 | 
					 | 
				
			||||||
# These decorators are taken in consideration only for invalid-name.
 | 
					 | 
				
			||||||
property-classes=abc.abstractproperty
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct type variable names. If left empty, type
 | 
					 | 
				
			||||||
# variable names will be checked with the set naming style.
 | 
					 | 
				
			||||||
#typevar-rgx=
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Naming style matching correct variable names.
 | 
					 | 
				
			||||||
variable-naming-style=snake_case
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Regular expression matching correct variable names. Overrides variable-
 | 
					 | 
				
			||||||
# naming-style. If left empty, variable names will be checked with the set
 | 
					 | 
				
			||||||
# naming style.
 | 
					 | 
				
			||||||
#variable-rgx=
 | 
					 | 
				
			||||||
							
								
								
									
										34
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								Dockerfile
									
									
									
									
									
								
							@@ -1,37 +1,23 @@
 | 
				
			|||||||
FROM docker.io/library/python:3.10-slim as base
 | 
					FROM docker.io/library/python:3.9-slim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Configure piwheels repo to use pre-compiled numpy wheels for arm
 | 
					# Configure piwheels repo to use pre-compiled numpy wheels for arm
 | 
				
			||||||
RUN echo -n "[global]\nextra-index-url=https://www.piwheels.org/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
 | 
					RUN apt-get update && apt-get install -y libgl1 libglib2.0-0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#################
 | 
					RUN pip3 install numpy
 | 
				
			||||||
FROM base as model-builder
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN python3 -m pip install blobconverter
 | 
					ADD requirements.txt requirements.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN mkdir -p /models
 | 
					RUN pip3 install -r requirements.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN blobconverter --zoo-name mobile_object_localizer_192x192 --zoo-type depthai --shaves 6 --version 2021.4 --output-dir /models || echo ""
 | 
					ADD events events
 | 
				
			||||||
 | 
					ADD camera camera
 | 
				
			||||||
 | 
					ADD setup.cfg setup.cfg
 | 
				
			||||||
 | 
					ADD setup.py setup.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#################
 | 
					ENV PYTHON_EGG_CACHE=/tmp/cache
 | 
				
			||||||
FROM base as builder
 | 
					RUN python3 setup.py install
 | 
				
			||||||
 | 
					 | 
				
			||||||
RUN apt-get install -y git && \
 | 
					 | 
				
			||||||
    pip3 install poetry==1.2.0 && \
 | 
					 | 
				
			||||||
    poetry self add "poetry-dynamic-versioning[plugin]"
 | 
					 | 
				
			||||||
ADD . .
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RUN poetry build
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#################
 | 
					 | 
				
			||||||
FROM base
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RUN mkdir /models
 | 
					 | 
				
			||||||
COPY --from=model-builder /models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob /models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
COPY --from=builder dist/*.whl /tmp/
 | 
					 | 
				
			||||||
RUN pip3 install /tmp/*whl
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
WORKDIR /tmp
 | 
					WORKDIR /tmp
 | 
				
			||||||
USER 1234
 | 
					USER 1234
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,11 +2,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
IMAGE_NAME=robocar-oak-camera
 | 
					IMAGE_NAME=robocar-oak-camera
 | 
				
			||||||
TAG=$(git describe)
 | 
					TAG=$(git describe)
 | 
				
			||||||
FULL_IMAGE_NAME=git.cyrilix.bzh/robocars/${IMAGE_NAME}:${TAG}
 | 
					FULL_IMAGE_NAME=docker.io/cyrilix/${IMAGE_NAME}:${TAG}
 | 
				
			||||||
PLATFORM="linux/amd64,linux/arm64"
 | 
					PLATFORM="linux/amd64,linux/arm64"
 | 
				
			||||||
#PLATFORM="linux/amd64,linux/arm64,linux/arm/v7"
 | 
					#PLATFORM="linux/amd64,linux/arm64,linux/arm/v7"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
podman build . --platform "${PLATFORM}" --manifest "${IMAGE_NAME}:${TAG}"
 | 
					podman build . --platform "${PLATFORM}" --manifest "${IMAGE_NAME}:${TAG}"
 | 
				
			||||||
podman manifest push --all --format v2s2 "localhost/${IMAGE_NAME}:${TAG}" "docker://${FULL_IMAGE_NAME}"
 | 
					podman manifest push --format v2s2 "localhost/${IMAGE_NAME}:${TAG}" "docker://${FULL_IMAGE_NAME}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
printf "\nImage %s published" "docker://${FULL_IMAGE_NAME}"
 | 
					printf "\nImage %s published" "docker://${FULL_IMAGE_NAME}"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										124
									
								
								camera/cli.py
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								camera/cli.py
									
									
									
									
									
								
							@@ -1,114 +1,66 @@
 | 
				
			|||||||
"""
 | 
					"""
 | 
				
			||||||
Mqtt gateway for oak-lite device
 | 
					Publish data from oak-lite device
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Usage: rc-oak-camera [-u USERNAME | --mqtt-username=USERNAME] [--mqtt-password=PASSWORD] [--mqtt-broker=HOSTNAME] \
 | 
				
			||||||
 | 
					    [--mqtt-topic-robocar-oak-camera="TOPIC_CAMERA"] [--mqtt-client-id=CLIENT_ID] \
 | 
				
			||||||
 | 
					    [-H IMG_HEIGHT | --image-height=IMG_HEIGHT] [-W IMG_WIDTH | --image-width=IMG_width]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Options:
 | 
				
			||||||
 | 
					-h --help                                               Show this screen.
 | 
				
			||||||
 | 
					-u USERID --mqtt-username=USERNAME                      MQTT user
 | 
				
			||||||
 | 
					-p PASSWORD --mqtt-password=PASSWORD                    MQTT password
 | 
				
			||||||
 | 
					-b HOSTNAME --mqtt-broker=HOSTNAME                      MQTT broker host
 | 
				
			||||||
 | 
					-C CLIENT_ID --mqtt-client-id=CLIENT_ID                 MQTT client id
 | 
				
			||||||
 | 
					-c TOPIC_CAMERA --mqtt-topic-robocar-oak-camera=TOPIC_CAMERA        MQTT topic where to publish robocar-oak-camera frames
 | 
				
			||||||
 | 
					-H IMG_HEIGHT --image-height=IMG_HEIGHT                 IMG_HEIGHT image height
 | 
				
			||||||
 | 
					-W IMG_WIDTH --image-width=IMG_width                    IMG_WIDTH image width
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
import argparse
 | 
					 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import signal
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import paho.mqtt.client as mqtt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from . import depthai as cam
 | 
					from . import depthai as cam
 | 
				
			||||||
 | 
					from docopt import docopt
 | 
				
			||||||
 | 
					import paho.mqtt.client as mqtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
logging.basicConfig(level=logging.INFO)
 | 
					logging.basicConfig(level=logging.INFO)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_DEFAULT_CLIENT_ID = "robocar-depthai"
 | 
					default_client_id = "robocar-depthai"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _parse_args_cli() -> argparse.Namespace:
 | 
					def init_mqtt_client(broker_host: str, user: str, password: str, client_id: str) -> mqtt.Client:
 | 
				
			||||||
    parser = argparse.ArgumentParser()
 | 
					 | 
				
			||||||
    parser.add_argument("-u", "--mqtt-username",
 | 
					 | 
				
			||||||
                        help="MQTT user",
 | 
					 | 
				
			||||||
                        default=_get_env_value("MQTT_USERNAME", ""))
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--mqtt-password",
 | 
					 | 
				
			||||||
                        help="MQTT password",
 | 
					 | 
				
			||||||
                        default=_get_env_value("MQTT_PASSWORD", ""))
 | 
					 | 
				
			||||||
    parser.add_argument("-b", "--mqtt-broker-host",
 | 
					 | 
				
			||||||
                        help="MQTT broker host",
 | 
					 | 
				
			||||||
                        default=_get_env_value("MQTT_BROKER_HOST", "localhost"))
 | 
					 | 
				
			||||||
    parser.add_argument("-P", "--mqtt-broker-port",
 | 
					 | 
				
			||||||
                        help="MQTT broker port",
 | 
					 | 
				
			||||||
                        type=int,
 | 
					 | 
				
			||||||
                        default=_get_env_int_value("MQTT_BROKER_PORT", 1883))
 | 
					 | 
				
			||||||
    parser.add_argument("-C", "--mqtt-client-id",
 | 
					 | 
				
			||||||
                        help="MQTT client id",
 | 
					 | 
				
			||||||
                        default=_get_env_value("MQTT_CLIENT_ID", _DEFAULT_CLIENT_ID))
 | 
					 | 
				
			||||||
    parser.add_argument("-c", "--mqtt-topic-robocar-oak-camera",
 | 
					 | 
				
			||||||
                        help="MQTT topic where to publish robocar-oak-camera frames",
 | 
					 | 
				
			||||||
                        default=_get_env_value("MQTT_TOPIC_CAMERA", "/oak/camera_rgb"))
 | 
					 | 
				
			||||||
    parser.add_argument("-o", "---mqtt-topic-robocar-objects",
 | 
					 | 
				
			||||||
                        help="MQTT topic where to publish objects detection results",
 | 
					 | 
				
			||||||
                        default=_get_env_value("MQTT_TOPIC_OBJECTS", "/objects"))
 | 
					 | 
				
			||||||
    parser.add_argument("-t", "--objects-threshold",
 | 
					 | 
				
			||||||
                        help="threshold to filter detected objects",
 | 
					 | 
				
			||||||
                        type=float,
 | 
					 | 
				
			||||||
                        default=_get_env_float_value("OBJECTS_THRESHOLD", 0.2))
 | 
					 | 
				
			||||||
    parser.add_argument("-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))
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
    return args
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _init_mqtt_client(broker_host: str, broker_port, user: str, password: str, client_id: str) -> mqtt.Client:
 | 
					 | 
				
			||||||
    logger.info("Start part.py-robocar-oak-camera")
 | 
					    logger.info("Start part.py-robocar-oak-camera")
 | 
				
			||||||
    client = mqtt.Client(client_id=client_id, clean_session=True, userdata=None, protocol=mqtt.MQTTv311)
 | 
					    client = mqtt.Client(client_id=client_id, clean_session=True, userdata=None, protocol=mqtt.MQTTv311)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    client.username_pw_set(user, password)
 | 
					    client.username_pw_set(user, password)
 | 
				
			||||||
    logger.info("Connect to mqtt broker %s", broker_host)
 | 
					    logger.info("Connect to mqtt broker "+ broker_host)
 | 
				
			||||||
    client.connect(host=broker_host, port=broker_port, keepalive=60)
 | 
					    client.connect(host=broker_host, port=1883, keepalive=60)
 | 
				
			||||||
    logger.info("Connected to mqtt broker")
 | 
					    logger.info("Connected to mqtt broker")
 | 
				
			||||||
    return client
 | 
					    return client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def execute_from_command_line() -> None:
 | 
					def execute_from_command_line():
 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Cli entrypoint
 | 
					 | 
				
			||||||
    :return:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    logging.basicConfig(level=logging.INFO)
 | 
					    logging.basicConfig(level=logging.INFO)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    args = _parse_args_cli()
 | 
					    args = docopt(__doc__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    client = _init_mqtt_client(broker_host=args.mqtt_broker_host,
 | 
					    client = init_mqtt_client(broker_host=get_default_value(args["--mqtt-broker"], "MQTT_BROKER", "localhost"),
 | 
				
			||||||
                               broker_port=args.mqtt_broker_port,
 | 
					                              user=get_default_value(args["--mqtt-username"], "MQTT_USERNAME", ""),
 | 
				
			||||||
                               user=args.mqtt_username,
 | 
					                              password=get_default_value(args["--mqtt-password"], "MQTT_PASSWORD", ""),
 | 
				
			||||||
                               password=args.mqtt_password,
 | 
					                              client_id=get_default_value(args["--mqtt-client-id"], "MQTT_CLIENT_ID",
 | 
				
			||||||
                               client_id=args.mqtt_client_id,
 | 
					                                                          default_client_id),
 | 
				
			||||||
                               )
 | 
					                              )
 | 
				
			||||||
    frame_processor = cam.FrameProcessor(mqtt_client=client, frame_topic=args.mqtt_topic_robocar_oak_camera)
 | 
					    frame_topic = get_default_value(args["--mqtt-topic-robocar-oak-camera"], "MQTT_TOPIC_CAMERA", "/oak/camera_rgb")
 | 
				
			||||||
    object_processor = cam.ObjectProcessor(mqtt_client=client,
 | 
					 | 
				
			||||||
                                           objects_topic=args.mqtt_topic_robocar_objects,
 | 
					 | 
				
			||||||
                                           objects_threshold=args.objects_threshold)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pipeline_controller = cam.PipelineController(img_width=args.image_width,
 | 
					    frame_processor = cam.FramePublisher(mqtt_client=client,
 | 
				
			||||||
                                                 img_height=args.image_height,
 | 
					                                         frame_topic=frame_topic,
 | 
				
			||||||
                                                 frame_processor=frame_processor,
 | 
					                                         img_width=int(get_default_value(args["--image-width"], "IMAGE_WIDTH", 160)),
 | 
				
			||||||
                                                 object_processor=object_processor)
 | 
					                                         img_height=int(get_default_value(args["--image-height"], "IMAGE_HEIGHT", 120)))
 | 
				
			||||||
 | 
					    frame_processor.run()
 | 
				
			||||||
    def sigterm_handler():
 | 
					 | 
				
			||||||
        logger.info("exit on SIGTERM")
 | 
					 | 
				
			||||||
        pipeline_controller.stop()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    signal.signal(signal.SIGTERM, sigterm_handler)
 | 
					 | 
				
			||||||
    pipeline_controller.run()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _get_env_value(env_var: str, default_value: str) -> str:
 | 
					def get_default_value(value, env_var: str, default_value) -> str:
 | 
				
			||||||
 | 
					    if value:
 | 
				
			||||||
 | 
					        return value
 | 
				
			||||||
    if env_var in os.environ:
 | 
					    if env_var in os.environ:
 | 
				
			||||||
        return os.environ[env_var]
 | 
					        return os.environ[env_var]
 | 
				
			||||||
    return default_value
 | 
					    return default_value
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _get_env_int_value(env_var: str, default_value: int) -> int:
 | 
					 | 
				
			||||||
    value = _get_env_value(env_var, str(default_value))
 | 
					 | 
				
			||||||
    return int(value)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def _get_env_float_value(env_var: str, default_value: float) -> float:
 | 
					 | 
				
			||||||
    value = _get_env_value(env_var, str(default_value))
 | 
					 | 
				
			||||||
    return float(value)
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,153 +1,107 @@
 | 
				
			|||||||
"""
 | 
					 | 
				
			||||||
Camera event loop
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
import datetime
 | 
					import datetime
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import typing
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import cv2
 | 
					 | 
				
			||||||
import depthai as dai
 | 
					 | 
				
			||||||
import numpy as np
 | 
					 | 
				
			||||||
import paho.mqtt.client as mqtt
 | 
					import paho.mqtt.client as mqtt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import events.events_pb2
 | 
					import events.events_pb2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import depthai as dai
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = logging.getLogger(__name__)
 | 
					logger = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_NN_PATH = "/models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob"
 | 
					
 | 
				
			||||||
_NN_WIDTH = 192
 | 
					def to_tensor_result(packet):
 | 
				
			||||||
_NN_HEIGHT = 192
 | 
					    return {
 | 
				
			||||||
 | 
					        name: np.array(packet.getLayerFp16(name))
 | 
				
			||||||
 | 
					        for name in [tensor.name for tensor in packet.getRaw().tensors]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ObjectProcessor:
 | 
					def to_planar(frame):
 | 
				
			||||||
    """
 | 
					    return frame.transpose(2, 0, 1).flatten()
 | 
				
			||||||
    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) -> 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: np.array, frame_ref, scores: np.array) -> None:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        objects_msg = events.events_pb2.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):
 | 
					class FramePublisher:
 | 
				
			||||||
    """
 | 
					    def __init__(self, mqtt_client: mqtt.Client, frame_topic: str, img_width: int, img_height: int):
 | 
				
			||||||
    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._mqtt_client = mqtt_client
 | 
				
			||||||
        self._frame_topic = frame_topic
 | 
					        self._frame_topic = frame_topic
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def process(self, img: dai.ImgFrame) -> typing.Any:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Publish camera frames
 | 
					 | 
				
			||||||
        :param img:
 | 
					 | 
				
			||||||
        :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 = 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)
 | 
					 | 
				
			||||||
        return frame_msg.id
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PipelineController:
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Pipeline controller that drive camera device
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, img_width: int, img_height: int, frame_processor: FrameProcessor,
 | 
					 | 
				
			||||||
                 object_processor: ObjectProcessor):
 | 
					 | 
				
			||||||
        self._img_width = img_width
 | 
					        self._img_width = img_width
 | 
				
			||||||
        self._img_height = img_height
 | 
					        self._img_height = img_height
 | 
				
			||||||
        self._pipeline = self._configure_pipeline()
 | 
					        self._pipeline = self._configure_pipeline()
 | 
				
			||||||
        self._frame_processor = frame_processor
 | 
					 | 
				
			||||||
        self._object_processor = object_processor
 | 
					 | 
				
			||||||
        self._stop = False
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _configure_pipeline(self) -> dai.Pipeline:
 | 
					    def _configure_pipeline(self) -> dai.Pipeline:
 | 
				
			||||||
        logger.info("configure pipeline")
 | 
					        logger.info("configure pipeline")
 | 
				
			||||||
        pipeline = dai.Pipeline()
 | 
					        pipeline = dai.Pipeline()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pipeline.setOpenVINOVersion(version=dai.OpenVINO.VERSION_2021_4)
 | 
					        version = "2021.2"
 | 
				
			||||||
 | 
					        pipeline.setOpenVINOVersion(version=dai.OpenVINO.Version.VERSION_2021_2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        detection_nn = self._configure_detection_nn(pipeline)
 | 
					        # colorCam = pipeline.create(dai.node.ColorCamera)
 | 
				
			||||||
        xout_nn = self._configure_xout_nn(pipeline)
 | 
					        # colorCam.setPreviewSize(256, 256)
 | 
				
			||||||
 | 
					        # colorCam.setVideoSize(1024, 1024)  # 4 times larger in both axis
 | 
				
			||||||
 | 
					        # colorCam.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
 | 
				
			||||||
 | 
					        # colorCam.setInterleaved(False)
 | 
				
			||||||
 | 
					        # colorCam.setBoardSocket(dai.CameraBoardSocket.RGB)
 | 
				
			||||||
 | 
					        # colorCam.setFps(10)
 | 
				
			||||||
 | 
					        #
 | 
				
			||||||
 | 
					        # controlIn = pipeline.create(dai.node.XLinkIn)
 | 
				
			||||||
 | 
					        # controlIn.setStreamName('control')
 | 
				
			||||||
 | 
					        # controlIn.out.link(colorCam.inputControl)
 | 
				
			||||||
 | 
					        #
 | 
				
			||||||
 | 
					        # cam_xout = pipeline.create(dai.node.XLinkOut)
 | 
				
			||||||
 | 
					        # cam_xout.setStreamName('video')
 | 
				
			||||||
 | 
					        # colorCam.video.link(cam_xout.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # ---------------------------------------
 | 
				
			||||||
 | 
					        # 1st stage NN - text-detection
 | 
				
			||||||
 | 
					        # ---------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nn = pipeline.create(dai.node.NeuralNetwork)
 | 
				
			||||||
 | 
					        nn.setBlobPath(
 | 
				
			||||||
 | 
					            blobconverter.from_zoo(name="east_text_detection_256x256", zoo_type="depthai", shaves=6, version=version))
 | 
				
			||||||
 | 
					        colorCam.preview.link(nn.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nn_xout = pipeline.create(dai.node.XLinkOut)
 | 
				
			||||||
 | 
					        nn_xout.setStreamName('detections')
 | 
				
			||||||
 | 
					        nn.out.link(nn_xout.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # ---------------------------------------
 | 
				
			||||||
 | 
					        # 2nd stage NN - text-recognition-0012
 | 
				
			||||||
 | 
					        # ---------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Resize image
 | 
					 | 
				
			||||||
        manip = pipeline.create(dai.node.ImageManip)
 | 
					        manip = pipeline.create(dai.node.ImageManip)
 | 
				
			||||||
        manip.initialConfig.setResize(_NN_WIDTH, _NN_HEIGHT)
 | 
					        manip.setWaitForConfigInput(True)
 | 
				
			||||||
        manip.initialConfig.setFrameType(dai.ImgFrame.Type.RGB888p)
 | 
					
 | 
				
			||||||
        manip.initialConfig.setKeepAspectRatio(False)
 | 
					        manip_img = pipeline.create(dai.node.XLinkIn)
 | 
				
			||||||
 | 
					        manip_img.setStreamName('manip_img')
 | 
				
			||||||
 | 
					        manip_img.out.link(manip.inputImage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        manip_cfg = pipeline.create(dai.node.XLinkIn)
 | 
				
			||||||
 | 
					        manip_cfg.setStreamName('manip_cfg')
 | 
				
			||||||
 | 
					        manip_cfg.out.link(manip.inputConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        manip_xout = pipeline.create(dai.node.XLinkOut)
 | 
				
			||||||
 | 
					        manip_xout.setStreamName('manip_out')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nn2 = pipeline.create(dai.node.NeuralNetwork)
 | 
				
			||||||
 | 
					        nn2.setBlobPath(blobconverter.from_zoo(name="text-recognition-0012", shaves=6, version=version))
 | 
				
			||||||
 | 
					        nn2.setNumInferenceThreads(2)
 | 
				
			||||||
 | 
					        manip.out.link(nn2.input)
 | 
				
			||||||
 | 
					        manip.out.link(manip_xout.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        nn2_xout = pipeline.create(dai.node.XLinkOut)
 | 
				
			||||||
 | 
					        nn2_xout.setStreamName("recognitions")
 | 
				
			||||||
 | 
					        nn2.out.link(nn2_xout.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cam_rgb = pipeline.create(dai.node.ColorCamera)
 | 
					        cam_rgb = pipeline.create(dai.node.ColorCamera)
 | 
				
			||||||
        xout_rgb = pipeline.create(dai.node.XLinkOut)
 | 
					        xout_rgb = pipeline.create(dai.node.XLinkOut)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        xout_rgb.setStreamName("rgb")
 | 
					        xout_rgb.setStreamName("rgb")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Properties
 | 
					        # Properties
 | 
				
			||||||
@@ -157,90 +111,191 @@ class PipelineController:
 | 
				
			|||||||
        cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.RGB)
 | 
					        cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.RGB)
 | 
				
			||||||
        cam_rgb.setFps(30)
 | 
					        cam_rgb.setFps(30)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Link preview to manip and manip to nn
 | 
					        # Linking
 | 
				
			||||||
        cam_rgb.preview.link(manip.inputImage)
 | 
					 | 
				
			||||||
        manip.out.link(detection_nn.input)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Linking to output
 | 
					 | 
				
			||||||
        cam_rgb.preview.link(xout_rgb.input)
 | 
					        cam_rgb.preview.link(xout_rgb.input)
 | 
				
			||||||
        detection_nn.out.link(xout_nn.input)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        logger.info("pipeline configured")
 | 
					        logger.info("pipeline configured")
 | 
				
			||||||
        return pipeline
 | 
					        return pipeline
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					    def run(self):
 | 
				
			||||||
    def _configure_xout_nn(pipeline: dai.Pipeline) -> dai.node.XLinkOut:
 | 
					
 | 
				
			||||||
        xout_nn = pipeline.create(dai.node.XLinkOut)
 | 
					        with dai.Device(self._pipeline) as device:
 | 
				
			||||||
        xout_nn.setStreamName("nn")
 | 
					            q_vid = device.getOutputQueue("video", 4, blocking=False)
 | 
				
			||||||
        xout_nn.input.setBlocking(False)
 | 
					            # This should be set to block, but would get to some extreme queuing/latency!
 | 
				
			||||||
        return xout_nn
 | 
					            q_det = device.getOutputQueue("detections", 4, blocking=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            q_rec = device.getOutputQueue("recognitions", 4, blocking=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            q_manip_img = device.getInputQueue("manip_img")
 | 
				
			||||||
 | 
					            q_manip_cfg = device.getInputQueue("manip_cfg")
 | 
				
			||||||
 | 
					            q_manip_out = device.getOutputQueue("manip_out", 4, blocking=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            controlQueue = device.getInputQueue('control')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            frame = None
 | 
				
			||||||
 | 
					            cropped_stacked = None
 | 
				
			||||||
 | 
					            rotated_rectangles = []
 | 
				
			||||||
 | 
					            rec_pushed = 0
 | 
				
			||||||
 | 
					            rec_received = 0
 | 
				
			||||||
 | 
					            host_sync = HostSeqSync()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            characters = '0123456789abcdefghijklmnopqrstuvwxyz#'
 | 
				
			||||||
 | 
					            codec = CTCCodec(characters)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ctrl = dai.CameraControl()
 | 
				
			||||||
 | 
					            ctrl.setAutoFocusMode(dai.CameraControl.AutoFocusMode.CONTINUOUS_VIDEO)
 | 
				
			||||||
 | 
					            ctrl.setAutoFocusTrigger()
 | 
				
			||||||
 | 
					            controlQueue.send(ctrl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            while True:
 | 
				
			||||||
 | 
					                vid_in = q_vid.tryGet()
 | 
				
			||||||
 | 
					                if vid_in is not None:
 | 
				
			||||||
 | 
					                    host_sync.add_msg(vid_in)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # Multiple recognition results may be available, read until queue is empty
 | 
				
			||||||
 | 
					                while True:
 | 
				
			||||||
 | 
					                    in_rec = q_rec.tryGet()
 | 
				
			||||||
 | 
					                    if in_rec is None:
 | 
				
			||||||
 | 
					                        break
 | 
				
			||||||
 | 
					                    rec_data = bboxes = np.array(in_rec.getFirstLayerFp16()).reshape(30, 1, 37)
 | 
				
			||||||
 | 
					                    decoded_text = codec.decode(rec_data)[0]
 | 
				
			||||||
 | 
					                    pos = rotated_rectangles[rec_received]
 | 
				
			||||||
 | 
					                    print("{:2}: {:20}".format(rec_received, decoded_text),
 | 
				
			||||||
 | 
					                          "center({:3},{:3}) size({:3},{:3}) angle{:5.1f} deg".format(
 | 
				
			||||||
 | 
					                              int(pos[0][0]), int(pos[0][1]), pos[1][0], pos[1][1], pos[2]))
 | 
				
			||||||
 | 
					                    # Draw the text on the right side of 'cropped_stacked' - placeholder
 | 
				
			||||||
 | 
					                    if cropped_stacked is not None:
 | 
				
			||||||
 | 
					                        cv2.putText(cropped_stacked, decoded_text,
 | 
				
			||||||
 | 
					                                    (120 + 10, 32 * rec_received + 24),
 | 
				
			||||||
 | 
					                                    cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
 | 
				
			||||||
 | 
					                        cv2.imshow('cropped_stacked', cropped_stacked)
 | 
				
			||||||
 | 
					                    rec_received += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if cv2.waitKey(1) == ord('q'):
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if rec_received >= rec_pushed:
 | 
				
			||||||
 | 
					                    in_det = q_det.tryGet()
 | 
				
			||||||
 | 
					                    if in_det is not None:
 | 
				
			||||||
 | 
					                        frame = host_sync.get_msg(in_det.getSequenceNum()).getCvFrame().copy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        scores, geom1, geom2 = to_tensor_result(in_det).values()
 | 
				
			||||||
 | 
					                        scores = np.reshape(scores, (1, 1, 64, 64))
 | 
				
			||||||
 | 
					                        geom1 = np.reshape(geom1, (1, 4, 64, 64))
 | 
				
			||||||
 | 
					                        geom2 = np.reshape(geom2, (1, 1, 64, 64))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        bboxes, confs, angles = east.decode_predictions(scores, geom1, geom2)
 | 
				
			||||||
 | 
					                        boxes, angles = east.non_max_suppression(np.array(bboxes), probs=confs, angles=np.array(angles))
 | 
				
			||||||
 | 
					                        rotated_rectangles = [
 | 
				
			||||||
 | 
					                            east.get_cv_rotated_rect(bbox, angle * -1)
 | 
				
			||||||
 | 
					                            for (bbox, angle) in zip(boxes, angles)
 | 
				
			||||||
 | 
					                        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        rec_received = 0
 | 
				
			||||||
 | 
					                        rec_pushed = len(rotated_rectangles)
 | 
				
			||||||
 | 
					                        if rec_pushed:
 | 
				
			||||||
 | 
					                            print("====== Pushing for recognition, count:", rec_pushed)
 | 
				
			||||||
 | 
					                        cropped_stacked = None
 | 
				
			||||||
 | 
					                        for idx, rotated_rect in enumerate(rotated_rectangles):
 | 
				
			||||||
 | 
					                            # Detections are done on 256x256 frames, we are sending back 1024x1024
 | 
				
			||||||
 | 
					                            # That's why we multiply center and size values by 4
 | 
				
			||||||
 | 
					                            rotated_rect[0][0] = rotated_rect[0][0] * 4
 | 
				
			||||||
 | 
					                            rotated_rect[0][1] = rotated_rect[0][1] * 4
 | 
				
			||||||
 | 
					                            rotated_rect[1][0] = rotated_rect[1][0] * 4
 | 
				
			||||||
 | 
					                            rotated_rect[1][1] = rotated_rect[1][1] * 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            # Draw detection crop area on input frame
 | 
				
			||||||
 | 
					                            points = np.int0(cv2.boxPoints(rotated_rect))
 | 
				
			||||||
 | 
					                            print(rotated_rect)
 | 
				
			||||||
 | 
					                            cv2.polylines(frame, [points], isClosed=True, color=(255, 0, 0), thickness=1,
 | 
				
			||||||
 | 
					                                          lineType=cv2.LINE_8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            # TODO make it work taking args like in OpenCV:
 | 
				
			||||||
 | 
					                            # rr = ((256, 256), (128, 64), 30)
 | 
				
			||||||
 | 
					                            rr = dai.RotatedRect()
 | 
				
			||||||
 | 
					                            rr.center.x = rotated_rect[0][0]
 | 
				
			||||||
 | 
					                            rr.center.y = rotated_rect[0][1]
 | 
				
			||||||
 | 
					                            rr.size.width = rotated_rect[1][0]
 | 
				
			||||||
 | 
					                            rr.size.height = rotated_rect[1][1]
 | 
				
			||||||
 | 
					                            rr.angle = rotated_rect[2]
 | 
				
			||||||
 | 
					                            cfg = dai.ImageManipConfig()
 | 
				
			||||||
 | 
					                            cfg.setCropRotatedRect(rr, False)
 | 
				
			||||||
 | 
					                            cfg.setResize(120, 32)
 | 
				
			||||||
 | 
					                            # Send frame and config to device
 | 
				
			||||||
 | 
					                            if idx == 0:
 | 
				
			||||||
 | 
					                                w, h, c = frame.shape
 | 
				
			||||||
 | 
					                                imgFrame = dai.ImgFrame()
 | 
				
			||||||
 | 
					                                imgFrame.setData(to_planar(frame))
 | 
				
			||||||
 | 
					                                imgFrame.setType(dai.ImgFrame.Type.BGR888p)
 | 
				
			||||||
 | 
					                                imgFrame.setWidth(w)
 | 
				
			||||||
 | 
					                                imgFrame.setHeight(h)
 | 
				
			||||||
 | 
					                                q_manip_img.send(imgFrame)
 | 
				
			||||||
 | 
					                            else:
 | 
				
			||||||
 | 
					                                cfg.setReusePreviousImage(True)
 | 
				
			||||||
 | 
					                            q_manip_cfg.send(cfg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            # Get manipulated image from the device
 | 
				
			||||||
 | 
					                            transformed = q_manip_out.get().getCvFrame()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            rec_placeholder_img = np.zeros((32, 200, 3), np.uint8)
 | 
				
			||||||
 | 
					                            transformed = np.hstack((transformed, rec_placeholder_img))
 | 
				
			||||||
 | 
					                            if cropped_stacked is None:
 | 
				
			||||||
 | 
					                                cropped_stacked = transformed
 | 
				
			||||||
 | 
					                            else:
 | 
				
			||||||
 | 
					                                cropped_stacked = np.vstack((cropped_stacked, transformed))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if cropped_stacked is not None:
 | 
				
			||||||
 | 
					                    cv2.imshow('cropped_stacked', cropped_stacked)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if frame is not None:
 | 
				
			||||||
 | 
					                    cv2.imshow('frame', frame)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                key = cv2.waitKey(1)
 | 
				
			||||||
 | 
					                if key == ord('q'):
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					                elif key == ord('t'):
 | 
				
			||||||
 | 
					                    print("Autofocus trigger (and disable continuous)")
 | 
				
			||||||
 | 
					                    ctrl = dai.CameraControl()
 | 
				
			||||||
 | 
					                    ctrl.setAutoFocusMode(dai.CameraControl.AutoFocusMode.AUTO)
 | 
				
			||||||
 | 
					                    ctrl.setAutoFocusTrigger()
 | 
				
			||||||
 | 
					                    controlQueue.send(ctrl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @staticmethod
 | 
					 | 
				
			||||||
    def _configure_detection_nn(pipeline: dai.Pipeline) -> dai.node.NeuralNetwork:
 | 
					 | 
				
			||||||
        # Define a neural network that will make predictions based on the source frames
 | 
					 | 
				
			||||||
        detection_nn = pipeline.create(dai.node.NeuralNetwork)
 | 
					 | 
				
			||||||
        detection_nn.setBlobPath(_NN_PATH)
 | 
					 | 
				
			||||||
        detection_nn.setNumPoolFrames(4)
 | 
					 | 
				
			||||||
        detection_nn.input.setBlocking(False)
 | 
					 | 
				
			||||||
        detection_nn.setNumInferenceThreads(2)
 | 
					 | 
				
			||||||
        return detection_nn
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run(self) -> None:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Start event loop
 | 
					 | 
				
			||||||
        :return:
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        # Connect to device and start pipeline
 | 
					        # Connect to device and start pipeline
 | 
				
			||||||
        with dai.Device(self._pipeline) as device:
 | 
					        with dai.Device(self._pipeline) as device:
 | 
				
			||||||
            logger.info('MxId: %s', device.getDeviceInfo().getMxId())
 | 
					            logger.info('MxId: %s', device.getDeviceInfo().getMxId())
 | 
				
			||||||
            logger.info('USB speed: %s', device.getUsbSpeed())
 | 
					            logger.info('USB speed: %s', device.getUsbSpeed())
 | 
				
			||||||
            logger.info('Connected cameras: %s', device.getConnectedCameras())
 | 
					            logger.info('Connected cameras: %s', device.getConnectedCameras())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            logger.info("output queues found: %s", device.getOutputQueueNames())
 | 
					            logger.info("output queues found: %s", device.getOutputQueueNames())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            device.startPipeline()
 | 
					            device.startPipeline()
 | 
				
			||||||
            # Queues
 | 
					            # Queues
 | 
				
			||||||
            queue_size = 4
 | 
					            queue_size = 4
 | 
				
			||||||
            q_rgb = device.getOutputQueue(name="rgb", maxSize=queue_size, blocking=False)
 | 
					            q_rgb = device.getOutputQueue("rgb", maxSize=queue_size, blocking=False)
 | 
				
			||||||
            q_nn = device.getOutputQueue(name="nn", maxSize=queue_size, blocking=False)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self._stop = False
 | 
					 | 
				
			||||||
            while True:
 | 
					            while True:
 | 
				
			||||||
                if self._stop:
 | 
					 | 
				
			||||||
                    logger.info("stop loop event")
 | 
					 | 
				
			||||||
                    return
 | 
					 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    self._loop_on_camera_events(q_nn, q_rgb)
 | 
					                    logger.debug("wait for new frame")
 | 
				
			||||||
                # pylint: disable=broad-except # bad frame or event must not stop loop
 | 
					                    inRgb = q_rgb.get()  # blocking call, will wait until a new data has arrived
 | 
				
			||||||
                except Exception as ex:
 | 
					 | 
				
			||||||
                    logger.exception("unexpected error: %s", str(ex))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _loop_on_camera_events(self, q_nn: dai.DataOutputQueue, q_rgb: dai.DataOutputQueue):
 | 
					                    im_resize = inRgb.getCvFrame()
 | 
				
			||||||
        logger.debug("wait for new frame")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Wait for frame
 | 
					                    is_success, im_buf_arr = cv2.imencode(".jpg", im_resize)
 | 
				
			||||||
        in_rgb: dai.ImgFrame = q_rgb.get()  # blocking call, will wait until a new data has arrived
 | 
					                    byte_im = im_buf_arr.tobytes()
 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            frame_ref = self._frame_processor.process(in_rgb)
 | 
					 | 
				
			||||||
        except FrameProcessError as ex:
 | 
					 | 
				
			||||||
            logger.error("unable to process frame: %s", str(ex))
 | 
					 | 
				
			||||||
        # Read NN result
 | 
					 | 
				
			||||||
        in_nn: dai.NNData = q_nn.get()
 | 
					 | 
				
			||||||
        self._object_processor.process(in_nn, frame_ref)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def stop(self):
 | 
					                    now = datetime.datetime.now()
 | 
				
			||||||
        """
 | 
					                    frame_msg = events.events_pb2.FrameMessage()
 | 
				
			||||||
        Stop event loop, if loop is not running, do nothing
 | 
					                    frame_msg.id.name = "robocar-oak-camera-oak"
 | 
				
			||||||
        :return:
 | 
					                    frame_msg.id.id = str(int(now.timestamp() * 1000))
 | 
				
			||||||
        """
 | 
					                    frame_msg.id.created_at.FromDatetime(now)
 | 
				
			||||||
        self._stop = True
 | 
					                    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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _bbox_to_object(bbox: np.array, score: float) -> events.events_pb2.Object:
 | 
					                except Exception as e:
 | 
				
			||||||
    obj = events.events_pb2.Object()
 | 
					                    logger.exception("unexpected error: %s", str(e))
 | 
				
			||||||
    obj.type = events.events_pb2.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
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										232
									
								
								camera/east.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								camera/east.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,232 @@
 | 
				
			|||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					import depthai
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_conf_threshold = 0.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_cv_rotated_rect(bbox, angle):
 | 
				
			||||||
 | 
					    x0, y0, x1, y1 = bbox
 | 
				
			||||||
 | 
					    width = abs(x0 - x1)
 | 
				
			||||||
 | 
					    height = abs(y0 - y1)
 | 
				
			||||||
 | 
					    x = x0 + width * 0.5
 | 
				
			||||||
 | 
					    y = y0 + height * 0.5
 | 
				
			||||||
 | 
					    return [x.tolist(), y.tolist()], [width.tolist(), height.tolist()], np.rad2deg(angle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def rotated_Rectangle(bbox, angle):
 | 
				
			||||||
 | 
					    X0, Y0, X1, Y1 = bbox
 | 
				
			||||||
 | 
					    width = abs(X0 - X1)
 | 
				
			||||||
 | 
					    height = abs(Y0 - Y1)
 | 
				
			||||||
 | 
					    x = int(X0 + width * 0.5)
 | 
				
			||||||
 | 
					    y = int(Y0 + height * 0.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pt1_1 = (int(x + width / 2), int(y + height / 2))
 | 
				
			||||||
 | 
					    pt2_1 = (int(x + width / 2), int(y - height / 2))
 | 
				
			||||||
 | 
					    pt3_1 = (int(x - width / 2), int(y - height / 2))
 | 
				
			||||||
 | 
					    pt4_1 = (int(x - width / 2), int(y + height / 2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    t = np.array([[np.cos(angle), -np.sin(angle), x - x * np.cos(angle) + y * np.sin(angle)],
 | 
				
			||||||
 | 
					                  [np.sin(angle), np.cos(angle), y - x * np.sin(angle) - y * np.cos(angle)],
 | 
				
			||||||
 | 
					                  [0, 0, 1]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tmp_pt1_1 = np.array([[pt1_1[0]], [pt1_1[1]], [1]])
 | 
				
			||||||
 | 
					    tmp_pt1_2 = np.dot(t, tmp_pt1_1)
 | 
				
			||||||
 | 
					    pt1_2 = (int(tmp_pt1_2[0][0]), int(tmp_pt1_2[1][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tmp_pt2_1 = np.array([[pt2_1[0]], [pt2_1[1]], [1]])
 | 
				
			||||||
 | 
					    tmp_pt2_2 = np.dot(t, tmp_pt2_1)
 | 
				
			||||||
 | 
					    pt2_2 = (int(tmp_pt2_2[0][0]), int(tmp_pt2_2[1][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tmp_pt3_1 = np.array([[pt3_1[0]], [pt3_1[1]], [1]])
 | 
				
			||||||
 | 
					    tmp_pt3_2 = np.dot(t, tmp_pt3_1)
 | 
				
			||||||
 | 
					    pt3_2 = (int(tmp_pt3_2[0][0]), int(tmp_pt3_2[1][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tmp_pt4_1 = np.array([[pt4_1[0]], [pt4_1[1]], [1]])
 | 
				
			||||||
 | 
					    tmp_pt4_2 = np.dot(t, tmp_pt4_1)
 | 
				
			||||||
 | 
					    pt4_2 = (int(tmp_pt4_2[0][0]), int(tmp_pt4_2[1][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    points = np.array([pt1_2, pt2_2, pt3_2, pt4_2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return points
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def non_max_suppression(boxes, probs=None, angles=None, overlapThresh=0.3):
 | 
				
			||||||
 | 
					    # if there are no boxes, return an empty list
 | 
				
			||||||
 | 
					    if len(boxes) == 0:
 | 
				
			||||||
 | 
					        return [], []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # if the bounding boxes are integers, convert them to floats -- this
 | 
				
			||||||
 | 
					    # is important since we'll be doing a bunch of divisions
 | 
				
			||||||
 | 
					    if boxes.dtype.kind == "i":
 | 
				
			||||||
 | 
					        boxes = boxes.astype("float")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # initialize the list of picked indexes
 | 
				
			||||||
 | 
					    pick = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # grab the coordinates of the bounding boxes
 | 
				
			||||||
 | 
					    x1 = boxes[:, 0]
 | 
				
			||||||
 | 
					    y1 = boxes[:, 1]
 | 
				
			||||||
 | 
					    x2 = boxes[:, 2]
 | 
				
			||||||
 | 
					    y2 = boxes[:, 3]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # compute the area of the bounding boxes and grab the indexes to sort
 | 
				
			||||||
 | 
					    # (in the case that no probabilities are provided, simply sort on the bottom-left y-coordinate)
 | 
				
			||||||
 | 
					    area = (x2 - x1 + 1) * (y2 - y1 + 1)
 | 
				
			||||||
 | 
					    idxs = y2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # if probabilities are provided, sort on them instead
 | 
				
			||||||
 | 
					    if probs is not None:
 | 
				
			||||||
 | 
					        idxs = probs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # sort the indexes
 | 
				
			||||||
 | 
					    idxs = np.argsort(idxs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # keep looping while some indexes still remain in the indexes list
 | 
				
			||||||
 | 
					    while len(idxs) > 0:
 | 
				
			||||||
 | 
					        # grab the last index in the indexes list and add the index value to the list of picked indexes
 | 
				
			||||||
 | 
					        last = len(idxs) - 1
 | 
				
			||||||
 | 
					        i = idxs[last]
 | 
				
			||||||
 | 
					        pick.append(i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # find the largest (x, y) coordinates for the start of the bounding box and the smallest (x, y) coordinates
 | 
				
			||||||
 | 
					        # for the end of the bounding box
 | 
				
			||||||
 | 
					        xx1 = np.maximum(x1[i], x1[idxs[:last]])
 | 
				
			||||||
 | 
					        yy1 = np.maximum(y1[i], y1[idxs[:last]])
 | 
				
			||||||
 | 
					        xx2 = np.minimum(x2[i], x2[idxs[:last]])
 | 
				
			||||||
 | 
					        yy2 = np.minimum(y2[i], y2[idxs[:last]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # compute the width and height of the bounding box
 | 
				
			||||||
 | 
					        w = np.maximum(0, xx2 - xx1 + 1)
 | 
				
			||||||
 | 
					        h = np.maximum(0, yy2 - yy1 + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # compute the ratio of overlap
 | 
				
			||||||
 | 
					        overlap = (w * h) / area[idxs[:last]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # delete all indexes from the index list that have overlap greater than the provided overlap threshold
 | 
				
			||||||
 | 
					        idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlapThresh)[0])))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # return only the bounding boxes that were picked
 | 
				
			||||||
 | 
					    return boxes[pick].astype("int"), angles[pick]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def decode_predictions(scores, geometry1, geometry2):
 | 
				
			||||||
 | 
					    # grab the number of rows and columns from the scores volume, then
 | 
				
			||||||
 | 
					    # initialize our set of bounding box rectangles and corresponding
 | 
				
			||||||
 | 
					    # confidence scores
 | 
				
			||||||
 | 
					    (numRows, numCols) = scores.shape[2:4]
 | 
				
			||||||
 | 
					    rects = []
 | 
				
			||||||
 | 
					    confidences = []
 | 
				
			||||||
 | 
					    angles = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # loop over the number of rows
 | 
				
			||||||
 | 
					    for y in range(0, numRows):
 | 
				
			||||||
 | 
					        # extract the scores (probabilities), followed by the
 | 
				
			||||||
 | 
					        # geometrical data used to derive potential bounding box
 | 
				
			||||||
 | 
					        # coordinates that surround text
 | 
				
			||||||
 | 
					        scoresData = scores[0, 0, y]
 | 
				
			||||||
 | 
					        xData0 = geometry1[0, 0, y]
 | 
				
			||||||
 | 
					        xData1 = geometry1[0, 1, y]
 | 
				
			||||||
 | 
					        xData2 = geometry1[0, 2, y]
 | 
				
			||||||
 | 
					        xData3 = geometry1[0, 3, y]
 | 
				
			||||||
 | 
					        anglesData = geometry2[0, 0, y]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # loop over the number of columns
 | 
				
			||||||
 | 
					        for x in range(0, numCols):
 | 
				
			||||||
 | 
					            # if our score does not have sufficient probability,
 | 
				
			||||||
 | 
					            # ignore it
 | 
				
			||||||
 | 
					            if scoresData[x] < _conf_threshold:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # compute the offset factor as our resulting feature
 | 
				
			||||||
 | 
					            # maps will be 4x smaller than the input image
 | 
				
			||||||
 | 
					            (offsetX, offsetY) = (x * 4.0, y * 4.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # extract the rotation angle for the prediction and
 | 
				
			||||||
 | 
					            # then compute the sin and cosine
 | 
				
			||||||
 | 
					            angle = anglesData[x]
 | 
				
			||||||
 | 
					            cos = np.cos(angle)
 | 
				
			||||||
 | 
					            sin = np.sin(angle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # use the geometry volume to derive the width and height
 | 
				
			||||||
 | 
					            # of the bounding box
 | 
				
			||||||
 | 
					            h = xData0[x] + xData2[x]
 | 
				
			||||||
 | 
					            w = xData1[x] + xData3[x]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # compute both the starting and ending (x, y)-coordinates
 | 
				
			||||||
 | 
					            # for the text prediction bounding box
 | 
				
			||||||
 | 
					            endX = int(offsetX + (cos * xData1[x]) + (sin * xData2[x]))
 | 
				
			||||||
 | 
					            endY = int(offsetY - (sin * xData1[x]) + (cos * xData2[x]))
 | 
				
			||||||
 | 
					            startX = int(endX - w)
 | 
				
			||||||
 | 
					            startY = int(endY - h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # add the bounding box coordinates and probability score
 | 
				
			||||||
 | 
					            # to our respective lists
 | 
				
			||||||
 | 
					            rects.append((startX, startY, endX, endY))
 | 
				
			||||||
 | 
					            confidences.append(scoresData[x])
 | 
				
			||||||
 | 
					            angles.append(angle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # return a tuple of the bounding boxes and associated confidences
 | 
				
			||||||
 | 
					    return (rects, confidences, angles)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def decode_east(nnet_packet, **kwargs):
 | 
				
			||||||
 | 
					    scores = nnet_packet.get_tensor(0)
 | 
				
			||||||
 | 
					    geometry1 = nnet_packet.get_tensor(1)
 | 
				
			||||||
 | 
					    geometry2 = nnet_packet.get_tensor(2)
 | 
				
			||||||
 | 
					    bboxes, confs, angles = decode_predictions(scores, geometry1, geometry2
 | 
				
			||||||
 | 
					                                               )
 | 
				
			||||||
 | 
					    boxes, angles = non_max_suppression(np.array(bboxes), probs=confs, angles=np.array(angles))
 | 
				
			||||||
 | 
					    boxesangles = (boxes, angles)
 | 
				
			||||||
 | 
					    return boxesangles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def show_east(boxesangles, frame, **kwargs):
 | 
				
			||||||
 | 
					    bboxes = boxesangles[0]
 | 
				
			||||||
 | 
					    angles = boxesangles[1]
 | 
				
			||||||
 | 
					    for ((X0, Y0, X1, Y1), angle) in zip(bboxes, angles):
 | 
				
			||||||
 | 
					        width = abs(X0 - X1)
 | 
				
			||||||
 | 
					        height = abs(Y0 - Y1)
 | 
				
			||||||
 | 
					        cX = int(X0 + width * 0.5)
 | 
				
			||||||
 | 
					        cY = int(Y0 + height * 0.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rotRect = ((cX, cY), ((X1 - X0), (Y1 - Y0)), angle * (-1))
 | 
				
			||||||
 | 
					        points = rotated_Rectangle(frame, rotRect, color=(255, 0, 0), thickness=1)
 | 
				
			||||||
 | 
					        cv2.polylines(frame, [points], isClosed=True, color=(255, 0, 0), thickness=1, lineType=cv2.LINE_8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return frame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def order_points(pts):
 | 
				
			||||||
 | 
					    rect = np.zeros((4, 2), dtype="float32")
 | 
				
			||||||
 | 
					    s = pts.sum(axis=1)
 | 
				
			||||||
 | 
					    rect[0] = pts[np.argmin(s)]
 | 
				
			||||||
 | 
					    rect[2] = pts[np.argmax(s)]
 | 
				
			||||||
 | 
					    diff = np.diff(pts, axis=1)
 | 
				
			||||||
 | 
					    rect[1] = pts[np.argmin(diff)]
 | 
				
			||||||
 | 
					    rect[3] = pts[np.argmax(diff)]
 | 
				
			||||||
 | 
					    return rect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def four_point_transform(image, pts):
 | 
				
			||||||
 | 
					    rect = order_points(pts)
 | 
				
			||||||
 | 
					    (tl, tr, br, bl) = rect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
 | 
				
			||||||
 | 
					    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
 | 
				
			||||||
 | 
					    maxWidth = max(int(widthA), int(widthB))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
 | 
				
			||||||
 | 
					    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
 | 
				
			||||||
 | 
					    maxHeight = max(int(heightA), int(heightB))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dst = np.array([
 | 
				
			||||||
 | 
					        [0, 0],
 | 
				
			||||||
 | 
					        [maxWidth - 1, 0],
 | 
				
			||||||
 | 
					        [maxWidth - 1, maxHeight - 1],
 | 
				
			||||||
 | 
					        [0, maxHeight - 1]], dtype="float32")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    M = cv2.getPerspectiveTransform(rect, dst)
 | 
				
			||||||
 | 
					    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return warped
 | 
				
			||||||
@@ -1,150 +0,0 @@
 | 
				
			|||||||
import datetime
 | 
					 | 
				
			||||||
import unittest.mock
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import depthai as dai
 | 
					 | 
				
			||||||
import numpy as np
 | 
					 | 
				
			||||||
import paho.mqtt.client as mqtt
 | 
					 | 
				
			||||||
import pytest
 | 
					 | 
				
			||||||
import pytest_mock
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import camera.depthai
 | 
					 | 
				
			||||||
import events.events_pb2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Object = dict[str, float]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@pytest.fixture
 | 
					 | 
				
			||||||
def mqtt_client(mocker: pytest_mock.MockerFixture) -> mqtt.Client:
 | 
					 | 
				
			||||||
    return mocker.MagicMock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TestObjectProcessor:
 | 
					 | 
				
			||||||
    @pytest.fixture
 | 
					 | 
				
			||||||
    def frame_ref(self):
 | 
					 | 
				
			||||||
        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):
 | 
					 | 
				
			||||||
            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):
 | 
					 | 
				
			||||||
            if name == "ExpandDims":  # Detection boxes
 | 
					 | 
				
			||||||
                boxes = [[0] * 4] * 100
 | 
					 | 
				
			||||||
                boxes[0] = [object1["top"], object1["left"], object1["bottom"], object1["right"]]
 | 
					 | 
				
			||||||
                return np.array(boxes)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            elif name == "ExpandDims_2":  # Detection scores
 | 
					 | 
				
			||||||
                scores = [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) -> camera.depthai.ObjectProcessor:
 | 
					 | 
				
			||||||
        return camera.depthai.ObjectProcessor(mqtt_client, "topic/object", 0.2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_process_without_object(self, object_processor: camera.depthai.ObjectProcessor, mqtt_client,
 | 
					 | 
				
			||||||
                                    raw_objects_empty, frame_ref):
 | 
					 | 
				
			||||||
        object_processor.process(raw_objects_empty, frame_ref)
 | 
					 | 
				
			||||||
        mqtt_client.publish.assert_not_called()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_process_with_object_with_low_score(self, object_processor: camera.depthai.ObjectProcessor, mqtt_client,
 | 
					 | 
				
			||||||
                                                raw_objects_one, frame_ref):
 | 
					 | 
				
			||||||
        object_processor._objects_threshold = 0.9
 | 
					 | 
				
			||||||
        object_processor.process(raw_objects_one, frame_ref)
 | 
					 | 
				
			||||||
        mqtt_client.publish.assert_not_called()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_process_with_one_object(self,
 | 
					 | 
				
			||||||
                                     object_processor: camera.depthai.ObjectProcessor, mqtt_client,
 | 
					 | 
				
			||||||
                                     raw_objects_one, frame_ref, object1: Object):
 | 
					 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
        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) -> camera.depthai.FrameProcessor:
 | 
					 | 
				
			||||||
        return camera.depthai.FrameProcessor(mqtt_client, "topic/frame")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_process(self, frame_processor: camera.depthai.FrameProcessor, mocker: pytest_mock.MockerFixture,
 | 
					 | 
				
			||||||
                     mqtt_client: mqtt.Client):
 | 
					 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
        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: camera.depthai.FrameProcessor, mocker: pytest_mock.MockerFixture,
 | 
					 | 
				
			||||||
                           mqtt_client: mqtt.Client):
 | 
					 | 
				
			||||||
        img: dai.ImgFrame = mocker.MagicMock()
 | 
					 | 
				
			||||||
        mocker.patch(target="cv2.imencode").return_value = (False, None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        with pytest.raises(camera.depthai.FrameProcessError) as ex:
 | 
					 | 
				
			||||||
            _ = frame_processor.process(img)
 | 
					 | 
				
			||||||
        exception_raised = ex.value
 | 
					 | 
				
			||||||
        assert exception_raised.message == "unable to process to encode frame to jpg"
 | 
					 | 
				
			||||||
							
								
								
									
										61
									
								
								camera/text.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								camera/text.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					class HostSeqSync:
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.imfFrames = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_msg(self, msg):
 | 
				
			||||||
 | 
					        self.imfFrames.append(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_msg(self, target_seq):
 | 
				
			||||||
 | 
					        for i, imgFrame in enumerate(self.imfFrames):
 | 
				
			||||||
 | 
					            if target_seq == imgFrame.getSequenceNum():
 | 
				
			||||||
 | 
					                self.imfFrames = self.imfFrames[i:]
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					        return self.imfFrames[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CTCCodec(object):
 | 
				
			||||||
 | 
					    """ Convert between text-label and text-index """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, characters):
 | 
				
			||||||
 | 
					        # characters (str): set of the possible characters.
 | 
				
			||||||
 | 
					        dict_character = list(characters)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.dict = {}
 | 
				
			||||||
 | 
					        for i, char in enumerate(dict_character):
 | 
				
			||||||
 | 
					            self.dict[char] = i + 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.characters = dict_character
 | 
				
			||||||
 | 
					        # print(self.characters)
 | 
				
			||||||
 | 
					        # input()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def decode(self, preds):
 | 
				
			||||||
 | 
					        """ convert text-index into text-label. """
 | 
				
			||||||
 | 
					        texts = []
 | 
				
			||||||
 | 
					        index = 0
 | 
				
			||||||
 | 
					        # Select max probabilty (greedy decoding) then decode index to character
 | 
				
			||||||
 | 
					        preds = preds.astype(np.float16)
 | 
				
			||||||
 | 
					        preds_index = np.argmax(preds, 2)
 | 
				
			||||||
 | 
					        preds_index = preds_index.transpose(1, 0)
 | 
				
			||||||
 | 
					        preds_index_reshape = preds_index.reshape(-1)
 | 
				
			||||||
 | 
					        preds_sizes = np.array([preds_index.shape[1]] * preds_index.shape[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for l in preds_sizes:
 | 
				
			||||||
 | 
					            t = preds_index_reshape[index:index + l]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # NOTE: t might be zero size
 | 
				
			||||||
 | 
					            if t.shape[0] == 0:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            char_list = []
 | 
				
			||||||
 | 
					            for i in range(l):
 | 
				
			||||||
 | 
					                # removing repeated characters and blank.
 | 
				
			||||||
 | 
					                if not (i > 0 and t[i - 1] == t[i]):
 | 
				
			||||||
 | 
					                    if self.characters[t[i]] != '#':
 | 
				
			||||||
 | 
					                        char_list.append(self.characters[t[i]])
 | 
				
			||||||
 | 
					            text = ''.join(char_list)
 | 
				
			||||||
 | 
					            texts.append(text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            index += l
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return texts
 | 
				
			||||||
							
								
								
									
										229
									
								
								east.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								east.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,229 @@
 | 
				
			|||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					import depthai
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					_conf_threshold = 0.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_cv_rotated_rect(bbox, angle):
 | 
				
			||||||
 | 
					    x0, y0, x1, y1 = bbox
 | 
				
			||||||
 | 
					    width = abs(x0 - x1)
 | 
				
			||||||
 | 
					    height = abs(y0 - y1)
 | 
				
			||||||
 | 
					    x = x0 + width * 0.5
 | 
				
			||||||
 | 
					    y = y0 + height * 0.5
 | 
				
			||||||
 | 
					    return ([x.tolist(), y.tolist()], [width.tolist(), height.tolist()], np.rad2deg(angle))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def rotated_Rectangle(bbox, angle):
 | 
				
			||||||
 | 
					    X0, Y0, X1, Y1 = bbox
 | 
				
			||||||
 | 
					    width = abs(X0 - X1)
 | 
				
			||||||
 | 
					    height = abs(Y0 - Y1)
 | 
				
			||||||
 | 
					    x = int(X0 + width * 0.5)
 | 
				
			||||||
 | 
					    y = int(Y0 + height * 0.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pt1_1 = (int(x + width / 2), int(y + height / 2))
 | 
				
			||||||
 | 
					    pt2_1 = (int(x + width / 2), int(y - height / 2))
 | 
				
			||||||
 | 
					    pt3_1 = (int(x - width / 2), int(y - height / 2))
 | 
				
			||||||
 | 
					    pt4_1 = (int(x - width / 2), int(y + height / 2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    t = np.array([[np.cos(angle), -np.sin(angle), x - x * np.cos(angle) + y * np.sin(angle)],
 | 
				
			||||||
 | 
					                  [np.sin(angle), np.cos(angle), y - x * np.sin(angle) - y * np.cos(angle)],
 | 
				
			||||||
 | 
					                  [0, 0, 1]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tmp_pt1_1 = np.array([[pt1_1[0]], [pt1_1[1]], [1]])
 | 
				
			||||||
 | 
					    tmp_pt1_2 = np.dot(t, tmp_pt1_1)
 | 
				
			||||||
 | 
					    pt1_2 = (int(tmp_pt1_2[0][0]), int(tmp_pt1_2[1][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tmp_pt2_1 = np.array([[pt2_1[0]], [pt2_1[1]], [1]])
 | 
				
			||||||
 | 
					    tmp_pt2_2 = np.dot(t, tmp_pt2_1)
 | 
				
			||||||
 | 
					    pt2_2 = (int(tmp_pt2_2[0][0]), int(tmp_pt2_2[1][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tmp_pt3_1 = np.array([[pt3_1[0]], [pt3_1[1]], [1]])
 | 
				
			||||||
 | 
					    tmp_pt3_2 = np.dot(t, tmp_pt3_1)
 | 
				
			||||||
 | 
					    pt3_2 = (int(tmp_pt3_2[0][0]), int(tmp_pt3_2[1][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tmp_pt4_1 = np.array([[pt4_1[0]], [pt4_1[1]], [1]])
 | 
				
			||||||
 | 
					    tmp_pt4_2 = np.dot(t, tmp_pt4_1)
 | 
				
			||||||
 | 
					    pt4_2 = (int(tmp_pt4_2[0][0]), int(tmp_pt4_2[1][0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    points = np.array([pt1_2, pt2_2, pt3_2, pt4_2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return points
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def non_max_suppression(boxes, probs=None, angles=None, overlapThresh=0.3):
 | 
				
			||||||
 | 
					    # if there are no boxes, return an empty list
 | 
				
			||||||
 | 
					    if len(boxes) == 0:
 | 
				
			||||||
 | 
					        return [], []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # if the bounding boxes are integers, convert them to floats -- this
 | 
				
			||||||
 | 
					    # is important since we'll be doing a bunch of divisions
 | 
				
			||||||
 | 
					    if boxes.dtype.kind == "i":
 | 
				
			||||||
 | 
					        boxes = boxes.astype("float")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # initialize the list of picked indexes
 | 
				
			||||||
 | 
					    pick = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # grab the coordinates of the bounding boxes
 | 
				
			||||||
 | 
					    x1 = boxes[:, 0]
 | 
				
			||||||
 | 
					    y1 = boxes[:, 1]
 | 
				
			||||||
 | 
					    x2 = boxes[:, 2]
 | 
				
			||||||
 | 
					    y2 = boxes[:, 3]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # compute the area of the bounding boxes and grab the indexes to sort
 | 
				
			||||||
 | 
					    # (in the case that no probabilities are provided, simply sort on the bottom-left y-coordinate)
 | 
				
			||||||
 | 
					    area = (x2 - x1 + 1) * (y2 - y1 + 1)
 | 
				
			||||||
 | 
					    idxs = y2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # if probabilities are provided, sort on them instead
 | 
				
			||||||
 | 
					    if probs is not None:
 | 
				
			||||||
 | 
					        idxs = probs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # sort the indexes
 | 
				
			||||||
 | 
					    idxs = np.argsort(idxs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # keep looping while some indexes still remain in the indexes list
 | 
				
			||||||
 | 
					    while len(idxs) > 0:
 | 
				
			||||||
 | 
					        # grab the last index in the indexes list and add the index value to the list of picked indexes
 | 
				
			||||||
 | 
					        last = len(idxs) - 1
 | 
				
			||||||
 | 
					        i = idxs[last]
 | 
				
			||||||
 | 
					        pick.append(i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # find the largest (x, y) coordinates for the start of the bounding box and the smallest (x, y) coordinates for the end of the bounding box
 | 
				
			||||||
 | 
					        xx1 = np.maximum(x1[i], x1[idxs[:last]])
 | 
				
			||||||
 | 
					        yy1 = np.maximum(y1[i], y1[idxs[:last]])
 | 
				
			||||||
 | 
					        xx2 = np.minimum(x2[i], x2[idxs[:last]])
 | 
				
			||||||
 | 
					        yy2 = np.minimum(y2[i], y2[idxs[:last]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # compute the width and height of the bounding box
 | 
				
			||||||
 | 
					        w = np.maximum(0, xx2 - xx1 + 1)
 | 
				
			||||||
 | 
					        h = np.maximum(0, yy2 - yy1 + 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # compute the ratio of overlap
 | 
				
			||||||
 | 
					        overlap = (w * h) / area[idxs[:last]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # delete all indexes from the index list that have overlap greater than the provided overlap threshold
 | 
				
			||||||
 | 
					        idxs = np.delete(idxs, np.concatenate(([last], np.where(overlap > overlapThresh)[0])))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # return only the bounding boxes that were picked
 | 
				
			||||||
 | 
					    return boxes[pick].astype("int"), angles[pick]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def decode_predictions(scores, geometry1, geometry2):
 | 
				
			||||||
 | 
					    # grab the number of rows and columns from the scores volume, then
 | 
				
			||||||
 | 
					    # initialize our set of bounding box rectangles and corresponding
 | 
				
			||||||
 | 
					    # confidence scores
 | 
				
			||||||
 | 
					    (numRows, numCols) = scores.shape[2:4]
 | 
				
			||||||
 | 
					    rects = []
 | 
				
			||||||
 | 
					    confidences = []
 | 
				
			||||||
 | 
					    angles = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # loop over the number of rows
 | 
				
			||||||
 | 
					    for y in range(0, numRows):
 | 
				
			||||||
 | 
					        # extract the scores (probabilities), followed by the
 | 
				
			||||||
 | 
					        # geometrical data used to derive potential bounding box
 | 
				
			||||||
 | 
					        # coordinates that surround text
 | 
				
			||||||
 | 
					        scoresData = scores[0, 0, y]
 | 
				
			||||||
 | 
					        xData0 = geometry1[0, 0, y]
 | 
				
			||||||
 | 
					        xData1 = geometry1[0, 1, y]
 | 
				
			||||||
 | 
					        xData2 = geometry1[0, 2, y]
 | 
				
			||||||
 | 
					        xData3 = geometry1[0, 3, y]
 | 
				
			||||||
 | 
					        anglesData = geometry2[0, 0, y]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # loop over the number of columns
 | 
				
			||||||
 | 
					        for x in range(0, numCols):
 | 
				
			||||||
 | 
					            # if our score does not have sufficient probability,
 | 
				
			||||||
 | 
					            # ignore it
 | 
				
			||||||
 | 
					            if scoresData[x] < _conf_threshold:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # compute the offset factor as our resulting feature
 | 
				
			||||||
 | 
					            # maps will be 4x smaller than the input image
 | 
				
			||||||
 | 
					            (offsetX, offsetY) = (x * 4.0, y * 4.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # extract the rotation angle for the prediction and
 | 
				
			||||||
 | 
					            # then compute the sin and cosine
 | 
				
			||||||
 | 
					            angle = anglesData[x]
 | 
				
			||||||
 | 
					            cos = np.cos(angle)
 | 
				
			||||||
 | 
					            sin = np.sin(angle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # use the geometry volume to derive the width and height
 | 
				
			||||||
 | 
					            # of the bounding box
 | 
				
			||||||
 | 
					            h = xData0[x] + xData2[x]
 | 
				
			||||||
 | 
					            w = xData1[x] + xData3[x]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # compute both the starting and ending (x, y)-coordinates
 | 
				
			||||||
 | 
					            # for the text prediction bounding box
 | 
				
			||||||
 | 
					            endX = int(offsetX + (cos * xData1[x]) + (sin * xData2[x]))
 | 
				
			||||||
 | 
					            endY = int(offsetY - (sin * xData1[x]) + (cos * xData2[x]))
 | 
				
			||||||
 | 
					            startX = int(endX - w)
 | 
				
			||||||
 | 
					            startY = int(endY - h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # add the bounding box coordinates and probability score
 | 
				
			||||||
 | 
					            # to our respective lists
 | 
				
			||||||
 | 
					            rects.append((startX, startY, endX, endY))
 | 
				
			||||||
 | 
					            confidences.append(scoresData[x])
 | 
				
			||||||
 | 
					            angles.append(angle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # return a tuple of the bounding boxes and associated confidences
 | 
				
			||||||
 | 
					    return (rects, confidences, angles)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def decode_east(nnet_packet, **kwargs):
 | 
				
			||||||
 | 
					    scores = nnet_packet.get_tensor(0)
 | 
				
			||||||
 | 
					    geometry1 = nnet_packet.get_tensor(1)
 | 
				
			||||||
 | 
					    geometry2 = nnet_packet.get_tensor(2)
 | 
				
			||||||
 | 
					    bboxes, confs, angles = decode_predictions(scores, geometry1, geometry2
 | 
				
			||||||
 | 
					                                               )
 | 
				
			||||||
 | 
					    boxes, angles = non_max_suppression(np.array(bboxes), probs=confs, angles=np.array(angles))
 | 
				
			||||||
 | 
					    boxesangles = (boxes, angles)
 | 
				
			||||||
 | 
					    return boxesangles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def show_east(boxesangles, frame, **kwargs):
 | 
				
			||||||
 | 
					    bboxes = boxesangles[0]
 | 
				
			||||||
 | 
					    angles = boxesangles[1]
 | 
				
			||||||
 | 
					    for ((X0, Y0, X1, Y1), angle) in zip(bboxes, angles):
 | 
				
			||||||
 | 
					        width = abs(X0 - X1)
 | 
				
			||||||
 | 
					        height = abs(Y0 - Y1)
 | 
				
			||||||
 | 
					        cX = int(X0 + width * 0.5)
 | 
				
			||||||
 | 
					        cY = int(Y0 + height * 0.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rotRect = ((cX, cY), ((X1 - X0), (Y1 - Y0)), angle * (-1))
 | 
				
			||||||
 | 
					        points = rotated_Rectangle(frame, rotRect, color=(255, 0, 0), thickness=1)
 | 
				
			||||||
 | 
					        cv2.polylines(frame, [points], isClosed=True, color=(255, 0, 0), thickness=1, lineType=cv2.LINE_8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return frame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def order_points(pts):
 | 
				
			||||||
 | 
					    rect = np.zeros((4, 2), dtype="float32")
 | 
				
			||||||
 | 
					    s = pts.sum(axis=1)
 | 
				
			||||||
 | 
					    rect[0] = pts[np.argmin(s)]
 | 
				
			||||||
 | 
					    rect[2] = pts[np.argmax(s)]
 | 
				
			||||||
 | 
					    diff = np.diff(pts, axis=1)
 | 
				
			||||||
 | 
					    rect[1] = pts[np.argmin(diff)]
 | 
				
			||||||
 | 
					    rect[3] = pts[np.argmax(diff)]
 | 
				
			||||||
 | 
					    return rect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def four_point_transform(image, pts):
 | 
				
			||||||
 | 
					    rect = order_points(pts)
 | 
				
			||||||
 | 
					    (tl, tr, br, bl) = rect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
 | 
				
			||||||
 | 
					    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
 | 
				
			||||||
 | 
					    maxWidth = max(int(widthA), int(widthB))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
 | 
				
			||||||
 | 
					    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
 | 
				
			||||||
 | 
					    maxHeight = max(int(heightA), int(heightB))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dst = np.array([
 | 
				
			||||||
 | 
					        [0, 0],
 | 
				
			||||||
 | 
					        [maxWidth - 1, 0],
 | 
				
			||||||
 | 
					        [maxWidth - 1, maxHeight - 1],
 | 
				
			||||||
 | 
					        [0, maxHeight - 1]], dtype="float32")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    M = cv2.getPerspectiveTransform(rect, dst)
 | 
				
			||||||
 | 
					    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return warped
 | 
				
			||||||
@@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
 | 
				
			|||||||
from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
 | 
					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(\x02\x12\x0b\n\x03top\x18\x03 \x01(\x02\x12\r\n\x05right\x18\x04 \x01(\x02\x12\x0e\n\x06\x62ottom\x18\x05 \x01(\x02\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')
 | 
					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.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
 | 
				
			||||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'events.events_pb2', globals())
 | 
					_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'events.events_pb2', globals())
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										274
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								main.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,274 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cv2
 | 
				
			||||||
 | 
					import numpy as np
 | 
				
			||||||
 | 
					import depthai as dai
 | 
				
			||||||
 | 
					import east
 | 
				
			||||||
 | 
					import blobconverter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HostSeqSync:
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.imfFrames = []
 | 
				
			||||||
 | 
					    def add_msg(self, msg):
 | 
				
			||||||
 | 
					        self.imfFrames.append(msg)
 | 
				
			||||||
 | 
					    def get_msg(self, target_seq):
 | 
				
			||||||
 | 
					        for i, imgFrame in enumerate(self.imfFrames):
 | 
				
			||||||
 | 
					            if target_seq == imgFrame.getSequenceNum():
 | 
				
			||||||
 | 
					                self.imfFrames = self.imfFrames[i:]
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					        return self.imfFrames[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pipeline = dai.Pipeline()
 | 
				
			||||||
 | 
					version = "2021.2"
 | 
				
			||||||
 | 
					pipeline.setOpenVINOVersion(version=dai.OpenVINO.Version.VERSION_2021_2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					colorCam = pipeline.create(dai.node.ColorCamera)
 | 
				
			||||||
 | 
					colorCam.setPreviewSize(256, 256)
 | 
				
			||||||
 | 
					colorCam.setVideoSize(1024, 1024) # 4 times larger in both axis
 | 
				
			||||||
 | 
					colorCam.setResolution(dai.ColorCameraProperties.SensorResolution.THE_1080_P)
 | 
				
			||||||
 | 
					colorCam.setInterleaved(False)
 | 
				
			||||||
 | 
					colorCam.setBoardSocket(dai.CameraBoardSocket.RGB)
 | 
				
			||||||
 | 
					colorCam.setFps(10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					controlIn = pipeline.create(dai.node.XLinkIn)
 | 
				
			||||||
 | 
					controlIn.setStreamName('control')
 | 
				
			||||||
 | 
					controlIn.out.link(colorCam.inputControl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cam_xout = pipeline.create(dai.node.XLinkOut)
 | 
				
			||||||
 | 
					cam_xout.setStreamName('video')
 | 
				
			||||||
 | 
					colorCam.video.link(cam_xout.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ---------------------------------------
 | 
				
			||||||
 | 
					# 1st stage NN - text-detection
 | 
				
			||||||
 | 
					# ---------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nn = pipeline.create(dai.node.NeuralNetwork)
 | 
				
			||||||
 | 
					nn.setBlobPath(blobconverter.from_zoo(name="east_text_detection_256x256",zoo_type="depthai",shaves=6, version=version))
 | 
				
			||||||
 | 
					colorCam.preview.link(nn.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nn_xout = pipeline.create(dai.node.XLinkOut)
 | 
				
			||||||
 | 
					nn_xout.setStreamName('detections')
 | 
				
			||||||
 | 
					nn.out.link(nn_xout.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ---------------------------------------
 | 
				
			||||||
 | 
					# 2nd stage NN - text-recognition-0012
 | 
				
			||||||
 | 
					# ---------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					manip = pipeline.create(dai.node.ImageManip)
 | 
				
			||||||
 | 
					manip.setWaitForConfigInput(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					manip_img = pipeline.create(dai.node.XLinkIn)
 | 
				
			||||||
 | 
					manip_img.setStreamName('manip_img')
 | 
				
			||||||
 | 
					manip_img.out.link(manip.inputImage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					manip_cfg = pipeline.create(dai.node.XLinkIn)
 | 
				
			||||||
 | 
					manip_cfg.setStreamName('manip_cfg')
 | 
				
			||||||
 | 
					manip_cfg.out.link(manip.inputConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					manip_xout = pipeline.create(dai.node.XLinkOut)
 | 
				
			||||||
 | 
					manip_xout.setStreamName('manip_out')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nn2 = pipeline.create(dai.node.NeuralNetwork)
 | 
				
			||||||
 | 
					nn2.setBlobPath(blobconverter.from_zoo(name="text-recognition-0012", shaves=6, version=version))
 | 
				
			||||||
 | 
					nn2.setNumInferenceThreads(2)
 | 
				
			||||||
 | 
					manip.out.link(nn2.input)
 | 
				
			||||||
 | 
					manip.out.link(manip_xout.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nn2_xout = pipeline.create(dai.node.XLinkOut)
 | 
				
			||||||
 | 
					nn2_xout.setStreamName("recognitions")
 | 
				
			||||||
 | 
					nn2.out.link(nn2_xout.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def to_tensor_result(packet):
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        name: np.array(packet.getLayerFp16(name))
 | 
				
			||||||
 | 
					        for name in [tensor.name for tensor in packet.getRaw().tensors]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def to_planar(frame):
 | 
				
			||||||
 | 
					    return frame.transpose(2, 0, 1).flatten()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					with dai.Device(pipeline) as device:
 | 
				
			||||||
 | 
					    q_vid = device.getOutputQueue("video", 4, blocking=False)
 | 
				
			||||||
 | 
					    # This should be set to block, but would get to some extreme queuing/latency!
 | 
				
			||||||
 | 
					    q_det = device.getOutputQueue("detections", 4, blocking=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    q_rec = device.getOutputQueue("recognitions", 4, blocking=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    q_manip_img = device.getInputQueue("manip_img")
 | 
				
			||||||
 | 
					    q_manip_cfg = device.getInputQueue("manip_cfg")
 | 
				
			||||||
 | 
					    q_manip_out = device.getOutputQueue("manip_out", 4, blocking=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    controlQueue = device.getInputQueue('control')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    frame = None
 | 
				
			||||||
 | 
					    cropped_stacked = None
 | 
				
			||||||
 | 
					    rotated_rectangles = []
 | 
				
			||||||
 | 
					    rec_pushed = 0
 | 
				
			||||||
 | 
					    rec_received = 0
 | 
				
			||||||
 | 
					    host_sync = HostSeqSync()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class CTCCodec(object):
 | 
				
			||||||
 | 
					        """ Convert between text-label and text-index """
 | 
				
			||||||
 | 
					        def __init__(self, characters):
 | 
				
			||||||
 | 
					            # characters (str): set of the possible characters.
 | 
				
			||||||
 | 
					            dict_character = list(characters)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.dict = {}
 | 
				
			||||||
 | 
					            for i, char in enumerate(dict_character):
 | 
				
			||||||
 | 
					                self.dict[char] = i + 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.characters = dict_character
 | 
				
			||||||
 | 
					            #print(self.characters)
 | 
				
			||||||
 | 
					            #input()
 | 
				
			||||||
 | 
					        def decode(self, preds):
 | 
				
			||||||
 | 
					            """ convert text-index into text-label. """
 | 
				
			||||||
 | 
					            texts = []
 | 
				
			||||||
 | 
					            index = 0
 | 
				
			||||||
 | 
					            # Select max probabilty (greedy decoding) then decode index to character
 | 
				
			||||||
 | 
					            preds = preds.astype(np.float16)
 | 
				
			||||||
 | 
					            preds_index = np.argmax(preds, 2)
 | 
				
			||||||
 | 
					            preds_index = preds_index.transpose(1, 0)
 | 
				
			||||||
 | 
					            preds_index_reshape = preds_index.reshape(-1)
 | 
				
			||||||
 | 
					            preds_sizes = np.array([preds_index.shape[1]] * preds_index.shape[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for l in preds_sizes:
 | 
				
			||||||
 | 
					                t = preds_index_reshape[index:index + l]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # NOTE: t might be zero size
 | 
				
			||||||
 | 
					                if t.shape[0] == 0:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                char_list = []
 | 
				
			||||||
 | 
					                for i in range(l):
 | 
				
			||||||
 | 
					                    # removing repeated characters and blank.
 | 
				
			||||||
 | 
					                    if not (i > 0 and t[i - 1] == t[i]):
 | 
				
			||||||
 | 
					                        if self.characters[t[i]] != '#':
 | 
				
			||||||
 | 
					                            char_list.append(self.characters[t[i]])
 | 
				
			||||||
 | 
					                text = ''.join(char_list)
 | 
				
			||||||
 | 
					                texts.append(text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                index += l
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return texts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    characters = '0123456789abcdefghijklmnopqrstuvwxyz#'
 | 
				
			||||||
 | 
					    codec = CTCCodec(characters)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctrl = dai.CameraControl()
 | 
				
			||||||
 | 
					    ctrl.setAutoFocusMode(dai.CameraControl.AutoFocusMode.CONTINUOUS_VIDEO)
 | 
				
			||||||
 | 
					    ctrl.setAutoFocusTrigger()
 | 
				
			||||||
 | 
					    controlQueue.send(ctrl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        vid_in = q_vid.tryGet()
 | 
				
			||||||
 | 
					        if vid_in is not None:
 | 
				
			||||||
 | 
					            host_sync.add_msg(vid_in)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Multiple recognition results may be available, read until queue is empty
 | 
				
			||||||
 | 
					        while True:
 | 
				
			||||||
 | 
					            in_rec = q_rec.tryGet()
 | 
				
			||||||
 | 
					            if in_rec is None:
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            rec_data = bboxes = np.array(in_rec.getFirstLayerFp16()).reshape(30,1,37)
 | 
				
			||||||
 | 
					            decoded_text = codec.decode(rec_data)[0]
 | 
				
			||||||
 | 
					            pos = rotated_rectangles[rec_received]
 | 
				
			||||||
 | 
					            print("{:2}: {:20}".format(rec_received, decoded_text),
 | 
				
			||||||
 | 
					                "center({:3},{:3}) size({:3},{:3}) angle{:5.1f} deg".format(
 | 
				
			||||||
 | 
					                    int(pos[0][0]), int(pos[0][1]), pos[1][0], pos[1][1], pos[2]))
 | 
				
			||||||
 | 
					            # Draw the text on the right side of 'cropped_stacked' - placeholder
 | 
				
			||||||
 | 
					            if cropped_stacked is not None:
 | 
				
			||||||
 | 
					                cv2.putText(cropped_stacked, decoded_text,
 | 
				
			||||||
 | 
					                                (120 + 10 , 32 * rec_received + 24),
 | 
				
			||||||
 | 
					                                cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
 | 
				
			||||||
 | 
					                cv2.imshow('cropped_stacked', cropped_stacked)
 | 
				
			||||||
 | 
					            rec_received += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if cv2.waitKey(1) == ord('q'):
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if rec_received >= rec_pushed:
 | 
				
			||||||
 | 
					            in_det = q_det.tryGet()
 | 
				
			||||||
 | 
					            if in_det is not None:
 | 
				
			||||||
 | 
					                frame = host_sync.get_msg(in_det.getSequenceNum()).getCvFrame().copy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                scores, geom1, geom2 = to_tensor_result(in_det).values()
 | 
				
			||||||
 | 
					                scores = np.reshape(scores, (1, 1, 64, 64))
 | 
				
			||||||
 | 
					                geom1 = np.reshape(geom1, (1, 4, 64, 64))
 | 
				
			||||||
 | 
					                geom2 = np.reshape(geom2, (1, 1, 64, 64))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                bboxes, confs, angles = east.decode_predictions(scores, geom1, geom2)
 | 
				
			||||||
 | 
					                boxes, angles = east.non_max_suppression(np.array(bboxes), probs=confs, angles=np.array(angles))
 | 
				
			||||||
 | 
					                rotated_rectangles = [
 | 
				
			||||||
 | 
					                    east.get_cv_rotated_rect(bbox, angle * -1)
 | 
				
			||||||
 | 
					                    for (bbox, angle) in zip(boxes, angles)
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                rec_received = 0
 | 
				
			||||||
 | 
					                rec_pushed = len(rotated_rectangles)
 | 
				
			||||||
 | 
					                if rec_pushed:
 | 
				
			||||||
 | 
					                    print("====== Pushing for recognition, count:", rec_pushed)
 | 
				
			||||||
 | 
					                cropped_stacked = None
 | 
				
			||||||
 | 
					                for idx, rotated_rect in enumerate(rotated_rectangles):
 | 
				
			||||||
 | 
					                    # Detections are done on 256x256 frames, we are sending back 1024x1024
 | 
				
			||||||
 | 
					                    # That's why we multiply center and size values by 4
 | 
				
			||||||
 | 
					                    rotated_rect[0][0] = rotated_rect[0][0] * 4
 | 
				
			||||||
 | 
					                    rotated_rect[0][1] = rotated_rect[0][1] * 4
 | 
				
			||||||
 | 
					                    rotated_rect[1][0] = rotated_rect[1][0] * 4
 | 
				
			||||||
 | 
					                    rotated_rect[1][1] = rotated_rect[1][1] * 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # Draw detection crop area on input frame
 | 
				
			||||||
 | 
					                    points = np.int0(cv2.boxPoints(rotated_rect))
 | 
				
			||||||
 | 
					                    print(rotated_rect)
 | 
				
			||||||
 | 
					                    cv2.polylines(frame, [points], isClosed=True, color=(255, 0, 0), thickness=1, lineType=cv2.LINE_8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # TODO make it work taking args like in OpenCV:
 | 
				
			||||||
 | 
					                    # rr = ((256, 256), (128, 64), 30)
 | 
				
			||||||
 | 
					                    rr = dai.RotatedRect()
 | 
				
			||||||
 | 
					                    rr.center.x    = rotated_rect[0][0]
 | 
				
			||||||
 | 
					                    rr.center.y    = rotated_rect[0][1]
 | 
				
			||||||
 | 
					                    rr.size.width  = rotated_rect[1][0]
 | 
				
			||||||
 | 
					                    rr.size.height = rotated_rect[1][1]
 | 
				
			||||||
 | 
					                    rr.angle       = rotated_rect[2]
 | 
				
			||||||
 | 
					                    cfg = dai.ImageManipConfig()
 | 
				
			||||||
 | 
					                    cfg.setCropRotatedRect(rr, False)
 | 
				
			||||||
 | 
					                    cfg.setResize(120, 32)
 | 
				
			||||||
 | 
					                    # Send frame and config to device
 | 
				
			||||||
 | 
					                    if idx == 0:
 | 
				
			||||||
 | 
					                        w,h,c = frame.shape
 | 
				
			||||||
 | 
					                        imgFrame = dai.ImgFrame()
 | 
				
			||||||
 | 
					                        imgFrame.setData(to_planar(frame))
 | 
				
			||||||
 | 
					                        imgFrame.setType(dai.ImgFrame.Type.BGR888p)
 | 
				
			||||||
 | 
					                        imgFrame.setWidth(w)
 | 
				
			||||||
 | 
					                        imgFrame.setHeight(h)
 | 
				
			||||||
 | 
					                        q_manip_img.send(imgFrame)
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        cfg.setReusePreviousImage(True)
 | 
				
			||||||
 | 
					                    q_manip_cfg.send(cfg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # Get manipulated image from the device
 | 
				
			||||||
 | 
					                    transformed = q_manip_out.get().getCvFrame()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    rec_placeholder_img = np.zeros((32, 200, 3), np.uint8)
 | 
				
			||||||
 | 
					                    transformed = np.hstack((transformed, rec_placeholder_img))
 | 
				
			||||||
 | 
					                    if cropped_stacked is None:
 | 
				
			||||||
 | 
					                        cropped_stacked = transformed
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        cropped_stacked = np.vstack((cropped_stacked, transformed))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if cropped_stacked is not None:
 | 
				
			||||||
 | 
					            cv2.imshow('cropped_stacked', cropped_stacked)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if frame is not None:
 | 
				
			||||||
 | 
					            cv2.imshow('frame', frame)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        key = cv2.waitKey(1)
 | 
				
			||||||
 | 
					        if  key == ord('q'):
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					        elif key == ord('t'):
 | 
				
			||||||
 | 
					            print("Autofocus trigger (and disable continuous)")
 | 
				
			||||||
 | 
					            ctrl = dai.CameraControl()
 | 
				
			||||||
 | 
					            ctrl.setAutoFocusMode(dai.CameraControl.AutoFocusMode.AUTO)
 | 
				
			||||||
 | 
					            ctrl.setAutoFocusTrigger()
 | 
				
			||||||
 | 
					            controlQueue.send(ctrl)
 | 
				
			||||||
							
								
								
									
										839
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										839
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							@@ -1,839 +0,0 @@
 | 
				
			|||||||
[[package]]
 | 
					 | 
				
			||||||
name = "astroid"
 | 
					 | 
				
			||||||
version = "2.12.12"
 | 
					 | 
				
			||||||
description = "An abstract syntax tree for Python with inference support."
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7.2"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
lazy-object-proxy = ">=1.4.0"
 | 
					 | 
				
			||||||
wrapt = [
 | 
					 | 
				
			||||||
    {version = ">=1.11,<2", markers = "python_version < \"3.11\""},
 | 
					 | 
				
			||||||
    {version = ">=1.14,<2", markers = "python_version >= \"3.11\""},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "attrs"
 | 
					 | 
				
			||||||
version = "22.1.0"
 | 
					 | 
				
			||||||
description = "Classes Without Boilerplate"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.5"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
 | 
					 | 
				
			||||||
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
 | 
					 | 
				
			||||||
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
 | 
					 | 
				
			||||||
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "beautifulsoup4"
 | 
					 | 
				
			||||||
version = "4.11.1"
 | 
					 | 
				
			||||||
description = "Screen-scraping library"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
soupsieve = ">1.2"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
html5lib = ["html5lib"]
 | 
					 | 
				
			||||||
lxml = ["lxml"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "blobconverter"
 | 
					 | 
				
			||||||
version = "1.3.0"
 | 
					 | 
				
			||||||
description = "The tool that allows you to convert neural networks to MyriadX blob"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = "*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
boto3 = "*"
 | 
					 | 
				
			||||||
PyYAML = "*"
 | 
					 | 
				
			||||||
requests = "*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "boto3"
 | 
					 | 
				
			||||||
version = "1.24.94"
 | 
					 | 
				
			||||||
description = "The AWS SDK for Python"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">= 3.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
botocore = ">=1.27.94,<1.28.0"
 | 
					 | 
				
			||||||
jmespath = ">=0.7.1,<2.0.0"
 | 
					 | 
				
			||||||
s3transfer = ">=0.6.0,<0.7.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "botocore"
 | 
					 | 
				
			||||||
version = "1.27.94"
 | 
					 | 
				
			||||||
description = "Low-level, data-driven core of boto 3."
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">= 3.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
jmespath = ">=0.7.1,<2.0.0"
 | 
					 | 
				
			||||||
python-dateutil = ">=2.1,<3.0.0"
 | 
					 | 
				
			||||||
urllib3 = ">=1.25.4,<1.27"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
crt = ["awscrt (==0.14.0)"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "certifi"
 | 
					 | 
				
			||||||
version = "2022.9.24"
 | 
					 | 
				
			||||||
description = "Python package for providing Mozilla's CA Bundle."
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "charset-normalizer"
 | 
					 | 
				
			||||||
version = "2.1.1"
 | 
					 | 
				
			||||||
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
unicode-backport = ["unicodedata2"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "colorama"
 | 
					 | 
				
			||||||
version = "0.4.5"
 | 
					 | 
				
			||||||
description = "Cross-platform colored terminal text."
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "depthai"
 | 
					 | 
				
			||||||
version = "2.17.4.0"
 | 
					 | 
				
			||||||
description = "DepthAI Python Library"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "dill"
 | 
					 | 
				
			||||||
version = "0.3.5.1"
 | 
					 | 
				
			||||||
description = "serialize all of python"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
graph = ["objgraph (>=1.7.2)"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "google"
 | 
					 | 
				
			||||||
version = "3.0.0"
 | 
					 | 
				
			||||||
description = "Python bindings to the Google search engine."
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = "*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
beautifulsoup4 = "*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "idna"
 | 
					 | 
				
			||||||
version = "3.4"
 | 
					 | 
				
			||||||
description = "Internationalized Domain Names in Applications (IDNA)"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.5"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "iniconfig"
 | 
					 | 
				
			||||||
version = "1.1.1"
 | 
					 | 
				
			||||||
description = "iniconfig: brain-dead simple config-ini parsing"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = "*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "isort"
 | 
					 | 
				
			||||||
version = "5.10.1"
 | 
					 | 
				
			||||||
description = "A Python utility / library to sort Python imports."
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6.1,<4.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
colors = ["colorama (>=0.4.3,<0.5.0)"]
 | 
					 | 
				
			||||||
pipfile-deprecated-finder = ["pipreqs", "requirementslib"]
 | 
					 | 
				
			||||||
plugins = ["setuptools"]
 | 
					 | 
				
			||||||
requirements-deprecated-finder = ["pip-api", "pipreqs"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "jmespath"
 | 
					 | 
				
			||||||
version = "1.0.1"
 | 
					 | 
				
			||||||
description = "JSON Matching Expressions"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "lazy-object-proxy"
 | 
					 | 
				
			||||||
version = "1.7.1"
 | 
					 | 
				
			||||||
description = "A fast and thorough lazy object proxy."
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "mccabe"
 | 
					 | 
				
			||||||
version = "0.7.0"
 | 
					 | 
				
			||||||
description = "McCabe checker, plugin for flake8"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "numpy"
 | 
					 | 
				
			||||||
version = "1.23.4"
 | 
					 | 
				
			||||||
description = "NumPy is the fundamental package for array computing with Python."
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.8"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "opencv-python"
 | 
					 | 
				
			||||||
version = "4.6.0.66"
 | 
					 | 
				
			||||||
description = "Wrapper package for OpenCV python bindings."
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
numpy = [
 | 
					 | 
				
			||||||
    {version = ">=1.21.2", markers = "python_version >= \"3.10\" or python_version >= \"3.6\" and platform_system == \"Darwin\" and platform_machine == \"arm64\""},
 | 
					 | 
				
			||||||
    {version = ">=1.19.3", markers = "python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\" or python_version >= \"3.9\""},
 | 
					 | 
				
			||||||
    {version = ">=1.14.5", markers = "python_version >= \"3.7\""},
 | 
					 | 
				
			||||||
    {version = ">=1.17.3", markers = "python_version >= \"3.8\""},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "packaging"
 | 
					 | 
				
			||||||
version = "21.3"
 | 
					 | 
				
			||||||
description = "Core utilities for Python packages"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "paho-mqtt"
 | 
					 | 
				
			||||||
version = "1.6.1"
 | 
					 | 
				
			||||||
description = "MQTT version 5.0/3.1.1 client class"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = "*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
proxy = ["PySocks"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "platformdirs"
 | 
					 | 
				
			||||||
version = "2.5.2"
 | 
					 | 
				
			||||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"]
 | 
					 | 
				
			||||||
test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "pluggy"
 | 
					 | 
				
			||||||
version = "1.0.0"
 | 
					 | 
				
			||||||
description = "plugin and hook calling mechanisms for python"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
dev = ["pre-commit", "tox"]
 | 
					 | 
				
			||||||
testing = ["pytest", "pytest-benchmark"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "protobuf"
 | 
					 | 
				
			||||||
version = "4.21.8"
 | 
					 | 
				
			||||||
description = ""
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "protobuf3"
 | 
					 | 
				
			||||||
version = "0.2.1"
 | 
					 | 
				
			||||||
description = "Protocol buffers library for Python 3"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = "*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "py"
 | 
					 | 
				
			||||||
version = "1.11.0"
 | 
					 | 
				
			||||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "pylint"
 | 
					 | 
				
			||||||
version = "2.15.4"
 | 
					 | 
				
			||||||
description = "python code static checker"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7.2"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
astroid = ">=2.12.11,<=2.14.0-dev0"
 | 
					 | 
				
			||||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
 | 
					 | 
				
			||||||
dill = ">=0.2"
 | 
					 | 
				
			||||||
isort = ">=4.2.5,<6"
 | 
					 | 
				
			||||||
mccabe = ">=0.6,<0.8"
 | 
					 | 
				
			||||||
platformdirs = ">=2.2.0"
 | 
					 | 
				
			||||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
 | 
					 | 
				
			||||||
tomlkit = ">=0.10.1"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
spelling = ["pyenchant (>=3.2,<4.0)"]
 | 
					 | 
				
			||||||
testutils = ["gitpython (>3)"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "pyparsing"
 | 
					 | 
				
			||||||
version = "3.0.9"
 | 
					 | 
				
			||||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6.8"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
diagrams = ["jinja2", "railroad-diagrams"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "pytest"
 | 
					 | 
				
			||||||
version = "7.1.3"
 | 
					 | 
				
			||||||
description = "pytest: simple powerful testing with Python"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
attrs = ">=19.2.0"
 | 
					 | 
				
			||||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
 | 
					 | 
				
			||||||
iniconfig = "*"
 | 
					 | 
				
			||||||
packaging = "*"
 | 
					 | 
				
			||||||
pluggy = ">=0.12,<2.0"
 | 
					 | 
				
			||||||
py = ">=1.8.2"
 | 
					 | 
				
			||||||
tomli = ">=1.0.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "pytest-mock"
 | 
					 | 
				
			||||||
version = "3.10.0"
 | 
					 | 
				
			||||||
description = "Thin-wrapper around the mock package for easier use with pytest"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
pytest = ">=5.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
dev = ["pre-commit", "pytest-asyncio", "tox"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "python-dateutil"
 | 
					 | 
				
			||||||
version = "2.8.2"
 | 
					 | 
				
			||||||
description = "Extensions to the standard Python datetime module"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
six = ">=1.5"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "pyyaml"
 | 
					 | 
				
			||||||
version = "6.0"
 | 
					 | 
				
			||||||
description = "YAML parser and emitter for Python"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "requests"
 | 
					 | 
				
			||||||
version = "2.28.1"
 | 
					 | 
				
			||||||
description = "Python HTTP for Humans."
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7, <4"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
certifi = ">=2017.4.17"
 | 
					 | 
				
			||||||
charset-normalizer = ">=2,<3"
 | 
					 | 
				
			||||||
idna = ">=2.5,<4"
 | 
					 | 
				
			||||||
urllib3 = ">=1.21.1,<1.27"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
 | 
					 | 
				
			||||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "s3transfer"
 | 
					 | 
				
			||||||
version = "0.6.0"
 | 
					 | 
				
			||||||
description = "An Amazon S3 Transfer Manager"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">= 3.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.dependencies]
 | 
					 | 
				
			||||||
botocore = ">=1.12.36,<2.0a.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "six"
 | 
					 | 
				
			||||||
version = "1.16.0"
 | 
					 | 
				
			||||||
description = "Python 2 and 3 compatibility utilities"
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "soupsieve"
 | 
					 | 
				
			||||||
version = "2.3.2.post1"
 | 
					 | 
				
			||||||
description = "A modern CSS selector implementation for Beautiful Soup."
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "tomli"
 | 
					 | 
				
			||||||
version = "2.0.1"
 | 
					 | 
				
			||||||
description = "A lil' TOML parser"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "tomlkit"
 | 
					 | 
				
			||||||
version = "0.11.5"
 | 
					 | 
				
			||||||
description = "Style preserving TOML library"
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=3.6,<4.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "urllib3"
 | 
					 | 
				
			||||||
version = "1.26.12"
 | 
					 | 
				
			||||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
 | 
					 | 
				
			||||||
category = "main"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[package.extras]
 | 
					 | 
				
			||||||
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
 | 
					 | 
				
			||||||
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
 | 
					 | 
				
			||||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "wrapt"
 | 
					 | 
				
			||||||
version = "1.14.1"
 | 
					 | 
				
			||||||
description = "Module for decorators, wrappers and monkey patching."
 | 
					 | 
				
			||||||
category = "dev"
 | 
					 | 
				
			||||||
optional = false
 | 
					 | 
				
			||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[metadata]
 | 
					 | 
				
			||||||
lock-version = "1.1"
 | 
					 | 
				
			||||||
python-versions = "^3.10"
 | 
					 | 
				
			||||||
content-hash = "d062fb11b00c63a20b69560322be0723850a7fe3a4363bfe0339f1f75ffd0e2e"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[metadata.files]
 | 
					 | 
				
			||||||
astroid = [
 | 
					 | 
				
			||||||
    {file = "astroid-2.12.12-py3-none-any.whl", hash = "sha256:72702205200b2a638358369d90c222d74ebc376787af8fb2f7f2a86f7b5cc85f"},
 | 
					 | 
				
			||||||
    {file = "astroid-2.12.12.tar.gz", hash = "sha256:1c00a14f5a3ed0339d38d2e2e5b74ea2591df5861c0936bb292b84ccf3a78d83"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
attrs = [
 | 
					 | 
				
			||||||
    {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
 | 
					 | 
				
			||||||
    {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
beautifulsoup4 = [
 | 
					 | 
				
			||||||
    {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"},
 | 
					 | 
				
			||||||
    {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
blobconverter = [
 | 
					 | 
				
			||||||
    {file = "blobconverter-1.3.0-py3-none-any.whl", hash = "sha256:7dc96c94ea4d609cd2421b5aa3f31f164b51cfa9cfd3747f9e6e3997f6d27cab"},
 | 
					 | 
				
			||||||
    {file = "blobconverter-1.3.0.tar.gz", hash = "sha256:6f1a2cc6a4d0347a675eddc217aa0751bd5bac09fd6b19195b641f6ea9eb1cc1"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
boto3 = [
 | 
					 | 
				
			||||||
    {file = "boto3-1.24.94-py3-none-any.whl", hash = "sha256:f13db0beb3c9fe2cc1ed0f031189f144610d2909b5874a616e77b0bd1ae3b686"},
 | 
					 | 
				
			||||||
    {file = "boto3-1.24.94.tar.gz", hash = "sha256:f4842b395d1580454756622069f4ca0408993885ecede967001d2c101201cdfa"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
botocore = [
 | 
					 | 
				
			||||||
    {file = "botocore-1.27.94-py3-none-any.whl", hash = "sha256:8237c070d2ab29fac4fbcfe9dd2e84e0ee147402e0fed3ac1629f92459c7f1d2"},
 | 
					 | 
				
			||||||
    {file = "botocore-1.27.94.tar.gz", hash = "sha256:572224608a0b7662966fc303b768e2eba61bf53bdbf314481cd9e63a0d8e1a66"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
certifi = [
 | 
					 | 
				
			||||||
    {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
 | 
					 | 
				
			||||||
    {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
charset-normalizer = [
 | 
					 | 
				
			||||||
    {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
 | 
					 | 
				
			||||||
    {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
colorama = [
 | 
					 | 
				
			||||||
    {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
 | 
					 | 
				
			||||||
    {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
depthai = [
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b482cf5a2bc378b4f9e39dc1fecae8c1b731aae014c6722452e9740e9acdafb5"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbeb827ca450e01e18edabad3e66a18d54e431955badefb98c31ad398effb95f"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aaac971ef52b32f004ecab595426b9d6d16dc59d83f5ee6d989dff2dc1e6e68"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02ed24cc624f6e8d4a6e878fbab847f2d9acab48aeb1b17abda649fb538eed30"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp310-cp310-win32.whl", hash = "sha256:2975cf96f46f9903bbe3355b7df2c6c3b4a9005250088065848d9c6a263ef19f"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:be2b99b002c5e9bb63dd79edb51974436d8f9a942632d2b81146faa9613137d6"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:ca5876c9fd13a07685922e349d76161ba914e07c6fd9153b50198a32f3b3a485"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9a6df834e49e600d4359c153c6aa2c1d3cbb2a0f9ebca6c1b746e048492e4e"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cca0c20f29f88a0872de4bc1f9039c5deb2cefd43227c43b191ec3c99f0da7e"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp36-cp36m-win32.whl", hash = "sha256:abddb6db252f6afcdda6c59c60263e495ada99474e820cb6313ef16bbcbe81b6"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:e7245ceab5e9a5bde237b72acd3a6f701899e977670bda3c5186aa94e6f1823c"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:cacbcb96c25815020692027324e594a95713e021731cfa031613da57b020d619"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:b5868196838b603260b5c8533447ae6f24e2d89958e5297356fc98328b66c215"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78f9fe96e769123797dd5f06cf0ec74021352bee4dd59f74c76bb9c57a6db18a"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a730a3530f35b216f7efbdbad9343d3427024f69093a95ec5848a505f6f4e23"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp37-cp37m-win32.whl", hash = "sha256:e90e10678b2d70d543873fce39e24bb1e18adf2ce9b877a840c3c69fd2009d69"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1515f9d1c3c1078e7cf95b87c9e4e24d03a5ba36f4c0a14704d816d4a378ead4"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:92e71a879a9861bc28feb7d729d59746d0c202563c4b27831d30e0bd736caf6d"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4628e27bc70a2f0e229abb1d68489c5b567b2c0bb477d60214a6491c24f37dad"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ecf3d6f39313a8e34a370979b7272fe476b75d6c1fb129cf77d88d81e8b1112"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp38-cp38-win32.whl", hash = "sha256:e510f7562a17db7b847b0b96b497db885a134d94263f5b6d9696fb1806518fd6"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:227150025f2dac183dd4ed8052b923b8904cca42cac387de61f5a6d317e630af"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:47b2585f088f5473469b0a67bac50863128ca70407b57fbbbdf152a6cbe2f920"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:36728e5db17b0d9d1924387a11015fcb32e0edd35597991dc048870a46611c8e"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2fad8a0b7ff938926bbd30fbb8c273e2b6d0a84c2e83405178d9c70138899c8b"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20c1502ae539db6f5599af2a7bdf94dc55e866aa98c8146ec497f469791b4801"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c81ff583e4d0c1e19914df6c9f83c3e742afbbb97b0cdca926b7e04f019996d"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp39-cp39-win32.whl", hash = "sha256:700638ad886fa03b4d2e3a8d76e1c19b9ebcc4a08f4202100670f7ac7de6f7a9"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:de7b5e4a15b0a9e15fdf19c5388e6aa9629ecc25a955675d400173f9a2dd199b"},
 | 
					 | 
				
			||||||
    {file = "depthai-2.17.4.0.tar.gz", hash = "sha256:b8e3c2eaa643c64e737435adaa23d54e3fda58baad1477f5176908689473a85f"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
dill = [
 | 
					 | 
				
			||||||
    {file = "dill-0.3.5.1-py2.py3-none-any.whl", hash = "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302"},
 | 
					 | 
				
			||||||
    {file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
google = [
 | 
					 | 
				
			||||||
    {file = "google-3.0.0-py2.py3-none-any.whl", hash = "sha256:889cf695f84e4ae2c55fbc0cfdaf4c1e729417fa52ab1db0485202ba173e4935"},
 | 
					 | 
				
			||||||
    {file = "google-3.0.0.tar.gz", hash = "sha256:143530122ee5130509ad5e989f0512f7cb218b2d4eddbafbad40fd10e8d8ccbe"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
idna = [
 | 
					 | 
				
			||||||
    {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
 | 
					 | 
				
			||||||
    {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
iniconfig = [
 | 
					 | 
				
			||||||
    {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
 | 
					 | 
				
			||||||
    {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
isort = [
 | 
					 | 
				
			||||||
    {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
 | 
					 | 
				
			||||||
    {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
jmespath = [
 | 
					 | 
				
			||||||
    {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"},
 | 
					 | 
				
			||||||
    {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
lazy-object-proxy = [
 | 
					 | 
				
			||||||
    {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"},
 | 
					 | 
				
			||||||
    {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
mccabe = [
 | 
					 | 
				
			||||||
    {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
 | 
					 | 
				
			||||||
    {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
numpy = [
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:95d79ada05005f6f4f337d3bb9de8a7774f259341c70bc88047a1f7b96a4bcb2"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:926db372bc4ac1edf81cfb6c59e2a881606b409ddc0d0920b988174b2e2a767f"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c237129f0e732885c9a6076a537e974160482eab8f10db6292e92154d4c67d71"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8365b942f9c1a7d0f0dc974747d99dd0a0cdfc5949a33119caf05cb314682d3"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp310-cp310-win32.whl", hash = "sha256:2341f4ab6dba0834b685cce16dad5f9b6606ea8a00e6da154f5dbded70fdc4dd"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp310-cp310-win_amd64.whl", hash = "sha256:d331afac87c92373826af83d2b2b435f57b17a5c74e6268b79355b970626e329"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:488a66cb667359534bc70028d653ba1cf307bae88eab5929cd707c761ff037db"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce03305dd694c4873b9429274fd41fc7eb4e0e4dea07e0af97a933b079a5814f"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8981d9b5619569899666170c7c9748920f4a5005bf79c72c07d08c8a035757b0"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a70a7d3ce4c0e9284e92285cba91a4a3f5214d87ee0e95928f3614a256a1488"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp311-cp311-win32.whl", hash = "sha256:5e13030f8793e9ee42f9c7d5777465a560eb78fa7e11b1c053427f2ccab90c79"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp311-cp311-win_amd64.whl", hash = "sha256:7607b598217745cc40f751da38ffd03512d33ec06f3523fb0b5f82e09f6f676d"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7ab46e4e7ec63c8a5e6dbf5c1b9e1c92ba23a7ebecc86c336cb7bf3bd2fb10e5"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8aae2fb3180940011b4862b2dd3756616841c53db9734b27bb93813cd79fce6"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c053d7557a8f022ec823196d242464b6955a7e7e5015b719e76003f63f82d0f"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0882323e0ca4245eb0a3d0a74f88ce581cc33aedcfa396e415e5bba7bf05f68"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp38-cp38-win32.whl", hash = "sha256:dada341ebb79619fe00a291185bba370c9803b1e1d7051610e01ed809ef3a4ba"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp38-cp38-win_amd64.whl", hash = "sha256:0fe563fc8ed9dc4474cbf70742673fc4391d70f4363f917599a7fa99f042d5a8"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c67b833dbccefe97cdd3f52798d430b9d3430396af7cdb2a0c32954c3ef73894"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f76025acc8e2114bb664294a07ede0727aa75d63a06d2fae96bf29a81747e4a7"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12ac457b63ec8ded85d85c1e17d85efd3c2b0967ca39560b307a35a6703a4735"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95de7dc7dc47a312f6feddd3da2500826defdccbc41608d0031276a24181a2c0"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp39-cp39-win32.whl", hash = "sha256:f2f390aa4da44454db40a1f0201401f9036e8d578a25f01a6e237cea238337ef"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-cp39-cp39-win_amd64.whl", hash = "sha256:f260da502d7441a45695199b4e7fd8ca87db659ba1c78f2bbf31f934fe76ae0e"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61be02e3bf810b60ab74e81d6d0d36246dbfb644a462458bb53b595791251911"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296d17aed51161dbad3c67ed6d164e51fcd18dbcd5dd4f9d0a9c6055dce30810"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4d52914c88b4930dafb6c48ba5115a96cbab40f45740239d9f4159c4ba779962"},
 | 
					 | 
				
			||||||
    {file = "numpy-1.23.4.tar.gz", hash = "sha256:ed2cc92af0efad20198638c69bb0fc2870a58dabfba6eb722c933b48556c686c"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
opencv-python = [
 | 
					 | 
				
			||||||
    {file = "opencv-python-4.6.0.66.tar.gz", hash = "sha256:c5bfae41ad4031e66bb10ec4a0a2ffd3e514d092652781e8b1ac98d1b59f1158"},
 | 
					 | 
				
			||||||
    {file = "opencv_python-4.6.0.66-cp36-abi3-macosx_10_15_x86_64.whl", hash = "sha256:e6e448b62afc95c5b58f97e87ef84699e6607fe5c58730a03301c52496005cae"},
 | 
					 | 
				
			||||||
    {file = "opencv_python-4.6.0.66-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5af8ba35a4fcb8913ffb86e92403e9a656a4bff4a645d196987468f0f8947875"},
 | 
					 | 
				
			||||||
    {file = "opencv_python-4.6.0.66-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbdc84a9b4ea2cbae33861652d25093944b9959279200b7ae0badd32439f74de"},
 | 
					 | 
				
			||||||
    {file = "opencv_python-4.6.0.66-cp36-abi3-win32.whl", hash = "sha256:f482e78de6e7b0b060ff994ffd859bddc3f7f382bb2019ef157b0ea8ca8712f5"},
 | 
					 | 
				
			||||||
    {file = "opencv_python-4.6.0.66-cp36-abi3-win_amd64.whl", hash = "sha256:0dc82a3d8630c099d2f3ac1b1aabee164e8188db54a786abb7a4e27eba309440"},
 | 
					 | 
				
			||||||
    {file = "opencv_python-4.6.0.66-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:6e32af22e3202748bd233ed8f538741876191863882eba44e332d1a34993165b"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
packaging = [
 | 
					 | 
				
			||||||
    {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
 | 
					 | 
				
			||||||
    {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
paho-mqtt = [
 | 
					 | 
				
			||||||
    {file = "paho-mqtt-1.6.1.tar.gz", hash = "sha256:2a8291c81623aec00372b5a85558a372c747cbca8e9934dfe218638b8eefc26f"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
platformdirs = [
 | 
					 | 
				
			||||||
    {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
 | 
					 | 
				
			||||||
    {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
pluggy = [
 | 
					 | 
				
			||||||
    {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
 | 
					 | 
				
			||||||
    {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
protobuf = [
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp310-abi3-win32.whl", hash = "sha256:c252c55ee15175aa1b21b7b9896e6add5162d066d5202e75c39f96136f08cce3"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp310-abi3-win_amd64.whl", hash = "sha256:809ca0b225d3df42655a12f311dd0f4148a943c51f1ad63c38343e457492b689"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bbececaf3cfea9ea65ebb7974e6242d310d2a7772a6f015477e0d79993af4511"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:b02eabb9ebb1a089ed20626a90ad7a69cee6bcd62c227692466054b19c38dd1f"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:4761201b93e024bb70ee3a6a6425d61f3152ca851f403ba946fb0cde88872661"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp37-cp37m-win32.whl", hash = "sha256:f2d55ff22ec300c4d954d3b0d1eeb185681ec8ad4fbecff8a5aee6a1cdd345ba"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp37-cp37m-win_amd64.whl", hash = "sha256:c5f94911dd8feb3cd3786fc90f7565c9aba7ce45d0f254afd625b9628f578c3f"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp38-cp38-win32.whl", hash = "sha256:b37b76efe84d539f16cba55ee0036a11ad91300333abd213849cbbbb284b878e"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp38-cp38-win_amd64.whl", hash = "sha256:2c92a7bfcf4ae76a8ac72e545e99a7407e96ffe52934d690eb29a8809ee44d7b"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp39-cp39-win32.whl", hash = "sha256:89d641be4b5061823fa0e463c50a2607a97833e9f8cfb36c2f91ef5ccfcc3861"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-cp39-cp39-win_amd64.whl", hash = "sha256:bc471cf70a0f53892fdd62f8cd4215f0af8b3f132eeee002c34302dff9edd9b6"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-py2.py3-none-any.whl", hash = "sha256:a55545ce9eec4030cf100fcb93e861c622d927ef94070c1a3c01922902464278"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8-py3-none-any.whl", hash = "sha256:0f236ce5016becd989bf39bd20761593e6d8298eccd2d878eda33012645dc369"},
 | 
					 | 
				
			||||||
    {file = "protobuf-4.21.8.tar.gz", hash = "sha256:427426593b55ff106c84e4a88cac855175330cb6eb7e889e85aaa7b5652b686d"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
protobuf3 = [
 | 
					 | 
				
			||||||
    {file = "protobuf3-0.2.1.tar.gz", hash = "sha256:ddd878b3f991beff566ab384d3588cf8e89758e3a16a78f4099dbe70de3c41a2"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
py = [
 | 
					 | 
				
			||||||
    {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
 | 
					 | 
				
			||||||
    {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
pylint = [
 | 
					 | 
				
			||||||
    {file = "pylint-2.15.4-py3-none-any.whl", hash = "sha256:629cf1dbdfb6609d7e7a45815a8bb59300e34aa35783b5ac563acaca2c4022e9"},
 | 
					 | 
				
			||||||
    {file = "pylint-2.15.4.tar.gz", hash = "sha256:5441e9294335d354b7bad57c1044e5bd7cce25c433475d76b440e53452fa5cb8"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
pyparsing = [
 | 
					 | 
				
			||||||
    {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
 | 
					 | 
				
			||||||
    {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
pytest = [
 | 
					 | 
				
			||||||
    {file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"},
 | 
					 | 
				
			||||||
    {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
pytest-mock = [
 | 
					 | 
				
			||||||
    {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"},
 | 
					 | 
				
			||||||
    {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
python-dateutil = [
 | 
					 | 
				
			||||||
    {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
 | 
					 | 
				
			||||||
    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
pyyaml = [
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
 | 
					 | 
				
			||||||
    {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
requests = [
 | 
					 | 
				
			||||||
    {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
 | 
					 | 
				
			||||||
    {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
s3transfer = [
 | 
					 | 
				
			||||||
    {file = "s3transfer-0.6.0-py3-none-any.whl", hash = "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd"},
 | 
					 | 
				
			||||||
    {file = "s3transfer-0.6.0.tar.gz", hash = "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
six = [
 | 
					 | 
				
			||||||
    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
 | 
					 | 
				
			||||||
    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
soupsieve = [
 | 
					 | 
				
			||||||
    {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"},
 | 
					 | 
				
			||||||
    {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
tomli = [
 | 
					 | 
				
			||||||
    {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
 | 
					 | 
				
			||||||
    {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
tomlkit = [
 | 
					 | 
				
			||||||
    {file = "tomlkit-0.11.5-py3-none-any.whl", hash = "sha256:f2ef9da9cef846ee027947dc99a45d6b68a63b0ebc21944649505bf2e8bc5fe7"},
 | 
					 | 
				
			||||||
    {file = "tomlkit-0.11.5.tar.gz", hash = "sha256:571854ebbb5eac89abcb4a2e47d7ea27b89bf29e09c35395da6f03dd4ae23d1c"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
urllib3 = [
 | 
					 | 
				
			||||||
    {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
 | 
					 | 
				
			||||||
    {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
wrapt = [
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"},
 | 
					 | 
				
			||||||
    {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"},
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
@@ -1,43 +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" },
 | 
					 | 
				
			||||||
    { include = "events" },
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[tool.poetry.dependencies]
 | 
					 | 
				
			||||||
python = "^3.10"
 | 
					 | 
				
			||||||
paho-mqtt = "^1.6.1"
 | 
					 | 
				
			||||||
depthai = "^2.17.4.0"
 | 
					 | 
				
			||||||
protobuf3 = "^0.2.1"
 | 
					 | 
				
			||||||
google = "^3.0.0"
 | 
					 | 
				
			||||||
opencv-python = "^4.6.0.66"
 | 
					 | 
				
			||||||
blobconverter = "^1.3.0"
 | 
					 | 
				
			||||||
protobuf = "^4.21.8"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[tool.poetry.group.test.dependencies]
 | 
					 | 
				
			||||||
pytest = "^7.1.3"
 | 
					 | 
				
			||||||
pytest-mock = "^3.10.0"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[tool.poetry.group.dev.dependencies]
 | 
					 | 
				
			||||||
pylint = "^2.15.4"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[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
 | 
					 | 
				
			||||||
							
								
								
									
										9
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					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
 | 
				
			||||||
 | 
					blobconverter>=1.2.9
 | 
				
			||||||
							
								
								
									
										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