From a28e0c2a9825f32d2b88f312b2329d7e023e4ce4 Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Wed, 15 Oct 2025 17:08:35 +0200 Subject: [PATCH 01/14] feat: add interface IImageSegmentationPipeline --- SolARFramework.pri | 1 + .../api/pipeline/IImageSegmentationPipeline.h | 90 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 interfaces/api/pipeline/IImageSegmentationPipeline.h diff --git a/SolARFramework.pri b/SolARFramework.pri index 020e2b08..91ba4347 100644 --- a/SolARFramework.pri +++ b/SolARFramework.pri @@ -62,6 +62,7 @@ interfaces/api/output/files/IMeshExporter.h \ interfaces/api/output/files/IPointCloudExporter.h \ interfaces/api/output/files/IMapExporter.h \ interfaces/api/pipeline/IDenseMappingPipeline.h \ +interfaces/api/pipeline/IImageSegmentationPipeline.h \ interfaces/api/pipeline/IMappingPipeline.h \ interfaces/api/pipeline/IMapUpdatePipeline.h \ interfaces/api/pipeline/IPipeline.h \ diff --git a/interfaces/api/pipeline/IImageSegmentationPipeline.h b/interfaces/api/pipeline/IImageSegmentationPipeline.h new file mode 100644 index 00000000..cbefda22 --- /dev/null +++ b/interfaces/api/pipeline/IImageSegmentationPipeline.h @@ -0,0 +1,90 @@ +/** + * @copyright Copyright (c) 2024 B-com http://www.b-com.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOLAR_IMAGESEGMENTATIONPIPELINE_H +#define SOLAR_IMAGESEGMENTATIONPIPELINE_H + +#include "api/pipeline/IPipeline.h" +#include "datastructure/Image.h" +#include +#include +#include + +namespace SolAR { +namespace api { +namespace pipeline { + +/** + * @struct MaskValueInterpretation + * @brief struct used to interpret pixel value in the segmentation mask + * if classId < 0, it means that the current pixel is unsegmented + * if instanceId < 0, it means that the current pixel belongs to a "stuff" class which is uncountable + * if instanceId >= 0, it means that the current pixel belongs to a "thing" class + * confidence is the confidence score of the pixel's segmentation (usually it has value between 0 and 1) + * if confidence < 0, it simply means that the confidence score is not available for this pixel + */ +struct MaskValueInterpretation { + int16_t classId = -1; + int16_t instanceId = -1; + float confidence = -1.f; +}; + +/** + * @class IImageSegmentationPipeline + * @brief Defines an image segmentation pipeline. + * UUID: 0a897dee-74f1-42de-a6c1-f7855e0f0bbb + * + * This class provides the interface to define an image segmentation pipeline. + */ + +class XPCF_CLIENTUUID("2215b6ef-e6fa-455c-84c6-820d95630eb5") XPCF_SERVERUUID("40aede65-0170-4384-9623-6a9d320ae56d") + XPCF_GRPC_CLIENT_RECV_SIZE("-1") XPCF_GRPC_CLIENT_SEND_SIZE("-1") + IImageSegmentationPipeline : virtual public IPipeline { +public: + /// @brief default constructor + IImageSegmentationPipeline() = default; + + /// @brief default destructor + virtual ~IImageSegmentationPipeline() = default; + + /// @brief segmentation request + /// @param[in] image pointer to image data to be segmented + /// @param[out] mask output mask (pixel value of type uint8_t) + /// @param[out] maskInterpretation mapping from mask pixel value to the MaskValueInterpretation + /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) + virtual FrameworkReturnCode segmentationRequest(SRef image, + SRef& mask, + std::map& maskInterpretation) = 0; + + /// @brief segmentation request + /// @param[in] images list of pointers to images to be segmented + /// @param[out] masks list of output masks (pixel value of type uint8_t) + /// @param[out] masksInterpretations list of mappings from mask pixel value to the MaskValueInterpretation + /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) + virtual FrameworkReturnCode segmentationRequest(const std::vector>& images, + std::vector>& masks, + std::vector>& masksInterpretations) = 0; +}; +} // namespace pipeline +} // namespace api +} // namespace SolAR + +XPCF_DEFINE_INTERFACE_TRAITS(SolAR::api::pipeline::IImageSegmentationPipeline, + "0a897dee-74f1-42de-a6c1-f7855e0f0bbb", + "IImageSegmentationPipeline", + "The interface to define an image segmentation pipeline") + +#endif // SOLAR_IMAGESEGMENTATIONPIPELINE_H \ No newline at end of file From 37e83cb4195f5dd89b1c05f68278cfd719891f44 Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Wed, 22 Oct 2025 17:32:32 +0200 Subject: [PATCH 02/14] feat(seg): add enum struct to IImageSegmentationPipeline --- .../api/pipeline/IImageSegmentationPipeline.h | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/interfaces/api/pipeline/IImageSegmentationPipeline.h b/interfaces/api/pipeline/IImageSegmentationPipeline.h index cbefda22..a8d97833 100644 --- a/interfaces/api/pipeline/IImageSegmentationPipeline.h +++ b/interfaces/api/pipeline/IImageSegmentationPipeline.h @@ -1,5 +1,5 @@ /** - * @copyright Copyright (c) 2024 B-com http://www.b-com.com/ + * @copyright Copyright (c) 2025 B-com http://www.b-com.com/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,19 +24,30 @@ #include namespace SolAR { +using namespace datastructure; namespace api { namespace pipeline { /** - * @struct MaskValueInterpretation - * @brief struct used to interpret pixel value in the segmentation mask - * if classId < 0, it means that the current pixel is unsegmented - * if instanceId < 0, it means that the current pixel belongs to a "stuff" class which is uncountable - * if instanceId >= 0, it means that the current pixel belongs to a "thing" class - * confidence is the confidence score of the pixel's segmentation (usually it has value between 0 and 1) - * if confidence < 0, it simply means that the confidence score is not available for this pixel + * @enum class ImageSegType */ -struct MaskValueInterpretation { +enum class ImageSegType { + INSTANCE, + PANOPTIC, + SEMANTIC, + UNDEFINED +}; + +/** + * @struct SegInfo + * @brief this struct SegInfo is used to interpret pixel value in the segmentation mask + * classId, the Id of the class, if classId < 0, it means that the current pixel is unsegmented (e.g. background) + * instanceId, the instance Id of the detected object, if instanceId < 0, it means that the current pixel belongs to a "stuff" class which is uncountable, otherwise it belongs to a "thing" class + * confidence, confidence score between 0 and 1, the confidence score of the segmentation, if confidence < 0, it means that the confidence score is not available + */ +struct SegInfo { + SegInfo() = default; + SegInfo(int16_t c, int16_t i, float cf) : classId(c), instanceId(i), confidence(cf) {} int16_t classId = -1; int16_t instanceId = -1; float confidence = -1.f; @@ -60,24 +71,27 @@ class XPCF_CLIENTUUID("2215b6ef-e6fa-455c-84c6-820d95630eb5") XPCF_SERVERUUID("4 /// @brief default destructor virtual ~IImageSegmentationPipeline() = default; - /// @brief segmentation request + /// @brief segmentation request for a single image /// @param[in] image pointer to image data to be segmented /// @param[out] mask output mask (pixel value of type uint8_t) - /// @param[out] maskInterpretation mapping from mask pixel value to the MaskValueInterpretation + /// @param[out] maskInfo mapping from mask pixel value to the SegInfo /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) - virtual FrameworkReturnCode segmentationRequest(SRef image, - SRef& mask, - std::map& maskInterpretation) = 0; + virtual FrameworkReturnCode segmentationRequest(SRef image, + SRef& mask, + std::map& maskInfo) = 0; - /// @brief segmentation request + /// @brief segmentation request for a list of input images /// @param[in] images list of pointers to images to be segmented + /// @param[in] temporalConsistency boolean value indicating if the images are temporally consistent (true) or not (false) /// @param[out] masks list of output masks (pixel value of type uint8_t) - /// @param[out] masksInterpretations list of mappings from mask pixel value to the MaskValueInterpretation + /// @param[out] masksInfos list of mappings from mask pixel value to the SegInfo /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) - virtual FrameworkReturnCode segmentationRequest(const std::vector>& images, - std::vector>& masks, - std::vector>& masksInterpretations) = 0; + virtual FrameworkReturnCode segmentationRequest(const std::vector>& images, + bool temporalConsistency, + std::vector>& masks, + std::vector>& masksInfos) = 0; }; + } // namespace pipeline } // namespace api } // namespace SolAR From cb8430cfa84a2c0830bb8486f1147eb5eecd616c Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Wed, 29 Oct 2025 16:07:30 +0100 Subject: [PATCH 03/14] feat: add new data structures Mask2D and Mask2DCollection --- SolARFramework.pri | 4 + .../api/pipeline/IImageSegmentationPipeline.h | 47 ++---- interfaces/datastructure/Frame.h | 14 +- interfaces/datastructure/Map.h | 15 ++ interfaces/datastructure/Mask2D.h | 148 ++++++++++++++++++ interfaces/datastructure/Mask2DCollection.h | 104 ++++++++++++ src/datastructure/Frame.cpp | 10 +- src/datastructure/KeyframeCollection.cpp | 2 +- src/datastructure/Map.cpp | 16 ++ src/datastructure/Mask2D.cpp | 100 ++++++++++++ src/datastructure/Mask2DCollection.cpp | 119 ++++++++++++++ 11 files changed, 528 insertions(+), 51 deletions(-) create mode 100644 interfaces/datastructure/Mask2D.h create mode 100644 interfaces/datastructure/Mask2DCollection.h create mode 100644 src/datastructure/Mask2D.cpp create mode 100644 src/datastructure/Mask2DCollection.cpp diff --git a/SolARFramework.pri b/SolARFramework.pri index 91ba4347..187b33e8 100644 --- a/SolARFramework.pri +++ b/SolARFramework.pri @@ -139,6 +139,8 @@ interfaces/datastructure/Image.h \ interfaces/datastructure/ImageMarker.h \ interfaces/datastructure/Keyframe.h \ interfaces/datastructure/Keypoint.h \ +interfaces/datastructure/Mask2D.h \ +interfaces/datastructure/Mask2DCollection.h \ interfaces/datastructure/MathDefinitions.h \ interfaces/datastructure/Mesh.h \ interfaces/datastructure/PointCloud.h \ @@ -201,6 +203,8 @@ src/datastructure/CovisibilityGraph.cpp \ src/datastructure/KeyframeRetrieval.cpp \ src/datastructure/KeyframeCollection.cpp \ src/datastructure/Map.cpp \ +src/datastructure/Mask2D.cpp \ +src/datastructure/Mask2DCollection.cpp \ src/datastructure/Mesh.cpp \ src/datastructure/StorageTrackable.cpp \ src/datastructure/StorageWorldAnchor.cpp \ diff --git a/interfaces/api/pipeline/IImageSegmentationPipeline.h b/interfaces/api/pipeline/IImageSegmentationPipeline.h index a8d97833..f7a22404 100644 --- a/interfaces/api/pipeline/IImageSegmentationPipeline.h +++ b/interfaces/api/pipeline/IImageSegmentationPipeline.h @@ -19,8 +19,7 @@ #include "api/pipeline/IPipeline.h" #include "datastructure/Image.h" -#include -#include +#include "datastructure/Mask2DCollection.h" #include namespace SolAR { @@ -28,31 +27,6 @@ using namespace datastructure; namespace api { namespace pipeline { -/** - * @enum class ImageSegType - */ -enum class ImageSegType { - INSTANCE, - PANOPTIC, - SEMANTIC, - UNDEFINED -}; - -/** - * @struct SegInfo - * @brief this struct SegInfo is used to interpret pixel value in the segmentation mask - * classId, the Id of the class, if classId < 0, it means that the current pixel is unsegmented (e.g. background) - * instanceId, the instance Id of the detected object, if instanceId < 0, it means that the current pixel belongs to a "stuff" class which is uncountable, otherwise it belongs to a "thing" class - * confidence, confidence score between 0 and 1, the confidence score of the segmentation, if confidence < 0, it means that the confidence score is not available - */ -struct SegInfo { - SegInfo() = default; - SegInfo(int16_t c, int16_t i, float cf) : classId(c), instanceId(i), confidence(cf) {} - int16_t classId = -1; - int16_t instanceId = -1; - float confidence = -1.f; -}; - /** * @class IImageSegmentationPipeline * @brief Defines an image segmentation pipeline. @@ -73,23 +47,20 @@ class XPCF_CLIENTUUID("2215b6ef-e6fa-455c-84c6-820d95630eb5") XPCF_SERVERUUID("4 /// @brief segmentation request for a single image /// @param[in] image pointer to image data to be segmented - /// @param[out] mask output mask (pixel value of type uint8_t) - /// @param[out] maskInfo mapping from mask pixel value to the SegInfo + /// @param[out] mask output mask2D object /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) virtual FrameworkReturnCode segmentationRequest(SRef image, - SRef& mask, - std::map& maskInfo) = 0; + SRef& mask) = 0; /// @brief segmentation request for a list of input images - /// @param[in] images list of pointers to images to be segmented - /// @param[in] temporalConsistency boolean value indicating if the images are temporally consistent (true) or not (false) - /// @param[out] masks list of output masks (pixel value of type uint8_t) - /// @param[out] masksInfos list of mappings from mask pixel value to the SegInfo + /// @param[in] images list of pointers to images to be segmented + /// @param[out] maskCollection list of output masks + /// @param[in] temporalConsistency boolean value indicating if the images are temporally consistent (true) or not (false) /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) virtual FrameworkReturnCode segmentationRequest(const std::vector>& images, - bool temporalConsistency, - std::vector>& masks, - std::vector>& masksInfos) = 0; + SRef& maskCollection, + bool temporalConsistency = false) = 0; + }; } // namespace pipeline diff --git a/interfaces/datastructure/Frame.h b/interfaces/datastructure/Frame.h index 724cdd47..9c724536 100644 --- a/interfaces/datastructure/Frame.h +++ b/interfaces/datastructure/Frame.h @@ -58,17 +58,17 @@ class SOLARFRAMEWORK_API Frame { /// @return view image const SRef& getView() const; - /// @brief get mask - /// @return mask - const SRef getMask() const; + /// @brief get mask IDs + /// @return vector of mask IDs + const std::vector& getMaskIDs() const; /// @brief set view image /// @param[in] view: view image void setView(const SRef &view); - /// @brief set mask - /// @param[in] mask semantic segmentation mask - void setMask(const SRef mask); + /// @brief set mask IDs + /// @param[in] maskIDs mask IDs + void setMaskIDs(const std::vector& maskIDs); /// @brief get camera pose /// @return camera pose @@ -193,7 +193,7 @@ class SOLARFRAMEWORK_API Frame { protected: Transform3Df m_pose; SRef m_view; - SRef m_mask; // to store 2D image segmentation result + std::vector m_maskIDs; SRef m_referenceKeyFrame ; SRef m_descriptors; SRef m_globalDescriptor; diff --git a/interfaces/datastructure/Map.h b/interfaces/datastructure/Map.h index 1a47374d..f3d7b13c 100644 --- a/interfaces/datastructure/Map.h +++ b/interfaces/datastructure/Map.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -150,6 +151,19 @@ class SOLARFRAMEWORK_API Map : public Trackable { /// void setKeyframeCollection(const SRef keyframeCollection); + /// @brief This method returns the 2D mask collection + /// @return the mask collection + SRef getConstMask2DCollection() const; + + /// @brief This method returns the 2D mask collection + /// @param[out] maskCollection the mask collection of map + /// @return the mask collection + std::unique_lock getMask2DCollection(SRef& maskCollection); + + /// @brief This method is to set the 2D mask collection + /// @param[in] maskCollection the 2D mask collection of map + void setMask2DCollection(SRef maskCollection); + /// /// @brief This method returns the camera parameters collection /// @return the camera parameters collection @@ -270,6 +284,7 @@ class SOLARFRAMEWORK_API Map : public Trackable { SRef m_coordinateSystem = org::bcom::xpcf::utils::make_shared(); SRef m_pointCloud = org::bcom::xpcf::utils::make_shared(); SRef m_keyframeCollection = org::bcom::xpcf::utils::make_shared(); + SRef m_mask2DCollection = org::bcom::xpcf::utils::make_shared(); SRef m_covisibilityGraph = org::bcom::xpcf::utils::make_shared(); SRef m_keyframeRetrieval = org::bcom::xpcf::utils::make_shared(); SRef m_cameraParametersCollection = org::bcom::xpcf::utils::make_shared(); diff --git a/interfaces/datastructure/Mask2D.h b/interfaces/datastructure/Mask2D.h new file mode 100644 index 00000000..51c6329a --- /dev/null +++ b/interfaces/datastructure/Mask2D.h @@ -0,0 +1,148 @@ +/** + * @copyright Copyright (c) 2017-2025 B-com http://www.b-com.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MASK2D_H +#define MASK2D_H + +#include "core/SerializationDefinitions.h" +#include "core/SolARFrameworkDefinitions.h" +#include "datastructure/Image.h" +#include +#include + +namespace SolAR { +namespace datastructure { + +/** + * @class Mask2D + * @brief A 2D mask. + * + * This class provides the definition of a 2D mask. + */ +class SOLARFRAMEWORK_API Mask2D { +public: + /** + * @enum class Segmentation2DType + */ + enum class Segmentation2DType { + INSTANCE, + PANOPTIC, + SEMANTIC, + UNDEFINED + }; + + /** + * @struct SegInfo + * @brief this struct SegInfo is used to interpret pixel value in the segmentation mask + * classId, the Id of the class, if classId < 0, it means that the current pixel is unsegmented (e.g. background) + * instanceId, the instance Id of the detected object, if instanceId < 0, it means that the current pixel belongs to a "stuff" class which is uncountable, otherwise it belongs to a "thing" class + * confidence, confidence score between 0 and 1, the confidence score of the segmentation, if confidence < 0, it means that the confidence score is not available + */ + struct SegInfo { + SegInfo() = default; + SegInfo(int16_t c, int16_t i, float cf) : classId(c), instanceId(i), confidence(cf) {} + int16_t classId = -1; + int16_t instanceId = -1; + float confidence = -1.f; + + friend class boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version) { + ar& classId; + ar& instanceId; + ar& confidence; + } + }; + + /// @brief default constructor + Mask2D() = default; + + /// @brief constructor with args + /// @param[in] id ID of the Mask2D + /// @param[in] type segmentation type + /// @param[in] mask mask + /// @param[in] info mask info + /// @param[in] label mapping from class ID to label + Mask2D(uint32_t id, Segmentation2DType type, SRef mask, const std::map& info, const std::map& label); + + /// @brief default destructor + ~Mask2D() = default; + + /// @brief set Id + /// @param[in] id Mask2D ID + void setId(uint32_t id); + + /// @brief set mask + /// @param[in] mask pointer to mask buffer + void setMask(SRef mask); + + /// @brief set mask info + /// @param[in] maskInfo mask info used to interpret the mask + void setMaskInfo(const std::map& maskInfo); + + /// @brief set mapping from class ID to label (string) + /// @param[in] classIdToLabel mapping from class ID to label + void setClassLabels(const std::map& classIdToLabel); + + /// @brief set segmentation type + /// @param[in] type segmentation type + void setSegmentationType(Segmentation2DType type); + + /// @brief get Id + /// @return ID of the Mask2D object + uint32_t getId() const; + + /// @brief get mask + /// @return pointer to mask + SRef getMask() const; + + /// @brief get const mask + /// @return pointer to const mask + SRef getConstMask() const; + + /// @brief get mask info + /// @return mask info + const std::map& getMaskInfo() const; + + /// @brief get labels of classes + /// @return labels of classes + const std::map& getClassLabels() const; + + /// @brief get segmentation type + /// @return segmentation type + Segmentation2DType getSegmentationType() const; + +private: + /// @brief serialize + friend class boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version); + + uint32_t m_id = 0; + SRef m_mask; + std::map m_maskInfo; + std::map m_classIdToLabel; + Segmentation2DType m_segmentationType = Segmentation2DType::UNDEFINED; +}; + +DECLARESERIALIZE(Mask2D); + +} // datastructure +} // SolAR + +BOOST_CLASS_EXPORT_KEY(SolAR::datastructure::Mask2D); + +#endif \ No newline at end of file diff --git a/interfaces/datastructure/Mask2DCollection.h b/interfaces/datastructure/Mask2DCollection.h new file mode 100644 index 00000000..2b0fee61 --- /dev/null +++ b/interfaces/datastructure/Mask2DCollection.h @@ -0,0 +1,104 @@ +/** + * @copyright Copyright (c) 2017-2025 B-com http://www.b-com.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MASK2DCOLLECTION_H +#define MASK2DCOLLECTION_H + +#include "core/SolARFrameworkDefinitions.h" +#include "datastructure/Lockable.h" +#include "datastructure/Mask2D.h" + +namespace SolAR { +namespace datastructure { + +/** +* @class Mask2DCollection +* @brief A set of 2D masks. +* This class provides a set of 2D masks. +*/ +class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { +public: + /// @brief default constructor + Mask2DCollection() = default; + + /// @brief default copy constructor + Mask2DCollection(const Mask2DCollection&) = default; + + /// @brief default operator = + Mask2DCollection& operator=(const Mask2DCollection&) = default; + + /// @brief default destructor + ~Mask2DCollection() = default; + + /// @brief This method allow to add a Mask2D to the Mask2D manager component + /// @param[in] mask the Mask2D to add to the set of persistent Mask2Ds + /// @param[in] defineMaskId if true an id will be set for the added Mask2D, if false the id of the Mask2D will be used + /// @return FrameworkReturnCode::_SUCCESS_ if the addition succeed, else FrameworkReturnCode::_ERROR. + FrameworkReturnCode addMask(SRef mask, bool defineMaskId = true); + + /// @brief This method allow to add a Mask2D to the key Mask2D manager component + /// @param[in] mask the Mask2D to add to the set of persistent Mask2Ds + /// @param[in] defineMaskId if true an id will be set for the added Mask2D, if false the id of the Mask2D will be used + /// @return FrameworkReturnCode::_SUCCESS_ if the addition succeed, else FrameworkReturnCode::_ERROR. + FrameworkReturnCode addMask(const Mask2D& mask, bool defineMaskId = true); + + /// @brief This method allows to get a Mask2D by its id + /// @param[in] id of the Mask2D to get + /// @param[out] mask Mask2D stored in the Mask2Ds manager + /// @return FrameworkReturnCode::_SUCCESS_ if succeed, else FrameworkReturnCode::_ERROR. + FrameworkReturnCode getMask(uint32_t id, SRef& mask) const; + + /// @brief This method allows to get a set of Mask2Ds by their ids + /// @param[in] ids vector of ids of the Mask2Ds to get + /// @param[out] masks vector of Mask2Ds stored in the Mask2D manager + /// @return FrameworkReturnCode::_SUCCESS_ if succeed, else FrameworkReturnCode::_ERROR. + FrameworkReturnCode getMasks(const std::vector& ids, std::vector>& masks) const; + + /// @brief This method allows to get all Mask2Ds + /// @param[out] masks set of Mask2Ds + /// @return FrameworkReturnCode::_SUCCESS_ if succeed, else FrameworkReturnCode::_ERROR. + FrameworkReturnCode getAllMasks(std::vector>& masks) const; + + /// @brief This method allow to suppress a Mask2D by its id + /// @param[in] id of the Mask2D to suppress + /// @return FrameworkReturnCode::_SUCCESS_ if the suppression succeed, else FrameworkReturnCode::_ERROR. + FrameworkReturnCode suppressMask(uint32_t id); + + /// @brief This method allows to know if a Mask2D is already stored in the component + /// @param[in] id ID of this Mask2D + /// @return true if exist, else false + bool isExistMask(uint32_t id) const; + + /// @brief This method allows to get the number of Mask2Ds stored in the point cloud + /// @return the number of Mask2Ds + size_t getNbMasks() const; + +private: + /// @brief serialize + friend class boost::serialization::access; + template + void serialize(Archive &ar, const unsigned int version); + + std::map> m_masks; + uint32_t m_currentId = 0; +}; + +DECLARESERIALIZE(Mask2DCollection); + +} // datastructure +} // SolAR + +#endif // MASK2DCOLLECTION_H diff --git a/src/datastructure/Frame.cpp b/src/datastructure/Frame.cpp index 80cd83f6..14776f56 100644 --- a/src/datastructure/Frame.cpp +++ b/src/datastructure/Frame.cpp @@ -47,9 +47,9 @@ const SRef& Frame::getView() const return m_view; } -const SRef Frame::getMask() const +const std::vector& Frame::getMaskIDs() const { - return m_mask; + return m_maskIDs; } void Frame::setView(const SRef& view) @@ -57,9 +57,9 @@ void Frame::setView(const SRef& view) m_view = view; } -void Frame::setMask(const SRef mask) +void Frame::setMaskIDs(const std::vector& maskIDs) { - m_mask = mask; + m_maskIDs = maskIDs; } const Transform3Df& Frame::getPose() const @@ -223,7 +223,7 @@ template void Frame::serialize(Archive &ar, const unsigned int /* version */) { ar & boost::serialization::make_array(m_pose.data(), 12); ar & m_view; - ar & m_mask; + ar & m_maskIDs; ar & m_descriptors; ar & m_keypoints; ar & m_keypointsUndistort; diff --git a/src/datastructure/KeyframeCollection.cpp b/src/datastructure/KeyframeCollection.cpp index 2985b8ba..e64cc121 100644 --- a/src/datastructure/KeyframeCollection.cpp +++ b/src/datastructure/KeyframeCollection.cpp @@ -93,7 +93,7 @@ FrameworkReturnCode KeyframeCollection::getAllKeyframesWithoutImages(std::vector for (const auto& [id, kf]: m_keyframes) { SRef keyframeWithoutImage = xpcf::utils::make_shared(kf); keyframeWithoutImage->setId(kf->getId()); - keyframeWithoutImage->setMask(kf->getMask()); + keyframeWithoutImage->setMaskIDs(kf->getMaskIDs()); // Remove image keyframeWithoutImage->setView(nullptr); newKeyframesMap[id] = keyframeWithoutImage; diff --git a/src/datastructure/Map.cpp b/src/datastructure/Map.cpp index 3fffa39a..16fa3dd4 100644 --- a/src/datastructure/Map.cpp +++ b/src/datastructure/Map.cpp @@ -228,6 +228,21 @@ bool Map::isMapCompatible(datastructure::DescriptorType descriptorType, return true; } +SRef Map::getConstMask2DCollection() const +{ + return m_mask2DCollection; +} + +std::unique_lock Map::getMask2DCollection(SRef& maskCollection) +{ + maskCollection = m_mask2DCollection; + return m_mask2DCollection->acquireLock(); +} + +void Map::setMask2DCollection(SRef maskCollection) +{ + m_mask2DCollection = maskCollection; +} template void Map::serialize(Archive &ar, const unsigned int /* version */) { @@ -236,6 +251,7 @@ void Map::serialize(Archive &ar, const unsigned int /* version */) { ar & m_coordinateSystem; ar & m_pointCloud; ar & m_keyframeCollection; + ar & m_mask2DCollection; ar & m_covisibilityGraph; ar & m_keyframeRetrieval; ar & m_cameraParametersCollection; diff --git a/src/datastructure/Mask2D.cpp b/src/datastructure/Mask2D.cpp new file mode 100644 index 00000000..ae59c1ae --- /dev/null +++ b/src/datastructure/Mask2D.cpp @@ -0,0 +1,100 @@ +/** + * @copyright Copyright (c) 2017-2025 B-com http://www.b-com.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "datastructure/Mask2D.h" + +BOOST_CLASS_EXPORT_IMPLEMENT(SolAR::datastructure::Mask2D); + +namespace SolAR { +namespace datastructure { + +Mask2D::Mask2D(uint32_t id, Mask2D::Segmentation2DType type, SRef mask, const std::map& info, const std::map& label) +{ + m_id = id; + m_segmentationType = type; + m_mask = mask; + m_maskInfo = info; + m_classIdToLabel = label; +} + +void Mask2D::setId(uint32_t id) +{ + m_id = id; +} + +void Mask2D::setMask(SRef mask) +{ + m_mask = mask; +} + +void Mask2D::setMaskInfo(const std::map& maskInfo) +{ + m_maskInfo = maskInfo; +} + +void Mask2D::setClassLabels(const std::map& classIdToLabel) +{ + m_classIdToLabel = classIdToLabel; +} + +uint32_t Mask2D::getId() const +{ + return m_id; +} + +SRef Mask2D::getMask() const +{ + return m_mask; +} + +SRef Mask2D::getConstMask() const +{ + return m_mask; +} + +const std::map& Mask2D::getMaskInfo() const +{ + return m_maskInfo; +} + +const std::map& Mask2D::getClassLabels() const +{ + return m_classIdToLabel; +} + +void Mask2D::setSegmentationType(Segmentation2DType type) +{ + m_segmentationType = type; +} + +Mask2D::Segmentation2DType Mask2D::getSegmentationType() const +{ + return m_segmentationType; +} + +template +void Mask2D::serialize(Archive &ar, const unsigned int /* version */) { + ar& m_id; + ar& m_mask; + ar& m_maskInfo; + ar& m_classIdToLabel; + ar& m_segmentationType; +} + +IMPLEMENTSERIALIZE(Mask2D); + +} +} diff --git a/src/datastructure/Mask2DCollection.cpp b/src/datastructure/Mask2DCollection.cpp new file mode 100644 index 00000000..4b435a19 --- /dev/null +++ b/src/datastructure/Mask2DCollection.cpp @@ -0,0 +1,119 @@ +/** + * @copyright Copyright (c) 2017-2025 B-com http://www.b-com.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "datastructure/Mask2DCollection.h" +#include "core/Log.h" +#include + +BOOST_CLASS_EXPORT_IMPLEMENT(SolAR::datastructure::Mask2DCollection); + +namespace xpcf = org::bcom::xpcf; + +namespace SolAR { +namespace datastructure { + +FrameworkReturnCode Mask2DCollection::addMask(SRef mask, bool defineMaskId) +{ + if (!mask) { + LOG_ERROR("Mask2DCollection::addMask - invalid input mask pointer."); + return FrameworkReturnCode::_ERROR_; + } + return addMask(*mask, defineMaskId); +} + +FrameworkReturnCode Mask2DCollection::addMask(const Mask2D& mask, bool defineMaskId) +{ + auto newMask = xpcf::utils::make_shared(mask); + if (defineMaskId) { + newMask->setId(m_currentId++); + } + m_masks[newMask->getId()] = newMask; + return FrameworkReturnCode::_SUCCESS; +} + +FrameworkReturnCode Mask2DCollection::getMask(uint32_t id, SRef& mask) const +{ + auto maskIt = m_masks.find(id); + if (maskIt != m_masks.end()) { + mask = maskIt->second; + return FrameworkReturnCode::_SUCCESS; + } + else { + LOG_DEBUG("Mask2DCollection::getMask - cannot find mask with id {}", id); + return FrameworkReturnCode::_ERROR_; + } +} + +FrameworkReturnCode Mask2DCollection::getMasks(const std::vector& ids, std::vector>& masks) const +{ + masks.clear(); + masks.reserve(ids.size()); + for (const auto& id : ids) { + SRef mask; + if (getMask(id, mask) != FrameworkReturnCode::_SUCCESS) { + continue; + } + masks.push_back(mask); + } + if (masks.empty()) { + LOG_ERROR("Mask2DCollection::getMasks - didn't find any masks."); + return FrameworkReturnCode::_ERROR_; + } + return FrameworkReturnCode::_SUCCESS; +} + +FrameworkReturnCode Mask2DCollection::getAllMasks(std::vector>& masks) const +{ + masks.clear(); + masks.reserve(m_masks.size()); + for (const auto& mask: m_masks) { + masks.push_back(mask.second); + } + return FrameworkReturnCode::_SUCCESS; +} + +FrameworkReturnCode Mask2DCollection::suppressMask(uint32_t id) +{ + auto maskIt = m_masks.find(id); + if (maskIt == m_masks.end()) { + LOG_ERROR("Mask2DCollection::suppressMask - cannot find mask with id {} to suppress", id); + return FrameworkReturnCode::_ERROR_; + } + m_masks.erase(maskIt); + return FrameworkReturnCode::_SUCCESS; +} + +bool Mask2DCollection::isExistMask(uint32_t id) const +{ + return m_masks.find(id) != m_masks.end(); +} + +size_t Mask2DCollection::getNbMasks() const +{ + return m_masks.size(); +} + +template +void Mask2DCollection::serialize(Archive &ar, const unsigned int /* version */) +{ + ar & m_currentId; + ar & m_masks; +} + +IMPLEMENTSERIALIZE(Mask2DCollection); + +} // end of namespace datastructure +} // end of namespace SolAR From ace01cd6c121879134f8c41c7fd4ba523a9fbebf Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Fri, 31 Oct 2025 11:00:07 +0100 Subject: [PATCH 04/14] fix(seg): update image segmentation pipeline APIs --- .../api/pipeline/IImageSegmentationPipeline.h | 40 +++++++++++++++---- interfaces/datastructure/Mask2DCollection.h | 3 ++ src/datastructure/Mask2DCollection.cpp | 6 +++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/interfaces/api/pipeline/IImageSegmentationPipeline.h b/interfaces/api/pipeline/IImageSegmentationPipeline.h index f7a22404..40ac163f 100644 --- a/interfaces/api/pipeline/IImageSegmentationPipeline.h +++ b/interfaces/api/pipeline/IImageSegmentationPipeline.h @@ -27,6 +27,26 @@ using namespace datastructure; namespace api { namespace pipeline { +/** + * @enum class ImageSegmentationStatus + */ +enum class ImageSegmentationStatus { + UNINITIALIZED = 0, // processing not initialized + INITIALIZED = 1, // processing correctly initialized, but not started + IN_PROGRESS = 2, // processing in progress + COMPLETED = 3, // processing completed + ABORTED = 4 // processing aborted before completion +}; + +/// @brief mapping status to string +const static std::map imageSegmentationStatusToString = { + {ImageSegmentationStatus::UNINITIALIZED, "UNINITIALIZED"}, + {ImageSegmentationStatus::INITIALIZED, "INITIALIZED"}, + {ImageSegmentationStatus::IN_PROGRESS, "IN_PROGRESS"}, + {ImageSegmentationStatus::COMPLETED, "COMPLETED"}, + {ImageSegmentationStatus::ABORTED, "ABORTED"} +}; + /** * @class IImageSegmentationPipeline * @brief Defines an image segmentation pipeline. @@ -47,19 +67,25 @@ class XPCF_CLIENTUUID("2215b6ef-e6fa-455c-84c6-820d95630eb5") XPCF_SERVERUUID("4 /// @brief segmentation request for a single image /// @param[in] image pointer to image data to be segmented - /// @param[out] mask output mask2D object /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) - virtual FrameworkReturnCode segmentationRequest(SRef image, - SRef& mask) = 0; + virtual FrameworkReturnCode segmentationRequest(SRef image) = 0; /// @brief segmentation request for a list of input images /// @param[in] images list of pointers to images to be segmented - /// @param[out] maskCollection list of output masks /// @param[in] temporalConsistency boolean value indicating if the images are temporally consistent (true) or not (false) /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) - virtual FrameworkReturnCode segmentationRequest(const std::vector>& images, - SRef& maskCollection, - bool temporalConsistency = false) = 0; + virtual FrameworkReturnCode segmentationRequest(const std::vector>& images, bool temporalConsistency = false) = 0; + + /// @brief get status and progress percentage + /// @param[out] status the current image segmentation processing status + /// @param[out] progress the current progress percentage (valid value should be between 0 and 1) + /// @return FrameworkReturnCode::_SUCCESS if the status and progress are available, otherwise FrameworkReturnCode::_ERROR_ + virtual FrameworkReturnCode getStatus(ImageSegmentationStatus& status, float& progress) const = 0; + + /// @brief get output mask + /// @param[out] mask output mask collection + /// @return FrameworkReturnCode::_SUCCESS (get output mask succeeded) or FrameworkReturnCode::_ERROR_ (get output mask failed) + virtual FrameworkReturnCode getOutputMask(SRef& mask) const = 0; }; diff --git a/interfaces/datastructure/Mask2DCollection.h b/interfaces/datastructure/Mask2DCollection.h index 2b0fee61..fbdb1bf6 100644 --- a/interfaces/datastructure/Mask2DCollection.h +++ b/interfaces/datastructure/Mask2DCollection.h @@ -86,6 +86,9 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { /// @return the number of Mask2Ds size_t getNbMasks() const; + /// @brief This method allows to clear the contents of current Mask2DCollection + void clear(); + private: /// @brief serialize friend class boost::serialization::access; diff --git a/src/datastructure/Mask2DCollection.cpp b/src/datastructure/Mask2DCollection.cpp index 4b435a19..4b4aa6e5 100644 --- a/src/datastructure/Mask2DCollection.cpp +++ b/src/datastructure/Mask2DCollection.cpp @@ -106,6 +106,12 @@ size_t Mask2DCollection::getNbMasks() const return m_masks.size(); } +void Mask2DCollection::clear() +{ + m_masks.clear(); + m_currentId = 0; +} + template void Mask2DCollection::serialize(Archive &ar, const unsigned int /* version */) { From f29023454abb4b60b345ca5e0021d871c9c12f54 Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Fri, 31 Oct 2025 17:07:17 +0100 Subject: [PATCH 05/14] fix(seg): use SRef instead of SRef in segmentation API --- interfaces/api/pipeline/IImageSegmentationPipeline.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfaces/api/pipeline/IImageSegmentationPipeline.h b/interfaces/api/pipeline/IImageSegmentationPipeline.h index 40ac163f..bcf4b368 100644 --- a/interfaces/api/pipeline/IImageSegmentationPipeline.h +++ b/interfaces/api/pipeline/IImageSegmentationPipeline.h @@ -68,7 +68,7 @@ class XPCF_CLIENTUUID("2215b6ef-e6fa-455c-84c6-820d95630eb5") XPCF_SERVERUUID("4 /// @brief segmentation request for a single image /// @param[in] image pointer to image data to be segmented /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) - virtual FrameworkReturnCode segmentationRequest(SRef image) = 0; + virtual FrameworkReturnCode segmentationRequest(SRef image) = 0; /// @brief segmentation request for a list of input images /// @param[in] images list of pointers to images to be segmented From 314c703aea241c763c678f15e83ea281c39fc124 Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Mon, 3 Nov 2025 16:51:57 +0100 Subject: [PATCH 06/14] feat(seg): add IMasks2DManager --- SolARFramework.pri | 1 + interfaces/api/storage/IMapManager.h | 12 +++ interfaces/api/storage/IMasks2DManager.h | 120 +++++++++++++++++++++++ interfaces/datastructure/Map.h | 6 +- src/datastructure/Map.cpp | 5 + 5 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 interfaces/api/storage/IMasks2DManager.h diff --git a/SolARFramework.pri b/SolARFramework.pri index 187b33e8..a99c480d 100644 --- a/SolARFramework.pri +++ b/SolARFramework.pri @@ -115,6 +115,7 @@ interfaces/api/storage/ICameraParametersManager.h \ interfaces/api/storage/IPointCloudManager.h \ interfaces/api/storage/IWorldGraphManager.h \ interfaces/api/storage/IMapManager.h \ +interfaces/api/storage/IMasks2DManager.h \ interfaces/api/tracking/IOpticalFlowEstimator.h \ interfaces/core/Log.h \ interfaces/core/Timer.h \ diff --git a/interfaces/api/storage/IMapManager.h b/interfaces/api/storage/IMapManager.h index 5af56396..0936fdcf 100644 --- a/interfaces/api/storage/IMapManager.h +++ b/interfaces/api/storage/IMapManager.h @@ -32,6 +32,7 @@ #include "api/storage/ICameraParametersManager.h" #include "api/storage/ICovisibilityGraphManager.h" #include "api/storage/IKeyframesManager.h" +#include "api/storage/IMasks2DManager.h" #include "api/storage/IPointCloudManager.h" #include "api/reloc/IKeyframeRetriever.h" @@ -109,6 +110,17 @@ class XPCF_IGNORE IMapManager : /// @return FrameworkReturnCode::_SUCCESS if succeed, else FrameworkReturnCode::_ERROR_ virtual FrameworkReturnCode removeKeyframe(const SRef keyframe) = 0; + /// @brief Add a 2d mask to map manager + /// @param[in] mask 2D mask to be added to map manager + /// @param[in] defineId if true an id will be set for the added mask, if false the id of the mask will be used + /// @return FrameworkReturnCode::_SUCCESS if succeed, else FrameworkReturnCode::_ERROR_ + virtual FrameworkReturnCode addMask2D(SRef mask, bool defineId = true) = 0; + + /// @brief remove mask + /// @param[in] id mask ID + /// @return FrameworkReturnCode::_SUCCESS if succeed, else FrameworkReturnCode::_ERROR_ + virtual FrameworkReturnCode removeMask2D(uint32_t id) = 0; + /// @brief Add camera parameters to map manager /// @param[in] cameraParameters the camera paramaters to add to the map manager /// @param[in] defineCameraParametersId if true an id will be set for the added CameraParameters, if false the id of the CameraParameters will be used diff --git a/interfaces/api/storage/IMasks2DManager.h b/interfaces/api/storage/IMasks2DManager.h new file mode 100644 index 00000000..e320576f --- /dev/null +++ b/interfaces/api/storage/IMasks2DManager.h @@ -0,0 +1,120 @@ +/** + * @copyright Copyright (c) 2017-2025 B-com http://www.b-com.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOLAR_I2DMASKSMANAGER_H +#define SOLAR_I2DMASKSMANAGER_H + +#include "datastructure/Mask2DCollection.h" +#include + +namespace SolAR { +using namespace datastructure; +namespace api { +namespace storage { + +/** + * @class IMasks2DManager + * @brief Allows to store a set of 2D masks. + * UUID: 56f36544-85c4-4636-bd4e-b3493a9e95c4 + * + * This storage component can be accessed by processing components to share persistent data. + */ + +class XPCF_IGNORE IMasks2DManager : virtual public org::bcom::xpcf::IComponentIntrospect { +public: + /// @brief IMasks2DManager default constructor + IMasks2DManager() = default; + + /// @brief IMasks2DManager default destructor + virtual ~IMasks2DManager() = default; + + /// @brief This method allows to add a mask to the 2D mask manager component + /// @param[in] mask the mask to add to the set of persistent masks + /// @param[in] defineMaskId if true an id will be set for the added mask, if false the id of the mask will be used + /// @return FrameworkReturnCode::_SUCCESS_ if the addition succeed, else FrameworkReturnCode::_ERROR. + virtual FrameworkReturnCode addMask(SRef mask, bool defineMaskId = true) = 0; + + /// @brief This method allows to get a mask by its id + /// @param[in] id id of the mask to get + /// @param[out] mask a mask stored in the masks manager + /// @return FrameworkReturnCode::_SUCCESS_ if succeed, else FrameworkReturnCode::_ERROR. + virtual FrameworkReturnCode getMask(uint32_t id, SRef& mask) const = 0; + + /// @brief This method allows to get a set of masks by their ids + /// @param[in] ids a vector of ids of the masks to get + /// @param[out] masks a vector of masks stored in the mask manager + /// @return FrameworkReturnCode::_SUCCESS_ if succeed, else FrameworkReturnCode::_ERROR. + virtual FrameworkReturnCode getMasks(const std::vector& ids, std::vector>& masks) const = 0; + + /// @brief This method allows to get all masks + /// @param[out] masks the set of masks + /// @return FrameworkReturnCode::_SUCCESS_ if succeed, else FrameworkReturnCode::_ERROR. + virtual FrameworkReturnCode getAllMasks(std::vector>& masks) const = 0; + + /// @brief This method allows to know if a mask is already stored in the component + /// @param[in] id id of this mask + /// @return true if exist, else false + virtual bool isExistMask(uint32_t id) const = 0; + + /// @brief This method allows to get the number of masks stored in the component + /// @return The number of masks + virtual size_t getNbMasks() const = 0; + + /// @brief This method allow to suppress a mask by its id + /// @param[in] id id of the mask to suppress + /// @return FrameworkReturnCode::_SUCCESS_ if the suppression succeed, else FrameworkReturnCode::_ERROR. + virtual FrameworkReturnCode suppressMask(uint32_t id) = 0; + + /// @brief This method is to set the mask collection + /// @param[in] maskCollection the mask collection of map + virtual void setMaskCollection(SRef maskCollection) = 0; + + /// @brief This method returns the const mask collection + /// @return the mask collection + virtual SRef getConstMaskCollection() const = 0; + + /// @brief This method returns the mask collection + /// @return the mask collection + virtual SRef getMaskCollection() const = 0; + + /// @brief This method returns the mask collection + /// @param[out] maskCollection the mask collection of map + /// @return the mask collection + virtual std::unique_lock getMaskCollection(SRef& maskCollection) = 0; + + /// @brief This method allows to save the masks to the external file + /// @param[in] file the file name + /// @return FrameworkReturnCode::_SUCCESS_ if the suppression succeed, else FrameworkReturnCode::_ERROR. + virtual FrameworkReturnCode saveToFile(const std::string& file) const = 0; + + /// @brief This method allows to load the masks from the external file + /// @param[in] file the file name + /// @return FrameworkReturnCode::_SUCCESS_ if the suppression succeed, else FrameworkReturnCode::_ERROR. + virtual FrameworkReturnCode loadFromFile(const std::string& file) = 0; +}; + +} // storage +} // api +} // SolAR + +XPCF_DEFINE_INTERFACE_TRAITS(SolAR::api::storage::IMasks2DManager, + "56f36544-85c4-4636-bd4e-b3493a9e95c4", + "IMasks2DManager", + "A component interface for storing a set of 2D masks accessible by processing components."); + + + +#endif \ No newline at end of file diff --git a/interfaces/datastructure/Map.h b/interfaces/datastructure/Map.h index f3d7b13c..7bbca1dd 100644 --- a/interfaces/datastructure/Map.h +++ b/interfaces/datastructure/Map.h @@ -151,10 +151,14 @@ class SOLARFRAMEWORK_API Map : public Trackable { /// void setKeyframeCollection(const SRef keyframeCollection); - /// @brief This method returns the 2D mask collection + /// @brief This method returns the const 2D mask collection /// @return the mask collection SRef getConstMask2DCollection() const; + /// @brief This method returns the 2D mask collection + /// @return the mask collection + SRef getMask2DCollection() const; + /// @brief This method returns the 2D mask collection /// @param[out] maskCollection the mask collection of map /// @return the mask collection diff --git a/src/datastructure/Map.cpp b/src/datastructure/Map.cpp index 16fa3dd4..fbefdf2c 100644 --- a/src/datastructure/Map.cpp +++ b/src/datastructure/Map.cpp @@ -233,6 +233,11 @@ SRef Map::getConstMask2DCollection() const return m_mask2DCollection; } +SRef Map::getMask2DCollection() const +{ + return m_mask2DCollection; +} + std::unique_lock Map::getMask2DCollection(SRef& maskCollection) { maskCollection = m_mask2DCollection; From 4cc24304553f5a50d562df387037b0fe6d842988 Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Tue, 4 Nov 2025 11:23:44 +0100 Subject: [PATCH 07/14] feat(seg): add print function in Mask2D --- interfaces/datastructure/Mask2D.h | 47 +++++++++++++++++++------------ src/datastructure/Mask2D.cpp | 26 +++++++++++++---- 2 files changed, 49 insertions(+), 24 deletions(-) diff --git a/interfaces/datastructure/Mask2D.h b/interfaces/datastructure/Mask2D.h index 51c6329a..f78d514c 100644 --- a/interfaces/datastructure/Mask2D.h +++ b/interfaces/datastructure/Mask2D.h @@ -26,6 +26,22 @@ namespace SolAR { namespace datastructure { +/** + * @enum class Segmentation2DType + */ +enum class Segmentation2DType { + INSTANCE, + PANOPTIC, + SEMANTIC, + UNDEFINED +}; +static const std::map segmentation2DTypeToStr { + {Segmentation2DType::INSTANCE, "INSTANCE"}, + {Segmentation2DType::PANOPTIC, "PANOPTIC"}, + {Segmentation2DType::SEMANTIC, "SEMANTIC"}, + {Segmentation2DType::UNDEFINED, "UNDEFINED"} +}; + /** * @class Mask2D * @brief A 2D mask. @@ -34,16 +50,6 @@ namespace datastructure { */ class SOLARFRAMEWORK_API Mask2D { public: - /** - * @enum class Segmentation2DType - */ - enum class Segmentation2DType { - INSTANCE, - PANOPTIC, - SEMANTIC, - UNDEFINED - }; - /** * @struct SegInfo * @brief this struct SegInfo is used to interpret pixel value in the segmentation mask @@ -66,6 +72,8 @@ class SOLARFRAMEWORK_API Mask2D { ar& confidence; } }; + using MaskInfoType = std::map; + using ClassLabelType = std::map; /// @brief default constructor Mask2D() = default; @@ -76,7 +84,7 @@ class SOLARFRAMEWORK_API Mask2D { /// @param[in] mask mask /// @param[in] info mask info /// @param[in] label mapping from class ID to label - Mask2D(uint32_t id, Segmentation2DType type, SRef mask, const std::map& info, const std::map& label); + Mask2D(uint32_t id, Segmentation2DType type, SRef mask, const MaskInfoType& info, const ClassLabelType& label); /// @brief default destructor ~Mask2D() = default; @@ -91,11 +99,11 @@ class SOLARFRAMEWORK_API Mask2D { /// @brief set mask info /// @param[in] maskInfo mask info used to interpret the mask - void setMaskInfo(const std::map& maskInfo); + void setMaskInfo(const MaskInfoType& maskInfo); /// @brief set mapping from class ID to label (string) /// @param[in] classIdToLabel mapping from class ID to label - void setClassLabels(const std::map& classIdToLabel); + void setClassLabels(const ClassLabelType& classIdToLabel); /// @brief set segmentation type /// @param[in] type segmentation type @@ -115,16 +123,19 @@ class SOLARFRAMEWORK_API Mask2D { /// @brief get mask info /// @return mask info - const std::map& getMaskInfo() const; + const MaskInfoType& getMaskInfo() const; /// @brief get labels of classes /// @return labels of classes - const std::map& getClassLabels() const; + const ClassLabelType& getClassLabels() const; /// @brief get segmentation type /// @return segmentation type Segmentation2DType getSegmentationType() const; + /// @brief print info + void print() const; + private: /// @brief serialize friend class boost::serialization::access; @@ -133,8 +144,8 @@ class SOLARFRAMEWORK_API Mask2D { uint32_t m_id = 0; SRef m_mask; - std::map m_maskInfo; - std::map m_classIdToLabel; + MaskInfoType m_maskInfo; + ClassLabelType m_classIdToLabel; Segmentation2DType m_segmentationType = Segmentation2DType::UNDEFINED; }; @@ -145,4 +156,4 @@ DECLARESERIALIZE(Mask2D); BOOST_CLASS_EXPORT_KEY(SolAR::datastructure::Mask2D); -#endif \ No newline at end of file +#endif diff --git a/src/datastructure/Mask2D.cpp b/src/datastructure/Mask2D.cpp index ae59c1ae..7e52476b 100644 --- a/src/datastructure/Mask2D.cpp +++ b/src/datastructure/Mask2D.cpp @@ -15,13 +15,14 @@ */ #include "datastructure/Mask2D.h" +#include "core/Log.h" BOOST_CLASS_EXPORT_IMPLEMENT(SolAR::datastructure::Mask2D); namespace SolAR { namespace datastructure { -Mask2D::Mask2D(uint32_t id, Mask2D::Segmentation2DType type, SRef mask, const std::map& info, const std::map& label) +Mask2D::Mask2D(uint32_t id, Segmentation2DType type, SRef mask, const Mask2D::MaskInfoType& info, const Mask2D::ClassLabelType& label) { m_id = id; m_segmentationType = type; @@ -40,12 +41,12 @@ void Mask2D::setMask(SRef mask) m_mask = mask; } -void Mask2D::setMaskInfo(const std::map& maskInfo) +void Mask2D::setMaskInfo(const Mask2D::MaskInfoType& maskInfo) { m_maskInfo = maskInfo; } -void Mask2D::setClassLabels(const std::map& classIdToLabel) +void Mask2D::setClassLabels(const Mask2D::ClassLabelType& classIdToLabel) { m_classIdToLabel = classIdToLabel; } @@ -65,12 +66,12 @@ SRef Mask2D::getConstMask() const return m_mask; } -const std::map& Mask2D::getMaskInfo() const +const Mask2D::MaskInfoType& Mask2D::getMaskInfo() const { return m_maskInfo; } -const std::map& Mask2D::getClassLabels() const +const Mask2D::ClassLabelType& Mask2D::getClassLabels() const { return m_classIdToLabel; } @@ -80,11 +81,24 @@ void Mask2D::setSegmentationType(Segmentation2DType type) m_segmentationType = type; } -Mask2D::Segmentation2DType Mask2D::getSegmentationType() const +Segmentation2DType Mask2D::getSegmentationType() const { return m_segmentationType; } +void Mask2D::print() const +{ + LOG_INFO("ID: {}", m_id); + LOG_INFO("SegmentationType: {}", segmentation2DTypeToStr.at(m_segmentationType)); + LOG_INFO("has valid mask image: {}", m_mask ? "true" : "false"); + for (const auto& [v, info]: m_maskInfo) { + LOG_INFO("[Mask info] pixel_value {}: class_id {}, instance_id {}, confidence {:.2f}", v, info.classId, info.instanceId, info.confidence); + } + for (const auto& [classId, label]: m_classIdToLabel) { + LOG_INFO("[Class label] class_id {}: label {}", classId, label); + } +} + template void Mask2D::serialize(Archive &ar, const unsigned int /* version */) { ar& m_id; From 156e0919b9de0ec30d362b6c64fc35988d0a399f Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Tue, 4 Nov 2025 15:06:42 +0100 Subject: [PATCH 08/14] fix(serialization): serialize Frame according to different class versions to ensure backward compatibility --- interfaces/datastructure/Frame.h | 4 ++-- src/datastructure/Frame.cpp | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/interfaces/datastructure/Frame.h b/interfaces/datastructure/Frame.h index 0ee9d2d4..5b119ee4 100644 --- a/interfaces/datastructure/Frame.h +++ b/interfaces/datastructure/Frame.h @@ -1,7 +1,6 @@ #ifndef FRAME_H #define FRAME_H - #include #include #include @@ -11,8 +10,8 @@ #include #include #include - #include + namespace SolAR { namespace datastructure { @@ -219,5 +218,6 @@ DECLARESERIALIZE(Frame); } BOOST_CLASS_EXPORT_KEY(SolAR::datastructure::Frame); +BOOST_CLASS_VERSION(SolAR::datastructure::Frame, 1); #endif // FRAME_H diff --git a/src/datastructure/Frame.cpp b/src/datastructure/Frame.cpp index 0c6344ea..183ae6a6 100644 --- a/src/datastructure/Frame.cpp +++ b/src/datastructure/Frame.cpp @@ -236,16 +236,25 @@ void Frame::nextSerializationWithoutImage() } template -void Frame::serialize(Archive &ar, const unsigned int /* version */) { +void Frame::serialize(Archive &ar, const unsigned int version) { ar & boost::serialization::make_array(m_pose.data(), 12); - ar & m_maskIDs; + if (version > 0) { + ar & m_maskIDs; + } if (m_serializeImage) { ar & m_view; + if (version == 0) { // old version should serialize mask (SRef) + SRef emptyImage; + ar & emptyImage; + } } else { // Do not serialize Image object, but only for this time SRef emptyImage; ar & emptyImage; // view + if (version == 0) { // mask (old version) + ar & emptyImage; + } m_serializeImage = true; } ar & m_descriptors; From 0c9c055a68379fb3601a63e88a6622d6a379a640 Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Wed, 5 Nov 2025 10:51:56 +0100 Subject: [PATCH 09/14] feat(seg): export Mask2D to more visualizable formats (png, json) --- interfaces/datastructure/Mask2D.h | 6 ++++ interfaces/datastructure/Mask2DCollection.h | 8 +++++ src/datastructure/Mask2D.cpp | 34 +++++++++++++++++++++ src/datastructure/Mask2DCollection.cpp | 31 +++++++++++++++++++ 4 files changed, 79 insertions(+) diff --git a/interfaces/datastructure/Mask2D.h b/interfaces/datastructure/Mask2D.h index f78d514c..78b90af9 100644 --- a/interfaces/datastructure/Mask2D.h +++ b/interfaces/datastructure/Mask2D.h @@ -136,6 +136,12 @@ class SOLARFRAMEWORK_API Mask2D { /// @brief print info void print() const; + /// @brief save to PNG and json + /// @param[in] filePng filename PNG used to save mask image + /// @param[in] fileJson filename json used to save other information of the mask + /// @return boolean true (success) or false (failure) + bool save(const std::string& filePng, const std::string& fileJson) const; + private: /// @brief serialize friend class boost::serialization::access; diff --git a/interfaces/datastructure/Mask2DCollection.h b/interfaces/datastructure/Mask2DCollection.h index fbdb1bf6..981c03da 100644 --- a/interfaces/datastructure/Mask2DCollection.h +++ b/interfaces/datastructure/Mask2DCollection.h @@ -89,6 +89,14 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { /// @brief This method allows to clear the contents of current Mask2DCollection void clear(); + /// @brief save list of Mask2Ds to folder (each mask corresponds to a PNG file and a json file) + /// @param[in] pathToFolder path to folder + /// @return FrameworkReturnCode::_SUCCESS (saved successfully) or FrameworkReturnCode::_ERROR_ (failed to save) + FrameworkReturnCode save(const std::string& pathToFolder) const; + + /// @brief print info + void print() const; + private: /// @brief serialize friend class boost::serialization::access; diff --git a/src/datastructure/Mask2D.cpp b/src/datastructure/Mask2D.cpp index 7e52476b..e0a9e7b5 100644 --- a/src/datastructure/Mask2D.cpp +++ b/src/datastructure/Mask2D.cpp @@ -16,9 +16,12 @@ #include "datastructure/Mask2D.h" #include "core/Log.h" +#include BOOST_CLASS_EXPORT_IMPLEMENT(SolAR::datastructure::Mask2D); +using json = nlohmann::json; + namespace SolAR { namespace datastructure { @@ -99,6 +102,37 @@ void Mask2D::print() const } } +bool Mask2D::save(const std::string& filePng, const std::string& fileJson) const +{ + if (m_mask->getImageEncoding() != Image::ENCODING_PNG) { + m_mask->setImageEncoding(Image::ENCODING_PNG); + } + m_mask->setImageEncodingQuality(100); + if (m_mask->save(filePng) != FrameworkReturnCode::_SUCCESS) { + LOG_ERROR("Mask2D::save - failed to save mask image to {}", filePng); + return false; + } + json j; + j["id"] = m_id; + j["segmentation_type"] = segmentation2DTypeToStr.at(m_segmentationType); + auto maskInfo = json::array(); + for (const auto& [v, info] : m_maskInfo) { + maskInfo.push_back({ {"pixel_value", v}, + {"class", info.classId}, + {"instance", info.instanceId}, + {"confidence_score", info.confidence} }); + } + j["mask_pixel_info"] = maskInfo; + auto classLabel = json::array(); + for (const auto& [classId, label] : m_classIdToLabel) { + classLabel.push_back({{"class", classId}, {"label", label}}); + } + j["class_label_info"] = classLabel; + std::ofstream o(fileJson); + o << j.dump(0) << std::endl; + return true; +} + template void Mask2D::serialize(Archive &ar, const unsigned int /* version */) { ar& m_id; diff --git a/src/datastructure/Mask2DCollection.cpp b/src/datastructure/Mask2DCollection.cpp index 4b4aa6e5..d679ccb0 100644 --- a/src/datastructure/Mask2DCollection.cpp +++ b/src/datastructure/Mask2DCollection.cpp @@ -16,6 +16,7 @@ #include "datastructure/Mask2DCollection.h" #include "core/Log.h" +#include #include BOOST_CLASS_EXPORT_IMPLEMENT(SolAR::datastructure::Mask2DCollection); @@ -112,6 +113,36 @@ void Mask2DCollection::clear() m_currentId = 0; } +FrameworkReturnCode Mask2DCollection::save(const std::string& pathToFolder) const +{ + if (m_masks.empty()) { + LOG_ERROR("Mask2DCollection::save - empty mask list."); + return FrameworkReturnCode::_ERROR_; + } + if (!std::filesystem::exists(pathToFolder)) { + std::filesystem::create_directory(pathToFolder); + } + for (const auto& [id, mask] : m_masks) { + std::string filenamePng = pathToFolder + "/" + std::to_string(id) + ".png"; + std::string filenameJson = pathToFolder + "/" + std::to_string(id) + ".json"; + if (!mask->save(filenamePng, filenameJson)) { + LOG_ERROR("Mask2DCollection::save - failed to save mask {}", id); + return FrameworkReturnCode::_ERROR_; + } + } + return FrameworkReturnCode::_SUCCESS; +} + +void Mask2DCollection::print() const +{ + LOG_INFO("Current ID: {}", m_currentId); + LOG_INFO("Number of masks: {}", m_masks.size()); + for (const auto& [id, mask] : m_masks) { + LOG_INFO("Mask ID: {}", id); + mask->print(); + } +} + template void Mask2DCollection::serialize(Archive &ar, const unsigned int /* version */) { From 95d745b5a182e54980fac771d1242d77f7f0e690 Mon Sep 17 00:00:00 2001 From: Yitian Zhou Date: Wed, 5 Nov 2025 11:44:57 +0100 Subject: [PATCH 10/14] fix(seg): serialize 2D masks in Map when version is above 0 --- .../api/pipeline/IImageSegmentationPipeline.h | 22 +++++++++---------- interfaces/api/storage/IMapManager.h | 4 ++-- interfaces/api/storage/IMasks2DManager.h | 10 ++++----- interfaces/datastructure/Map.h | 15 ++++++++----- interfaces/datastructure/Mask2DCollection.h | 2 +- src/datastructure/Frame.cpp | 6 ++--- src/datastructure/Map.cpp | 6 +++-- 7 files changed, 35 insertions(+), 30 deletions(-) diff --git a/interfaces/api/pipeline/IImageSegmentationPipeline.h b/interfaces/api/pipeline/IImageSegmentationPipeline.h index bcf4b368..6ed86460 100644 --- a/interfaces/api/pipeline/IImageSegmentationPipeline.h +++ b/interfaces/api/pipeline/IImageSegmentationPipeline.h @@ -31,15 +31,15 @@ namespace pipeline { * @enum class ImageSegmentationStatus */ enum class ImageSegmentationStatus { - UNINITIALIZED = 0, // processing not initialized + UNINITIALIZED = 0, // processing not initialized INITIALIZED = 1, // processing correctly initialized, but not started IN_PROGRESS = 2, // processing in progress COMPLETED = 3, // processing completed ABORTED = 4 // processing aborted before completion }; -/// @brief mapping status to string -const static std::map imageSegmentationStatusToString = { +/// @brief image segmentation status to string +static const std::map imageSegmentationStatusToString = { {ImageSegmentationStatus::UNINITIALIZED, "UNINITIALIZED"}, {ImageSegmentationStatus::INITIALIZED, "INITIALIZED"}, {ImageSegmentationStatus::IN_PROGRESS, "IN_PROGRESS"}, @@ -65,24 +65,24 @@ class XPCF_CLIENTUUID("2215b6ef-e6fa-455c-84c6-820d95630eb5") XPCF_SERVERUUID("4 /// @brief default destructor virtual ~IImageSegmentationPipeline() = default; - /// @brief segmentation request for a single image - /// @param[in] image pointer to image data to be segmented + /// @brief segmentation request from an input image + /// @param[in] image pointer to image /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) virtual FrameworkReturnCode segmentationRequest(SRef image) = 0; - - /// @brief segmentation request for a list of input images - /// @param[in] images list of pointers to images to be segmented - /// @param[in] temporalConsistency boolean value indicating if the images are temporally consistent (true) or not (false) + + /// @brief segmentation request from a number of input images + /// @param[in] images list of pointers to images to be segmented + /// @param[in] temporalConsistency flag indicating whether the images are temporally consistent (true) or not (false) /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) virtual FrameworkReturnCode segmentationRequest(const std::vector>& images, bool temporalConsistency = false) = 0; /// @brief get status and progress percentage /// @param[out] status the current image segmentation processing status /// @param[out] progress the current progress percentage (valid value should be between 0 and 1) - /// @return FrameworkReturnCode::_SUCCESS if the status and progress are available, otherwise FrameworkReturnCode::_ERROR_ + /// @return FrameworkReturnCode::_SUCCESS if the status and progress are successfully retrieved, otherwise FrameworkReturnCode::_ERROR_ virtual FrameworkReturnCode getStatus(ImageSegmentationStatus& status, float& progress) const = 0; - /// @brief get output mask + /// @brief get output masks /// @param[out] mask output mask collection /// @return FrameworkReturnCode::_SUCCESS (get output mask succeeded) or FrameworkReturnCode::_ERROR_ (get output mask failed) virtual FrameworkReturnCode getOutputMask(SRef& mask) const = 0; diff --git a/interfaces/api/storage/IMapManager.h b/interfaces/api/storage/IMapManager.h index 0936fdcf..f2228d5e 100644 --- a/interfaces/api/storage/IMapManager.h +++ b/interfaces/api/storage/IMapManager.h @@ -112,13 +112,13 @@ class XPCF_IGNORE IMapManager : /// @brief Add a 2d mask to map manager /// @param[in] mask 2D mask to be added to map manager - /// @param[in] defineId if true an id will be set for the added mask, if false the id of the mask will be used + /// @param[in] defineId if true an id will be set for the added mask, if false the id of the input mask will be used /// @return FrameworkReturnCode::_SUCCESS if succeed, else FrameworkReturnCode::_ERROR_ virtual FrameworkReturnCode addMask2D(SRef mask, bool defineId = true) = 0; /// @brief remove mask /// @param[in] id mask ID - /// @return FrameworkReturnCode::_SUCCESS if succeed, else FrameworkReturnCode::_ERROR_ + /// @return FrameworkReturnCode::_SUCCESS if mask removed successfully, otherwise FrameworkReturnCode::_ERROR_ virtual FrameworkReturnCode removeMask2D(uint32_t id) = 0; /// @brief Add camera parameters to map manager diff --git a/interfaces/api/storage/IMasks2DManager.h b/interfaces/api/storage/IMasks2DManager.h index e320576f..d1c33e6c 100644 --- a/interfaces/api/storage/IMasks2DManager.h +++ b/interfaces/api/storage/IMasks2DManager.h @@ -82,27 +82,27 @@ class XPCF_IGNORE IMasks2DManager : virtual public org::bcom::xpcf::IComponentIn /// @param[in] maskCollection the mask collection of map virtual void setMaskCollection(SRef maskCollection) = 0; - /// @brief This method returns the const mask collection + /// @brief This method returns a pointer to the const mask collection /// @return the mask collection virtual SRef getConstMaskCollection() const = 0; - /// @brief This method returns the mask collection + /// @brief This method returns a pointer to the mask collection /// @return the mask collection virtual SRef getMaskCollection() const = 0; /// @brief This method returns the mask collection /// @param[out] maskCollection the mask collection of map - /// @return the mask collection + /// @return std::unique_lock which can be used to lock the access to data virtual std::unique_lock getMaskCollection(SRef& maskCollection) = 0; /// @brief This method allows to save the masks to the external file /// @param[in] file the file name - /// @return FrameworkReturnCode::_SUCCESS_ if the suppression succeed, else FrameworkReturnCode::_ERROR. + /// @return FrameworkReturnCode::_SUCCESS_ if the save succeeded, else FrameworkReturnCode::_ERROR. virtual FrameworkReturnCode saveToFile(const std::string& file) const = 0; /// @brief This method allows to load the masks from the external file /// @param[in] file the file name - /// @return FrameworkReturnCode::_SUCCESS_ if the suppression succeed, else FrameworkReturnCode::_ERROR. + /// @return FrameworkReturnCode::_SUCCESS_ if the load succeeded, else FrameworkReturnCode::_ERROR. virtual FrameworkReturnCode loadFromFile(const std::string& file) = 0; }; diff --git a/interfaces/datastructure/Map.h b/interfaces/datastructure/Map.h index a8cddc34..a136cb87 100644 --- a/interfaces/datastructure/Map.h +++ b/interfaces/datastructure/Map.h @@ -151,17 +151,17 @@ class SOLARFRAMEWORK_API Map : public Trackable { /// void setKeyframeCollection(const SRef keyframeCollection); - /// @brief This method returns the const 2D mask collection - /// @return the mask collection + /// @brief This method returns a pointer to the const 2D mask collection + /// @return pointer to const mask collection SRef getConstMask2DCollection() const; - /// @brief This method returns the 2D mask collection - /// @return the mask collection + /// @brief This method returns a pointer to the 2D mask collection + /// @return pointer to the mask collection SRef getMask2DCollection() const; /// @brief This method returns the 2D mask collection /// @param[out] maskCollection the mask collection of map - /// @return the mask collection + /// @return std::unique_lock which can be used to lock the access to data std::unique_lock getMask2DCollection(SRef& maskCollection); /// @brief This method is to set the 2D mask collection @@ -293,7 +293,7 @@ class SOLARFRAMEWORK_API Map : public Trackable { SRef m_coordinateSystem = org::bcom::xpcf::utils::make_shared(); SRef m_pointCloud = org::bcom::xpcf::utils::make_shared(); SRef m_keyframeCollection = org::bcom::xpcf::utils::make_shared(); - SRef m_mask2DCollection = org::bcom::xpcf::utils::make_shared(); + SRef m_mask2DCollection = org::bcom::xpcf::utils::make_shared(); SRef m_covisibilityGraph = org::bcom::xpcf::utils::make_shared(); SRef m_keyframeRetrieval = org::bcom::xpcf::utils::make_shared(); SRef m_cameraParametersCollection = org::bcom::xpcf::utils::make_shared(); @@ -307,4 +307,7 @@ class SOLARFRAMEWORK_API Map : public Trackable { DECLARESERIALIZE(Map); } } // end of namespace SolAR + +BOOST_CLASS_VERSION(SolAR::datastructure::Map, 1); + #endif // SOLAR_MAP_H diff --git a/interfaces/datastructure/Mask2DCollection.h b/interfaces/datastructure/Mask2DCollection.h index 981c03da..dfd25702 100644 --- a/interfaces/datastructure/Mask2DCollection.h +++ b/interfaces/datastructure/Mask2DCollection.h @@ -109,7 +109,7 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { DECLARESERIALIZE(Mask2DCollection); -} // datastructure +} // datastructure } // SolAR #endif // MASK2DCOLLECTION_H diff --git a/src/datastructure/Frame.cpp b/src/datastructure/Frame.cpp index 183ae6a6..98c38b96 100644 --- a/src/datastructure/Frame.cpp +++ b/src/datastructure/Frame.cpp @@ -58,7 +58,7 @@ const SRef& Frame::getView() const return m_view; } -const std::vector& Frame::getMaskIDs() const +const std::vector& Frame::getMaskIDs() const { return m_maskIDs; } @@ -243,7 +243,7 @@ void Frame::serialize(Archive &ar, const unsigned int version) { } if (m_serializeImage) { ar & m_view; - if (version == 0) { // old version should serialize mask (SRef) + if (version == 0) { // old version should serialize mask (SRef) SRef emptyImage; ar & emptyImage; } @@ -252,7 +252,7 @@ void Frame::serialize(Archive &ar, const unsigned int version) { // Do not serialize Image object, but only for this time SRef emptyImage; ar & emptyImage; // view - if (version == 0) { // mask (old version) + if (version == 0) { // old version should serialize mask (SRef) ar & emptyImage; } m_serializeImage = true; diff --git a/src/datastructure/Map.cpp b/src/datastructure/Map.cpp index c82d0d74..f13c914b 100644 --- a/src/datastructure/Map.cpp +++ b/src/datastructure/Map.cpp @@ -257,13 +257,12 @@ void Map::setMask2DCollection(SRef maskCollection) } template -void Map::serialize(Archive &ar, const unsigned int /* version */) { +void Map::serialize(Archive &ar, const unsigned int version) { ar & m_mapSupportedTypes; ar & m_identification; ar & m_coordinateSystem; ar & m_pointCloud; ar & m_keyframeCollection; - ar & m_mask2DCollection; ar & m_covisibilityGraph; ar & m_keyframeRetrieval; ar & m_cameraParametersCollection; @@ -272,6 +271,9 @@ void Map::serialize(Archive &ar, const unsigned int /* version */) { ar & m_descriptorType; ar & m_globalDescriptorType; ar & m_embedKeyframeImages; + if (version > 0) { + ar & m_mask2DCollection; + } } IMPLEMENTSERIALIZE(Map); From db547a1eb72e1ff6c77e93c50ed4902157fa6564 Mon Sep 17 00:00:00 2001 From: "Yitian.Zhou" Date: Fri, 14 Nov 2025 15:06:41 +0100 Subject: [PATCH 11/14] refactor(mask2d): move segType and classLabels to Mask2DCollection etc --- .../api/pipeline/IImageSegmentationPipeline.h | 45 +++++----- interfaces/api/storage/IMasks2DManager.h | 2 +- interfaces/datastructure/Map.h | 2 +- interfaces/datastructure/Mask2D.h | 45 +--------- interfaces/datastructure/Mask2DCollection.h | 49 +++++++++-- src/datastructure/Frame.cpp | 17 ++-- src/datastructure/Map.cpp | 4 +- src/datastructure/Mask2D.cpp | 36 +------- src/datastructure/Mask2DCollection.cpp | 86 +++++++++++++++---- 9 files changed, 150 insertions(+), 136 deletions(-) diff --git a/interfaces/api/pipeline/IImageSegmentationPipeline.h b/interfaces/api/pipeline/IImageSegmentationPipeline.h index 6ed86460..22cdb314 100644 --- a/interfaces/api/pipeline/IImageSegmentationPipeline.h +++ b/interfaces/api/pipeline/IImageSegmentationPipeline.h @@ -27,26 +27,6 @@ using namespace datastructure; namespace api { namespace pipeline { -/** - * @enum class ImageSegmentationStatus - */ -enum class ImageSegmentationStatus { - UNINITIALIZED = 0, // processing not initialized - INITIALIZED = 1, // processing correctly initialized, but not started - IN_PROGRESS = 2, // processing in progress - COMPLETED = 3, // processing completed - ABORTED = 4 // processing aborted before completion -}; - -/// @brief image segmentation status to string -static const std::map imageSegmentationStatusToString = { - {ImageSegmentationStatus::UNINITIALIZED, "UNINITIALIZED"}, - {ImageSegmentationStatus::INITIALIZED, "INITIALIZED"}, - {ImageSegmentationStatus::IN_PROGRESS, "IN_PROGRESS"}, - {ImageSegmentationStatus::COMPLETED, "COMPLETED"}, - {ImageSegmentationStatus::ABORTED, "ABORTED"} -}; - /** * @class IImageSegmentationPipeline * @brief Defines an image segmentation pipeline. @@ -59,6 +39,25 @@ class XPCF_CLIENTUUID("2215b6ef-e6fa-455c-84c6-820d95630eb5") XPCF_SERVERUUID("4 XPCF_GRPC_CLIENT_RECV_SIZE("-1") XPCF_GRPC_CLIENT_SEND_SIZE("-1") IImageSegmentationPipeline : virtual public IPipeline { public: + + /// Status of image segmentation processing + enum class Status { + UNINITIALIZED, ///< processing not initialized + INITIALIZED, ///< processing correctly initialized, but not started + IN_PROGRESS, ///< processing in progress + COMPLETED, ///< processing completed + ABORTED ///< processing aborted before completion + }; + + /// @brief image segmentation status to string + static const std::map statusToString = { + {Status::UNINITIALIZED, "UNINITIALIZED"}, + {Status::INITIALIZED, "INITIALIZED"}, + {Status::IN_PROGRESS, "IN_PROGRESS"}, + {Status::COMPLETED, "COMPLETED"}, + {Status::ABORTED, "ABORTED"} + }; + /// @brief default constructor IImageSegmentationPipeline() = default; @@ -68,19 +67,19 @@ class XPCF_CLIENTUUID("2215b6ef-e6fa-455c-84c6-820d95630eb5") XPCF_SERVERUUID("4 /// @brief segmentation request from an input image /// @param[in] image pointer to image /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) - virtual FrameworkReturnCode segmentationRequest(SRef image) = 0; + virtual FrameworkReturnCode segment(SRef image) = 0; /// @brief segmentation request from a number of input images /// @param[in] images list of pointers to images to be segmented /// @param[in] temporalConsistency flag indicating whether the images are temporally consistent (true) or not (false) /// @return FrameworkReturnCode::_SUCCESS (segmentation succeeded) or FrameworkReturnCode::_ERROR_ (segmentation failed) - virtual FrameworkReturnCode segmentationRequest(const std::vector>& images, bool temporalConsistency = false) = 0; + virtual FrameworkReturnCode segment(const std::vector>& images, bool temporalConsistency = false) = 0; /// @brief get status and progress percentage /// @param[out] status the current image segmentation processing status /// @param[out] progress the current progress percentage (valid value should be between 0 and 1) /// @return FrameworkReturnCode::_SUCCESS if the status and progress are successfully retrieved, otherwise FrameworkReturnCode::_ERROR_ - virtual FrameworkReturnCode getStatus(ImageSegmentationStatus& status, float& progress) const = 0; + virtual FrameworkReturnCode getStatus(Status& status, float& progress) const = 0; /// @brief get output masks /// @param[out] mask output mask collection diff --git a/interfaces/api/storage/IMasks2DManager.h b/interfaces/api/storage/IMasks2DManager.h index d1c33e6c..f4926c1b 100644 --- a/interfaces/api/storage/IMasks2DManager.h +++ b/interfaces/api/storage/IMasks2DManager.h @@ -1,5 +1,5 @@ /** - * @copyright Copyright (c) 2017-2025 B-com http://www.b-com.com/ + * @copyright Copyright (c) 2025 B-com http://www.b-com.com/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/interfaces/datastructure/Map.h b/interfaces/datastructure/Map.h index a136cb87..267a7eef 100644 --- a/interfaces/datastructure/Map.h +++ b/interfaces/datastructure/Map.h @@ -153,7 +153,7 @@ class SOLARFRAMEWORK_API Map : public Trackable { /// @brief This method returns a pointer to the const 2D mask collection /// @return pointer to const mask collection - SRef getConstMask2DCollection() const; + const Mask2DCollection& getConstMask2DCollection() const; /// @brief This method returns a pointer to the 2D mask collection /// @return pointer to the mask collection diff --git a/interfaces/datastructure/Mask2D.h b/interfaces/datastructure/Mask2D.h index 78b90af9..6825e57e 100644 --- a/interfaces/datastructure/Mask2D.h +++ b/interfaces/datastructure/Mask2D.h @@ -26,22 +26,6 @@ namespace SolAR { namespace datastructure { -/** - * @enum class Segmentation2DType - */ -enum class Segmentation2DType { - INSTANCE, - PANOPTIC, - SEMANTIC, - UNDEFINED -}; -static const std::map segmentation2DTypeToStr { - {Segmentation2DType::INSTANCE, "INSTANCE"}, - {Segmentation2DType::PANOPTIC, "PANOPTIC"}, - {Segmentation2DType::SEMANTIC, "SEMANTIC"}, - {Segmentation2DType::UNDEFINED, "UNDEFINED"} -}; - /** * @class Mask2D * @brief A 2D mask. @@ -53,9 +37,9 @@ class SOLARFRAMEWORK_API Mask2D { /** * @struct SegInfo * @brief this struct SegInfo is used to interpret pixel value in the segmentation mask - * classId, the Id of the class, if classId < 0, it means that the current pixel is unsegmented (e.g. background) - * instanceId, the instance Id of the detected object, if instanceId < 0, it means that the current pixel belongs to a "stuff" class which is uncountable, otherwise it belongs to a "thing" class - * confidence, confidence score between 0 and 1, the confidence score of the segmentation, if confidence < 0, it means that the confidence score is not available + * * classId, the Id of the class, if classId < 0, it means that the current pixel is unsegmented (e.g. background) + * * instanceId, the instance Id of the detected object, if instanceId < 0, it means that the current pixel belongs to a "stuff" class which is uncountable, otherwise it belongs to a "thing" class + * * confidence, confidence score between 0 and 1, the confidence score of the segmentation, if confidence < 0, it means that the confidence score is not available */ struct SegInfo { SegInfo() = default; @@ -73,18 +57,15 @@ class SOLARFRAMEWORK_API Mask2D { } }; using MaskInfoType = std::map; - using ClassLabelType = std::map; /// @brief default constructor Mask2D() = default; /// @brief constructor with args /// @param[in] id ID of the Mask2D - /// @param[in] type segmentation type /// @param[in] mask mask /// @param[in] info mask info - /// @param[in] label mapping from class ID to label - Mask2D(uint32_t id, Segmentation2DType type, SRef mask, const MaskInfoType& info, const ClassLabelType& label); + Mask2D(uint32_t id, SRef mask, const MaskInfoType& info); /// @brief default destructor ~Mask2D() = default; @@ -101,14 +82,6 @@ class SOLARFRAMEWORK_API Mask2D { /// @param[in] maskInfo mask info used to interpret the mask void setMaskInfo(const MaskInfoType& maskInfo); - /// @brief set mapping from class ID to label (string) - /// @param[in] classIdToLabel mapping from class ID to label - void setClassLabels(const ClassLabelType& classIdToLabel); - - /// @brief set segmentation type - /// @param[in] type segmentation type - void setSegmentationType(Segmentation2DType type); - /// @brief get Id /// @return ID of the Mask2D object uint32_t getId() const; @@ -125,14 +98,6 @@ class SOLARFRAMEWORK_API Mask2D { /// @return mask info const MaskInfoType& getMaskInfo() const; - /// @brief get labels of classes - /// @return labels of classes - const ClassLabelType& getClassLabels() const; - - /// @brief get segmentation type - /// @return segmentation type - Segmentation2DType getSegmentationType() const; - /// @brief print info void print() const; @@ -151,8 +116,6 @@ class SOLARFRAMEWORK_API Mask2D { uint32_t m_id = 0; SRef m_mask; MaskInfoType m_maskInfo; - ClassLabelType m_classIdToLabel; - Segmentation2DType m_segmentationType = Segmentation2DType::UNDEFINED; }; DECLARESERIALIZE(Mask2D); diff --git a/interfaces/datastructure/Mask2DCollection.h b/interfaces/datastructure/Mask2DCollection.h index dfd25702..bc555127 100644 --- a/interfaces/datastructure/Mask2DCollection.h +++ b/interfaces/datastructure/Mask2DCollection.h @@ -24,6 +24,22 @@ namespace SolAR { namespace datastructure { +/// Type of 2D segmentation +enum class Segmentation2DType { + INSTANCE, ///< instance segmentation + PANOPTIC, ///< panoptic segmentation + SEMANTIC, ///< semantic segmentation + UNDEFINED ///< undefined +}; + +/// Segmentation2DType to string +static const std::map segmentation2DTypeToStr { + {Segmentation2DType::INSTANCE, "INSTANCE"}, + {Segmentation2DType::PANOPTIC, "PANOPTIC"}, + {Segmentation2DType::SEMANTIC, "SEMANTIC"}, + {Segmentation2DType::UNDEFINED, "UNDEFINED"} +}; + /** * @class Mask2DCollection * @brief A set of 2D masks. @@ -31,6 +47,9 @@ namespace datastructure { */ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { public: + /// @brief map from class Id to label + using ClassLabelType = std::map; + /// @brief default constructor Mask2DCollection() = default; @@ -45,15 +64,13 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { /// @brief This method allow to add a Mask2D to the Mask2D manager component /// @param[in] mask the Mask2D to add to the set of persistent Mask2Ds - /// @param[in] defineMaskId if true an id will be set for the added Mask2D, if false the id of the Mask2D will be used /// @return FrameworkReturnCode::_SUCCESS_ if the addition succeed, else FrameworkReturnCode::_ERROR. - FrameworkReturnCode addMask(SRef mask, bool defineMaskId = true); + FrameworkReturnCode addMask(SRef mask); /// @brief This method allow to add a Mask2D to the key Mask2D manager component /// @param[in] mask the Mask2D to add to the set of persistent Mask2Ds - /// @param[in] defineMaskId if true an id will be set for the added Mask2D, if false the id of the Mask2D will be used /// @return FrameworkReturnCode::_SUCCESS_ if the addition succeed, else FrameworkReturnCode::_ERROR. - FrameworkReturnCode addMask(const Mask2D& mask, bool defineMaskId = true); + FrameworkReturnCode addMask(const Mask2D& mask); /// @brief This method allows to get a Mask2D by its id /// @param[in] id of the Mask2D to get @@ -80,7 +97,7 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { /// @brief This method allows to know if a Mask2D is already stored in the component /// @param[in] id ID of this Mask2D /// @return true if exist, else false - bool isExistMask(uint32_t id) const; + bool contains(uint32_t id) const; /// @brief This method allows to get the number of Mask2Ds stored in the point cloud /// @return the number of Mask2Ds @@ -97,6 +114,25 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { /// @brief print info void print() const; + /// @brief set segmentation type + /// @param[in] type segmentation type + void setSegmentationType(Segmentation2DType type); + + /// @brief get segmentation type + /// @return segmentation type + Segmentation2DType getSegmentationType() const; + + /// @brief set class labels + /// @param[in] classIdToLabel map from class Id to label + void setClassLabels(const ClassLabelType& classIdToLabel); + + /// @brief get class labels + /// @return map from class Id to label + const ClassLabelType& getClassLabels() const; + + /// @brief get current min max mask Ids + FrameworkReturnCode getMinMaxMaskIds(uint32_t& minId, uint32_t& maxId) const; + private: /// @brief serialize friend class boost::serialization::access; @@ -104,7 +140,8 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { void serialize(Archive &ar, const unsigned int version); std::map> m_masks; - uint32_t m_currentId = 0; + ClassLabelType m_classIdToLabel; + Segmentation2DType m_segmentationType = Segmentation2DType::UNDEFINED; }; DECLARESERIALIZE(Mask2DCollection); diff --git a/src/datastructure/Frame.cpp b/src/datastructure/Frame.cpp index 98c38b96..5f56b7e8 100644 --- a/src/datastructure/Frame.cpp +++ b/src/datastructure/Frame.cpp @@ -238,24 +238,21 @@ void Frame::nextSerializationWithoutImage() template void Frame::serialize(Archive &ar, const unsigned int version) { ar & boost::serialization::make_array(m_pose.data(), 12); - if (version > 0) { - ar & m_maskIDs; - } if (m_serializeImage) { ar & m_view; - if (version == 0) { // old version should serialize mask (SRef) - SRef emptyImage; - ar & emptyImage; - } } else { // Do not serialize Image object, but only for this time SRef emptyImage; ar & emptyImage; // view - if (version == 0) { // old version should serialize mask (SRef) - ar & emptyImage; - } m_serializeImage = true; + } + if (version == 0) { // old version should serialize mask (SRef) + SRef emptyImage; + ar & emptyImage; + } + else { + ar & m_maskIDs; } ar & m_descriptors; ar & m_keypoints; diff --git a/src/datastructure/Map.cpp b/src/datastructure/Map.cpp index f13c914b..767a70f9 100644 --- a/src/datastructure/Map.cpp +++ b/src/datastructure/Map.cpp @@ -235,9 +235,9 @@ bool Map::isMapCompatible(datastructure::DescriptorType descriptorType, return true; } -SRef Map::getConstMask2DCollection() const +const Mask2DCollection& Map::getConstMask2DCollection() const { - return m_mask2DCollection; + return *m_mask2DCollection; } SRef Map::getMask2DCollection() const diff --git a/src/datastructure/Mask2D.cpp b/src/datastructure/Mask2D.cpp index e0a9e7b5..588abac2 100644 --- a/src/datastructure/Mask2D.cpp +++ b/src/datastructure/Mask2D.cpp @@ -25,13 +25,11 @@ using json = nlohmann::json; namespace SolAR { namespace datastructure { -Mask2D::Mask2D(uint32_t id, Segmentation2DType type, SRef mask, const Mask2D::MaskInfoType& info, const Mask2D::ClassLabelType& label) +Mask2D::Mask2D(uint32_t id, SRef mask, const Mask2D::MaskInfoType& info) { m_id = id; - m_segmentationType = type; m_mask = mask; m_maskInfo = info; - m_classIdToLabel = label; } void Mask2D::setId(uint32_t id) @@ -49,11 +47,6 @@ void Mask2D::setMaskInfo(const Mask2D::MaskInfoType& maskInfo) m_maskInfo = maskInfo; } -void Mask2D::setClassLabels(const Mask2D::ClassLabelType& classIdToLabel) -{ - m_classIdToLabel = classIdToLabel; -} - uint32_t Mask2D::getId() const { return m_id; @@ -74,32 +67,13 @@ const Mask2D::MaskInfoType& Mask2D::getMaskInfo() const return m_maskInfo; } -const Mask2D::ClassLabelType& Mask2D::getClassLabels() const -{ - return m_classIdToLabel; -} - -void Mask2D::setSegmentationType(Segmentation2DType type) -{ - m_segmentationType = type; -} - -Segmentation2DType Mask2D::getSegmentationType() const -{ - return m_segmentationType; -} - void Mask2D::print() const { LOG_INFO("ID: {}", m_id); - LOG_INFO("SegmentationType: {}", segmentation2DTypeToStr.at(m_segmentationType)); LOG_INFO("has valid mask image: {}", m_mask ? "true" : "false"); for (const auto& [v, info]: m_maskInfo) { LOG_INFO("[Mask info] pixel_value {}: class_id {}, instance_id {}, confidence {:.2f}", v, info.classId, info.instanceId, info.confidence); } - for (const auto& [classId, label]: m_classIdToLabel) { - LOG_INFO("[Class label] class_id {}: label {}", classId, label); - } } bool Mask2D::save(const std::string& filePng, const std::string& fileJson) const @@ -114,7 +88,6 @@ bool Mask2D::save(const std::string& filePng, const std::string& fileJson) const } json j; j["id"] = m_id; - j["segmentation_type"] = segmentation2DTypeToStr.at(m_segmentationType); auto maskInfo = json::array(); for (const auto& [v, info] : m_maskInfo) { maskInfo.push_back({ {"pixel_value", v}, @@ -123,11 +96,6 @@ bool Mask2D::save(const std::string& filePng, const std::string& fileJson) const {"confidence_score", info.confidence} }); } j["mask_pixel_info"] = maskInfo; - auto classLabel = json::array(); - for (const auto& [classId, label] : m_classIdToLabel) { - classLabel.push_back({{"class", classId}, {"label", label}}); - } - j["class_label_info"] = classLabel; std::ofstream o(fileJson); o << j.dump(0) << std::endl; return true; @@ -138,8 +106,6 @@ void Mask2D::serialize(Archive &ar, const unsigned int /* version */) { ar& m_id; ar& m_mask; ar& m_maskInfo; - ar& m_classIdToLabel; - ar& m_segmentationType; } IMPLEMENTSERIALIZE(Mask2D); diff --git a/src/datastructure/Mask2DCollection.cpp b/src/datastructure/Mask2DCollection.cpp index d679ccb0..1a76995d 100644 --- a/src/datastructure/Mask2DCollection.cpp +++ b/src/datastructure/Mask2DCollection.cpp @@ -17,32 +17,34 @@ #include "datastructure/Mask2DCollection.h" #include "core/Log.h" #include +#include #include BOOST_CLASS_EXPORT_IMPLEMENT(SolAR::datastructure::Mask2DCollection); namespace xpcf = org::bcom::xpcf; +using json = nlohmann::json; namespace SolAR { namespace datastructure { -FrameworkReturnCode Mask2DCollection::addMask(SRef mask, bool defineMaskId) +FrameworkReturnCode Mask2DCollection::addMask(SRef mask) { if (!mask) { LOG_ERROR("Mask2DCollection::addMask - invalid input mask pointer."); return FrameworkReturnCode::_ERROR_; } - return addMask(*mask, defineMaskId); + if (m_masks.find(mask->getId()) != m_masks.end()) { + LOG_ERROR("Mask2DCollection::addMask - mask with id {} already exists in mask collection.", mask->getId()); + return FrameworkReturnCode::_ERROR_; + } + m_masks[mask->getId()] = mask; + return FrameworkReturnCode::_SUCCESS; } -FrameworkReturnCode Mask2DCollection::addMask(const Mask2D& mask, bool defineMaskId) +FrameworkReturnCode Mask2DCollection::addMask(const Mask2D& mask) { - auto newMask = xpcf::utils::make_shared(mask); - if (defineMaskId) { - newMask->setId(m_currentId++); - } - m_masks[newMask->getId()] = newMask; - return FrameworkReturnCode::_SUCCESS; + return addMask(xpcf::utils::make_shared(mask)); } FrameworkReturnCode Mask2DCollection::getMask(uint32_t id, SRef& mask) const @@ -54,7 +56,7 @@ FrameworkReturnCode Mask2DCollection::getMask(uint32_t id, SRef& mask) c } else { LOG_DEBUG("Mask2DCollection::getMask - cannot find mask with id {}", id); - return FrameworkReturnCode::_ERROR_; + return FrameworkReturnCode::_NOT_FOUND; } } @@ -65,13 +67,14 @@ FrameworkReturnCode Mask2DCollection::getMasks(const std::vector& ids, for (const auto& id : ids) { SRef mask; if (getMask(id, mask) != FrameworkReturnCode::_SUCCESS) { + LOG_WARNING("Mask2DCollection::getMasks - failed to get mask with id {}", id); continue; } masks.push_back(mask); } if (masks.empty()) { LOG_ERROR("Mask2DCollection::getMasks - didn't find any masks."); - return FrameworkReturnCode::_ERROR_; + return FrameworkReturnCode::_NOT_FOUND; } return FrameworkReturnCode::_SUCCESS; } @@ -91,13 +94,13 @@ FrameworkReturnCode Mask2DCollection::suppressMask(uint32_t id) auto maskIt = m_masks.find(id); if (maskIt == m_masks.end()) { LOG_ERROR("Mask2DCollection::suppressMask - cannot find mask with id {} to suppress", id); - return FrameworkReturnCode::_ERROR_; + return FrameworkReturnCode::_NOT_FOUND; } m_masks.erase(maskIt); return FrameworkReturnCode::_SUCCESS; } -bool Mask2DCollection::isExistMask(uint32_t id) const +bool Mask2DCollection::contains(uint32_t id) const { return m_masks.find(id) != m_masks.end(); } @@ -110,7 +113,8 @@ size_t Mask2DCollection::getNbMasks() const void Mask2DCollection::clear() { m_masks.clear(); - m_currentId = 0; + m_classIdToLabel.clear(); + m_segmentationType = Segmentation2DType::UNDEFINED; } FrameworkReturnCode Mask2DCollection::save(const std::string& pathToFolder) const @@ -122,6 +126,18 @@ FrameworkReturnCode Mask2DCollection::save(const std::string& pathToFolder) cons if (!std::filesystem::exists(pathToFolder)) { std::filesystem::create_directory(pathToFolder); } + // mask collection info + std::string fileJson = pathToFolder + "/mask_collection_info.json"; + json j; + j["segmentation_type"] = segmentation2DTypeToStr.at(m_segmentationType); + auto classLabel = json::array(); + for (const auto& [classId, label] : m_classIdToLabel) { + classLabel.push_back({{"class", classId}, {"label", label}}); + } + j["class_label_info"] = classLabel; + std::ofstream o(fileJson); + o << j.dump(0) << std::endl; + // save masks for (const auto& [id, mask] : m_masks) { std::string filenamePng = pathToFolder + "/" + std::to_string(id) + ".png"; std::string filenameJson = pathToFolder + "/" + std::to_string(id) + ".json"; @@ -135,7 +151,11 @@ FrameworkReturnCode Mask2DCollection::save(const std::string& pathToFolder) cons void Mask2DCollection::print() const { - LOG_INFO("Current ID: {}", m_currentId); + LOG_INFO("Segmentation type: {}", segmentation2DTypeToStr.at(m_segmentationType)); + LOG_INFO("Number of classes: {}", m_classIdToLabel.size()); + for (const auto& [classId, label]: m_classIdToLabel) { + LOG_INFO("Class ID {}: {}", classId, label); + } LOG_INFO("Number of masks: {}", m_masks.size()); for (const auto& [id, mask] : m_masks) { LOG_INFO("Mask ID: {}", id); @@ -143,11 +163,43 @@ void Mask2DCollection::print() const } } +void Mask2DCollection::setSegmentationType(Segmentation2DType type) +{ + m_segmentationType = type; +} + +Segmentation2DType Mask2DCollection::getSegmentationType() const +{ + return m_segmentationType; +} + +void Mask2DCollection::setClassLabels(const ClassLabelType& classIdToLabel) +{ + m_classIdToLabel = classIdToLabel; +} + +const Mask2DCollection::ClassLabelType& Mask2DCollection::getClassLabels() const +{ + return m_classIdToLabel; +} + +FrameworkReturnCode Mask2DCollection::getMinMaxMaskIds(uint32_t& minId, uint32_t& maxId) const +{ + if (m_masks.empty()) { + LOG_ERROR("Mask2DCollection::getMinMaxMaskIds - empty mask list."); + return FrameworkReturnCode::_ERROR_; + } + minId = m_masks.begin()->first; + maxId = (--m_masks.end())->first; + return FrameworkReturnCode::_SUCCESS; +} + template void Mask2DCollection::serialize(Archive &ar, const unsigned int /* version */) { - ar & m_currentId; - ar & m_masks; + ar & m_segmentationType; + ar & m_classIdToLabel; + ar & m_masks; } IMPLEMENTSERIALIZE(Mask2DCollection); From 56131ef6e69693c7670e894c40d7f70661ce7105 Mon Sep 17 00:00:00 2001 From: "Yitian.Zhou" Date: Mon, 17 Nov 2025 17:27:26 +0100 Subject: [PATCH 12/14] refactor(mask2d): add func toString in Mask2D, return SRef, use rbegin etc --- interfaces/datastructure/Map.h | 2 +- interfaces/datastructure/Mask2D.h | 4 +++ src/datastructure/Map.cpp | 4 +-- src/datastructure/Mask2D.cpp | 36 +++++++++++++------------- src/datastructure/Mask2DCollection.cpp | 2 +- 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/interfaces/datastructure/Map.h b/interfaces/datastructure/Map.h index 267a7eef..a136cb87 100644 --- a/interfaces/datastructure/Map.h +++ b/interfaces/datastructure/Map.h @@ -153,7 +153,7 @@ class SOLARFRAMEWORK_API Map : public Trackable { /// @brief This method returns a pointer to the const 2D mask collection /// @return pointer to const mask collection - const Mask2DCollection& getConstMask2DCollection() const; + SRef getConstMask2DCollection() const; /// @brief This method returns a pointer to the 2D mask collection /// @return pointer to the mask collection diff --git a/interfaces/datastructure/Mask2D.h b/interfaces/datastructure/Mask2D.h index 6825e57e..0665fffd 100644 --- a/interfaces/datastructure/Mask2D.h +++ b/interfaces/datastructure/Mask2D.h @@ -108,6 +108,10 @@ class SOLARFRAMEWORK_API Mask2D { bool save(const std::string& filePng, const std::string& fileJson) const; private: + /// @brief to string + /// @return string describing the current Mask2D object + std::string toString() const; + /// @brief serialize friend class boost::serialization::access; template diff --git a/src/datastructure/Map.cpp b/src/datastructure/Map.cpp index 767a70f9..f13c914b 100644 --- a/src/datastructure/Map.cpp +++ b/src/datastructure/Map.cpp @@ -235,9 +235,9 @@ bool Map::isMapCompatible(datastructure::DescriptorType descriptorType, return true; } -const Mask2DCollection& Map::getConstMask2DCollection() const +SRef Map::getConstMask2DCollection() const { - return *m_mask2DCollection; + return m_mask2DCollection; } SRef Map::getMask2DCollection() const diff --git a/src/datastructure/Mask2D.cpp b/src/datastructure/Mask2D.cpp index 588abac2..8ef3d43c 100644 --- a/src/datastructure/Mask2D.cpp +++ b/src/datastructure/Mask2D.cpp @@ -20,8 +20,6 @@ BOOST_CLASS_EXPORT_IMPLEMENT(SolAR::datastructure::Mask2D); -using json = nlohmann::json; - namespace SolAR { namespace datastructure { @@ -67,13 +65,25 @@ const Mask2D::MaskInfoType& Mask2D::getMaskInfo() const return m_maskInfo; } -void Mask2D::print() const +std::string Mask2D::toString() const { - LOG_INFO("ID: {}", m_id); - LOG_INFO("has valid mask image: {}", m_mask ? "true" : "false"); - for (const auto& [v, info]: m_maskInfo) { - LOG_INFO("[Mask info] pixel_value {}: class_id {}, instance_id {}, confidence {:.2f}", v, info.classId, info.instanceId, info.confidence); + nlohmann::json j; + j["id"] = m_id; + j["has_valid_mask_image"] = m_mask ? true : false; + auto maskInfo = nlohmann::json::array(); + for (const auto& [v, info] : m_maskInfo) { + maskInfo.push_back({ {"pixel_value", v}, + {"class", info.classId}, + {"instance", info.instanceId}, + {"confidence_score", info.confidence} }); } + j["mask_pixel_info"] = maskInfo; + return j.dump(0); +} + +void Mask2D::print() const +{ + LOG_INFO("Mask2D::print - {}", toString()); } bool Mask2D::save(const std::string& filePng, const std::string& fileJson) const @@ -86,18 +96,8 @@ bool Mask2D::save(const std::string& filePng, const std::string& fileJson) const LOG_ERROR("Mask2D::save - failed to save mask image to {}", filePng); return false; } - json j; - j["id"] = m_id; - auto maskInfo = json::array(); - for (const auto& [v, info] : m_maskInfo) { - maskInfo.push_back({ {"pixel_value", v}, - {"class", info.classId}, - {"instance", info.instanceId}, - {"confidence_score", info.confidence} }); - } - j["mask_pixel_info"] = maskInfo; std::ofstream o(fileJson); - o << j.dump(0) << std::endl; + o << toString() << std::endl; return true; } diff --git a/src/datastructure/Mask2DCollection.cpp b/src/datastructure/Mask2DCollection.cpp index 1a76995d..07efba26 100644 --- a/src/datastructure/Mask2DCollection.cpp +++ b/src/datastructure/Mask2DCollection.cpp @@ -190,7 +190,7 @@ FrameworkReturnCode Mask2DCollection::getMinMaxMaskIds(uint32_t& minId, uint32_t return FrameworkReturnCode::_ERROR_; } minId = m_masks.begin()->first; - maxId = (--m_masks.end())->first; + maxId = m_masks.rbegin()->first; return FrameworkReturnCode::_SUCCESS; } From 1243dadf311622c49b85282b500a7e722fdfdbe0 Mon Sep 17 00:00:00 2001 From: "Yitian.Zhou" Date: Tue, 18 Nov 2025 17:38:30 +0100 Subject: [PATCH 13/14] fix(mask2d): move out id from Mask2D to Mask2DCollection --- .../api/pipeline/IImageSegmentationPipeline.h | 28 ++++++--- interfaces/api/storage/IMapManager.h | 4 +- interfaces/api/storage/IMasks2DManager.h | 10 +--- interfaces/datastructure/Mask2D.h | 17 ++---- interfaces/datastructure/Mask2DCollection.h | 55 +++++++++++++++--- src/datastructure/Mask2D.cpp | 58 ++++++++++++++----- src/datastructure/Mask2DCollection.cpp | 37 +++++++++--- 7 files changed, 149 insertions(+), 60 deletions(-) diff --git a/interfaces/api/pipeline/IImageSegmentationPipeline.h b/interfaces/api/pipeline/IImageSegmentationPipeline.h index 22cdb314..42c93edc 100644 --- a/interfaces/api/pipeline/IImageSegmentationPipeline.h +++ b/interfaces/api/pipeline/IImageSegmentationPipeline.h @@ -49,14 +49,26 @@ class XPCF_CLIENTUUID("2215b6ef-e6fa-455c-84c6-820d95630eb5") XPCF_SERVERUUID("4 ABORTED ///< processing aborted before completion }; - /// @brief image segmentation status to string - static const std::map statusToString = { - {Status::UNINITIALIZED, "UNINITIALIZED"}, - {Status::INITIALIZED, "INITIALIZED"}, - {Status::IN_PROGRESS, "IN_PROGRESS"}, - {Status::COMPLETED, "COMPLETED"}, - {Status::ABORTED, "ABORTED"} - }; + /// @brief Return the text definition (string) of a Status object + /// @param[in] status the Status type + /// @return the text definition (string) + static std::string toString(Status status) + { + switch (status) { + case Status::UNINITIALIZED: + return "UNINITIALIZED"; + case Status::INITIALIZED: + return "INITIALIZED"; + case Status::IN_PROGRESS: + return "IN_PROGRESS"; + case Status::COMPLETED: + return "COMPLETED"; + case Status::ABORTED: + return "ABORTED"; + default: + return "Unknown value"; + } + } /// @brief default constructor IImageSegmentationPipeline() = default; diff --git a/interfaces/api/storage/IMapManager.h b/interfaces/api/storage/IMapManager.h index f2228d5e..48ae82bd 100644 --- a/interfaces/api/storage/IMapManager.h +++ b/interfaces/api/storage/IMapManager.h @@ -112,9 +112,9 @@ class XPCF_IGNORE IMapManager : /// @brief Add a 2d mask to map manager /// @param[in] mask 2D mask to be added to map manager - /// @param[in] defineId if true an id will be set for the added mask, if false the id of the input mask will be used + /// @param[out] maskId id of the added mask in mask collection /// @return FrameworkReturnCode::_SUCCESS if succeed, else FrameworkReturnCode::_ERROR_ - virtual FrameworkReturnCode addMask2D(SRef mask, bool defineId = true) = 0; + virtual FrameworkReturnCode addMask2D(SRef mask, uint32_t& maskId) = 0; /// @brief remove mask /// @param[in] id mask ID diff --git a/interfaces/api/storage/IMasks2DManager.h b/interfaces/api/storage/IMasks2DManager.h index f4926c1b..a661afce 100644 --- a/interfaces/api/storage/IMasks2DManager.h +++ b/interfaces/api/storage/IMasks2DManager.h @@ -43,9 +43,9 @@ class XPCF_IGNORE IMasks2DManager : virtual public org::bcom::xpcf::IComponentIn /// @brief This method allows to add a mask to the 2D mask manager component /// @param[in] mask the mask to add to the set of persistent masks - /// @param[in] defineMaskId if true an id will be set for the added mask, if false the id of the mask will be used + /// @param[out] maskId id of the added mask in mask collection /// @return FrameworkReturnCode::_SUCCESS_ if the addition succeed, else FrameworkReturnCode::_ERROR. - virtual FrameworkReturnCode addMask(SRef mask, bool defineMaskId = true) = 0; + virtual FrameworkReturnCode addMask(SRef mask, uint32_t& maskId) = 0; /// @brief This method allows to get a mask by its id /// @param[in] id id of the mask to get @@ -67,7 +67,7 @@ class XPCF_IGNORE IMasks2DManager : virtual public org::bcom::xpcf::IComponentIn /// @brief This method allows to know if a mask is already stored in the component /// @param[in] id id of this mask /// @return true if exist, else false - virtual bool isExistMask(uint32_t id) const = 0; + virtual bool hasMask(uint32_t id) const = 0; /// @brief This method allows to get the number of masks stored in the component /// @return The number of masks @@ -86,10 +86,6 @@ class XPCF_IGNORE IMasks2DManager : virtual public org::bcom::xpcf::IComponentIn /// @return the mask collection virtual SRef getConstMaskCollection() const = 0; - /// @brief This method returns a pointer to the mask collection - /// @return the mask collection - virtual SRef getMaskCollection() const = 0; - /// @brief This method returns the mask collection /// @param[out] maskCollection the mask collection of map /// @return std::unique_lock which can be used to lock the access to data diff --git a/interfaces/datastructure/Mask2D.h b/interfaces/datastructure/Mask2D.h index 0665fffd..bb10c8c0 100644 --- a/interfaces/datastructure/Mask2D.h +++ b/interfaces/datastructure/Mask2D.h @@ -62,18 +62,13 @@ class SOLARFRAMEWORK_API Mask2D { Mask2D() = default; /// @brief constructor with args - /// @param[in] id ID of the Mask2D /// @param[in] mask mask /// @param[in] info mask info - Mask2D(uint32_t id, SRef mask, const MaskInfoType& info); + Mask2D(SRef mask, const MaskInfoType& info); /// @brief default destructor ~Mask2D() = default; - /// @brief set Id - /// @param[in] id Mask2D ID - void setId(uint32_t id); - /// @brief set mask /// @param[in] mask pointer to mask buffer void setMask(SRef mask); @@ -82,10 +77,6 @@ class SOLARFRAMEWORK_API Mask2D { /// @param[in] maskInfo mask info used to interpret the mask void setMaskInfo(const MaskInfoType& maskInfo); - /// @brief get Id - /// @return ID of the Mask2D object - uint32_t getId() const; - /// @brief get mask /// @return pointer to mask SRef getMask() const; @@ -107,6 +98,11 @@ class SOLARFRAMEWORK_API Mask2D { /// @return boolean true (success) or false (failure) bool save(const std::string& filePng, const std::string& fileJson) const; + /// @brief check if mask is the same as the input + /// @param[in] inputMask input mask + /// @return true (the content of current mask object is the same as that of inputMask) or false (otherwise) + bool equals(SRef inputMask) const; + private: /// @brief to string /// @return string describing the current Mask2D object @@ -117,7 +113,6 @@ class SOLARFRAMEWORK_API Mask2D { template void serialize(Archive &ar, const unsigned int version); - uint32_t m_id = 0; SRef m_mask; MaskInfoType m_maskInfo; }; diff --git a/interfaces/datastructure/Mask2DCollection.h b/interfaces/datastructure/Mask2DCollection.h index bc555127..83171778 100644 --- a/interfaces/datastructure/Mask2DCollection.h +++ b/interfaces/datastructure/Mask2DCollection.h @@ -17,9 +17,11 @@ #ifndef MASK2DCOLLECTION_H #define MASK2DCOLLECTION_H +#include "core/Log.h" #include "core/SolARFrameworkDefinitions.h" #include "datastructure/Lockable.h" #include "datastructure/Mask2D.h" +#include namespace SolAR { namespace datastructure { @@ -32,13 +34,39 @@ enum class Segmentation2DType { UNDEFINED ///< undefined }; -/// Segmentation2DType to string -static const std::map segmentation2DTypeToStr { - {Segmentation2DType::INSTANCE, "INSTANCE"}, - {Segmentation2DType::PANOPTIC, "PANOPTIC"}, - {Segmentation2DType::SEMANTIC, "SEMANTIC"}, - {Segmentation2DType::UNDEFINED, "UNDEFINED"} -}; +/// @brief Return the text definition (string) of a Segmentation2DType object +/// @param[in] Segmentation2DType the 2D segmentation type +/// @return the text definition (string) +static std::string toString(Segmentation2DType segmentation2DType) +{ + switch (segmentation2DType) { + case Segmentation2DType::INSTANCE: + return "INSTANCE"; + case Segmentation2DType::PANOPTIC: + return "PANOPTIC"; + case Segmentation2DType::SEMANTIC: + return "SEMANTIC"; + case Segmentation2DType::UNDEFINED: + return "UNDEFINED"; + default: + return "Unknown value"; + } +} + +/// @brief Return the Segmentation2DType object from a text definition (string) +/// @param[in] textDefinition the text definition (string) +/// @return the 2D segmentation type +static std::optional parseSegmentation2DType(const std::string textDefinition) +{ + if (textDefinition == "INSTANCE") return Segmentation2DType::INSTANCE; + if (textDefinition == "PANOPTIC") return Segmentation2DType::PANOPTIC; + if (textDefinition == "SEMANTIC") return Segmentation2DType::SEMANTIC; + if (textDefinition == "UNDEFINED") return Segmentation2DType::UNDEFINED; + + LOG_ERROR("Unknown 2D segmentation type: {}", textDefinition); + + return {}; +} /** * @class Mask2DCollection @@ -64,13 +92,15 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { /// @brief This method allow to add a Mask2D to the Mask2D manager component /// @param[in] mask the Mask2D to add to the set of persistent Mask2Ds + /// @param[out] maskId the Id of the added mask in the mask collection /// @return FrameworkReturnCode::_SUCCESS_ if the addition succeed, else FrameworkReturnCode::_ERROR. - FrameworkReturnCode addMask(SRef mask); + FrameworkReturnCode addMask(SRef mask, uint32_t& maskId); /// @brief This method allow to add a Mask2D to the key Mask2D manager component /// @param[in] mask the Mask2D to add to the set of persistent Mask2Ds + /// @param[out] maskId the Id of the added mask in the mask collection /// @return FrameworkReturnCode::_SUCCESS_ if the addition succeed, else FrameworkReturnCode::_ERROR. - FrameworkReturnCode addMask(const Mask2D& mask); + FrameworkReturnCode addMask(const Mask2D& mask, uint32_t& maskId); /// @brief This method allows to get a Mask2D by its id /// @param[in] id of the Mask2D to get @@ -89,6 +119,12 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { /// @return FrameworkReturnCode::_SUCCESS_ if succeed, else FrameworkReturnCode::_ERROR. FrameworkReturnCode getAllMasks(std::vector>& masks) const; + /// @brief This method allows to get all Mask2Ds as well as their IDs + /// @param[out] masks set of Mask2Ds + /// @param[out] maskIds set of mask Ids + /// @return FrameworkReturnCode::_SUCCESS_ if succeed, else FrameworkReturnCode::_ERROR. + FrameworkReturnCode getAllMasks(std::vector>& masks, std::vector& maskIds) const; + /// @brief This method allow to suppress a Mask2D by its id /// @param[in] id of the Mask2D to suppress /// @return FrameworkReturnCode::_SUCCESS_ if the suppression succeed, else FrameworkReturnCode::_ERROR. @@ -139,6 +175,7 @@ class SOLARFRAMEWORK_API Mask2DCollection : public Lockable { template void serialize(Archive &ar, const unsigned int version); + uint32_t m_currentId = 0; std::map> m_masks; ClassLabelType m_classIdToLabel; Segmentation2DType m_segmentationType = Segmentation2DType::UNDEFINED; diff --git a/src/datastructure/Mask2D.cpp b/src/datastructure/Mask2D.cpp index 8ef3d43c..97159d87 100644 --- a/src/datastructure/Mask2D.cpp +++ b/src/datastructure/Mask2D.cpp @@ -23,18 +23,12 @@ BOOST_CLASS_EXPORT_IMPLEMENT(SolAR::datastructure::Mask2D); namespace SolAR { namespace datastructure { -Mask2D::Mask2D(uint32_t id, SRef mask, const Mask2D::MaskInfoType& info) +Mask2D::Mask2D(SRef mask, const Mask2D::MaskInfoType& info) { - m_id = id; m_mask = mask; m_maskInfo = info; } -void Mask2D::setId(uint32_t id) -{ - m_id = id; -} - void Mask2D::setMask(SRef mask) { m_mask = mask; @@ -45,11 +39,6 @@ void Mask2D::setMaskInfo(const Mask2D::MaskInfoType& maskInfo) m_maskInfo = maskInfo; } -uint32_t Mask2D::getId() const -{ - return m_id; -} - SRef Mask2D::getMask() const { return m_mask; @@ -68,7 +57,6 @@ const Mask2D::MaskInfoType& Mask2D::getMaskInfo() const std::string Mask2D::toString() const { nlohmann::json j; - j["id"] = m_id; j["has_valid_mask_image"] = m_mask ? true : false; auto maskInfo = nlohmann::json::array(); for (const auto& [v, info] : m_maskInfo) { @@ -101,9 +89,51 @@ bool Mask2D::save(const std::string& filePng, const std::string& fileJson) const return true; } +bool Mask2D::equals(SRef inputMask) const +{ + if (!inputMask) { + return false; + } + const auto& inputMaskInfo = inputMask->getMaskInfo(); + if (m_maskInfo.size() != inputMaskInfo.size()) { + return false; + } + auto it = m_maskInfo.begin(); + auto itInput = inputMaskInfo.begin(); + while (it != m_maskInfo.end()) { + if (it->first != itInput->first) { + return false; + } + auto segInfo = it->second; + auto segInfoInput = itInput->second; + if (segInfo.classId != segInfoInput.classId || segInfo.instanceId != segInfoInput.instanceId || segInfo.confidence != segInfoInput.confidence) { + return false; + } + ++it; + ++itInput; + } + if (!m_mask && !inputMask->getMask()) { + return true; + } + if (m_mask && inputMask->getMask()) { + uint32_t bufSize = m_mask->getBufferSize(); + if (inputMask->getMask()->getBufferSize() != bufSize) { + return false; + } + const uint8_t* buffer = static_cast(m_mask->data()); + const uint8_t* bufferInput = static_cast(inputMask->getMask()->data()); + for (uint32_t i = 0; i < bufSize; ++i) { + if (buffer[i] != bufferInput[i]) { + return false; + } + } + return true; + } + return false; +} + template void Mask2D::serialize(Archive &ar, const unsigned int /* version */) { - ar& m_id; ar& m_mask; ar& m_maskInfo; } diff --git a/src/datastructure/Mask2DCollection.cpp b/src/datastructure/Mask2DCollection.cpp index 07efba26..1a2aa29f 100644 --- a/src/datastructure/Mask2DCollection.cpp +++ b/src/datastructure/Mask2DCollection.cpp @@ -28,23 +28,27 @@ using json = nlohmann::json; namespace SolAR { namespace datastructure { -FrameworkReturnCode Mask2DCollection::addMask(SRef mask) +FrameworkReturnCode Mask2DCollection::addMask(SRef mask, uint32_t& maskId) { if (!mask) { LOG_ERROR("Mask2DCollection::addMask - invalid input mask pointer."); return FrameworkReturnCode::_ERROR_; } - if (m_masks.find(mask->getId()) != m_masks.end()) { - LOG_ERROR("Mask2DCollection::addMask - mask with id {} already exists in mask collection.", mask->getId()); - return FrameworkReturnCode::_ERROR_; + for (auto& [existingId, existingMask] : m_masks) { + if (mask->equals(existingMask)) { + LOG_WARNING("Mask2DCollection::addMask - may need some further checks, the same mask exists in mask collection (id {}).", existingId); + break; + } } - m_masks[mask->getId()] = mask; + m_masks[m_currentId] = mask; + maskId = m_currentId; + m_currentId++; return FrameworkReturnCode::_SUCCESS; } -FrameworkReturnCode Mask2DCollection::addMask(const Mask2D& mask) +FrameworkReturnCode Mask2DCollection::addMask(const Mask2D& mask, uint32_t& maskId) { - return addMask(xpcf::utils::make_shared(mask)); + return addMask(xpcf::utils::make_shared(mask), maskId); } FrameworkReturnCode Mask2DCollection::getMask(uint32_t id, SRef& mask) const @@ -89,6 +93,19 @@ FrameworkReturnCode Mask2DCollection::getAllMasks(std::vector>& mas return FrameworkReturnCode::_SUCCESS; } +FrameworkReturnCode Mask2DCollection::getAllMasks(std::vector>& masks, std::vector& maskIds) const +{ + masks.clear(); + masks.reserve(m_masks.size()); + maskIds.clear(); + maskIds.reserve(m_masks.size()); + for (const auto& [id, mask] : m_masks) { + masks.push_back(mask); + maskIds.push_back(id); + } + return FrameworkReturnCode::_SUCCESS; +} + FrameworkReturnCode Mask2DCollection::suppressMask(uint32_t id) { auto maskIt = m_masks.find(id); @@ -115,6 +132,7 @@ void Mask2DCollection::clear() m_masks.clear(); m_classIdToLabel.clear(); m_segmentationType = Segmentation2DType::UNDEFINED; + m_currentId = 0; } FrameworkReturnCode Mask2DCollection::save(const std::string& pathToFolder) const @@ -129,7 +147,7 @@ FrameworkReturnCode Mask2DCollection::save(const std::string& pathToFolder) cons // mask collection info std::string fileJson = pathToFolder + "/mask_collection_info.json"; json j; - j["segmentation_type"] = segmentation2DTypeToStr.at(m_segmentationType); + j["segmentation_type"] = toString(m_segmentationType); auto classLabel = json::array(); for (const auto& [classId, label] : m_classIdToLabel) { classLabel.push_back({{"class", classId}, {"label", label}}); @@ -151,7 +169,7 @@ FrameworkReturnCode Mask2DCollection::save(const std::string& pathToFolder) cons void Mask2DCollection::print() const { - LOG_INFO("Segmentation type: {}", segmentation2DTypeToStr.at(m_segmentationType)); + LOG_INFO("Segmentation type: {}", toString(m_segmentationType)); LOG_INFO("Number of classes: {}", m_classIdToLabel.size()); for (const auto& [classId, label]: m_classIdToLabel) { LOG_INFO("Class ID {}: {}", classId, label); @@ -199,6 +217,7 @@ void Mask2DCollection::serialize(Archive &ar, const unsigned int /* version */) { ar & m_segmentationType; ar & m_classIdToLabel; + ar & m_currentId; ar & m_masks; } From 9d1cb10789f8a8252f8f69d5646e40c9e6aaebb6 Mon Sep 17 00:00:00 2001 From: "Yitian.Zhou" Date: Wed, 19 Nov 2025 09:16:03 +0100 Subject: [PATCH 14/14] refactor(mask2d): simplify code checking if two masks are equivalent --- src/datastructure/Mask2D.cpp | 29 ++++++++++++++------------ src/datastructure/Mask2DCollection.cpp | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/datastructure/Mask2D.cpp b/src/datastructure/Mask2D.cpp index 97159d87..cc69cba3 100644 --- a/src/datastructure/Mask2D.cpp +++ b/src/datastructure/Mask2D.cpp @@ -94,6 +94,7 @@ bool Mask2D::equals(SRef inputMask) const if (!inputMask) { return false; } + // first, compare the two mask infos const auto& inputMaskInfo = inputMask->getMaskInfo(); if (m_maskInfo.size() != inputMaskInfo.size()) { return false; @@ -112,24 +113,26 @@ bool Mask2D::equals(SRef inputMask) const ++it; ++itInput; } + // second, compare the two masks + if ( (!m_mask && inputMask->getMask()) || + (m_mask && !inputMask->getMask()) ) { + return false; + } if (!m_mask && !inputMask->getMask()) { - return true; + return true; // the same both are invalid + } + uint32_t bufSize = m_mask->getBufferSize(); + if (inputMask->getMask()->getBufferSize() != bufSize) { + return false; } - if (m_mask && inputMask->getMask()) { - uint32_t bufSize = m_mask->getBufferSize(); - if (inputMask->getMask()->getBufferSize() != bufSize) { + const uint8_t* buffer = static_cast(m_mask->data()); + const uint8_t* bufferInput = static_cast(inputMask->getMask()->data()); + for (uint32_t i = 0; i < bufSize; ++i) { + if (buffer[i] != bufferInput[i]) { return false; } - const uint8_t* buffer = static_cast(m_mask->data()); - const uint8_t* bufferInput = static_cast(inputMask->getMask()->data()); - for (uint32_t i = 0; i < bufSize; ++i) { - if (buffer[i] != bufferInput[i]) { - return false; - } - } - return true; } - return false; + return true; } template diff --git a/src/datastructure/Mask2DCollection.cpp b/src/datastructure/Mask2DCollection.cpp index 1a2aa29f..57ae9bb7 100644 --- a/src/datastructure/Mask2DCollection.cpp +++ b/src/datastructure/Mask2DCollection.cpp @@ -36,7 +36,7 @@ FrameworkReturnCode Mask2DCollection::addMask(SRef mask, uint32_t& maskI } for (auto& [existingId, existingMask] : m_masks) { if (mask->equals(existingMask)) { - LOG_WARNING("Mask2DCollection::addMask - may need some further checks, the same mask exists in mask collection (id {}).", existingId); + LOG_WARNING("Mask2DCollection::addMask - mask to add is the same as an existing mask (id {}).", existingId); break; } }