diff --git a/SolARFramework.pri b/SolARFramework.pri index 1090fbe6..b3cdea0b 100644 --- a/SolARFramework.pri +++ b/SolARFramework.pri @@ -5,6 +5,7 @@ interfaces/datastructure/StorageCapabilities.h \ interfaces/datastructure/DetectedObject.h \ interfaces/api/solver/pose/ITrackablePose.h \ interfaces/api/input/devices/IDepthCamera.h \ +interfaces/api/display/I3DMeshViewer.h \ interfaces/api/display/I3DOverlay.h \ interfaces/api/display/I3DPointsViewer.h \ interfaces/api/display/IImageViewer.h \ @@ -83,6 +84,7 @@ interfaces/api/service/IServiceManager.h \ interfaces/api/service/IAuthorizationChecker.h \ interfaces/api/service/IClientContextManager.h \ interfaces/api/map/I3DGaussianSplatting.h \ +interfaces/api/map/I3DGaussianSplattingDensifier.h \ interfaces/api/map/IMeshing.h \ interfaces/api/map/IMultiViewStereo.h \ interfaces/api/map/IProcessMap.h \ diff --git a/interfaces/api/display/I2DOverlay.h b/interfaces/api/display/I2DOverlay.h index 1c76f2dd..fd7a8e4a 100644 --- a/interfaces/api/display/I2DOverlay.h +++ b/interfaces/api/display/I2DOverlay.h @@ -17,6 +17,7 @@ #ifndef SOLAR_I2DOVERLAY_H #define SOLAR_I2DOVERLAY_H +#include #include "datastructure/MathDefinitions.h" #include #include @@ -81,6 +82,20 @@ class XPCF_IGNORE I2DOverlay : /// @param[in,out] displayImage The image on which the squared binary pattern will be drawn (on the whole image). virtual void drawSBPattern (const SolAR::datastructure::SquaredBinaryPattern & pattern, SRef displayImage) = 0; + /// @brief Draw a loss/metric curve plot onto an image. + /// The whole image is used as the plotting canvas: axes, grid, the polyline of values + /// and numeric labels are rendered with drawing parameters taken from the configuration file. + /// Non-pure so existing I2DOverlay implementers remain valid; the default does nothing. + /// @param[in] values the ordered sequence of values to plot (e.g. per-iteration loss). + /// @param[in,out] displayImage the image used as the plot canvas. + /// @param[in] title (optional) a title label drawn on the plot. + virtual void drawLossCurve(const std::vector & values, + SRef displayImage, + const std::string & title = "Loss") + { + (void)values; (void)displayImage; (void)title; + } + }; } } diff --git a/interfaces/api/display/I3DMeshViewer.h b/interfaces/api/display/I3DMeshViewer.h new file mode 100644 index 00000000..1c908fdf --- /dev/null +++ b/interfaces/api/display/I3DMeshViewer.h @@ -0,0 +1,58 @@ +/******************************************************************************* + * SolAR ARCloud + * (C) Copyright 2026 b<>com. All rights reserved. + * + * IDDN: FR.001.020021.005.S.C.2024.000.00000 + * This software is the confidential intellectual property of b<>com. + * You shall not disclose it and shall use it only in accordance with + * the terms of the license agreement you entered into with b<>com. + * + ********************************************************************************/ +#ifndef SOLAR_API_DISPLAY_I3DMESHVIEWER_H +#define SOLAR_API_DISPLAY_I3DMESHVIEWER_H + +#include +#include +#include "core/Messages.h" +#include "datastructure/Mesh.h" +#include "datastructure/GeometryDefinitions.h" +#include "datastructure/MathDefinitions.h" + +#include + +namespace SolAR { +namespace api { +namespace display { + +/** + * @class I3DMeshViewer + * @brief Display a triangle mesh in an interactive 3D OpenGL window. + * UUID: a9f5bdee-c3f6-4b7a-a1d2-1e2f3a4b5c6d + */ +class XPCF_IGNORE I3DMeshViewer : virtual public org::bcom::xpcf::IComponentIntrospect +{ +public: + I3DMeshViewer() = default; + virtual ~I3DMeshViewer() = default; + + /// @brief Display the mesh in a 3D window. + /// @param[in] mesh The triangle mesh to render. + /// @param[in] pose Current camera pose (world-to-camera transform used to draw a frustum overlay). + /// @param[in] keyframePoses Optional set of keyframe poses to overlay as frustums. + /// @return FrameworkReturnCode::_SUCCESS, or _STOP when the user requests to close the window. + virtual FrameworkReturnCode display( + const SRef & mesh, + const SolAR::datastructure::Transform3Df & pose, + const std::vector & keyframePoses = {}) = 0; +}; + +} +} +} + +XPCF_DEFINE_INTERFACE_TRAITS(SolAR::api::display::I3DMeshViewer, + "a9f5bdee-c3f6-4b7a-a1d2-1e2f3a4b5c6d", + "I3DMeshViewer", + "Display a 3D triangle mesh in an interactive OpenGL window"); + +#endif // SOLAR_API_DISPLAY_I3DMESHVIEWER_H \ No newline at end of file diff --git a/interfaces/api/display/I3DPointsViewer.h b/interfaces/api/display/I3DPointsViewer.h index 15ffec90..29f2b88c 100644 --- a/interfaces/api/display/I3DPointsViewer.h +++ b/interfaces/api/display/I3DPointsViewer.h @@ -98,6 +98,49 @@ class XPCF_IGNORE I3DPointsViewer : /// @return FrameworkReturnCode::_SUCCESS if the window is created, else FrameworkReturnCode::_ERROR_ virtual FrameworkReturnCode buffer(const SRef pointCloud, const SRef pointCloud2 = nullptr) = 0; + + /// @brief Display a set of 3D Gaussians rendered as ellipsoid splats in a window. + /// The data is passed as flat, contiguous, libtorch-free float buffers so that + /// implementers (e.g. an OpenGL viewer) need no tensor library dependency. + /// The viewer is expected to own its interactive (orbit) camera, hence no pose is required. + /// @param[in] centers flat array of Gaussian centers, size 3*N (x,y,z per Gaussian). + /// @param[in] scales flat array of per-axis scales, size 3*N (sx,sy,sz per Gaussian). + /// @param[in] quaternions flat array of rotation quaternions, size 4*N (w,x,y,z per Gaussian). + /// @param[in] colors flat array of RGB colors in [0,1], size 3*N (r,g,b per Gaussian). + /// @param[in] opacities array of opacities in [0,1], size N. + /// @return FrameworkReturnCode::_SUCCESS if displayed, FrameworkReturnCode::_NOT_IMPLEMENTED + /// if the implementer does not support Gaussian display, else FrameworkReturnCode::_ERROR_. + virtual FrameworkReturnCode displayGaussians(const std::vector & centers, + const std::vector & scales, + const std::vector & quaternions, + const std::vector & colors, + const std::vector & opacities) + { + (void)centers; (void)scales; (void)quaternions; (void)colors; (void)opacities; + return FrameworkReturnCode::_NOT_IMPLEMENTED; + } + + /// @brief Display 3D Gaussians with view-dependent colour (spherical harmonics). + /// Same as displayGaussians() above, plus the higher-order SH coefficients so the + /// viewer can evaluate view-dependent colour per frame (degree 0 = DC is in @p colors). + /// @param[in] centers size 3*N. @param[in] scales size 3*N. @param[in] quaternions size 4*N. + /// @param[in] colors DC RGB base in [0,1], size 3*N. @param[in] opacities size N. + /// @param[in] shCoeffsRest higher-order SH coefficients, size N*3*K with K = coeffs per + /// channel for the degree (deg1=3, deg2=8, deg3=15); per Gaussian channel-major + /// [R(K) G(K) B(K)] (Inria/PLY f_rest order). + /// @param[in] shDegree SH degree of the rest coefficients (1, 2 or 3). + /// @return _SUCCESS, _NOT_IMPLEMENTED, or _ERROR_. + virtual FrameworkReturnCode displayGaussians(const std::vector & centers, + const std::vector & scales, + const std::vector & quaternions, + const std::vector & colors, + const std::vector & opacities, + const std::vector & shCoeffsRest, + int shDegree) + { + (void)shCoeffsRest; (void)shDegree; + return displayGaussians(centers, scales, quaternions, colors, opacities); + } }; } } diff --git a/interfaces/api/gs/I3DGSTrainer.h b/interfaces/api/gs/I3DGSTrainer.h index 9da46f5a..81ddbf49 100644 --- a/interfaces/api/gs/I3DGSTrainer.h +++ b/interfaces/api/gs/I3DGSTrainer.h @@ -28,7 +28,7 @@ namespace gs { /** * @class I3DGSTrainer - * @brief Returns trained 3DGS model from poses images. + * @brief Returns trained 3DGS model from posed images. * UUID: c4bed096-4d34-11f1-aa46-325096b39f47 * * This class provides a solution to train 3DGS model. diff --git a/interfaces/api/map/I3DGaussianSplatting.h b/interfaces/api/map/I3DGaussianSplatting.h index c2bc7239..43166177 100644 --- a/interfaces/api/map/I3DGaussianSplatting.h +++ b/interfaces/api/map/I3DGaussianSplatting.h @@ -35,13 +35,25 @@ class XPCF_IGNORE I3DGaussianSplatting : virtual public IProcessMap public: /// @enum class GSProcessingStatus - /// @brief define the different status of processing + /// @brief define the different status of 3D Gaussian Splatting processing. + /// Values chain on after the base IProcessMap::ProcessingStatus (which ends at 4), + /// starting at 5, mirroring SfmProcessingStatus. enum class GSProcessingStatus: std::underlying_type_t { + RUNNING_INITIALIZATION = 5, ///< seeding the Gaussian model from the input map + IDLE_INITIALIZATION_FINISHED, ///< Gaussian model initialized + RUNNING_TRAINING, ///< optimizing the Gaussians (forward/backward/densify) + IDLE_TRAINING_FINISHED, ///< optimization finished + RUNNING_EXPORT, ///< building the output map from the trained Gaussians }; /// @brief return a string value of a ProcessingStatus value std::string toString(ProcessingStatus status) final { switch (static_cast(status)) { + case GSProcessingStatus::RUNNING_INITIALIZATION: return "RUNNING_INITIALIZATION"; + case GSProcessingStatus::IDLE_INITIALIZATION_FINISHED: return "IDLE_INITIALIZATION_FINISHED"; + case GSProcessingStatus::RUNNING_TRAINING: return "RUNNING_TRAINING"; + case GSProcessingStatus::IDLE_TRAINING_FINISHED: return "IDLE_TRAINING_FINISHED"; + case GSProcessingStatus::RUNNING_EXPORT: return "RUNNING_EXPORT"; default: return IProcessMap::toString(status); } } diff --git a/interfaces/api/map/I3DGaussianSplattingDensifier.h b/interfaces/api/map/I3DGaussianSplattingDensifier.h new file mode 100644 index 00000000..cb5dc5fc --- /dev/null +++ b/interfaces/api/map/I3DGaussianSplattingDensifier.h @@ -0,0 +1,85 @@ +/** + * @copyright Copyright (c) 2026 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 I3DGAUSSIANSPLATTINGDENSIFIER_H +#define I3DGAUSSIANSPLATTINGDENSIFIER_H + +#include + +namespace SolAR { +namespace api { +namespace map { + +/** + * @class I3DGaussianSplattingDensifier + * @brief Convert a trained 3D Gaussian Splatting map into a dense coloured point cloud map. + * UUID: 44f7f040-4d38-4dcc-8e0f-a6580a93b8b0 + * + * Mirrors I3DGaussianSplatting: it consumes a map whose point cloud carries one Gaussian + * per CloudPoint (means/featuresDc/normal/featuresRest/opacity/scale/quat) and produces a + * new map holding a dense, sampled point cloud (plain xyz + rgb CloudPoints). It does not + * perform any training: the input Gaussian map is assumed to be already trained. + * + */ + +class XPCF_IGNORE I3DGaussianSplattingDensifier : virtual public IProcessMap +{ +public: + + /// @enum class DensifyProcessingStatus + /// @brief define the different status of 3DGS-to-point-cloud densification. + /// Values chain on after the base IProcessMap::ProcessingStatus (which ends at 4), + /// starting at 5, mirroring GSProcessingStatus. + enum class DensifyProcessingStatus: std::underlying_type_t { + RUNNING_LOAD = 5, ///< reading the Gaussian model from the input map + IDLE_LOAD_FINISHED, ///< Gaussian model loaded + RUNNING_SAMPLING, ///< sampling dense points from the Gaussians + IDLE_SAMPLING_FINISHED, ///< sampling finished + RUNNING_EXPORT, ///< building the output map from the dense points + }; + + /// @brief return a string value of a ProcessingStatus value + std::string toString(ProcessingStatus status) { + switch (static_cast(status)) { + case DensifyProcessingStatus::RUNNING_LOAD: return "RUNNING_LOAD"; + case DensifyProcessingStatus::IDLE_LOAD_FINISHED: return "IDLE_LOAD_FINISHED"; + case DensifyProcessingStatus::RUNNING_SAMPLING: return "RUNNING_SAMPLING"; + case DensifyProcessingStatus::IDLE_SAMPLING_FINISHED: return "IDLE_SAMPLING_FINISHED"; + case DensifyProcessingStatus::RUNNING_EXPORT: return "RUNNING_EXPORT"; + default: return IProcessMap::toString(status); + } + } + +public: + ///@brief I3DGaussianSplattingDensifier default constructor. + I3DGaussianSplattingDensifier() = default; + + ///@brief I3DGaussianSplattingDensifier default destructor. + virtual ~I3DGaussianSplattingDensifier() override = default; + +}; + + +} // namespace map +} // namespace api +} // namespace SolAR + +XPCF_DEFINE_INTERFACE_TRAITS(SolAR::api::map::I3DGaussianSplattingDensifier, + "44f7f040-4d38-4dcc-8e0f-a6580a93b8b0", + "I3DGaussianSplattingDensifier", + "I3DGaussianSplattingDensifier interface description"); + +#endif // I3DGAUSSIANSPLATTINGDENSIFIER_H diff --git a/interfaces/core/Log.h b/interfaces/core/Log.h index 315d1ffe..409633c6 100644 --- a/interfaces/core/Log.h +++ b/interfaces/core/Log.h @@ -12,6 +12,7 @@ #include "SolARFrameworkDefinitions.h" #include #include +#include // external-fmt formatters for std::filesystem::path, std::thread::id, etc. #include #include diff --git a/interfaces/datastructure/Map.h b/interfaces/datastructure/Map.h index a6ad70c4..1849e2fd 100644 --- a/interfaces/datastructure/Map.h +++ b/interfaces/datastructure/Map.h @@ -57,22 +57,26 @@ enum class MapProcessingApplied: std::uint8_t { */ class MapProcessingStep { public: - MapProcessingStep(const MapProcessingApplied& processingApplied, const std::string& originalMapUUID): - m_processingApplied{processingApplied}, m_originalMapUUID{originalMapUUID} { - const std::time_t t_c = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + MapProcessingStep(const MapProcessingApplied& processingApplied, const std::string& sourceMapUUID, const std::string& targetMapUUID): + m_processingApplied{processingApplied}, m_sourceMapUUID{sourceMapUUID}, m_targetMapUUID{targetMapUUID} { + const auto now = std::chrono::system_clock::now(); + m_processingTimestamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + const std::time_t t_c = std::chrono::system_clock::to_time_t(now); std::string dateTime = std::ctime(&t_c); m_processingDateTime = dateTime.substr(0, dateTime.size() - 1); } - MapProcessingStep(const MapProcessingApplied& processingApplied, const std::string& originalMapUUID, const std::string& processingDateTime): - m_processingApplied{processingApplied}, m_originalMapUUID{originalMapUUID}, m_processingDateTime{processingDateTime} { + MapProcessingStep(const MapProcessingApplied& processingApplied, const std::string& sourceMapUUID, const std::string& targetMapUUID, const uint64_t processingTimestamp, const std::string& processingDateTime): + m_processingApplied{processingApplied}, m_sourceMapUUID{sourceMapUUID}, m_targetMapUUID{targetMapUUID}, m_processingTimestamp(processingTimestamp), m_processingDateTime{processingDateTime} { } ~MapProcessingStep() = default; MapProcessingApplied getProcessingApplied() const { return m_processingApplied; } - std::string getOriginalMapUUID() const { return m_originalMapUUID; } - std::string getTimestamp() const { return m_processingDateTime; } + std::string getSourceMapUUID() const { return m_sourceMapUUID; } + std::string getTargetMapUUID() const { return m_targetMapUUID; } + uint64_t getTimestamp() const { return m_processingTimestamp; } + std::string getDateTime() const { return m_processingDateTime; } private: /// @@ -81,15 +85,19 @@ class MapProcessingStep { MapProcessingStep() = default; MapProcessingApplied m_processingApplied; // Processing applied to obtain the map - std::string m_originalMapUUID; // Original map processed to obtain the current map - std::string m_processingDateTime; // Date and time of the processing + std::string m_sourceMapUUID; // Source map to which the processing is applied + std::string m_targetMapUUID; // Target map resulting from processing (may be identical to the source map) + uint64_t m_processingTimestamp; // Timestamp of processing in seconds since epoch + std::string m_processingDateTime; // Date and time of the processing in a string format friend class boost::serialization::access; template void serialize(Archive &ar, const unsigned int /* version */) { ar & m_processingApplied; - ar & m_originalMapUUID; + ar & m_sourceMapUUID; + ar & m_targetMapUUID; + ar & m_processingTimestamp; ar & m_processingDateTime; } }; @@ -347,6 +355,9 @@ class SOLARFRAMEWORK_API Map : public Trackable { bool isMapCompatible(datastructure::DescriptorType descriptorType, datastructure::GlobalDescriptorType globalDescriptorType) const; + /// @brief Reset all processing steps of the map history + void resetMapProcessingHistory(); + /// @brief Add a processing step to the map information /// @param[in] mapProcessingStep the new step of processing applied to the map void addMapProcessingStep(const MapProcessingStep & mapProcessingStep); diff --git a/src/datastructure/Map.cpp b/src/datastructure/Map.cpp index 340f71ba..19646ff1 100644 --- a/src/datastructure/Map.cpp +++ b/src/datastructure/Map.cpp @@ -267,6 +267,12 @@ bool Map::isMapCompatible(datastructure::DescriptorType descriptorType, return true; } +void Map::resetMapProcessingHistory() +{ + m_mapProcessingHistory.clear(); +} + + void Map::addMapProcessingStep(const MapProcessingStep & mapProcessingStep) { m_mapProcessingHistory.push_back(mapProcessingStep);