diff --git a/meshroom/aliceVision/ConvertSfMFormat.py b/meshroom/aliceVision/ConvertSfMFormat.py index 3130ae4b02..db853e6423 100644 --- a/meshroom/aliceVision/ConvertSfMFormat.py +++ b/meshroom/aliceVision/ConvertSfMFormat.py @@ -26,7 +26,7 @@ class ConvertSfMFormat(desc.AVCommandLineNode): label="SfM File Format", description="Output SfM file format.", value="abc", - values=["abc", "sfm", "json", "ply", "baf"], + values=["abc", "sfm", "json", "ply", "baf", "fbd"], group="", # exclude from command line ), desc.ChoiceParam( diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dfa00ff0bd..64d1eaeb57 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -370,6 +370,18 @@ if (ALICEVISION_BUILD_SFM) ) endif() +# ============================================================================== +# FlatBuffers +# ============================================================================== +include (FetchContent) +FetchContent_Declare( + flatbuffers + GIT_REPOSITORY https://github.com/google/flatbuffers.git + GIT_TAG v25.2.10 +) +FetchContent_MakeAvailable(flatbuffers) + + # ============================================================================== # Eigen # ============================================================================== @@ -550,6 +562,7 @@ if (ALICEVISION_BUILD_SFM) endif() endif() + # ============================================================================== # Alembic # ============================================================================== diff --git a/src/aliceVision/camera/Distortion.hpp b/src/aliceVision/camera/Distortion.hpp index 646287f3b2..2660c3b71c 100644 --- a/src/aliceVision/camera/Distortion.hpp +++ b/src/aliceVision/camera/Distortion.hpp @@ -22,6 +22,9 @@ namespace camera { */ class Distortion { + public: + using sptr = std::shared_ptr; + public: Distortion() = default; diff --git a/src/aliceVision/camera/Undistortion.hpp b/src/aliceVision/camera/Undistortion.hpp index c0f52dde02..3c80603dc7 100644 --- a/src/aliceVision/camera/Undistortion.hpp +++ b/src/aliceVision/camera/Undistortion.hpp @@ -29,6 +29,9 @@ namespace camera { class Undistortion { public: + using sptr = std::shared_ptr; + public: + Undistortion(int width, int height) { _pixelAspectRatio = 1.0; @@ -67,6 +70,11 @@ class Undistortion void setOffset(const Vec2& offset) { _offset = offset; } + void setSize(const Vec2& size) + { + setSize(size.x(), size.y()); + } + void setSize(int width, int height) { double hh = height; diff --git a/src/aliceVision/sfmData/ImageInfo.hpp b/src/aliceVision/sfmData/ImageInfo.hpp index 5985b68b74..196252bad0 100644 --- a/src/aliceVision/sfmData/ImageInfo.hpp +++ b/src/aliceVision/sfmData/ImageInfo.hpp @@ -21,7 +21,10 @@ namespace aliceVision { namespace sfmData { class ImageInfo -{ +{ + public: + using sptr = std::shared_ptr; + public: /** * @brief Image Constructor diff --git a/src/aliceVision/sfmData/View.hpp b/src/aliceVision/sfmData/View.hpp index 6441983b03..5ce557cb6b 100644 --- a/src/aliceVision/sfmData/View.hpp +++ b/src/aliceVision/sfmData/View.hpp @@ -106,12 +106,24 @@ class View */ ImageInfo& getImage() { return *_image; } + /** + * @brief Get Image info pointer + * @return a shared pointer + */ + const std::shared_ptr getImageInfo() const { return _image; } + /** * @brief Get Image info pointer * @return a shared pointer */ std::shared_ptr getImageInfo() { return _image; } + /** + * @brief Set Image info object + * @param image the new imageInfo to use + */ + void setImageInfo(std::shared_ptr & image) { _image = image; } + /** * @brief Get the view id * @return view id diff --git a/src/aliceVision/sfmDataIO/CMakeLists.txt b/src/aliceVision/sfmDataIO/CMakeLists.txt index 07cd600a24..81ba8f62ca 100644 --- a/src/aliceVision/sfmDataIO/CMakeLists.txt +++ b/src/aliceVision/sfmDataIO/CMakeLists.txt @@ -9,6 +9,8 @@ set(sfmDataIO_files_headers plyIO.hpp viewIO.hpp sceneSample.hpp + FlatBuffersIO.hpp + MappedFile.hpp ) # Sources @@ -22,6 +24,8 @@ set(sfmDataIO_files_sources plyIO.cpp viewIO.cpp sceneSample.cpp + FlatBuffersIO.cpp + MappedFile.cpp ) if (ALICEVISION_HAVE_ALEMBIC) @@ -37,6 +41,42 @@ if (ALICEVISION_HAVE_ALEMBIC) ) endif() +list (APPEND FLATBUFFERS_SCHEMA_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/Vec.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/Rig.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/CameraPose.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/Observation.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/Landmark.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/Image.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/View.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/Intrinsic.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/Distortion.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/Undistortion.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/SurveyPoint.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/SfmData.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas/Root.fbs +) + +list (APPEND FLATBUFFERS_SCHEMA_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/FlatBuffersIO/schemas +) + +build_flatbuffers( + "${FLATBUFFERS_SCHEMA_FILES}" + "${FLATBUFFERS_SCHEMA_INCLUDE_DIRS}" + "fbschemas" + "" + "${generatedDir}" + "${generatedDir}" + "${generatedDir}" +) + +set (FLATBUFFERS_FLATC_SCHEMA_EXTRA_ARGS "--gen-object-api") +add_library(FlatBuffersTarget INTERFACE) +target_include_directories(FlatBuffersTarget INTERFACE ${flatbuffers_SOURCE_DIR}/include) +add_dependencies(FlatBuffersTarget fbschemas) + + alicevision_add_library(aliceVision_sfmDataIO SOURCES ${sfmDataIO_files_headers} ${sfmDataIO_files_sources} PUBLIC_LINKS @@ -47,9 +87,9 @@ alicevision_add_library(aliceVision_sfmDataIO aliceVision_image Boost::regex Boost::boost + FlatBuffersTarget ) - if (ALICEVISION_HAVE_ALEMBIC) target_link_libraries(aliceVision_sfmDataIO PRIVATE Alembic::Alembic diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO.cpp b/src/aliceVision/sfmDataIO/FlatBuffersIO.cpp new file mode 100644 index 0000000000..8f1e3feb40 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO.cpp @@ -0,0 +1,274 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + + +namespace aliceVision { +namespace sfmDataIO { + +template +const T * optionalPtr(const std::optional & opt) +{ + if (opt.has_value()) + { + return &opt.value(); + } + + return nullptr; +} + +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, const sfmData::SfMData & obj, ESfMData partFlag) +{ + using namespace AliceVisionIO; + using namespace flatbuffers; + + Offset ret; + + // save flags + const bool saveViews = (partFlag & VIEWS) == VIEWS; + const bool saveAncestors = (partFlag & ANCESTORS) == ANCESTORS; + const bool saveIntrinsics = (partFlag & INTRINSICS) == INTRINSICS; + const bool saveExtrinsics = (partFlag & EXTRINSICS) == EXTRINSICS; + const bool saveStructure = (partFlag & STRUCTURE) == STRUCTURE; + const bool saveSurveys = (partFlag & SURVEYS) == SURVEYS; + const bool saveFeatures = (partFlag & OBSERVATIONS_WITH_FEATURES) == OBSERVATIONS_WITH_FEATURES; + const bool saveObservations = saveFeatures || ((partFlag & OBSERVATIONS) == OBSERVATIONS); + + auto features = Pack(builder, obj.getRelativeFeaturesFolders()); + auto matches = Pack(builder, obj.getRelativeMatchesFolders()); + + + std::optional>> packedViews; + std::optional>> packedImages; + std::optional>> packedPoses; + std::optional>> packedLandmarks; + std::optional> packedObservations; + std::optional>> packedDistortions; + std::optional>> packedUndistortions; + std::optional>> packedIntrinsics; + std::optional>> packedRigs; + std::optional>> packedSurveys; + + if (saveExtrinsics) + { + packedRigs = Pack(builder, obj.getRigs()); + packedPoses = Pack(builder, obj.getPoses()); + } + + if (saveIntrinsics) + { + packedIntrinsics = Pack(builder, obj.getIntrinsics()); + packedDistortions = Pack(builder, getDistortions(obj.getIntrinsics())); + packedUndistortions = Pack(builder, getUndistortions(obj.getIntrinsics())); + } + + if (saveStructure) + { + packedLandmarks = Pack(builder, obj.getLandmarks()); + + if (saveObservations) + { + packedObservations = Pack(builder, getObservations(obj.getLandmarks())); + } + } + + if (saveViews) + { + packedViews = Pack(builder, obj.getViews()); + packedImages = Pack(builder, getImagesInfos(obj.getViews())); + } + + if (saveSurveys) + { + packedSurveys = Pack(builder, obj.getSurveyPoints()); + } + + return V1::CreateSfmDataDirect(builder, + &features, + &matches, + optionalPtr(packedViews), + optionalPtr(packedImages), + optionalPtr(packedPoses), + optionalPtr(packedLandmarks), + optionalPtr(packedObservations), + optionalPtr(packedDistortions), + optionalPtr(packedUndistortions), + optionalPtr(packedIntrinsics), + optionalPtr(packedRigs), + optionalPtr(packedSurveys)); +} + +void Unpack(sfmData::SfMData & sfmData, const AliceVisionIO::V1::SfmData * packedSfmData, ESfMData partFlag) +{ + if (!packedSfmData) + { + return; + } + + const bool loadViews = (partFlag & VIEWS) == VIEWS; + const bool loadAncestors = (partFlag & ANCESTORS) == ANCESTORS; + const bool loadIntrinsics = (partFlag & INTRINSICS) == INTRINSICS; + const bool loadExtrinsics = (partFlag & EXTRINSICS) == EXTRINSICS; + const bool loadStructure = (partFlag & STRUCTURE) == STRUCTURE; + const bool loadSurveys = (partFlag & SURVEYS) == SURVEYS; + const bool loadFeatures = (partFlag & OBSERVATIONS_WITH_FEATURES) == OBSERVATIONS_WITH_FEATURES; + const bool loadObservations = loadFeatures || ((partFlag & OBSERVATIONS) == OBSERVATIONS); + + sfmData.setFeaturesFolders(Unpack(packedSfmData->features_folders())); + sfmData.setMatchesFolders(Unpack(packedSfmData->matches_folders())); + + if (loadViews) + { + Unpack(sfmData.getViews(), packedSfmData->views()); + + std::map imageInfos; + Unpack(imageInfos, packedSfmData->images()); + + /*Assign image to view*/ + for (auto & [idView, image] : imageInfos) + { + sfmData.getView(idView).setImageInfo(image); + } + } + + if (loadExtrinsics) + { + Unpack(sfmData.getPoses(), packedSfmData->poses()); + Unpack(sfmData.getRigs(), packedSfmData->rigs()); + } + + if (loadIntrinsics) + { + Unpack(sfmData.getIntrinsics(), packedSfmData->intrinsics()); + sfmData::SharedPtrMap distortions; + Unpack(distortions, packedSfmData->distortions()); + + // Assign distortion to view + for (auto & [idIntrinsic, distortion] : distortions) + { + auto intrinsic = sfmData.getIntrinsics().at(idIntrinsic); + auto isod = std::dynamic_pointer_cast(intrinsic); + isod->setDistortionObject(distortion); + } + + sfmData::SharedPtrMap undistortions; + Unpack(undistortions, packedSfmData->undistortions()); + + //Assign distortion to view + for (auto & [idIntrinsic, undistortion] : undistortions) + { + auto intrinsic = sfmData.getIntrinsics().at(idIntrinsic); + auto isod = std::dynamic_pointer_cast(intrinsic); + isod->setUndistortionObject(undistortion); + } + } + + if (loadStructure) + { + Unpack(sfmData.getLandmarks(), packedSfmData->landmarks()); + + if (loadObservations) + { + std::map observations; + Unpack(observations, packedSfmData->observations()); + + //Assign observations + sfmData::Landmarks & landmarks = sfmData.getLandmarks(); + for (const auto & [pair, observationToAdd] : observations) + { + auto & observations = landmarks.at(pair.first).getObservations(); + observations.emplace(pair.second, observationToAdd); + } + + } + + + } + + if (loadSurveys) + { + Unpack(sfmData.getSurveyPoints(), packedSfmData->surveys()); + } +} + +bool saveFlatBuffers(const sfmData::SfMData& sfmData, const std::string& filename, ESfMData partFlag) +{ + //Open binary stream on disk + std::ofstream fs(filename, std::ios::out | std::ios::binary); + if (!fs.is_open()) + { + ALICEVISION_LOG_ERROR("Unable to create the file " << filename); + return false; + } + + // Create the FlatBuffer + flatbuffers::FlatBufferBuilder builder(1024); + + // Version + AliceVisionIO::V1::Version version(ALICEVISION_VERSION_MAJOR, + ALICEVISION_VERSION_MINOR, + ALICEVISION_SFMDATAIO_VERSION_REVISION); + + // Pack the sfmData + auto packedRoot = AliceVisionIO::V1::CreateRoot(builder, + &version, + Pack(builder, sfmData, partFlag)); + + builder.Finish(packedRoot, "ASFM"); + + // Grab the buffers + uint8_t* buf = builder.GetBufferPointer(); + int size = builder.GetSize(); + + // Write the binary data + fs.write(reinterpret_cast(buf), size); + fs.close(); + + return true; +} + +bool loadFlatBuffers(sfmData::SfMData& sfmData, const std::string& filename, ESfMData partFlag) +{ + MemoryMappedFile input; + + if (!input.open(filename)) + { + ALICEVISION_LOG_ERROR("Unable to open the file " << filename); + return false; + } + + if (!flatbuffers::BufferHasIdentifier(input.data(), "ASFM")) + { + ALICEVISION_LOG_ERROR("Incorrect file header in " << filename); + return false; + } + + const AliceVisionIO::V1::Root * root = AliceVisionIO::V1::GetRoot(input.data()); + + Unpack(sfmData, root->sfm_data(), partFlag); + + return true; +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO.hpp b/src/aliceVision/sfmDataIO/FlatBuffersIO.hpp new file mode 100644 index 0000000000..2846fc5398 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO.hpp @@ -0,0 +1,33 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include + +namespace aliceVision { +namespace sfmDataIO { + +/** + * @brief Load a FlatBuffers SfMData file. + * @param[out] sfmData The output SfMData + * @param[in] filename The filename + * @param[in] partFlag The ESfMData load flag + * @return true if completed + */ +bool loadFlatBuffers(sfmData::SfMData& sfmData, const std::string& filename, ESfMData partFlag); + +/** + * @brief Save an SfMData in a FlatBuffers file + * @param[in] sfmData The input SfMData + * @param[in] filename The filename + * @param[in] partFlag The ESfMData save flag + * @return true if completed + */ +bool saveFlatBuffers(const sfmData::SfMData& sfmData, const std::string& filename, ESfMData partFlag); + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/CameraPose.hpp b/src/aliceVision/sfmDataIO/FlatBuffersIO/CameraPose.hpp new file mode 100644 index 0000000000..aa5ada7cf7 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/CameraPose.hpp @@ -0,0 +1,92 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include +#include + +namespace aliceVision { +namespace sfmDataIO { + +/** + * Pack a sfmData::CameraPose to flatbuffers + * @param builder the flatbuffers builder to use + * @param obj the CameraPose to serialize + * @param index the CameraPose id + * @return flatbuffers object +*/ +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, + const sfmData::CameraPose & obj, + IndexT index) +{ + const geometry::Pose3 & p = obj.getTransform(); + + AliceVisionIO::Vec3 rotation = sfmDataIO::Pack(SO3::logm(p.rotation())); + AliceVisionIO::Vec3 center = Pack(p.center()); + + return AliceVisionIO::V1::CreateCameraPose(builder, + index, + &rotation, + ¢er, + obj.isLocked(), + obj.isRotationOnly(), + obj.isRemovable()); +} + +/** + * Unpack a sfmData::CameraPose from flatbuffers + * @param outPose the CameraPose to construct + * @param obj the CameraPose flatbuffer object +*/ +void Unpack(sfmData::CameraPose & outPose, + const AliceVisionIO::V1::CameraPose & obj) +{ + Eigen::Matrix3d R = SO3::expm(Unpack(*obj.rotation())); + geometry::Pose3 p(R, Unpack(*obj.center())); + + sfmData::CameraPose cp(p); + outPose.setRotationOnly(obj.rotation_only()); + + if (obj.locked()) + { + outPose.lock(); + } + else + { + outPose.unlock(); + } + + outPose.setRemovable(obj.removable()); +} + +/** + * Unpack a vector of flatbuffers camerapose to sfmData::Poses + * @param outPoses the map of CameraPose to construct + * @param obj the CameraPose flatbuffer vector +*/ +void Unpack(sfmData::Poses & outPoses, const flatbuffers::Vector> * obj) +{ + if (!obj) + { + return; + } + + outPoses.clear(); + + for (const auto & pose: *obj) + { + sfmData::CameraPose cp; + Unpack(cp, *pose); + + outPoses.assign(pose->id(), cp); + } + +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/Eigen.hpp b/src/aliceVision/sfmDataIO/FlatBuffersIO/Eigen.hpp new file mode 100644 index 0000000000..7500fc6bd7 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/Eigen.hpp @@ -0,0 +1,57 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include +#include + +namespace aliceVision { +namespace sfmDataIO { + +/** + * @brief Convert a vec2 to a flatbuffers vec2 + * @param obj an input vec2 + * @return a flatbuffer object containing the vec2 +*/ +AliceVisionIO::Vec2 Pack(const Vec2 & obj) +{ + return AliceVisionIO::Vec2(obj.x(), obj.y()); +} + +/** + * @brief Convert a vec3 to a flatbuffers vec3 + * @param obj an input vec3 + * @return a flatbuffer object containing the vec3 +*/ +AliceVisionIO::Vec3 Pack(const Vec3 & obj) +{ + return AliceVisionIO::Vec3(obj.x(), obj.y(), obj.z()); +} + +/** + * @brief Convert a flatbuffers vec3 to a vec3 + * @param obj an input flatbuffers vec3 + * @return a vec3 +*/ +Vec3 Unpack(const AliceVisionIO::Vec3 & obj) +{ + return Vec3(obj.x(), obj.y(), obj.z()); +} + +/** + * @brief Convert a flatbuffers vec2 to a vec2 + * @param obj an input flatbuffers vec2 + * @return a vec2 +*/ +Vec2 Unpack(const AliceVisionIO::Vec2 & obj) +{ + return Vec2(obj.x(), obj.y()); +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/Intrinsics.hpp b/src/aliceVision/sfmDataIO/FlatBuffersIO/Intrinsics.hpp new file mode 100644 index 0000000000..c849b7abb2 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/Intrinsics.hpp @@ -0,0 +1,289 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include + + +namespace aliceVision { +namespace sfmDataIO { + +AliceVisionIO::V1::INITMODE Pack(const camera::EInitMode val) +{ + return static_cast(val); +} + +camera::EInitMode Unpack(const AliceVisionIO::V1::INITMODE val) +{ + return static_cast(val); +} + +AliceVisionIO::V1::TYPEINTRINSIC Pack(const camera::EINTRINSIC val) +{ + return static_cast(val); +} + +camera::EINTRINSIC Unpack(const AliceVisionIO::V1::TYPEINTRINSIC val) +{ + return static_cast(val); +} + +AliceVisionIO::V1::TYPEDISTORTION Pack(const camera::EDISTORTION val) +{ + return static_cast(val); +} + +camera::EDISTORTION Unpack(const AliceVisionIO::V1::TYPEDISTORTION val) +{ + return static_cast(val); +} + +AliceVisionIO::V1::TYPEUNDISTORTION Pack(const camera::EUNDISTORTION val) +{ + return static_cast(val); +} + +camera::EUNDISTORTION Unpack(const AliceVisionIO::V1::TYPEUNDISTORTION val) +{ + return static_cast(val); +} + +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, + const camera::IntrinsicBase & obj, + IndexT index) +{ + const camera::IntrinsicScaleOffsetDisto & isod = dynamic_cast(obj); + + const AliceVisionIO::Vec2 packedOffset = Pack(isod.getOffset()); + + double circleRadius = 0.0; + Vec2 circleCenter(0.0, 0.0); + + if (obj.getType() == camera::EQUIDISTANT_CAMERA) + { + const camera::Equidistant & equidistant = dynamic_cast(obj); + circleRadius = equidistant.getCircleRadius(); + circleCenter = equidistant.getCircleCenter(); + } + + auto packedCircleCenter = Pack(circleCenter); + + + return AliceVisionIO::V1::CreateIntrinsicDirect( + builder, + index, + Pack(obj.getType()), + Pack(obj.getInitializationMode()), + obj.isLocked(), + obj.w(), + obj.h(), + obj.sensorWidth(), + obj.sensorHeight(), + obj.serialNumber().c_str(), + isod.getFocalLength(), + isod.getPixelAspectRatio(), + isod.getInitialFocalLength(), + &packedOffset, + isod.isRatioLocked(), + isod.isOffsetLocked(), + isod.isScaleLocked(), + Pack(isod.getDistortionInitializationMode()), + circleRadius, + &packedCircleCenter + ); +} + +std::map getDistortions(const sfmData::Intrinsics &intrinsics) +{ + std::map ret; + + for (const auto & [idIntrinsic, intrinsic] : intrinsics.valueRange()) + { + const camera::IntrinsicScaleOffsetDisto & isod = dynamic_cast(intrinsic); + + const camera::Distortion::sptr d = isod.getDistortion(); + if (d) + { + ret.insert({idIntrinsic, d}); + } + } + + return ret; +} + +std::map getUndistortions(const sfmData::Intrinsics &intrinsics) +{ + std::map ret; + + for (const auto & [idIntrinsic, intrinsic] : intrinsics.valueRange()) + { + const camera::IntrinsicScaleOffsetDisto & isod = dynamic_cast(intrinsic); + + const camera::Undistortion::sptr d = isod.getUndistortion(); + if (d) + { + ret.insert({idIntrinsic, d}); + } + } + + return ret; +} + +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, + const camera::Distortion & obj, + IndexT index) +{ + const std::vector & params = obj.getParameters(); + return AliceVisionIO::V1::CreateDistortionDirect( + builder, + index, + Pack(obj.getType()), + obj.isLocked(), + ¶ms); +} + +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, + const camera::Undistortion & obj, + IndexT index) +{ + const std::vector & params = obj.getParameters(); + + const AliceVisionIO::Vec2 packedSize = Pack(obj.getSize()); + const AliceVisionIO::Vec2 packedOffset = Pack(obj.getOffset()); + + return AliceVisionIO::V1::CreateUndistortionDirect( + builder, + index, + Pack(obj.getType()), + obj.isLocked(), + ¶ms, + &packedSize, + &packedOffset, + obj.getPixelAspectRatio(), + obj.isDesqueezed() + ); +} + + +void Unpack(sfmData::SharedPtrMap & outIntrinsics, + const flatbuffers::Vector> * obj) +{ + if (!obj) + { + return; + } + + for (const auto & intrinsic: *obj) + { + IndexT id = intrinsic->id(); + + + camera::IntrinsicBase::sptr cam = createIntrinsic(Unpack(intrinsic->type()), + camera::EDISTORTION::DISTORTION_NONE, + camera::EUNDISTORTION::UNDISTORTION_NONE, + intrinsic->width(), + intrinsic->height()); + + if (intrinsic->locked()) + { + cam->lock(); + } + else + { + cam->unlock(); + } + + cam->setInitializationMode(Unpack(intrinsic->initialization_mode())); + cam->setSensorWidth(intrinsic->sensor_width()); + cam->setSensorHeight(intrinsic->sensor_height()); + cam->setSerialNumber(intrinsic->serial_number()->str()); + cam->setDistortionInitializationMode(Unpack(intrinsic->distortion_initialization_mode())); + + auto isod = std::dynamic_pointer_cast(cam); + isod->setInitialFocalLength(intrinsic->initial_focal_length(), intrinsic->pixel_ratio()); + isod->setFocalLength(intrinsic->focal_length(), intrinsic->pixel_ratio()); + isod->setOffset(Unpack(*intrinsic->offset())); + isod->setRatioLocked(intrinsic->ratio_locked()); + isod->setOffsetLocked(intrinsic->offset_locked()); + isod->setScaleLocked(intrinsic->scale_locked()); + + auto equidistant = std::dynamic_pointer_cast(cam); + if (equidistant) + { + equidistant->setCircleCenterX(intrinsic->circle_center()->x()); + equidistant->setCircleCenterY(intrinsic->circle_center()->y()); + equidistant->setCircleRadius(intrinsic->circle_radius()); + } + + outIntrinsics.emplace(id, cam); + } +} + +void Unpack(sfmData::SharedPtrMap & outDistortions, + const flatbuffers::Vector> * obj) +{ + if (!obj) + { + return; + } + + outDistortions.clear(); + + for (const auto & distortion: *obj) + { + IndexT id = distortion->id(); + + std::vector distortionParams; + for (const double val: *distortion->parameters()) + { + distortionParams.push_back(val); + } + + auto outDistortion = createDistortion(Unpack(distortion->type())); + outDistortion->setParameters(distortionParams); + outDistortion->setLocked(distortion->locked()); + + outDistortions.emplace(id, outDistortion); + } +} + +void Unpack(sfmData::SharedPtrMap & outUndistortions, + const flatbuffers::Vector> * obj) +{ + if (!obj) + { + return; + } + + outUndistortions.clear(); + + for (const auto & undistortion: *obj) + { + IndexT id = undistortion->id(); + + std::vector undistortionParams; + for (const double val: *undistortion->parameters()) + { + undistortionParams.push_back(val); + } + + auto outUndistortion = createUndistortion(Unpack(undistortion->type())); + + outUndistortion->setLocked(undistortion->locked()); + outUndistortion->setParameters(undistortionParams); + outUndistortion->setSize(Unpack(*undistortion->size())); + outUndistortion->setOffset(Unpack(*undistortion->offset())); + outUndistortion->setPixelAspectRatio(undistortion->pixel_aspect_ratio()); + outUndistortion->setDesqueezed(undistortion->is_desqueezed()); + + outUndistortions.emplace(id, outUndistortion); + } +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/Landmark.hpp b/src/aliceVision/sfmDataIO/FlatBuffersIO/Landmark.hpp new file mode 100644 index 0000000000..1e4b3da4d1 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/Landmark.hpp @@ -0,0 +1,141 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include + + +namespace aliceVision { +namespace sfmDataIO { + +AliceVisionIO::V1::IMAGEDESCRIBERTYPE Pack(const feature::EImageDescriberType val) +{ + return static_cast(val); +} + +feature::EImageDescriberType Unpack(const AliceVisionIO::V1::IMAGEDESCRIBERTYPE val) +{ + return static_cast(val); +} + +/** + * @brief Convert a vec3 to a flatbuffers vec3 + * @param obj an input vec3 + * @return a flatbuffer object containing the vec2 +*/ +AliceVisionIO::Rgb Pack(const image::RGBColor & obj) +{ + return AliceVisionIO::Rgb(obj.x(), obj.y(), obj.z()); +} + +image::RGBColor Unpack(const AliceVisionIO::Rgb & obj) +{ + return image::RGBColor(obj.r(), obj.g(), obj.b()); +} + +std::map getObservations(const sfmData::Landmarks & landmarks) +{ + std::map ret; + + for (const auto & [idLandmark, landmark] : landmarks) + { + for (const auto & [idView, observation] : landmark.getObservations()) + { + Pair p = std::make_pair(idLandmark, idView); + ret[p] = &observation; + } + } + + return ret; +} + +AliceVisionIO::V1::Observation Pack(flatbuffers::FlatBufferBuilder & builder, + const sfmData::Observation & obj, + IndexT indexLandmark, + IndexT indexView) +{ + AliceVisionIO::Vec2 vec = Pack(obj.getCoordinates()); + + + return AliceVisionIO::V1::Observation(indexLandmark, + indexView, + vec, + obj.getFeatureId(), + obj.getScale()); +} + +std::vector Pack(flatbuffers::FlatBufferBuilder & builder, const std::map & obj) +{ + std::vector ret; + + for (const auto & [pair, observationPtr]: obj) + { + ret.push_back(Pack(builder, *observationPtr, pair.first, pair.second)); + } + + return ret; +} + +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, + const sfmData::Landmark & obj, + IndexT index) +{ + AliceVisionIO::Vec3 X = Pack(obj.X); + AliceVisionIO::Rgb rgb = Pack(obj.rgb); + + return AliceVisionIO::V1::CreateLandmark(builder, + index, + &X, + Pack(obj.descType), + &rgb); +} + +void Unpack(std::map & outLandmarks, + const flatbuffers::Vector> * obj) +{ + outLandmarks.clear(); + + for (const auto & landmark: *obj) + { + IndexT id = landmark->id(); + + sfmData::Landmark l(Unpack(*landmark->coordinates()), + Unpack(landmark->describer_type()), + Unpack(*landmark->rgb())); + + outLandmarks.emplace(id, l); + } +} + +void Unpack(std::map & outObservations, + const flatbuffers::Vector * obj) +{ + if (!obj) + { + return; + } + + outObservations.clear(); + + for (const auto & observation: *obj) + { + aliceVision::Pair id; + id.first = observation->id_landmark(); + id.second = observation->id_view(); + + + sfmData::Observation obs(Unpack(observation->coordinates()), + observation->id_feature(), + observation->scale()); + + outObservations.emplace(id, obs); + } +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/Rig.hpp b/src/aliceVision/sfmDataIO/FlatBuffersIO/Rig.hpp new file mode 100644 index 0000000000..73ce7b8947 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/Rig.hpp @@ -0,0 +1,99 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include + + +namespace aliceVision { +namespace sfmDataIO { + + +AliceVisionIO::V1::RIGSUBPOSESTATUS Pack(const sfmData::ERigSubPoseStatus val) +{ + return static_cast(val); +} + +sfmData::ERigSubPoseStatus Unpack(const AliceVisionIO::V1::RIGSUBPOSESTATUS val) +{ + return static_cast(val); +} + +/** + * Pack a sfmData::RigSubPose to flatbuffers + * @param builder the flatbuffers builder to use + * @param obj the subpose to serialize + * @return flatbuffers object +*/ +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, + const sfmData::RigSubPose & obj) +{ + AliceVisionIO::Vec3 packedRotation = Pack(SO3::logm(obj.pose.rotation())); + AliceVisionIO::Vec3 packedCenter = Pack(obj.pose.center()); + + return AliceVisionIO::V1::CreateRigSubPose(builder, + Pack(obj.status), + &packedRotation, + &packedCenter); +} + + +/** + * Pack a sfmData::Rig to flatbuffers + * @param builder the flatbuffers builder to use + * @param obj the Rig to serialize + * @param index the Rig id + * @return flatbuffers object +*/ +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, + const sfmData::Rig & obj, + IndexT index) +{ + std::vector> sub_poses; + + for (const auto & item : obj.getSubPoses()) + { + sub_poses.push_back(Pack(builder, item)); + } + + return AliceVisionIO::V1::CreateRigDirect(builder, + index, + &sub_poses); +} + +void Unpack(sfmData::Rigs & outRigs, const flatbuffers::Vector> * obj) +{ + if (!obj) + { + return; + } + + outRigs.clear(); + + for (const auto & rig: *obj) + { + sfmData::Rig outRig; + + for (const auto & subpose: *rig->sub_poses()) + { + sfmData::RigSubPose rsp; + rsp.status = Unpack(subpose->status()); + + Eigen::Matrix3d R = SO3::expm(Unpack(*subpose->rotation())); + rsp.pose = geometry::Pose3(R, Unpack(*subpose->center())); + + outRig.getSubPoses().push_back(rsp); + } + + outRigs[rig->id()] = outRig; + } + +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/Std.hpp b/src/aliceVision/sfmDataIO/FlatBuffersIO/Std.hpp new file mode 100644 index 0000000000..148e098c6a --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/Std.hpp @@ -0,0 +1,195 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include +#include +#include + + +namespace aliceVision { +namespace sfmDataIO { + + +/** + * @brief Convert a flatbuffers string to a std::string + * @param obj an input flatbuffers string + * @return an std::string +*/ +std::string Unpack(const flatbuffers::String & obj) +{ + return obj.str(); +} + +/** + * @brief Convert a std::string to a flatbuffers string + * @param builder an instance of FlatBufferBuilder used to do the computation + * @param obj an input std::string + * @return a flatbuffers string +*/ +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, const std::string & obj) +{ + return builder.CreateString(obj.c_str()); +} + +/** + * @brief enable serialization of shared_ptr + * @param builder an instance of FlatBufferBuilder used to do the computation + * @param obj an input shared_ptr containing a pointer to T + * @param index T object index +*/ +template +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, + const std::shared_ptr & obj, + IndexT index) +{ + return Pack(builder, *obj, index); +} + +/** + * @brief enable serialization of containers + * @param builder an instance of FlatBufferBuilder used to do the computation + * @param obj an input associative container + * @return a vector of flatbuffers objects +*/ +template +std::vector> Pack(flatbuffers::FlatBufferBuilder & builder, const std::vector & obj) +{ + std::vector> ret; + + for (const auto & item: obj) + { + ret.push_back(Pack(builder, item)); + } + + return ret; +} + +/** + * @brief enable serialization of containers + * @param builder an instance of FlatBufferBuilder used to do the computation + * @param obj an input associative container + * @return a vector of flatbuffers objects +*/ +template +std::vector> Pack(flatbuffers::FlatBufferBuilder & builder, const std::vector> & obj) +{ + std::vector> ret; + + for (const auto & item: obj) + { + ret.push_back(Pack(builder, item)); + } + + return ret; +} + +/** + * @brief enable serialization of containers + * @param builder an instance of FlatBufferBuilder used to do the computation + * @param obj an input associative container + * @return a vector of flatbuffers objects +*/ +template +std::vector> Pack(flatbuffers::FlatBufferBuilder & builder, + const std::map & obj) +{ + std::vector> ret; + + for (const auto & [index, item]: obj) + { + ret.push_back(Pack(builder, item, index)); + } + + return ret; +} + +/** + * @brief enable serialization of containers + * @param builder an instance of FlatBufferBuilder used to do the computation + * @param obj an input associative container + * @return a vector of flatbuffers objects +*/ +template +std::vector> Pack(flatbuffers::FlatBufferBuilder & builder, + const std::map> & obj) +{ + std::vector> ret; + + for (const auto & [index, vec]: obj) + { + for (const auto & item : vec) + { + ret.push_back(Pack(builder, item, index)); + } + } + + return ret; +} + +/** + * @brief enable serialization of containers + * @param builder an instance of FlatBufferBuilder used to do the computation + * @param obj an input associative container + * @return a vector of flatbuffers objects +*/ +template +std::vector> Pack(flatbuffers::FlatBufferBuilder & builder, + const std::map> & obj) +{ + std::vector> ret; + + for (const auto & [index, item]: obj) + { + ret.push_back(Pack(builder, item, index)); + } + + return ret; +} + +/** + * @brief enable serialization of containers + * @param builder an instance of FlatBufferBuilder used to do the computation + * @param obj an input associative container + * @return a vector of flatbuffers objects +*/ +template +std::vector<::flatbuffers::Offset> Pack(::flatbuffers::FlatBufferBuilder & builder, + const sfmData::SharedPtrMap & obj) +{ + std::vector> ret; + + for (const auto & [index, item]: obj) + { + ret.push_back(Pack(builder, item, index)); + } + + return ret; +} + +/** + * @brief enable deserialization of containers + * @param obj an input flatbuffers vector of string (pointer to the vector) + * @return a vector of string +*/ +std::vector Unpack(const flatbuffers::Vector> * obj) +{ + std::vector ret; + + for (const auto item: *obj) + { + ret.push_back(item->str()); + } + + return ret; +} + + + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/SurveyPoint.hpp b/src/aliceVision/sfmDataIO/FlatBuffersIO/SurveyPoint.hpp new file mode 100644 index 0000000000..631df37c92 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/SurveyPoint.hpp @@ -0,0 +1,49 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include + +namespace aliceVision { +namespace sfmDataIO { + + +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, const sfmData::SurveyPoint & obj, IndexT index) +{ + AliceVisionIO::Vec3 point = Pack(obj.point3d); + AliceVisionIO::Vec2 survey = Pack(obj.survey); + + return AliceVisionIO::V1::CreateSurveyPoint(builder, + index, + &point, + &survey); +} + +void Unpack(sfmData::SurveyPoints & outSurveys, + const flatbuffers::Vector> * obj) +{ + if (!obj) + { + return; + } + + outSurveys.clear(); + + for (const auto & surveyPoint : *obj) + { + IndexT id = surveyPoint->id_view(); + + sfmData::SurveyPoint sp(Unpack(*surveyPoint->point3d()), + Unpack(*surveyPoint->survey())); + + outSurveys[id].push_back(sp); + } +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/View.hpp b/src/aliceVision/sfmDataIO/FlatBuffersIO/View.hpp new file mode 100644 index 0000000000..db1951e988 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/View.hpp @@ -0,0 +1,123 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include + +namespace aliceVision { +namespace sfmDataIO { + +std::map getImagesInfos(const sfmData::Views & views) +{ + std::map ret; + + for (const auto & [idView, view] : views.valueRange()) + { + ret.insert({idView, view.getImageInfo()}); + } + + return ret; +} + +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, const std::pair & obj) +{ + return AliceVisionIO::V1::CreateMetaDataItemDirect(builder, obj.first.c_str(), obj.second.c_str()); +} + +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, const sfmData::ImageInfo & obj, IndexT index) +{ + std::vector> packedMetadatas; + + for (const auto & pair: obj.getMetadata()) + { + packedMetadatas.push_back(Pack(builder, pair)); + } + + return AliceVisionIO::V1::CreateImageDirect(builder, + index, + obj.getImagePath().c_str(), + obj.getWidth(), + obj.getHeight(), + &packedMetadatas); +} + +flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder & builder, const sfmData::View & obj, IndexT index) +{ + return AliceVisionIO::V1::CreateViewDirect(builder, + index, + obj.getIntrinsicId(), + obj.getPoseId(), + obj.getRigId(), + obj.getSubPoseId(), + obj.getFrameId(), + obj.getResectionId(), + obj.isPoseIndependant(), + &obj.getAncestors() + ); +} + +void Unpack(sfmData::Views & outViews, const flatbuffers::Vector> * obj) +{ + if (!obj) + { + return; + } + + outViews.clear(); + + for (const auto & view: *obj) + { + sfmData::View outView; + + IndexT id = view->id(); + + outView.setViewId(id); + outView.setIntrinsicId(view->intrinsic()); + outView.setPoseId(view->pose()); + outView.setRigAndSubPoseId(view->rig(), view->sub_pose()); + outView.setFrameId(view->frame()); + outView.setResectionId(view->resection()); + outView.setIndependantPose(view->is_pose_independent()); + + for (const uint item: *view->ancestors()) + { + outView.addAncestor(item); + } + + outViews.assign(id, outView); + } +} + +void Unpack(std::map & outImages, const flatbuffers::Vector> * obj) +{ + if (!obj) + { + return; + } + + outImages.clear(); + + for (const auto & image: *obj) + { + IndexT id = image->id(); + + std::map outMetadata; + for (const auto & metadata: *image->metadatas()) + { + outMetadata[metadata->key()->str()] = metadata->value()->str(); + } + + outImages[id] = std::make_shared(image->image_path()->str(), + image->width(), + image->height(), + outMetadata); + } +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/CameraPose.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/CameraPose.fbs new file mode 100644 index 0000000000..ba16591782 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/CameraPose.fbs @@ -0,0 +1,12 @@ +include "Vec.fbs"; + +namespace AliceVisionIO.V1; + +table CameraPose { + id: uint; + rotation: Vec3; + center: Vec3; + locked: bool; + rotation_only: bool; + removable: bool; +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Distortion.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Distortion.fbs new file mode 100644 index 0000000000..b190bb5d05 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Distortion.fbs @@ -0,0 +1,22 @@ +include "Vec.fbs"; + +namespace AliceVisionIO.V1; + +enum TYPEDISTORTION : byte +{ + DISTORTION_NONE = 0, + DISTORTION_RADIALK1 = 1, + DISTORTION_RADIALK3 = 2, + DISTORTION_RADIALK3PT = 3, + DISTORTION_BROWN = 4, + DISTORTION_FISHEYE = 5, + DISTORTION_FISHEYE1 = 6, +} + +table Distortion +{ + id: uint; + type: TYPEDISTORTION; + locked: bool; + parameters: [double]; +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Image.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Image.fbs new file mode 100644 index 0000000000..a292db6c6b --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Image.fbs @@ -0,0 +1,16 @@ +namespace AliceVisionIO.V1; + +table MetaDataItem +{ + key: string; + value: string; +} + +table Image +{ + id: uint; + image_path: string; + width: uint; + height: uint; + metadatas : [MetaDataItem]; +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Intrinsic.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Intrinsic.fbs new file mode 100644 index 0000000000..1353b50252 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Intrinsic.fbs @@ -0,0 +1,42 @@ +include "Vec.fbs"; + +namespace AliceVisionIO.V1; + +enum INITMODE: byte +{ + NONE = 0, + CALIBRATED = 1, + ESTIMATED = 2, + UNKNOWN = 3, +} + +enum TYPEINTRINSIC: byte +{ + UNKNOWN = 0, + PINHOLE_CAMERA = 1, + EQUIDISTANT_CAMERA = 2, + EQUIRECTANGULAR_CAMERA = 4 +} + +table Intrinsic +{ + id: uint; + type: TYPEINTRINSIC; + initialization_mode: INITMODE; + locked: bool; + width: uint; + height: uint; + sensor_width: float; + sensor_height: float; + serial_number: string; + focal_length: double; + pixel_ratio: double; + initial_focal_length: double; + offset: Vec2; + ratio_locked: bool; + offset_locked: bool; + scale_locked: bool; + distortion_initialization_mode: INITMODE; + circle_radius: double; + circle_center: Vec2; +} diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Landmark.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Landmark.fbs new file mode 100644 index 0000000000..8b83f7329b --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Landmark.fbs @@ -0,0 +1,30 @@ +include "Vec.fbs"; + +namespace AliceVisionIO.V1; + +enum IMAGEDESCRIBERTYPE : byte +{ + UNKNOWN = 0, + UNINITIALIZED = 1, + SIFT = 10, + SIFT_FLOAT = 11, + SIFT_UPRIGHT = 12, + DSPSIFT = 13, + AKAZE = 20, + AKAZE_LIOP = 21, + AKAZE_MLDB = 22, + CCTAG3 = 30, + CCTAG4 = 31, + SIFT_OCV = 40, + AKAZE_OCV = 41, + APRILTAG16H5 = 50 +} + +table Landmark +{ + id: uint; + coordinates: Vec3; + describer_type: IMAGEDESCRIBERTYPE; + rgb: Rgb; +} + diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Observation.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Observation.fbs new file mode 100644 index 0000000000..4d3e9e319d --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Observation.fbs @@ -0,0 +1,12 @@ +include "Vec.fbs"; + +namespace AliceVisionIO.V1; + +struct Observation +{ + id_landmark: uint; + id_view: uint; + coordinates: Vec2; + id_feature: uint; + scale: float; +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Rig.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Rig.fbs new file mode 100644 index 0000000000..b4b91b7f9f --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Rig.fbs @@ -0,0 +1,23 @@ +include "CameraPose.fbs"; + +namespace AliceVisionIO.V1; + +enum RIGSUBPOSESTATUS : byte +{ + UNINITIALIZED = 0, + ESTIMATED = 1, + CONSTANT = 2 +} + +table RigSubPose +{ + status: RIGSUBPOSESTATUS; + rotation: AliceVisionIO.Vec3; + center: AliceVisionIO.Vec3; +} + +table Rig +{ + id: uint; + sub_poses: [RigSubPose]; +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Root.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Root.fbs new file mode 100644 index 0000000000..0de1244b01 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Root.fbs @@ -0,0 +1,19 @@ +include "SfmData.fbs"; + +namespace AliceVisionIO.V1; + +struct Version +{ + major: uint; + minor: uint; + revision: uint; +} + +table Root +{ + version: Version; + sfm_data: SfmData; +} + +root_type Root; +file_identifier "ASFM"; \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/SfmData.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/SfmData.fbs new file mode 100644 index 0000000000..a703c204e3 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/SfmData.fbs @@ -0,0 +1,29 @@ +include "View.fbs"; +include "CameraPose.fbs"; +include "Landmark.fbs"; +include "Observation.fbs"; +include "Distortion.fbs"; +include "Undistortion.fbs"; +include "Intrinsic.fbs"; +include "Rig.fbs"; +include "Image.fbs"; +include "SurveyPoint.fbs"; + +namespace AliceVisionIO.V1; + +table SfmData +{ + features_folders: [string]; + matches_folders: [string]; + + views: [View]; + images: [Image]; + poses: [CameraPose]; + landmarks: [Landmark]; + observations: [Observation]; + distortions: [Distortion]; + undistortions: [Undistortion]; + intrinsics: [Intrinsic]; + rigs: [Rig]; + surveys: [SurveyPoint]; +} diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/SurveyPoint.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/SurveyPoint.fbs new file mode 100644 index 0000000000..62691834ad --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/SurveyPoint.fbs @@ -0,0 +1,10 @@ +include "Vec.fbs"; + +namespace AliceVisionIO.V1; + +table SurveyPoint +{ + id_view: uint; + point3d: Vec3; + survey: Vec2; +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Undistortion.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Undistortion.fbs new file mode 100644 index 0000000000..0c58ce3821 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Undistortion.fbs @@ -0,0 +1,24 @@ +include "Vec.fbs"; + +namespace AliceVisionIO.V1; + +enum TYPEUNDISTORTION : byte +{ + UNDISTORTION_NONE = 0, + UNDISTORTION_RADIALK3 = 1, + UNDISTORTION_3DEANAMORPHIC4 = 2, + UNDISTORTION_3DECLASSICLD = 3, + UNDISTORTION_3DERADIAL4 = 4, +} + +table Undistortion +{ + id: uint; + type: TYPEUNDISTORTION; + locked: bool; + parameters: [double]; + size: AliceVisionIO.Vec2; + offset: AliceVisionIO.Vec2; + pixel_aspect_ratio: double; + is_desqueezed: bool; +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Vec.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Vec.fbs new file mode 100644 index 0000000000..6b0d726f56 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/Vec.fbs @@ -0,0 +1,21 @@ +namespace AliceVisionIO; + +struct Vec3 +{ + x: float; + y: float; + z: float; +} + +struct Vec2 +{ + x: float; + y: float; +} + +struct Rgb +{ + r: ubyte; + g: ubyte; + b: ubyte; +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/View.fbs b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/View.fbs new file mode 100644 index 0000000000..1154cb06a9 --- /dev/null +++ b/src/aliceVision/sfmDataIO/FlatBuffersIO/schemas/View.fbs @@ -0,0 +1,14 @@ +namespace AliceVisionIO.V1; + +table View +{ + id: uint; + intrinsic: uint; + pose: uint; + rig: uint; + sub_pose: uint; + frame: uint; + resection: uint; + is_pose_independent: uint; + ancestors: [uint]; +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/MappedFile.cpp b/src/aliceVision/sfmDataIO/MappedFile.cpp new file mode 100644 index 0000000000..a49c251148 --- /dev/null +++ b/src/aliceVision/sfmDataIO/MappedFile.cpp @@ -0,0 +1,185 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#include + +#ifdef _WIN32 + #include +#else + #include + #include + #include + #include +#endif + +namespace aliceVision { +namespace sfmDataIO { + +MemoryMappedFile::MemoryMappedFile() + : _data(nullptr) + , _size(0) +#ifdef _WIN32 + , _fileHandle(INVALID_HANDLE_VALUE) + , _mapHandle(INVALID_HANDLE_VALUE) +#else + , _fileDescriptor(-1) +#endif +{ +} + +MemoryMappedFile::~MemoryMappedFile() +{ + close(); +} + +bool MemoryMappedFile::open(const std::string& filename) { + if (isOpen()) + { + close(); + } + +#ifdef _WIN32 + // Windows implementation - read-only + _fileHandle = CreateFileA(filename.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if (_fileHandle == INVALID_HANDLE_VALUE) { + return false; + } + + // Get file size + LARGE_INTEGER fileSize; + if (!GetFileSizeEx(_fileHandle, &fileSize)) + { + cleanup(); + return false; + } + + _size = static_cast(fileSize.QuadPart); + + if (_size == 0) + { + cleanup(); + return false; + } + + // Create file mapping - read-only + _mapHandle = CreateFileMappingA(_fileHandle, nullptr, PAGE_READONLY, 0, 0, nullptr); + + if (_mapHandle == INVALID_HANDLE_VALUE) + { + cleanup(); + return false; + } + + // Map view of file - read-only + _data = MapViewOfFile(_mapHandle, FILE_MAP_READ, 0, 0, 0); + + if (_data == nullptr) + { + cleanup(); + return false; + } + +#else + // Unix/Linux implementation - read-only + _fileDescriptor = ::open(filename.c_str(), O_RDONLY); + + if (_fileDescriptor == -1) + { + return false; + } + + // Get file size + struct stat fileStats; + if (fstat(_fileDescriptor, &fileStats) == -1) + { + cleanup(); + return false; + } + + _size = static_cast(fileStats.st_size); + + if (_size == 0) + { + cleanup(); + return false; + } + + // Map the file - read-only + _data = mmap(nullptr, _size, PROT_READ, MAP_SHARED, _fileDescriptor, 0); + + if (_data == MAP_FAILED) + { + _data = nullptr; + cleanup(); + return false; + } +#endif + + return true; +} + +void MemoryMappedFile::close() +{ + cleanup(); + _size = 0; +} + +bool MemoryMappedFile::isOpen() const +{ + return _data != nullptr; +} + +const void* MemoryMappedFile::data() const +{ + return _data; +} + +size_t MemoryMappedFile::size() const +{ + return _size; +} + +void MemoryMappedFile::cleanup() +{ +#ifdef _WIN32 + if (_data != nullptr) { + UnmapViewOfFile(_data); + _data = nullptr; + } + + if (_mapHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(_mapHandle); + _mapHandle = INVALID_HANDLE_VALUE; + } + + if (_fileHandle != INVALID_HANDLE_VALUE) + { + CloseHandle(_fileHandle); + _fileHandle = INVALID_HANDLE_VALUE; + } +#else + if (_data != nullptr) { + munmap(_data, _size); + _data = nullptr; + } + + if (_fileDescriptor != -1) { + ::close(_fileDescriptor); + _fileDescriptor = -1; + } +#endif +} + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/MappedFile.hpp b/src/aliceVision/sfmDataIO/MappedFile.hpp new file mode 100644 index 0000000000..719f46c76b --- /dev/null +++ b/src/aliceVision/sfmDataIO/MappedFile.hpp @@ -0,0 +1,79 @@ +// This file is part of the AliceVision project. +// Copyright (c) 2025 AliceVision contributors. +// This Source Code Form is subject to the terms of the Mozilla Public License, +// v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include + +#ifdef _WIN32 + #include +#endif + +namespace aliceVision { +namespace sfmDataIO { + +class MemoryMappedFile { +public: + MemoryMappedFile(); + ~MemoryMappedFile(); + MemoryMappedFile(const MemoryMappedFile&) = delete; + MemoryMappedFile& operator=(const MemoryMappedFile&) = delete; + + /** + * @brief Open a file for read-only memory mapping + * @param filename path to the file to open + * @return true on success + */ + bool open(const std::string& filename); + + /** + * @brief Close the file + */ + void close(); + + /** + * @brief Is a file opened + * @return true if opened + */ + bool isOpen() const; + + /** + * @brief Get a void pointer to the mapped data + * @return a pointer to the file's data + */ + const void* data() const; + + /** + * @brief Get a casted pointer to the mapped data + * @return a pointer to the file's data + */ + template + const T* data() const { + return static_cast(data()); + } + + /** + * @brief Get the byte size of the mapped data + * @return a size in bytes + */ + size_t size() const; + +private: + void cleanup(); + + void * _data; + size_t _size; + +#ifdef _WIN32 + HANDLE _fileHandle; + HANDLE _mapHandle; +#else + int _fileDescriptor; +#endif +}; + +} +} \ No newline at end of file diff --git a/src/aliceVision/sfmDataIO/sfmDataIO.cpp b/src/aliceVision/sfmDataIO/sfmDataIO.cpp index 567b255838..7b869d1ad1 100644 --- a/src/aliceVision/sfmDataIO/sfmDataIO.cpp +++ b/src/aliceVision/sfmDataIO/sfmDataIO.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) @@ -115,6 +116,10 @@ bool load(aliceVision::sfmData::SfMData& sfmData, const std::string& filename, E { status = loadJSON(sfmData, filename, partFlag); } + else if (extension == ".fbd") // FlatBuffer File + { + status = loadFlatBuffers(sfmData, filename, partFlag); + } else if (extension == ".abc") // Alembic { #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) @@ -159,6 +164,10 @@ bool save(const aliceVision::sfmData::SfMData& sfmData, const std::string& filen { status = saveJSON(sfmData, tmpPath, partFlag); } + else if (extension == ".fbd") // Flat buffers + { + status = saveFlatBuffers(sfmData, tmpPath, partFlag); + } else if (extension == ".ply") // Polygon File { status = savePLY(sfmData, tmpPath, partFlag); diff --git a/src/aliceVision/sfmDataIO/sfmDataIO.hpp b/src/aliceVision/sfmDataIO/sfmDataIO.hpp index 74e5054819..3067a25ed3 100644 --- a/src/aliceVision/sfmDataIO/sfmDataIO.hpp +++ b/src/aliceVision/sfmDataIO/sfmDataIO.hpp @@ -10,9 +10,9 @@ #include #include -#define ALICEVISION_SFMDATAIO_VERSION_MAJOR 1 -#define ALICEVISION_SFMDATAIO_VERSION_MINOR 2 -#define ALICEVISION_SFMDATAIO_VERSION_REVISION 12 +#define ALICEVISION_SFMDATAIO_VERSION_MAJOR 1 //1 char max +#define ALICEVISION_SFMDATAIO_VERSION_MINOR 2 //1 char max +#define ALICEVISION_SFMDATAIO_VERSION_REVISION 12 //2 chars max // AliceVision version as a string; for example "0.9.0". #define ALICEVISION_SFMDATAIO_VERSION_STRING \ diff --git a/src/aliceVision/sfmDataIO/sfmDataIO_test.cpp b/src/aliceVision/sfmDataIO/sfmDataIO_test.cpp index 22b1569e92..b8baefbcb2 100644 --- a/src/aliceVision/sfmDataIO/sfmDataIO_test.cpp +++ b/src/aliceVision/sfmDataIO/sfmDataIO_test.cpp @@ -76,7 +76,7 @@ sfmData::SfMData createTestScene(std::size_t viewsCount = 2, std::size_t observa BOOST_AUTO_TEST_CASE(SfMData_IO_SAVE_LOAD) { - std::vector ext_Type = {"sfm", "json"}; + std::vector ext_Type = {"sfm", "json", "fbd"}; #if ALICEVISION_IS_DEFINED(ALICEVISION_HAVE_ALEMBIC) ext_Type.push_back("abc"); diff --git a/src/software/convert/main_convertSfMFormat.cpp b/src/software/convert/main_convertSfMFormat.cpp index dd829a0e69..29f5429341 100644 --- a/src/software/convert/main_convertSfMFormat.cpp +++ b/src/software/convert/main_convertSfMFormat.cpp @@ -104,7 +104,7 @@ int aliceVision_main(int argc, char** argv) // load input SfMData scene sfmData::SfMData sfmData; - if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData::ALL)) + if (!sfmDataIO::load(sfmData, sfmDataFilename, sfmDataIO::ESfMData(flags))) { ALICEVISION_LOG_ERROR("The input SfMData file '" << sfmDataFilename << "' cannot be read"); return EXIT_FAILURE;