From db2e356d0a61a264557e344ccbc588605cfa8ab4 Mon Sep 17 00:00:00 2001 From: Fabian Peller-Konrad <fabian.peller-konrad@kit.edu> Date: Mon, 21 Mar 2022 15:31:37 +0100 Subject: [PATCH] ltm updates and improved export of depth images in exr format --- .../libraries/armem/server/CMakeLists.txt | 6 ++ .../server/ltm/base/converter/Converter.h | 8 +- .../ltm/base/converter/image/Converter.cpp | 6 +- .../ltm/base/converter/image/Converter.h | 12 +-- .../base/converter/image/exr/ExrConverter.cpp | 33 +++++++++ .../base/converter/image/exr/ExrConverter.h | 21 ++++++ .../base/converter/image/png/PngConverter.cpp | 45 ++++++++++-- .../base/converter/image/png/PngConverter.h | 6 +- .../ltm/base/converter/object/Converter.cpp | 6 +- .../ltm/base/converter/object/Converter.h | 12 +-- .../converter/object/bson/BsonConverter.cpp | 10 +-- .../converter/object/bson/BsonConverter.h | 6 +- .../converter/object/json/JsonConverter.cpp | 6 +- .../converter/object/json/JsonConverter.h | 6 +- .../armem/server/ltm/base/detail/MemoryBase.h | 15 +--- .../armem/server/ltm/base/detail/MemoryItem.h | 41 +---------- .../server/ltm/base/detail/Processors.cpp | 35 +++++++++ .../armem/server/ltm/base/detail/Processors.h | 52 +++++++++++++ .../server/ltm/base/extractor/Extractor.h | 3 +- .../imageExtractor/DepthImageExtractor.cpp | 48 ++++++++++++ .../imageExtractor/DepthImageExtractor.h | 31 ++++++++ .../imageExtractor/ImageExtractor.cpp | 2 +- .../extractor/imageExtractor/ImageExtractor.h | 2 +- .../armem/server/ltm/disk/CoreSegment.cpp | 5 +- .../armem/server/ltm/disk/Entity.cpp | 49 +++++++++++-- .../armem/server/ltm/disk/EntitySnapshot.cpp | 73 +++++++++++-------- .../armem/server/ltm/disk/Memory.cpp | 28 ++++--- .../libraries/armem/server/ltm/disk/Memory.h | 5 +- .../armem/server/ltm/disk/ProviderSegment.cpp | 5 +- .../server/ltm/disk/detail/util/util.cpp | 23 ++++++ .../armem/server/ltm/disk/detail/util/util.h | 2 + .../armem_gui/disk/ControlWidget.cpp | 10 +-- .../armem_gui/instance/InstanceView.cpp | 5 +- 33 files changed, 457 insertions(+), 160 deletions(-) create mode 100644 source/RobotAPI/libraries/armem/server/ltm/base/converter/image/exr/ExrConverter.cpp create mode 100644 source/RobotAPI/libraries/armem/server/ltm/base/converter/image/exr/ExrConverter.h create mode 100644 source/RobotAPI/libraries/armem/server/ltm/base/detail/Processors.cpp create mode 100644 source/RobotAPI/libraries/armem/server/ltm/base/detail/Processors.h create mode 100644 source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/DepthImageExtractor.cpp create mode 100644 source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/DepthImageExtractor.h diff --git a/source/RobotAPI/libraries/armem/server/CMakeLists.txt b/source/RobotAPI/libraries/armem/server/CMakeLists.txt index 7fc48a638..51efca0f2 100644 --- a/source/RobotAPI/libraries/armem/server/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/server/CMakeLists.txt @@ -45,6 +45,7 @@ set(LIB_FILES MemoryRemoteGui.cpp RemoteGuiAronDataVisitor.cpp + ltm/base/detail/Processors.cpp ltm/base/detail/MemoryItem.cpp ltm/base/detail/MemoryBase.cpp ltm/base/detail/BufferedMemoryBase.cpp @@ -60,6 +61,7 @@ set(LIB_FILES ltm/base/extractor/Extractor.cpp ltm/base/extractor/imageExtractor/ImageExtractor.cpp + ltm/base/extractor/imageExtractor/DepthImageExtractor.cpp ltm/base/converter/Converter.cpp ltm/base/converter/object/Converter.cpp @@ -67,6 +69,7 @@ set(LIB_FILES ltm/base/converter/object/bson/BsonConverter.cpp ltm/base/converter/image/Converter.cpp ltm/base/converter/image/png/PngConverter.cpp + ltm/base/converter/image/exr/ExrConverter.cpp ltm/base/forgetter/Forgetter.cpp ltm/base/forgetter/LRUForgetter/LRUForgetter.cpp @@ -120,6 +123,7 @@ set(LIB_HEADERS MemoryRemoteGui.h RemoteGuiAronDataVisitor.h + ltm/base/detail/Processors.h ltm/base/detail/MemoryItem.h ltm/base/detail/MemoryBase.h ltm/base/detail/BufferedMemoryBase.h @@ -135,6 +139,7 @@ set(LIB_HEADERS ltm/base/extractor/Extractor.h ltm/base/extractor/imageExtractor/ImageExtractor.h + ltm/base/extractor/imageExtractor/DepthImageExtractor.h ltm/base/converter/Converter.h ltm/base/converter/object/Converter.h @@ -142,6 +147,7 @@ set(LIB_HEADERS ltm/base/converter/object/bson/BsonConverter.h ltm/base/converter/image/Converter.h ltm/base/converter/image/png/PngConverter.h + ltm/base/converter/image/exr/ExrConverter.h ltm/base/forgetter/Forgetter.h diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/Converter.h b/source/RobotAPI/libraries/armem/server/ltm/base/converter/Converter.h index f6eb1f71e..b109bb9af 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/Converter.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/Converter.h @@ -17,17 +17,19 @@ namespace armarx::armem::server::ltm Binary }; - Converter(const ConverterType t, const std::string& s, const aron::type::Descriptor c): + Converter(const ConverterType t, const std::string& id, const std::string& s, const aron::type::Descriptor c): type(t), + identifier(id), suffix(s), convertsType(c) {} virtual ~Converter() = default; - virtual std::vector<unsigned char> convert(const aron::data::VariantPtr& data) = 0; - virtual aron::data::VariantPtr convert(const std::vector<unsigned char>& data) = 0; + virtual std::pair<std::vector<unsigned char>, std::string> convert(const aron::data::VariantPtr& data) = 0; + virtual aron::data::VariantPtr convert(const std::vector<unsigned char>& data, const std::string&) = 0; const ConverterType type; + const std::string identifier; const std::string suffix; const aron::type::Descriptor convertsType; bool enabled = false; diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/Converter.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/Converter.cpp index a36db667d..39b4d94b0 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/Converter.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/Converter.cpp @@ -3,15 +3,15 @@ namespace armarx::armem::server::ltm { - std::vector<unsigned char> ImageConverter::convert(const aron::data::VariantPtr& data) + std::pair<std::vector<unsigned char>, std::string> ImageConverter::convert(const aron::data::VariantPtr& data) { auto d = aron::data::NDArray::DynamicCastAndCheck(data); return _convert(d); } - aron::data::VariantPtr ImageConverter::convert(const std::vector<unsigned char>& data) + aron::data::VariantPtr ImageConverter::convert(const std::vector<unsigned char>& data, const std::string& m) { - auto d = _convert(data); + auto d = _convert(data, m); return d; } diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/Converter.h b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/Converter.h index 78ba28f4a..a2b2df045 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/Converter.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/Converter.h @@ -14,17 +14,17 @@ namespace armarx::armem::server::ltm class ImageConverter : public Converter { public: - ImageConverter(const ConverterType t, const std::string& s): - Converter(t, s, aron::type::Descriptor::eImage) + ImageConverter(const ConverterType t, const std::string& id, const std::string& s): + Converter(t, id, s, aron::type::Descriptor::eImage) {} virtual ~ImageConverter() = default; - std::vector<unsigned char> convert(const aron::data::VariantPtr& data) final; - aron::data::VariantPtr convert(const std::vector<unsigned char>& data) final; + std::pair<std::vector<unsigned char>, std::string> convert(const aron::data::VariantPtr& data) final; + aron::data::VariantPtr convert(const std::vector<unsigned char>& data, const std::string&) final; protected: - virtual std::vector<unsigned char> _convert(const aron::data::NDArrayPtr& data) = 0; - virtual aron::data::NDArrayPtr _convert(const std::vector<unsigned char>& data) = 0; + virtual std::pair<std::vector<unsigned char>, std::string> _convert(const aron::data::NDArrayPtr& data) = 0; + virtual aron::data::NDArrayPtr _convert(const std::vector<unsigned char>& data, const std::string&) = 0; }; } diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/exr/ExrConverter.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/exr/ExrConverter.cpp new file mode 100644 index 000000000..c5eee777b --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/exr/ExrConverter.cpp @@ -0,0 +1,33 @@ +#include "ExrConverter.h" + +// ArmarX +#include <RobotAPI/libraries/aron/converter/opencv/OpenCVConverter.h> + +#include <opencv2/opencv.hpp> +#include <opencv2/imgcodecs.hpp> +#include <opencv2/imgproc.hpp> + + +namespace armarx::armem::server::ltm::converter::image +{ + std::pair<std::vector<unsigned char>, std::string> ExrConverter::_convert(const aron::data::NDArrayPtr& data) + { + ARMARX_CHECK_NOT_NULL(data); + + auto img = aron::converter::AronOpenCVConverter::ConvertToMat(data); + std::vector<unsigned char> buffer; + + auto shape = data->getShape(); // we know from the extraction that the shape has 3 elements + ARMARX_CHECK_EQUAL(shape.size(), 3); + ARMARX_CHECK_EQUAL(shape[2], 4); + + cv::imencode(".exr", img, buffer); + return std::make_pair(buffer, ""); + } + + aron::data::NDArrayPtr ExrConverter::_convert(const std::vector<unsigned char>& data, const std::string& m) + { + cv::Mat img = cv::imdecode(data, cv::IMREAD_ANYDEPTH); + return aron::converter::AronOpenCVConverter::ConvertFromMat(img); + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/exr/ExrConverter.h b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/exr/ExrConverter.h new file mode 100644 index 000000000..ad4b1bfa2 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/exr/ExrConverter.h @@ -0,0 +1,21 @@ +#pragma once + +// Base Class +#include "../Converter.h" + +namespace armarx::armem::server::ltm::converter::image +{ + class ExrConverter : public ImageConverter + { + public: + ExrConverter() : + ImageConverter(ConverterType::Binary, "depthimage", ".exr") + { + enabled = true; // enabled by default + } + + protected: + std::pair<std::vector<unsigned char>, std::string> _convert(const aron::data::NDArrayPtr& data) final; + aron::data::NDArrayPtr _convert(const std::vector<unsigned char>& data, const std::string&) final; + }; +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/png/PngConverter.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/png/PngConverter.cpp index 24fc8be1f..e8536cf64 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/png/PngConverter.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/png/PngConverter.cpp @@ -10,21 +10,52 @@ namespace armarx::armem::server::ltm::converter::image { - std::vector<unsigned char> PngConverter::_convert(const aron::data::NDArrayPtr& data) + std::pair<std::vector<unsigned char>, std::string> PngConverter::_convert(const aron::data::NDArrayPtr& data) { ARMARX_CHECK_NOT_NULL(data); auto img = aron::converter::AronOpenCVConverter::ConvertToMat(data); - cv::cvtColor(img, img, CV_RGB2BGR); std::vector<unsigned char> buffer; - cv::imencode(".png", img, buffer); - return buffer; + + + auto shape = data->getShape(); // we know from the extraction that the shape has 3 elements + ARMARX_CHECK_EQUAL(shape.size(), 3); + + if (shape[2] == 3) // its probably a rgb image + { + cv::cvtColor(img, img, CV_RGB2BGR); + cv::imencode(suffix, img, buffer); + return std::make_pair(buffer, ".rgb"); + } + + if (shape[2] == 1) // its probably a grayscale image + { + cv::imencode(suffix, img, buffer); + return std::make_pair(buffer, ".gs"); + } + + // try to export without conversion + cv::imencode(suffix, img, buffer); + return std::make_pair(buffer, ""); } - aron::data::NDArrayPtr PngConverter::_convert(const std::vector<unsigned char>& data) + aron::data::NDArrayPtr PngConverter::_convert(const std::vector<unsigned char>& data, const std::string& m) { - cv::Mat img = cv::imdecode(data, cv::IMREAD_COLOR); - cv::cvtColor(img, img, CV_BGR2RGB); + if (m == ".rgb") + { + cv::Mat img = cv::imdecode(data, cv::IMREAD_COLOR); + cv::cvtColor(img, img, CV_BGR2RGB); + return aron::converter::AronOpenCVConverter::ConvertFromMat(img); + } + + if (m == ".gs") + { + cv::Mat img = cv::imdecode(data, cv::IMREAD_GRAYSCALE); + return aron::converter::AronOpenCVConverter::ConvertFromMat(img); + } + + // try to load without conversion + cv::Mat img = cv::imdecode(data, cv::IMREAD_ANYCOLOR); return aron::converter::AronOpenCVConverter::ConvertFromMat(img); } } diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/png/PngConverter.h b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/png/PngConverter.h index a2ba7f86c..5b6c80134 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/png/PngConverter.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/image/png/PngConverter.h @@ -9,13 +9,13 @@ namespace armarx::armem::server::ltm::converter::image { public: PngConverter() : - ImageConverter(ConverterType::Binary, ".png") + ImageConverter(ConverterType::Binary, "image", ".png") { enabled = true; // enabled by default } protected: - std::vector<unsigned char> _convert(const aron::data::NDArrayPtr& data) final; - aron::data::NDArrayPtr _convert(const std::vector<unsigned char>& data) final; + std::pair<std::vector<unsigned char>, std::string> _convert(const aron::data::NDArrayPtr& data) final; + aron::data::NDArrayPtr _convert(const std::vector<unsigned char>& data, const std::string&) final; }; } diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/Converter.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/Converter.cpp index 26b9ffac7..4f0808a6f 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/Converter.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/Converter.cpp @@ -3,15 +3,15 @@ namespace armarx::armem::server::ltm { - std::vector<unsigned char> ObjectConverter::convert(const aron::data::VariantPtr& data) + std::pair<std::vector<unsigned char>, std::string> ObjectConverter::convert(const aron::data::VariantPtr& data) { auto d = aron::data::Dict::DynamicCastAndCheck(data); return _convert(d); } - aron::data::VariantPtr ObjectConverter::convert(const std::vector<unsigned char>& data) + aron::data::VariantPtr ObjectConverter::convert(const std::vector<unsigned char>& data, const std::string& m) { - auto d = _convert(data); + auto d = _convert(data, m); return d; } diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/Converter.h b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/Converter.h index 3ae9dcfa2..e5c8e9f80 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/Converter.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/Converter.h @@ -14,17 +14,17 @@ namespace armarx::armem::server::ltm class ObjectConverter : public Converter { public: - ObjectConverter(const ConverterType t, const std::string& s): - Converter(t, s, aron::type::Descriptor::eObject) + ObjectConverter(const ConverterType t, const std::string& id, const std::string& s): + Converter(t, id, s, aron::type::Descriptor::eObject) {} virtual ~ObjectConverter() = default; - std::vector<unsigned char> convert(const aron::data::VariantPtr& data) final; - aron::data::VariantPtr convert(const std::vector<unsigned char>& data) final; + std::pair<std::vector<unsigned char>, std::string> convert(const aron::data::VariantPtr& data) final; + aron::data::VariantPtr convert(const std::vector<unsigned char>& data, const std::string&) final; protected: - virtual std::vector<unsigned char> _convert(const aron::data::DictPtr& data) = 0; - virtual aron::data::DictPtr _convert(const std::vector<unsigned char>& data) = 0; + virtual std::pair<std::vector<unsigned char>, std::string> _convert(const aron::data::DictPtr& data) = 0; + virtual aron::data::DictPtr _convert(const std::vector<unsigned char>& data, const std::string&) = 0; }; } diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/bson/BsonConverter.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/bson/BsonConverter.cpp index d483958fa..8465201f3 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/bson/BsonConverter.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/bson/BsonConverter.cpp @@ -10,9 +10,9 @@ namespace armarx::armem::server::ltm::converter::object namespace bsoncxxbuilder = bsoncxx::builder::stream; namespace bsoncxxdoc = bsoncxx::document; - std::vector<unsigned char> BsonConverter::_convert(const aron::data::DictPtr& data) + std::pair<std::vector<unsigned char>, std::string> BsonConverter::_convert(const aron::data::DictPtr& data) { - std::vector<unsigned char> jsonVec = jsonConverter.convert(data); + auto [jsonVec, str] = jsonConverter.convert(data); std::string json(jsonVec.begin(), jsonVec.end()); auto view = bsoncxx::from_json(json).view(); @@ -21,16 +21,16 @@ namespace armarx::armem::server::ltm::converter::object { std::memcpy(bson.data(), view.data(), view.length()); } - return bson; + return std::make_pair(bson, str); } - aron::data::DictPtr BsonConverter::_convert(const std::vector<unsigned char>& data) + aron::data::DictPtr BsonConverter::_convert(const std::vector<unsigned char>& data, const std::string& m) { bsoncxx::document::view view(data.data(), data.size()); nlohmann::json json = bsoncxx::to_json(view); std::string str = json.dump(2); std::vector<unsigned char> jsonVec(str.begin(), str.end()); - auto v = jsonConverter.convert(jsonVec); + auto v = jsonConverter.convert(jsonVec, m); return aron::data::Dict::DynamicCast(v); } } diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/bson/BsonConverter.h b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/bson/BsonConverter.h index 2dbb293ab..6eb183be2 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/bson/BsonConverter.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/bson/BsonConverter.h @@ -15,12 +15,12 @@ namespace armarx::armem::server::ltm::converter::object { public: BsonConverter() : - ObjectConverter(ConverterType::Binary, ".bson") + ObjectConverter(ConverterType::Binary, "dict", ".bson") {} protected: - std::vector<unsigned char> _convert(const aron::data::DictPtr& data) final; - aron::data::DictPtr _convert(const std::vector<unsigned char>& data) final; + std::pair<std::vector<unsigned char>, std::string> _convert(const aron::data::DictPtr& data) final; + aron::data::DictPtr _convert(const std::vector<unsigned char>& data, const std::string&) final; private: JsonConverter jsonConverter; diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/json/JsonConverter.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/json/JsonConverter.cpp index 55fe9fddb..f2baa4865 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/json/JsonConverter.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/json/JsonConverter.cpp @@ -4,14 +4,14 @@ namespace armarx::armem::server::ltm::converter::object { - std::vector<unsigned char> JsonConverter::_convert(const aron::data::DictPtr& data) + std::pair<std::vector<unsigned char>, std::string> JsonConverter::_convert(const aron::data::DictPtr& data) { nlohmann::json j = aron::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON(data); auto str = j.dump(2); - return std::vector<unsigned char>(str.begin(), str.end()); + return std::make_pair(std::vector<unsigned char>(str.begin(), str.end()), ""); } - aron::data::DictPtr JsonConverter::_convert(const std::vector<unsigned char>& data) + aron::data::DictPtr JsonConverter::_convert(const std::vector<unsigned char>& data, const std::string&) { std::string str(data.begin(), data.end()); nlohmann::json j = nlohmann::json::parse(str); diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/json/JsonConverter.h b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/json/JsonConverter.h index 98be34661..1b9f86ee9 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/json/JsonConverter.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/converter/object/json/JsonConverter.h @@ -12,13 +12,13 @@ namespace armarx::armem::server::ltm::converter::object { public: JsonConverter() : - ObjectConverter(ConverterType::Str, ".json") + ObjectConverter(ConverterType::Str, "dict", ".json") { enabled = true; // always true! } protected: - std::vector<unsigned char> _convert(const aron::data::DictPtr& data) final; - aron::data::DictPtr _convert(const std::vector<unsigned char>& data) final; + std::pair<std::vector<unsigned char>, std::string> _convert(const aron::data::DictPtr& data) final; + aron::data::DictPtr _convert(const std::vector<unsigned char>& data, const std::string&) final; }; } diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryBase.h b/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryBase.h index ce25bee41..386ce278b 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryBase.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryBase.h @@ -9,7 +9,6 @@ #include "CoreSegmentBase.h" // ArmarX -#include <ArmarXCore/core/application/properties/Properties.h> #include <ArmarXCore/core/time/TimeUtil.h> #include <ArmarXCore/core/logging/LoggingUtil.h> @@ -112,19 +111,7 @@ namespace armarx::armem::server::ltm /// parameters virtual void createPropertyDefinitions(PropertyDefinitionsPtr& defs, const std::string& prefix) { - // filters - defs->optional(processors->memFreqFilter.enabled, prefix + "memFreqFilter.Enabled"); - defs->optional(processors->memFreqFilter.waitingTimeInMs, prefix + "memFreqFilter.WaitingTime", "Waiting time in MS after each LTM update."); - defs->optional(processors->snapFreqFilter.enabled, prefix + "snapFreqFilter.Enabled"); - defs->optional(processors->snapFreqFilter.waitingTimeInMs, prefix + "snapFreqFilter.WaitingTime", "Waiting time in MS after each Entity update."); - defs->optional(processors->snapEqFilter.enabled, prefix + "snapEqFilter.Enabled"); - defs->optional(processors->snapEqFilter.maxWaitingTimeInMs, prefix + "snapEqFilter.MaxWaitingTime", "Max Waiting time in MS after each Entity update."); - - // extractors - defs->optional(processors->imageExtractor.enabled, prefix + "imageExtractor.Enabled"); - - // converters - defs->optional(processors->pngConverter.enabled, prefix + "pngConverter.Enabled"); + processors->createPropertyDefinitions(defs, prefix); } /// get level name diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.h b/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.h index 27b91fdb9..570231509 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/detail/MemoryItem.h @@ -5,49 +5,10 @@ #include <optional> #include <string> -#include "../filter/frequencyFilter/FrequencyFilter.h" -#include "../filter/equalityFilter/EqualityFilter.h" -#include "../extractor/imageExtractor/ImageExtractor.h" -#include "../converter/object/json/JsonConverter.h" -#include "../converter/image/png/PngConverter.h" - -#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include "Processors.h" namespace armarx::armem::server::ltm { - /// all necessary classes to filter and convert an entry of the ltm to some other format(s) - struct Processors - { - // Unique Memory Filters - std::vector<MemoryFilter*> memFilters; - filter::MemoryFrequencyFilter memFreqFilter; - - // Unique Snapshot filters - std::vector<SnapshotFilter*> snapFilters; - filter::SnapshotFrequencyFilter snapFreqFilter; - filter::SnapshotEqualityFilter snapEqFilter; - - // Extractors - std::vector<Extractor*> extractors; - extractor::ImageExtractor imageExtractor; - - // Converters - std::map<aron::type::Descriptor, Converter*> converters; - converter::object::JsonConverter jsonConverter; - converter::image::PngConverter pngConverter; - - Processors() - { - // setup containers - memFilters.push_back(&memFreqFilter); - snapFilters.push_back(&snapFreqFilter); - snapFilters.push_back(&snapEqFilter); - extractors.push_back(&imageExtractor); - converters.insert({jsonConverter.convertsType, &jsonConverter}); - converters.insert({pngConverter.convertsType, &pngConverter}); - } - }; - /// @brief Interface functions for the longterm memory classes class MemoryItem { diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/detail/Processors.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/detail/Processors.cpp new file mode 100644 index 000000000..6e176bf79 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/base/detail/Processors.cpp @@ -0,0 +1,35 @@ +#include "Processors.h" + +namespace armarx::armem::server::ltm +{ + Processors::Processors() + { + // setup containers + memFilters.push_back(&memFreqFilter); + snapFilters.push_back(&snapFreqFilter); + snapFilters.push_back(&snapEqFilter); + extractors.push_back(&imageExtractor); + extractors.push_back(&depthImageExtractor); + converters.insert({pngConverter.identifier, &pngConverter}); + converters.insert({exrConverter.identifier, &exrConverter}); + } + + void Processors::createPropertyDefinitions(PropertyDefinitionsPtr& defs, const std::string& prefix) + { + // filters + defs->optional(memFreqFilter.enabled, prefix + "memFreqFilter.Enabled"); + defs->optional(memFreqFilter.waitingTimeInMs, prefix + "memFreqFilter.WaitingTime", "Waiting time in MS after each LTM update."); + defs->optional(snapFreqFilter.enabled, prefix + "snapFreqFilter.Enabled"); + defs->optional(snapFreqFilter.waitingTimeInMs, prefix + "snapFreqFilter.WaitingTime", "Waiting time in MS after each Entity update."); + defs->optional(snapEqFilter.enabled, prefix + "snapEqFilter.Enabled"); + defs->optional(snapEqFilter.maxWaitingTimeInMs, prefix + "snapEqFilter.MaxWaitingTime", "Max Waiting time in MS after each Entity update."); + + // extractors + defs->optional(imageExtractor.enabled, prefix + "imageExtractor.Enabled"); + defs->optional(depthImageExtractor.enabled, prefix + "depthImageExtractor.Enabled"); + + // converters + defs->optional(pngConverter.enabled, prefix + "pngConverter.Enabled"); + defs->optional(exrConverter.enabled, prefix + "exrConverter.Enabled"); + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/detail/Processors.h b/source/RobotAPI/libraries/armem/server/ltm/base/detail/Processors.h new file mode 100644 index 000000000..232e6f32b --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/base/detail/Processors.h @@ -0,0 +1,52 @@ +#pragma once + +#include <map> +#include <mutex> +#include <optional> +#include <string> + +#include <ArmarXCore/core/application/properties/Properties.h> + +#include "../filter/frequencyFilter/FrequencyFilter.h" +#include "../filter/equalityFilter/EqualityFilter.h" +#include "../extractor/imageExtractor/ImageExtractor.h" +#include "../extractor/imageExtractor/DepthImageExtractor.h" +#include "../converter/object/json/JsonConverter.h" +#include "../converter/image/png/PngConverter.h" +#include "../converter/image/exr/ExrConverter.h" + +#include <RobotAPI/libraries/armem/core/MemoryID.h> + +namespace armarx::armem::server::ltm +{ + /// all necessary classes to filter and convert an entry of the ltm to some other format(s) + class Processors + { + public: + Processors(); + void createPropertyDefinitions(PropertyDefinitionsPtr& defs, const std::string& prefix); + + public: + // Unique Memory Filters + std::vector<MemoryFilter*> memFilters; + filter::MemoryFrequencyFilter memFreqFilter; + + // Unique Snapshot filters + std::vector<SnapshotFilter*> snapFilters; + filter::SnapshotFrequencyFilter snapFreqFilter; + filter::SnapshotEqualityFilter snapEqFilter; + + // Special Extractors + std::vector<Extractor*> extractors; + extractor::ImageExtractor imageExtractor; + extractor::DepthImageExtractor depthImageExtractor; + + // Special Converters + std::map<std::string, Converter*> converters; + converter::image::PngConverter pngConverter; + converter::image::ExrConverter exrConverter; + + // Default converter + converter::object::JsonConverter defaultObjectConverter; + }; +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/extractor/Extractor.h b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/Extractor.h index 8fbedc484..0fd5ca5ad 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/extractor/Extractor.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/Extractor.h @@ -18,13 +18,14 @@ namespace armarx::armem::server::ltm std::map<std::string, aron::data::VariantPtr> extraction; }; - Extractor(const aron::type::Descriptor t) : extractsType(t) {}; + Extractor(const aron::type::Descriptor t, const std::string& id) : extractsType(t), identifier(id) {}; virtual ~Extractor() = default; virtual Extraction extract(aron::data::DictPtr& data) = 0; virtual aron::data::DictPtr merge(Extraction& encoding) = 0; const aron::type::Descriptor extractsType; + const std::string identifier; bool enabled = false; }; } diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/DepthImageExtractor.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/DepthImageExtractor.cpp new file mode 100644 index 000000000..4640cca47 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/DepthImageExtractor.cpp @@ -0,0 +1,48 @@ +#include "DepthImageExtractor.h" + + +namespace armarx::armem::server::ltm::extractor +{ + void DepthImageExtractorVisitor::visitDictOnEnter(Input& data) + { + ARMARX_CHECK_NOT_NULL(data); + + auto dict = aron::data::Dict::DynamicCastAndCheck(data); + for (const auto& [key, child] : dict->getElements()) + { + if (child && child->getDescriptor() == aron::data::Descriptor::eNDArray) + { + auto ndarray = aron::data::NDArray::DynamicCastAndCheck(child); + auto shape = ndarray->getShape(); + if (shape.size() == 3 && shape[2] == 4 && std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<int>()) > 200) // must be big enough to assume an image (instead of 4x4x4 poses) + { + depthImages[key] = ndarray; + dict->setElement(key, nullptr); + } + } + } + } + + void DepthImageExtractorVisitor::visitUnknown(Input&) + { + // A member is null. Simply ignore... + } + + Extractor::Extraction DepthImageExtractor::extract(aron::data::DictPtr& data) + { + DepthImageExtractorVisitor visitor; + aron::data::VariantPtr var = std::static_pointer_cast<aron::data::Variant>(data); + aron::data::VariantPtr p; + aron::data::visitRecursive(visitor, var); + + Extraction encoding; + encoding.dataWithoutExtraction = data; + encoding.extraction = visitor.depthImages; + return encoding; + } + + aron::data::DictPtr DepthImageExtractor::merge(Extraction& encoding) + { + return encoding.dataWithoutExtraction; + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/DepthImageExtractor.h b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/DepthImageExtractor.h new file mode 100644 index 000000000..09b305e3c --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/DepthImageExtractor.h @@ -0,0 +1,31 @@ +#pragma once + +// Base Class +#include "../Extractor.h" + +#include <RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.h> + +namespace armarx::armem::server::ltm::extractor +{ + class DepthImageExtractorVisitor : public aron::data::RecursiveVariantVisitor + { + public: + std::map<std::string, aron::data::VariantPtr> depthImages; + + void visitDictOnEnter(Input& data); + void visitUnknown(Input& data); + }; + + class DepthImageExtractor : public Extractor + { + public: + DepthImageExtractor() : + Extractor(aron::type::Descriptor::eImage, "depthimage") + { + enabled = true; + }; + + virtual Extraction extract(aron::data::DictPtr& data) override; + virtual aron::data::DictPtr merge(Extraction& encoding) override; + }; +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/ImageExtractor.cpp b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/ImageExtractor.cpp index ff86e0624..c646a4f47 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/ImageExtractor.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/ImageExtractor.cpp @@ -14,7 +14,7 @@ namespace armarx::armem::server::ltm::extractor { auto ndarray = aron::data::NDArray::DynamicCastAndCheck(child); auto shape = ndarray->getShape(); - if (shape.size() == 3 && std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<int>()) > 200) // must be big enough to assume an image (instead of 4x4x4 poses) + if (shape.size() == 3 && (shape[2] == 3 || shape[2] == 1 /* 3 channel color or grayscale */) && std::accumulate(std::begin(shape), std::end(shape), 1, std::multiplies<int>()) > 200) // must be big enough to assume an image (instead of 4x4x4 poses) { images[key] = ndarray; dict->setElement(key, nullptr); diff --git a/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/ImageExtractor.h b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/ImageExtractor.h index 73a923579..be9c80ba7 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/ImageExtractor.h +++ b/source/RobotAPI/libraries/armem/server/ltm/base/extractor/imageExtractor/ImageExtractor.h @@ -20,7 +20,7 @@ namespace armarx::armem::server::ltm::extractor { public: ImageExtractor() : - Extractor(aron::type::Descriptor::eImage) + Extractor(aron::type::Descriptor::eImage, "image") { enabled = true; }; diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/CoreSegment.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/CoreSegment.cpp index 14e8f31fa..ef68e7f01 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/CoreSegment.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/CoreSegment.cpp @@ -91,6 +91,9 @@ namespace armarx::armem::server::ltm::disk void CoreSegment::_store(const armem::wm::CoreSegment& c) { + auto currentMaxExport = currentExport; + auto encodingModeOfPast = currentMode; + if (id().coreSegmentName.empty()) { ARMARX_WARNING << "During storage of segment '" << c.id().str() << "' I noticed that the corresponding LTM has no id set. " << @@ -110,7 +113,7 @@ namespace armarx::armem::server::ltm::disk c.forEachProviderSegment([&](const auto& prov) { - ProviderSegment c(memoryParentPath, id().withProviderSegmentName(prov.id().providerSegmentName), processors, currentMode, currentExport); + ProviderSegment c(memoryParentPath, id().withProviderSegmentName(prov.id().providerSegmentName), processors, encodingModeOfPast, currentMaxExport); util::ensureFolderExists(defaultMPath, c.getRelativePathForMode(defaultMode), true); c.store(prov); }); diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/Entity.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/Entity.cpp index a46aee88a..ac1703ac9 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/Entity.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/Entity.cpp @@ -29,17 +29,47 @@ namespace armarx::armem::server::ltm::disk return false; } - for (const auto& subdirName : util::getAllDirectories(mPath, relPath)) + for (const auto& hName : util::getAllDirectories(mPath, relPath)) { - if (!util::isNumber(subdirName)) + if (!util::isNumber(hName)) { - ARMARX_WARNING << "Found a non-timestamp folder inside an entity '" << id().str() << "' with name '" << subdirName << "'. " << + ARMARX_WARNING << "Found a non-timestamp folder inside an entity '" << id().str() << "' with name '" << hName << "'. " << "Ignoring this folder, however this is a bad situation."; continue; } - EntitySnapshot c(memoryParentPath, id().withTimestamp(timeFromStringMicroSeconds(subdirName)), processors, currentMode, currentExport); - func(c); + // check if this is already a microsec folder (legacy export support) + //if (std::stol(secName) > 1647524607 /* the time in us the new export was implemented */) + //{ + // EntitySnapshot c(memoryParentPath, id().withTimestamp(timeFromStringMicroSeconds(secName)), processors, currentMode, currentExport); + // func(c); + // continue; + //} + + auto hRelPath = relPath / hName; + for (const auto& secName : util::getAllDirectories(mPath, hRelPath)) + { + if (!util::isNumber(secName)) + { + ARMARX_WARNING << "Found a non-timestamp folder inside an entity '" << id().str() << "' with name '" << secName << "'. " << + "Ignoring this folder, however this is a bad situation."; + continue; + } + + auto secRelPath = hRelPath / secName; + for (const auto& usecName : util::getAllDirectories(mPath, secRelPath)) + { + if (!util::isNumber(usecName)) + { + ARMARX_WARNING << "Found a non-timestamp folder inside an entity '" << id().str() << "' with name '" << usecName << "'. " << + "Ignoring this folder, however this is a bad situation."; + continue; + } + + EntitySnapshot c(memoryParentPath, id().withTimestamp(timeFromStringMicroSeconds(usecName)), processors, currentMode, currentExport); + func(c); + } + } } return true; } @@ -272,9 +302,12 @@ namespace armarx::armem::server::ltm::disk c.forEachSnapshot([&](const auto& snap) { - for (unsigned long i = 0; i < currentExport; ++i) + auto currentMaxExport = currentExport; + auto encodingModeOfPast = currentMode; + + for (unsigned long i = 0; i < currentMaxExport; ++i) { - MemoryEncodingMode mode = i == 0 ? defaultMode : currentMode; + MemoryEncodingMode mode = i == 0 ? defaultMode : encodingModeOfPast; auto mPath = getMemoryBasePathForMode(mode, i); EntitySnapshot c(memoryParentPath, id().withTimestamp(snap.id().timestamp), processors, mode, i); @@ -294,7 +327,7 @@ namespace armarx::armem::server::ltm::disk } } - EntitySnapshot c(memoryParentPath, id().withTimestamp(snap.id().timestamp), processors, currentMode, currentExport); + EntitySnapshot c(memoryParentPath, id().withTimestamp(snap.id().timestamp), processors, encodingModeOfPast, currentMaxExport); util::ensureFolderExists(defaultMPath, c.getRelativePathForMode(defaultMode)); c.store(snap); }); diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/EntitySnapshot.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/EntitySnapshot.cpp index 870292195..a4aece1fd 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/EntitySnapshot.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/EntitySnapshot.cpp @@ -14,7 +14,7 @@ namespace armarx::armem::server::ltm::disk { EntitySnapshot::EntitySnapshot(const std::filesystem::path& p, const MemoryID& id, const std::shared_ptr<Processors>& filters, const DiskMemoryItem::MemoryEncodingMode mode, const unsigned long e) : EntitySnapshotBase(id, filters), - DiskMemoryItem(p, EscapeSegmentName(id.memoryName), std::filesystem::path(EscapeSegmentName(id.coreSegmentName)) / EscapeSegmentName(id.providerSegmentName) / EscapeSegmentName(id.entityName) / id.timestampStr()), + DiskMemoryItem(p, EscapeSegmentName(id.memoryName), std::filesystem::path(EscapeSegmentName(id.coreSegmentName)) / EscapeSegmentName(id.providerSegmentName) / EscapeSegmentName(id.entityName) / std::to_string(id.timestamp.toSeconds() / 3600 /* hours */) / std::to_string(id.timestamp.toSeconds()) / id.timestampStr()), currentMode(mode), currentExport(e) { @@ -53,7 +53,7 @@ namespace armarx::armem::server::ltm::disk return; } - auto& dictConverter = processors->converters.at(aron::type::Descriptor::eObject); + auto& dictConverter = processors->defaultObjectConverter; // Get data from disk for (unsigned int i = 0; i < e.size(); ++i) @@ -61,17 +61,22 @@ namespace armarx::armem::server::ltm::disk std::filesystem::path relIPath = relPath / std::to_string(i) / ""; if (util::checkIfFolderExists(mPath, relIPath)) { - std::filesystem::path relDataPath = relIPath / (constantes::DATA_FILENAME + dictConverter->suffix); - std::filesystem::path relMetadataPath = relIPath / (constantes::METADATA_FILENAME + dictConverter->suffix); + std::string dataFilename = (constantes::DATA_FILENAME + dictConverter.suffix); + std::string metadataFilename = (constantes::METADATA_FILENAME + dictConverter.suffix); + std::filesystem::path relDataPath = relIPath / dataFilename; + std::filesystem::path relMetadataPath = relIPath / metadataFilename; auto& ins = e.getInstance(i); aron::data::DictPtr datadict = nullptr; aron::data::DictPtr metadatadict = nullptr; - if (util::checkIfFileExists(mPath, relDataPath)) + // get list of all files. This ensures that we only have to call fs::exists once for each file + auto allFilesInIndexFolder = util::getAllFiles(mPath, relIPath); + + if (std::find(allFilesInIndexFolder.begin(), allFilesInIndexFolder.end(), dataFilename) != allFilesInIndexFolder.end()) { auto datafilecontent = util::readDataFromFile(mPath, relDataPath); - auto dataaron = dictConverter->convert(datafilecontent); + auto dataaron = dictConverter.convert(datafilecontent, ""); datadict = aron::data::Dict::DynamicCastAndCheck(dataaron); // check for special members @@ -79,18 +84,18 @@ namespace armarx::armem::server::ltm::disk { for (auto& [t, f] : processors->converters) { - if (t == aron::type::Descriptor::eObject) - { - continue; - } - - std::filesystem::path relMemberPath = relIPath / (key + f->suffix); - - if (util::checkIfFileExists(mPath, relMemberPath)) + for (const auto& filename : allFilesInIndexFolder) // iterate over all files and search for matching ones { - auto memberfilecontent = util::readDataFromFile(mPath, relMemberPath); - auto memberaron = f->convert(memberfilecontent); - datadict->setElement(key, memberaron); + if (simox::alg::starts_with(filename, key) and simox::alg::ends_with(filename, f->suffix)) + { + std::filesystem::path relMemberPath = relIPath / filename; + std::string mode = simox::alg::remove_suffix(simox::alg::remove_prefix(filename, key), f->suffix); + + auto memberfilecontent = util::readDataFromFile(mPath, relMemberPath); + auto memberaron = f->convert(memberfilecontent, mode); + datadict->setElement(key, memberaron); + break; + } } } } @@ -100,10 +105,10 @@ namespace armarx::armem::server::ltm::disk ARMARX_ERROR << "Could not find the data file '" << relDataPath.string() << "'. Continuing without data."; } - if (util::checkIfFileExists(mPath, relMetadataPath)) + if (std::find(allFilesInIndexFolder.begin(), allFilesInIndexFolder.end(), metadataFilename) != allFilesInIndexFolder.end()) { auto metadatafilecontent = util::readDataFromFile(mPath, relMetadataPath); - auto metadataaron = dictConverter->convert(metadatafilecontent); + auto metadataaron = dictConverter.convert(metadatafilecontent, ""); metadatadict = aron::data::Dict::DynamicCastAndCheck(metadataaron); } else @@ -122,6 +127,9 @@ namespace armarx::armem::server::ltm::disk void EntitySnapshot::_store(const armem::wm::EntitySnapshot& e) const { + //auto currentMaxExport = currentExport; + //auto encodingModeOfPast = currentMode; + if (id().timestampStr().empty()) { ARMARX_WARNING << "During storage of segment '" << e.id().str() << "' I noticed that the corresponding LTM has no id set. " << @@ -131,7 +139,7 @@ namespace armarx::armem::server::ltm::disk auto defaultMode = MemoryEncodingMode::FILESYSTEM; - auto& dictConverter = processors->converters.at(aron::type::Descriptor::eObject); + auto& dictConverter = processors->defaultObjectConverter; auto defaultMPath = getMemoryBasePathForMode(defaultMode, 0); auto defaultRelPath = getRelativePathForMode(defaultMode); @@ -152,8 +160,8 @@ namespace armarx::armem::server::ltm::disk { util::ensureFolderExists(defaultMPath, defaultRelIPath); - std::filesystem::path relDataPath = defaultRelIPath / (constantes::DATA_FILENAME + dictConverter->suffix); - std::filesystem::path relMetadataPath = defaultRelIPath / (constantes::METADATA_FILENAME + dictConverter->suffix); + std::filesystem::path relDataPath = defaultRelIPath / (constantes::DATA_FILENAME + dictConverter.suffix); + std::filesystem::path relMetadataPath = defaultRelIPath / (constantes::METADATA_FILENAME + dictConverter.suffix); auto& ins = e.getInstance(i); @@ -167,7 +175,7 @@ namespace armarx::armem::server::ltm::disk { if (!x->enabled) continue; - auto t = x->extractsType; + const auto& t = x->identifier; Converter* conv = nullptr; // find suitable converter for (const auto& [ct, c] : processors->converters) @@ -184,9 +192,11 @@ namespace armarx::armem::server::ltm::disk for (const auto& [memberName, var] : dataExt.extraction) { ARMARX_CHECK_NOT_NULL(var); - std::filesystem::path relMemberPath = defaultRelIPath / (memberName + conv->suffix); - auto memberDataVec = conv->convert(var); - util::writeDataToFile(defaultMPath, relMemberPath, memberDataVec); + + auto [memberDataVec, memberDataModeSuffix] = conv->convert(var); + std::filesystem::path relMemberPath = defaultRelIPath / (memberName + memberDataModeSuffix + conv->suffix); + + util::writeDataToFileRepeated(defaultMPath, relMemberPath, memberDataVec); } dataAron = dataExt.dataWithoutExtraction; @@ -195,10 +205,13 @@ namespace armarx::armem::server::ltm::disk } // convert dict and metadata - auto dataVec = dictConverter->convert(dataAron); - auto metadataVec = dictConverter->convert(metadataAron); - util::writeDataToFile(defaultMPath, relDataPath, dataVec); - util::writeDataToFile(defaultMPath, relMetadataPath, metadataVec); + auto [dataVec, dataVecModeSuffix] = dictConverter.convert(dataAron); + auto [metadataVec, metadataVecModeSuffix] = dictConverter.convert(metadataAron); + ARMARX_CHECK_EMPTY(dataVecModeSuffix); + ARMARX_CHECK_EMPTY(metadataVecModeSuffix); + + util::writeDataToFileRepeated(defaultMPath, relDataPath, dataVec); + util::writeDataToFileRepeated(defaultMPath, relMetadataPath, metadataVec); } // Ignore if the full index already exists. Actually this should not happen since the existence of the ts folder is checked on entity level } diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/Memory.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/Memory.cpp index c90cb8e33..50cde9d18 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/Memory.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/Memory.cpp @@ -11,7 +11,7 @@ namespace armarx::armem::server::ltm::disk { Base::createPropertyDefinitions(properties, prefix); properties->optional(memoryParentPath, prefix + "storagepath", "The path to the memory storage (the memory will be stored in a seperate subfolder)."); - properties->optional(sizeToCompressDataInBytes, prefix + "sizeToCompressDataInBytes", "The size in bytes to compress away the current export. Exports are numbered (lower number means newer)."); + properties->optional(sizeToCompressDataInMegaBytes, prefix + "sizeToCompressDataInMegaBytes", "The size in MB to compress away the current export. Exports are numbered (lower number means newer)."); } Memory::Memory() : @@ -129,7 +129,7 @@ namespace armarx::armem::server::ltm::disk std::lock_guard l(ltm_mutex); // we cannot store a memory multiple times simultaneously MemoryEncodingMode defaultMode = MemoryEncodingMode::FILESYSTEM; - MemoryEncodingMode defaultEncodeMode = exportEncodingMode; + MemoryEncodingMode encodeModeOfPast = exportEncodingMode; // Storage will always be in filesystem mode! // Somehow, minizip was not able to write data to images. It always created a folder named xyz.png without any data in it... // Another problem is that storing data directly in compressed format will require a lot of time when the compressed file is big (>20MB) @@ -143,31 +143,37 @@ namespace armarx::armem::server::ltm::disk auto defaultMPath = getMemoryBasePathForMode(defaultMode, 0); - // Check if we have to move the memory! + // Check if we have to compress the memory! // See above mentioned issues with directly compressing the data. Therefore we store data in plain text and compress from time to time - // using system calls. Also increase all old exports + // using system calls. Also increase the index of all old exports auto size = filesystem::util::getSizeOfDirectory(defaultMPath); //std::cout << "Current maxExportIndex is: " << maxExportIndex << std::endl; - if (size >= sizeToCompressDataInBytes) + if (size >= (sizeToCompressDataInMegaBytes * 1024 * 1024)) { - // increase old memories + ARMARX_INFO << "Compressen of memory " + id().memoryName + " needed because the size of last export is " + std::to_string(size / 1024.f / 1024.f) + " (>= " + std::to_string(sizeToCompressDataInMegaBytes) + ")"; + + // increase index of old memories for (unsigned long i = maxExportIndex; i >= 1; --i) { - auto exportPath = getMemoryBasePathForMode(defaultEncodeMode, i); - auto newExportPath = getMemoryBasePathForMode(defaultEncodeMode, i+1); + ARMARX_INFO << "Increasing the index of old compressed memory " + id().memoryName + " (" + std::to_string(i) + " to " + std::to_string(i+1) + ")"; + auto exportPath = getMemoryBasePathForMode(encodeModeOfPast, i); + auto newExportPath = getMemoryBasePathForMode(encodeModeOfPast, i+1); std::string moveCommand = "mv " + exportPath.string() + " " + newExportPath.string(); //std::cout << "Exec command: " << moveCommand << std::endl; int ret = system(moveCommand.c_str()); (void) ret; } - // zip current export - auto newExportPath = getMemoryBasePathForMode(defaultEncodeMode, 1); // 1 will be the new export + // zip away current export + ARMARX_INFO << "Compressing the last export of " + id().memoryName; + auto newExportPath = getMemoryBasePathForMode(encodeModeOfPast, 1); // 1 will be the new export std::string zipCommand = "cd " + memoryParentPath.string() + " && zip -r " + newExportPath.string() + " " + escapedMemoryName; //std::cout << "Exec command: " << zipCommand << std::endl; int ret = system(zipCommand.c_str()); (void) ret; + // remove unzipped memory export + ARMARX_INFO << "Removing the last export of " + id().memoryName; std::string rmCommand = "rm -r " + defaultMPath.string(); ret = system(rmCommand.c_str()); (void) ret; @@ -179,7 +185,7 @@ namespace armarx::armem::server::ltm::disk memory.forEachCoreSegment([&](const auto& core) { - CoreSegment c(memoryParentPath, id().withCoreSegmentName(core.id().coreSegmentName), processors, defaultEncodeMode, maxExportIndex); + CoreSegment c(memoryParentPath, id().withCoreSegmentName(core.id().coreSegmentName), processors, encodeModeOfPast, 0 /* how far to look back in past on enity level. For full lookup use maxExportIndex. */); util::ensureFolderExists(defaultMPath, c.getRelativePathForMode(defaultMode), true); // create subfolder c.store(core); }); diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/Memory.h b/source/RobotAPI/libraries/armem/server/ltm/disk/Memory.h index e0b8a6551..085a56459 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/Memory.h +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/Memory.h @@ -38,6 +38,9 @@ namespace armarx::armem::server::ltm::disk private: MemoryEncodingMode exportEncodingMode = MemoryEncodingMode::MINIZIP; unsigned long maxExportIndex = 0; - unsigned long sizeToCompressDataInBytes = long(5) * 1024 * 1024 * 1024; // 5GB + unsigned long sizeToCompressDataInMegaBytes = long(1) * 1024; // 1GB + + public: + static const int DEPTH_TO_DATA_FILES = 7; // from memory folder = 1 (cseg) + 1 (pseg) + 1 (ent) + 3 (snap) + 1 (inst) }; } // namespace armarx::armem::server::ltm::disk diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/ProviderSegment.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/ProviderSegment.cpp index a056f85ce..067ff6bdd 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/ProviderSegment.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/ProviderSegment.cpp @@ -93,6 +93,9 @@ namespace armarx::armem::server::ltm::disk void ProviderSegment::_store(const armem::wm::ProviderSegment& p) { + auto currentMaxExport = currentExport; + auto encodingModeOfPast = currentMode; + if (id().providerSegmentName.empty()) { ARMARX_WARNING << "During storage of segment '" << p.id().str() << "' I noticed that the corresponding LTM has no id set. " << @@ -112,7 +115,7 @@ namespace armarx::armem::server::ltm::disk p.forEachEntity([&](const auto& e) { - Entity c(memoryParentPath, id().withEntityName(e.id().entityName), processors, currentMode, currentExport); + Entity c(memoryParentPath, id().withEntityName(e.id().entityName), processors, encodingModeOfPast, currentMaxExport); util::ensureFolderExists(defaultMPath, c.getRelativePathForMode(defaultMode), true); c.store(e); }); diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/util.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/util.cpp index 701245811..8828dc595 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/util.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/util.cpp @@ -1,5 +1,8 @@ #include "util.h" +#include <thread> +#include <chrono> + namespace armarx::armem::server::ltm::disk { namespace util @@ -87,6 +90,26 @@ namespace armarx::armem::server::ltm::disk return filesystem::util::writeDataInFilesystemFile(mPath / p, data); } + void writeDataToFileRepeated(const std::filesystem::path& mPath, const std::filesystem::path& p, const std::vector<unsigned char>& data, const unsigned int maxTries, const unsigned int sleepTimeMs) + { + for (unsigned int i = 0; i < maxTries; ++i) + { + try + { + writeDataToFile(mPath, p, data); + return; + } + catch (const error::ArMemError&) + { + // wait a bit to give the filesystem enough time to manage the workload + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs)); + } + } + + // even after all the tries we did not succeeded. This is very bad! + throw error::ArMemError("ATTENTION! Even after " + std::to_string(maxTries) + " tries, the memory was not able to store the instance at path '" + p.string() + "'. This means this instance will be lost!"); + } + std::vector<unsigned char> readDataFromFile(const std::filesystem::path& mPath, const std::filesystem::path& p) { if (mPath.extension() == minizip::util::MINIZIP_SUFFIX) diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/util.h b/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/util.h index 58dc28123..e260d7092 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/util.h +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/detail/util/util.h @@ -33,6 +33,8 @@ namespace armarx::armem::server::ltm::disk void writeDataToFile(const std::filesystem::path& mPath, const std::filesystem::path& p, const std::vector<unsigned char>& data); + void writeDataToFileRepeated(const std::filesystem::path& mPath, const std::filesystem::path& p, const std::vector<unsigned char>& data, const unsigned int maxTries = 100, const unsigned int sleepTimeMs = 10); + std::vector<unsigned char> readDataFromFile(const std::filesystem::path& mPath, const std::filesystem::path& p); std::vector<std::string> getAllDirectories(const std::filesystem::path& mPath, const std::filesystem::path& p); diff --git a/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp b/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp index ff966676a..d8755bbfd 100644 --- a/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp @@ -144,9 +144,9 @@ namespace armarx::armem::gui::disk bool isSingleMemory = false; for (auto i = std::filesystem::recursive_directory_iterator(path); i != std::filesystem::recursive_directory_iterator(); ++i) { - if (i.depth() > 7) + if (i.depth() > armem::server::ltm::disk::Memory::DEPTH_TO_DATA_FILES + 2) { - // After depth 7 we stop searching to not freeze GUI too long + // After some depth we stop searching to not freeze GUI too long setStatus("Could not import a memory from " + path.string() + ". Data files were not found until max-depth 7. Skipping import."); return memoryData; } @@ -156,7 +156,7 @@ namespace armarx::armem::gui::disk // if one matches it is enough to check if (std::filesystem::is_regular_file(dir.path()) && simox::alg::starts_with(dir.path().filename(), "data.aron")) { - isSingleMemory = (i.depth() == 5); + isSingleMemory = (i.depth() == armem::server::ltm::disk::Memory::DEPTH_TO_DATA_FILES); break; } } @@ -168,7 +168,7 @@ namespace armarx::armem::gui::disk // const query::data::Input queryIce = queryInput.toIce(); int numLoaded = 0; - auto loadMemory = [&](const std::filesystem::path& p){ + auto loadMemory = [&](const std::filesystem::path& p) { if (std::filesystem::is_directory(p)) { armem::server::ltm::disk::Memory ltm(p.parent_path(), p.filename()); @@ -185,7 +185,7 @@ namespace armarx::armem::gui::disk } else { - // we load multiple memories + // we have to load multiple memories (each subfolder) for (const auto& dir : std::filesystem::directory_iterator(path)) { loadMemory(dir.path()); diff --git a/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp b/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp index 708ba8739..61918cb20 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp @@ -696,7 +696,10 @@ namespace armarx::armem::gui::instance try { // TODO We cannot know what the str in the pixeltype belongs to (e.g. coming from java, python, c++ it may contain different values! - //pixelType = aron::type::Image::pixelTypeFromName(imageData->getType()); + // pixelType = aron::type::Image::pixelTypeFromName(imageData->getType()); + + // For now we assume it comes from c++ where '5' means CV_32FC1 (=5) + pixelType = (imageData->getType() == "5" ? PixelType::depth32 : PixelType::rgb24); } catch (const aron::error::AronException&) { -- GitLab