├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── README.md
├── catkin_ws
└── src
│ ├── CMakeLists.txt
│ └── sgloop_ros
│ ├── CMakeLists.txt
│ ├── include
│ ├── SlidingWindow.h
│ ├── Visualization.h
│ ├── communication
│ │ └── Communication.h
│ ├── registration
│ │ ├── Prune.h
│ │ └── robustPoseAvg.h
│ └── utility
│ │ └── CameraPoseVisualization.h
│ ├── launch
│ ├── mapping.rviz
│ ├── render_our_sgslam.launch
│ ├── render_semantic_map.launch
│ ├── semantic_mapping.launch
│ ├── slabim_mapping.launch
│ └── visualize.launch
│ ├── msg
│ ├── CoarseGraph.msg
│ └── DenseGraph.msg
│ ├── package.xml
│ └── src
│ ├── MappingNode.cpp
│ ├── RenderNode.cpp
│ ├── Visualization.cpp
│ ├── communication
│ └── Communication.cpp
│ ├── registration
│ ├── Prune.cpp
│ └── robustPoseAvg.cpp
│ └── utility
│ └── CameraPoseVisualization.cpp
├── cmake
├── eigen.cmake
├── glog.cmake
├── gtsam.cmake
├── jsoncpp.cmake
├── open3d.cmake
└── opencv.cmake
├── config
├── fusion_portable.yaml
├── matterport.yaml
├── realsense.yaml
├── render_3rscan.yaml
├── rio.yaml
├── scannet.yaml
└── slabim.yaml
├── doc
├── DATA.md
├── rviz_gui.png
├── seng_map_720p.gif
├── system.png
└── ust_atrium_light.gif
├── measurement_model
├── bayesian
│ ├── detection_likelihood.png
│ ├── likelihood_matrix.png
│ └── ram_likelihood.png
├── bayesian_model.txt
└── hardcode
│ └── likelihood_matrix.png
├── scripts
├── process_slabim_pcd.py
├── rerun_visualize.py
├── uncompress_data.py
└── utils.py
└── src
├── CMakeLists.txt
├── Common.h
├── GraphMatch.cpp
├── IntegrateInstanceMap.cpp
├── IntegrateRGBD.cpp
├── cluster
├── PoseGraph.cpp
└── PoseGraph.h
├── mapping
├── BayesianLabel.h
├── Detection.cpp
├── Detection.h
├── Instance.cpp
├── Instance.h
├── SemanticDict.h
├── SemanticMapping.cpp
├── SemanticMapping.h
├── SubVolume.cpp
└── SubVolume.h
├── sgloop
├── BertBow.cpp
├── BertBow.h
├── Graph.cpp
├── Graph.h
├── Initialization.h
├── LoopDetector.cpp
├── LoopDetector.h
├── SGNet.cpp
├── SGNet.h
├── ShapeEncoder.cpp
└── ShapeEncoder.h
├── tokenizer
├── CMakeLists.txt
├── basic_string_util.h
├── bert_tokenizer.cc
├── bert_tokenizer.h
├── logging.h
├── source
│ ├── utf8.h
│ └── utf8
│ │ ├── checked.h
│ │ ├── core.h
│ │ ├── cpp11.h
│ │ ├── cpp17.h
│ │ ├── cpp20.h
│ │ └── unchecked.h
├── text_tokenizer.cc
└── text_tokenizer.h
└── tools
├── Color.h
├── Eval.h
├── IO.cpp
├── IO.h
├── TicToc.h
├── Tools.h
├── Utility.cpp
├── Utility.h
├── Visualization.cpp
└── g3reg_api.h
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Ignore folders
3 | python/__pycache__/
4 | measurement_model/prompt
5 | build
6 | .history
7 | torchscript
8 | catkin_ws/devel
9 | catkin_ws/build
10 | catkin_ws/src/sgloop_ros/thirdparty
11 | catkin_ws/.catkin_workspace
12 | install
13 | eval/fmfusion
14 | tmp
15 | hydra_lcd
16 | images
17 | data
18 | output
19 |
20 | # Ignore files
21 | *.pyc
22 | *.zip
23 | *.npy
24 | .idea
25 | .vscode
26 | datasets
27 | Log
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "catkin_ws/src/perception_open3d"]
2 | path = catkin_ws/src/perception_open3d
3 | url = git@github.com:ros-perception/perception_open3d.git
4 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.18)
2 |
3 | project(FmFusion LANGUAGES C CXX)
4 |
5 | #### Options ####
6 | option(LOOP_DETECTION OFF)
7 | option(INSTALL_FMFUSION ON) # Install as static library
8 | option(RUN_HYDRA OFF)
9 | #################
10 |
11 | set(ALL_TARGET_LIBRARIES "")
12 | include(cmake/eigen.cmake)
13 | include(cmake/glog.cmake)
14 | include(cmake/opencv.cmake)
15 | include(cmake/open3d.cmake)
16 | include(cmake/jsoncpp.cmake)
17 |
18 | set(PROJECT_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
19 | set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/install)
20 | message(STATUS "Project root directory: ${PROJECT_ROOT_DIR}")
21 | message(STATUS "CMake install prefix: ${CMAKE_INSTALL_PREFIX}")
22 | add_subdirectory(src)
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
FM-Fusion: Instance-aware Semantic Mapping Boosted by Vision-Language Foundation Models
3 | IEEE RA-L 2024
4 |
5 | Chuhao Liu 1 ,
6 | Ke Wang 2,* ,
7 | Jieqi Shi 1 ,
8 | Zhijian Qiao 1 , and
9 | Shaojie Shen 1
10 |
11 |
12 | 1 HKUST Aerial Robotics Group
13 | 2 Chang'an University, China
14 |
15 |
16 | * Corresponding Author
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | ### News
28 | * [27th Nov 2024] Support semantic reconstruction using [SLABIM](https://github.com/HKUST-Aerial-Robotics/SLABIM) dataset.
29 | * [25th Oct 2024] Publish code and RGB-D sequences from ScanNet and SgSlam.
30 | * [5th Jan 2024] Paper accepted by IEEE RA-L.
31 |
32 | **FM-Fusion** utilizes [RAM](https://github.com/xinyu1205/recognize-anything), [GroundingDINO](https://github.com/IDEA-Research/GroundingDINO) and [SAM](https://github.com/facebookresearch/segment-anything) to reconstruct an instance-aware semantic map. Boosted by the vision foundational models, FM-Fusion can reconstruct semantic instances in real-world cluttered indoor environments.
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | The following instruction explains how to run FM-Fusion on our RGB-D sequences or ScanNet sequences. If you find its useful, please cite our paper.
41 |
42 | ```latex
43 | @article{
44 | title={{FM-Fusion}: Instance-aware Semantic Mapping Boosted by Vision-Language Foundation Models},
45 | author={Liu, Chuhao and Wang, Ke and Shi, Jieqi and Qiao, Zhijian and Shen, Shaojie},
46 | journal={IEEE Robotics and Automation Letters(RA-L)},
47 | year={2024},
48 | volume={9},
49 | number={3},
50 | pages={2232-2239}
51 | }
52 | ```
53 |
54 | ## Tabel of Contents
55 | 1. Install
56 | 2. Download Data
57 | 3. Run Instance-aware Semantic Mapping
58 | 4. Use it in Your RGB-D Camera
59 | 5. Acknowledge
60 | 6. License
61 |
62 | ## 1. Install
63 | Install dependency packages from Ubuntu Source
64 | ```
65 | sudo apt-get install libboost-dev libomp-dev libeigen3-dev
66 | ```
67 | Install Open3D from its source code.([Install Tutorial](https://www.open3d.org/docs/release/compilation.html#compilation))
68 | ```
69 | git clone https://github.com/isl-org/Open3D
70 | cd Open3D
71 | mkdir build && cd build
72 | cmake -DBUILD_SHARED_LIBS=ON ..
73 | make -j12
74 | sudo make install
75 | ```
76 | Follow the official tutorials to install [OpenCV](https://docs.opencv.org/4.x/d7/d9f/tutorial_linux_install.html), [GLOG](https://github.com/google/glog), [jsoncpp](https://github.com/open-source-parsers/jsoncpp/blob/master/README.md).
77 | To make it compatible with ROS, please install ```OpenCV 3.4.xx```.
78 |
79 | Clone and compile FM-Fusion,
80 | ```
81 | git clone git@github.com:HKUST-Aerial-Robotics/FM-Fusion.git
82 | mkdir build && cd build
83 | cmake .. -DINSTALL_FMFUSION=ON
84 | make -j12
85 | make install
86 | ```
87 |
88 | Install the ROS node program, which renders the semantic instance map in Rviz. Install ROS platform following its [official guidance](http://wiki.ros.org/noetic/Installation/Ubuntu). Then, build the ros node we provide,
89 | ```
90 | git submodule update --init --recursive
91 | cd catkin_ws && catkin_make
92 | source devel/setup.bash
93 | ```
94 |
95 | ## 2. Download Data
96 | We provide two datasets to evaluate: SgSlam (captured using Intel [Realsense L-515](https://www.intelrealsense.com/lidar-camera-l515/)) and ScanNet. Their sequences can be downloaded:
97 | * [SgSlam_OneDrive](https://hkustconnect-my.sharepoint.com/:f:/g/personal/cliuci_connect_ust_hk/EnIjnIl7a2NBtioZFfireM4B_PnZcIpwcB-P2-Fnh3FEPg?e=BKsgLQ) or [SgSlam_Nutstore(坚果云)](https://www.jianguoyun.com/p/DSM4iw0Q4cyEDRjUvekFIAA).
98 | * [ScanNet_OneDrive](https://hkustconnect-my.sharepoint.com/:f:/g/personal/cliuci_connect_ust_hk/EhUu2zzwUGNKq8h2yUGHIawBpC8EI73YB_GTG9BUXXBoVA?e=o4TfsA) or [ScanNet_NutStore(坚果云)](https://www.jianguoyun.com/p/DVuHdogQ4cyEDRjQvekFIAA).
99 |
100 | Please check [data format](doc/DATA.md) for the illustration about data in each sequence.
101 | After download the ```scans``` folder in each dataset, go to [uncompress_data.py](scripts/uncompress_data.py) and set the data directories to your local directories. Then, un-compress the sequence data.
102 | ```
103 | python scripts/uncompress_data.py
104 | ```
105 |
106 | ## 3. Run Instance-aware Semantic Mapping
107 | #### a. Run with Rviz.
108 |
109 | In ```launch/semantic_mapping.launch```, set the directories to your local dataset directories. Then, launch Rviz and the ROS node,
110 | ```
111 | roslaunch sgloop_ros visualize.launch
112 | roslaunch sgloop_ros semantic_mapping.launch
113 | ```
114 |
115 |
116 |
117 |
118 | It should incremental reconstruct the semantic map and render the results on Rviz. At the end of the sequence, the program save the output results, where the output format is illustrated in the [data format](doc/DATA.md). To visualize the results that are previously reconstructed, open ```launch/render_semantic_map.launch``` and set the ```result_folder``` directory accordingly. Then,
119 | ```
120 | roslaunch sgloop_ros render_semantic_map.launch
121 | ```
122 |
123 | ***Tips***: If you are running the program on a remote server, you can utilize the [ROS across machine](http://wiki.ros.org/ROS/Tutorials/MultipleMachines) function. After set the ```rosmaster``` following the ROS tutorial, you can launch ```visualize.launch``` at your local machine and ```semantic_mapping.launch``` at the server. So, you can still visualize the result on your local machine.
124 |
125 | #### b. Run without visualization.
126 |
127 | If you do not need the ROS node to visualize, you can skip its install in the above instruction. Then, simply run the C++ executable program and the results will be saved at ```${SGSLAM_DATAROOT}/output```. The output directory can be set before run the program.
128 | ```
129 | ./build/src/IntegrateInstanceMap --config config/realsense.yaml --root ${SGSLAM_DATAROOT}/scans/ab0201_03a --output ${SGSLAM_DATAROOT}/output
130 | ```
131 |
132 | ## 4. Use it in Your RGB-D Camera
133 | In ```SgSlam``` dataset, we use Intel Realsense-D515 camera and DJI A3 flight controller to collect data sequence Details of the hardware suite can be found in this [paper](https://arxiv.org/abs/2201.03312). You can also collect your own dataset using a similar hardware suite.
134 | #### a. Prepare RGB-D and Camera poses.
135 | We use [VINS-Mono](https://github.com/HKUST-Aerial-Robotics/VINS-Mono) to compute visual-inertial odometry (VIO). We save the camera poses of its keyframes in a ```pose``` folder.
136 |
137 | #### b. Run [RAM](https://github.com/xinyu1205/recognize-anything), [GroundingDINO](https://github.com/IDEA-Research/GroundingDINO) and [SAM](https://github.com/facebookresearch/segment-anything).
138 |
139 | The three models are combined to run in [Grounded-SAM](https://github.com/IDEA-Research/Grounded-Segment-Anything). Please find our adopted Grounded-SAM [here](https://github.com/glennliu/Grounded-Segment-Anything). It should generate a ```prediction``` folder as explained in [data format](DATA.md). Then, you can run the semantic mapping on your dataset.
140 |
141 | ## 5. Acknowledge
142 | The hardware used in SgSlam is supported by [Luqi Wang](https://lwangax.wordpress.com). The lidar-camera hardware used in [SLABIM](https://github.com/HKUST-Aerial-Robotics/SLABIM) is supported by [Skyland Innovation](https://www.skylandx.com).
143 | In our program, we use and adopt [Open3D](https://www.open3d.org) to reconstruct instance sub-volume. The vision foundation models RAM, GroundingDINO, and SAM provide instance segmentation on images.
144 |
145 | ## 6. License
146 | The source code is released under [GPLv3](https://www.gnu.org/licenses/) license.
147 | For technical issues, please contact Chuhao LIU (cliuci@connect.ust.hk).
148 |
--------------------------------------------------------------------------------
/catkin_ws/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | /opt/ros/noetic/share/catkin/cmake/toplevel.cmake
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0.2)
2 | project(sgloop_ros)
3 |
4 | # add_compile_options(-std=c++11)
5 | add_compile_options(-fPIC)
6 | set(CMAKE_CXX_STANDARD 17)
7 | option(LOOP_DETECTION OFF)
8 |
9 | find_package(catkin REQUIRED COMPONENTS
10 | roscpp
11 | rospy
12 | std_msgs
13 | geometry_msgs
14 | sensor_msgs
15 | nav_msgs
16 | visualization_msgs
17 | open3d_conversions
18 | cv_bridge
19 | tf
20 | message_generation
21 | )
22 |
23 | set(FMFUSION_LIBRARIES ${CMAKE_CURRENT_SOURCE_DIR}/../../../build/src/libfmfusion.so)
24 | set(FMFUSION_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../install/include/fmfusion)
25 | find_package(OpenCV REQUIRED)
26 | find_package(Open3D REQUIRED)
27 | message(STATUS "FMFUSION LIBRARIES: ${FMFUSION_LIBRARIES}")
28 | message(STATUS "FMFUSION INCLUDE DIR: ${FMFUSION_INCLUDE_DIR}")
29 |
30 | #
31 | if(LOOP_DETECTION)
32 | set(CMAKE_PREFIX_PATH "/home/cliuci/tools/libtorch")
33 | find_package(Torch REQUIRED)
34 | add_subdirectory(thirdparty/G3Reg)
35 | # add_subdirectory(thirdparty/Kimera-RPGO)
36 | # include_directories("thirdparty/Kimera-RPGO/include")
37 | message(STATUS "TORCH_LIBRARIES: ${TORCH_LIBRARIES}")
38 | message(STATUS "TORCH_INCLUDE_DIRS: ${TORCH_INCLUDE_DIRS}")
39 | add_message_files(
40 | FILES
41 | CoarseGraph.msg
42 | DenseGraph.msg
43 | )
44 |
45 | generate_messages(
46 | DEPENDENCIES
47 | std_msgs
48 | geometry_msgs
49 | )
50 | endif()
51 |
52 | catkin_package(
53 | INCLUDE_DIRS include
54 | CATKIN_DEPENDS roscpp std_msgs sensor_msgs tf open3d_conversions cv_bridge message_runtime
55 | DEPENDS Open3D
56 | )
57 |
58 | ###########
59 | ## Build ##
60 | ###########
61 |
62 | ## Specify additional locations of header files
63 | ## Your package locations should be listed before other locations
64 | include_directories(
65 | include
66 | ${catkin_INCLUDE_DIRS}
67 | ${FMFUSION_INCLUDE_DIR}
68 | ${GTSAM_INCLUDE_DIR}
69 | )
70 |
71 | set(ALL_TARGET_LINK_LIBARIES
72 | ${catkin_LIBRARIES}
73 | Open3D::Open3D
74 | ${FMFUSION_LIBRARIES}
75 | ${OpenCV_LIBS}
76 | )
77 |
78 | # Build Library
79 | set(SGLOOP_SRC src/utility/CameraPoseVisualization.cpp
80 | src/Visualization.cpp)
81 | if(LOOP_DETECTION)
82 | list(APPEND SGLOOP_SRC
83 | src/communication/Communication.cpp
84 | src/registration/Prune.cpp
85 | src/registration/robustPoseAvg.cpp)
86 |
87 | list(APPEND ALL_TARGET_LINK_LIBARIES
88 | ${TORCH_LIBRARIES}
89 | G3REG::g3reg
90 | gtsam
91 | )
92 | message("Enable Loop Detection")
93 | endif(LOOP_DETECTION)
94 |
95 | add_library(sgloop_ros SHARED ${SGLOOP_SRC})
96 | target_link_libraries(sgloop_ros ${ALL_TARGET_LINK_LIBARIES})
97 |
98 | # Build Executables
99 | add_executable(MappingNode src/MappingNode.cpp)
100 | target_link_libraries(MappingNode ${ALL_TARGET_LINK_LIBARIES} sgloop_ros)
101 |
102 | add_executable(RenderNode src/RenderNode.cpp)
103 | target_link_libraries(RenderNode ${ALL_TARGET_LINK_LIBARIES} sgloop_ros)
104 |
105 | if(LOOP_DETECTION)
106 | add_executable(PoseGraphNode src/PoseGraphNode.cpp)
107 | target_link_libraries(PoseGraphNode PUBLIC ${ALL_TARGET_LINK_LIBARIES} sgloop_ros)
108 |
109 | add_executable(OfflineLoop src/OfflineLoopNode.cpp)
110 | target_link_libraries(OfflineLoop ${ALL_TARGET_LINK_LIBARIES} sgloop_ros)
111 |
112 | add_executable(OnlineLoop src/OnlineLoopNode.cpp)
113 | target_link_libraries(OnlineLoop ${ALL_TARGET_LINK_LIBARIES} sgloop_ros)
114 | endif(LOOP_DETECTION)
115 |
116 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/include/SlidingWindow.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | namespace sgloop_ros
8 | {
9 | class SlidingWindow
10 | {
11 | private:
12 | float translation_threshold;
13 | std::vector frame_ids;
14 | std::vector translations;
15 | float total_translation;
16 |
17 | Eigen::Matrix4d last_pose;
18 |
19 | public:
20 | SlidingWindow(float translation_threshold_ = 100.0);
21 | ~SlidingWindow(){};
22 |
23 | void update_translation(const int &frame_id, const Eigen::Matrix4d &cur_pose);
24 | // float translation);
25 |
26 | int get_window_start_frame();
27 |
28 | };
29 |
30 | SlidingWindow::SlidingWindow(float translation_threshold_):
31 | translation_threshold(translation_threshold_), total_translation(0.0)
32 | {
33 |
34 | }
35 |
36 | void SlidingWindow::update_translation(const int &frame_id, const Eigen::Matrix4d &cur_pose)
37 | {
38 | float translation = (cur_pose.block<3,1>(0,3) - last_pose.block<3,1>(0,3)).norm();
39 |
40 | translations.push_back(translation);
41 | frame_ids.push_back(frame_id);
42 | total_translation += translation;
43 |
44 | if(total_translation > translation_threshold)
45 | { // remove frames from the start
46 | while(total_translation > translation_threshold)
47 | {
48 | total_translation -= translations[0];
49 | frame_ids.erase(frame_ids.begin());
50 | translations.erase(translations.begin());
51 | }
52 | }
53 |
54 | last_pose = cur_pose;
55 | }
56 |
57 | int SlidingWindow::get_window_start_frame()
58 | {
59 | int start_frame;
60 | if(frame_ids.empty()) start_frame = 0;
61 | else start_frame = std::max(0,frame_ids[0]-1);
62 |
63 | // std::cout<<"start_frame: "< edge_color;
23 | float centroid_size = 0.1;
24 | std::array centroid_color = {0.0,0.0,0.0};
25 | float centroid_voffset = 0.0;
26 | float annotation_size = 0.2;
27 | float annotation_voffset = 0.3;
28 | std::array annotation_color = {0.0,0.0,0.0};
29 | };
30 |
31 | class Visualizer
32 | {
33 | public:
34 | Visualizer(ros::NodeHandle &nh, ros::NodeHandle &nh_private, std::vector remote_agents={});
35 |
36 | ~Visualizer(){};
37 |
38 | public:
39 | ros::Publisher ref_graph, src_graph;
40 | ros::Publisher ref_global_map, src_global_map;
41 | ros::Publisher ref_centroids, src_centroids;
42 | ros::Publisher ref_edges, src_edges;
43 | ros::Publisher instance_match, point_match;
44 |
45 | ros::Publisher rgb_image, pred_image;
46 | ros::Publisher camera_pose, path;
47 | ros::Publisher node_annotation, node_dir_lines;
48 |
49 | ros::Publisher src_map_aligned, path_aligned;
50 |
51 | nav_msgs::Path path_msg;
52 | VizParam param;
53 | std::array local_frame_offset;
54 |
55 | std::map t_local_remote;
56 | std::map Transfrom_local_remote;
57 |
58 | };
59 |
60 | bool render_point_cloud(const std::shared_ptr &pcd, ros::Publisher pub, std::string frame_id="world");
61 |
62 | bool inter_graph_edges(const std::vector ¢roids,
63 | const std::vector> &edges,
64 | ros::Publisher pub,
65 | float width=0.02,
66 | std::array color={0.0,0.0,1.0},
67 | std::string frame_id="world");
68 |
69 | bool correspondences(const std::vector &src_centroids,
70 | const std::vector &ref_centroids,
71 | ros::Publisher pub,
72 | std::string frame_id="world",
73 | std::vector pred_masks={},
74 | Eigen::Matrix4d T_local_remote = Eigen::Matrix4d::Identity(),
75 | float line_width=0.02,
76 | float alpha=1.0);
77 |
78 | bool instance_centroids(const std::vector ¢roids,
79 | ros::Publisher pub,
80 | std::string frame_id="world",
81 | float scale=0.1,
82 | std::array color={0.0,0.0,1.0},
83 | float offset_z=0.0);
84 |
85 | bool node_annotation(const std::vector ¢roids,
86 | const std::vector &annotations,
87 | ros::Publisher pub,
88 | std::string frame_id="world",
89 | float scale=0.1,
90 | float z_offset=0.3,
91 | std::array color={0.0,0.0,1.0});
92 |
93 | bool render_image(const cv::Mat &image, ros::Publisher pub, std::string frame_id="world");
94 |
95 | bool render_camera_pose(const Eigen::Matrix4d &pose, ros::Publisher pub,
96 | std::string frame_id="world", int sequence_id = 0);
97 |
98 | bool render_path(const Eigen::Matrix4d &pose,
99 | nav_msgs::Path &path_msg,
100 | ros::Publisher pub,
101 | std::string frame_id="world",
102 | int sequence_id = 0);
103 |
104 | bool render_path(const std::vector &T_a_camera,
105 | const Eigen::Matrix4d & T_b_a,
106 | const std::string &agnetB_frame_id,
107 | nav_msgs::Path &path_msg,
108 | ros::Publisher pub);
109 |
110 | bool render_rgb_detections(const open3d::geometry::Image &color,
111 | const std::vector &detections,
112 | ros::Publisher pub,
113 | std::string frame_id="world");
114 |
115 | int render_semantic_map(const std::shared_ptr &cloud,
116 | const std::vector &instance_centroids,
117 | const std::vector &instance_annotations,
118 | const Visualization::Visualizer &viz,
119 | const std::string &agent_name);
120 | }
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/include/communication/Communication.h:
--------------------------------------------------------------------------------
1 | #ifndef COMMUNICATION_H
2 | #define COMMUNICATION_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | namespace SgCom
16 | {
17 | struct Log
18 | {
19 | int frame_id;
20 | float timestamp;
21 | int N;
22 | int X=0;
23 | std::string direction; // "pub" or "sub"
24 | std::string msg_type; // "CoarseGraph", "DenseGraph"
25 | bool checksum;
26 | };
27 |
28 | struct AgentDataDict{
29 | int frame_id=-1;
30 | float received_timestamp;
31 | int N=0;
32 | int X=0; // number of points
33 | int D=0; // coarse node feature dimension
34 | std::vector nodes; // (N,)
35 | std::vector instances; // (N,)
36 | std::vector centroids; // (N,3)
37 | std::vector> features_vec; // (N,D)
38 | std::vector xyz; // (X,3)
39 | std::vector labels; // (X,) node ids, each label in [0,N)
40 |
41 | void clear(){
42 | nodes.clear();
43 | instances.clear();
44 | centroids.clear();
45 | features_vec.clear();
46 | xyz.clear();
47 | labels.clear();
48 | N=0;
49 | X=0;
50 | D=0;
51 | };
52 |
53 | std::string print_msg()const{
54 | std::stringstream ss;
55 | for(int i=0;i> &features_vector,
65 | const int &N, const int &D, torch::Tensor &features_tensor);
66 |
67 | class Communication
68 | {
69 | public:
70 | /// @brief Each MASTER_AGENT publish their features to "MASTER_AGENT/graph_topics"
71 | /// And it subscribes to "OTHER_AGENT/graph_topics" for other agents' features.
72 | /// @param nh
73 | /// @param nh_private
74 | /// @param master_agent
75 | /// @param other_agents
76 | Communication(ros::NodeHandle &nh, ros::NodeHandle &nh_private,
77 | std::string master_agent="agent_a", std::vector other_agents={});
78 | ~Communication(){};
79 |
80 | // bool broadcast_coarse_graph(int frame_id,
81 | // const std::vector &instances,
82 | // const std::vector ¢roids,
83 | // const int& N, const int &D,
84 | // const std::vector> &features);
85 |
86 |
87 | /// @brief Broadcast dense graph to other agents.
88 | /// If X<1, broadcast the coarse graph.
89 | /// @param frame_id
90 | /// @param nodes (N,)
91 | /// @param instances (N,)
92 | /// @param centroids (N,3)
93 | /// @param features (N,D) coarse node features
94 | /// @param xyz (X,3) global point cloud
95 | /// @param labels (X,) node ids, each label in [0,N)
96 | bool broadcast_dense_graph(int frame_id,
97 | const std::vector &nodes,
98 | const std::vector &instances,
99 | const std::vector ¢roids,
100 | const std::vector> &features,
101 | const std::vector &xyz,
102 | const std::vector &labels);
103 |
104 | /// @brief Publish request message to the target agent.
105 | /// @param target_agent_name
106 | /// @return
107 | bool send_request_dense(const std::string &target_agent_name);
108 |
109 | // void coarse_graph_callback(const sgloop_ros::CoarseGraph::ConstPtr &msg,std::string agent_name);
110 |
111 | void dense_graph_callback(const sgloop_ros::DenseGraph::ConstPtr &msg,std::string agent_name);
112 |
113 | void request_dense_callback(const std_msgs::Int32::ConstPtr &msg);
114 |
115 | const AgentDataDict& get_remote_agent_data(const std::string agent_name)const;
116 |
117 | bool write_logs(const std::string &out_dir);
118 |
119 | bool get_pub_dense_msg()const{return pub_dense_msg;};
120 |
121 | void reset_pub_dense_msg(){pub_dense_msg=false;};
122 |
123 | public:
124 | int pub_dense_frame_id; // master publish dense graph message
125 |
126 | private:
127 | float frame_duration; // in seconds
128 | float time_offset; // in seconds
129 |
130 | // In masterAgent
131 | ros::Publisher pub_dense_sg;
132 | ros::Subscriber sub_request_dense;
133 | std::string local_agent;
134 |
135 | // In otherAgents
136 | std::vector agents_dense_subscriber;
137 | std::map pub_agents_request_dense;
138 | std::map agents_data_dict;
139 |
140 | std::vector pub_logs;
141 | std::vector sub_logs;
142 |
143 | bool pub_dense_msg;
144 |
145 | int last_exchange_frame_id; // At an exchange frame, the latest agent data dict is exported.
146 |
147 | };
148 |
149 |
150 |
151 | }
152 |
153 |
154 | #endif // COMMUNICATION_H
155 |
156 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/include/registration/Prune.h:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "Common.h"
4 | #include "back_end/reglib.h"
5 | // #include "tools/g3reg_api.h"
6 | #include "sgloop/Graph.h"
7 |
8 |
9 | namespace Registration
10 | {
11 |
12 | void pruneInsOutliers(const fmfusion::RegistrationConfig &config,
13 | const std::vector &src_nodes,
14 | const std::vector &ref_nodes,
15 | const std::vector &match_pairs,
16 | std::vector &pruned_true_masks);
17 |
18 |
19 | } // namespace Registration
20 |
21 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/include/registration/robustPoseAvg.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by qzj on 6/24/24.
3 | //
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | namespace registration
10 | {
11 | gtsam::Pose3 gncRobustPoseAveraging(const std::vector &input_poses,
12 | const double &rot_sigma = 0.1,
13 | const double &trans_sigma = 0.5);
14 | } // namespace registration
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/include/utility/CameraPoseVisualization.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | // #include "../parameters.h"
11 |
12 | class CameraPoseVisualization {
13 | public:
14 | std::string m_marker_ns;
15 |
16 | CameraPoseVisualization(float r, float g, float b, float a);
17 |
18 | void setImageBoundaryColor(float r, float g, float b, float a=1.0);
19 | void setOpticalCenterConnectorColor(float r, float g, float b, float a=1.0);
20 | void setScale(double s);
21 | void setLineWidth(double width);
22 |
23 | void add_pose(const Eigen::Vector3d& p, const Eigen::Quaterniond& q);
24 | void reset();
25 |
26 | void publish_by(ros::Publisher& pub, const std_msgs::Header& header);
27 | void add_edge(const Eigen::Vector3d& p0, const Eigen::Vector3d& p1);
28 | void add_loopedge(const Eigen::Vector3d& p0, const Eigen::Vector3d& p1);
29 | //void add_image(const Eigen::Vector3d& T, const Eigen::Matrix3d& R, const cv::Mat &src);
30 | void publish_image_by( ros::Publisher &pub, const std_msgs::Header &header);
31 | private:
32 | std::vector m_markers;
33 | std_msgs::ColorRGBA m_image_boundary_color;
34 | std_msgs::ColorRGBA m_optical_center_connector_color;
35 | double m_scale;
36 | double m_line_width;
37 | visualization_msgs::Marker image;
38 | int LOOP_EDGE_NUM;
39 | int tmp_loop_edge_num;
40 |
41 | static const Eigen::Vector3d imlt;
42 | static const Eigen::Vector3d imlb;
43 | static const Eigen::Vector3d imrt;
44 | static const Eigen::Vector3d imrb;
45 | static const Eigen::Vector3d oc ;
46 | static const Eigen::Vector3d lt0 ;
47 | static const Eigen::Vector3d lt1 ;
48 | static const Eigen::Vector3d lt2 ;
49 | };
50 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/launch/render_our_sgslam.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/launch/render_semantic_map.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/launch/semantic_mapping.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/launch/slabim_mapping.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/launch/visualize.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/msg/CoarseGraph.msg:
--------------------------------------------------------------------------------
1 | std_msgs/Header header
2 | int16[] instances
3 | geometry_msgs/Point[] centroids
4 | std_msgs/Float32MultiArray features
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/msg/DenseGraph.msg:
--------------------------------------------------------------------------------
1 | std_msgs/Header header
2 | int32 nodes_number
3 | int32 points_number
4 | int16[] nodes
5 | int16[] instances
6 | geometry_msgs/Point[] centroids
7 | std_msgs/Float32MultiArray features
8 | geometry_msgs/Point[] points
9 | int16[] labels
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | sgloop_ros
4 | 0.0.0
5 | The sgloop_ros package
6 |
7 |
8 |
9 |
10 | cliuci
11 |
12 |
13 |
14 |
15 |
16 | TODO
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | catkin
52 | geometry_msgs
53 | roscpp
54 | rospy
55 | std_msgs
56 | sensor_msgs
57 | open3d_conversions
58 | message_generation
59 | geometry_msgs
60 | roscpp
61 | rospy
62 | std_msgs
63 | sensor_msgs
64 | open3d_conversions
65 | geometry_msgs
66 | roscpp
67 | rospy
68 | std_msgs
69 | sensor_msgs
70 | open3d_conversions
71 | message_runtime
72 | cv_bridge
73 | nav_msgs
74 | tf
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/src/MappingNode.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include
8 | #include
9 | #include "tf/transform_listener.h"
10 |
11 | #include
12 | #include
13 |
14 | #include "tools/Utility.h"
15 | #include "tools/IO.h"
16 | #include "tools/TicToc.h"
17 | #include "mapping/SemanticMapping.h"
18 |
19 | #include "Visualization.h"
20 |
21 | int main(int argc, char **argv)
22 | {
23 | using namespace fmfusion;
24 | using namespace open3d::utility::filesystem;
25 | using namespace open3d::io;
26 |
27 | ros::init(argc, argv, "MappingNode");
28 | ros::NodeHandle n;
29 | ros::NodeHandle nh_private("~");
30 |
31 | // Settings
32 | std::string config_file;
33 | std::string root_dir;
34 | std::string LOCAL_AGENT;
35 |
36 | assert(nh_private.getParam("cfg_file", config_file));
37 | assert(nh_private.getParam("active_sequence_dir", root_dir));
38 | nh_private.getParam("local_agent", LOCAL_AGENT);
39 | int frame_gap = nh_private.param("frame_gap", 1);
40 | std::string prediction_folder = nh_private.param("prediction_folder", std::string("prediction_no_augment"));
41 | std::string output_folder = nh_private.param("output_folder", std::string(""));
42 | std::string association_name = nh_private.param("association_name", std::string(""));
43 | std::string trajectory_name = nh_private.param("trajectory_name", std::string("trajectory.log"));
44 | int o3d_verbose_level = nh_private.param("o3d_verbose_level", 2);
45 | int visualization = nh_private.param("visualization", 0);
46 | int max_frames = nh_private.param("max_frames", 5000);
47 | bool debug = nh_private.param("debug", false);
48 | ROS_WARN("MappingNode started");
49 |
50 | // Inits
51 | Config *global_config;
52 | SemanticMapping *semantic_mapping;
53 | std::string sequence_name = *GetPathComponents(root_dir).rbegin();
54 | Visualization::Visualizer viz(n,nh_private);
55 | {
56 | global_config = utility::create_scene_graph_config(config_file, true);
57 | open3d::utility::SetVerbosityLevel((open3d::utility::VerbosityLevel)o3d_verbose_level);
58 | if(output_folder.size()>0 && !DirectoryExists(output_folder))
59 | MakeDirectory(output_folder);
60 |
61 | std::ofstream out_file(output_folder+"/config.txt");
62 | out_file<mapping_cfg, global_config->instance_cfg);
66 | }
67 |
68 | // Load frames information
69 | std::vector rgbd_table;
70 | std::vector pose_table;
71 | if(association_name.empty()) {
72 | ROS_WARN("--- Read all RGB-D frames in %s ---",root_dir.c_str());
73 | IO::construct_sorted_frame_table(root_dir,
74 | rgbd_table,
75 | pose_table);
76 | if(rgbd_table.size()>4000) return 0;
77 | }
78 | else{
79 | ROS_WARN("--- Read RGB-D frames from %s/%s ---",
80 | root_dir.c_str(),
81 | association_name.c_str());
82 | bool read_ret = IO::construct_preset_frame_table(root_dir,
83 | association_name,
84 | trajectory_name,
85 | rgbd_table,
86 | pose_table);
87 | if(!read_ret) return 0;
88 | }
89 |
90 | if(rgbd_table.empty()){
91 | ROS_ERROR("No RGB-D frames found in %s",root_dir.c_str());
92 | return 0;
93 | }
94 |
95 | // Integration
96 | open3d::geometry::Image depth, color;
97 | int prev_frame_id = -100;
98 | int prev_save_frame = -100;
99 | fmfusion::TicTocSequence tic_toc_seq("# Load Integration Export", 3);
100 |
101 | for(int k=0;kmax_frames) break;
108 | // map_timing.create_frame(seq_id);
109 | std::cout<<"Processing frame "< detections;
115 | std::shared_ptr rgbd;
116 | { // load RGB-D, detections
117 | ReadImage(frame_dirs.second, depth);
118 | ReadImage(frame_dirs.first, color);
119 | rgbd = open3d::geometry::RGBDImage::CreateFromColorAndDepth(
120 | color, depth, global_config->mapping_cfg.depth_scale, global_config->mapping_cfg.depth_max, false);
121 |
122 | loaded = fmfusion::utility::LoadPredictions(root_dir+'/'+prediction_folder, frame_name,
123 | global_config->mapping_cfg, global_config->instance_cfg.intrinsic.width_, global_config->instance_cfg.intrinsic.height_,
124 | detections);
125 | tic_toc_seq.toc();
126 | }
127 | if(!loaded) {
128 | ROS_WARN("Cannot find detection file at %s",root_dir+'/'+prediction_folder+"/"+frame_name);
129 | continue;
130 | }
131 | semantic_mapping->integrate(seq_id,rgbd, pose_table[k], detections);
132 | tic_toc_seq.toc();
133 | prev_frame_id = seq_id;
134 |
135 | { // Viz poses
136 | Visualization::render_camera_pose(pose_table[k],
137 | viz.camera_pose,
138 | LOCAL_AGENT,
139 | seq_id);
140 | Visualization::render_path(pose_table[k],
141 | viz.path_msg,
142 | viz.path,
143 | LOCAL_AGENT,
144 | seq_id);
145 |
146 | }
147 |
148 | if(viz.pred_image.getNumSubscribers()>0) Visualization::render_rgb_detections(color,
149 | detections,
150 | viz.pred_image,
151 | LOCAL_AGENT);
152 |
153 | {// Viz 3D
154 | Visualization::render_semantic_map(semantic_mapping->export_global_pcd(true,0.05),
155 | semantic_mapping->export_instance_centroids(0, debug),
156 | semantic_mapping->export_instance_annotations(0),
157 | viz,
158 | LOCAL_AGENT);
159 | }
160 | tic_toc_seq.toc();
161 | }
162 |
163 | ROS_WARN("Finished sequence with %ld frames",rgbd_table.size());
164 | { // Process at the end of the sequence
165 | semantic_mapping->extract_point_cloud();
166 | semantic_mapping->merge_floor(true);
167 | // semantic_mapping->merge_overlap_instances();
168 |
169 | Visualization::render_semantic_map(semantic_mapping->export_global_pcd(true,0.05),
170 | semantic_mapping->export_instance_centroids(0, debug),
171 | semantic_mapping->export_instance_annotations(0),
172 | viz,
173 | LOCAL_AGENT);
174 | }
175 |
176 | // Save
177 | ROS_WARN("Save results to %s",output_folder.c_str());
178 | semantic_mapping->Save(output_folder+"/"+sequence_name);
179 | tic_toc_seq.export_data(output_folder+"/"+sequence_name+"/time_records.txt");
180 | fmfusion::utility::write_config(output_folder+"/"+sequence_name+"/config.txt",*global_config);
181 |
182 |
183 | return 0;
184 | }
185 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/src/RenderNode.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "ros/ros.h"
5 | #include "std_msgs/String.h"
6 | #include "sensor_msgs/PointCloud2.h"
7 | #include "open3d_conversions/open3d_conversions.h"
8 |
9 | #include "tools/IO.h"
10 |
11 | #include "Visualization.h"
12 | #include "mapping/SemanticMapping.h"
13 |
14 | int main(int argc, char **argv)
15 | {
16 | using namespace open3d::utility::filesystem;
17 | using namespace fmfusion;
18 |
19 | ros::init(argc, argv, "RenderNode");
20 | ros::NodeHandle nh;
21 | ros::NodeHandle nh_private("~");
22 |
23 | // Params
24 | std::string config_file;
25 | std::string scene_result_folder;
26 | assert(nh_private.getParam("cfg_file", config_file));
27 | assert(nh_private.getParam("sequence_result_folder", scene_result_folder));
28 | std::string agent_name = nh_private.param("agent_name", std::string("agentA"));
29 | std::string map_name = nh_private.param("map_name", std::string(""));
30 | int iter_num = nh_private.param("iter_num", 3);
31 | std::string sequence_name = *GetPathComponents(scene_result_folder).rbegin();
32 | ROS_WARN("Render %s semantic mapping results from %s", agent_name.c_str(),
33 | scene_result_folder.c_str());
34 |
35 | // Init
36 | Config *global_config;
37 | SemanticMapping *semantic_mapping;
38 | Visualization::Visualizer viz(nh,nh_private);
39 | {
40 | global_config = utility::create_scene_graph_config(config_file, false);
41 | semantic_mapping = new SemanticMapping(global_config->mapping_cfg, global_config->instance_cfg);
42 | }
43 |
44 | // Load
45 | semantic_mapping->load(scene_result_folder);
46 | semantic_mapping->refresh_all_semantic_dict();
47 | semantic_mapping->merge_floor(false);
48 | semantic_mapping->merge_overlap_instances();
49 |
50 | // Viz
51 | for(int i=0;i0){
54 | render_map_pcd = open3d::io::CreatePointCloudFromFile(scene_result_folder+"/"+map_name);
55 | ROS_WARN("Load and render map from %s", map_name.c_str());
56 | }
57 | else
58 | render_map_pcd = semantic_mapping->export_global_pcd(true,0.05);
59 |
60 | Visualization::render_semantic_map(render_map_pcd,
61 | semantic_mapping->export_instance_centroids(0),
62 | semantic_mapping->export_instance_annotations(0),
63 | viz,
64 | agent_name);
65 | ros::Duration(0.5).sleep();
66 | ros::spinOnce();
67 | }
68 |
69 |
70 | return 0;
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/src/registration/Prune.cpp:
--------------------------------------------------------------------------------
1 | #include "registration/Prune.h"
2 |
3 | namespace Registration
4 | {
5 |
6 | void pruneInsOutliers(const fmfusion::RegistrationConfig &config,
7 | const std::vector &src_nodes,
8 | const std::vector &ref_nodes,
9 | const std::vector &match_pairs,
10 | std::vector &pruned_true_masks) {
11 |
12 | // 存储配准单位
13 | std::vector v1, v2;
14 | // 一致性图的节点有多种类型
15 | clique_solver::VertexInfo vertex_info;
16 | // 不同的节点会有不同的一致性测量函数
17 | vertex_info.type = clique_solver::VertexType::POINT;
18 | // 一致性判断的阈值,可以设置为多个,从小到大,会得到多个内点结果。
19 | // 由于Instance中心点不确定性较大,我们这里只选取一个比较大的阈值,不做精确估计
20 | // 注意,阈值越大,图越稠密,最大团求解时间也会上升
21 | vertex_info.noise_bound_vec = config.noise_bound_vec; //{1.0};
22 | // 初始化配准节点
23 | for (const auto &src_node: src_nodes) {
24 | const Eigen::Vector3d ¢er = src_node->centroid;
25 | v1.push_back(clique_solver::create_vertex(center, vertex_info));
26 | }
27 | for (const auto &ref_node: ref_nodes) {
28 | const Eigen::Vector3d ¢er = ref_node->centroid;
29 | v2.push_back(clique_solver::create_vertex(center, vertex_info));
30 | }
31 |
32 | // 匹配的数量
33 | uint64_t num_corr = match_pairs.size();
34 | // TRIM(Translation Rotation Invariant Measurements)的数量
35 | uint64_t num_tims = num_corr * (num_corr - 1) / 2;
36 | // 初始化一致性图和最大团
37 | std::vector graphs;
38 | std::vector> max_cliques;
39 | int num_graphs = vertex_info.noise_bound_vec.size();
40 | for (int i = 0; i < num_graphs; ++i) {
41 | clique_solver::Graph graph;
42 | graph.clear();
43 | graph.setType(false);
44 | graph.populateVertices(num_corr);
45 | graphs.push_back(graph);
46 | max_cliques.push_back(std::vector());
47 | }
48 |
49 | // 构建多个一致性图
50 | #pragma omp parallel for default(none) shared(num_corr, num_tims, v1, v2, graphs, match_pairs, num_graphs)
51 | for (size_t k = 0; k < num_tims; ++k) {
52 | size_t i, j;
53 | // 一共需要进行num_tims次边的计算,第k次对应第i,j个节点
54 | std::tie(i, j) = clique_solver::k2ij(k, num_corr);
55 | // 由于之前的noise_bound_vec会有多个阈值,因此results也是多个
56 | const auto &results = (*v1[match_pairs[j].first] - *v1[match_pairs[i].first])->consistent(
57 | *(*v2[match_pairs[j].second] - *v2[match_pairs[i].second]));
58 | for (int level = 0; level < num_graphs; ++level) {
59 | // 判断是否通过一致性检验
60 | if (results(level) > 0.0) {
61 | #pragma omp critical
62 | {
63 | graphs[level].addEdge(i, j);
64 | }
65 | }
66 | }
67 | }
68 |
69 |
70 | // 求解最大团
71 | clique_solver::MaxCliqueSolver::Params clique_params;
72 | clique_params.solver_mode = clique_solver::MaxCliqueSolver::CLIQUE_SOLVER_MODE::PMC_EXACT;
73 | int prune_level = 0;
74 | // 渐进式求解每个图的最大团
75 | for (int level = 0; level < num_graphs; ++level) {
76 | clique_solver::MaxCliqueSolver mac_solver(clique_params);
77 | max_cliques[level] = mac_solver.findMaxClique(graphs[level], prune_level);
78 | prune_level = max_cliques[level].size();
79 | }
80 | // 最大团的元素表示第几个匹配关系是正确的。排序,为了好看。
81 | for (int level = 0; level < num_graphs; ++level) {
82 | auto &clique = max_cliques[level];
83 | std::sort(clique.begin(), clique.end());
84 | }
85 |
86 | std::stringstream msg;
87 | msg << "The inlier match pairs are: \n";
88 | // 默认只有一个noise bound,我们只看第一个最大团的结果
89 | for (auto i: max_cliques[0]) {
90 | auto pair = match_pairs[i];
91 | pruned_true_masks[i] = true;
92 | const auto &src_node = src_nodes[pair.first];
93 | const auto &ref_node = ref_nodes[pair.second];
94 | msg << "(" << pair.first << "," << pair.second << ") "
95 | << "(" << src_node->semantic << "," << ref_node->semantic << ")\n";
96 | }
97 | // std::cout << msg.str() << std::endl;
98 | }
99 |
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/catkin_ws/src/sgloop_ros/src/registration/robustPoseAvg.cpp:
--------------------------------------------------------------------------------
1 | #include "registration/robustPoseAvg.h"
2 |
3 | namespace registration
4 | {
5 | gtsam::Pose3 gncRobustPoseAveraging(const std::vector &input_poses,
6 | const double &rot_sigma,
7 | const double &trans_sigma) {
8 | gtsam::Values initial;
9 | initial.insert(0, gtsam::Pose3()); // identity pose as initialization
10 |
11 | gtsam::NonlinearFactorGraph graph;
12 | gtsam::Vector sigmas;
13 | sigmas.resize(6);
14 | sigmas.head(3).setConstant(rot_sigma);
15 | sigmas.tail(3).setConstant(trans_sigma);
16 | const gtsam::noiseModel::Diagonal::shared_ptr noise =
17 | gtsam::noiseModel::Diagonal::Sigmas(sigmas);
18 | // add measurements
19 | for (const auto &pose: input_poses) {
20 | graph.add(gtsam::PriorFactor(0, pose, noise));
21 | }
22 |
23 | gtsam::GncParams gncParams;
24 | auto gnc = gtsam::GncOptimizer>(graph, initial, gncParams);
25 |
26 | gnc.setInlierCostThresholdsAtProbability(0.99);
27 |
28 | gtsam::Values estimate = gnc.optimize();
29 | return estimate.at(0);
30 | }
31 |
32 |
33 | } // namespace registration
--------------------------------------------------------------------------------
/cmake/eigen.cmake:
--------------------------------------------------------------------------------
1 | find_package(Eigen3 REQUIRED)
2 | include_directories(${EIGEN3_INCLUDE_DIR})
3 | message(${EIGEN3_INCLUDE_DIR})
--------------------------------------------------------------------------------
/cmake/glog.cmake:
--------------------------------------------------------------------------------
1 | find_package(glog REQUIRED)
2 | include_directories(${GLOG_INCLUDE_DIRS})
3 | list(APPEND ALL_TARGET_LIBRARIES ${GLOG_LIBRARIES})
4 |
--------------------------------------------------------------------------------
/cmake/gtsam.cmake:
--------------------------------------------------------------------------------
1 | find_package(GTSAM REQUIRED)
2 | include_directories(${GTSAM_INCLUDE_DIR})
3 | list(APPEND ALL_TARGET_LIBRARIES gtsam)
4 | message(STATUS "GTSAM VERSION: ${GTSAM_VERSION} ${GTSAM_INCLUDE_DIR} ${gtsam}")
--------------------------------------------------------------------------------
/cmake/jsoncpp.cmake:
--------------------------------------------------------------------------------
1 | find_package(jsoncpp REQUIRED)
2 | include_directories(${jsoncpp_INCLUDE_DIRS})
3 | list(APPEND ALL_TARGET_LIBRARIES jsoncpp_lib)
--------------------------------------------------------------------------------
/cmake/open3d.cmake:
--------------------------------------------------------------------------------
1 | find_package(Open3D REQUIRED)
2 | include_directories(${Open3D_INCLUDE_DIRS})
3 | list(APPEND ALL_TARGET_LIBRARIES Open3D::Open3D)
4 | message(STATUS "Open3D_LIBRARIES: ${Open3D_LIBRARIES}")
5 |
--------------------------------------------------------------------------------
/cmake/opencv.cmake:
--------------------------------------------------------------------------------
1 | find_package(OpenCV REQUIRED)
2 | include_directories(${OpenCV_INCLUDE_DIRS})
3 | list(APPEND ALL_TARGET_LIBRARIES ${OpenCV_LIBS})
--------------------------------------------------------------------------------
/config/fusion_portable.yaml:
--------------------------------------------------------------------------------
1 | %YAML:1.0
2 |
3 | dataset: "fusion_portable" # scannet, fusion_portable, realsense
4 |
5 | # camera parameters
6 | image_width: 1024
7 | image_height: 768
8 | camera_fx: 605.128
9 | camera_fy: 604.974
10 | camera_cx: 520.45
11 | camera_cy: 393.88
12 | depth_scale: 1000.0
13 | depth_max: 20.0
14 |
15 | # volumetric
16 | voxel_length: 0.04
17 | sdf_trunc: 0.6
18 | min_instance_points: 100 # for initial new instance
19 |
20 | # associations
21 | min_det_masks: 30 # pixel-wise
22 | max_box_area_ratio: 0.9 # box_area/image_area
23 | # min_instance_masks: 20 # pixel-wise
24 | dilate_kernal: 3
25 | min_iou: 0.3
26 |
27 | # shape
28 | min_voxel_weight: 0.05 # weight/observation_count
29 |
30 | # merge over-segmentation
31 | merge_inflation: 3.5
32 | merge_iou: 0.4
33 |
34 | # output
35 | save_da_images: 1
36 | tmp_dir: "/media/lch/SeagateExp/dataset_/FusionPortable/output"
37 |
--------------------------------------------------------------------------------
/config/matterport.yaml:
--------------------------------------------------------------------------------
1 | %YAML:1.0
2 |
3 | dataset: "scannet" # scannet, fusion_portable, realsense
4 |
5 | # camera parameters
6 | image_width: 640
7 | image_height: 512
8 | camera_fx: 535.825
9 | camera_fy: 536.13
10 | camera_cx: 315.4215
11 | camera_cy: 252.159
12 |
13 | Mapping:
14 | # Instance
15 | voxel_length: 0.02
16 | sdf_trunc: 0.04
17 | min_voxel_weight: 0.1 # weight/observation_count
18 |
19 | #
20 | depth_scale: 4000.0
21 | depth_max: 4.0
22 |
23 | # associations
24 | min_det_masks: 50 # pixel-wise
25 | min_active_points: 50 # active instances
26 | max_box_area_ratio: 0.9 # box_area/image_area
27 | query_depth_vx_size: -1.0
28 | dilate_kernal: 3
29 | min_iou: 0.2
30 | search_radius: 4.0
31 |
32 | # merge over-segmentation
33 | merge_inflation: 4.0
34 | merge_iou: 0.15
35 | realtime_merge_floor: 1
36 | recent_window_size: 240
37 | update_period: 20
38 |
39 | # Bayesian fusion
40 | bayesian_semantic_likelihood: ""
41 |
42 | # export
43 | shape_min_points: 50 # export and bbox extract
44 |
45 | save_da_dir: "" #"/data2/sgslam/output/da"
46 | #
47 | Graph:
48 | edge_radius_ratio: 2.0
49 | voxel_size: 0.02
50 | involve_floor_edge: 1
51 | ignore_labels: "ceiling." # Append " floor. carpet." if ignore floors
52 |
53 | SGNet:
54 | triplet_number: 20
55 | instance_match_threshold: 0.05
56 | warm_up: 10
57 |
58 | ShapeEncoder:
59 | init_voxel_size: 0.05
60 | init_radius: 0.0625
61 | padding: "random"
62 | K_shape_samples: 1024
63 | K_match_samples: 128
64 |
65 | LoopDetector:
66 | fuse_shape: 1 # set to 1 if fuse node feature with shape feature
67 | lcd_nodes: 3
68 | recall_nodes: 2
69 |
70 | Registration:
71 | noise_bound: 1.0
72 |
73 | # output
74 | save_da_images: 0
75 | tmp_dir: "/media/lch/SeagateExp/bag/sgslam/output"
76 |
--------------------------------------------------------------------------------
/config/realsense.yaml:
--------------------------------------------------------------------------------
1 | %YAML:1.0
2 |
3 | dataset: "realsense" # scannet, fusion_portable, realsense
4 |
5 | # camera parameters
6 | image_width: 640
7 | image_height: 480
8 | camera_fx: 619.18
9 | camera_fy: 618.17
10 | camera_cx: 336.51
11 | camera_cy: 246.95
12 |
13 | Mapping:
14 | # Instance
15 | voxel_length: 0.02
16 | sdf_trunc: 0.04
17 | min_voxel_weight: 0.1 # weight/observation_count
18 |
19 | #
20 | depth_scale: 1000.0
21 | depth_max: 4.0
22 |
23 | # associations
24 | min_det_masks: 150 # pixel-wise
25 | min_active_points: 80 # active instances
26 | max_box_area_ratio: 0.9 # box_area/image_area
27 | query_depth_vx_size: -1.0
28 | dilate_kernal: 3
29 | min_iou: 0.2
30 | search_radius: 4.0
31 |
32 | # merge over-segmentation
33 | merge_inflation: 3.5
34 | merge_iou: 0.4
35 | realtime_merge_floor: 1
36 | recent_window_size: 240
37 | update_period: 20
38 |
39 | # Bayesian fusion
40 | bayesian_semantic_likelihood: "" # To enable it, set it to the likelihood matrix path
41 |
42 | # export
43 | shape_min_points: 500 # export and bbox extract
44 |
45 | save_da_dir: "" #"/data2/sgslam/output/da"
46 | #
47 | Graph:
48 | edge_radius_ratio: 2.0
49 | voxel_size: 0.02
50 | involve_floor_edge: 1
51 | ignore_labels: "ceiling." # Append " floor. carpet." if ignore floors
52 |
53 | SGNet:
54 | triplet_number: 20
55 | instance_match_threshold: 0.05
56 | warm_up: 10
57 |
58 | ShapeEncoder:
59 | init_voxel_size: 0.05
60 | init_radius: 0.0625
61 | padding: "random"
62 | K_shape_samples: 1024
63 | K_match_samples: 128
64 |
65 | LoopDetector:
66 | fuse_shape: 1 # set to 1 if fuse node feature with shape feature
67 | lcd_nodes: 3
68 | recall_nodes: 2
69 |
70 | Registration:
71 | noise_bound: 1.0
72 |
73 | # output
74 | save_da_images: 0
75 | tmp_dir: "/media/lch/SeagateExp/bag/sgslam/output"
76 |
--------------------------------------------------------------------------------
/config/render_3rscan.yaml:
--------------------------------------------------------------------------------
1 | %YAML:1.0
2 |
3 | dataset: "realsense" # scannet, fusion_portable, realsense
4 |
5 | # camera parameters
6 | image_width: 640
7 | image_height: 480
8 | camera_fx: 619.18
9 | camera_fy: 618.17
10 | camera_cx: 336.51
11 | camera_cy: 246.95
12 |
13 | Mapping:
14 | # Instance
15 | voxel_length: 0.02
16 | sdf_trunc: 0.04
17 | min_voxel_weight: 0.1 # weight/observation_count
18 |
19 | #
20 | depth_scale: 1000.0
21 | depth_max: 4.0
22 |
23 | # associations
24 | min_det_masks: 150 # pixel-wise
25 | min_active_points: 80 # active instances
26 | max_box_area_ratio: 0.9 # box_area/image_area
27 | query_depth_vx_size: -1.0
28 | dilate_kernal: 3
29 | min_iou: 0.2
30 | search_radius: 4.0
31 |
32 | # merge over-segmentation
33 | merge_inflation: 3.5
34 | merge_iou: 0.4
35 | cleanup_period: 6600
36 | recent_window_size: 240
37 | update_period: 20
38 |
39 | # export
40 | shape_min_points: 100 # export and bbox extract
41 |
42 | save_da_dir: "" #"/data2/sgslam/output/da"
43 | #
44 | Graph:
45 | edge_radius_ratio: 2.0
46 | voxel_size: 0.02
47 | involve_floor_edge: 1
48 | ignore_labels: "ceiling" # Append " floor. carpet." if ignore floors
49 |
50 | SGNet:
51 | triplet_number: 20
52 | instance_match_threshold: 0.05
53 | warm_up: 10
54 |
55 | ShapeEncoder:
56 | init_voxel_size: 0.05
57 | init_radius: 0.0625
58 | padding: "random"
59 | K_shape_samples: 1024
60 | K_match_samples: 128
61 |
62 | LoopDetector:
63 | fuse_shape: 1 # set to 1 if fuse node feature with shape feature
64 | lcd_nodes: 3
65 | recall_nodes: 2
66 |
67 | Registration:
68 | noise_bound: 1.0
69 |
70 | # output
71 | save_da_images: 0
72 | tmp_dir: "/media/lch/SeagateExp/bag/sgslam/output"
73 |
--------------------------------------------------------------------------------
/config/rio.yaml:
--------------------------------------------------------------------------------
1 | %YAML:1.0
2 |
3 | dataset: "rio" # scannet, fusion_portable, realsense
4 |
5 | # camera parameters
6 | image_width: 960
7 | image_height: 540
8 | camera_fx: 756.832
9 | camera_fy: 756.026
10 | camera_cx: 492.889
11 | camera_cy: 270.419
12 | depth_scale: 1000.0
13 | depth_max: 5.0
14 |
15 | # volumetric
16 | voxel_length: 0.02
17 | sdf_trunc: 0.04
18 | min_active_points: 100 # active instances
19 | shape_min_points: 500 # export and bbox extract
20 |
21 | # associations
22 | min_det_masks: 100 # pixel-wise
23 | max_box_area_ratio: 0.9 # box_area/image_area
24 | query_depth_vx_size: -1.0
25 | dilate_kernal: 3
26 | min_iou: 0.3
27 | search_radius: 10.0
28 |
29 | # shape
30 | min_voxel_weight: 0.1 # weight/observation_count
31 |
32 | # merge over-segmentation
33 | merge_inflation: 3.5
34 | merge_iou: 0.4
35 | cleanup_period: 6600
36 |
37 | # output
38 | save_da_images: 0 # 1 for save
39 | tmp_dir: "/data2/3rscan_raw/viz"
40 |
--------------------------------------------------------------------------------
/config/scannet.yaml:
--------------------------------------------------------------------------------
1 | %YAML:1.0
2 |
3 | dataset: "scannet" # scannet, fusion_portable, realsense
4 |
5 | # camera parameters
6 | image_width: 640
7 | image_height: 480
8 | camera_fx: 557.59
9 | camera_fy: 578.73
10 | camera_cx: 318.91
11 | camera_cy: 242.68
12 |
13 | Mapping:
14 | # Instance
15 | voxel_length: 0.02
16 | sdf_trunc: 0.04
17 | min_voxel_weight: 0.1 # weight/observation_count
18 |
19 | #
20 | depth_scale: 1000.0
21 | depth_max: 4.0
22 |
23 | # associations
24 | min_det_masks: 100 # pixel-wise
25 | min_active_points: 80 # active instances
26 | max_box_area_ratio: 0.9 # box_area/image_area
27 | query_depth_vx_size: -1.0
28 | dilate_kernal: 3
29 | min_iou: 0.3
30 | search_radius: 4.0
31 |
32 | # merge over-segmentation
33 | merge_inflation: 4.0
34 | merge_iou: 0.3
35 | realtime_merge_floor: 1
36 | recent_window_size: 240
37 | update_period: 20
38 |
39 | # Bayesian fusion
40 | bayesian_semantic_likelihood: "/home/cliuci/code_ws/OpensetFusion/measurement_model/bayesian_model.txt"
41 |
42 | # export
43 | shape_min_points: 500 # export and bbox extract
44 |
45 | save_da_dir: "" #"/data2/sgslam/output/da"
46 | #
47 | Graph:
48 | edge_radius_ratio: 2.0
49 | voxel_size: 0.02
50 | involve_floor_edge: 1
51 | ignore_labels: "ceiling." # Append " floor. carpet." if ignore floors
52 |
53 | SGNet:
54 | triplet_number: 20
55 | instance_match_threshold: 0.05
56 | warm_up: 10
57 |
58 | ShapeEncoder:
59 | init_voxel_size: 0.05
60 | init_radius: 0.0625
61 | padding: "random"
62 | K_shape_samples: 1024
63 | K_match_samples: 128
64 |
65 | LoopDetector:
66 | fuse_shape: 1 # set to 1 if fuse node feature with shape feature
67 | lcd_nodes: 3
68 | recall_nodes: 2
69 |
70 | Registration:
71 | noise_bound: 1.0
72 |
73 | # output
74 | save_da_images: 0
75 | tmp_dir: "/media/lch/SeagateExp/bag/sgslam/output"
76 |
--------------------------------------------------------------------------------
/config/slabim.yaml:
--------------------------------------------------------------------------------
1 | %YAML:1.0
2 |
3 | dataset: "realsense" # scannet, fusion_portable, realsense
4 |
5 | # camera parameters
6 | image_width: 1600
7 | image_height: 1600
8 | camera_fx: 800.0
9 | camera_fy: 800.0
10 | camera_cx: 800.0
11 | camera_cy: 800.0
12 |
13 | Mapping:
14 | # Instance
15 | voxel_length: 0.05
16 | sdf_trunc: 0.04
17 | min_voxel_weight: 0.1 # weight/observation_count
18 |
19 | #
20 | depth_scale: 1000.0
21 | depth_max: 10.0
22 |
23 | # associations
24 | min_det_masks: 50 # pixel-wise
25 | min_active_points: 80 # active instances
26 | max_box_area_ratio: 0.9 # box_area/image_area
27 | query_depth_vx_size: -1.0
28 | dilate_kernal: 3
29 | min_iou: 0.2
30 | search_radius: 4.0
31 |
32 | # merge over-segmentation
33 | merge_inflation: 3.5
34 | merge_iou: 0.4
35 | realtime_merge_floor: 1
36 | recent_window_size: 240
37 | update_period: 1
38 |
39 | # Bayesian fusion
40 | bayesian_semantic_likelihood: "" # To enable it, set it to the likelihood matrix path
41 |
42 | # export
43 | shape_min_points: 20 # export and bbox extract
44 |
45 | save_da_dir: "" #"/data2/sgslam/output/da"
46 | #
47 | Graph:
48 | edge_radius_ratio: 2.0
49 | voxel_size: 0.02
50 | involve_floor_edge: 1
51 | ignore_labels: "ceiling." # Append " floor. carpet." if ignore floors
52 |
53 | SGNet:
54 | triplet_number: 20
55 | instance_match_threshold: 0.05
56 | warm_up: 10
57 |
58 | ShapeEncoder:
59 | init_voxel_size: 0.05
60 | init_radius: 0.0625
61 | padding: "random"
62 | K_shape_samples: 1024
63 | K_match_samples: 128
64 |
65 | LoopDetector:
66 | fuse_shape: 1 # set to 1 if fuse node feature with shape feature
67 | lcd_nodes: 3
68 | recall_nodes: 2
69 |
70 | Registration:
71 | noise_bound: 1.0
72 |
73 | # output
74 | save_da_images: 0
75 | tmp_dir: "/media/lch/SeagateExp/bag/sgslam/output"
76 |
--------------------------------------------------------------------------------
/doc/DATA.md:
--------------------------------------------------------------------------------
1 | # Data Format
2 |
3 | ## 1. Input RGB-D Sequence
4 | In SgSlam and ScanNet, their data sequence are collected from RGB-D sensors.
5 | The data format is as follow,
6 | ```bash
7 | |---ab0201_03a
8 | |---color
9 | |---depth
10 | |---pose
11 | |---intrinsic
12 | |---prediction_no_augment
13 | |---frame-000000_label.json # Image taging and detection results
14 | |---frame-000000_mask.png # Pixel-wise instance mask
15 | |--- ...
16 | ```
17 | In the sequence name ```ab0201_03a```, ```ab0201``` is a scene id and ```03a``` is a scanning index.
18 |
19 | ## 2. Output Results
20 | At the end of each sequence, the mapping node save the results in a pre-defined output folder. The output format is as below.
21 | ```bash
22 | |---output
23 | |---ab0201_03a
24 | |---instance_map.ply # points are colored instance-wise
25 | |---instance_info.txt
26 | |---instance_box.txt
27 | |---${idx}.ply (intanace-wise point cloud)
28 | |--- ...
29 | ```
30 |
31 | ## 3. Dataset Folder
32 | We organize the dataset folder as below.
33 | ```bash
34 | ${SGSLAM_DATAROOT}
35 | |---splits
36 | |---val.txt
37 | |---scans
38 | |---ab0201_03a
39 | |--- ...
40 | |---output
41 | |---online_mapping # mapping results
42 | ```
43 | ```ScanNet``` dataset folder is organized in the same way.
--------------------------------------------------------------------------------
/doc/rviz_gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HKUST-Aerial-Robotics/FM-Fusion/84f6a0f971ca3c4a7dd7ddd6342ca7bd08aff740/doc/rviz_gui.png
--------------------------------------------------------------------------------
/doc/seng_map_720p.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HKUST-Aerial-Robotics/FM-Fusion/84f6a0f971ca3c4a7dd7ddd6342ca7bd08aff740/doc/seng_map_720p.gif
--------------------------------------------------------------------------------
/doc/system.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HKUST-Aerial-Robotics/FM-Fusion/84f6a0f971ca3c4a7dd7ddd6342ca7bd08aff740/doc/system.png
--------------------------------------------------------------------------------
/doc/ust_atrium_light.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HKUST-Aerial-Robotics/FM-Fusion/84f6a0f971ca3c4a7dd7ddd6342ca7bd08aff740/doc/ust_atrium_light.gif
--------------------------------------------------------------------------------
/measurement_model/bayesian/detection_likelihood.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HKUST-Aerial-Robotics/FM-Fusion/84f6a0f971ca3c4a7dd7ddd6342ca7bd08aff740/measurement_model/bayesian/detection_likelihood.png
--------------------------------------------------------------------------------
/measurement_model/bayesian/likelihood_matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HKUST-Aerial-Robotics/FM-Fusion/84f6a0f971ca3c4a7dd7ddd6342ca7bd08aff740/measurement_model/bayesian/likelihood_matrix.png
--------------------------------------------------------------------------------
/measurement_model/bayesian/ram_likelihood.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HKUST-Aerial-Robotics/FM-Fusion/84f6a0f971ca3c4a7dd7ddd6342ca7bd08aff740/measurement_model/bayesian/ram_likelihood.png
--------------------------------------------------------------------------------
/measurement_model/bayesian_model.txt:
--------------------------------------------------------------------------------
1 | # openset_names closet_names likelihood_matrix
2 | wall,tile wall,floor,carpet,cabinet,closet,file cabinet,bed,dresser,chair,armchair,swivel chair,couch,table,side table,round table,kitchen table,door,glass door,window,bookshelf,shelf,picture,picture frame,poster,counter top,kitchen counter,computer desk,office desk,curtain,fridge,shower curtain,toilet bowl,sink,bathroom sink,bath,tub,bin
3 | wall,floor,cabinet,bed,chair,sofa,table,door,window,bookshelf,picture,counter,desk,curtain,refrigerator,shower curtain,toilet,sink,bathtub,otherfurniture
4 | 0.075617,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.009470,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
5 | 0.080379,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.011047,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
6 | 0.009790,0.720144,0.011175,0.000000,0.002166,0.000000,0.009081,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.009830
7 | 0.000000,0.074926,0.000000,0.000000,0.000000,0.000000,0.019295,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.006994
8 | 0.026366,0.000000,0.397324,0.000000,0.000000,0.000000,0.000000,0.027258,0.015156,0.121678,0.000000,0.000000,0.021404,0.000000,0.069495,0.000000,0.000000,0.000000,0.000000,0.049126
9 | 0.065171,0.000000,0.120085,0.000000,0.000000,0.000000,0.000000,0.215352,0.019302,0.063645,0.000000,0.000000,0.000000,0.000000,0.029548,0.000000,0.000000,0.000000,0.000000,0.056627
10 | 0.000000,0.000000,0.079965,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.056741,0.000000,0.000000,0.023806,0.000000,0.027828,0.000000,0.000000,0.000000,0.000000,0.016295
11 | 0.014688,0.000000,0.000000,0.763208,0.000000,0.016948,0.008452,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.042558
12 | 0.000000,0.000000,0.096581,0.000000,0.000000,0.000000,0.004585,0.000000,0.000000,0.047322,0.000000,0.000000,0.052364,0.000000,0.036029,0.000000,0.000000,0.000000,0.000000,0.032088
13 | 0.000000,0.000000,0.000000,0.017061,0.644090,0.186446,0.030744,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.090755
14 | 0.000000,0.000000,0.000000,0.000000,0.161128,0.210941,0.022109,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.067746
15 | 0.000000,0.000000,0.000000,0.000000,0.318009,0.022146,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
16 | 0.000000,0.000000,0.000000,0.000000,0.053638,0.685248,0.008946,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.035547
17 | 0.000000,0.002227,0.056196,0.000000,0.005525,0.000000,0.666827,0.000000,0.000000,0.000000,0.000000,0.062246,0.377809,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.070300
18 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.043157,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
19 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.062319,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
20 | 0.000000,0.000000,0.000000,0.000000,0.001482,0.000000,0.146021,0.000000,0.000000,0.000000,0.000000,0.000000,0.023519,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
21 | 0.041309,0.000000,0.022602,0.000000,0.000000,0.000000,0.000000,0.321690,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.010685
22 | 0.007111,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.033882,0.025441,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
23 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.007158,0.336141,0.000000,0.000000,0.000000,0.000000,0.055282,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
24 | 0.025842,0.000000,0.043706,0.000000,0.000000,0.000000,0.005226,0.000000,0.000000,0.622479,0.000000,0.000000,0.075105,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.018120
25 | 0.030105,0.000000,0.055712,0.000000,0.000000,0.000000,0.000000,0.008287,0.000000,0.432767,0.000000,0.000000,0.026736,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.021360
26 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.151566,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
27 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.518353,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
28 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.033725,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
29 | 0.000000,0.000000,0.045695,0.000000,0.000000,0.000000,0.008059,0.000000,0.000000,0.000000,0.000000,0.536332,0.000000,0.000000,0.000000,0.000000,0.000000,0.063300,0.000000,0.000000
30 | 0.000000,0.000000,0.029269,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.506114,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
31 | 0.000000,0.003906,0.057221,0.000000,0.003223,0.000000,0.165106,0.000000,0.000000,0.000000,0.000000,0.028585,0.603372,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.018662
32 | 0.000000,0.003917,0.039822,0.000000,0.000000,0.000000,0.129846,0.000000,0.000000,0.000000,0.000000,0.000000,0.398751,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000
33 | 0.028696,0.000000,0.015369,0.000000,0.000000,0.000000,0.000000,0.008345,0.021142,0.000000,0.000000,0.000000,0.000000,0.663413,0.000000,0.664545,0.000000,0.000000,0.000000,0.007796
34 | 0.014792,0.000000,0.010071,0.000000,0.000000,0.000000,0.000000,0.017180,0.000000,0.017153,0.000000,0.000000,0.000000,0.000000,0.460342,0.000000,0.000000,0.000000,0.000000,0.000000
35 | 0.008267,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.044914,0.000000,0.765570,0.000000,0.000000,0.000000,0.000000
36 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.948199,0.000000,0.000000,0.008644
37 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.670517,0.000000,0.000000
38 | 0.000000,0.000000,0.022246,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.343074,0.000000,0.000000
39 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.757643,0.000000
40 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.659859,0.000000
41 | 0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.292872
42 |
--------------------------------------------------------------------------------
/measurement_model/hardcode/likelihood_matrix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HKUST-Aerial-Robotics/FM-Fusion/84f6a0f971ca3c4a7dd7ddd6342ca7bd08aff740/measurement_model/hardcode/likelihood_matrix.png
--------------------------------------------------------------------------------
/scripts/process_slabim_pcd.py:
--------------------------------------------------------------------------------
1 | import os, sys, glob
2 | import cv2
3 | import open3d as o3d
4 | import numpy as np
5 | import multiprocessing as mp
6 |
7 | def read_sensor_shapes(dir):
8 | assert os.path.exists(dir), 'File not found: {}'.format(dir)
9 | with open(dir, 'r') as f:
10 | lines = f.readlines()
11 | height, width = 0, 0
12 | for line in lines:
13 | if 'color_width' in line:
14 | width = int(line.split(':')[1].strip())
15 | elif 'color_height' in line:
16 | height = int(line.split(':')[1].strip())
17 | return height, width
18 |
19 | def read_intrinsics(intrinsic_folder):
20 |
21 | h,w = read_sensor_shapes(os.path.join(intrinsic_folder,
22 | 'sensor_shapes.txt'))
23 | K = np.loadtxt(os.path.join(intrinsic_folder, 'intrinsic_depth.txt'))
24 |
25 | return K, h, w
26 |
27 | def project_xyz(xyz, K, h, w, depth_scale):
28 | z = xyz[2]
29 | u, v = np.dot(K, xyz)[:2] / z
30 | u, v = int(u), int(v)
31 | if u>=0 and u=0 and v0: depth_map[int(v), int(u)] = d
53 |
54 | return depth_map
55 |
56 | def project_pcd2image_cuda(points:np.ndarray,
57 | K:np.ndarray,
58 | height:int, width:int,
59 | DEPTH_SCALE:float=1000):
60 | import torch
61 | depth_map = torch.zeros((height, width)).cuda()
62 | N = points.shape[0]
63 |
64 | points = torch.from_numpy(points).float().cuda() # (N,3)
65 | height = torch.tensor(height).cuda()
66 | width = torch.tensor(width).cuda()
67 | DEPTH_SCALE = torch.tensor(DEPTH_SCALE).cuda()
68 | K = torch.from_numpy(K).float().cuda() # (3,3)
69 |
70 | # extend K to (N,3,3)
71 | K = K.unsqueeze(0).expand(N, -1, -1)
72 | z = points[:,2] # (N,)
73 | points = points.unsqueeze(-1) # (N,3,1)
74 |
75 | uv = torch.matmul(K, points) # (N,3,3) x (N,3,1) -> (N,3,1)
76 | uv = uv.squeeze() / z.unsqueeze(-1) # (N,3)
77 | u, v = uv[:, 0], uv[:, 1]
78 | u, v = u.int(), v.int()
79 |
80 | mask = (u>=0) & (u=0) & (v0)
81 | u, v, z = u[mask], v[mask], z[mask]
82 | depth_map[v, u] = z * DEPTH_SCALE
83 |
84 | depth_map = depth_map.cpu().numpy()
85 | return depth_map
86 |
87 | if __name__=='__main__':
88 | ############ SET CONFIG ############
89 | DATA_ROOT = '/data2/slabim/scans'
90 | DEPTH_SCALE = 1000
91 | DEPTH_POSFIX = '.png'
92 | PROJECT_DEPTH = -1 # 0: run on CPU, 1: run on GPU, -1: not run
93 | MERGE_GLOBAL_MAP=True
94 | SAVE_VIZ = False
95 |
96 | ###################################
97 | scans = [
98 | # 'ab0202_00a',
99 | # 'ab0205_00a',
100 | # 'ab0206_00a',
101 | # 'ab0304_00a',
102 | 'jk0001_00a',
103 | # 'jk0102_00a'
104 | ]
105 |
106 | if False: # debug
107 | # tmpdir = '/data2/sgslam/scans/uc0204_00a/depth/frame-000338.png'
108 | tmpdir = '/data2/slabim/scans/ab0202_00a/depth/frame-000000.png'
109 | depth = cv2.imread(tmpdir, cv2.IMREAD_UNCHANGED)
110 | print(depth.dtype, depth.shape)
111 | exit(0)
112 |
113 | for scan in scans:
114 | print('------------ Processing {} ------------'.format(scan))
115 |
116 | pcd_files = glob.glob(os.path.join(DATA_ROOT, scan, 'pcd', '*.pcd'))
117 | K, h, w = read_intrinsics(os.path.join(DATA_ROOT, scan, 'intrinsic'))
118 | os.makedirs(os.path.join(DATA_ROOT, scan, 'depth'), exist_ok=True)
119 | global_map = o3d.geometry.PointCloud()
120 |
121 | for pcd_file in sorted(pcd_files):
122 | frame_name = os.path.basename(pcd_file).split('.')[0]
123 | pcd = o3d.io.read_point_cloud(pcd_file) # in world coordinate
124 | print('Load {} points at {}'.format(len(pcd.points), os.path.basename(pcd_file)))
125 |
126 | if MERGE_GLOBAL_MAP:
127 | global_map += pcd
128 |
129 | if PROJECT_DEPTH<0: continue
130 | # Run depth projection
131 | T_wc = np.loadtxt(os.path.join(DATA_ROOT, scan, 'pose', frame_name+'.txt'))
132 | pcd.transform(np.linalg.inv(T_wc)) # in camera coordinate
133 | if PROJECT_DEPTH==1:
134 | depth = project_pcd2image_cuda(np.asarray(pcd.points), K, h, w)
135 | elif PROJECT_DEPTH==0:
136 | depth = project_pcd2image(np.asarray(pcd.points), K, h, w)
137 |
138 | depth = depth.astype(np.uint16)
139 | cv2.imwrite(os.path.join(DATA_ROOT, scan, 'depth', frame_name+DEPTH_POSFIX),
140 | depth)
141 | print('Write {} depth image'.format(frame_name+DEPTH_POSFIX))
142 |
143 | if SAVE_VIZ:
144 | depth_viz = cv2.applyColorMap(cv2.convertScaleAbs(depth, alpha=0.03), cv2.COLORMAP_JET)
145 | cv2.imwrite(os.path.join(DATA_ROOT, scan, 'viz', frame_name+'_depth'+DEPTH_POSFIX),
146 | depth_viz)
147 |
148 | if global_map.has_points():
149 | global_map = global_map.voxel_down_sample(voxel_size=0.05)
150 | o3d.io.write_point_cloud(os.path.join(DATA_ROOT, scan, 'mesh_o3d.ply'),
151 | global_map)
152 | print('Write global map')
153 |
154 |
--------------------------------------------------------------------------------
/scripts/uncompress_data.py:
--------------------------------------------------------------------------------
1 | import os,glob
2 | import zipfile
3 |
4 |
5 | if __name__=='__main__':
6 | ############## SET Configurations Here ##############
7 | dataroot = '/data2/sample_data/ScanNet'
8 | DELETE_ZIP_FILE = True
9 | #####################################################
10 |
11 | sequence_zips = glob.glob(os.path.join(dataroot, 'scans', '*.zip'))
12 |
13 | for sequence_zip in sequence_zips:
14 | sequence_name = os.path.basename(sequence_zip).split('.')[0]
15 | sequence_folder = os.path.join(dataroot, 'scans', sequence_name)
16 | if os.path.exists(sequence_folder):
17 | print('[Warning] Skip {} because it already exists.'.format(sequence_folder))
18 | continue
19 | print('------- {} -------'.format(sequence_name))
20 |
21 | # unzip the sequence
22 | with zipfile.ZipFile(sequence_zip, 'r') as z:
23 | z.extractall(os.path.join(dataroot, 'scans'))
24 |
25 | if DELETE_ZIP_FILE: # after unzipping, delete the zip file
26 | os.remove(sequence_zip)
27 | print('deleted {}'.format(sequence_zip))
28 |
29 |
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.18)
2 |
3 | project(fmfusion LANGUAGES CXX VERSION 1.0)
4 |
5 | set(CMAKE_BUILD_TYPE Release)
6 | set(CMAKE_CXX_STANDARD 17)
7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
8 |
9 | # Dependencies
10 | add_definitions(-D _GLIBCXX_USE_CXX11_ABI=1)
11 | include_directories(${CMAKE_CURRENT_SOURCE_DIR})
12 |
13 | # FMFusion::Mapping
14 | set(FMFUSION_HEADER
15 | mapping/SubVolume.h
16 | mapping/Detection.h
17 | mapping/Instance.h
18 | mapping/SemanticMapping.h
19 | cluster/PoseGraph.h
20 | tools/Tools.h
21 | tools/Utility.h
22 | tools/IO.h
23 | tools/TicToc.h
24 | Common.h
25 | )
26 |
27 | set(FMFUSION_SRC
28 | mapping/SubVolume.cpp
29 | mapping/Detection.cpp
30 | mapping/Instance.cpp
31 | mapping/SemanticMapping.cpp
32 | cluster/PoseGraph.cpp
33 | tools/Visualization.cpp
34 | tools/Utility.cpp
35 | tools/IO.cpp
36 | )
37 |
38 | # FMFusion::LoopDetection
39 | if (LOOP_DETECTION)
40 | include(${PROJECT_ROOT_DIR}/cmake/gtsam.cmake)
41 | include_directories("thirdparty/Kimera-RPGO/include")
42 | include_directories("thirdparty/extensions")
43 | set(CMAKE_PREFIX_PATH "/home/cliuci/tools/libtorch")
44 | set(LIB_UTF8PROC "/usr/local/lib/libutf8proc.so")
45 |
46 | add_subdirectory(tokenizer)
47 | add_subdirectory(thirdparty/extensions)
48 | add_subdirectory(thirdparty/G3Reg)
49 | add_subdirectory(thirdparty/Kimera-RPGO)
50 | find_package(Torch REQUIRED)
51 |
52 | list(APPEND FMFUSION_HEADER
53 | tools/g3reg_api.h
54 | sgloop/Graph.h
55 | sgloog/BertBow.h
56 | sgloop/SGNet.h
57 | sgloop/ShapeEncoder.h
58 | sgloop/LoopDetector.h
59 | )
60 |
61 | list(APPEND FMFUSION_SRC
62 | tools/IO.cpp
63 | sgloop/Graph.cpp
64 | sgloop/BertBow.cpp
65 | sgloop/SGNet.cpp
66 | sgloop/ShapeEncoder.cpp
67 | sgloop/LoopDetector.cpp
68 | )
69 |
70 | list(APPEND ALL_TARGET_LIBRARIES
71 | ${TORCH_LIBRARIES}
72 | G3REG::g3reg
73 | KimeraRPGO
74 | tokenizer
75 | extensions
76 | )
77 | message(STATUS "TORCH_LIBRARIES: ${TORCH_LIBRARIES}")
78 | message(STATUS "TORCH_INCLUDE_DIRS: ${TORCH_INCLUDE_DIRS}")
79 | endif()
80 |
81 | add_library(fmfusion SHARED ${FMFUSION_SRC})
82 | target_link_libraries(fmfusion PRIVATE ${ALL_TARGET_LIBRARIES})
83 |
84 | # Executables
85 | add_executable(IntegrateRGBD)
86 | target_sources(IntegrateRGBD PRIVATE IntegrateRGBD.cpp)
87 | target_link_libraries(IntegrateRGBD PRIVATE ${ALL_TARGET_LIBRARIES} fmfusion)
88 |
89 | add_executable(IntegrateInstanceMap)
90 | target_sources(IntegrateInstanceMap PRIVATE IntegrateInstanceMap.cpp)
91 | target_link_libraries(IntegrateInstanceMap PRIVATE ${ALL_TARGET_LIBRARIES} fmfusion)
92 |
93 | # Install loop closure detection(LCD) module
94 | # The LCD is in developing. More complete instruction will be released soon.
95 | if (LOOP_DETECTION)
96 | add_executable(TestLoop)
97 | target_sources(TestLoop PRIVATE TestLoop.cpp)
98 | target_link_libraries(TestLoop PRIVATE ${ALL_TARGET_LIBRARIES} fmfusion)
99 |
100 | add_executable(TestRegister)
101 | target_sources(TestRegister PRIVATE TestRegister.cpp)
102 | target_link_libraries(TestRegister PRIVATE ${ALL_TARGET_LIBRARIES} fmfusion)
103 |
104 | add_executable(Test3RScanRegister)
105 | target_sources(Test3RScanRegister PRIVATE Test3RscanRegister.cpp)
106 | target_link_libraries(Test3RScanRegister PRIVATE ${ALL_TARGET_LIBRARIES} fmfusion)
107 |
108 | add_executable(robustPoseAvg)
109 | target_sources(robustPoseAvg PRIVATE robustPoseAvg.cpp)
110 | target_link_libraries(robustPoseAvg PRIVATE ${ALL_TARGET_LIBRARIES} fmfusion -ltbb)
111 |
112 | add_executable(testTwoAgentsPGO)
113 | target_sources(testTwoAgentsPGO PRIVATE testTwoAgentsPGO.cpp)
114 | target_link_libraries(testTwoAgentsPGO PRIVATE ${ALL_TARGET_LIBRARIES} fmfusion -ltbb)
115 |
116 | endif ()
117 |
118 | if (RUN_HYDRA)
119 | find_package(GTest REQUIRED)
120 | find_package(DBoW2 REQUIRED)
121 | add_library(DBoW2::DBoW2 INTERFACE IMPORTED)
122 | set_target_properties(DBoW2::DBoW2 PROPERTIES
123 | INTERFACE_LINK_LIBRARIES "${DBoW2_LIBRARIES}"
124 | INTERFACE_INCLUDE_DIRECTORIES "${DBoW2_INCLUDE_DIRS}")
125 |
126 | add_executable(TestHydra)
127 | target_sources(TestHydra PRIVATE TestHydra.cpp)
128 | target_link_libraries(TestHydra PRIVATE ${ALL_TARGET_LIBRARIES}
129 | fmfusion
130 | DBoW2::DBoW2
131 | GTest::gtest_main)
132 | endif()
133 |
134 | # Install fmfusion library
135 | if (INSTALL_FMFUSION)
136 | message(STATUS "Installing fmfusion library. So it can be used in ros warpper.")
137 |
138 | install(TARGETS fmfusion
139 | LIBRARY DESTINATION lib
140 | ARCHIVE DESTINATION lib
141 | RUNTIME DESTINATION bin
142 | INCLUDES DESTINATION include
143 | )
144 | install(FILES
145 | Common.h
146 | DESTINATION include/fmfusion
147 | )
148 | install(FILES
149 | cluster/PoseGraph.h
150 | DESTINATION include/fmfusion/cluster
151 | )
152 | install(FILES
153 | mapping/BayesianLabel.h
154 | mapping/SemanticDict.h
155 | mapping/SubVolume.h
156 | mapping/Detection.h
157 | mapping/Instance.h
158 | mapping/SemanticMapping.h
159 | DESTINATION include/fmfusion/mapping
160 | )
161 | install(FILES
162 | sgloop/Graph.h
163 | sgloop/SGNet.h
164 | sgloop/ShapeEncoder.h
165 | sgloop/LoopDetector.h
166 | sgloop/BertBow.h
167 | sgloop/Initialization.h
168 | DESTINATION include/fmfusion/sgloop
169 | )
170 | install(FILES
171 | tools/Tools.h
172 | tools/Eval.h
173 | tools/Utility.h
174 | tools/Color.h
175 | tools/IO.h
176 | tools/TicToc.h
177 | DESTINATION include/fmfusion/tools
178 | )
179 |
180 | if(LOOP_DETECTION)
181 | install(FILES
182 | tools/g3reg_api.h
183 | DESTINATION include/fmfusion/tools
184 | )
185 |
186 | install(FILES
187 | tokenizer/logging.h
188 | tokenizer/basic_string_util.h
189 | tokenizer/text_tokenizer.h
190 | tokenizer/bert_tokenizer.h
191 | DESTINATION include/fmfusion/tokenizer
192 | )
193 |
194 | install(FILES
195 | thirdparty/extensions/cpu/torch_helper.h
196 | thirdparty/extensions/cpu/grid_subsampling.h
197 | thirdparty/extensions/cpu/radius_neighbors.h
198 | DESTINATION include/fmfusion/thirdparty/extensions/cpu
199 | )
200 | endif()
201 | endif ()
202 |
--------------------------------------------------------------------------------
/src/Common.h:
--------------------------------------------------------------------------------
1 | #ifndef FMFUSOIN_COMMON_H
2 | #define FMFUSOIN_COMMON_H
3 |
4 | #include "open3d/Open3D.h"
5 | #include "opencv2/opencv.hpp"
6 | #include "cluster/PoseGraph.h"
7 |
8 | namespace fmfusion
9 | {
10 | using namespace std;
11 |
12 | struct InstanceConfig{
13 | double voxel_length = 0.02;
14 | double sdf_trunc = 0.04;
15 | open3d::camera::PinholeCameraIntrinsic intrinsic;
16 | int max_label_measures = 20;
17 | double min_voxel_weight = 2.0;
18 | double cluster_eps = 0.05;
19 | int cluster_min_points = 20;
20 | bool bayesian_semantic = false;
21 |
22 | const std::string print_msg() const{
23 | std::stringstream msg;
24 | msg<<" - voxel_length: "< noise_bound_vec = {1.0};
172 | const std::string print_msg() const{
173 | std::stringstream msg;
174 | msg<<" - noise_bound_vec: "< CvMatPtr;
212 | typedef open3d::geometry::Image O3d_Image;
213 | typedef open3d::geometry::Geometry O3d_Geometry;
214 | typedef std::shared_ptr O3d_Geometry_Ptr;
215 | typedef std::shared_ptr O3d_Image_Ptr;
216 | typedef std::pair NodePair;
217 | typedef std::pair LoopPair;
218 |
219 | typedef uint32_t InstanceId;
220 | typedef std::vector InstanceIdList;
221 | typedef open3d::geometry::PointCloud O3d_Cloud;
222 | typedef std::shared_ptr O3d_Cloud_Ptr;
223 |
224 | }
225 |
226 | #endif //FMFUSOIN_COMMON_H
--------------------------------------------------------------------------------
/src/GraphMatch.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "open3d/Open3D.h"
7 | #include "opencv2/opencv.hpp"
8 |
9 | #include "tools/Utility.h"
10 | #include "mapping/SemanticMapping.h"
11 |
12 | using namespace open3d::visualization;
13 |
14 | class InstanceWindow: public gui::Window{
15 | // using Super = gui::window;
16 |
17 | public:
18 | InstanceWindow(const std::string &title)
19 | : gui::Window(title, 1280, 720) {
20 | // gui::Application::GetInstance().Initialize();
21 | // gui::Application::GetInstance().AddWindow(this);
22 | auto pcd = open3d::io::CreatePointCloudFromFile("/media/lch/SeagateExp/dataset_/FusionPortable/output/bday_04/instance_map.ply");
23 | std::cout<<"pcd size: "<points_.size()<mapping_cfg, sg_config->instance_cfg);
49 | fmfusion::SemanticMapping scene_graph_tar(sg_config->mapping_cfg, sg_config->instance_cfg);
50 |
51 | // load
52 | scene_graph_src.load(map_folder+"/"+src_sequence);
53 |
54 | // Update
55 | scene_graph_src.merge_overlap_instances();
56 | scene_graph_src.extract_bounding_boxes();
57 | scene_graph_src.merge_overlap_structural_instances();
58 | if(tar_sequence!=""){
59 | fmfusion::o3d_utility::LogInfo("Todo {}.",tar_sequence);
60 | Eigen::Matrix4d T_tar_offset;
61 | T_tar_offset<<1,0,0,0,
62 | 0,1,0,0,
63 | 0,0,1,6.0,
64 | 0,0,0,1;
65 | scene_graph_tar.load(map_folder+"/"+tar_sequence);
66 | scene_graph_tar.Transform(T_tar_offset);
67 | scene_graph_tar.merge_overlap_instances();
68 | scene_graph_tar.extract_bounding_boxes();
69 | scene_graph_tar.merge_overlap_structural_instances();
70 | }
71 |
72 | // Visualize
73 | auto geometries = scene_graph_src.get_geometries(true,true);
74 | if(!scene_graph_tar.is_empty()){
75 | auto geometries_tar = scene_graph_tar.get_geometries(true,true);
76 | geometries.insert(geometries.end(),geometries_tar.begin(),geometries_tar.end());
77 | std::cout<<"Update tar scene graph from "<
5 | #include "open3d/Open3D.h"
6 | #include "open3d/utility/IJsonConvertible.h"
7 |
8 | namespace fmfusion
9 | {
10 |
11 | class PoseGraphFile: public open3d::utility::IJsonConvertible
12 | {
13 | public:
14 | PoseGraphFile() {};
15 |
16 | public:
17 | bool ConvertToJsonValue(Json::Value &value) const override {return true;};
18 | bool ConvertFromJsonValue(const Json::Value &value) override;
19 |
20 | public:
21 | std::unordered_map poses_;
22 |
23 | };
24 |
25 | }
26 | #endif
--------------------------------------------------------------------------------
/src/mapping/BayesianLabel.h:
--------------------------------------------------------------------------------
1 | #ifndef BAYESIANLABEL_H
2 | #define BAYESIANLABEL_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "Detection.h"
10 | #include "tools/Utility.h"
11 |
12 | namespace fmfusion
13 | {
14 |
15 | class BayesianLabel
16 | {
17 | public:
18 | /// \brief Load the likelihood matrix from file.
19 | BayesianLabel(const std::string &likelihood_file, bool verbose=false)
20 | {
21 | std::cout<<"Semantic fusion load likelihood matrix from "
22 | < &measurements,
36 | Eigen::VectorXf &probability_vector)
37 | {
38 | if(measurements.empty() || !is_loaded) return false;
39 |
40 | int rows = measurements.size();
41 | int cols = predict_label_vec.size();
42 | probability_vector = Eigen::VectorXf::Zero(cols);
43 | for(int i=0;i &measurements,
56 | Eigen::VectorXf &probability_vector)
57 | {
58 | if(measurements.empty() || !is_loaded) return false;
59 | std::vector measurements_vec;
60 | for(const auto &label_score:measurements){
61 | measurements_vec.emplace_back(std::make_pair(label_score.first, label_score.second));
62 | }
63 |
64 | return update_measurements(measurements_vec, probability_vector);
65 | }
66 |
67 | std::vector get_label_vec()const{
68 | return predict_label_vec;
69 | }
70 |
71 | int get_num_classes() const{
72 | return predict_label_vec.size();
73 | }
74 |
75 |
76 | private:
77 |
78 | /// \brief Load the likelihood matrix from text file.
79 | bool load_likelihood_matrix(const std::string &likelihood_file, bool verbose)
80 | {
81 | std::ifstream file(likelihood_file);
82 | std::stringstream msg;
83 | if(file.is_open()){
84 | // read in lines
85 | std::string line;
86 | std::getline(file, line); // header
87 | std::getline(file, line); //
88 | // std::cout< values = utility::split_str(line, ",");
101 | for(int j=0;j predict_label_vec;
132 | std::vector measure_label_vec;
133 | std::map measure_label_map;
134 |
135 | Eigen::MatrixXf likelihood_matrix;
136 |
137 |
138 | };
139 |
140 | }
141 |
142 | #endif // BAYESIANLABEL_H
143 |
--------------------------------------------------------------------------------
/src/mapping/Detection.cpp:
--------------------------------------------------------------------------------
1 | #include "json/json.h"
2 | #include "Detection.h"
3 |
4 |
5 | namespace fmfusion
6 | {
7 |
8 | Detection::Detection(const int id): id_(id)
9 | {
10 |
11 | }
12 |
13 | std::string Detection::extract_label_string() const
14 | {
15 | std::stringstream ss;
16 | for(auto label_score:labels_){
17 | ss< DetectionFile::split_str(
24 | const std::string s, const std::string delim)
25 | {
26 | std::vector list;
27 | auto start = 0U;
28 | auto end = s.find(delim);
29 | while (true) {
30 | list.push_back(s.substr(start, end - start));
31 | if (end == std::string::npos)
32 | break;
33 | start = end + delim.length();
34 | end = s.find(delim, start);
35 | }
36 | return list;
37 | }
38 |
39 | bool DetectionFile::ConvertFromJsonValue(const Json::Value &value)
40 | {
41 | using namespace open3d;
42 |
43 | if (!value.isObject()) {
44 | std::cerr<<"DetectionField read JSON failed: unsupported json format."<(detection_id);
65 |
66 | auto label_score_map = object["labels"]; // {label:score}
67 | msg<labels_.push_back(std::make_pair(label,score));
72 | msg<<"("<bbox_.u0 = box_array[0].asDouble();
77 | detection->bbox_.v0 = box_array[1].asDouble();
78 | detection->bbox_.u1 = box_array[2].asDouble();
79 | detection->bbox_.v1 = box_array[3].asDouble();
80 |
81 | // update
82 | // int box_area = (detection->bbox_.u1-detection->bbox_.u0)*(detection->bbox_.v1-detection->bbox_.v0);
83 | // if(box_areabbox_.u0<<","<bbox_.v0<<","<bbox_.u1<<","<bbox_.v1<<") ";
86 | msg<<"\n";
87 | // }
88 | }
89 |
90 | // update raw tags
91 | auto raw_tag_array = split_str(value["raw_tags"].asString(), ".");
92 | msg<<"raw tags (";
93 | for(auto tag:raw_tag_array){
94 | raw_tags.push_back(tag.substr(tag.find_first_not_of(" ")));
95 | msg<<"$"< to_remove_detections;
129 |
130 | for(int idx=1;idx<=K;++idx){
131 | cv::Mat mask = (detection_map == idx);
132 | assert (detections[idx-1]->id_ == idx), "detection id is not consistent";
133 | detections[idx-1]->instances_idxs_ = mask; // [H,W], CV_8UC1
134 | if(cv::countNonZero(mask)get_box_area()>max_box_area_){
135 | to_remove_detections.push_back(idx-1);
136 | }
137 | }
138 |
139 | // remove invalid detections
140 | if(to_remove_detections.size()>0){
141 | std::cerr<<"Remove "<=0;--i){
143 | detections.erase(detections.begin()+to_remove_detections[i]);
144 | }
145 | }
146 |
147 | return true;
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/src/mapping/Detection.h:
--------------------------------------------------------------------------------
1 | #ifndef FMFUSION_DETECTION_H
2 | #define FMFUSION_DETECTION_H
3 |
4 | #include "opencv2/opencv.hpp"
5 | #include "open3d/utility/IJsonConvertible.h"
6 |
7 |
8 | namespace fmfusion
9 | {
10 |
11 | typedef std::pair LabelScore;
12 |
13 | struct BoundingBox{
14 | double u0,v0,u1,v1;
15 | };
16 |
17 | class Detection
18 | {
19 | public:
20 | Detection(const int id);
21 | Detection(const std::vector &labels, const BoundingBox &bbox, const cv::Mat &instances_idxs):
22 | labels_(labels), bbox_(bbox), instances_idxs_(instances_idxs) {};
23 | std::string extract_label_string() const;
24 | const cv::Point get_box_center(){return cv::Point((bbox_.u0+bbox_.u1)/2,(bbox_.v0+bbox_.v1)/2);};
25 | const int get_box_area(){return (bbox_.u1-bbox_.u0)*(bbox_.v1-bbox_.v0);}
26 |
27 | public:
28 | unsigned int id_;
29 | std::vector labels_;
30 | BoundingBox bbox_;
31 | cv::Mat instances_idxs_; // [H,W], CV_8UC1
32 | };
33 |
34 | typedef std::shared_ptr DetectionPtr;
35 |
36 | class DetectionFile: public open3d::utility::IJsonConvertible
37 | {
38 | public:
39 | DetectionFile(int min_mask, int max_box_area): min_mask_(min_mask), max_box_area_(max_box_area) {};
40 |
41 | bool updateInstanceMap(const std::string &instance_file);
42 |
43 | public:
44 | bool ConvertToJsonValue(Json::Value &value) const override {return true;};
45 | bool ConvertFromJsonValue(const Json::Value &value) override;
46 |
47 | protected:
48 | std::vector split_str(const std::string s, const std::string delim);
49 | int min_mask_;
50 | int max_box_area_;
51 |
52 | public:
53 | std::vector detections;
54 | std::vector raw_tags;
55 | std::vector filter_tags;
56 | };
57 |
58 | }
59 |
60 | #endif //FMFUSION_DETECTION_H
--------------------------------------------------------------------------------
/src/mapping/Instance.h:
--------------------------------------------------------------------------------
1 | #ifndef FMFUSION_INSTANCE_H
2 | #define FMFUSION_INSTANCE_H
3 |
4 | #include
5 | #include
6 |
7 | #include "open3d/Open3D.h"
8 | #include "Detection.h"
9 | #include "Common.h"
10 | #include "SubVolume.h"
11 |
12 | namespace fmfusion {
13 |
14 | namespace o3d_utility = open3d::utility;
15 |
16 | class Instance {
17 |
18 | public:
19 | Instance(const InstanceId id, const unsigned int frame_id, const InstanceConfig &config);
20 |
21 | ~Instance() {};
22 |
23 | void init_bayesian_fusion(const std::vector &label_set);
24 | public:
25 | void integrate(const int &frame_id,
26 | const std::shared_ptr &rgbd_image, const Eigen::Matrix4d &pose);
27 |
28 | // Record measured labels and update the predicted label.
29 | void update_label(const DetectionPtr &detection);
30 |
31 | /// \brief Update the centroid from volume units origins.
32 | void fast_update_centroid() { centroid = volume_->get_centroid(); };
33 |
34 | bool update_point_cloud(int cur_frame_id, int min_frame_gap = 10);
35 |
36 | /// @brief add the probability vector from each measurement.
37 | /// And update the predicted label.
38 | /// @param probability_vector_ computed from BayesianLabel
39 | /// @return success flag
40 | bool update_semantic_probability(const Eigen::VectorXf &probability_vector_);
41 |
42 | void merge_with(const O3d_Cloud_Ptr &other_cloud,
43 | const std::unordered_map &label_measurements,
44 | const int &observations_);
45 |
46 | void extract_write_point_cloud();
47 |
48 | void filter_pointcloud_statistic();
49 |
50 | bool filter_pointcloud_by_cluster();
51 |
52 | void CreateMinimalBoundingBox();
53 |
54 | /// \brief label_str:label_name(score),label_name(score),...
55 | /// record previouse measured labels
56 | void load_previous_labels(const std::string &labels_str);
57 |
58 | void load_obser_count(const int &obs_count){
59 | observation_count = obs_count;
60 | }
61 |
62 | void save(const std::string &path);
63 |
64 | void load(const std::string &path);
65 |
66 | public:
67 | SubVolume *get_volume() { return volume_; }
68 |
69 | LabelScore get_predicted_class() const {
70 | return predicted_label;
71 | }
72 |
73 | std::unordered_map get_measured_labels() const {
74 | return measured_labels;
75 | }
76 |
77 | size_t get_cloud_size() const;
78 |
79 | O3d_Cloud_Ptr get_complete_cloud() const;
80 |
81 | InstanceConfig get_config() const { return config_; }
82 |
83 | std::string get_measured_labels_string() const {
84 | std::stringstream label_measurements;
85 | for (const auto &label_score: measured_labels) {
86 | label_measurements << label_score.first
87 | << "(" << std::fixed << std::setprecision(2) << label_score.second << "),";
88 | }
89 | return label_measurements.str();
90 | }
91 |
92 | int get_observation_count() const {
93 | return observation_count;
94 | }
95 |
96 | InstanceId get_id() const { return id_; }
97 |
98 | void change_id(InstanceId new_id) {
99 | id_ = new_id;
100 | }
101 |
102 | O3d_Cloud_Ptr get_point_cloud() const;
103 |
104 | bool isBayesianFusion() const { return bayesian_label; }
105 |
106 | private:
107 | /// @brief Extract the label with the maximum probability
108 | /// as the predicted class.
109 | void extract_bayesian_prediciton();
110 |
111 | public:
112 | unsigned int frame_id_; // latest integration frame id
113 | unsigned int update_frame_id; // update point cloud and bounding box
114 | Eigen::Vector3d color_;
115 | std::shared_ptr observed_image_mask; // Poject volume on image plane;
116 | // open3d::pipelines::integration::InstanceTSDFVolume *volume_;
117 | SubVolume *volume_;
118 | O3d_Cloud_Ptr point_cloud;
119 | Eigen::Vector3d centroid;
120 | Eigen::Vector3d normal;
121 | std::shared_ptr min_box;
122 |
123 | private:
124 | InstanceId id_; // >=1
125 | InstanceConfig config_;
126 | std::unordered_map measured_labels;
127 | LabelScore predicted_label;
128 | int observation_count;
129 | O3d_Cloud_Ptr merged_cloud;
130 |
131 | // For Bayesian likelihood
132 | std::vector semantic_labels;
133 | Eigen::VectorXf probability_vector;
134 | bool bayesian_label;
135 | };
136 |
137 | typedef std::shared_ptr InstancePtr;
138 |
139 | } // namespace fmfusion
140 |
141 | #endif // FMFUSION_INSTANCE_H
142 |
143 |
--------------------------------------------------------------------------------
/src/mapping/SemanticDict.h:
--------------------------------------------------------------------------------
1 | #ifndef SEMANTICDICT_H
2 | #define SEMANTICDICT_H
3 |
4 | #include
5 |
6 | namespace fmfusion
7 | {
8 | typedef unordered_map SemanticDictionary;
9 |
10 | class SemanticDictServer
11 | {
12 | public:
13 | SemanticDictServer():semantic_dict()
14 | {
15 | semantic_dict.clear();
16 | };
17 |
18 | ~SemanticDictServer(){};
19 |
20 | /// @brief Update the semantic dictionary.
21 | /// @param semantic_label
22 | /// @param instance_id
23 | void update_instance(const std::string &semantic_label,
24 | const InstanceId &instance_id)
25 | {
26 | if(semantic_label=="floor" ||semantic_label=="carpet"){
27 | if(semantic_dict.find(semantic_label)==semantic_dict.end())
28 | semantic_dict[semantic_label] = {instance_id};
29 | else
30 | semantic_dict[semantic_label].push_back(instance_id);
31 | }
32 | };
33 |
34 | void clear()
35 | {
36 | semantic_dict.clear();
37 | };
38 |
39 | std::vector query_instances(const std::string &label)
40 | {
41 | if(semantic_dict.find(label)==semantic_dict.end())
42 | return {};
43 | return semantic_dict[label];
44 | }
45 |
46 | // SemanticDictionary get_semantic_dict()
47 | // {
48 | // return semantic_dict;
49 | // }
50 |
51 | private:
52 | SemanticDictionary semantic_dict;
53 | };
54 |
55 | }
56 |
57 | #endif
58 |
--------------------------------------------------------------------------------
/src/mapping/SemanticMapping.h:
--------------------------------------------------------------------------------
1 | #ifndef FMFUSION_SEMANTICMAPPING_H
2 | #define FMFUSION_SEMANTICMAPPING_H
3 |
4 | #include
5 | #include
6 |
7 | #include "Common.h"
8 | #include "tools/Color.h"
9 | #include "tools/Utility.h"
10 | #include "Instance.h"
11 | #include "SemanticDict.h"
12 | #include "BayesianLabel.h"
13 |
14 | namespace fmfusion {
15 |
16 | class SemanticMapping {
17 | public:
18 | SemanticMapping(const MappingConfig &mapping_cfg, const InstanceConfig &instance_cfg);
19 |
20 | ~SemanticMapping() {};
21 |
22 | public:
23 | void integrate(const int &frame_id,
24 | const std::shared_ptr &rgbd_image, const Eigen::Matrix4d &pose,
25 | std::vector &detections);
26 |
27 | int merge_overlap_instances(std::vector instance_list = std::vector());
28 |
29 | int merge_floor(bool verbose=false);
30 |
31 | int merge_overlap_structural_instances(bool merge_all = true);
32 |
33 | int merge_other_instances(std::vector &instances);
34 |
35 | void extract_point_cloud(const std::vector instance_list = std::vector());
36 |
37 | /// \brief Extract and update bounding box for each instance.
38 | void extract_bounding_boxes();
39 |
40 | int update_instances(const int &cur_frame_id, const std::vector &instance_list);
41 |
42 | /// @brief clean all and update all.
43 | void refresh_all_semantic_dict();
44 |
45 | std::shared_ptr export_global_pcd(bool filter = false, float vx_size = -1.0);
46 |
47 | std::vector export_instance_centroids(int earliest_frame_id = -1,
48 | bool verbose=false) const;
49 |
50 | std::vector export_instance_annotations(int earliest_frame_id = -1
51 | ) const;
52 |
53 | bool query_instance_info(const std::vector &names,
54 | std::vector ¢roids,
55 | std::vector &labels);
56 |
57 | void remove_invalid_instances();
58 |
59 | /// \brief Get geometries for each instance.
60 | std::vector>
61 | get_geometries(bool point_cloud = true, bool bbox = false);
62 |
63 | bool is_empty() { return instance_map.empty(); }
64 |
65 | InstancePtr get_instance(const InstanceId &name) { return instance_map[name]; }
66 |
67 | void Transform(const Eigen::Matrix4d &pose);
68 |
69 | /// @brief
70 | /// @param path output sequence folder
71 | /// @return
72 | bool Save(const std::string &path);
73 |
74 | bool load(const std::string &path);
75 |
76 | /// \brief Export instances to the vector.
77 | /// Filter instances that are too small or not been observed for a long time.
78 | void export_instances(std::vector &names, std::vector &instances,
79 | int earliest_frame_id = 0);
80 |
81 | protected:
82 | /// \brief match vector in [K,1], K is the number of detections;
83 | /// If detection k is associated, match[k] = matched_instance_id
84 | int
85 | data_association(const std::vector &detections, const std::vector &active_instances,
86 | Eigen::VectorXi &matches,
87 | std::vector> &ambiguous_pairs);
88 |
89 | int create_new_instance(const DetectionPtr &detection, const unsigned int &frame_id,
90 | const std::shared_ptr &rgbd_image,
91 | const Eigen::Matrix4d &pose);
92 |
93 | std::vector search_active_instances(const O3d_Cloud_Ptr &depth_cloud, const Eigen::Matrix4d &pose,
94 | const double search_radius = 5.0);
95 |
96 | void update_active_instances(const std::vector &active_instances);
97 |
98 | void update_recent_instances(const int &frame_id,
99 | const std::vector &active_instances,
100 | const std::vector &new_instances);
101 |
102 | bool IsSemanticSimilar(const std::unordered_map &measured_labels_a,
103 | const std::unordered_map &measured_labels_b);
104 |
105 | /// \brief Compute the 2D IoU (horizontal plane) between two oriented bounding boxes.
106 | double Compute2DIoU(const open3d::geometry::OrientedBoundingBox &box_a,
107 | const open3d::geometry::OrientedBoundingBox &box_b);
108 |
109 | /// \brief Compute the 3D IoU between two point clouds.
110 | /// \param cloud_a, point cloud of the larger instance
111 | /// \param cloud_b, point cloud of the smaller instance
112 | double Compute3DIoU(const O3d_Cloud_Ptr &cloud_a, const O3d_Cloud_Ptr &cloud_b, double inflation = 1.0);
113 |
114 | int merge_ambiguous_instances(const std::vector> &ambiguous_pairs);
115 |
116 | // Recent observed instances
117 | std::unordered_set recent_instances;
118 |
119 | private:
120 | // Config config_;
121 | MappingConfig mapping_config;
122 | InstanceConfig instance_config;
123 | std::unordered_map instance_map;
124 | std::unordered_map> label_instance_map;
125 | SemanticDictServer semantic_dict_server;
126 | BayesianLabel *bayesian_label;
127 |
128 | InstanceId latest_created_instance_id;
129 | int last_cleanup_frame_id;
130 | int last_update_frame_id;
131 |
132 | };
133 |
134 |
135 | } // namespace fmfusion
136 |
137 |
138 | #endif //FMFUSION_SEMANTICMAPPING_H
139 |
--------------------------------------------------------------------------------
/src/mapping/SubVolume.cpp:
--------------------------------------------------------------------------------
1 | #include "SubVolume.h"
2 |
3 | namespace fmfusion
4 | {
5 |
6 | SubVolume::SubVolume(double voxel_length,
7 | double sdf_trunc,
8 | TSDFVolumeColorType color_type,
9 | int volume_unit_resolution,
10 | int depth_sampling_stride)
11 | : ScalableTSDFVolume(voxel_length,
12 | sdf_trunc,
13 | color_type,
14 | volume_unit_resolution,
15 | depth_sampling_stride){}
16 |
17 | SubVolume::~SubVolume() {}
18 |
19 | bool SubVolume::query_observed_points(const PointCloudPtr &cloud_scan,
20 | PointCloudPtr &cloud_observed,
21 | const float max_dist)
22 | {
23 |
24 | for (size_t i = 0; i < cloud_scan->points_.size(); i++)
25 | {
26 | Eigen::Vector3d point = cloud_scan->points_[i];
27 | // if (volume_units_.find(index)!=volume_units_.end())
28 | // observed_units.insert(index);
29 |
30 | Eigen::Vector3d p_locate =
31 | point - Eigen::Vector3d(0.5, 0.5, 0.5) * voxel_length_;
32 | Eigen::Vector3i index0 = LocateVolumeUnit(p_locate);
33 | auto unit_itr = volume_units_.find(index0);
34 | if (unit_itr == volume_units_.end()) {
35 | continue;
36 | }
37 |
38 | const auto &volume0 = *unit_itr->second.volume_;
39 | Eigen::Vector3i idx0; // root voxel index
40 | Eigen::Vector3d p_grid =
41 | (p_locate - index0.cast() * volume_unit_length_) /
42 | voxel_length_; // point position in voxels
43 | for (int i = 0; i < 3; i++) {
44 | idx0(i) = (int)std::floor(p_grid(i));
45 | if (idx0(i) < 0) idx0(i) = 0;
46 | if (idx0(i) >= volume_unit_resolution_)
47 | idx0(i) = volume_unit_resolution_ - 1;
48 | }
49 |
50 | // iterate over neighbor voxels
51 | for (int i = 0; i < 8; i++) {
52 | float w0 = 0.0f;
53 | float f0 = 0.0f;
54 | Eigen::Vector3i idx1 = idx0 + shift[i];
55 | if (idx1(0) < volume_unit_resolution_ &&
56 | idx1(1) < volume_unit_resolution_ &&
57 | idx1(2) < volume_unit_resolution_) {
58 | w0 = volume0.voxels_[volume0.IndexOf(idx1)].weight_;
59 | f0 = volume0.voxels_[volume0.IndexOf(idx1)].tsdf_;
60 | }
61 | else {
62 | Eigen::Vector3i index1 = index0;
63 | for (int j = 0; j < 3; j++) {
64 | if (idx1(j) >= volume_unit_resolution_) {
65 | idx1(j) -= volume_unit_resolution_;
66 | index1(j) += 1;
67 | }
68 | }
69 | auto unit_itr1 = volume_units_.find(index1);
70 | if (unit_itr1 != volume_units_.end()) {
71 | const auto &volume1 = *unit_itr1->second.volume_;
72 | w0 = volume1.voxels_[volume1.IndexOf(idx1)].weight_;
73 | f0 = volume1.voxels_[volume1.IndexOf(idx1)].tsdf_;
74 | }
75 | }
76 | if(w0!=0.0f && f0<0.98f && f0>=-0.98f){
77 | cloud_observed->points_.push_back(point);
78 | break;
79 | }
80 | }
81 |
82 | }
83 | return true;
84 |
85 | }
86 |
87 | Eigen::Vector3d SubVolume::get_centroid()
88 | {
89 | Eigen::Vector3d centroid = Eigen::Vector3d::Zero();
90 | int count = 0;
91 | for(const auto&unit:volume_units_){
92 | if(unit.second.volume_){
93 | centroid += unit.second.volume_->origin_;
94 | count++;
95 | }
96 | }
97 |
98 | if(count>0) return centroid/count;
99 | else return Eigen::Vector3d::Zero();
100 | }
101 |
102 | } // namespace fmfusion
103 |
104 |
--------------------------------------------------------------------------------
/src/mapping/SubVolume.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "open3d/utility/Logging.h"
11 | #include "open3d/geometry/PointCloud.h"
12 | #include "open3d/pipelines/integration/UniformTSDFVolume.h"
13 | #include "open3d/pipelines/integration/ScalableTSDFVolume.h"
14 | #include "open3d/pipelines/integration/MarchingCubesConst.h"
15 |
16 | namespace fmfusion
17 | {
18 | // class UniformTSDFVolume;
19 | typedef open3d::pipelines::integration::ScalableTSDFVolume ScalableTSDFVolume;
20 | typedef std::shared_ptr PointCloudPtr;
21 | typedef open3d::pipelines::integration::TSDFVolumeColorType TSDFVolumeColorType;
22 |
23 | class SubVolume : public ScalableTSDFVolume {
24 | public:
25 | SubVolume(double voxel_length,
26 | double sdf_trunc,
27 | TSDFVolumeColorType color_type,
28 | int volume_unit_resolution = 16,
29 | int depth_sampling_stride = 4);
30 | ~SubVolume() override;
31 |
32 | public:
33 | // std::shared_ptr ExtractWeightedPointCloud(const float min_weight=0.0);
34 |
35 | /// @brief Generate scan cloud from depth image.
36 | /// And query observed voxel points from the volume.
37 | bool query_observed_points(const PointCloudPtr &cloud_scan,
38 | PointCloudPtr &cloud_observed,
39 | const float max_dist=0.98f);
40 |
41 | //todo: inaccurate
42 | /// @brief Get the centroid of all volume units origin.
43 | /// @return
44 | Eigen::Vector3d get_centroid();
45 |
46 | protected:
47 | Eigen::Vector3i LocateVolumeUnit(const Eigen::Vector3d &point) {
48 | return Eigen::Vector3i((int)std::floor(point(0) / volume_unit_length_),
49 | (int)std::floor(point(1) / volume_unit_length_),
50 | (int)std::floor(point(2) / volume_unit_length_));
51 | };
52 |
53 | Eigen::Vector3d GetNormalAt(const Eigen::Vector3d &p);
54 |
55 | double GetTSDFAt(const Eigen::Vector3d &p);
56 |
57 | };
58 |
59 |
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/src/sgloop/BertBow.cpp:
--------------------------------------------------------------------------------
1 | #include "BertBow.h"
2 |
3 |
4 | namespace fmfusion
5 | {
6 |
7 | std::vector get_the_bytes(const std::string &filename){
8 | std::ifstream input(filename, std::ios::binary);
9 | if(!input.is_open()){
10 | throw std::runtime_error("Cannot open file " + filename + " for reading.");
11 | return std::vector();
12 | }
13 |
14 | std::vector bytes(
15 | (std::istreambuf_iterator(input)),
16 | (std::istreambuf_iterator()));
17 | input.close();
18 | return bytes;
19 | }
20 |
21 |
22 | BertBow::BertBow(const std::string &words_file, const std::string &word_features_file, bool cuda_device_):
23 | cuda_device(cuda_device_)
24 | {
25 | if(load_word2int(words_file) && load_word_features(word_features_file))
26 | {
27 | N = word2int.size();
28 | assert(N==(word_features.size(0)-1));
29 | load_success = true;
30 | if(cuda_device) word_features = word_features.to(torch::kCUDA);
31 | std::cout<<"Load "<< N <<" words features on CUDA\n";
32 | // < &words,
43 | torch::Tensor &features)
44 | {
45 | int Q = words.size();
46 | if(Q<1) return false;
47 | float indices[Q];
48 | // float mask[Q]; // mask the invalid query
49 |
50 | for(int i=0; i bytes = get_the_bytes(word_features_file);
97 | if(bytes.empty()){
98 | return false;
99 | }
100 | else{
101 | // std::cout<<"Load word features from "<()==0);
105 |
106 | torch::Tensor zero_features = torch::zeros({1, word_features.size(1)});
107 | word_features = torch::cat({zero_features, word_features}, 0);
108 | if(cuda_device) word_features = word_features.to(torch::kCUDA);
109 | // std::cout<<"feature shape "<
14 | #include
15 | #include
16 | #include
17 |
18 |
19 | namespace fmfusion
20 | {
21 |
22 | std::vector get_the_bytes(const std::string &filename);
23 |
24 | class BertBow
25 | {
26 | public:
27 | BertBow(const std::string &words_file, const std::string &word_features_file, bool cuda_device_=false);
28 | ~BertBow() {};
29 |
30 | /// Read N words, and return NxD features.
31 | bool query_semantic_features(const std::vector &words,
32 | torch::Tensor &features);
33 |
34 | bool is_loaded() const {return load_success;};
35 |
36 | void get_word2int(std::map& word2int_map)const{
37 | word2int_map = word2int;
38 | }
39 |
40 | private:
41 | bool load_word2int(const std::string &word2int_file);
42 | bool load_word_features(const std::string &word_features_file);
43 |
44 | void warm_up();
45 |
46 | private:
47 | bool load_success;
48 | bool cuda_device;
49 | std::map word2int;
50 | torch::Tensor word_features;
51 | int N;
52 |
53 | torch::Tensor indices_tensor;
54 |
55 | };
56 |
57 | } // namespace fmfusion
58 |
59 |
60 | #endif // BERTBOW_H
61 |
62 |
--------------------------------------------------------------------------------
/src/sgloop/Graph.h:
--------------------------------------------------------------------------------
1 | #ifndef FMFUSION_GRAPH_H
2 | #define FMFUSION_GRAPH_H
3 |
4 | #include
5 | #include
6 |
7 | namespace fmfusion
8 | {
9 |
10 | typedef std::array Corner;
11 |
12 | class Node
13 | {
14 | public:
15 | Node(uint32_t node_id_, InstanceId instance_id_):
16 | id(node_id_), instance_id(instance_id_){};
17 |
18 | /// @brief
19 | /// @param max_corner_number
20 | /// @param corner_vector
21 | /// @param padding_value For the corners less than max_corner_number, padding with this value.
22 | void sample_corners(const int &max_corner_number, std::vector &corner_vector, int padding_value=0);
23 |
24 | ~Node(){};
25 | public:
26 | uint32_t id; // node id, reordered from 0
27 | InstanceId instance_id; // corresponds to original instance id
28 | std::string semantic;
29 | std::vector neighbors;
30 | std::vector corners; // corner1, corner2. The anchor node and corner nodes form a triplet.
31 | O3d_Cloud_Ptr cloud;
32 | Eigen::Vector3d centroid;
33 | Eigen::Vector3d bbox_shape; // x,y,z
34 |
35 | };
36 | typedef std::shared_ptr NodePtr;
37 |
38 |
39 | class Edge
40 | {
41 | public:
42 | Edge(const uint32_t &id1, const uint32_t &id2): src_id(id1), ref_id(id2) {
43 | distance = 0.0;
44 | }
45 | ~Edge() {};
46 |
47 | public:
48 | uint32_t src_id; // start node
49 | uint32_t ref_id; // end node
50 | double distance;
51 | };
52 | typedef std::shared_ptr EdgePtr;
53 |
54 |
55 | struct DataDict{
56 | std::vector xyz; // (X,3)
57 | std::vector length_vec; // (B,) [|X1|,|X2|,...,|XB|]
58 | std::vector labels; // (X,)
59 | std::vector centroids; // (N,3)
60 | std::vector nodes; // (N,)
61 | std::vector instances; // (N,)
62 | std::string print_instances(){
63 | std::stringstream msg;
64 | for (auto instance:instances){
65 | msg< &instances);
87 |
88 | /// \brief Update the node information received from communication server.
89 | /// It cleared the graph and insert.
90 | int subscribe_coarse_nodes(const float &latest_timestamp,
91 | const std::vector &node_indices,
92 | const std::vector &instances,
93 | const std::vector ¢roids);
94 |
95 | /// \brief Update the dense point cloud information received from communication server.
96 | /// It should be updated after the coarse nodes are updated.
97 | int subscribde_dense_points(const float &sub_timestamp,
98 | const std::vector &xyz,
99 | const std::vector