From 198a40a5191ba3897310aff91906b1ec08e34c57 Mon Sep 17 00:00:00 2001 From: AljazD Date: Thu, 13 Nov 2025 08:57:40 +0100 Subject: [PATCH 1/3] NNArchive now clears extractFolder upon destruction --- include/depthai/nn_archive/NNArchive.hpp | 5 +++++ src/nn_archive/NNArchive.cpp | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/depthai/nn_archive/NNArchive.hpp b/include/depthai/nn_archive/NNArchive.hpp index 8e0b826f0c..acf2312bc6 100644 --- a/include/depthai/nn_archive/NNArchive.hpp +++ b/include/depthai/nn_archive/NNArchive.hpp @@ -39,6 +39,11 @@ class NNArchive { */ NNArchive(const std::filesystem::path& archivePath, NNArchiveOptions options = {}); + /** + * @brief Destruct the NNArchive object and handle clearing of the extractFolder inside NNArchiveOptions + */ + ~NNArchive(); + /** * @brief Return a SuperVINO::Blob from the archive if getModelType() returns BLOB, nothing otherwise * diff --git a/src/nn_archive/NNArchive.cpp b/src/nn_archive/NNArchive.cpp index ee1de2d909..949e7cf6c1 100644 --- a/src/nn_archive/NNArchive.cpp +++ b/src/nn_archive/NNArchive.cpp @@ -20,7 +20,7 @@ NNArchiveOptions::NNArchiveOptions() { extractFolder(platform::getTempPath()); } -NNArchive::NNArchive(const std::filesystem::path& archivePath, NNArchiveOptions options) : archiveOptions(options) { +NNArchive::NNArchive(const std::filesystem::path& archivePath, NNArchiveOptions options) : archiveOptions(std::move(options)) { // Make sure archive exits if(!std::filesystem::exists(archivePath)) DAI_CHECK_V(false, "Archive file does not exist: {}", archivePath); @@ -58,6 +58,10 @@ NNArchive::NNArchive(const std::filesystem::path& archivePath, NNArchiveOptions } } +NNArchive::~NNArchive() { + std::filesystem::remove_all(archiveOptions.extractFolder()); +} + std::optional NNArchive::getBlob() const { switch(modelType) { case model::ModelType::BLOB: From 54b0fe8f641f49842cd946bdd56bd29bf3fc15a9 Mon Sep 17 00:00:00 2001 From: AljazD Date: Thu, 13 Nov 2025 13:03:19 +0100 Subject: [PATCH 2/3] NNArchive no longer unpacks models inside /tmp/ --- .../src/nn_archive/NNArchiveBindings.cpp | 10 +-- .../RVC2/NNArchive/nn_archive_superblob.cpp | 2 - .../RVC2/NNArchive/nn_archive_superblob.py | 6 +- include/depthai/nn_archive/NNArchive.hpp | 22 ++---- .../depthai/pipeline/node/NeuralNetwork.hpp | 15 ++++ src/nn_archive/NNArchive.cpp | 69 +++++++------------ src/pipeline/node/NeuralNetwork.cpp | 24 +++++-- .../nn_archive/nn_archive_test.cpp | 59 ++-------------- 8 files changed, 72 insertions(+), 135 deletions(-) diff --git a/bindings/python/src/nn_archive/NNArchiveBindings.cpp b/bindings/python/src/nn_archive/NNArchiveBindings.cpp index 56250f7d50..a1d143088a 100644 --- a/bindings/python/src/nn_archive/NNArchiveBindings.cpp +++ b/bindings/python/src/nn_archive/NNArchiveBindings.cpp @@ -65,15 +65,13 @@ void NNArchiveBindings::bind(pybind11::module& m, void* pCallstack) { /////////////////////////////////////////////////////////////////////// // Bind NNArchive - nnArchive.def(py::init([](const std::filesystem::path& archivePath, NNArchiveEntry::Compression compression, const std::string& extractFolder) { + nnArchive.def(py::init([](const std::filesystem::path& archivePath, NNArchiveEntry::Compression compression) { NNArchiveOptions options; options.compression(compression); - options.extractFolder(extractFolder); return NNArchive(archivePath, options); }), py::arg("archivePath"), py::arg("compression") = NNArchiveEntry::Compression::AUTO, - py::arg("extractFolder") = "/tmp/", DOC(dai, NNArchive, NNArchive)); nnArchive.def(py::init(), py::arg("archivePath"), @@ -81,7 +79,7 @@ void NNArchiveBindings::bind(pybind11::module& m, void* pCallstack) { DOC(dai, NNArchive, NNArchive)); nnArchive.def("getBlob", &NNArchive::getBlob, DOC(dai, NNArchive, getBlob)); nnArchive.def("getSuperBlob", &NNArchive::getSuperBlob, DOC(dai, NNArchive, getBlob)); - nnArchive.def("getModelPath", &NNArchive::getModelPath, DOC(dai, NNArchive, getModelPath)); + nnArchive.def("getOtherModelFormat", &NNArchive::getOtherModelFormat, DOC(dai, NNArchive, getOtherModelFormat)); nnArchive.def("getConfig", &NNArchive::getConfig, DOC(dai, NNArchive, getConfig)); nnArchive.def("getConfigV1", &NNArchive::getConfig, DOC(dai, NNArchive, getConfig)); nnArchive.def("getModelType", &NNArchive::getModelType, DOC(dai, NNArchive, getModelType)); @@ -96,10 +94,6 @@ void NNArchiveBindings::bind(pybind11::module& m, void* pCallstack) { "compression", [](const NNArchiveOptions& opt) { return opt.compression(); }, [](NNArchiveOptions& opt, NNArchiveEntry::Compression compression) { opt.compression(compression); }); - nnArchiveOptions.def_property( - "extractFolder", - [](const NNArchiveOptions& opt) { return opt.extractFolder(); }, - [](NNArchiveOptions& opt, const std::string& extractFolder) { opt.extractFolder(extractFolder); }); // Bind NNArchiveVersionedConfig nnArchiveVersionedConfig.def(py::init(), diff --git a/examples/cpp/RVC2/NNArchive/nn_archive_superblob.cpp b/examples/cpp/RVC2/NNArchive/nn_archive_superblob.cpp index 7a066a763c..5638036f01 100644 --- a/examples/cpp/RVC2/NNArchive/nn_archive_superblob.cpp +++ b/examples/cpp/RVC2/NNArchive/nn_archive_superblob.cpp @@ -40,8 +40,6 @@ int main() { throw std::runtime_error("SuperBlob should not be null for superblob type"); } - std::cout << "Superblob path: " << archive.getModelPath().value() << std::endl; - if(archive.getBlob()) { throw std::runtime_error("Blob should be null for superblob type"); } diff --git a/examples/python/RVC2/NNArchive/nn_archive_superblob.py b/examples/python/RVC2/NNArchive/nn_archive_superblob.py index 221782593e..101172ab25 100644 --- a/examples/python/RVC2/NNArchive/nn_archive_superblob.py +++ b/examples/python/RVC2/NNArchive/nn_archive_superblob.py @@ -19,11 +19,9 @@ # Therefore, getSuperBlob() is available assert archive.getSuperBlob() is not None -# The archive is unpacked and thus a path to the superblob model is also available -assert archive.getModelPath() is not None - -# There is no blob available +# There is no blob or other model format available assert archive.getBlob() is None +assert archive.getOtherModelFormat() is None # You can access any config version v1config: dai.nn_archive.v1.Config = archive.getConfig() diff --git a/include/depthai/nn_archive/NNArchive.hpp b/include/depthai/nn_archive/NNArchive.hpp index acf2312bc6..b8b695c81a 100644 --- a/include/depthai/nn_archive/NNArchive.hpp +++ b/include/depthai/nn_archive/NNArchive.hpp @@ -14,8 +14,6 @@ namespace dai { struct NNArchiveOptions { - NNArchiveOptions(); - // General parameters DEPTAHI_ARG_DEFAULT(NNArchiveEntry::Compression, compression, NNArchiveEntry::Compression::AUTO); @@ -26,7 +24,7 @@ struct NNArchiveOptions { // ... // Parameters for other formats, ONNX, PT, etc.. - DEPTAHI_ARG_DEFAULT(std::filesystem::path, extractFolder, std::filesystem::path()); + // ... }; class NNArchive { @@ -39,11 +37,6 @@ class NNArchive { */ NNArchive(const std::filesystem::path& archivePath, NNArchiveOptions options = {}); - /** - * @brief Destruct the NNArchive object and handle clearing of the extractFolder inside NNArchiveOptions - */ - ~NNArchive(); - /** * @brief Return a SuperVINO::Blob from the archive if getModelType() returns BLOB, nothing otherwise * @@ -59,11 +52,11 @@ class NNArchive { std::optional getSuperBlob() const; /** - * @brief Return a path to the model inside the archive if getModelType() returns OTHER or DLC, nothing otherwise + * @brief Return a model from the archive if getModelType() returns OTHER or DLC, nothing otherwise * - * @return std::optional: Model path + * @return std::optional: Model */ - std::optional getModelPath() const; + std::optional> getOtherModelFormat() const; /** * @brief Get NNArchive config wrapper @@ -122,9 +115,6 @@ class NNArchive { // Read model from archive std::vector readModelFromArchive(const std::filesystem::path& archivePath, const std::string& modelPathInArchive) const; - // Unpack archive to tmp directory - void unpackArchiveInDirectory(const std::filesystem::path& archivePath, const std::filesystem::path& directory) const; - model::ModelType modelType; NNArchiveOptions archiveOptions; @@ -137,8 +127,8 @@ class NNArchive { // Superblob related stuff std::shared_ptr superblobPtr; - // Other formats - return path to the unpacked archive - std::filesystem::path unpackedModelPath; + // Other formats + std::shared_ptr> otherModelFormatPtr; }; } // namespace dai diff --git a/include/depthai/pipeline/node/NeuralNetwork.hpp b/include/depthai/pipeline/node/NeuralNetwork.hpp index 496cd08a42..0e77c7a9b0 100644 --- a/include/depthai/pipeline/node/NeuralNetwork.hpp +++ b/include/depthai/pipeline/node/NeuralNetwork.hpp @@ -125,6 +125,21 @@ class NeuralNetwork : public DeviceNodeCRTP model); + + /** + * Load network model into assets and use once pipeline is started. + * + * @throws Error if file doesn't exist or isn't a valid network model. + * @param path Path to the network model + */ + void setOtherModelFormat(const std::filesystem::path& path); + /** * Load network xml and bin files into assets. * @param xmlModelPath Path to the neural network model file. diff --git a/src/nn_archive/NNArchive.cpp b/src/nn_archive/NNArchive.cpp index 949e7cf6c1..7fb86a3a9e 100644 --- a/src/nn_archive/NNArchive.cpp +++ b/src/nn_archive/NNArchive.cpp @@ -15,11 +15,6 @@ namespace dai { -NNArchiveOptions::NNArchiveOptions() { - // Default options - extractFolder(platform::getTempPath()); -} - NNArchive::NNArchive(const std::filesystem::path& archivePath, NNArchiveOptions options) : archiveOptions(std::move(options)) { // Make sure archive exits if(!std::filesystem::exists(archivePath)) DAI_CHECK_V(false, "Archive file does not exist: {}", archivePath); @@ -34,11 +29,6 @@ NNArchive::NNArchive(const std::filesystem::path& archivePath, NNArchiveOptions // Read archive type modelType = model::readModelType(modelPathInArchive); - // Unpack model - std::filesystem::path unpackedArchivePath = std::filesystem::path(archiveOptions.extractFolder()) / std::filesystem::path(archivePath).filename(); - unpackArchiveInDirectory(archivePath, unpackedArchivePath); - unpackedModelPath = (unpackedArchivePath / modelPathInArchive); - switch(modelType) { case model::ModelType::BLOB: blobPtr.reset(new OpenVINO::Blob(readModelFromArchive(archivePath, modelPathInArchive))); @@ -48,7 +38,8 @@ NNArchive::NNArchive(const std::filesystem::path& archivePath, NNArchiveOptions break; case model::ModelType::DLC: case model::ModelType::OTHER: - break; // Just do nothing, model is already unpacked + otherModelFormatPtr = std::make_shared>(readModelFromArchive(archivePath, modelPathInArchive)); + break; case model::ModelType::NNARCHIVE: DAI_CHECK_V(false, "NNArchive inside NNArchive is not supported. Please unpack the inner archive first."); break; @@ -58,10 +49,6 @@ NNArchive::NNArchive(const std::filesystem::path& archivePath, NNArchiveOptions } } -NNArchive::~NNArchive() { - std::filesystem::remove_all(archiveOptions.extractFolder()); -} - std::optional NNArchive::getBlob() const { switch(modelType) { case model::ModelType::BLOB: @@ -100,13 +87,14 @@ std::optional NNArchive::getSuperBlob() const { } } -std::optional NNArchive::getModelPath() const { +std::optional> NNArchive::getOtherModelFormat() const { switch(modelType) { case model::ModelType::OTHER: case model::ModelType::DLC: + return *otherModelFormatPtr; case model::ModelType::BLOB: case model::ModelType::SUPERBLOB: - return unpackedModelPath; + return std::nullopt; break; case model::ModelType::NNARCHIVE: DAI_CHECK_V(false, "NNArchive inside NNArchive is not supported. Please unpack the inner archive first."); @@ -133,11 +121,6 @@ std::vector NNArchive::readModelFromArchive(const std::filesystem::path return modelBytes; } -void NNArchive::unpackArchiveInDirectory(const std::filesystem::path& archivePath, const std::filesystem::path& directory) const { - utility::ArchiveUtil archive(archivePath, archiveOptions.compression()); - archive.unpackArchiveInDirectory(directory); -} - std::optional> NNArchive::getInputSize(uint32_t index) const { auto inputs = archiveVersionedConfigPtr->getConfig().model.inputs; if(inputs.size() <= index) { @@ -190,34 +173,28 @@ std::optional NNArchive::getInputHeight(uint32_t index) const { } std::vector NNArchive::getSupportedPlatforms() const { - auto pathToModel = getModelPath(); - if(!pathToModel) { - return {}; - } - auto pathToModelChecked = *pathToModel; - - auto endsWith = [](const std::filesystem::path& path, const std::string& suffix) { return path.extension() == suffix; }; + switch(modelType) { + case model::ModelType::DLC: + return {Platform::RVC4}; - if(endsWith(pathToModelChecked, ".dlc")) { - return {Platform::RVC4}; - } - if(endsWith(pathToModelChecked, ".superblob")) { - return {Platform::RVC2}; - } - if(endsWith(pathToModelChecked, ".blob")) { - auto model = OpenVINO::Blob(pathToModelChecked); - if(model.device == OpenVINO::Device::VPUX) { - return {Platform::RVC3}; - } - if(model.device == OpenVINO::Device::VPU) { + case model::ModelType::SUPERBLOB: return {Platform::RVC2}; - } - // Should never get here - return {}; - } + case model::ModelType::BLOB: + if(blobPtr->device == OpenVINO::Device::VPUX) { + return {Platform::RVC3}; + } + if(blobPtr->device == OpenVINO::Device::VPU) { + return {Platform::RVC2}; + } + // Should never get here + return {}; - return {}; + case model::ModelType::NNARCHIVE: + case model::ModelType::OTHER: + default: + return {}; + } } } // namespace dai diff --git a/src/pipeline/node/NeuralNetwork.cpp b/src/pipeline/node/NeuralNetwork.cpp index ed2c2e60b8..d4cdd00c89 100644 --- a/src/pipeline/node/NeuralNetwork.cpp +++ b/src/pipeline/node/NeuralNetwork.cpp @@ -187,7 +187,9 @@ void NeuralNetwork::setNNArchiveSuperblob(const NNArchive& nnArchive, int numSha } void NeuralNetwork::setNNArchiveOther(const NNArchive& nnArchive) { - setModelPath(nnArchive.getModelPath().value()); + DAI_CHECK_V(nnArchive.getModelType() == model::ModelType::DLC || nnArchive.getModelType() == model::ModelType::OTHER, "NNArchive type is not DLC or OTHER"); + auto otherModelFormat = nnArchive.getOtherModelFormat().value(); + setOtherModelFormat(otherModelFormat); } // Specify local filesystem path to load the blob (which gets loaded at loadAssets) @@ -214,6 +216,18 @@ void NeuralNetwork::setBlob(OpenVINO::Blob blob) { properties.modelSource = Properties::ModelSource::BLOB; } +void NeuralNetwork::setOtherModelFormat(std::vector otherModel) { + auto asset = assetManager.set("__model", std::move(otherModel)); + properties.modelUri = asset->getRelativeUri(); + properties.modelSource = Properties::ModelSource::CUSTOM_MODEL; +} + +void NeuralNetwork::setOtherModelFormat(const std::filesystem::path& path) { + auto modelAsset = assetManager.set("__model", path); + properties.modelUri = modelAsset->getRelativeUri(); + properties.modelSource = Properties::ModelSource::CUSTOM_MODEL; +} + void NeuralNetwork::setModelPath(const std::filesystem::path& modelPath) { switch(model::readModelType(modelPath)) { case model::ModelType::BLOB: @@ -226,11 +240,9 @@ void NeuralNetwork::setModelPath(const std::filesystem::path& modelPath) { setNNArchive(NNArchive(modelPath)); break; case model::ModelType::DLC: - case model::ModelType::OTHER: { - auto modelAsset = assetManager.set("__model", modelPath); - properties.modelUri = modelAsset->getRelativeUri(); - properties.modelSource = Properties::ModelSource::CUSTOM_MODEL; - } break; + case model::ModelType::OTHER: + setOtherModelFormat(modelPath); + break; } } diff --git a/tests/src/onhost_tests/nn_archive/nn_archive_test.cpp b/tests/src/onhost_tests/nn_archive/nn_archive_test.cpp index 21cb853101..223d9f681a 100644 --- a/tests/src/onhost_tests/nn_archive/nn_archive_test.cpp +++ b/tests/src/onhost_tests/nn_archive/nn_archive_test.cpp @@ -5,89 +5,42 @@ #include #include -namespace { -class TestHelper { - public: - TestHelper() { - srand(time(nullptr)); - extractFolder = std::filesystem::path("/tmp/depthai_test_" + std::to_string(rand())).string(); - } - - ~TestHelper() { - std::filesystem::remove_all(extractFolder); - } - - std::string extractFolder; -}; -} // namespace - TEST_CASE("NNArchive loads a BLOB properly") { dai::NNArchive nnArchive(BLOB_ARCHIVE_PATH); - auto helper = std::make_unique(); - std::string extractFolder = helper->extractFolder; - // Loaded archive is BLOB REQUIRE(nnArchive.getModelType() == dai::model::ModelType::BLOB); // Returns blob REQUIRE(nnArchive.getBlob().has_value()); - - // Returns path to blob - REQUIRE(nnArchive.getModelPath().has_value()); - std::filesystem::path path = nnArchive.getModelPath().value(); - REQUIRE(std::filesystem::exists(path)); - REQUIRE(path.extension() == ".blob"); - - // Returns nothing for other types REQUIRE(!nnArchive.getSuperBlob().has_value()); + REQUIRE(!nnArchive.getOtherModelFormat().has_value()); } TEST_CASE("NNArchive loads a SUPERBLOB properly") { dai::NNArchive nnArchive(SUPERBLOB_ARCHIVE_PATH); - auto helper = std::make_unique(); - std::string extractFolder = helper->extractFolder; - // Loaded archive is SUPERBLOB REQUIRE(nnArchive.getModelType() == dai::model::ModelType::SUPERBLOB); // Returns superblob REQUIRE(nnArchive.getSuperBlob().has_value()); - - // Returns path to superblob - REQUIRE(nnArchive.getModelPath().has_value()); - std::filesystem::path path = nnArchive.getModelPath().value(); - REQUIRE(std::filesystem::exists(path)); - REQUIRE(path.extension() == ".superblob"); + REQUIRE(!nnArchive.getBlob().has_value()); + REQUIRE(!nnArchive.getOtherModelFormat().has_value()); // Returns nothing for other types - REQUIRE(!nnArchive.getBlob().has_value()); REQUIRE(nnArchive.getSupportedPlatforms().size() == 1); REQUIRE(nnArchive.getSupportedPlatforms()[0] == dai::Platform::RVC2); } TEST_CASE("NNArchive loads other formats properly") { - // Used for initialization and cleanup (even if the test fails) - auto helper = std::make_unique(); - std::string extractFolder = helper->extractFolder; - - dai::NNArchive nnArchive(ONNX_ARCHIVE_PATH, dai::NNArchiveOptions().extractFolder(extractFolder)); + dai::NNArchive nnArchive(ONNX_ARCHIVE_PATH); // Loaded archive is ONNX REQUIRE(nnArchive.getModelType() == dai::model::ModelType::OTHER); - // Returns model path - REQUIRE(nnArchive.getModelPath().has_value()); - - // Check that the returned path exists - std::filesystem::path modelPath = nnArchive.getModelPath().value(); - REQUIRE(std::filesystem::exists(modelPath)); - - // Check that it has the .onnx extension - REQUIRE(modelPath.extension() == ".onnx"); - - // Returns nothing for other types + // Returns model + REQUIRE(nnArchive.getOtherModelFormat().has_value()); REQUIRE(!nnArchive.getBlob().has_value()); REQUIRE(!nnArchive.getSuperBlob().has_value()); From f59a6b3494c5fdd4c61f5230eca329c259242767 Mon Sep 17 00:00:00 2001 From: AljazD Date: Thu, 13 Nov 2025 14:34:16 +0100 Subject: [PATCH 3/3] Update NeuralNetwork.cpp --- src/pipeline/node/NeuralNetwork.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pipeline/node/NeuralNetwork.cpp b/src/pipeline/node/NeuralNetwork.cpp index d4cdd00c89..8d99c1850d 100644 --- a/src/pipeline/node/NeuralNetwork.cpp +++ b/src/pipeline/node/NeuralNetwork.cpp @@ -188,8 +188,10 @@ void NeuralNetwork::setNNArchiveSuperblob(const NNArchive& nnArchive, int numSha void NeuralNetwork::setNNArchiveOther(const NNArchive& nnArchive) { DAI_CHECK_V(nnArchive.getModelType() == model::ModelType::DLC || nnArchive.getModelType() == model::ModelType::OTHER, "NNArchive type is not DLC or OTHER"); - auto otherModelFormat = nnArchive.getOtherModelFormat().value(); - setOtherModelFormat(otherModelFormat); + if(nnArchive.getOtherModelFormat().has_value()) { + auto otherModelFormat = nnArchive.getOtherModelFormat().value(); + setOtherModelFormat(otherModelFormat); + } } // Specify local filesystem path to load the blob (which gets loaded at loadAssets)