From 55f5045428b8da0182422769b0ffe9467e49fd36 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Sun, 28 Nov 2021 02:14:48 +0100 Subject: [PATCH 01/44] Updated DeviceInfo API --- depthai-core | 2 +- src/XLinkBindings.cpp | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/depthai-core b/depthai-core index efa3deb7c..260309340 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit efa3deb7c17e223238696416cd45d2d3eb35674f +Subproject commit 2603093406ec037f0a86a226835b3c47427c70d4 diff --git a/src/XLinkBindings.cpp b/src/XLinkBindings.cpp index 91d5f0238..011c43093 100644 --- a/src/XLinkBindings.cpp +++ b/src/XLinkBindings.cpp @@ -48,9 +48,16 @@ void XLinkBindings::bind(pybind11::module &m, void *pCallstack) // Bindings deviceInfo .def(py::init<>()) - .def_readwrite("desc", &DeviceInfo::desc) - .def_readwrite("state", &DeviceInfo::state) + .def(py::init(), py::arg("mxidOrName"), DOC(dai, DeviceInfo)) + .def(py::init()) .def("getMxId", &DeviceInfo::getMxId) + .def("getXLinkDeviceDesc", &DeviceInfo::getXLinkDeviceDesc) + .def_readwrite("name", &DeviceInfo::name) + .def_readwrite("mxid", &DeviceInfo::mxid) + .def_readwrite("state", &DeviceInfo::state) + .def_readwrite("protocol", &DeviceInfo::protocol) + .def_readwrite("platform", &DeviceInfo::platform) + .def("__str__", &DeviceInfo::toString) ; deviceDesc @@ -62,7 +69,10 @@ void XLinkBindings::bind(pybind11::module &m, void *pCallstack) [](deviceDesc_t &o) { return std::string(o.name); }, [](deviceDesc_t &o, std::string n) - { std::strncpy(o.name, n.c_str(), std::min(XLINK_MAX_NAME_SIZE, (int)n.size())); }) + { + memset(o.name, 0, sizeof(o.name)); + std::strncpy(o.name, n.c_str(), sizeof(o.name)); + }) ; xLinkDeviceState From 0cdf70996f15889272e3f1528fcd9ed2f3a41bc0 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Fri, 18 Feb 2022 04:23:40 +0100 Subject: [PATCH 02/44] Added bindings for DeviceInfo changes --- depthai-core | 2 +- src/XLinkBindings.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 9a21fc9a4..a814bab64 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 9a21fc9a424c702f8b6ee02dcf4d25678a4c5100 +Subproject commit a814bab644ed952d671b497452894bb9bcbd86dd diff --git a/src/XLinkBindings.cpp b/src/XLinkBindings.cpp index 011c43093..6b0c70ad7 100644 --- a/src/XLinkBindings.cpp +++ b/src/XLinkBindings.cpp @@ -57,6 +57,7 @@ void XLinkBindings::bind(pybind11::module &m, void *pCallstack) .def_readwrite("state", &DeviceInfo::state) .def_readwrite("protocol", &DeviceInfo::protocol) .def_readwrite("platform", &DeviceInfo::platform) + .def_readwrite("status", &DeviceInfo::status) .def("__str__", &DeviceInfo::toString) ; @@ -64,6 +65,8 @@ void XLinkBindings::bind(pybind11::module &m, void *pCallstack) .def(py::init<>()) .def_readwrite("protocol", &deviceDesc_t::protocol) .def_readwrite("platform", &deviceDesc_t::platform) + .def_readwrite("state", &deviceDesc_t::state) + .def_readwrite("status", &deviceDesc_t::status) .def_property( "name", [](deviceDesc_t &o) @@ -73,6 +76,15 @@ void XLinkBindings::bind(pybind11::module &m, void *pCallstack) memset(o.name, 0, sizeof(o.name)); std::strncpy(o.name, n.c_str(), sizeof(o.name)); }) + .def_property( + "mxid", + [](deviceDesc_t &o) + { return std::string(o.mxid); }, + [](deviceDesc_t &o, std::string n) + { + memset(o.mxid, 0, sizeof(o.mxid)); + std::strncpy(o.mxid, n.c_str(), sizeof(o.mxid)); + }) ; xLinkDeviceState From 682598dde411486e6fc7cb4f6222a1da2606fce0 Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Tue, 22 Feb 2022 19:42:09 +0200 Subject: [PATCH 03/44] Add automatic/optional IMU firmware update --- depthai-core | 2 +- src/pipeline/NodeBindings.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index f7cb4088f..b6ed88a86 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit f7cb4088f781b043e1442aaada3d23440480149a +Subproject commit b6ed88a86c6094684a589dbf6be8b51ff298cf81 diff --git a/src/pipeline/NodeBindings.cpp b/src/pipeline/NodeBindings.cpp index af5a11e39..b7401f51b 100644 --- a/src/pipeline/NodeBindings.cpp +++ b/src/pipeline/NodeBindings.cpp @@ -426,6 +426,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ .def_readwrite("imuSensors", &IMUProperties::imuSensors, DOC(dai, IMUProperties, imuSensors)) .def_readwrite("batchReportThreshold", &IMUProperties::batchReportThreshold, DOC(dai, IMUProperties, batchReportThreshold)) .def_readwrite("maxBatchReports", &IMUProperties::maxBatchReports, DOC(dai, IMUProperties, maxBatchReports)) + .def_readwrite("doFirmwareUpdate", &IMUProperties::doFirmwareUpdate, DOC(dai, IMUProperties, doFirmwareUpdate)) ; // EdgeDetector node properties @@ -1182,6 +1183,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ .def("getBatchReportThreshold", &IMU::getBatchReportThreshold, DOC(dai, node, IMU, getBatchReportThreshold)) .def("setMaxBatchReports", &IMU::setMaxBatchReports, py::arg("maxBatchReports"), DOC(dai, node, IMU, setMaxBatchReports)) .def("getMaxBatchReports", &IMU::getMaxBatchReports, DOC(dai, node, IMU, getMaxBatchReports)) + .def("doFirmwareUpdate", &IMU::doFirmwareUpdate, DOC(dai, node, IMU, doFirmwareUpdate)) ; daiNodeModule.attr("IMU").attr("Properties") = imuProperties; From 86cc4f83aa285e2a32fc2144cbfee20418d60695 Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Sat, 26 Mar 2022 00:33:48 +0200 Subject: [PATCH 04/44] Update core/firmware --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index b6ed88a86..748667d35 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit b6ed88a86c6094684a589dbf6be8b51ff298cf81 +Subproject commit 748667d35bc088e150f0fea881f7fa9aabb8b68a From 95cf438f20296ee4d219232ad22cb415d6ea95bb Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Wed, 27 Apr 2022 21:33:33 +0200 Subject: [PATCH 05/44] Updated core --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index a814bab64..4a89e804c 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit a814bab644ed952d671b497452894bb9bcbd86dd +Subproject commit 4a89e804c55913bd3b41b6c4ef2f16299ca7acf8 From f36208983bae8ac134fcc5cf3effcc99fd0f486c Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 28 Apr 2022 17:34:23 +0200 Subject: [PATCH 06/44] Updated pybind11 to 2.9.2 for VS2022 fixes --- cmake/Hunter/config.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index a7cd428b6..2d90c72ea 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -1,5 +1,7 @@ -# Pybind11 2.7.0 +# Pybind11 2.9.2 hunter_config( pybind11 - VERSION "2.7.0" + VERSION "2.9.2" + URL "https://github.com/pybind/pybind11/archive/refs/tags/v2.9.2.tar.gz" + SHA1 "5e05583a210282c3251281b6ee5677915f0cbf95" ) From 6725386085db2590fc5b6ad50c6b7a3ce4f9d7c6 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 28 Apr 2022 18:24:08 +0200 Subject: [PATCH 07/44] Fixed changes after merge --- depthai-core | 2 +- src/DeviceBindings.cpp | 2 +- src/pybind11_common.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/depthai-core b/depthai-core index 8ec405a9f..f4cbcfa36 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 8ec405a9fc92c18711ffa75d9994ea4dbb020412 +Subproject commit f4cbcfa367122eabeb8724ec0072feb15b251f16 diff --git a/src/DeviceBindings.cpp b/src/DeviceBindings.cpp index 02bd67f75..ce25704c4 100644 --- a/src/DeviceBindings.cpp +++ b/src/DeviceBindings.cpp @@ -35,7 +35,7 @@ static auto deviceSearchHelper(Args&&... args){ py::gil_scoped_release release; std::tie(found, deviceInfo) = DEVICE::getFirstAvailableDevice(false); - if(strcmp("", deviceInfo.desc.name) == 0){ + if(deviceInfo.status != X_LINK_SUCCESS) { invalidDeviceFound = true; invalidDeviceInfo = deviceInfo; found = false; diff --git a/src/pybind11_common.hpp b/src/pybind11_common.hpp index 192206246..6dbc0967b 100644 --- a/src/pybind11_common.hpp +++ b/src/pybind11_common.hpp @@ -45,7 +45,7 @@ namespace pybind11 { namespace detail { bool isPath = false; try{ isPath = isinstance(src, module::import("pathlib").attr("PurePath")); - } catch (const std::exception& ex) { + } catch (const std::exception&) { //ignore } if(!isinstance(src) && !isPath) { From c2316956466690771d2979064c40d33f0f882b48 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 28 Apr 2022 19:19:46 +0200 Subject: [PATCH 08/44] Fixed stubgen output --- generate_stubs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/generate_stubs.py b/generate_stubs.py index 4407d2c69..556aa91ed 100644 --- a/generate_stubs.py +++ b/generate_stubs.py @@ -33,14 +33,18 @@ # Add imports stubs_import = 'import depthai.node as node\nimport typing\nimport json\n' + contents + # Create 'create' overloads nodes = re.findall('def \S*\(self\) -> node.(\S*):', stubs_import) overloads = '' for node in nodes: overloads = overloads + f'\\1@overload\\1def create(self, arg0: typing.Type[node.{node}]) -> node.{node}: ...' - #print(f'{overloads}') final_stubs = re.sub(r"([\s]*)def create\(self, arg0: object\) -> Node: ...", f'{overloads}', stubs_import) + # Modify "*View" naming + nodes = re.findall('View\\[(\S*)\\]', final_stubs) + final_stubs = re.sub(r"View\[(\S*)\]", f'View_\\1', final_stubs) + # Writeout changes file.seek(0) file.write(final_stubs) From 52efa1b85e09f79a09c0addd567cd3dcb02228da Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 28 Apr 2022 22:24:08 +0200 Subject: [PATCH 09/44] Windows - Added PATH for dlls provided by Hunter --- CMakeLists.txt | 11 +++++++++++ docs/CMakeLists.txt | 2 ++ examples/CMakeLists.txt | 6 ++++++ tests/CMakeLists.txt | 2 ++ 4 files changed, 21 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88bd29a85..b4667fb4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,12 @@ if(NOT WIN32) set(HUNTER_CONFIGURATION_TYPES "Release" CACHE STRING "Hunter dependencies list of build configurations") endif() +# Specify path separator +set(SYS_PATH_SEPARATOR ";") +if(UNIX) + set(SYS_PATH_SEPARATOR ":") +endif() + # Generate combined Hunter config file(READ depthai-core/cmake/Hunter/config.cmake depthai_core_hunter_config) file(READ cmake/Hunter/config.cmake hunter_config) @@ -108,6 +114,11 @@ else() endif() message(STATUS "Mypy available, creating and checking stubs. Running with generate_stubs.py ${TARGET_NAME} ${bindings_directory}") add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND + ${CMAKE_COMMAND} -E env + # PATH (dlls) + "PATH=${HUNTER_INSTALL_PREFIX}/bin${SYS_PATH_SEPARATOR}$ENV{PATH}" + # Python path (to find compiled module) + "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/generate_stubs.py" "${TARGET_NAME}" "${bindings_directory}" DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generate_stubs.py" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 6e4d2d9c6..b1f664b03 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -40,6 +40,8 @@ else() add_custom_target(sphinx ALL ${CMAKE_COMMAND} -E env # Environment variables + # PATH (dlls) + "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6d92ec22e..845e4b1dc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -33,6 +33,8 @@ function(add_python_example example_name python_script_path) add_custom_target(${example_name} ${CMAKE_COMMAND} -E env # Environment variables + # PATH (dlls) + "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers @@ -49,6 +51,8 @@ function(add_python_example example_name python_script_path) # Adds test with 5 seconds timeout and bumps all python warnings to errors add_test(NAME ${example_name} COMMAND ${CMAKE_COMMAND} -E env + # PATH (dlls) + "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers @@ -70,6 +74,8 @@ if(DEPTHAI_PYTHON_TEST_EXAMPLES) # Adds install requirements test with 5 minute timeout add_test(NAME install_requirements COMMAND ${CMAKE_COMMAND} -E env + # PATH (dlls) + "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3c0eec650..15cdedb37 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,6 +21,8 @@ pybind11_add_module(${TARGET_TEST_MODULE} THIN_LTO ${TARGET_TEST_MODULE}.cpp ${P add_custom_target( pytest COMMAND ${CMAKE_COMMAND} -E env + # PATH (dlls) + "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled modules) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers From dda8efc6e64113a77c82486f7d6cfc7426c5f899 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Fri, 29 Apr 2022 11:47:15 +0200 Subject: [PATCH 10/44] Fixed PATH and modified CI to not fail fast on matrix builds --- .github/workflows/main.yml | 2 ++ docs/CMakeLists.txt | 2 +- examples/CMakeLists.txt | 6 +++--- tests/CMakeLists.txt | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 42997bd37..0354f12b7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -177,6 +177,7 @@ jobs: matrix: python-version: [3.6, 3.7, 3.8, 3.9, '3.10'] python-architecture: [x64, x86] + fail-fast: false steps: - name: Cache .hunter folder uses: actions/cache@v2 @@ -230,6 +231,7 @@ jobs: strategy: matrix: python-version: [3.6, 3.7, 3.8, 3.9, '3.10'] + fail-fast: false steps: - name: Cache .hunter folder uses: actions/cache@v2 diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index b1f664b03..6bc5a25ac 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -41,7 +41,7 @@ else() ${CMAKE_COMMAND} -E env # Environment variables # PATH (dlls) - "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" + "PATH=${HUNTER_INSTALL_PREFIX}/bin${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 845e4b1dc..7937142b8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -34,7 +34,7 @@ function(add_python_example example_name python_script_path) ${CMAKE_COMMAND} -E env # Environment variables # PATH (dlls) - "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" + "PATH=${HUNTER_INSTALL_PREFIX}/bin${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers @@ -52,7 +52,7 @@ function(add_python_example example_name python_script_path) add_test(NAME ${example_name} COMMAND ${CMAKE_COMMAND} -E env # PATH (dlls) - "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" + "PATH=${HUNTER_INSTALL_PREFIX}/bin${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers @@ -75,7 +75,7 @@ if(DEPTHAI_PYTHON_TEST_EXAMPLES) add_test(NAME install_requirements COMMAND ${CMAKE_COMMAND} -E env # PATH (dlls) - "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" + "PATH=${HUNTER_INSTALL_PREFIX}/bin${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 15cdedb37..5fe352e5e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,7 +22,7 @@ add_custom_target( pytest COMMAND ${CMAKE_COMMAND} -E env # PATH (dlls) - "PATH=${HUNTER_INSTALL_PREFIX}${SYS_PATH_SEPARATOR}$ENV{PATH}" + "PATH=${HUNTER_INSTALL_PREFIX}/bin${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled modules) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" # ASAN in case of sanitizers From 86b6e3172165c8a9dd0c9c0738116cbe83d806d8 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Fri, 29 Apr 2022 20:14:41 +0200 Subject: [PATCH 11/44] Windows - added copying of dlls for created python module --- CMakeLists.txt | 14 ++++++++++++++ depthai-core | 2 +- setup.py | 19 +++++++++++++------ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b4667fb4f..82c53644f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,20 @@ pybind11_add_module(${TARGET_NAME} src/log/LogBindings.cpp ) +# Copy dlls to target directory - Windows only +if(WIN32) + # TARGET_RUNTIME_DLLS generator expression available since CMake 3.21 + if(CMAKE_VERSION VERSION_LESS "3.21") + file(GLOB depthai_dll_libraries "${HUNTER_INSTALL_PREFIX}/bin/*.dll") + else() + set(depthai_dll_libraries "$") + endif() + add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND + ${CMAKE_COMMAND} -E copy ${depthai_dll_libraries} $ + COMMAND_EXPAND_LISTS + ) +endif() + # Add stubs (pyi) generation step after building bindings execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" "from mypy import api" RESULT_VARIABLE error OUTPUT_QUIET ERROR_QUIET) if(error) diff --git a/depthai-core b/depthai-core index f4cbcfa36..1f96deac0 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit f4cbcfa367122eabeb8724ec0072feb15b251f16 +Subproject commit 1f96deac0f30bb3087d2981d99bf6e1bc62f0415 diff --git a/setup.py b/setup.py index 0e9bdcbbc..6a2db6abb 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,7 @@ from setuptools import setup, Extension from setuptools.command.build_ext import build_ext from distutils.version import LooseVersion +from pathlib import Path ### NAME MODULE_NAME = 'depthai' @@ -95,9 +96,16 @@ def build_extension(self, ext): # initialize cmake_args and build_args cmake_args = [] build_args = [] + env = os.environ.copy() # Specify output directory and python executable cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, '-DPYTHON_EXECUTABLE=' + sys.executable] + # Specify dir of python executable (pybind11) + if platform.system() == "Windows": + # Windows - remove case insensitive variants + env = {key:env[key] for key in env if key.upper() != 'pythonLocation'.upper()} + env['pythonLocation'] = str(Path(sys.executable).parent.absolute()) + # Pass a commit hash if buildCommitHash != None : @@ -127,8 +135,6 @@ def build_extension(self, ext): raise except: freeMemory = 4000 - # Memcheck (guard if it fails) - # Configure and build # Windows @@ -149,8 +155,8 @@ def build_extension(self, ext): # if macos add some additional env vars if sys.platform == 'darwin': from distutils import util - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.9' - os.environ['_PYTHON_HOST_PLATFORM'] = re.sub(r'macosx-[0-9]+\.[0-9]+-(.+)', r'macosx-10.9-\1', util.get_platform()) + env['MACOSX_DEPLOYMENT_TARGET'] = '10.9' + env['_PYTHON_HOST_PLATFORM'] = re.sub(r'macosx-[0-9]+\.[0-9]+-(.+)', r'macosx-10.9-\1', util.get_platform()) # Specify how many threads to use when building, depending on available memory max_threads = multiprocessing.cpu_count() @@ -161,7 +167,6 @@ def build_extension(self, ext): build_args += ['--', '-j' + str(num_threads)] cmake_args += ['-DHUNTER_JOBS_NUMBER=' + str(num_threads)] - env = os.environ.copy() env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), self.distribution.get_version()) # Add additional cmake args from environment @@ -170,8 +175,10 @@ def build_extension(self, ext): if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) + + # Configure and build subprocess.check_call(['cmake', ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env) - subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp) + subprocess.check_call(['cmake', '--build', '.'] + build_args, cwd=self.build_temp, env=env) setup( name=MODULE_NAME, From e10c82d876712c3fbab522b3d7d8677328691ef7 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Mon, 2 May 2022 16:00:20 +0200 Subject: [PATCH 12/44] Simplified device searching and aligned it with core --- depthai-core | 2 +- src/DeviceBindings.cpp | 48 +++++++----------------------------------- 2 files changed, 9 insertions(+), 41 deletions(-) diff --git a/depthai-core b/depthai-core index 1f96deac0..5972eb8d9 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 1f96deac0f30bb3087d2981d99bf6e1bc62f0415 +Subproject commit 5972eb8d9c5a6c3d84103c11858a85c1040acf32 diff --git a/src/DeviceBindings.cpp b/src/DeviceBindings.cpp index ce25704c4..879621214 100644 --- a/src/DeviceBindings.cpp +++ b/src/DeviceBindings.cpp @@ -24,46 +24,14 @@ PYBIND11_MAKE_OPAQUE(std::unordered_map); template static auto deviceSearchHelper(Args&&... args){ - auto startTime = std::chrono::steady_clock::now(); - bool found = false; - bool invalidDeviceFound = false; - dai::DeviceInfo deviceInfo = {}; - dai::DeviceInfo invalidDeviceInfo = {}; - do { - { - // releases python GIL - py::gil_scoped_release release; - std::tie(found, deviceInfo) = DEVICE::getFirstAvailableDevice(false); - - if(deviceInfo.status != X_LINK_SUCCESS) { - invalidDeviceFound = true; - invalidDeviceInfo = deviceInfo; - found = false; - } - - // Check if found - if(found){ - break; - } else { - // block for 100ms - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - } - // reacquires python GIL for PyErr_CheckSignals call - // check if interrupt triggered in between + bool found; + dai::DeviceInfo deviceInfo; + // releases python GIL + py::gil_scoped_release release; + std::tie(found, deviceInfo) = DEVICE::getAnyAvailableDevice(DEVICE::getDefaultSearchTime(), [](){ + py::gil_scoped_acquire acquire; if (PyErr_CheckSignals() != 0) throw py::error_already_set(); - } while(std::chrono::steady_clock::now() - startTime < DEVICE::getDefaultSearchTime()); - - // Check if its an invalid device - if(invalidDeviceFound){ - // Warn - // spdlog::warn("skipping {} device having name \"{}\"", XLinkDeviceStateToStr(invalidDeviceInfo.state), invalidDeviceInfo.desc.name); - // TODO(themarpe) - move device search into C++ and expose a callback - DEVICE::getFirstAvailableDevice(true); - } - - // If neither UNBOOTED nor BOOTLOADER were found (after 'DEFAULT_SEARCH_TIME'), try BOOTED - if(!found) std::tie(found, deviceInfo) = dai::XLinkConnection::getFirstDevice(X_LINK_BOOTED); + }); // if no devices found, then throw if(!found) throw std::runtime_error("No available devices"); @@ -329,7 +297,7 @@ void DeviceBindings::bind(pybind11::module& m, void* pCallstack){ //dai::Device methods //static - .def_static("getAnyAvailableDevice", [](std::chrono::microseconds us){ return Device::getAnyAvailableDevice(us); }, py::arg("timeout"), DOC(dai, DeviceBase, getAnyAvailableDevice)) + .def_static("getAnyAvailableDevice", [](std::chrono::milliseconds ms){ return DeviceBase::getAnyAvailableDevice(ms); }, py::arg("timeout"), DOC(dai, DeviceBase, getAnyAvailableDevice)) .def_static("getAnyAvailableDevice", [](){ return DeviceBase::getAnyAvailableDevice(); }, DOC(dai, DeviceBase, getAnyAvailableDevice, 2)) .def_static("getFirstAvailableDevice", &DeviceBase::getFirstAvailableDevice, py::arg("skipInvalidDevices") = true, DOC(dai, DeviceBase, getFirstAvailableDevice)) .def_static("getAllAvailableDevices", &DeviceBase::getAllAvailableDevices, DOC(dai, DeviceBase, getAllAvailableDevices)) From 2eb14ff6d91de260b99e049a0164da3b6f4586f0 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Tue, 3 May 2022 13:20:19 +0200 Subject: [PATCH 13/44] Updated core and fixed Windows library debug build --- CMakeLists.txt | 7 +++++-- depthai-core | 2 +- generate_stubs.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82c53644f..411208f6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,8 +103,8 @@ pybind11_add_module(${TARGET_NAME} src/log/LogBindings.cpp ) -# Copy dlls to target directory - Windows only if(WIN32) + # Copy dlls to target directory - Windows only # TARGET_RUNTIME_DLLS generator expression available since CMake 3.21 if(CMAKE_VERSION VERSION_LESS "3.21") file(GLOB depthai_dll_libraries "${HUNTER_INSTALL_PREFIX}/bin/*.dll") @@ -115,6 +115,9 @@ if(WIN32) ${CMAKE_COMMAND} -E copy ${depthai_dll_libraries} $ COMMAND_EXPAND_LISTS ) + + # Disable "d" postfix, so python can import the library as is + set_target_properties(${TARGET_NAME} PROPERTIES DEBUG_POSTFIX "") endif() # Add stubs (pyi) generation step after building bindings @@ -133,7 +136,7 @@ else() "PATH=${HUNTER_INSTALL_PREFIX}/bin${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" - ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/generate_stubs.py" "${TARGET_NAME}" "${bindings_directory}" + ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_LIST_DIR}/generate_stubs.py" "${TARGET_NAME}" "$" DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generate_stubs.py" WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) diff --git a/depthai-core b/depthai-core index 5972eb8d9..9c864d0f1 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 5972eb8d9c5a6c3d84103c11858a85c1040acf32 +Subproject commit 9c864d0f1903782fa792f5d7fdeffbcc68bc6052 diff --git a/generate_stubs.py b/generate_stubs.py index 556aa91ed..0c17e9c95 100644 --- a/generate_stubs.py +++ b/generate_stubs.py @@ -12,7 +12,7 @@ MODULE_NAME = sys.argv[1] DIRECTORY = sys.argv[2] -print(f'Generating stubs for module: {MODULE_NAME} in directory: {DIRECTORY}') +print(f'Generating stubs for module: "{MODULE_NAME}" in directory: "{DIRECTORY}"') try: From cab6d1520e9aa24e7a007975571864d6bfe0aa96 Mon Sep 17 00:00:00 2001 From: Erol444 Date: Thu, 5 May 2022 13:50:02 +0200 Subject: [PATCH 14/44] Edited specifying IP for POE in examples/docs --- docs/source/tutorials/standalone_mode.rst | 5 ++--- examples/bootloader/bootloader_config.py | 2 +- examples/bootloader/bootloader_version.py | 2 +- examples/bootloader/poe_set_ip.py | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/source/tutorials/standalone_mode.rst b/docs/source/tutorials/standalone_mode.rst index 27c0a5f20..6b804835a 100644 --- a/docs/source/tutorials/standalone_mode.rst +++ b/docs/source/tutorials/standalone_mode.rst @@ -139,10 +139,9 @@ You can also **factory reset OAK POE at specific IP** if it's not reachable (not with tempfile.NamedTemporaryFile() as tmpBlFw: tmpBlFw.write(bytes(blBinary)) - device_info = dai.DeviceInfo() + device_info = dai.DeviceInfo("192.168.34.110") # Set IP here device_info.state = dai.XLinkDeviceState.X_LINK_BOOTLOADER - device_info.desc.protocol = dai.XLinkProtocol.X_LINK_TCP_IP - device_info.desc.name = "192.168.34.110" # Set IP here + device_info.protocol = dai.XLinkProtocol.X_LINK_TCP_IP with dai.DeviceBootloader(device_info, allowFlashingBootloader=True) as bootloader: progress = lambda p : print(f'Factory reset progress: {p*100:.1f}%') diff --git a/examples/bootloader/bootloader_config.py b/examples/bootloader/bootloader_config.py index 0967094ab..b6de9b1ac 100755 --- a/examples/bootloader/bootloader_config.py +++ b/examples/bootloader/bootloader_config.py @@ -31,7 +31,7 @@ (res, info) = dai.DeviceBootloader.getFirstAvailableDevice() if res: - print(f'Found device with name: {info.desc.name}'); + print(f'Found device with name: {info.name}'); with dai.DeviceBootloader(info) as bl: if read: print('Current flashed configuration') diff --git a/examples/bootloader/bootloader_version.py b/examples/bootloader/bootloader_version.py index 6c370469d..87f254823 100755 --- a/examples/bootloader/bootloader_version.py +++ b/examples/bootloader/bootloader_version.py @@ -5,7 +5,7 @@ (res, info) = dai.DeviceBootloader.getFirstAvailableDevice() if res == True: - print(f'Found device with name: {info.desc.name}') + print(f'Found device with name: {info.name}') bl = dai.DeviceBootloader(info) print(f'Version: {bl.getVersion()}') else: diff --git a/examples/bootloader/poe_set_ip.py b/examples/bootloader/poe_set_ip.py index 029569c92..7314dacd6 100644 --- a/examples/bootloader/poe_set_ip.py +++ b/examples/bootloader/poe_set_ip.py @@ -12,7 +12,7 @@ def check_str(s: str): return s if found: - print(f'Found device with name: {info.desc.name}') + print(f'Found device with name: {info.name}') print('-------------------------------------') print('"1" to set a static IPv4 address') print('"2" to set a dynamic IPv4 address') From 9b226be7508126b38da00f3e47b118c3c9b8a7c1 Mon Sep 17 00:00:00 2001 From: Erol444 Date: Thu, 5 May 2022 17:06:02 +0200 Subject: [PATCH 15/44] Added docs --- docs/source/components/device.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/source/components/device.rst b/docs/source/components/device.rst index b538633a4..8bfdfd104 100644 --- a/docs/source/components/device.rst +++ b/docs/source/components/device.rst @@ -40,6 +40,20 @@ When you create the device in the code, firmware is uploaded together with the p cfg = depthai.ImageManipConfig() input_q.send(cfg) +Connect to specified device +########################### + +If you have multiple devices and only want to connect to a specific one, or if your OAK PoE camera is outside of your +subnet, you can specify the device (either with MxID, IP, or USB port name) you want to connect to. + +.. code-block:: python + + # Specify MXID, IP Address or USB path + device_info = depthai.DeviceInfo("14442C108144F1D000") # MXID + #device_info = depthai.DeviceInfo("192.168.1.44") # IP Address + #device_info = depthai.DeviceInfo("3.3.3") # USB port name + with depthai.Device(pipeline, device_info) as device: + # ... Multiple devices ################ From e4a1f8b25ac3cec5c55762180c2cbd3fcbf794cd Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Tue, 10 May 2022 11:21:05 +0200 Subject: [PATCH 16/44] Updated pybind to v2.9.2 - fixes VS2022 compilation issues --- cmake/Hunter/config.cmake | 6 ++++-- depthai-core | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index a7cd428b6..2d90c72ea 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -1,5 +1,7 @@ -# Pybind11 2.7.0 +# Pybind11 2.9.2 hunter_config( pybind11 - VERSION "2.7.0" + VERSION "2.9.2" + URL "https://github.com/pybind/pybind11/archive/refs/tags/v2.9.2.tar.gz" + SHA1 "5e05583a210282c3251281b6ee5677915f0cbf95" ) diff --git a/depthai-core b/depthai-core index 1503d545d..434836dea 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 1503d545d4caad91e0c1a8436c7951ff79b400ef +Subproject commit 434836deaea917b8b9451974c2571ec1f56d3ee1 From 07f022d05057f1149c6d7b65363267f4430eaa3e Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Tue, 10 May 2022 23:32:53 +0200 Subject: [PATCH 17/44] Fixed stub generation --- generate_stubs.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/generate_stubs.py b/generate_stubs.py index 4407d2c69..0c17e9c95 100644 --- a/generate_stubs.py +++ b/generate_stubs.py @@ -12,7 +12,7 @@ MODULE_NAME = sys.argv[1] DIRECTORY = sys.argv[2] -print(f'Generating stubs for module: {MODULE_NAME} in directory: {DIRECTORY}') +print(f'Generating stubs for module: "{MODULE_NAME}" in directory: "{DIRECTORY}"') try: @@ -33,14 +33,18 @@ # Add imports stubs_import = 'import depthai.node as node\nimport typing\nimport json\n' + contents + # Create 'create' overloads nodes = re.findall('def \S*\(self\) -> node.(\S*):', stubs_import) overloads = '' for node in nodes: overloads = overloads + f'\\1@overload\\1def create(self, arg0: typing.Type[node.{node}]) -> node.{node}: ...' - #print(f'{overloads}') final_stubs = re.sub(r"([\s]*)def create\(self, arg0: object\) -> Node: ...", f'{overloads}', stubs_import) + # Modify "*View" naming + nodes = re.findall('View\\[(\S*)\\]', final_stubs) + final_stubs = re.sub(r"View\[(\S*)\]", f'View_\\1', final_stubs) + # Writeout changes file.seek(0) file.write(final_stubs) From 6e22db63c6bfbd2291ce940486a235386571d1e2 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Mon, 16 May 2022 13:24:27 +0200 Subject: [PATCH 18/44] Added bindings for DetectionParser and sync examples with core --- depthai-core | 2 +- examples/MobileNet/rgb_mobilenet.py | 15 +++ examples/NeuralNetwork/detection_parser.py | 114 ++++++++++++++++++ .../SpatialDetection/spatial_tiny_yolo.py | 13 ++ src/pipeline/CommonBindings.cpp | 12 ++ src/pipeline/NodeBindings.cpp | 44 +++++-- src/pipeline/PipelineBindings.cpp | 2 + 7 files changed, 194 insertions(+), 8 deletions(-) create mode 100755 examples/NeuralNetwork/detection_parser.py diff --git a/depthai-core b/depthai-core index 4747c8373..1942ec46f 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 4747c83733cf2bea07775c62d17dc9c18974de71 +Subproject commit 1942ec46f3217c87d165ea80da9e6948dbeee699 diff --git a/examples/MobileNet/rgb_mobilenet.py b/examples/MobileNet/rgb_mobilenet.py index e595cde17..94d734128 100755 --- a/examples/MobileNet/rgb_mobilenet.py +++ b/examples/MobileNet/rgb_mobilenet.py @@ -29,9 +29,11 @@ nn = pipeline.create(dai.node.MobileNetDetectionNetwork) xoutRgb = pipeline.create(dai.node.XLinkOut) nnOut = pipeline.create(dai.node.XLinkOut) +nnNetworkOut = pipeline.create(dai.node.XLinkOut) xoutRgb.setStreamName("rgb") nnOut.setStreamName("nn") +nnNetworkOut.setStreamName("nnNetwork"); # Properties camRgb.setPreviewSize(300, 300) @@ -51,6 +53,7 @@ camRgb.preview.link(nn.input) nn.out.link(nnOut.input) +nn.outNetwork.link(nnNetworkOut.input); # Connect to device and start pipeline with dai.Device(pipeline) as device: @@ -58,6 +61,7 @@ # Output queues will be used to get the rgb frames and nn data from the outputs defined above qRgb = device.getOutputQueue(name="rgb", maxSize=4, blocking=False) qDet = device.getOutputQueue(name="nn", maxSize=4, blocking=False) + qNN = device.getOutputQueue(name="nnNetwork", maxSize=4, blocking=False); frame = None detections = [] @@ -81,15 +85,19 @@ def displayFrame(name, frame): # Show the frame cv2.imshow(name, frame) + printOutputLayersOnce = True + while True: if args.sync: # Use blocking get() call to catch frame and inference result synced inRgb = qRgb.get() inDet = qDet.get() + inNN = qNN.get() else: # Instead of get (blocking), we use tryGet (non-blocking) which will return the available data or None otherwise inRgb = qRgb.tryGet() inDet = qDet.tryGet() + inNN = qNN.tryGet() if inRgb is not None: frame = inRgb.getCvFrame() @@ -100,6 +108,13 @@ def displayFrame(name, frame): detections = inDet.detections counter += 1 + if printOutputLayersOnce and inNN is not None: + toPrint = 'Output layer names:' + for ten in inNN.getAllLayerNames(): + toPrint = f'{toPrint} {ten},' + print(toPrint) + printOutputLayersOnce = False; + # If the frame is available, draw bounding boxes on it and show the frame if frame is not None: displayFrame("rgb", frame) diff --git a/examples/NeuralNetwork/detection_parser.py b/examples/NeuralNetwork/detection_parser.py new file mode 100755 index 000000000..340fb6040 --- /dev/null +++ b/examples/NeuralNetwork/detection_parser.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +from pathlib import Path +import cv2 +import depthai as dai +import numpy as np +import time +import argparse + +nnPathDefault = str((Path(__file__).parent / Path('../models/mobilenet-ssd_openvino_2021.4_6shave.blob')).resolve().absolute()) +parser = argparse.ArgumentParser() +parser.add_argument('nnPath', nargs='?', help="Path to mobilenet detection network blob", default=nnPathDefault) +parser.add_argument('-s', '--sync', action="store_true", help="Sync RGB output with NN output", default=False) +args = parser.parse_args() + +if not Path(nnPathDefault).exists(): + import sys + raise FileNotFoundError(f'Required file/s not found, please run "{sys.executable} install_requirements.py"') + +# MobilenetSSD label texts +labelMap = ["background", "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", + "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"] + +# Create pipeline +pipeline = dai.Pipeline() + +# Define sources and outputs +camRgb = pipeline.create(dai.node.ColorCamera) +nn = pipeline.create(dai.node.NeuralNetwork) +det = pipeline.create(dai.node.DetectionParser) +xoutRgb = pipeline.create(dai.node.XLinkOut) +nnOut = pipeline.create(dai.node.XLinkOut) + +xoutRgb.setStreamName("rgb") +nnOut.setStreamName("nn") + +# Properties +camRgb.setPreviewSize(300, 300) +camRgb.setInterleaved(False) +camRgb.setFps(40) +# Define a neural network that will make predictions based on the source frames +nn.setNumInferenceThreads(2) +nn.input.setBlocking(False) + +blob = dai.OpenVINO.Blob(args.nnPath); +nn.setBlob(blob); +det.setBlob(blob); +det.setNNFamily(dai.DetectionNetworkType.MOBILENET); +det.setConfidenceThreshold(0.5); + +# Linking +if args.sync: + nn.passthrough.link(xoutRgb.input) +else: + camRgb.preview.link(xoutRgb.input) + +camRgb.preview.link(nn.input) +nn.out.link(det.input) +det.out.link(nnOut.input) + +# Connect to device and start pipeline +with dai.Device(pipeline) as device: + + # Output queues will be used to get the rgb frames and nn data from the outputs defined above + qRgb = device.getOutputQueue(name="rgb", maxSize=4, blocking=False) + qDet = device.getOutputQueue(name="nn", maxSize=4, blocking=False) + + frame = None + detections = [] + startTime = time.monotonic() + counter = 0 + color2 = (255, 255, 255) + + # nn data (bounding box locations) are in <0..1> range - they need to be normalized with frame width/height + def frameNorm(frame, bbox): + normVals = np.full(len(bbox), frame.shape[0]) + normVals[::2] = frame.shape[1] + return (np.clip(np.array(bbox), 0, 1) * normVals).astype(int) + + def displayFrame(name, frame): + color = (255, 0, 0) + for detection in detections: + bbox = frameNorm(frame, (detection.xmin, detection.ymin, detection.xmax, detection.ymax)) + cv2.putText(frame, labelMap[detection.label], (bbox[0] + 10, bbox[1] + 20), cv2.FONT_HERSHEY_TRIPLEX, 0.5, color) + cv2.putText(frame, f"{int(detection.confidence * 100)}%", (bbox[0] + 10, bbox[1] + 40), cv2.FONT_HERSHEY_TRIPLEX, 0.5, color) + cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, 2) + # Show the frame + cv2.imshow(name, frame) + + while True: + if args.sync: + # Use blocking get() call to catch frame and inference result synced + inRgb = qRgb.get() + inDet = qDet.get() + else: + # Instead of get (blocking), we use tryGet (non-blocking) which will return the available data or None otherwise + inRgb = qRgb.tryGet() + inDet = qDet.tryGet() + + if inRgb is not None: + frame = inRgb.getCvFrame() + cv2.putText(frame, "NN fps: {:.2f}".format(counter / (time.monotonic() - startTime)), + (2, frame.shape[0] - 4), cv2.FONT_HERSHEY_TRIPLEX, 0.4, color2) + + if inDet is not None: + detections = inDet.detections + counter += 1 + + # If the frame is available, draw bounding boxes on it and show the frame + if frame is not None: + displayFrame("rgb", frame) + + if cv2.waitKey(1) == ord('q'): + break diff --git a/examples/SpatialDetection/spatial_tiny_yolo.py b/examples/SpatialDetection/spatial_tiny_yolo.py index 3aaa6981d..094f4d087 100755 --- a/examples/SpatialDetection/spatial_tiny_yolo.py +++ b/examples/SpatialDetection/spatial_tiny_yolo.py @@ -57,6 +57,7 @@ monoLeft = pipeline.create(dai.node.MonoCamera) monoRight = pipeline.create(dai.node.MonoCamera) stereo = pipeline.create(dai.node.StereoDepth) +nnNetworkOut = pipeline.create(dai.node.XLinkOut) xoutRgb = pipeline.create(dai.node.XLinkOut) xoutNN = pipeline.create(dai.node.XLinkOut) @@ -67,6 +68,7 @@ xoutNN.setStreamName("detections") xoutBoundingBoxDepthMapping.setStreamName("boundingBoxDepthMapping") xoutDepth.setStreamName("depth") +nnNetworkOut.setStreamName("nnNetwork") # Properties camRgb.setPreviewSize(416, 416) @@ -114,6 +116,7 @@ stereo.depth.link(spatialDetectionNetwork.inputDepth) spatialDetectionNetwork.passthroughDepth.link(xoutDepth.input) +spatialDetectionNetwork.outNetwork.link(nnNetworkOut.input); # Connect to device and start pipeline with dai.Device(pipeline) as device: @@ -123,16 +126,26 @@ detectionNNQueue = device.getOutputQueue(name="detections", maxSize=4, blocking=False) xoutBoundingBoxDepthMappingQueue = device.getOutputQueue(name="boundingBoxDepthMapping", maxSize=4, blocking=False) depthQueue = device.getOutputQueue(name="depth", maxSize=4, blocking=False) + networkQueue = device.getOutputQueue(name="nnNetwork", maxSize=4, blocking=False); startTime = time.monotonic() counter = 0 fps = 0 color = (255, 255, 255) + printOutputLayersOnce = True while True: inPreview = previewQueue.get() inDet = detectionNNQueue.get() depth = depthQueue.get() + inNN = networkQueue.get() + + if printOutputLayersOnce: + toPrint = 'Output layer names:' + for ten in inNN.getAllLayerNames(): + toPrint = f'{toPrint} {ten},' + print(toPrint) + printOutputLayersOnce = False; frame = inPreview.getCvFrame() depthFrame = depth.getFrame() # depthFrame values are in millimeters diff --git a/src/pipeline/CommonBindings.cpp b/src/pipeline/CommonBindings.cpp index 1ff9d2e38..70e39dd06 100644 --- a/src/pipeline/CommonBindings.cpp +++ b/src/pipeline/CommonBindings.cpp @@ -14,6 +14,7 @@ #include "depthai-shared/common/Size2f.hpp" #include "depthai-shared/common/UsbSpeed.hpp" #include "depthai-shared/common/DetectionNetworkType.hpp" +#include "depthai-shared/common/DetectionParserOptions.hpp" void CommonBindings::bind(pybind11::module& m, void* pCallstack){ @@ -37,6 +38,7 @@ void CommonBindings::bind(pybind11::module& m, void* pCallstack){ py::enum_ processorType(m, "ProcessorType"); py::enum_ detectionNetworkType(m, "DetectionNetworkType"); py::enum_ serializationType(m, "SerializationType"); + py::class_ detectionParserOptions(m, "DetectionParserOptions", DOC(dai, DetectionParserOptions)); /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// @@ -205,4 +207,14 @@ void CommonBindings::bind(pybind11::module& m, void* pCallstack){ .value("JSON_MSGPACK", SerializationType::JSON_MSGPACK) ; + detectionParserOptions + .def_readwrite("nnFamily", &DetectionParserOptions::nnFamily) + .def_readwrite("confidenceThreshold", &DetectionParserOptions::confidenceThreshold) + .def_readwrite("classes", &DetectionParserOptions::classes) + .def_readwrite("coordinates", &DetectionParserOptions::coordinates) + .def_readwrite("anchors", &DetectionParserOptions::anchors) + .def_readwrite("anchorMasks", &DetectionParserOptions::anchorMasks) + .def_readwrite("iouThreshold", &DetectionParserOptions::iouThreshold) + ; + } diff --git a/src/pipeline/NodeBindings.cpp b/src/pipeline/NodeBindings.cpp index 0f69ee360..3a7a64ef5 100644 --- a/src/pipeline/NodeBindings.cpp +++ b/src/pipeline/NodeBindings.cpp @@ -22,6 +22,7 @@ #include "depthai/pipeline/node/EdgeDetector.hpp" #include "depthai/pipeline/node/FeatureTracker.hpp" #include "depthai/pipeline/node/AprilTag.hpp" +#include "depthai/pipeline/node/DetectionParser.hpp" // Libraries #include "hedley/hedley.h" @@ -182,6 +183,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ py::enum_ nodeOutputType(pyOutput, "Type"); py::class_ scriptProperties(m, "ScriptProperties", DOC(dai, ScriptProperties)); py::class_> pyProperties(m, "Properties", DOC(dai, Properties)); + py::class_ detectionParserProperties(m, "DetectionParserProperties", DOC(dai, DetectionParserProperties)); // Node::Id bindings @@ -216,6 +218,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ auto edgeDetector = ADD_NODE(EdgeDetector); auto featureTracker = ADD_NODE(FeatureTracker); auto aprilTag = ADD_NODE(AprilTag); + auto detectionParser = ADD_NODE(DetectionParser); py::enum_ stereoDepthPresetMode(stereoDepth, "PresetMode", DOC(dai, node, StereoDepth, PresetMode)); @@ -353,13 +356,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ detectionNetworkProperties - .def_readwrite("nnFamily", &DetectionNetworkProperties::nnFamily) - .def_readwrite("confidenceThreshold", &DetectionNetworkProperties::confidenceThreshold) - .def_readwrite("classes", &DetectionNetworkProperties::classes) - .def_readwrite("coordinates", &DetectionNetworkProperties::coordinates) - .def_readwrite("anchors", &DetectionNetworkProperties::anchors) - .def_readwrite("anchorMasks", &DetectionNetworkProperties::anchorMasks) - .def_readwrite("iouThreshold", &DetectionNetworkProperties::iouThreshold) + .def_readwrite("parser", &DetectionNetworkProperties::parser) ; @@ -470,6 +467,11 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ .def_readwrite("numMemorySlices", &FeatureTrackerProperties::numMemorySlices, DOC(dai, FeatureTrackerProperties, numMemorySlices)) ; + // DetectionParser node properties + detectionParserProperties + .def_readwrite("parser", &DetectionParserProperties::parser, DOC(dai, DetectionParserProperties, parser)) + ; + //////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////// @@ -684,6 +686,8 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ .def("setNumNCEPerInferenceThread", &NeuralNetwork::setNumNCEPerInferenceThread, py::arg("numNCEPerThread"), DOC(dai, node, NeuralNetwork, setNumNCEPerInferenceThread)) .def("getNumInferenceThreads", &NeuralNetwork::getNumInferenceThreads, DOC(dai, node, NeuralNetwork, getNumInferenceThreads)) + .def("setBlob", &NeuralNetwork::setBlob, DOC(dai, node, NeuralNetwork, setBlob)) + .def_readonly("inputs", &NeuralNetwork::inputs, DOC(dai, node, NeuralNetwork, inputs)) .def_readonly("passthroughs", &NeuralNetwork::passthroughs, DOC(dai, node, NeuralNetwork, passthroughs)) @@ -1037,6 +1041,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ detectionNetwork .def_readonly("input", &DetectionNetwork::input, DOC(dai, node, NeuralNetwork, input)) .def_readonly("out", &DetectionNetwork::out, DOC(dai, node, DetectionNetwork, out)) + .def_readonly("outNetwork", &DetectionNetwork::outNetwork, DOC(dai, node, DetectionNetwork, outNetwork)) .def_readonly("passthrough", &DetectionNetwork::passthrough, DOC(dai, node, NeuralNetwork, passthrough)) .def("setConfidenceThreshold", &DetectionNetwork::setConfidenceThreshold, py::arg("thresh"), DOC(dai, node, DetectionNetwork, setConfidenceThreshold)) .def("getConfidenceThreshold", &DetectionNetwork::getConfidenceThreshold, DOC(dai, node, DetectionNetwork, getConfidenceThreshold)) @@ -1249,6 +1254,31 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ ; daiNodeModule.attr("FeatureTracker").attr("Properties") = featureTrackerProperties; + // FeatureTracker node + detectionParser + .def_readonly("input", &DetectionParser::input, DOC(dai, node, DetectionParser, input)) + .def_readonly("out", &DetectionParser::out, DOC(dai, node, DetectionParser, out)) + .def("setNumFramesPool", &DetectionParser::setNumFramesPool, py::arg("numFramesPool"), DOC(dai, node, DetectionParser, setNumFramesPool)) + .def("getNumFramesPool", &DetectionParser::getNumFramesPool, DOC(dai, node, DetectionParser, getNumFramesPool)) + .def("setBlob", &DetectionParser::setBlob, py::arg("blob"), DOC(dai, node, DetectionParser, setBlob)) + .def("setNNFamily", &DetectionParser::setNNFamily, py::arg("type"), DOC(dai, node, DetectionParser, setNNFamily)) + .def("getNNFamily", &DetectionParser::getNNFamily, DOC(dai, node, DetectionParser, getNNFamily)) + .def("setConfidenceThreshold", &DetectionParser::setConfidenceThreshold, py::arg("thresh"), DOC(dai, node, DetectionParser, setConfidenceThreshold)) + .def("getConfidenceThreshold", &DetectionParser::getConfidenceThreshold, DOC(dai, node, DetectionParser, getConfidenceThreshold)) + .def("setNumClasses", &DetectionParser::setNumClasses, py::arg("numClasses"), DOC(dai, node, DetectionParser, setNumClasses)) + .def("setCoordinateSize", &DetectionParser::setCoordinateSize, py::arg("coordinates"), DOC(dai, node, DetectionParser, setCoordinateSize)) + .def("setAnchors", &DetectionParser::setAnchors, py::arg("anchors"), DOC(dai, node, DetectionParser, setAnchors)) + .def("setAnchorMasks", &DetectionParser::setAnchorMasks, py::arg("anchorMasks"), DOC(dai, node, DetectionParser, setAnchorMasks)) + .def("setIouThreshold", &DetectionParser::setIouThreshold, py::arg("thresh"), DOC(dai, node, DetectionParser, setIouThreshold)) + .def("getNumClasses", &DetectionParser::getNumClasses, DOC(dai, node, DetectionParser, getNumClasses)) + .def("getCoordinateSize", &DetectionParser::getCoordinateSize, DOC(dai, node, DetectionParser, getCoordinateSize)) + .def("getAnchors", &DetectionParser::getAnchors, DOC(dai, node, DetectionParser, getAnchors)) + .def("getAnchorMasks", &DetectionParser::getAnchorMasks, DOC(dai, node, DetectionParser, getAnchorMasks)) + .def("getIouThreshold", &DetectionParser::getIouThreshold, DOC(dai, node, DetectionParser, getIouThreshold)) + + ; + daiNodeModule.attr("DetectionParser").attr("Properties") = detectionParserProperties; + } diff --git a/src/pipeline/PipelineBindings.cpp b/src/pipeline/PipelineBindings.cpp index bc90c6373..168516b18 100644 --- a/src/pipeline/PipelineBindings.cpp +++ b/src/pipeline/PipelineBindings.cpp @@ -26,6 +26,7 @@ #include "depthai/pipeline/node/EdgeDetector.hpp" #include "depthai/pipeline/node/FeatureTracker.hpp" #include "depthai/pipeline/node/AprilTag.hpp" +#include "depthai/pipeline/node/DetectionParser.hpp" // depthai-shared #include "depthai-shared/properties/GlobalProperties.hpp" @@ -134,6 +135,7 @@ void PipelineBindings::bind(pybind11::module& m, void* pCallstack){ .def("createEdgeDetector", &Pipeline::create) .def("createFeatureTracker", &Pipeline::create) .def("createAprilTag", &Pipeline::create) + .def("createDetectionParser", &Pipeline::create) ; From b9793d9c8960c854bcaf6281c5459e058fed8fe3 Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Mon, 16 May 2022 16:44:46 +0300 Subject: [PATCH 19/44] Add API for subpixel fractional bits --- src/DatatypeBindings.cpp | 1 + src/pipeline/NodeBindings.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/DatatypeBindings.cpp b/src/DatatypeBindings.cpp index 776058bf4..c9577d77c 100644 --- a/src/DatatypeBindings.cpp +++ b/src/DatatypeBindings.cpp @@ -1385,6 +1385,7 @@ void DatatypeBindings::bind(pybind11::module& m, void* pCallstack){ .def("setLeftRightCheck", &StereoDepthConfig::setLeftRightCheck, py::arg("enable"), DOC(dai, StereoDepthConfig, setLeftRightCheck)) .def("setExtendedDisparity", &StereoDepthConfig::setExtendedDisparity, py::arg("enable"), DOC(dai, StereoDepthConfig, setExtendedDisparity)) .def("setSubpixel", &StereoDepthConfig::setSubpixel, py::arg("enable"), DOC(dai, StereoDepthConfig, setSubpixel)) + .def("setSubpixelFractionalBits", &StereoDepthConfig::setSubpixelFractionalBits, py::arg("subpixelFractionalBits"), DOC(dai, StereoDepthConfig, setSubpixelFractionalBits)) .def("getMaxDisparity", &StereoDepthConfig::getMaxDisparity, DOC(dai, StereoDepthConfig, getMaxDisparity)) .def("setDepthUnit", &StereoDepthConfig::setDepthUnit, DOC(dai, StereoDepthConfig, setDepthUnit)) .def("getDepthUnit", &StereoDepthConfig::getDepthUnit, DOC(dai, StereoDepthConfig, getDepthUnit)) diff --git a/src/pipeline/NodeBindings.cpp b/src/pipeline/NodeBindings.cpp index 3a7a64ef5..ebfa8d0e7 100644 --- a/src/pipeline/NodeBindings.cpp +++ b/src/pipeline/NodeBindings.cpp @@ -859,6 +859,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ .def("setRectification", &StereoDepth::setRectification, py::arg("enable"), DOC(dai, node, StereoDepth, setRectification)) .def("setLeftRightCheck", &StereoDepth::setLeftRightCheck, py::arg("enable"), DOC(dai, node, StereoDepth, setLeftRightCheck)) .def("setSubpixel", &StereoDepth::setSubpixel, py::arg("enable"), DOC(dai, node, StereoDepth, setSubpixel)) + .def("setSubpixelFractionalBits", &StereoDepth::setSubpixelFractionalBits, py::arg("subpixelFractionalBits"), DOC(dai, StereoDepth, setSubpixelFractionalBits)) .def("setExtendedDisparity", &StereoDepth::setExtendedDisparity, py::arg("enable"), DOC(dai, node, StereoDepth, setExtendedDisparity)) .def("setRectifyEdgeFillColor", &StereoDepth::setRectifyEdgeFillColor, py::arg("color"), DOC(dai, node, StereoDepth, setRectifyEdgeFillColor)) .def("setRectifyMirrorFrame", [](StereoDepth& s, bool enable) { From 0041b8a81847f39070ba56061506d18392113963 Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Mon, 16 May 2022 19:55:03 +0300 Subject: [PATCH 20/44] Update core to latest develop --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 1942ec46f..f31975b86 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 1942ec46f3217c87d165ea80da9e6948dbeee699 +Subproject commit f31975b86bccf204d108da98fea21313370d45de From 98b459bde2c72db4f4417d238e47d492daf4035b Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Mon, 16 May 2022 20:01:07 +0300 Subject: [PATCH 21/44] Fix docs build --- src/pipeline/NodeBindings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pipeline/NodeBindings.cpp b/src/pipeline/NodeBindings.cpp index ebfa8d0e7..02db0917d 100644 --- a/src/pipeline/NodeBindings.cpp +++ b/src/pipeline/NodeBindings.cpp @@ -859,7 +859,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ .def("setRectification", &StereoDepth::setRectification, py::arg("enable"), DOC(dai, node, StereoDepth, setRectification)) .def("setLeftRightCheck", &StereoDepth::setLeftRightCheck, py::arg("enable"), DOC(dai, node, StereoDepth, setLeftRightCheck)) .def("setSubpixel", &StereoDepth::setSubpixel, py::arg("enable"), DOC(dai, node, StereoDepth, setSubpixel)) - .def("setSubpixelFractionalBits", &StereoDepth::setSubpixelFractionalBits, py::arg("subpixelFractionalBits"), DOC(dai, StereoDepth, setSubpixelFractionalBits)) + .def("setSubpixelFractionalBits", &StereoDepth::setSubpixelFractionalBits, py::arg("subpixelFractionalBits"), DOC(dai, node, StereoDepth, setSubpixelFractionalBits)) .def("setExtendedDisparity", &StereoDepth::setExtendedDisparity, py::arg("enable"), DOC(dai, node, StereoDepth, setExtendedDisparity)) .def("setRectifyEdgeFillColor", &StereoDepth::setRectifyEdgeFillColor, py::arg("color"), DOC(dai, node, StereoDepth, setRectifyEdgeFillColor)) .def("setRectifyMirrorFrame", [](StereoDepth& s, bool enable) { From 33ebe9a5d70d1399435dedb5f9b61d3604896a50 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Mon, 16 May 2022 22:25:54 +0200 Subject: [PATCH 22/44] Added OpenVINO.DEFAULT_VERSION binding --- src/openvino/OpenVINOBindings.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openvino/OpenVINOBindings.cpp b/src/openvino/OpenVINOBindings.cpp index 29cb6da94..5a56c69df 100644 --- a/src/openvino/OpenVINOBindings.cpp +++ b/src/openvino/OpenVINOBindings.cpp @@ -53,6 +53,8 @@ void OpenVINOBindings::bind(pybind11::module& m, void* pCallstack){ .value("VERSION_2021_4", OpenVINO::Version::VERSION_2021_4) .export_values() ; + // DEFAULT_VERSION binding + openvino.attr("DEFAULT_VERSION") = dai::OpenVINO::DEFAULT_VERSION; // Bind OpenVINO::Blob openvinoBlob From dadfc7d544e61dc93bcdc44acacc9deef7c1e0e7 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Tue, 17 May 2022 13:10:57 +0200 Subject: [PATCH 23/44] Updated core --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 865b59b5b..d39e71bcc 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 865b59b5bee0b55f8f023de1ca9b3f8167945e7a +Subproject commit d39e71bcc86f9501229824a012463593a08a69ec From e5996f27d5056f21cbb98e23a9915c0b08162521 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Wed, 18 May 2022 05:12:31 +0200 Subject: [PATCH 24/44] Modified bindings to not error out if initialization doesn't work from beggining. Note, subsequent initialization can cause issues. --- ci/Dockerfile | 6 ------ depthai-core | 2 +- generate_stubs.py | 7 +++++++ src/py_bindings.cpp | 6 +++++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/ci/Dockerfile b/ci/Dockerfile index 3046aa9e0..e97711d0f 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -4,12 +4,6 @@ RUN apt-get update && apt-get install -y wget build-essential cmake pkg-config l ADD ci/docker_dependencies.sh . RUN ./docker_dependencies.sh -RUN wget https://github.com/libusb/libusb/releases/download/v1.0.24/libusb-1.0.24.tar.bz2 -RUN tar xf libusb-1.0.24.tar.bz2 -RUN cd libusb-1.0.24 && \ - ./configure --disable-udev && \ - make -j && make install - RUN pip install -U pip && pip install --extra-index-url https://www.piwheels.org/simple/ --prefer-binary opencv-python diff --git a/depthai-core b/depthai-core index 2aeb3413b..92c753786 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 2aeb3413b457c3611f55c32f630d04eee51e05ef +Subproject commit 92c753786ebe36a2a3b0616ca62991a227d0e93c diff --git a/generate_stubs.py b/generate_stubs.py index 0c17e9c95..2f2466b35 100644 --- a/generate_stubs.py +++ b/generate_stubs.py @@ -20,6 +20,13 @@ # CWD to to extdir where the built module can be found to extract the types env = os.environ env['PYTHONPATH'] = f'{DIRECTORY}{os.pathsep}{env.get("PYTHONPATH", "")}' + + # Test importing depthai after PYTHONPATH is specified + try: + import depthai + except Exception as ex: + print(f'Could not import depthai: {ex}') + print(f'PYTHONPATH set to {env["PYTHONPATH"]}') subprocess.check_call(['stubgen', '-p', MODULE_NAME, '-o', f'{DIRECTORY}'], cwd=DIRECTORY, env=env) diff --git a/src/py_bindings.cpp b/src/py_bindings.cpp index 1a3927ab9..0376c7e2a 100644 --- a/src/py_bindings.cpp +++ b/src/py_bindings.cpp @@ -79,6 +79,10 @@ PYBIND11_MODULE(depthai, m) } // Call dai::initialize on 'import depthai' to initialize asap with additional information to print - dai::initialize(std::string("Python bindings - version: ") + DEPTHAI_PYTHON_VERSION + " from " + DEPTHAI_PYTHON_COMMIT_DATETIME + " build: " + DEPTHAI_PYTHON_BUILD_DATETIME, installSignalHandler); + try { + dai::initialize(std::string("Python bindings - version: ") + DEPTHAI_PYTHON_VERSION + " from " + DEPTHAI_PYTHON_COMMIT_DATETIME + " build: " + DEPTHAI_PYTHON_BUILD_DATETIME, installSignalHandler); + } catch (const std::exception&) { + // ignore, will be initialized later on if possible + } } From 895967cdc6932a08f1eb3c13cdddb7c078f201d1 Mon Sep 17 00:00:00 2001 From: Martin Peterlin Date: Thu, 19 May 2022 04:24:54 +0200 Subject: [PATCH 25/44] Added bindings for updated VideoEncoder capabilities --- depthai-core | 2 +- src/pipeline/NodeBindings.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 92c753786..3f0f9d0f6 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 92c753786ebe36a2a3b0616ca62991a227d0e93c +Subproject commit 3f0f9d0f6809c36b21d8baee48760bd2ca60a010 diff --git a/src/pipeline/NodeBindings.cpp b/src/pipeline/NodeBindings.cpp index 02db0917d..a1e64bda9 100644 --- a/src/pipeline/NodeBindings.cpp +++ b/src/pipeline/NodeBindings.cpp @@ -338,6 +338,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ .def_readwrite("profile", &VideoEncoderProperties::profile) .def_readwrite("quality", &VideoEncoderProperties::quality) .def_readwrite("rateCtrlMode", &VideoEncoderProperties::rateCtrlMode) + .def_readwrite("outputFrameSize", &VideoEncoderProperties::outputFrameSize) ; @@ -973,6 +974,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ .def("setBitrate", &VideoEncoder::setBitrate, py::arg("bitrate"), DOC(dai, node, VideoEncoder, setBitrate)) .def("setBitrateKbps", &VideoEncoder::setBitrateKbps, py::arg("bitrateKbps"), DOC(dai, node, VideoEncoder, setBitrateKbps)) .def("setKeyframeFrequency", &VideoEncoder::setKeyframeFrequency, py::arg("freq"), DOC(dai, node, VideoEncoder, setKeyframeFrequency)) + .def("setMaxOutputFrameSize", &VideoEncoder::setMaxOutputFrameSize, py::arg("maxFrameSize"), DOC(dai, node, VideoEncoder, setMaxOutputFrameSize)) //.def("setMaxBitrate", &VideoEncoder::setMaxBitrate) .def("setNumBFrames", &VideoEncoder::setNumBFrames, py::arg("numBFrames"), DOC(dai, node, VideoEncoder, setNumBFrames)) .def("setQuality", &VideoEncoder::setQuality, py::arg("quality"), DOC(dai, node, VideoEncoder, setQuality)) @@ -1009,6 +1011,7 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack){ }, DOC(dai, node, VideoEncoder, getSize)) .def("getFrameRate", &VideoEncoder::getFrameRate, DOC(dai, node, VideoEncoder, getFrameRate)) .def("getLossless", &VideoEncoder::getLossless, DOC(dai, node, VideoEncoder, getLossless)) + .def("getMaxOutputFrameSize", &VideoEncoder::getMaxOutputFrameSize, DOC(dai, node, VideoEncoder, getMaxOutputFrameSize)) ; // ALIAS daiNodeModule.attr("VideoEncoder").attr("Properties") = videoEncoderProperties; From 26866775d8a2f9b882df91d99797a489864b16a8 Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Thu, 19 May 2022 06:33:21 +0300 Subject: [PATCH 26/44] Add OpenVINO 2022.1 support --- depthai-core | 2 +- src/openvino/OpenVINOBindings.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 3f0f9d0f6..18ead73f7 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 3f0f9d0f6809c36b21d8baee48760bd2ca60a010 +Subproject commit 18ead73f7b060b7335c905c19083b4997da9d9fb diff --git a/src/openvino/OpenVINOBindings.cpp b/src/openvino/OpenVINOBindings.cpp index 5a56c69df..e2b9e1451 100644 --- a/src/openvino/OpenVINOBindings.cpp +++ b/src/openvino/OpenVINOBindings.cpp @@ -51,6 +51,7 @@ void OpenVINOBindings::bind(pybind11::module& m, void* pCallstack){ .value("VERSION_2021_2", OpenVINO::Version::VERSION_2021_2) .value("VERSION_2021_3", OpenVINO::Version::VERSION_2021_3) .value("VERSION_2021_4", OpenVINO::Version::VERSION_2021_4) + .value("VERSION_2022_1", OpenVINO::Version::VERSION_2022_1) .export_values() ; // DEFAULT_VERSION binding From 52700ee534bd0f816d86ffd4d892096420063e3c Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Thu, 19 May 2022 06:41:20 +0300 Subject: [PATCH 27/44] Update core --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 18ead73f7..4f1cc2333 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 18ead73f7b060b7335c905c19083b4997da9d9fb +Subproject commit 4f1cc233376d9f987a6dbf642006a987fe91bf33 From 9f380e3451ec84cbbc361a6eeb202f5d8070b516 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Thu, 19 May 2022 17:29:39 +0200 Subject: [PATCH 28/44] Fixed VideoEncoder crash --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 3f0f9d0f6..e1a17c29c 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 3f0f9d0f6809c36b21d8baee48760bd2ca60a010 +Subproject commit e1a17c29c469cad69267a24f34a4186cc6adac74 From 4f9d1cabc8b831e34596c736ac0f1affda3417bc Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Sat, 21 May 2022 01:34:59 +0300 Subject: [PATCH 29/44] Add imu firmware update example --- depthai-core | 2 +- examples/IMU/imu_firmware_update.py | 67 +++++++++++++++++++++++++++++ examples/IMU/imu_rotation_vector.py | 1 + 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100755 examples/IMU/imu_firmware_update.py diff --git a/depthai-core b/depthai-core index d0055b237..055a74b2e 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit d0055b2378736b86f14749b3b39579e055cad19f +Subproject commit 055a74b2ee372e76baaae32e3f535789002f759e diff --git a/examples/IMU/imu_firmware_update.py b/examples/IMU/imu_firmware_update.py new file mode 100755 index 000000000..daee59b9c --- /dev/null +++ b/examples/IMU/imu_firmware_update.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +import cv2 +import depthai as dai +import time +import math + +# Create pipeline +pipeline = dai.Pipeline() + +# Define sources and outputs +imu = pipeline.create(dai.node.IMU) +xlinkOut = pipeline.create(dai.node.XLinkOut) + +xlinkOut.setStreamName("imu") + +# enable ACCELEROMETER_RAW at 500 hz rate +imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_RAW, 500) +# enable GYROSCOPE_RAW at 400 hz rate +imu.enableIMUSensor(dai.IMUSensor.GYROSCOPE_RAW, 400) +# it's recommended to set both setBatchReportThreshold and setMaxBatchReports to 20 when integrating in a pipeline with a lot of input/output connections +# above this threshold packets will be sent in batch of X, if the host is not blocked and USB bandwidth is available +imu.setBatchReportThreshold(1) +# maximum number of IMU packets in a batch, if it's reached device will block sending until host can receive it +# if lower or equal to batchReportThreshold then the sending is always blocking on device +# useful to reduce device's CPU load and number of lost packets, if CPU load is high on device side due to multiple nodes +imu.setMaxBatchReports(10) + +# Link plugins IMU -> XLINK +imu.out.link(xlinkOut.input) + +imu.enableFirmwareUpdate(True) + +# Pipeline is defined, now we can connect to the device +with dai.Device(pipeline) as device: + + def timeDeltaToMilliS(delta) -> float: + return delta.total_seconds()*1000 + + # Output queue for imu bulk packets + imuQueue = device.getOutputQueue(name="imu", maxSize=50, blocking=False) + baseTs = None + while True: + imuData = imuQueue.get() # blocking call, will wait until a new data has arrived + + imuPackets = imuData.packets + for imuPacket in imuPackets: + acceleroValues = imuPacket.acceleroMeter + gyroValues = imuPacket.gyroscope + + acceleroTs = acceleroValues.timestamp.get() + gyroTs = gyroValues.timestamp.get() + if baseTs is None: + baseTs = acceleroTs if acceleroTs < gyroTs else gyroTs + acceleroTs = timeDeltaToMilliS(acceleroTs - baseTs) + gyroTs = timeDeltaToMilliS(gyroTs - baseTs) + + imuF = "{:.06f}" + tsF = "{:.03f}" + + print(f"Accelerometer timestamp: {tsF.format(acceleroTs)} ms") + print(f"Accelerometer [m/s^2]: x: {imuF.format(acceleroValues.x)} y: {imuF.format(acceleroValues.y)} z: {imuF.format(acceleroValues.z)}") + print(f"Gyroscope timestamp: {tsF.format(gyroTs)} ms") + print(f"Gyroscope [rad/s]: x: {imuF.format(gyroValues.x)} y: {imuF.format(gyroValues.y)} z: {imuF.format(gyroValues.z)} ") + + if cv2.waitKey(1) == ord('q'): + break diff --git a/examples/IMU/imu_rotation_vector.py b/examples/IMU/imu_rotation_vector.py index ae76c8c25..1485c096f 100755 --- a/examples/IMU/imu_rotation_vector.py +++ b/examples/IMU/imu_rotation_vector.py @@ -16,6 +16,7 @@ # enable ROTATION_VECTOR at 400 hz rate imu.enableIMUSensor(dai.IMUSensor.ROTATION_VECTOR, 400) +# it's recommended to set both setBatchReportThreshold and setMaxBatchReports to 20 when integrating in a pipeline with a lot of input/output connections # above this threshold packets will be sent in batch of X, if the host is not blocked and USB bandwidth is available imu.setBatchReportThreshold(1) # maximum number of IMU packets in a batch, if it's reached device will block sending until host can receive it From 9a167df5100e9908d7c9fb90adb52e09cc538d23 Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Tue, 24 May 2022 17:14:23 +0300 Subject: [PATCH 30/44] Fix ColorCamera resource allocation when setIspScale is used --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 055a74b2e..a72618f29 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 055a74b2ee372e76baaae32e3f535789002f759e +Subproject commit a72618f29a423f9c27a13cf02995221a7044447c From 281becdf6e6c4c7bea2dd5491b95b63d762738fe Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Tue, 24 May 2022 18:40:43 +0300 Subject: [PATCH 31/44] OpenVINO add more explicit error logging --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index a72618f29..2307e9ca4 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit a72618f29a423f9c27a13cf02995221a7044447c +Subproject commit 2307e9ca464eb904d812f6cc3b1a68547862c96b From fafabbd6ab35a98b3acc57830e63e0af8564d2d0 Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Wed, 25 May 2022 21:41:16 +0200 Subject: [PATCH 32/44] Update core with updated 2021.4.2 blob used for testing --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 005bab925..5978d4c68 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 005bab9254947c479492dd2a3577bcbc9cacd49b +Subproject commit 5978d4c68816aeee8a00bdec678a26f633d16151 From c2a1ef0abba6838f25a4f24ef52059a4b3f53e60 Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Thu, 26 May 2022 13:43:03 +0200 Subject: [PATCH 33/44] Update IMU driver for CM4-POE --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 2307e9ca4..9a9d89c0f 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 2307e9ca464eb904d812f6cc3b1a68547862c96b +Subproject commit 9a9d89c0f52a2ee964901a2502e0b4d3de308633 From 9019f5b99ba76666d6adffac19dd40e5e57a7c84 Mon Sep 17 00:00:00 2001 From: Erol444 Date: Fri, 27 May 2022 19:38:02 +0200 Subject: [PATCH 34/44] Fix syntax error due to API change of DeviceInfo --- examples/bootloader/flash_bootloader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/bootloader/flash_bootloader.py b/examples/bootloader/flash_bootloader.py index 191f4dad1..50fc077da 100755 --- a/examples/bootloader/flash_bootloader.py +++ b/examples/bootloader/flash_bootloader.py @@ -20,7 +20,7 @@ exit(-1) else: for i, di in enumerate(deviceInfos): - print(f'[{i}] {di.getMxId()} [{di.desc.protocol.name}]', end='') + print(f'[{i}] {di.getMxId()} [{di.protocol.name}]', end='') if di.state == dai.XLinkDeviceState.X_LINK_BOOTLOADER: with dai.DeviceBootloader(di) as bl: print(f' current bootloader: {bl.getVersion()}', end='') From 2c6da14923181fb643ac0f48d3ad5c9b2c68976a Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Fri, 27 May 2022 21:56:23 +0200 Subject: [PATCH 35/44] Add prompt to IMU firmware updater --- depthai-core | 2 +- examples/IMU/imu_firmware_update.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 9a9d89c0f..1592ecfb4 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 9a9d89c0f52a2ee964901a2502e0b4d3de308633 +Subproject commit 1592ecfb46cf76f35ed0b34252879e50661b0ff5 diff --git a/examples/IMU/imu_firmware_update.py b/examples/IMU/imu_firmware_update.py index daee59b9c..1dbf071bb 100755 --- a/examples/IMU/imu_firmware_update.py +++ b/examples/IMU/imu_firmware_update.py @@ -5,6 +5,13 @@ import time import math +print("Warning! Flashing IMU firmware can potentially soft brick your device and should be done with caution.") +print("Do not unplug your device while the IMU firmware is flashing.") +print("Type 'y' and press enter to proceed, otherwise exits: ") +if input() != 'y': + print("Prompt declined, exiting...") + exit(-1) + # Create pipeline pipeline = dai.Pipeline() From 63f6cbf973bff6acbfbc153bb869d4c514cf65bf Mon Sep 17 00:00:00 2001 From: Erol444 Date: Fri, 27 May 2022 22:27:14 +0200 Subject: [PATCH 36/44] Specify the IP for device amanger --- utilities/device_manager.py | 52 ++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/utilities/device_manager.py b/utilities/device_manager.py index 2d4d5a578..4e3d7fc5b 100644 --- a/utilities/device_manager.py +++ b/utilities/device_manager.py @@ -69,6 +69,25 @@ def wait(self): type = getattr(dai.DeviceBootloader.Type, values['bootType']) return (str(event) == "Submit", type) +class SelectIP: + def __init__(self): + self.ok = False + layout = [ + [sg.Text("Specify the custom IP of the OAK PoE\ncamera you want to connect to")], + [ + sg.Text("IPv4:", font=('Arial', 10, 'bold'), text_color="black"), + sg.InputText(key="ip", size=(16, 2)), + ], + [sg.Submit(), sg.Cancel()], + ] + self.window = sg.Window("Specify IP", layout, size=(300,110), modal=True, finalize=True) + def wait(self): + event, values = self.window.Read() + self.window.close() + if str(event) == "Cancel" or values is None or not check_ip(values["ip"]): + return False, "" + return True, values["ip"] + def unlockConfig(window, devType): if devType == "POE": for el in CONF_INPUT_POE: @@ -322,7 +341,8 @@ def connectToDevice(device): [ sg.Text("Select device: ", size=(15, 1), font=('Arial', 10, 'bold'), text_color="black"), sg.Combo(allDevices, tmp, size=(30, 5), key="devices", enable_events=True), - sg.Button("Search") + sg.Button("Search", font=('Arial', 10, 'bold')), + sg.Button("Specify IP") ], [ sg.Text("Name of connected device:", size=(30, 1), font=('Arial', 10, 'bold'), text_color="black"), @@ -443,6 +463,7 @@ def connectToDevice(device): if event == sg.WIN_CLOSED: break dev = values['devices'] + if event == "devices": if dev != "Select device": # "allow flashing bootloader" boots latest bootloader first @@ -459,13 +480,26 @@ def connectToDevice(device): unlockConfig(window, devType) else: window.Element('progress').update("No device selected.") - if event == "Search": + elif event == "Search": getDevices(window, devices) lockConfig(window) - if event == "Flash newest Bootloader": + elif event == "Specify IP": + select = SelectIP() + ok, ip = select.wait() + if ok: + di = dai.DeviceInfo(ip) + di.state = dai.XLinkDeviceState.X_LINK_BOOTLOADER + di.protocol = dai.XLinkProtocol.X_LINK_TCP_IP + devices[ip] = di # Add to devices dict + window.Element('devices').update(ip) # Show to user + bl = connectToDevice(di) + devType = getDeviceType(bl) + getConfigs(window, bl, devType, di) + unlockConfig(window, devType) + elif event == "Flash newest Bootloader": bl.close() flashBootloader(window, devices[values['devices']]) - if event == "Flash configuration": + elif event == "Flash configuration": bl.close() flashConfig(values, devices[values['devices']], devType, values['staticBut']) bl = connectToDevice(devices[values['devices']]) @@ -476,20 +510,20 @@ def connectToDevice(device): else: devices.clear() window.Element('devices').update("Search for devices", values=[]) - if event == "Factory reset": + elif event == "Factory reset": bl.close() factoryReset(devices[values['devices']]) - if event == "Flash DAP": + elif event == "Flash DAP": file = sg.popup_get_file("Select .dap file", file_types=(('DepthAI Application Package', '*.dap'), ('All Files', '*.* *'))) bl = None flashFromFile(file, devices[values['devices']]) - if event == "configReal": + elif event == "configReal": window['-COL1-'].update(visible=False) window['-COL2-'].update(visible=True) - if event == "aboutReal": + elif event == "aboutReal": window['-COL2-'].update(visible=False) window['-COL1-'].update(visible=True) - if event == "recoveryMode": + elif event == "recoveryMode": bl = None flashFromUsb(devices[values['devices']]) window.close() From fc28841097665c914406c9d6b8bcdad5ec1ac5ce Mon Sep 17 00:00:00 2001 From: Erol444 Date: Thu, 2 Jun 2022 14:36:02 +0200 Subject: [PATCH 37/44] Updated device manager docs --- docs/source/components/bootloader.rst | 4 ++-- utilities/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/components/bootloader.rst b/docs/source/components/bootloader.rst index 87927e32a..9b98a7f7c 100644 --- a/docs/source/components/bootloader.rst +++ b/docs/source/components/bootloader.rst @@ -21,14 +21,14 @@ Device Manager ``device_manager.py`` is a Python helper that interfaces with device :ref:`Bootloader` and bootloader configuration. It can be found at `depthai-python/utilities `__. -.. image:: https://user-images.githubusercontent.com/18037362/170479657-faacd06d-5f7e-4215-a821-005d58a5f379.png +.. image:: https://user-images.githubusercontent.com/18037362/171629704-0f78f31a-1778-4338-8ac0-bdfb0d2d593f.png Device Manager Usage -------------------- **About device tab** - Select a camera to see its metadata - like MxID, flashed bootloader version, device state etc. -* First, we need to select the device using the dropdown. You can click ``Search`` to search for all available cameras, either via USB port or on LAN (PoE OAKs). +* First, we need to select the device using the dropdown. You can click ``Search`` to search for all available cameras, either via USB port or on LAN (PoE OAKs). You can also **Specify IP** if your OAK PoE camera isn't in the same LAN. * ``Flash newest Bootloader`` button will flash the ``newest bootloader`` to the device. You can select AUTO, USB or NETWORK bootloader. * **AUTO** will select the connection type of bootloader with which the camera is currently connected to. If you are connected via USB (doing factory reset) to an OAK PoE camera, you shouldn't select AUTO, as it will flash USB bootloader. diff --git a/utilities/README.md b/utilities/README.md index e2c12c0b7..02856eb4c 100644 --- a/utilities/README.md +++ b/utilities/README.md @@ -4,6 +4,6 @@ This folder contains DepthAI utility tools. ## Device Manager -![Device Manager](https://user-images.githubusercontent.com/18037362/170479657-faacd06d-5f7e-4215-a821-005d58a5f379.png) +![Device Manager](https://user-images.githubusercontent.com/18037362/171629704-0f78f31a-1778-4338-8ac0-bdfb0d2d593f.png) ``device_manager.py`` helps interfacing with the device [Bootloader](https://docs.luxonis.com/projects/api/en/latest/components/bootloader) and bootloader configuration. See [Device Manager Usage](https://docs.luxonis.com/projects/api/en/latest/components/bootloader/#device-manager-usage) to see how to use this utility. From 782b341460c14a3c0931950e1df9599d9f4d720a Mon Sep 17 00:00:00 2001 From: Erol444 Date: Thu, 2 Jun 2022 21:11:12 +0200 Subject: [PATCH 38/44] Added device search (table popup), improved exception printing, --- docs/source/components/bootloader.rst | 7 +- utilities/device_manager.py | 161 ++++++++++++++++++-------- 2 files changed, 117 insertions(+), 51 deletions(-) diff --git a/docs/source/components/bootloader.rst b/docs/source/components/bootloader.rst index 9b98a7f7c..97abd163b 100644 --- a/docs/source/components/bootloader.rst +++ b/docs/source/components/bootloader.rst @@ -28,13 +28,16 @@ Device Manager Usage **About device tab** - Select a camera to see its metadata - like MxID, flashed bootloader version, device state etc. -* First, we need to select the device using the dropdown. You can click ``Search`` to search for all available cameras, either via USB port or on LAN (PoE OAKs). You can also **Specify IP** if your OAK PoE camera isn't in the same LAN. +* First we have to select the device we want to connect (boot) to, you can select that using: + + * **Dropdown** which contains found device MX Ids. Dropdown will only get updated when starting the app. + * **Specify IP** button if your OAK PoE camera isn't in the same LAN. + * **Search** feature - a new window will show that has a table with all available cameras (either via USB port or on LAN - OAK PoEs), their MxId, name, and status. Clicking on a table row will select that device and boot to it. * ``Flash newest Bootloader`` button will flash the ``newest bootloader`` to the device. You can select AUTO, USB or NETWORK bootloader. * **AUTO** will select the connection type of bootloader with which the camera is currently connected to. If you are connected via USB (doing factory reset) to an OAK PoE camera, you shouldn't select AUTO, as it will flash USB bootloader. * **USB** bootloader will try to boot the application that is stored on flash memory. If it can't find flashed application, it will just behave as normal USB OAK - so it will wait until a host computer initializes the application. * **NETWORK** bootloader is used by the OAK PoE cameras, and is flashed at the factory. It handles network initialization so the OAK PoE cameras can be booted through the LAN. - * ``Factory reset`` will erase the whole flash content and re-flash it with only the USB or NETWORK bootloader. Flashed application (pipeline, assets) and bootloader configurations will be lost. * ``Boot into USB recovery mode`` will force eg. OAK PoE camera to be available through the USB connector, even if its boot pins are set to PoE booting. It is mostly used by our firmware developers. diff --git a/utilities/device_manager.py b/utilities/device_manager.py index 4e3d7fc5b..5f7e91cc4 100644 --- a/utilities/device_manager.py +++ b/utilities/device_manager.py @@ -4,7 +4,7 @@ import depthai as dai import tempfile import PySimpleGUI as sg - +import sys CONF_TEXT_POE = ['ipTypeText', 'ipText', 'maskText', 'gatewayText', 'dnsText', 'dnsAltText', 'networkTimeoutText', 'macText'] CONF_INPUT_POE = ['staticBut', 'dynamicBut', 'ip', 'mask', 'gateway', 'dns', 'dnsAlt', 'networkTimeout', 'mac'] @@ -12,6 +12,15 @@ CONF_INPUT_USB = ['usbTimeout', 'usbSpeed'] USB_SPEEDS = ["UNKNOWN", "LOW", "FULL", "HIGH", "SUPER", "SUPER_PLUS"] +devices = dict() + +def PrintException(): + exc_type, exc_obj, tb = sys.exc_info() + f = tb.tb_frame + lineno = tb.tb_lineno + filename = f.f_code.co_filename + print('Exception in {}, line {}; {}'.format(filename, lineno, exc_obj)) + def check_ip(s: str): if s == "": return False @@ -88,6 +97,58 @@ def wait(self): return False, "" return True, values["ip"] +class SearchDevice: + def __init__(self): + self.infos = [] + layout = [ + [sg.Text("Select an OAK camera you would like to connect to.", font=('Arial', 10, 'bold'))], + [sg.Table(values=[], headings=["MxId", "Name", "State"], + # col_widths=[25, 17, 17], + # def_col_width=25, + # max_col_width=200, + # background_color='light blue', + display_row_numbers=True, + justification='right', + num_rows=8, + alternating_row_color='lightyellow', + key='table', + row_height=35, + # size=(500, 300) + expand_x = True, + enable_events = True, + enable_click_events = True, + ) + ], + [sg.Button('Search', size=(15, 2), font=('Arial', 10, 'bold'))], + ] + self.window = sg.Window("Select Device", layout, size=(550,375), modal=True, finalize=True) + self.search_devices() + + def search_devices(self): + self.infos = dai.XLinkConnection.getAllConnectedDevices() + if not self.infos: + sg.Popup("No devices found.") + else: + rows = [] + for info in self.infos: + rows.append([info.getMxId(), info.name, deviceStateTxt(info.state)]) + self.window['table'].update(values=rows) + + def wait(self) -> dai.DeviceInfo: + while True: + event, values = self.window.Read() + if event is None: + self.window.close() + return None + elif str(event) == 'Search': + self.search_devices() + elif len(event) == 3 and event[0] == "table" and event[1] == "+CLICKED+": + # User selected a device + deviceIndex = event[2][0] + deviceSelected = self.infos[deviceIndex] + self.window.close() + return deviceSelected + def unlockConfig(window, devType): if devType == "POE": for el in CONF_INPUT_POE: @@ -107,7 +168,6 @@ def unlockConfig(window, devType): window['Flash DAP'].update(disabled=False) window['recoveryMode'].update(disabled=False) - def lockConfig(window): for conf in [CONF_INPUT_POE, CONF_INPUT_USB]: for el in conf: @@ -132,8 +192,7 @@ def lockConfig(window): window.Element('commit').update("-version-") window.Element('devState').update("-state-") - -def getDevices(window, devices): +def getDevices(window): try: listedDevices = [] devices.clear() @@ -143,16 +202,15 @@ def getDevices(window, devices): sg.Popup("No devices found.") else: for deviceInfo in deviceInfos: - # print(deviceInfo.state) - listedDevices.append(deviceInfo.name) - devices[deviceInfo.name] = deviceInfo + deviceTxt = deviceInfo.getMxId() + listedDevices.append(deviceTxt) + devices[deviceTxt] = deviceInfo window.Element('devices').update("Select device", values=listedDevices) except Exception as ex: - print(f'Exception: {ex}') + PrintException() sg.Popup(f'{ex}') - -def getConfigs(window, bl, devType, device): +def getConfigs(window, bl: dai.DeviceBootloader, devType, device: dai.DeviceInfo): try: conf = bl.readConfig() if conf is not None: @@ -173,7 +231,7 @@ def getConfigs(window, bl, devType, device): window.Element('usbSpeed').update(str(conf.getUsbMaxSpeed()).split('.')[1]) window.Element('devName').update(device.name) - window.Element('devNameConf').update(device.name) + window.Element('devNameConf').update(device.getMxId()) window.Element('newBoot').update(dai.DeviceBootloader.getEmbeddedBootloaderVersion()) # The "isEmbeddedVersion" tells you whether BL had to be booted, @@ -185,13 +243,12 @@ def getConfigs(window, bl, devType, device): window.Element('version').update(dai.__version__) window.Element('commit').update(dai.__commit__) - window.Element('devState').update(str(devices[device.name].state).split(".")[1]) + window.Element('devState').update(deviceStateTxt(device.state)) except Exception as ex: - print(f'Exception: {ex}') + PrintException() sg.Popup(f'{ex}') - -def flashBootloader(window, device): +def flashBootloader(window, device: dai.DeviceInfo): # FIXME - to flash bootloader, boot the same device again (from saved device info) but with allowFlashingBootloader = True try: sel = SelectBootloader(['AUTO', 'USB', 'NETWORK'], "Select bootloader type to flash.") @@ -208,11 +265,10 @@ def flashBootloader(window, device): window.Element('currBoot').update(bl.getVersion()) pr.finish("Flashed newest bootloader version.") except Exception as ex: - print(f'Exception: {ex}') + PrintException() sg.Popup(f'{ex}') - -def flashConfig(values, device, devType, staticIp): +def flashConfig(values, device: dai.DeviceInfo, devType: str, staticIp: bool): try: bl = dai.DeviceBootloader(device, True) conf = dai.DeviceBootloader.Config() @@ -248,11 +304,10 @@ def flashConfig(values, device, devType, staticIp): else: sg.Popup("Flashing successful.") except Exception as ex: - print(f'Exception: {ex}') + PrintException() sg.Popup(f'{ex}') - -def factoryReset(device): +def factoryReset(device: dai.DeviceInfo): sel = SelectBootloader(['USB', 'NETWORK'], "Select bootloader type used for factory reset.") ok, type = sel.wait() if not ok: @@ -273,20 +328,20 @@ def factoryReset(device): pr.finish(msg) tmpBlFw.close() except Exception as ex: - print(f'Exception: {ex}') + PrintException() sg.Popup(f'{ex}') -def getDeviceType(bl): +def getDeviceType(bl: dai.DeviceBootloader) -> str: try: if bl.getType() == dai.DeviceBootloader.Type.NETWORK: return "POE" else: return "USB" except Exception as ex: - print(f'Exception: {ex}') + PrintException() sg.Popup(f'{ex}') -def flashFromFile(file, device): +def flashFromFile(file, device: dai.DeviceInfo): try: bl = dai.DeviceBootloader(device, True) if str(file)[-3:] == "dap": @@ -294,40 +349,31 @@ def flashFromFile(file, device): else: sg.Popup("Selected file is not .dap!") except Exception as ex: - print(f'Exception: {ex}') + PrintException() sg.Popup(f'{ex}') - -def flashFromUsb(device): +def flashFromUsb(device: dai.DeviceInfo): try: bl = dai.DeviceBootloader(device, True) bl.bootUsbRomBootloader() except Exception as ex: - print(f'Exception: {ex}') + PrintException() sg.Popup(f'{ex}') - -def connectToDevice(device): +def connectToDevice(device: dai.DeviceInfo) -> dai.DeviceBootloader: try: bl = dai.DeviceBootloader(device, False) return bl except Exception as ex: - print(f'Exception: {ex}') + PrintException() sg.Popup(f'{ex}') -sg.theme('LightGrey2') +def deviceStateTxt(state: dai.XLinkDeviceState) -> str: + return str(state).replace("XLinkDeviceState.X_LINK_", "") -# first device search -allDevices = [] -devices = dict() -tmp = "Search for devices" -deviceInfos = dai.XLinkConnection.getAllConnectedDevices() +# Start defying layout -if deviceInfos: - tmp = "Select device" - for deviceInfo in deviceInfos: - allDevices.append(deviceInfo.name) - devices[deviceInfo.name] = deviceInfo +sg.theme('LightGrey2') # layout for device tab aboutDeviceLayout = [ @@ -340,7 +386,7 @@ def connectToDevice(device): [sg.HSeparator()], [ sg.Text("Select device: ", size=(15, 1), font=('Arial', 10, 'bold'), text_color="black"), - sg.Combo(allDevices, tmp, size=(30, 5), key="devices", enable_events=True), + sg.Combo([], "Select device", size=(30, 5), key="devices", enable_events=True), sg.Button("Search", font=('Arial', 10, 'bold')), sg.Button("Specify IP") ], @@ -456,7 +502,16 @@ def connectToDevice(device): devType = "" bl = None -window = sg.Window(title="Device Manager", icon="assets/icon.png", layout=layout, size=(645, 380)) + +window = sg.Window(title="Device Manager", + icon="assets/icon.png", + layout=layout, + size=(645, 380), + finalize=True # So we can do First search for devices + ) + +# First device search +getDevices(window) while True: event, values = window.read() @@ -470,19 +525,27 @@ def connectToDevice(device): # which makes the information of current bootloader, etc.. not correct (can be checked by "isEmbeddedVersion") # So leave it to false, uncomment the isEmbeddedVersion below and only boot into latest bootlaoder upon the request to flash new bootloader # bl = dai.DeviceBootloader(devices[values['devices']], False) - if str(devices[values['devices']].state).replace("XLinkDeviceState.X_LINK_", "") == "BOOTED": + device = devices[values['devices']] + if deviceStateTxt(device.state) == "BOOTED": # device is already booted somewhere else sg.Popup("Device is already booted somewhere else!") else: - bl = connectToDevice(devices[values['devices']]) + bl = connectToDevice(device) devType = getDeviceType(bl) - getConfigs(window, bl, devType, devices[values['devices']]) + getConfigs(window, bl, devType, device) unlockConfig(window, devType) else: window.Element('progress').update("No device selected.") elif event == "Search": - getDevices(window, devices) lockConfig(window) + selDev = SearchDevice() + di = selDev.wait() + if di is not None: + window.Element('devices').update(di.getMxId()) + bl = connectToDevice(di) + devType = getDeviceType(bl) + getConfigs(window, bl, devType, di) + unlockConfig(window, devType) elif event == "Specify IP": select = SelectIP() ok, ip = select.wait() From 66e6a0cdd74d50c3e09774062351a3faaf002c73 Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 3 Jun 2022 01:55:54 +0200 Subject: [PATCH 39/44] Fix device search on device manager (#598) --- utilities/device_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/utilities/device_manager.py b/utilities/device_manager.py index 5f7e91cc4..6b9cad826 100644 --- a/utilities/device_manager.py +++ b/utilities/device_manager.py @@ -537,6 +537,7 @@ def deviceStateTxt(state: dai.XLinkDeviceState) -> str: else: window.Element('progress').update("No device selected.") elif event == "Search": + getDevices(window) # Re-search devices for dropdown lockConfig(window) selDev = SearchDevice() di = selDev.wait() From 3122975c71f52ffb38c50d72dce1488b282b03d2 Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Fri, 3 Jun 2022 03:47:39 +0300 Subject: [PATCH 40/44] Update core --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 5978d4c68..29091c0d6 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 5978d4c68816aeee8a00bdec678a26f633d16151 +Subproject commit 29091c0d63d09d989fe029214c69d003b77f15f9 From 04157600ac9ab4a23fc90f9093be0bf75e43242b Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Fri, 3 Jun 2022 19:27:53 +0300 Subject: [PATCH 41/44] Update depthai-core --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 29091c0d6..68751dd90 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 29091c0d63d09d989fe029214c69d003b77f15f9 +Subproject commit 68751dd908433eda44b80eafddc40348df7b4397 From 42dcae08fda072bbd20231310758bdbfefaf1a9a Mon Sep 17 00:00:00 2001 From: SzabolcsGergely Date: Fri, 3 Jun 2022 20:12:26 +0300 Subject: [PATCH 42/44] Update core --- depthai-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/depthai-core b/depthai-core index 68751dd90..5d7f8afea 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 68751dd908433eda44b80eafddc40348df7b4397 +Subproject commit 5d7f8afea0a3346acbfa08ae47ee787c6eaa296c From db4f7ccaadf9b7c1d58783e05e9e835b0c2cf1ea Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Mon, 6 Jun 2022 14:51:51 +0200 Subject: [PATCH 43/44] Added "dai.DeviceInfo.desc" API compatibility --- CMakeLists.txt | 2 +- depthai-core | 2 +- src/XLinkBindings.cpp | 17 +++++++++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fd6d86eb..09990d7fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,7 +131,7 @@ else() endif() message(STATUS "Mypy available, creating and checking stubs. Running with generate_stubs.py ${TARGET_NAME} ${bindings_directory}") add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND - ${CMAKE_COMMAND} -E env + ${CMAKE_COMMAND} -E env # PATH (dlls) "PATH=${HUNTER_INSTALL_PREFIX}/bin${SYS_PATH_SEPARATOR}$ENV{PATH}" # Python path (to find compiled module) diff --git a/depthai-core b/depthai-core index 5d7f8afea..ce8b527be 160000 --- a/depthai-core +++ b/depthai-core @@ -1 +1 @@ -Subproject commit 5d7f8afea0a3346acbfa08ae47ee787c6eaa296c +Subproject commit ce8b527be57485a5d252a87f8284f55d2a914e9e diff --git a/src/XLinkBindings.cpp b/src/XLinkBindings.cpp index aa00d9179..7e8caaedc 100644 --- a/src/XLinkBindings.cpp +++ b/src/XLinkBindings.cpp @@ -1,10 +1,13 @@ #include "XLinkBindings.hpp" +// std +#include +#include + +// depthai #include "depthai/xlink/XLinkConnection.hpp" #include "depthai/xlink/XLinkStream.hpp" -#include -#include void XLinkBindings::bind(pybind11::module &m, void *pCallstack) { @@ -60,6 +63,16 @@ void XLinkBindings::bind(pybind11::module &m, void *pCallstack) .def_readwrite("platform", &DeviceInfo::platform) .def_readwrite("status", &DeviceInfo::status) .def("__repr__", &DeviceInfo::toString) + // deprecated + .def_property("desc", [](py::object& self) { + // Issue an deprecation warning + PyErr_WarnEx(PyExc_DeprecationWarning, "desc field is deprecated, use name/mxid and others instead.", 1); + return self; + },[](DeviceInfo& i, DeviceInfo di){ + // Issue an deprecation warning + PyErr_WarnEx(PyExc_DeprecationWarning, "desc field is deprecated, use name/mxid and others instead.", 1); + i = di; + }) ; deviceDesc From d9ad60c0264fc711ff61963ed62c9d05fa097a17 Mon Sep 17 00:00:00 2001 From: TheMarpe Date: Mon, 6 Jun 2022 17:47:29 +0200 Subject: [PATCH 44/44] Bump version to 2.16.0.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 09990d7fd..a02a1c2db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ endif() # Pybindings project set(TARGET_NAME depthai) -project(depthai VERSION "1") # revision of bindings [depthai-core].[rev] +project(depthai VERSION "0") # revision of bindings [depthai-core].[rev] # Set default build type depending on context set(default_build_type "Release")