From 1bfa465050fdb3267580477d9b8c4abe487d8dd1 Mon Sep 17 00:00:00 2001 From: asahtik Date: Wed, 11 Dec 2024 12:04:43 +0100 Subject: [PATCH 01/28] Drop addResize --- .../src/pipeline/datatype/ImageManipConfigV2Bindings.cpp | 1 - include/depthai/pipeline/datatype/ImageManipConfigV2.hpp | 6 ------ src/pipeline/datatype/ImageManipConfigV2.cpp | 4 ---- 3 files changed, 11 deletions(-) diff --git a/bindings/python/src/pipeline/datatype/ImageManipConfigV2Bindings.cpp b/bindings/python/src/pipeline/datatype/ImageManipConfigV2Bindings.cpp index 8ca319fd7..147f006cc 100644 --- a/bindings/python/src/pipeline/datatype/ImageManipConfigV2Bindings.cpp +++ b/bindings/python/src/pipeline/datatype/ImageManipConfigV2Bindings.cpp @@ -53,7 +53,6 @@ void bind_imagemanipconfigv2(pybind11::module& m, void* pCallstack) { py::arg("rect"), py::arg("normalizedCoords"), DOC(dai, ImageManipConfigV2, addCropRotatedRect)) - .def("addResize", &ImageManipConfigV2::addResize, py::arg("w"), py::arg("h"), DOC(dai, ImageManipConfigV2, addResize)) .def( "addScale", [](ImageManipConfigV2& self, float scale) { return self.addScale(scale); }, py::arg("scale"), DOC(dai, ImageManipConfigV2, addScale)) .def( diff --git a/include/depthai/pipeline/datatype/ImageManipConfigV2.hpp b/include/depthai/pipeline/datatype/ImageManipConfigV2.hpp index dd067ae48..5f79a1a99 100644 --- a/include/depthai/pipeline/datatype/ImageManipConfigV2.hpp +++ b/include/depthai/pipeline/datatype/ImageManipConfigV2.hpp @@ -440,12 +440,6 @@ class ImageManipConfigV2 : public Buffer { * @param normalizedCoords If true, the coordinates are normalized to range [0, 1] where 1 maps to the width/height of the image */ ImageManipConfigV2& addCropRotatedRect(dai::RotatedRect rotatedRect, bool normalizedCoords = false); - /** - * Resizes the image to the specified width and height - * @param w Width of the output image - * @param h Height of the output image - */ - ImageManipConfigV2& addResize(uint32_t w, uint32_t h); /** * Rescales the image using the specified factors * @param scaleX Scale factor for the X axis diff --git a/src/pipeline/datatype/ImageManipConfigV2.cpp b/src/pipeline/datatype/ImageManipConfigV2.cpp index c285da0af..174c38414 100644 --- a/src/pipeline/datatype/ImageManipConfigV2.cpp +++ b/src/pipeline/datatype/ImageManipConfigV2.cpp @@ -28,10 +28,6 @@ ImageManipConfigV2& ImageManipConfigV2::addCropRotatedRect(dai::RotatedRect rota normalizedCoords); return *this; } -ImageManipConfigV2& ImageManipConfigV2::addResize(uint32_t w, uint32_t h) { - base.resize(w, h); - return *this; -} ImageManipConfigV2& ImageManipConfigV2::addScale(float scaleX, float scaleY) { base.resize(scaleX, scaleY, true); return *this; From 55a5c29d133ad42e8ffc76b067f2b77b51f09c7e Mon Sep 17 00:00:00 2001 From: asahtik Date: Wed, 11 Dec 2024 12:22:44 +0100 Subject: [PATCH 02/28] Bump rvc4 fw (drop addResize) --- cmake/Depthai/DepthaiDeviceRVC4Config.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake index 7e7983dd4..98319a314 100644 --- a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake +++ b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake @@ -4,4 +4,4 @@ set(DEPTHAI_DEVICE_RVC4_MATURITY "snapshot") # "version if applicable" # set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+93f7b75a885aa32f44c5e9f53b74470c49d2b1af") -set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+d5266c0f39a05f037383771eb0b26b4a9ba9f1ce") +set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+83971dd8eb4d0c225f74026b02012c569e642a1b") From 5a9ff64b6c6bf19b722de6436043380f30d34441 Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Fri, 13 Dec 2024 17:25:54 +0100 Subject: [PATCH 03/28] Fix a bug on ImageManipV2 node when needsImage=false The bug happened whenever needsImage was set to false and inImageData was then a nullptr. --- include/depthai/pipeline/node/ImageManipV2.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/depthai/pipeline/node/ImageManipV2.hpp b/include/depthai/pipeline/node/ImageManipV2.hpp index ab2101fa5..ea84b4f2c 100644 --- a/include/depthai/pipeline/node/ImageManipV2.hpp +++ b/include/depthai/pipeline/node/ImageManipV2.hpp @@ -106,7 +106,6 @@ void ImageManipV2::loop(N& node, while(node.isRunning()) { std::shared_ptr pConfig; - std::shared_ptr inImageData; bool hasConfig = false; bool needsImage = true; bool skipImage = false; @@ -132,7 +131,6 @@ void ImageManipV2::loop(N& node, logger->warn("No input image, skipping frame"); continue; } - inImageData = inImage->data; if(!hasConfig) { auto _pConfig = node.inputConfig.template tryGet(); if(_pConfig != nullptr) { @@ -169,7 +167,7 @@ void ImageManipV2::loop(N& node, bool success = true; { auto t3 = steady_clock::now(); - success = apply(inImageData, outImage->getData()); + success = apply(inImage->getData(), outImage->getData()); auto t4 = steady_clock::now(); getFrame(config, *inImage, *outImage); From e33143c9b3c0628f780d269eaa98841ce5c1c5ac Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Fri, 13 Dec 2024 18:08:09 +0100 Subject: [PATCH 04/28] Fix compilation --- include/depthai/pipeline/node/ImageManipV2.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/depthai/pipeline/node/ImageManipV2.hpp b/include/depthai/pipeline/node/ImageManipV2.hpp index ea84b4f2c..a8528b02d 100644 --- a/include/depthai/pipeline/node/ImageManipV2.hpp +++ b/include/depthai/pipeline/node/ImageManipV2.hpp @@ -167,7 +167,7 @@ void ImageManipV2::loop(N& node, bool success = true; { auto t3 = steady_clock::now(); - success = apply(inImage->getData(), outImage->getData()); + success = apply(inImage->data, outImage->getData()); auto t4 = steady_clock::now(); getFrame(config, *inImage, *outImage); From 46de848897aadf80d069f0bc8ddd6f9de7f78fee Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Fri, 13 Dec 2024 18:40:56 +0100 Subject: [PATCH 05/28] Add tests and bump RVC4 FW --- cmake/Depthai/DepthaiDeviceRVC4Config.cmake | 2 +- tests/CMakeLists.txt | 4 + .../pipeline/node/image_manip_v2_test.cpp | 84 +++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 tests/src/ondevice_tests/pipeline/node/image_manip_v2_test.cpp diff --git a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake index 13d7b55b3..982160495 100644 --- a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake +++ b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake @@ -4,4 +4,4 @@ set(DEPTHAI_DEVICE_RVC4_MATURITY "snapshot") # "version if applicable" # set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+93f7b75a885aa32f44c5e9f53b74470c49d2b1af") -set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+72e173caa7b97651f1ca4428091c7e46ed2c62f1") +set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+81617bcfe7b7da9eda9654b5b3d3d3254b59a47d") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8b5481734..c83c632fd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -396,3 +396,7 @@ dai_set_test_labels(script_node_test ondevice rvc2_all rvc4 ci) # StereoDepth test dai_add_test(stereo_depth_node_test src/ondevice_tests/stereo_depth_node_test.cpp) dai_set_test_labels(stereo_depth_node_test ondevice rvc2_all rvc4 ci) + +# ImageManipV2 test +dai_add_test(image_manip_v2_node_test src/ondevice_tests/pipeline/node/image_manip_v2_test.cpp) +dai_set_test_labels(image_manip_v2_node_test ondevice rvc2_all rvc4 ci) diff --git a/tests/src/ondevice_tests/pipeline/node/image_manip_v2_test.cpp b/tests/src/ondevice_tests/pipeline/node/image_manip_v2_test.cpp new file mode 100644 index 000000000..c917050c0 --- /dev/null +++ b/tests/src/ondevice_tests/pipeline/node/image_manip_v2_test.cpp @@ -0,0 +1,84 @@ +#include +#include + +#include "depthai/common/CameraBoardSocket.hpp" +#include "depthai/depthai.hpp" +#include "depthai/pipeline/datatype/ImgFrame.hpp" +#include "depthai/pipeline/node/Camera.hpp" + +void testManipBasic(bool runSyncOnHost) { + // Create pipeline + dai::Pipeline p; + auto cam = p.create()->build(dai::CameraBoardSocket::CAM_A); + auto manip = p.create(); + cam->requestFullResolutionOutput()->link(manip->inputImage); + manip->initialConfig.addCrop(100, 200, 400, 400); + manip->initialConfig.setFrameType(dai::ImgFrame::Type::NV12); + + auto manipQueue = manip->out.createOutputQueue(); + p.start(); + + for(int i = 0; i < 10; i++) { + auto inFrame = manipQueue->get(); + REQUIRE(inFrame != nullptr); + REQUIRE(inFrame->getWidth() == 400); + REQUIRE(inFrame->getHeight() == 400); + REQUIRE(inFrame->getType() == dai::ImgFrame::Type::NV12); + } +} + +void testManipDynamic(bool runSyncOnHost, bool reuiseImage) { + // Create pipeline + dai::Pipeline p; + auto cam = p.create()->build(dai::CameraBoardSocket::CAM_A); + auto manip = p.create(); + cam->requestFullResolutionOutput()->link(manip->inputImage); + auto manipQueue = manip->out.createOutputQueue(); + manip->inputConfig.setReusePreviousMessage(false); // Control the rate with the config input + auto configQueue = manip->inputConfig.createInputQueue(); + p.start(); + int cropX = 100; + int cropY = 200; + int cropW = 400; + int cropH = 400; + for(int i = 0; i < 10; i++) { + cropX = cropX + 10; + cropY = cropY + 10; + cropW = cropW + 10; + cropH = cropH + 10; + auto manipConfig = std::make_shared(); + manipConfig->addCrop(cropX, cropY, cropW, cropH); + manipConfig->setFrameType(dai::ImgFrame::Type::NV12); + manipConfig->setReusePreviousImage(reuiseImage); + configQueue->send(manipConfig); + auto inFrame = manipQueue->get(); + REQUIRE(inFrame != nullptr); + REQUIRE(inFrame->getType() == dai::ImgFrame::Type::NV12); + REQUIRE(inFrame->getWidth() == cropW); + REQUIRE(inFrame->getHeight() == cropH); + } +} + +TEST_CASE("Sync node runs on device") { + testManipBasic(false); +} + +TEST_CASE("Sync node runs on host") { + testManipBasic(true); +} + +TEST_CASE("Dynamic config sync node runs on device, reuse") { + testManipDynamic(false, true); +} + +TEST_CASE("Dynamic config sync node runs on host, reuse") { + testManipDynamic(true, true); +} + +TEST_CASE("Dynamic config sync node runs on device, no reuse") { + testManipDynamic(false, false); +} + +TEST_CASE("Dynamic config sync node runs on host, no reuse") { + testManipDynamic(true, false); +} From f9e16867246078aae4f87291c541ea6b7528bc1c Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Mon, 16 Dec 2024 18:30:01 +0100 Subject: [PATCH 06/28] Bump WS with better memory safety --- 3rdparty/foxglove/ws-protocol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/foxglove/ws-protocol b/3rdparty/foxglove/ws-protocol index db02aacb1..51976b795 160000 --- a/3rdparty/foxglove/ws-protocol +++ b/3rdparty/foxglove/ws-protocol @@ -1 +1 @@ -Subproject commit db02aacb1b1ca1cd34a0f09f2276008050170ae4 +Subproject commit 51976b79560ecfa08ef3a0df4773ace1c986c328 From 2873abef5970a5c613d14be5a0e3651d4a6d257a Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Tue, 17 Dec 2024 14:27:30 +0100 Subject: [PATCH 07/28] Release GIL for pipline stop, avoid a lockup --- bindings/python/src/pipeline/PipelineBindings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/src/pipeline/PipelineBindings.cpp b/bindings/python/src/pipeline/PipelineBindings.cpp index 648f75415..0fbea8f87 100644 --- a/bindings/python/src/pipeline/PipelineBindings.cpp +++ b/bindings/python/src/pipeline/PipelineBindings.cpp @@ -240,7 +240,7 @@ void PipelineBindings::bind(pybind11::module& m, void* pCallstack){ py::gil_scoped_release release; p.wait(); }) - .def("stop", &Pipeline::stop) + .def("stop", &Pipeline::stop, py::call_guard(), DOC(dai, Pipeline, stop)) .def("run", [](Pipeline& p) { { From fd5cb64a2b696c405c758f6ff07b24a59f220850 Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Tue, 17 Dec 2024 17:12:31 +0100 Subject: [PATCH 08/28] Acquire the GIL when calling Python callbacks in the MessageQueue --- bindings/python/src/MessageQueueBindings.cpp | 26 ++++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/bindings/python/src/MessageQueueBindings.cpp b/bindings/python/src/MessageQueueBindings.cpp index 54201722e..e819d332a 100644 --- a/bindings/python/src/MessageQueueBindings.cpp +++ b/bindings/python/src/MessageQueueBindings.cpp @@ -40,15 +40,25 @@ void MessageQueueBindings::bind(pybind11::module& m, void* pCallstack) { // Bind DataOutputQueue auto addCallbackLambda = [](MessageQueue& q, py::function cb) -> int { - pybind11::module inspect_module = pybind11::module::import("inspect"); - pybind11::object result = inspect_module.attr("signature")(cb).attr("parameters"); + pybind11::module inspectModule = pybind11::module::import("inspect"); + pybind11::object result = inspectModule.attr("signature")(cb).attr("parameters"); auto numParams = pybind11::len(result); - if(numParams == 2) { - return q.addCallback(cb.cast)>>()); - } else if(numParams == 1) { - return q.addCallback(cb.cast)>>()); - } else if(numParams == 0) { - return q.addCallback(cb.cast>()); + + if (numParams == 2) { + return q.addCallback([cb](std::string msg, std::shared_ptr data) { + pybind11::gil_scoped_acquire gil; + cb(msg, data); + }); + } else if (numParams == 1) { + return q.addCallback([cb](std::shared_ptr data) { + pybind11::gil_scoped_acquire gil; + cb(data); + }); + } else if (numParams == 0) { + return q.addCallback([cb]() { + pybind11::gil_scoped_acquire gil; + cb(); + }); } else { throw py::value_error("Callback must take either zero, one or two arguments"); } From 353dda8aca5e670d782adc0c9ab79be9178dbf2f Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Wed, 18 Dec 2024 12:59:36 +0100 Subject: [PATCH 09/28] Bump the release to alpha.11 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b5ab48b8..2436230a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,7 +164,7 @@ endif() # Create depthai project project(depthai VERSION "3.0.0" LANGUAGES CXX C) set(DEPTHAI_PRE_RELEASE_TYPE "alpha") # Valid options are "alpha", "beta", "rc", "" -set(DEPTHAI_PRE_RELEASE_VERSION "10") +set(DEPTHAI_PRE_RELEASE_VERSION "11") # Set DEPTHAI_VERSION universally, not conditionally set(DEPTHAI_VERSION ${PROJECT_VERSION}-${DEPTHAI_PRE_RELEASE_TYPE}.${DEPTHAI_PRE_RELEASE_VERSION}) From bee44fa1ea4caa60e2af1e312cfe484d8e417d21 Mon Sep 17 00:00:00 2001 From: Adam Serafin Date: Thu, 19 Dec 2024 10:48:34 +0100 Subject: [PATCH 10/28] V3 snaps error handling (#1188) --- CMakeLists.txt | 14 +++-- examples/cpp/CMakeLists.txt | 2 +- examples/cpp/{HostNodes => Events}/events.cpp | 19 ------- .../python/{HostNodes => Events}/events.py | 9 +--- include/depthai/utility/EventsManager.hpp | 6 ++- src/utility/EventsManager.cpp | 54 +++++++++++++++---- 6 files changed, 57 insertions(+), 47 deletions(-) rename examples/cpp/{HostNodes => Events}/events.cpp (54%) rename examples/python/{HostNodes => Events}/events.py (69%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2436230a3..cafae167f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,6 @@ option(DEPTHAI_NEW_FIND_PYTHON "Use new FindPython module" ON) if(NOT DEPTHAI_OPENCV_SUPPORT) set(DEPTHAI_MERGED_TARGET OFF CACHE BOOL "Enable merged target build" FORCE) endif() -option(DEPTHAI_ENABLE_EVENTS_MANAGER "Enable Events Manager" ON) set(DEPTHAI_HAS_APRIL_TAG ${DEPTHAI_ENABLE_APRIL_TAG}) if(WIN32) @@ -108,18 +107,17 @@ endif() if(DEPTHAI_ENABLE_PROTOBUF) option(DEPTHAI_ENABLE_REMOTE_CONNECTION "Enable Remote Connection support" ON) + if(DEPTHAI_ENABLE_CURL) + option(DEPTHAI_ENABLE_EVENTS_MANAGER "Enable Events Manager" ON) + else() + message(STATUS "Events Manager disabled because Protobuf & curl support is disabled.") + option(DEPTHAI_ENABLE_EVENTS_MANAGER "Enable Events Manager" OFF) + endif() else() option(DEPTHAI_ENABLE_REMOTE_CONNECTION "Enable Remote Connection support" OFF) message(STATUS "Remote Connection support disabled because Protobuf support is disabled.") endif() -if(DEPTHAI_ENABLE_EVENTS_MANAGER) - if(NOT DEPTHAI_ENABLE_PROTOBUF OR NOT DEPTHAI_CURL_SUPPORT) - message(STATUS "Events Manager disabled because Protobuf & curl support is disabled.") - set(DEPTHAI_ENABLE_EVENTS_MANAGER OFF) - endif() -endif() - if(DEPTHAI_BUILD_PYTHON) list(APPEND VCPKG_MANIFEST_FEATURES "python-bindings") endif() diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index 7f82a68bf..2bfa0dc8c 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -286,7 +286,7 @@ dai_add_example(model_zoo RVC2/ModelZoo/model_zoo.cpp OFF OFF) # Events Manager if(DEPTHAI_ENABLE_EVENTS_MANAGER) - dai_add_example(events HostNodes/events.cpp ON OFF) + dai_add_example(events Events/events.cpp ON OFF) endif() # Image Align dai_add_example(image_align RVC2/ImageAlign/image_align.cpp OFF OFF) diff --git a/examples/cpp/HostNodes/events.cpp b/examples/cpp/Events/events.cpp similarity index 54% rename from examples/cpp/HostNodes/events.cpp rename to examples/cpp/Events/events.cpp index 01e26c7b1..dba3019a5 100644 --- a/examples/cpp/HostNodes/events.cpp +++ b/examples/cpp/Events/events.cpp @@ -1,4 +1,3 @@ - #include #include #include @@ -27,20 +26,6 @@ int main(int argc, char* argv[]) { std::vector> data; data.emplace_back(fileData); eventsManager->sendEvent("testdata", nullptr, data, {"tag3", "tag4"}, {{"key8", "value8"}}); - auto fileData2 = std::make_shared("/test.txt"); - std::vector> data2; - data2.push_back(fileData2); - // will fail, you sendEvent instead of sendSnap - eventsManager->sendSnap("testdata2", nullptr, data2, {"tag5", "tag6"}, {{"key8", "value8"}}); - auto fileData3 = std::make_shared("/test.jpg"); - std::vector> data3; - data3.push_back(fileData3); - eventsManager->sendSnap("testdata3", nullptr, data3, {"tag7", "tag8"}, {{"key8", "value8"}}); - std::vector> data4; - data4.push_back(fileData); - data4.push_back(fileData2); - eventsManager->sendEvent("testdata4", nullptr, data4, {"tag9", "tag10"}, {{"key8", "value8"}}); - data4.push_back(fileData3); while(pipeline.isRunning()) { auto rgb = previewQ->get(); @@ -49,10 +34,6 @@ int main(int argc, char* argv[]) { if(!sent) { eventsManager->sendSnap("rgb", rgb, {}, {"tag11", "tag12"}, {{"key", "value"}}); - // will fail due to two images being sent, use sendEvent instead - eventsManager->sendSnap("test2", rgb, data3, {"tag13", "tag14"}, {{"key8", "value8"}}); - // will fail, sendSnap requires only one image data to be present - eventsManager->sendSnap("test3", rgb, data4, {"tag13", "tag14"}, {{"key8", "value8"}}); sent = true; } // diff --git a/examples/python/HostNodes/events.py b/examples/python/Events/events.py similarity index 69% rename from examples/python/HostNodes/events.py rename to examples/python/Events/events.py index 9a7dba994..fdedb29ca 100644 --- a/examples/python/HostNodes/events.py +++ b/examples/python/Events/events.py @@ -1,4 +1,3 @@ - #!/usr/bin/env python3 import cv2 @@ -10,7 +9,7 @@ # Create pipeline with dai.Pipeline() as pipeline: # Define sources and outputs - camRgb = pipeline.create(dai.node.Camera) + camRgb = pipeline.create(dai.node.Camera).build() # Properties qRgb = camRgb.requestOutput((256,256)).createOutputQueue() @@ -22,10 +21,6 @@ time.sleep(2) fileData = dai.EventData(b'Hello, world!', "hello.txt", "text/plain") eventMan.sendEvent("test2", None, [fileData], ["tag1", "tag2"], {"key1": "value1"}) - fileData2 = dai.EventData("/test.txt") - # will fail, sendSnap needs an image - eventMan.sendSnap("test3", None, [fileData2], ["tag1", "tag2"], {"key1": "value1"}) - eventMan.sendEvent("test4", None, [fileData, fileData2], ["tag1", "tag2"], {"key1": "value1"}) pipeline.start() frame = None @@ -39,8 +34,6 @@ frame = inRgb.getCvFrame() if not eventSent: eventMan.sendSnap("rgb", inRgb, [], ["tag1", "tag2"], {"key1": "value1"}) - # will fail, sendSnap requires only image and no extra data - eventMan.sendSnap("rgb2", inRgb, [fileData2], ["tag1", "tag2"], {"key1": "value1"}) eventSent = True if frame is not None: diff --git a/include/depthai/utility/EventsManager.hpp b/include/depthai/utility/EventsManager.hpp index 5378e7731..8648d51f4 100644 --- a/include/depthai/utility/EventsManager.hpp +++ b/include/depthai/utility/EventsManager.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -19,7 +20,6 @@ class Event; } // namespace event } // namespace proto namespace utility { -#ifdef DEPTHAI_ENABLE_PROTOBUF enum class EventDataType { DATA, FILE_URL, IMG_FRAME, ENCODED_FRAME, NN_DATA }; class EventData { public: @@ -172,7 +172,9 @@ class EventsManager { std::string cacheDir; bool uploadCachedOnStart; bool cacheIfCannotSend; + std::atomic stopEventBuffer; + std::condition_variable eventBufferCondition; + std::mutex eventBufferConditionMutex; }; -#endif } // namespace utility } // namespace dai diff --git a/src/utility/EventsManager.cpp b/src/utility/EventsManager.cpp index 4921a2788..77cc239e1 100644 --- a/src/utility/EventsManager.cpp +++ b/src/utility/EventsManager.cpp @@ -10,6 +10,8 @@ #include "Environment.hpp" #include "Logging.hpp" +#include "cpr/cpr.h" +#include "depthai/schemas/Event.pb.h" namespace dai { namespace utility { @@ -101,18 +103,23 @@ EventsManager::EventsManager(std::string url, bool uploadCachedOnStart, float pu queueSize(10), publishInterval(publishInterval), logResponse(false), - verifySsl(false), + verifySsl(true), connected(false), cacheDir("/internal/private"), uploadCachedOnStart(uploadCachedOnStart), - cacheIfCannotSend(false) { + cacheIfCannotSend(false), + stopEventBuffer(false) { sourceAppId = utility::getEnv("AGENT_APP_ID"); sourceAppIdentifier = utility::getEnv("AGENT_APP_IDENTIFIER"); token = utility::getEnv("DEPTHAI_HUB_API_KEY"); + if(token.empty()) { + throw std::runtime_error("Missing token, please set DEPTHAI_HUB_API_KEY environment variable or use setToken method"); + } eventBufferThread = std::make_unique([this]() { - while(true) { + while(!stopEventBuffer) { sendEventBuffer(); - std::this_thread::sleep_for(std::chrono::milliseconds(static_cast(this->publishInterval * 1000))); + std::unique_lock lock(eventBufferMutex); + eventBufferCondition.wait_for(lock, std::chrono::seconds(static_cast(this->publishInterval))); } }); checkConnection(); @@ -122,7 +129,14 @@ EventsManager::EventsManager(std::string url, bool uploadCachedOnStart, float pu } EventsManager::~EventsManager() { - eventBufferThread->join(); + stopEventBuffer = true; + { + std::unique_lock lock(eventBufferMutex); + eventBufferCondition.notify_one(); + } + if(eventBufferThread->joinable()) { + eventBufferThread->join(); + } } void EventsManager::sendEventBuffer() { @@ -138,8 +152,6 @@ void EventsManager::sendEventBuffer() { } return; } - // Create request - cpr::Url url = static_cast(this->url + "/v1/events"); for(auto& eventM : eventBuffer) { auto& event = eventM->event; batchEvent->add_events()->Swap(event.get()); @@ -147,7 +159,19 @@ void EventsManager::sendEventBuffer() { } std::string serializedEvent; batchEvent->SerializeToString(&serializedEvent); - cpr::Response r = cpr::Post(cpr::Url{url}, cpr::Body{serializedEvent}, cpr::Header{{"Authorization", "Bearer " + token}}, cpr::VerifySsl(verifySsl)); + cpr::Url reqUrl = static_cast(this->url + "/v1/events"); + cpr::Response r = cpr::Post( + cpr::Url{reqUrl}, + cpr::Body{serializedEvent}, + cpr::Header{{"Authorization", "Bearer " + token}}, + cpr::VerifySsl(verifySsl), + cpr::ProgressCallback( + [&](cpr::cpr_off_t downloadTotal, cpr::cpr_off_t downloadNow, cpr::cpr_off_t uploadTotal, cpr::cpr_off_t uploadNow, intptr_t userdata) -> bool { + if(stopEventBuffer) { + return false; + } + return true; + })); if(r.status_code != cpr::status::HTTP_OK) { logger::error("Failed to send event: {} {}", r.text, r.status_code); } else { @@ -272,7 +296,19 @@ void EventsManager::sendFile(const std::shared_ptr& file, const std:: }}; header["File-Size"] = std::to_string(std::filesystem::file_size(file->data)); } - cpr::Response r = cpr::Post(cpr::Url{url}, cpr::Multipart{fileM}, cpr::Header{header}, cpr::VerifySsl(verifySsl)); + cpr::Response r = cpr::Post( + cpr::Url{url}, + cpr::Multipart{fileM}, + cpr::Header{header}, + cpr::VerifySsl(verifySsl), + + cpr::ProgressCallback( + [&](cpr::cpr_off_t downloadTotal, cpr::cpr_off_t downloadNow, cpr::cpr_off_t uploadTotal, cpr::cpr_off_t uploadNow, intptr_t userdata) -> bool { + if(stopEventBuffer) { + return false; + } + return true; + })); if(r.status_code != cpr::status::HTTP_OK) { logger::error("Failed to upload file: {} error code {}", r.text, r.status_code); } From d26fccde9c51211377160ca84ebe95db46e02945 Mon Sep 17 00:00:00 2001 From: lnotspotl Date: Thu, 19 Dec 2024 05:11:53 -0500 Subject: [PATCH 11/28] switch to restapi --- .../depthai/modelzoo/NNModelDescription.hpp | 2 +- include/depthai/modelzoo/Zoo.hpp | 2 +- src/modelzoo/Zoo.cpp | 68 +++++++++++-------- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/include/depthai/modelzoo/NNModelDescription.hpp b/include/depthai/modelzoo/NNModelDescription.hpp index b4f6f29f0..b731dd405 100644 --- a/include/depthai/modelzoo/NNModelDescription.hpp +++ b/include/depthai/modelzoo/NNModelDescription.hpp @@ -54,7 +54,7 @@ struct NNModelDescription { /** SNPE version = OPTIONAL parameter */ std::string snpeVersion; - /** modelPrecisionType */ + /** modelPrecisionType = OPTIONAL parameter */ std::string modelPrecisionType; }; diff --git a/include/depthai/modelzoo/Zoo.hpp b/include/depthai/modelzoo/Zoo.hpp index f20547fac..ad0dcc403 100644 --- a/include/depthai/modelzoo/Zoo.hpp +++ b/include/depthai/modelzoo/Zoo.hpp @@ -3,7 +3,7 @@ #include "depthai/modelzoo/NNModelDescription.hpp" namespace dai { -constexpr const char* MODEL_ZOO_URL = "https://api.cloud.luxonis.com/graphql"; +constexpr const char* MODEL_ZOO_URL = "https://easyml.cloud.luxonis.com/models/api/v1/models/download"; constexpr const char* MODEL_ZOO_DEFAULT_CACHE_DIRECTORY = ".depthai_cached_models"; /** diff --git a/src/modelzoo/Zoo.cpp b/src/modelzoo/Zoo.cpp index 7576a8f60..b2c59cf1a 100644 --- a/src/modelzoo/Zoo.cpp +++ b/src/modelzoo/Zoo.cpp @@ -9,7 +9,9 @@ #include "utility/Logging.hpp" #ifdef DEPTHAI_ENABLE_CURL - #include + #include + #include + #include namespace dai { class ZooManager { public: @@ -139,10 +141,10 @@ bool checkIsErrorHub(const cpr::Response& response) { // Check if response is an HTTP error if(response.status_code != cpr::status::HTTP_OK) return true; - // If there was no HTTP error, check response content for errors + // If there was no HTTP error, check presence of required fields nlohmann::json responseJson = nlohmann::json::parse(response.text); - if(responseJson.contains("errors")) return true; - if(responseJson["data"]["ml"]["modelDownloads"].is_null()) return true; + if(!responseJson.contains("hash")) return true; + if(!responseJson.contains("download_links")) return true; // All checks passed - no errors yay return false; @@ -206,30 +208,35 @@ bool ZooManager::isModelCached() const { } void ZooManager::downloadModel() { - // graphql query to send to Hub - always the same - constexpr std::string_view MODEL_ZOO_QUERY = "query MlDownloads($input: MlModelDownloadsInput!) {ml { modelDownloads(input : $input) { data }}}"; - - // Setup request body - nlohmann::json requestBody; - requestBody["query"] = MODEL_ZOO_QUERY; - - // Add REQUIRED parameters - requestBody["variables"]["input"]["platform"] = modelDescription.platform; - requestBody["variables"]["input"]["slug"] = modelDescription.model; - - // Add OPTIONAL parameters - if(!modelDescription.optimizationLevel.empty()) { - requestBody["variables"]["input"]["optimizationLevel"] = modelDescription.optimizationLevel; - } - if(!modelDescription.compressionLevel.empty()) { - requestBody["variables"]["input"]["compressionLevel"] = modelDescription.compressionLevel; - } - if(!modelDescription.snpeVersion.empty()) { - requestBody["variables"]["input"]["snpeVersion"] = modelDescription.snpeVersion; + // Add request parameters + cpr::Parameters params; + + // Required parameters + // clang-format off + std::vector> requiredParams = { + {"slug", modelDescription.model}, + {"platform", modelDescription.platform} + }; + // clang-format on + for(const auto& param : requiredParams) { + params.Add({param.first, param.second}); } - if(!modelDescription.modelPrecisionType.empty()) { - requestBody["variables"]["input"]["modelPrecisionType"] = modelDescription.modelPrecisionType; + + // Optional parameters + // clang-format off + std::vector> optionalParams = { + {"optimizationLevel", modelDescription.optimizationLevel}, + {"compressionLevel", modelDescription.compressionLevel}, + {"snpeVersion", modelDescription.snpeVersion}, + {"modelPrecisionType", modelDescription.modelPrecisionType} + }; + // clang-format on + for(const auto& param : optionalParams) { + if(!param.second.empty()) { + params.Add({param.first, param.second}); + } } + // Set the Authorization headers cpr::Header headers = { {"Content-Type", "application/json"}, @@ -237,16 +244,17 @@ void ZooManager::downloadModel() { if(!apiKey.empty()) { headers["Authorization"] = "Bearer " + apiKey; } - // Send HTTP request to Hub - cpr::Response response = cpr::Post(cpr::Url{MODEL_ZOO_URL}, headers, cpr::Body{requestBody.dump()}); + + // Send HTTP GET request to REST endpoint + cpr::Response response = cpr::Get(cpr::Url{MODEL_ZOO_URL}, headers, params); if(checkIsErrorHub(response)) { removeModelCacheFolder(); throw std::runtime_error(generateErrorMessageHub(response)); } - // Extract download link from response + // Extract download links from response nlohmann::json responseJson = nlohmann::json::parse(response.text); - auto downloadLinks = responseJson["data"]["ml"]["modelDownloads"]["data"].get>(); + auto downloadLinks = responseJson["download_links"].get>(); // Download all files and store them in cache folder for(const auto& downloadLink : downloadLinks) { From ffc3ab0ac506f8d99008f15518673082495d87d3 Mon Sep 17 00:00:00 2001 From: lnotspotl Date: Fri, 20 Dec 2024 02:40:37 -0500 Subject: [PATCH 12/28] Check if pipeline is built before serializing pipeline in RemoteConnection --- bindings/python/src/pipeline/PipelineBindings.cpp | 2 ++ src/remote_connection/RemoteConnectionImpl.cpp | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/bindings/python/src/pipeline/PipelineBindings.cpp b/bindings/python/src/pipeline/PipelineBindings.cpp index 0fbea8f87..91f9f6163 100644 --- a/bindings/python/src/pipeline/PipelineBindings.cpp +++ b/bindings/python/src/pipeline/PipelineBindings.cpp @@ -258,6 +258,8 @@ void PipelineBindings::bind(pybind11::module& m, void* pCallstack){ } p.stop(); }) + .def("build", &Pipeline::build) + .def("isBuilt", &Pipeline::isBuilt) .def("isRunning", &Pipeline::isRunning) .def("processTasks", &Pipeline::processTasks, py::arg("waitForTasks") = false, py::arg("timeoutSeconds") = -1.0) .def("enableHolisticRecord", &Pipeline::enableHolisticRecord, py::arg("recordConfig"), DOC(dai, Pipeline, enableHolisticRecord)) diff --git a/src/remote_connection/RemoteConnectionImpl.cpp b/src/remote_connection/RemoteConnectionImpl.cpp index c65c19f78..f3476cf15 100644 --- a/src/remote_connection/RemoteConnectionImpl.cpp +++ b/src/remote_connection/RemoteConnectionImpl.cpp @@ -10,6 +10,7 @@ #include "foxglove/websocket/common.hpp" #include "pipeline/datatype/Buffer.hpp" #include "pipeline/datatype/ImgAnnotations.hpp" +#include "utility/ErrorMacros.hpp" #include "utility/Logging.hpp" #include "utility/ProtoSerializable.hpp" #include "utility/Resources.hpp" @@ -352,6 +353,10 @@ void RemoteConnectionImpl::exposeKeyPressedService() { } void RemoteConnectionImpl::exposePipelineService(const Pipeline& pipeline) { + // Make sure pipeline is built so that we can serialize it. + // If not built, an error is thrown is case there are host -> device or device -> host connections. + DAI_CHECK(pipeline.isBuilt(), "Pipeline is not built. Call Pipeline::build first!"); + std::vector services; auto pipelineService = foxglove::ServiceWithoutId(); pipelineService.name = "pipelineSchema"; From 41c216a1ad7b6052b66d5fdd1c24ec7818901232 Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Sun, 29 Dec 2024 12:42:04 +0100 Subject: [PATCH 13/28] Add initial on host implementations --- CMakeLists.txt | 3 +- .../src/pipeline/node/BenchmarkBindings.cpp | 6 +- examples/python/HostNodes/benchmark.py | 34 +++++ .../pipeline/datatype/BenchmarkReport.hpp | 14 +- include/depthai/pipeline/node/BenchmarkIn.hpp | 37 ++++- .../depthai/pipeline/node/BenchmarkOut.hpp | 20 ++- .../properties/BenchmarkInProperties.hpp | 32 +++++ ...tiesOut.hpp => BenchmarkOutProperties.hpp} | 6 +- .../properties/BenchmarkProperties.hpp | 22 --- .../properties/BenchmarkPropertiesIn.hpp | 22 --- src/pipeline/node/Benchmark.cpp | 24 ---- src/pipeline/node/BenchmarkIn.cpp | 133 ++++++++++++++++++ src/pipeline/node/BenchmarkOut.cpp | 66 +++++++++ 13 files changed, 328 insertions(+), 91 deletions(-) create mode 100644 examples/python/HostNodes/benchmark.py create mode 100644 include/depthai/properties/BenchmarkInProperties.hpp rename include/depthai/properties/{BenchmarkPropertiesOut.hpp => BenchmarkOutProperties.hpp} (71%) delete mode 100644 include/depthai/properties/BenchmarkProperties.hpp delete mode 100644 include/depthai/properties/BenchmarkPropertiesIn.hpp delete mode 100644 src/pipeline/node/Benchmark.cpp create mode 100644 src/pipeline/node/BenchmarkIn.cpp create mode 100644 src/pipeline/node/BenchmarkOut.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cafae167f..ed2dd190d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,7 +368,8 @@ set(TARGET_CORE_SOURCES src/pipeline/node/DetectionNetwork.cpp src/pipeline/node/Script.cpp src/pipeline/node/Pool.cpp - src/pipeline/node/Benchmark.cpp + src/pipeline/node/BenchmarkIn.cpp + src/pipeline/node/BenchmarkOut.cpp src/pipeline/node/SpatialDetectionNetwork.cpp src/pipeline/node/SystemLogger.cpp src/pipeline/node/SpatialLocationCalculator.cpp diff --git a/bindings/python/src/pipeline/node/BenchmarkBindings.cpp b/bindings/python/src/pipeline/node/BenchmarkBindings.cpp index 781fbc1bc..79e6d23cc 100644 --- a/bindings/python/src/pipeline/node/BenchmarkBindings.cpp +++ b/bindings/python/src/pipeline/node/BenchmarkBindings.cpp @@ -30,9 +30,13 @@ void bind_benchmark(pybind11::module& m, void* pCallstack) { benchmarkOut.def_readonly("out", &BenchmarkOut::out, DOC(dai, node, BenchmarkOut, out)) .def_readonly("input", &BenchmarkOut::input, DOC(dai, node, BenchmarkOut, input)) .def("setNumMessagesToSend", &BenchmarkOut::setNumMessagesToSend, py::arg("num"), DOC(dai, node, BenchmarkOut, setNumMessagesToSend)) + .def("setRunOnHost", &BenchmarkOut::setRunOnHost, py::arg("runOnHost"), DOC(dai, node, BenchmarkOut, setRunOnHost)) .def("setFps", &BenchmarkOut::setFps, py::arg("fps"), DOC(dai, node, BenchmarkOut, setFps)); benchmarkIn.def_readonly("input", &BenchmarkIn::input, DOC(dai, node, BenchmarkIn, input)) .def_readonly("report", &BenchmarkIn::report, DOC(dai, node, BenchmarkIn, report)) .def_readonly("passthrough", &BenchmarkIn::passthrough, DOC(dai, node, BenchmarkIn, passthrough)) - .def("setNumMessagesToGet", &BenchmarkIn::setNumMessagesToGet, py::arg("num"), DOC(dai, node, BenchmarkIn, setNumMessagesToGet)); + .def("setRunOnHost", &BenchmarkIn::setRunOnHost, py::arg("runOnHost"), DOC(dai, node, BenchmarkIn, setRunOnHost)) + .def("logReportsAsWarnings", &BenchmarkIn::logReportsAsWarnings, py::arg("logReportsAsWarnings"), DOC(dai, node, BenchmarkIn, logReportsAsWarnings)) + .def("measureIndividualLatencies", &BenchmarkIn::measureIndividualLatencies, py::arg("attachLatencies"), DOC(dai, node, BenchmarkIn, measureIndividualLatencies)) + .def("sendReportEveryNMessages", &BenchmarkIn::sendReportEveryNMessages, py::arg("num"), DOC(dai, node, BenchmarkIn, sendReportEveryNMessages)); } diff --git a/examples/python/HostNodes/benchmark.py b/examples/python/HostNodes/benchmark.py new file mode 100644 index 000000000..701f15298 --- /dev/null +++ b/examples/python/HostNodes/benchmark.py @@ -0,0 +1,34 @@ +import depthai as dai +import time + +class TestSource(dai.node.ThreadedHostNode): + def __init__(self, name: str): + super().__init__() + self.name = name + self.output = self.createOutput() + + def run(self): + while self.isRunning(): + buffer = dai.Buffer() + print(f"{self.name} node is sending a buffer!") + self.output.send(buffer) + time.sleep(1) + +with dai.Pipeline(createImplicitDevice=False) as p: + # Create nodes + source = TestSource("source") + benchmarkIn = p.create(dai.node.BenchmarkIn) + benchmarkIn.setRunOnHost(True) + benchmarkIn.sendReportEveryNMessages(100) + benchmarkOut = p.create(dai.node.BenchmarkOut) + benchmarkOut.setRunOnHost(True) + benchmarkOut.setFps(30) + benchmarkOut.out.link(benchmarkIn.input) + outputQueue = benchmarkIn.report.createOutputQueue() + source.output.link(benchmarkOut.input) + + p.start() + while p.isRunning(): + benchmarkReport = outputQueue.get() + assert isinstance(benchmarkReport, dai.BenchmarkReport) + print(f"FPS is {benchmarkReport.fps}") \ No newline at end of file diff --git a/include/depthai/pipeline/datatype/BenchmarkReport.hpp b/include/depthai/pipeline/datatype/BenchmarkReport.hpp index 4d496449f..b70e53ca9 100644 --- a/include/depthai/pipeline/datatype/BenchmarkReport.hpp +++ b/include/depthai/pipeline/datatype/BenchmarkReport.hpp @@ -3,22 +3,20 @@ #include "depthai/pipeline/datatype/Buffer.hpp" namespace dai { -// TODO(before mainline) - API not supported on RVC2 /** * BenchmarkReport message. */ class BenchmarkReport : public Buffer { public: BenchmarkReport() = default; - virtual ~BenchmarkReport() = default; - float fps; - float timeTotal; // seconds - float numMessagesReceived; - float averageLatency; + float fps = 0.0f; + float timeTotal = 0.0f; // seconds + float numMessagesReceived = 0; + float averageLatency = 0.0f; // seconds + + // Only filled if measureIndividualLatencies is set to true std::vector latencies; - // TODO Add jitter, timestamps for start/end, possibly a vector of timestamps for all messages - // TODO BEFORE MAINLINE add setters and getters void serialize(std::vector& metadata, DatatypeEnum& datatype) const override { metadata = utility::serialize(*this); diff --git a/include/depthai/pipeline/node/BenchmarkIn.hpp b/include/depthai/pipeline/node/BenchmarkIn.hpp index 80166c1b8..6b6aa9e94 100644 --- a/include/depthai/pipeline/node/BenchmarkIn.hpp +++ b/include/depthai/pipeline/node/BenchmarkIn.hpp @@ -3,13 +3,12 @@ #include // shared -#include +#include namespace dai { namespace node { -// TODO(before mainline) - API not supported on RVC2 -class BenchmarkIn : public DeviceNodeCRTP { +class BenchmarkIn : public DeviceNodeCRTP, public HostRunnable { public: constexpr static const char* NAME = "BenchmarkIn"; using DeviceNodeCRTP::DeviceNodeCRTP; @@ -30,11 +29,35 @@ class BenchmarkIn : public DeviceNodeCRTP // shared -#include +#include namespace dai { namespace node { -class BenchmarkOut : public DeviceNodeCRTP { +class BenchmarkOut : public DeviceNodeCRTP, public HostRunnable{ public: constexpr static const char* NAME = "BenchmarkOut"; using DeviceNodeCRTP::DeviceNodeCRTP; @@ -34,7 +34,21 @@ class BenchmarkOut : public DeviceNodeCRTP { + /** + * Specify how many messages to measure for each report + */ + uint32_t reportEveryNMessages = 50; + + /** + * Specify whether the latenices are attached to the report individually + */ + bool attachLatencies = false; + + /** + * Send the reports also as logger warnings + */ + bool logReportsAsWarnings = true; +}; + +DEPTHAI_SERIALIZE_EXT(BenchmarkInProperties, reportEveryNMessages); + +} // namespace dai diff --git a/include/depthai/properties/BenchmarkPropertiesOut.hpp b/include/depthai/properties/BenchmarkOutProperties.hpp similarity index 71% rename from include/depthai/properties/BenchmarkPropertiesOut.hpp rename to include/depthai/properties/BenchmarkOutProperties.hpp index 19ee57c22..def89ad74 100644 --- a/include/depthai/properties/BenchmarkPropertiesOut.hpp +++ b/include/depthai/properties/BenchmarkOutProperties.hpp @@ -10,11 +10,11 @@ namespace dai { /** * Specify benchmark properties (number of messages to send/receive) */ -struct BenchmarkPropertiesOut : PropertiesSerializable { +struct BenchmarkOutProperties : PropertiesSerializable { /** * Number of messages to send */ - int numMessages = 50; + int numMessages = -1; /** * FPS for sending, 0 means as fast as possible @@ -22,6 +22,6 @@ struct BenchmarkPropertiesOut : PropertiesSerializable { - /** - * Number of messages to send - */ - int numMessages = 50; -}; - -DEPTHAI_SERIALIZE_EXT(BenchmarkProperties, numMessages); - -} // namespace dai diff --git a/include/depthai/properties/BenchmarkPropertiesIn.hpp b/include/depthai/properties/BenchmarkPropertiesIn.hpp deleted file mode 100644 index 8bbe350f3..000000000 --- a/include/depthai/properties/BenchmarkPropertiesIn.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "depthai/common/ProcessorType.hpp" -#include "depthai/common/optional.hpp" -#include "depthai/pipeline/datatype/DatatypeEnum.hpp" -#include "depthai/properties/Properties.hpp" - -namespace dai { - -/** - * Specify benchmark properties (number of messages to send/receive) - */ -struct BenchmarkPropertiesIn : PropertiesSerializable { - /** - * Number of messages to send - */ - int numMessages = 50; -}; - -DEPTHAI_SERIALIZE_EXT(BenchmarkPropertiesIn, numMessages); - -} // namespace dai diff --git a/src/pipeline/node/Benchmark.cpp b/src/pipeline/node/Benchmark.cpp deleted file mode 100644 index f4af2492e..000000000 --- a/src/pipeline/node/Benchmark.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "depthai/pipeline/node/BenchmarkIn.hpp" -#include "depthai/pipeline/node/BenchmarkOut.hpp" - -namespace dai { -namespace node { - -void BenchmarkOut::setNumMessagesToSend(int num) { - properties.numMessages = num; -} - -void BenchmarkOut::setFps(float fps) { - properties.fps = fps; -} - -void BenchmarkIn::setNumMessagesToGet(int num) { - properties.numMessages = num; -} - -void BenchmarkOut::buildInternal() { - properties.numMessages = -1; // By default send messages indefinitely -} - -} // namespace node -} // namespace dai diff --git a/src/pipeline/node/BenchmarkIn.cpp b/src/pipeline/node/BenchmarkIn.cpp new file mode 100644 index 000000000..414950840 --- /dev/null +++ b/src/pipeline/node/BenchmarkIn.cpp @@ -0,0 +1,133 @@ +#include "depthai/pipeline/node/BenchmarkIn.hpp" +#include "depthai/pipeline/datatype/BenchmarkReport.hpp" +#include + +namespace dai { +namespace node { + +void BenchmarkIn::sendReportEveryNMessages(uint32_t num) { + properties.reportEveryNMessages = num; +} + +void BenchmarkIn::setRunOnHost(bool runOnHost) { + runOnHostVar = runOnHost; +} + +bool BenchmarkIn::runOnHost() const { + return runOnHostVar; +} + +void BenchmarkIn::logReportsAsWarnings(bool logReportsAsWarnings) { + properties.logReportsAsWarnings = logReportsAsWarnings; +} + +void BenchmarkIn::measureIndividualLatencies(bool attachLatencies) { + properties.attachLatencies = attachLatencies; +} + +void BenchmarkIn::run() { + using namespace std::chrono; + + uint32_t numMessages = properties.reportEveryNMessages; + + // Decide if we will store latencies or not + bool storeLatencies = false; + if(properties.attachLatencies) { + if(numMessages <= 1000) { + // We'll store latencies for this batch + storeLatencies = true; + } else { + // Warn upfront if user wanted latencies but # messages is too high + logger->warn("Number of messages > 1000, latencies not individually attached."); + } + } + + uint32_t messageCount = 0; + float totalLatency = 0.0f; + + std::vector latencies; + if(storeLatencies) { + latencies.reserve(numMessages); + } + + auto start = steady_clock::now(); + + while(isRunning()) { + auto inMessage = input.get(); + + // If this is the first message of the batch, reset counters + if(messageCount == 0) { + start = steady_clock::now(); + totalLatency = 0.0f; + + // Clear vector if we are storing latencies + if(storeLatencies) { + latencies.clear(); + latencies.reserve(numMessages); + } + } + + if(messageCount < numMessages) { + auto currentTs = steady_clock::now(); + // If the message itself has a real timestamp, use that instead: + // auto messageTs = inMessage->getTimestamp(); + auto messageTs = currentTs; // In example, just use currentTs + + duration diff = currentTs - messageTs; + logger->trace("Frame latency: {} s", diff.count()); + + // Accumulate for average + totalLatency += diff.count(); + + // Optionally store individual latencies + if(storeLatencies) { + latencies.push_back(diff.count()); + } + + messageCount++; + } + else { + // We reached our batch size, so time to compute and send the report + auto stop = steady_clock::now(); + duration durationS = stop - start; + + auto reportMessage = std::make_shared(); + reportMessage->numMessagesReceived = numMessages; + reportMessage->timeTotal = durationS.count(); + reportMessage->fps = numMessages / durationS.count(); + reportMessage->averageLatency = totalLatency / numMessages; + + // Attach latencies only if we're storing them + if(storeLatencies) { + reportMessage->latencies = latencies; + } + + // Decide how to log (warn or info) once, then do all the logs + auto logFunc = [&](auto fmt, auto... args) { + if(properties.logReportsAsWarnings) { + logger->warn(fmt, std::forward(args)...); + } else { + logger->trace(fmt, std::forward(args)...); + } + }; + + // Unconditional logs, using chosen severity + logFunc("FPS: {}", reportMessage->fps); + logFunc("Messages took {} s", reportMessage->timeTotal); + logFunc("Average latency: {} s", reportMessage->averageLatency); + + // Send out the report + report.send(reportMessage); + logger->trace("Sent report message"); + + // Reset for next batch + messageCount = 0; + } + + // Passthrough the message + passthrough.send(inMessage); + } +} + +} // namespace node +} // namespace dai diff --git a/src/pipeline/node/BenchmarkOut.cpp b/src/pipeline/node/BenchmarkOut.cpp new file mode 100644 index 000000000..98a6bc63f --- /dev/null +++ b/src/pipeline/node/BenchmarkOut.cpp @@ -0,0 +1,66 @@ +#include "depthai/pipeline/node/BenchmarkOut.hpp" + +namespace dai { +namespace node { + +void BenchmarkOut::setNumMessagesToSend(int num) { + properties.numMessages = num; +} + +void BenchmarkOut::setFps(float fps) { + properties.fps = fps; +} + +void BenchmarkOut::setRunOnHost(bool runOnHost) { + runOnHostVar = runOnHost; +} + +bool BenchmarkOut::runOnHost() const { + return runOnHostVar; +} + +void BenchmarkOut::run() { + using namespace std::chrono; + + logger->trace("Wait for the input message."); + auto inMessage = input.get(); + + bool useTiming = (properties.fps > 0); + + auto frameDurationDouble = std::chrono::duration(1.0 / properties.fps); + auto frameDuration = std::chrono::duration_cast(frameDurationDouble); + + auto nextFrameTime = steady_clock::now(); + for(int i = 0; (i < properties.numMessages || properties.numMessages == -1) && isRunning(); i++) { + auto imgMessage = std::dynamic_pointer_cast(inMessage); + if(imgMessage != nullptr) { + logger->trace("Sending img message with id {}", i); + + // Copying metadata and pointing to same data + auto newMessage = std::make_shared(); + newMessage->setMetadata(imgMessage); + newMessage->data = imgMessage->data; + newMessage->setTimestampDevice(steady_clock::now()); + out.send(newMessage); + } else { + logger->trace("Sending message with id {}", i); + out.send(inMessage); + } + + if(useTiming) { + nextFrameTime += frameDuration; + + auto now = steady_clock::now(); + if(nextFrameTime > now) { + auto sleepTime = nextFrameTime - now; + std::this_thread::sleep_for(sleepTime); + } + } + } + + logger->trace("Benchmark out sent all messages!"); +} + + +} // namespace node +} // namespace dai From cd9f3371e1a3f081a8d3b44cccabae44258edf22 Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Sun, 29 Dec 2024 12:56:08 +0100 Subject: [PATCH 14/28] Change which timestamp is used based on where the node runs --- src/pipeline/node/BenchmarkIn.cpp | 9 ++++++--- src/pipeline/node/BenchmarkOut.cpp | 6 +++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/pipeline/node/BenchmarkIn.cpp b/src/pipeline/node/BenchmarkIn.cpp index 414950840..40bbf4bf1 100644 --- a/src/pipeline/node/BenchmarkIn.cpp +++ b/src/pipeline/node/BenchmarkIn.cpp @@ -69,9 +69,12 @@ void BenchmarkIn::run() { if(messageCount < numMessages) { auto currentTs = steady_clock::now(); - // If the message itself has a real timestamp, use that instead: - // auto messageTs = inMessage->getTimestamp(); - auto messageTs = currentTs; // In example, just use currentTs + auto messageTs = steady_clock::now(); + if(runOnHostVar) { + messageTs = inMessage->getTimestamp(); + } else { + messageTs = inMessage->getTimestampDevice(); + } duration diff = currentTs - messageTs; logger->trace("Frame latency: {} s", diff.count()); diff --git a/src/pipeline/node/BenchmarkOut.cpp b/src/pipeline/node/BenchmarkOut.cpp index 98a6bc63f..afbed9634 100644 --- a/src/pipeline/node/BenchmarkOut.cpp +++ b/src/pipeline/node/BenchmarkOut.cpp @@ -40,7 +40,11 @@ void BenchmarkOut::run() { auto newMessage = std::make_shared(); newMessage->setMetadata(imgMessage); newMessage->data = imgMessage->data; - newMessage->setTimestampDevice(steady_clock::now()); + if(runOnHostVar) { + newMessage->setTimestamp(steady_clock::now()); + } else { + newMessage->setTimestampDevice(steady_clock::now()); + } out.send(newMessage); } else { logger->trace("Sending message with id {}", i); From eaf27df37532aa46d99578e79863322f74b7ebb5 Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Sun, 29 Dec 2024 14:58:32 +0100 Subject: [PATCH 15/28] Bump RVC4 FW to use host implementations of benchmark node --- cmake/Depthai/DepthaiDeviceRVC4Config.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake index 982160495..407a52c43 100644 --- a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake +++ b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake @@ -4,4 +4,4 @@ set(DEPTHAI_DEVICE_RVC4_MATURITY "snapshot") # "version if applicable" # set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+93f7b75a885aa32f44c5e9f53b74470c49d2b1af") -set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+81617bcfe7b7da9eda9654b5b3d3d3254b59a47d") +set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+7f850f7d36329e0e994af1a1cd83de723a88edef") From 575f08bedda6648f3aad0f79ee89902a73de2090 Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Sun, 29 Dec 2024 19:46:38 +0100 Subject: [PATCH 16/28] Add missing serializations --- include/depthai/properties/BenchmarkInProperties.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/depthai/properties/BenchmarkInProperties.hpp b/include/depthai/properties/BenchmarkInProperties.hpp index 2497ed959..93a9349b8 100644 --- a/include/depthai/properties/BenchmarkInProperties.hpp +++ b/include/depthai/properties/BenchmarkInProperties.hpp @@ -12,7 +12,7 @@ namespace dai { */ struct BenchmarkInProperties : PropertiesSerializable { /** - * Specify how many messages to measure for each report + * Specify how many messages to measure for each report */ uint32_t reportEveryNMessages = 50; @@ -27,6 +27,6 @@ struct BenchmarkInProperties : PropertiesSerializable Date: Sun, 29 Dec 2024 20:08:42 +0100 Subject: [PATCH 17/28] [RVC4 FW] Bump FW with properties serialization --- cmake/Depthai/DepthaiDeviceRVC4Config.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake index 407a52c43..bda073a99 100644 --- a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake +++ b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake @@ -4,4 +4,4 @@ set(DEPTHAI_DEVICE_RVC4_MATURITY "snapshot") # "version if applicable" # set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+93f7b75a885aa32f44c5e9f53b74470c49d2b1af") -set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+7f850f7d36329e0e994af1a1cd83de723a88edef") +set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+19b67f81b54c146d079d2cbd4485fa153337dc5a") From ae6af8212f7f631e30128bbf22cf5028401c54fa Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Sun, 29 Dec 2024 20:16:45 +0100 Subject: [PATCH 18/28] [RVC2] Complete BenchmarkIn implementation --- cmake/Depthai/DepthaiDeviceSideConfig.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Depthai/DepthaiDeviceSideConfig.cmake b/cmake/Depthai/DepthaiDeviceSideConfig.cmake index 9a0146539..23835ccbb 100644 --- a/cmake/Depthai/DepthaiDeviceSideConfig.cmake +++ b/cmake/Depthai/DepthaiDeviceSideConfig.cmake @@ -2,7 +2,7 @@ set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot") # "full commit hash of device side binary" -set(DEPTHAI_DEVICE_SIDE_COMMIT "c3e98b39b6a5445b2187b4109d03a146c6df37dd") +set(DEPTHAI_DEVICE_SIDE_COMMIT "6b5e087c440e3833a0128d686fe3b6926681f113") # "version if applicable" set(DEPTHAI_DEVICE_SIDE_VERSION "") From 1a0c8f637ef648d48f5935ae54706e50992ae7cf Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Sun, 29 Dec 2024 22:27:37 +0100 Subject: [PATCH 19/28] [RVC2] Add BenchmarkOut implementation --- cmake/Depthai/DepthaiDeviceSideConfig.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Depthai/DepthaiDeviceSideConfig.cmake b/cmake/Depthai/DepthaiDeviceSideConfig.cmake index 23835ccbb..20e2509ad 100644 --- a/cmake/Depthai/DepthaiDeviceSideConfig.cmake +++ b/cmake/Depthai/DepthaiDeviceSideConfig.cmake @@ -2,7 +2,7 @@ set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot") # "full commit hash of device side binary" -set(DEPTHAI_DEVICE_SIDE_COMMIT "6b5e087c440e3833a0128d686fe3b6926681f113") +set(DEPTHAI_DEVICE_SIDE_COMMIT "5e016a328ac84324fb3c6bd8904141191f29dc2e") # "version if applicable" set(DEPTHAI_DEVICE_SIDE_VERSION "") From 45a0397f2285b3338d74d42ef19ef6dfb97190b6 Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Mon, 30 Dec 2024 11:21:54 +0100 Subject: [PATCH 20/28] Add examples and tests --- examples/python/Benchmark/benchmark_camera.py | 17 ++++ examples/python/Benchmark/benchmark_nn.py | 50 ++++++++++++ examples/python/Benchmark/benchmark_simple.py | 29 +++++++ examples/python/CMakeLists.txt | 12 +++ examples/python/HostNodes/benchmark.py | 34 -------- tests/CMakeLists.txt | 4 + .../pipeline/node/benchmark_test.cpp | 80 +++++++++++++++++++ .../regression/camera_concurrency.cpp | 2 +- 8 files changed, 193 insertions(+), 35 deletions(-) create mode 100644 examples/python/Benchmark/benchmark_camera.py create mode 100644 examples/python/Benchmark/benchmark_nn.py create mode 100644 examples/python/Benchmark/benchmark_simple.py delete mode 100644 examples/python/HostNodes/benchmark.py create mode 100644 tests/src/ondevice_tests/pipeline/node/benchmark_test.cpp diff --git a/examples/python/Benchmark/benchmark_camera.py b/examples/python/Benchmark/benchmark_camera.py new file mode 100644 index 000000000..97d7adc6f --- /dev/null +++ b/examples/python/Benchmark/benchmark_camera.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +import depthai as dai +import time + +# Create pipeline +with dai.Pipeline() as pipeline: + # Define source and output + cam = pipeline.create(dai.node.Camera).build() + benchmarkIn = pipeline.create(dai.node.BenchmarkIn) + # benchmarkIn.setRunOnHost(True) # The node can also run on host and include the transfer limitation, default is False + output = cam.requestFullResolutionOutput() + output.link(benchmarkIn.input) + + # Connect to device and start pipeline + pipeline.start() + while pipeline.isRunning(): + time.sleep(1) # Let the logger print out the FPS diff --git a/examples/python/Benchmark/benchmark_nn.py b/examples/python/Benchmark/benchmark_nn.py new file mode 100644 index 000000000..b327460ab --- /dev/null +++ b/examples/python/Benchmark/benchmark_nn.py @@ -0,0 +1,50 @@ +import depthai as dai +import cv2 +import numpy as np + +device = dai.Device() +modelPath = dai.getModelFromZoo(dai.NNModelDescription("yolov6-nano", platform=device.getPlatformAsString())) +modelArhive = dai.NNArchive(modelPath) +inputSize = modelArhive.getInputSize() +type = modelArhive.getConfig().model.inputs[0].preprocessing.daiType + +if type: + try: + frameType = dai.ImgFrame.Type.__getattribute__(type) + except AttributeError: + type = None + +if not type: + if device.getPlatform() == dai.Platform.RVC2: + frameType = dai.ImgFrame.Type.BGR888p + else: + frameType = dai.ImgFrame.Type.BGR888i + + +# Construct the input (white) image for benchmarking +img = np.ones((inputSize[1], inputSize[0], 3), np.uint8) * 255 +inputFrame = dai.ImgFrame() +inputFrame.setCvFrame(img, frameType) + +with dai.Pipeline(device) as p: + benchmarkOut = p.create(dai.node.BenchmarkOut) + benchmarkOut.setRunOnHost(False) # The node can run on host or on device + benchmarkOut.setFps(-1) # As fast as possible + + neuralNetwork = p.create(dai.node.NeuralNetwork).build(benchmarkOut.out, modelArhive) + + benchmarkIn = p.create(dai.node.BenchmarkIn) + benchmarkIn.setRunOnHost(False) # The node can run on host or on device + benchmarkIn.sendReportEveryNMessages(100) + benchmarkIn.logReportsAsWarnings(False) + neuralNetwork.out.link(benchmarkIn.input) + + outputQueue = benchmarkIn.report.createOutputQueue() + inputQueue = benchmarkOut.input.createInputQueue() + + p.start() + inputQueue.send(inputFrame) + while p.isRunning(): + benchmarkReport = outputQueue.get() + assert isinstance(benchmarkReport, dai.BenchmarkReport) + print(f"FPS is {benchmarkReport.fps}") diff --git a/examples/python/Benchmark/benchmark_simple.py b/examples/python/Benchmark/benchmark_simple.py new file mode 100644 index 000000000..9afe70b6d --- /dev/null +++ b/examples/python/Benchmark/benchmark_simple.py @@ -0,0 +1,29 @@ +import depthai as dai + +with dai.Pipeline(createImplicitDevice=False) as p: + # Create a BenchmarkOut node + # It will listen on the input to get the first message and then send it out at a specified rate + # The node sends the same message out (creates new pointers), not deep copies. + benchmarkOut = p.create(dai.node.BenchmarkOut) + benchmarkOut.setRunOnHost(True) # The node can run on host or on device + benchmarkOut.setFps(30) + + # Create a BenchmarkIn node + # Thisn node is receiving the messages on the input and measuring the FPS and latency. + # In the case that the input is with BenchmarkOut, the latency measurement is not always possible, as the message is not deep copied, + # which means that the timestamps stay the same. + benchmarkIn = p.create(dai.node.BenchmarkIn) + benchmarkIn.setRunOnHost(True) # The node can run on host or on device + benchmarkIn.sendReportEveryNMessages(100) + + benchmarkOut.out.link(benchmarkIn.input) + outputQueue = benchmarkIn.report.createOutputQueue() + inputQueue = benchmarkOut.input.createInputQueue() + + p.start() + imgFrame = dai.ImgFrame() + inputQueue.send(imgFrame) + while p.isRunning(): + benchmarkReport = outputQueue.get() + assert isinstance(benchmarkReport, dai.BenchmarkReport) + print(f"FPS is {benchmarkReport.fps}") diff --git a/examples/python/CMakeLists.txt b/examples/python/CMakeLists.txt index 2578d1b27..5c8b5ed0d 100644 --- a/examples/python/CMakeLists.txt +++ b/examples/python/CMakeLists.txt @@ -223,3 +223,15 @@ set_tests_properties(py_script_simple PROPERTIES FAIL_REGULAR_EXPRESSION "\\[err add_python_example(script_all_cameras Script/script_switch_all_cameras.py) dai_set_example_test_labels(script_all_cameras ondevice rvc2_all rvc4 ci) + +## Benchmark node +add_python_example(benchmark_node Benchmark/benchmark_simple.py) +dai_set_example_test_labels(benchmark_node ondevice rvc2_all rvc4 ci) +set_tests_properties(py_benchmark_node PROPERTIES FAIL_REGULAR_EXPRESSION "\\[error\\];\\[critical\\]") + +add_python_example(benchmark_cameras Benchmark/benchmark_camera.py) +dai_set_example_test_labels(benchmark_cameras ondevice rvc2_all rvc4 ci) +set_tests_properties(py_benchmark_cameras PROPERTIES FAIL_REGULAR_EXPRESSION "\\[error\\];\\[critical\\]") + +add_python_example(benchmark_nn Benchmark/benchmark_nn.py) +dai_set_example_test_labels(benchmark_nn ondevice rvc2_all rvc4 ci) diff --git a/examples/python/HostNodes/benchmark.py b/examples/python/HostNodes/benchmark.py deleted file mode 100644 index 701f15298..000000000 --- a/examples/python/HostNodes/benchmark.py +++ /dev/null @@ -1,34 +0,0 @@ -import depthai as dai -import time - -class TestSource(dai.node.ThreadedHostNode): - def __init__(self, name: str): - super().__init__() - self.name = name - self.output = self.createOutput() - - def run(self): - while self.isRunning(): - buffer = dai.Buffer() - print(f"{self.name} node is sending a buffer!") - self.output.send(buffer) - time.sleep(1) - -with dai.Pipeline(createImplicitDevice=False) as p: - # Create nodes - source = TestSource("source") - benchmarkIn = p.create(dai.node.BenchmarkIn) - benchmarkIn.setRunOnHost(True) - benchmarkIn.sendReportEveryNMessages(100) - benchmarkOut = p.create(dai.node.BenchmarkOut) - benchmarkOut.setRunOnHost(True) - benchmarkOut.setFps(30) - benchmarkOut.out.link(benchmarkIn.input) - outputQueue = benchmarkIn.report.createOutputQueue() - source.output.link(benchmarkOut.input) - - p.start() - while p.isRunning(): - benchmarkReport = outputQueue.get() - assert isinstance(benchmarkReport, dai.BenchmarkReport) - print(f"FPS is {benchmarkReport.fps}") \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c83c632fd..20017176f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -400,3 +400,7 @@ dai_set_test_labels(stereo_depth_node_test ondevice rvc2_all rvc4 ci) # ImageManipV2 test dai_add_test(image_manip_v2_node_test src/ondevice_tests/pipeline/node/image_manip_v2_test.cpp) dai_set_test_labels(image_manip_v2_node_test ondevice rvc2_all rvc4 ci) + +# Benchmark tests +dai_add_test(benchmark_test src/ondevice_tests/pipeline/node/benchmark_test.cpp) +dai_set_test_labels(benchmark_test ondevice rvc2_all rvc4 ci) diff --git a/tests/src/ondevice_tests/pipeline/node/benchmark_test.cpp b/tests/src/ondevice_tests/pipeline/node/benchmark_test.cpp new file mode 100644 index 000000000..f5eda08a8 --- /dev/null +++ b/tests/src/ondevice_tests/pipeline/node/benchmark_test.cpp @@ -0,0 +1,80 @@ +#include +#include +#include "depthai/capabilities/ImgFrameCapability.hpp" +#include "depthai/common/CameraBoardSocket.hpp" +#include "depthai/depthai.hpp" +#include "depthai/pipeline/MessageQueue.hpp" +#include "depthai/pipeline/datatype/ImgFrame.hpp" +#include "depthai/pipeline/node/Camera.hpp" + +void testBenchmarkIn(bool benchmarkInRunOnHost, bool benchmarkOutRunOnHost, float fps, bool passthrough) { + // Create pipeline + dai::Pipeline p; + auto benchmarkIn = p.create(); + benchmarkIn->setRunOnHost(benchmarkInRunOnHost); + auto benchmarkOut = p.create(); + benchmarkOut->setRunOnHost(benchmarkOutRunOnHost); + benchmarkOut->setFps(fps); + benchmarkOut->out.link(benchmarkIn->input); + + auto inputQueue = benchmarkOut->input.createInputQueue(); + auto reportQueue = benchmarkIn->report.createOutputQueue(); + std::shared_ptr passthroughQueue; + if(passthrough) { + passthroughQueue = benchmarkIn->passthrough.createOutputQueue(10, false); + } + p.start(); + auto inputFrame = std::make_shared(); + inputQueue->send(inputFrame); + for(int i = 0; i < 10; i++) { + if(passthrough) { + auto passthroughFrame = passthroughQueue->get(); + REQUIRE(passthroughFrame != nullptr); + } + auto reportData = reportQueue->get(); + REQUIRE(reportData != nullptr); + REQUIRE(reportData->numMessagesReceived > 1); + REQUIRE(reportData->fps == Catch::Approx(fps).epsilon(0.05)); + } +} + +void testCameraBenchmarking(float fps) { + dai::Pipeline p; + auto cam = p.create()->build(dai::CameraBoardSocket::CAM_A); + auto* output = cam->requestOutput(std::pair(640, 400), std::nullopt, dai::ImgResizeMode::CROP, fps); + REQUIRE(output != nullptr); + auto benchmarkIn = p.create(); + output->link(benchmarkIn->input); + auto reportQueue = benchmarkIn->report.createOutputQueue(); + p.start(); + for(int i = 0; i < 10; i++) { + auto reportData = reportQueue->get(); + REQUIRE(reportData != nullptr); + REQUIRE(reportData->numMessagesReceived > 1); + REQUIRE(reportData->fps == Catch::Approx(fps).epsilon(0.05)); + } +} + +TEST_CASE("BenchmarkIn and BenchmarkOut run on device") { + testBenchmarkIn(false, false, 30.0f, true); +} + +TEST_CASE("BenchmarkIn run on host, BenchmarkOut run on device") { + testBenchmarkIn(true, false, 30.0f, true); +} + +TEST_CASE("BenchmarkIn run on device, BenchmarkOut run on host") { + testBenchmarkIn(false, true, 30.0f, true); +} + +TEST_CASE("BenchmarkIn and BenchmarkOut run on host") { + testBenchmarkIn(true, true, 30.0f, true); +} + +TEST_CASE("BenchmarkIn and BenchmarkOut run on device - high FPS") { + testBenchmarkIn(false, false, 1000.0f, false); +} + +TEST_CASE("Camera benchmarking") { + testCameraBenchmarking(30.0f); +} diff --git a/tests/src/ondevice_tests/regression/camera_concurrency.cpp b/tests/src/ondevice_tests/regression/camera_concurrency.cpp index a3ab0f25f..fbe09ddd7 100644 --- a/tests/src/ondevice_tests/regression/camera_concurrency.cpp +++ b/tests/src/ondevice_tests/regression/camera_concurrency.cpp @@ -27,7 +27,7 @@ TEST_CASE("camera_concurrency") { for(auto* output : cameraOutputs) { auto node = pipeline.create(); output->link(node->input); - node->setNumMessagesToGet(numMessagesToGet); + node->sendReportEveryNMessages(numMessagesToGet); queues.push_back(node->report.createOutputQueue()); benchmarkNodes.push_back(node); } From e74550b0a5f5e71644ece81d24f78fd9dd23eb14 Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Mon, 30 Dec 2024 14:22:30 +0100 Subject: [PATCH 21/28] Bump the epsilon in benchmark test --- tests/src/ondevice_tests/pipeline/node/benchmark_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/ondevice_tests/pipeline/node/benchmark_test.cpp b/tests/src/ondevice_tests/pipeline/node/benchmark_test.cpp index f5eda08a8..cdb3688ed 100644 --- a/tests/src/ondevice_tests/pipeline/node/benchmark_test.cpp +++ b/tests/src/ondevice_tests/pipeline/node/benchmark_test.cpp @@ -34,7 +34,7 @@ void testBenchmarkIn(bool benchmarkInRunOnHost, bool benchmarkOutRunOnHost, floa auto reportData = reportQueue->get(); REQUIRE(reportData != nullptr); REQUIRE(reportData->numMessagesReceived > 1); - REQUIRE(reportData->fps == Catch::Approx(fps).epsilon(0.05)); + REQUIRE(reportData->fps == Catch::Approx(fps).epsilon(0.1)); } } @@ -51,7 +51,7 @@ void testCameraBenchmarking(float fps) { auto reportData = reportQueue->get(); REQUIRE(reportData != nullptr); REQUIRE(reportData->numMessagesReceived > 1); - REQUIRE(reportData->fps == Catch::Approx(fps).epsilon(0.05)); + REQUIRE(reportData->fps == Catch::Approx(fps).epsilon(0.1)); } } From 0b6ce61c6ebddbdd13bd5fa83bad9a50665db7aa Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Mon, 30 Dec 2024 18:49:28 +0100 Subject: [PATCH 22/28] Add bigger timeout for connecting to the device --- examples/python/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/python/CMakeLists.txt b/examples/python/CMakeLists.txt index 5c8b5ed0d..7ac0f52e6 100644 --- a/examples/python/CMakeLists.txt +++ b/examples/python/CMakeLists.txt @@ -40,6 +40,7 @@ function(add_python_example example_name python_script_path) # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" "DEPTHAI_SEARCH_TIMEOUT=15000" + "DEPTHAI_CONNECT_TIMEOUT=15000" "DEPTHAI_RECONNECT_TIMEOUT=0" # ASAN in case of sanitizers "${ASAN_ENVIRONMENT_VARS}" @@ -60,6 +61,7 @@ function(add_python_example example_name python_script_path) # Python path (to find compiled module) "PYTHONPATH=$${SYS_PATH_SEPARATOR}$ENV{PYTHONPATH}" "DEPTHAI_SEARCH_TIMEOUT=30000" + "DEPTHAI_CONNECT_TIMEOUT=30000" "DEPTHAI_RECONNECT_TIMEOUT=0" # ASAN in case of sanitizers ${ASAN_ENVIRONMENT_VARS} From c773172c56ae7f42197a5074d9279365a8cb6510 Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Tue, 31 Dec 2024 00:20:36 +0100 Subject: [PATCH 23/28] Update comments in the benchmark examples --- examples/python/Benchmark/benchmark_camera.py | 3 +-- examples/python/Benchmark/benchmark_nn.py | 5 +++-- examples/python/Benchmark/benchmark_simple.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/python/Benchmark/benchmark_camera.py b/examples/python/Benchmark/benchmark_camera.py index 97d7adc6f..e89398673 100644 --- a/examples/python/Benchmark/benchmark_camera.py +++ b/examples/python/Benchmark/benchmark_camera.py @@ -4,14 +4,13 @@ # Create pipeline with dai.Pipeline() as pipeline: - # Define source and output + # Create the nodes cam = pipeline.create(dai.node.Camera).build() benchmarkIn = pipeline.create(dai.node.BenchmarkIn) # benchmarkIn.setRunOnHost(True) # The node can also run on host and include the transfer limitation, default is False output = cam.requestFullResolutionOutput() output.link(benchmarkIn.input) - # Connect to device and start pipeline pipeline.start() while pipeline.isRunning(): time.sleep(1) # Let the logger print out the FPS diff --git a/examples/python/Benchmark/benchmark_nn.py b/examples/python/Benchmark/benchmark_nn.py index b327460ab..154584086 100644 --- a/examples/python/Benchmark/benchmark_nn.py +++ b/examples/python/Benchmark/benchmark_nn.py @@ -1,7 +1,8 @@ import depthai as dai -import cv2 import numpy as np + +# First prepare the model for benchmarking device = dai.Device() modelPath = dai.getModelFromZoo(dai.NNModelDescription("yolov6-nano", platform=device.getPlatformAsString())) modelArhive = dai.NNArchive(modelPath) @@ -43,7 +44,7 @@ inputQueue = benchmarkOut.input.createInputQueue() p.start() - inputQueue.send(inputFrame) + inputQueue.send(inputFrame) # Send the input image only once while p.isRunning(): benchmarkReport = outputQueue.get() assert isinstance(benchmarkReport, dai.BenchmarkReport) diff --git a/examples/python/Benchmark/benchmark_simple.py b/examples/python/Benchmark/benchmark_simple.py index 9afe70b6d..ac377c07b 100644 --- a/examples/python/Benchmark/benchmark_simple.py +++ b/examples/python/Benchmark/benchmark_simple.py @@ -9,9 +9,9 @@ benchmarkOut.setFps(30) # Create a BenchmarkIn node - # Thisn node is receiving the messages on the input and measuring the FPS and latency. + # This node is receiving the messages on the input and measuring the FPS and latency. # In the case that the input is with BenchmarkOut, the latency measurement is not always possible, as the message is not deep copied, - # which means that the timestamps stay the same. + # which means that the timestamps stay the same and latency virtually increases over time. benchmarkIn = p.create(dai.node.BenchmarkIn) benchmarkIn.setRunOnHost(True) # The node can run on host or on device benchmarkIn.sendReportEveryNMessages(100) From 1d10ac4d8bbcc9429087c688f03bc836a838abfc Mon Sep 17 00:00:00 2001 From: Matevz Morato Date: Tue, 31 Dec 2024 02:09:11 +0100 Subject: [PATCH 24/28] Bump RVC2 and RVC4 FW to v3_develop RVC2 FW also includes missing MessageGroup bindings --- cmake/Depthai/DepthaiDeviceRVC4Config.cmake | 2 +- cmake/Depthai/DepthaiDeviceSideConfig.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake index bda073a99..db0536b8d 100644 --- a/cmake/Depthai/DepthaiDeviceRVC4Config.cmake +++ b/cmake/Depthai/DepthaiDeviceRVC4Config.cmake @@ -4,4 +4,4 @@ set(DEPTHAI_DEVICE_RVC4_MATURITY "snapshot") # "version if applicable" # set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+93f7b75a885aa32f44c5e9f53b74470c49d2b1af") -set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+19b67f81b54c146d079d2cbd4485fa153337dc5a") +set(DEPTHAI_DEVICE_RVC4_VERSION "0.0.1+6fc71e8674fe7a520b93f4370cb157805f0bc0f2") diff --git a/cmake/Depthai/DepthaiDeviceSideConfig.cmake b/cmake/Depthai/DepthaiDeviceSideConfig.cmake index 20e2509ad..77bbf9672 100644 --- a/cmake/Depthai/DepthaiDeviceSideConfig.cmake +++ b/cmake/Depthai/DepthaiDeviceSideConfig.cmake @@ -2,7 +2,7 @@ set(DEPTHAI_DEVICE_SIDE_MATURITY "snapshot") # "full commit hash of device side binary" -set(DEPTHAI_DEVICE_SIDE_COMMIT "5e016a328ac84324fb3c6bd8904141191f29dc2e") +set(DEPTHAI_DEVICE_SIDE_COMMIT "052ee5648d9d5a49e6367c4a81d14a9b9ae8bfcf") # "version if applicable" set(DEPTHAI_DEVICE_SIDE_VERSION "") From d1d22a9b24257a7868b7503e77709836e2ac851e Mon Sep 17 00:00:00 2001 From: lnotspotl Date: Wed, 1 Jan 2025 20:26:33 -0500 Subject: [PATCH 25/28] Enable implicit conversion between str and NNModelDescription --- .../python/src/pipeline/node/DetectionNetworkBindings.cpp | 7 +++++++ .../python/src/pipeline/node/NeuralNetworkBindings.cpp | 3 +++ .../src/pipeline/node/SpatialDetectionNetworkBindings.cpp | 3 +++ 3 files changed, 13 insertions(+) diff --git a/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp b/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp index 9f3b4b722..c206ffb80 100644 --- a/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp +++ b/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp @@ -62,6 +62,13 @@ void bind_detectionnetwork(pybind11::module& m, void* pCallstack) { py::arg("input"), py::arg("model"), py::arg("fps") = 30.0f) + .def("build", + ([](DetectionNetwork& self, const std::shared_ptr& input, std::string model, float fps) { + return self.build(input, NNModelDescription{.model=model}, fps); + }), + py::arg("input"), + py::arg("model"), + py::arg("fps") = 30.0f) .def("build", py::overload_cast&, NNArchive, float>(&DetectionNetwork::build), py::arg("input"), diff --git a/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp b/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp index 0137fb376..72a4f3c90 100644 --- a/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp +++ b/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp @@ -55,6 +55,9 @@ void bind_neuralnetwork(pybind11::module& m, void* pCallstack){ .def("build", py::overload_cast(&NeuralNetwork::build), py::arg("input"), py::arg("nnArchive"), DOC(dai, node, NeuralNetwork, build)) .def("build", py::overload_cast&, dai::NNModelDescription, float>(&NeuralNetwork::build), py::arg("input"), py::arg("modelDesc"), py::arg("fps")=30.0f, DOC(dai, node, NeuralNetwork, build,2)) .def("build", py::overload_cast&, dai::NNArchive, float>(&NeuralNetwork::build), py::arg("input"), py::arg("nnArchive"), py::arg("fps")=30.0f, DOC(dai, node, NeuralNetwork, build, 3)) + .def("build", [](NeuralNetwork& self, const std::shared_ptr& input, const std::string& model, float fps) { + return self.build(input, NNModelDescription{.model=model}, fps); + }, py::arg("input"), py::arg("model"), py::arg("fps")=30.0f, DOC(dai, node, NeuralNetwork, build)) .def("setBlob", py::overload_cast(&NeuralNetwork::setBlob), py::arg("blob"), DOC(dai, node, NeuralNetwork, setBlob)) .def("setBlob", py::overload_cast(&NeuralNetwork::setBlob), py::arg("path"), DOC(dai, node, NeuralNetwork, setBlob, 2)) .def("setModelPath", diff --git a/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp b/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp index 560886d0f..3f41e0458 100644 --- a/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp +++ b/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp @@ -41,6 +41,9 @@ void bind_spatialdetectionnetwork(pybind11::module& m, void* pCallstack){ spatialDetectionNetwork // Copied from NN node .def("build", py::overload_cast&, const std::shared_ptr&, NNModelDescription, float>(&SpatialDetectionNetwork::build), py::arg("input"), py::arg("stereo"), py::arg("model"), py::arg("fps") = 30.0f, DOC(dai, node, SpatialDetectionNetwork, build)) + .def("build", ([](SpatialDetectionNetwork& self, const std::shared_ptr& input, const std::shared_ptr& stereo, std::string model, float fps) { + return self.build(input, stereo, NNModelDescription{.model=model}, fps); + }), py::arg("input"), py::arg("stereo"), py::arg("model"), py::arg("fps") = 30.0f, DOC(dai, node, SpatialDetectionNetwork, build, 2)) .def("build", py::overload_cast&, const std::shared_ptr&, NNArchive, float>(&SpatialDetectionNetwork::build), py::arg("input"), py::arg("stereo"), py::arg("nnArchive"), py::arg("fps") = 30.0f, DOC(dai, node, SpatialDetectionNetwork, build, 2)) .def("setBlobPath", &SpatialDetectionNetwork::setBlobPath, py::arg("path"), DOC(dai, node, SpatialDetectionNetwork, setBlobPath)) .def("setNumPoolFrames", &SpatialDetectionNetwork::setNumPoolFrames, py::arg("numFrames"), DOC(dai, node, SpatialDetectionNetwork, setNumPoolFrames)) From 735e3783aa161199caf0881a09f3a501bf8ff611 Mon Sep 17 00:00:00 2001 From: lnotspotl Date: Thu, 2 Jan 2025 13:51:50 -0500 Subject: [PATCH 26/28] wip --- .../python/src/pipeline/node/NodeBindings.cpp | 4 ++++ .../python/HostNodes/threaded_host_nodes.py | 17 +++++++++++++++++ include/depthai/pipeline/Node.hpp | 16 ++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/bindings/python/src/pipeline/node/NodeBindings.cpp b/bindings/python/src/pipeline/node/NodeBindings.cpp index 723765056..c21812f28 100644 --- a/bindings/python/src/pipeline/node/NodeBindings.cpp +++ b/bindings/python/src/pipeline/node/NodeBindings.cpp @@ -313,6 +313,8 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack) { static_cast(&Node::Input::getParent), py::return_value_policy::reference_internal, DOC(dai, Node, Input, getParent)) + .def("getPossibleDatatypes", &Node::Input::getPossibleDatatypes, DOC(dai, Node, Input, getPossibleDatatypes)) + .def("setPossibleDatatypes", &Node::Input::setPossibleDatatypes, py::arg("types"), DOC(dai, Node, Input, setPossibleDatatypes)) .def("setWaitForMessage", &Node::Input::setWaitForMessage, py::arg("waitForMessage"), DOC(dai, Node, Input, setWaitForMessage)) .def("getWaitForMessage", &Node::Input::getWaitForMessage, DOC(dai, Node, Input, getWaitForMessage)) .def("setReusePreviousMessage", &Node::Input::setReusePreviousMessage, py::arg("reusePreviousMessage"), DOC(dai, Node, Input, setReusePreviousMessage)) @@ -336,6 +338,8 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack) { py::arg("possibleDatatypes") = Node::OutputDescription{}.types, py::keep_alive<1, 0>()) .def("getPossibleDatatypes", &Node::Output::getPossibleDatatypes, DOC(dai, Node, Output, getPossibleDatatypes)) + .def("setPossibleDatatypes", &Node::Output::setPossibleDatatypes, py::arg("types"), DOC(dai, Node, Output, setPossibleDatatypes)) + .def("addPossibleDatatype", &Node::Output::addPossibleDatatype, py::arg("datatype"), py::arg("descendants") = true, DOC(dai, Node, Output, addPossibleDatatype)) .def("getParent", static_cast(&Node::Output::getParent), py::return_value_policy::reference_internal, diff --git a/examples/python/HostNodes/threaded_host_nodes.py b/examples/python/HostNodes/threaded_host_nodes.py index 83c4094ac..7b3c50827 100644 --- a/examples/python/HostNodes/threaded_host_nodes.py +++ b/examples/python/HostNodes/threaded_host_nodes.py @@ -8,6 +8,22 @@ def __init__(self, name: str): self.input = self.createInput() self.output = self.createOutput() + # Possible API 1: + self.input.setPossibleDatatypes([dai.Node.DatatypeHierarchy(dai.DatatypeEnum.ImgFrame, True)]) + self.output.setPossibleDatatypes([dai.Node.DatatypeHierarchy(dai.DatatypeEnum.ImgFrame, True)]) + + # Possible API 2: + self.input.setPossibleDatatypes([ + (dai.DatatypeEnum.ImgFrame, True), + (dai.DatatypeEnum.Buffer, True) + ]) + self.output.setPossibleDatatypes([ + (dai.DatatypeEnum.ImgFrame, True), + (dai.DatatypeEnum.Buffer, True) + ]) + + + def onStart(self): print("Hello, this is", self.name) @@ -24,6 +40,7 @@ class TestSink(dai.node.ThreadedHostNode): def __init__(self, name: str): super().__init__() self.input = self.createInput() + self.name = name def onStart(self): diff --git a/include/depthai/pipeline/Node.hpp b/include/depthai/pipeline/Node.hpp index d4c98c025..2985f3c45 100644 --- a/include/depthai/pipeline/Node.hpp +++ b/include/depthai/pipeline/Node.hpp @@ -184,6 +184,14 @@ class Node : public std::enable_shared_from_this { return desc.types; } + void setPossibleDatatypes(std::vector types) { + desc.types = std::move(types); + } + + void addPossibleDatatype(DatatypeEnum datatype, bool descendants = true) { + desc.types.push_back(DatatypeHierarchy{datatype, descendants}); + } + /** * Check if this output and given input are on the same pipeline. * @see canConnect for checking if connection is possible @@ -378,6 +386,14 @@ class Node : public std::enable_shared_from_this { */ bool getWaitForMessage() const; + std::vector getPossibleDatatypes() const { + return possibleDatatypes; + } + + void setPossibleDatatypes(std::vector types) { + possibleDatatypes = std::move(types); + } + /** * Equivalent to setWaitForMessage but with inverted logic. */ From 37388e64acf9ff141af6446c0efb5d0cdf2abaed Mon Sep 17 00:00:00 2001 From: lnotspotl Date: Thu, 2 Jan 2025 13:54:22 -0500 Subject: [PATCH 27/28] Remove designated initializers --- bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp | 2 +- bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp | 2 +- .../src/pipeline/node/SpatialDetectionNetworkBindings.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp b/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp index c206ffb80..a88467d49 100644 --- a/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp +++ b/bindings/python/src/pipeline/node/DetectionNetworkBindings.cpp @@ -64,7 +64,7 @@ void bind_detectionnetwork(pybind11::module& m, void* pCallstack) { py::arg("fps") = 30.0f) .def("build", ([](DetectionNetwork& self, const std::shared_ptr& input, std::string model, float fps) { - return self.build(input, NNModelDescription{.model=model}, fps); + return self.build(input, NNModelDescription{model}, fps); }), py::arg("input"), py::arg("model"), diff --git a/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp b/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp index 72a4f3c90..844624a7e 100644 --- a/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp +++ b/bindings/python/src/pipeline/node/NeuralNetworkBindings.cpp @@ -56,7 +56,7 @@ void bind_neuralnetwork(pybind11::module& m, void* pCallstack){ .def("build", py::overload_cast&, dai::NNModelDescription, float>(&NeuralNetwork::build), py::arg("input"), py::arg("modelDesc"), py::arg("fps")=30.0f, DOC(dai, node, NeuralNetwork, build,2)) .def("build", py::overload_cast&, dai::NNArchive, float>(&NeuralNetwork::build), py::arg("input"), py::arg("nnArchive"), py::arg("fps")=30.0f, DOC(dai, node, NeuralNetwork, build, 3)) .def("build", [](NeuralNetwork& self, const std::shared_ptr& input, const std::string& model, float fps) { - return self.build(input, NNModelDescription{.model=model}, fps); + return self.build(input, NNModelDescription{model}, fps); }, py::arg("input"), py::arg("model"), py::arg("fps")=30.0f, DOC(dai, node, NeuralNetwork, build)) .def("setBlob", py::overload_cast(&NeuralNetwork::setBlob), py::arg("blob"), DOC(dai, node, NeuralNetwork, setBlob)) .def("setBlob", py::overload_cast(&NeuralNetwork::setBlob), py::arg("path"), DOC(dai, node, NeuralNetwork, setBlob, 2)) diff --git a/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp b/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp index 3f41e0458..a0c03a69e 100644 --- a/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp +++ b/bindings/python/src/pipeline/node/SpatialDetectionNetworkBindings.cpp @@ -42,7 +42,7 @@ void bind_spatialdetectionnetwork(pybind11::module& m, void* pCallstack){ // Copied from NN node .def("build", py::overload_cast&, const std::shared_ptr&, NNModelDescription, float>(&SpatialDetectionNetwork::build), py::arg("input"), py::arg("stereo"), py::arg("model"), py::arg("fps") = 30.0f, DOC(dai, node, SpatialDetectionNetwork, build)) .def("build", ([](SpatialDetectionNetwork& self, const std::shared_ptr& input, const std::shared_ptr& stereo, std::string model, float fps) { - return self.build(input, stereo, NNModelDescription{.model=model}, fps); + return self.build(input, stereo, NNModelDescription{model}, fps); }), py::arg("input"), py::arg("stereo"), py::arg("model"), py::arg("fps") = 30.0f, DOC(dai, node, SpatialDetectionNetwork, build, 2)) .def("build", py::overload_cast&, const std::shared_ptr&, NNArchive, float>(&SpatialDetectionNetwork::build), py::arg("input"), py::arg("stereo"), py::arg("nnArchive"), py::arg("fps") = 30.0f, DOC(dai, node, SpatialDetectionNetwork, build, 2)) .def("setBlobPath", &SpatialDetectionNetwork::setBlobPath, py::arg("path"), DOC(dai, node, SpatialDetectionNetwork, setBlobPath)) From c8d729c89e8629e02e82d2ccf0fe977d2818284a Mon Sep 17 00:00:00 2001 From: lnotspotl Date: Sun, 5 Jan 2025 17:35:18 -0500 Subject: [PATCH 28/28] Refactor --- .../python/src/pipeline/node/NodeBindings.cpp | 17 ++++++++++- include/depthai/pipeline/Node.hpp | 29 +++++++++---------- src/pipeline/Node.cpp | 16 ++++++++++ 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/bindings/python/src/pipeline/node/NodeBindings.cpp b/bindings/python/src/pipeline/node/NodeBindings.cpp index c21812f28..0e6cca987 100644 --- a/bindings/python/src/pipeline/node/NodeBindings.cpp +++ b/bindings/python/src/pipeline/node/NodeBindings.cpp @@ -315,6 +315,14 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack) { DOC(dai, Node, Input, getParent)) .def("getPossibleDatatypes", &Node::Input::getPossibleDatatypes, DOC(dai, Node, Input, getPossibleDatatypes)) .def("setPossibleDatatypes", &Node::Input::setPossibleDatatypes, py::arg("types"), DOC(dai, Node, Input, setPossibleDatatypes)) + .def("setPossibleDatatypes", [](Node::Input& input, const std::vector>& types) { + std::vector converted; + converted.reserve(types.size()); + for(const auto& t : types) { + converted.emplace_back(std::get<0>(t), std::get<1>(t)); + } + input.setPossibleDatatypes(converted); + }, py::arg("types"), DOC(dai, Node, Input, setPossibleDatatypes)) .def("setWaitForMessage", &Node::Input::setWaitForMessage, py::arg("waitForMessage"), DOC(dai, Node, Input, setWaitForMessage)) .def("getWaitForMessage", &Node::Input::getWaitForMessage, DOC(dai, Node, Input, getWaitForMessage)) .def("setReusePreviousMessage", &Node::Input::setReusePreviousMessage, py::arg("reusePreviousMessage"), DOC(dai, Node, Input, setReusePreviousMessage)) @@ -339,7 +347,14 @@ void NodeBindings::bind(pybind11::module& m, void* pCallstack) { py::keep_alive<1, 0>()) .def("getPossibleDatatypes", &Node::Output::getPossibleDatatypes, DOC(dai, Node, Output, getPossibleDatatypes)) .def("setPossibleDatatypes", &Node::Output::setPossibleDatatypes, py::arg("types"), DOC(dai, Node, Output, setPossibleDatatypes)) - .def("addPossibleDatatype", &Node::Output::addPossibleDatatype, py::arg("datatype"), py::arg("descendants") = true, DOC(dai, Node, Output, addPossibleDatatype)) + .def("setPossibleDatatypes", [](Node::Output& output, const std::vector>& types) { + std::vector converted; + converted.reserve(types.size()); + for(const auto& t : types) { + converted.emplace_back(std::get<0>(t), std::get<1>(t)); + } + output.setPossibleDatatypes(converted); + }, py::arg("types"), DOC(dai, Node, Output, setPossibleDatatypes)) .def("getParent", static_cast(&Node::Output::getParent), py::return_value_policy::reference_internal, diff --git a/include/depthai/pipeline/Node.hpp b/include/depthai/pipeline/Node.hpp index 2985f3c45..ec8098558 100644 --- a/include/depthai/pipeline/Node.hpp +++ b/include/depthai/pipeline/Node.hpp @@ -180,17 +180,12 @@ class Node : public std::enable_shared_from_this { /** * Get possible datatypes that can be sent */ - std::vector getPossibleDatatypes() const { - return desc.types; - } - - void setPossibleDatatypes(std::vector types) { - desc.types = std::move(types); - } + std::vector getPossibleDatatypes() const; - void addPossibleDatatype(DatatypeEnum datatype, bool descendants = true) { - desc.types.push_back(DatatypeHierarchy{datatype, descendants}); - } + /** + * Set possible datatypes that can be sent + */ + void setPossibleDatatypes(std::vector types); /** * Check if this output and given input are on the same pipeline. @@ -386,13 +381,15 @@ class Node : public std::enable_shared_from_this { */ bool getWaitForMessage() const; - std::vector getPossibleDatatypes() const { - return possibleDatatypes; - } + /** + * Get possible datatypes that can be received + */ + std::vector getPossibleDatatypes() const; - void setPossibleDatatypes(std::vector types) { - possibleDatatypes = std::move(types); - } + /** + * Set possible datatypes that can be received + */ + void setPossibleDatatypes(std::vector types); /** * Equivalent to setWaitForMessage but with inverted logic. diff --git a/src/pipeline/Node.cpp b/src/pipeline/Node.cpp index 3ceed4b36..2b13c049e 100644 --- a/src/pipeline/Node.cpp +++ b/src/pipeline/Node.cpp @@ -731,4 +731,20 @@ std::vector>> Node::getRequi DAI_CHECK_V(false, "Node '{}' doesn't support node to node linking. Please link outputs <--> inputs manually.", getName()); } +void Node::Output::setPossibleDatatypes(std::vector types) { + desc.types = std::move(types); +} + +std::vector Node::Output::getPossibleDatatypes() const { + return desc.types; +} + +void Node::Input::setPossibleDatatypes(std::vector types) { + possibleDatatypes = std::move(types); +} + +std::vector Node::Input::getPossibleDatatypes() const { + return possibleDatatypes; +} + } // namespace dai