diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp index 0be8ac4ead84c01f2fdb095d20e6fc79ee7f636f..e9a9fc4ebc70dbcf0a0761ccd75f7cc21d4609d1 100644 --- a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp +++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp @@ -1,5 +1,6 @@ #include <VirtualRobot/XML/ObjectIO.h> +#include <SimoxUtility/algorithm/string.h> #include <SimoxUtility/filesystem/list_directory.h> #include <ArmarXCore/core/system/ArmarXDataPath.h> @@ -229,6 +230,43 @@ namespace armarx return loadObstacle(findObject(obj)); } + + static std::vector<std::string> _loadNames( + const ObjectFinder& finder, + const ObjectID& objectID, + const bool includeClassName, + const std::function<std::optional<std::vector<std::string>>(const ObjectInfo&)> loadNamesFn) + { + std::vector<std::string> names; + if (includeClassName) + { + names.push_back(simox::alg::to_lower(objectID.className())); + } + if (std::optional<ObjectInfo> info = finder.findObject(objectID)) + { + if (std::optional<std::vector<std::string>> loadedNames = loadNamesFn(*info)) + { + // Source: https://stackoverflow.com/a/201729 + names.insert(names.end(), loadedNames->begin(), loadedNames->end()); + } + } + return names; + } + std::vector<std::string> ObjectFinder::loadRecognizedNames(const ObjectID& objectID, bool includeClassName) const + { + return _loadNames(*this, objectID, includeClassName, [](const ObjectInfo& info) + { + return info.loadRecognizedNames(); + }); + } + std::vector<std::string> ObjectFinder::loadSpokenNames(const ObjectID& objectID, bool includeClassName) const + { + return _loadNames(*this, objectID, includeClassName, [](const ObjectInfo& info) + { + return info.loadSpokenNames(); + }); + } + ObjectFinder::path ObjectFinder::_rootDirAbs() const { return packageDataDir / packageName; diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.h b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.h index bb503090e966c43b7fb60bb47a90b0fee1cdaf97..f6e9831593f4a8714eb4f3c461016bce063cadaa 100644 --- a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.h +++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.h @@ -53,6 +53,32 @@ namespace armarx static loadObstacle(const std::optional<ObjectInfo>& ts); VirtualRobot::ObstaclePtr loadObstacle(const objpose::ObjectPose& obj) const; + + + /** + * @brief Load names to use when matched when recognizing an object by name. + * + * If the object's names JSON file does not exist, no names will be added from a file. + * If you would like to detect this case, first `findObject()`, then use + * `ObjectInfo::loadRecognizedNames()`, which returns a `std::optional`. + * + * @param includeClassName If true, include the raw class name in the result. + * @see `ObjectInfo::loadRecognizedNames()` + */ + std::vector<std::string> loadRecognizedNames(const ObjectID& objectID, bool includeClassName = false) const; + /** + * @brief Load names to use when verbalizing an object name. + * + * If the object's names JSON file does not exist, no names will be added from a file. + * If you would like to detect this case, first `findObject()`, then use + * `ObjectInfo::loadSpokenNames()`, which returns a `std::optional`. + * + * @param includeClassName If true, include the raw class name in the result. + * @see `ObjectInfo::loadSpokenNames()` + */ + std::vector<std::string> loadSpokenNames(const ObjectID& objectID, bool includeClassName = false) const; + + private: void init() const; diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp index 1dc1903ea050d8c2f9fba6596d04fc12d197f5ba..8c70a93aac07dae51aa5b82014038e009f2a8cd1 100644 --- a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp +++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp @@ -1,5 +1,6 @@ #include "ObjectInfo.h" +#include <SimoxUtility/algorithm/string.h> #include <SimoxUtility/json.h> #include <SimoxUtility/shapes/AxisAlignedBoundingBox.h> #include <SimoxUtility/shapes/OrientedBox.h> @@ -85,6 +86,11 @@ namespace armarx return file(".json", "_bb"); } + PackageFileLocation ObjectInfo::namesJson() const + { + return file(".json", "_names"); + } + std::optional<simox::AxisAlignedBoundingBox> ObjectInfo::loadAABB() const { nlohmann::json j; @@ -147,6 +153,47 @@ namespace armarx return oobb; } + std::optional<std::vector<std::string>> ObjectInfo::loadRecognizedNames() const + { + return loadNames("recognized_name"); + } + + std::optional<std::vector<std::string>> ObjectInfo::loadSpokenNames() const + { + return loadNames("spoken_name"); + } + + std::optional<std::vector<std::string> > ObjectInfo::loadNames(const std::string& jsonKey) const + { + const PackageFileLocation file = namesJson(); + if (fs::is_regular_file(file.absolutePath)) + { + nlohmann::json json; + + try + { + json = nlohmann::read_json(file.absolutePath); + } + catch (const nlohmann::json::exception& e) + { + ARMARX_WARNING << "Failed to parse JSON file " << file.absolutePath << ": \n" << e.what(); + return std::nullopt; + } + catch (const std::exception& e) + { + ARMARX_WARNING << "Failed to read file " << file.absolutePath << ": \n" << e.what(); + return std::nullopt; + } + + return json.at(jsonKey).get<std::vector<std::string>>(); + } + else + { + return std::nullopt; + } + } + + bool ObjectInfo::checkPaths() const { namespace fs = std::filesystem; diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h index 5f50ec95691bb881b00be5e64703f39c9e9e7b02..9888a4c7a2d448e9d6c5f81bfd3d849039776428 100644 --- a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h +++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h @@ -64,6 +64,10 @@ namespace armarx PackageFileLocation boundingBoxJson() const; + /// File containing recognized and spoken names of objects. + PackageFileLocation namesJson() const; + + /** * @brief Load the AABB (axis-aligned bounding-box) from the bounding box JSON file. * @return Return the AABB if successful, `std::nullopt` if file does not exist. @@ -76,6 +80,17 @@ namespace armarx */ std::optional<simox::OrientedBox<float>> loadOOBB() const; + /** + * @brief Load names to use when matched when recognizing an object by name. + * @see `namesJson()` + */ + std::optional<std::vector<std::string>> loadRecognizedNames() const; + /** + * @brief Load names to use when verbalizing an object name. + * @see `namesJson()` + */ + std::optional<std::vector<std::string>> loadSpokenNames() const; + /** * @brief Checks the existence of expected files. @@ -88,6 +103,7 @@ namespace armarx private: path objectDirectory() const; + std::optional<std::vector<std::string>> loadNames(const std::string& jsonKey) const; private: