Merge branch 'master' of github.com:introlab/rtabmap into gtest

This commit is contained in:
matlabbe
2025-12-21 11:59:54 -08:00
129 changed files with 11607 additions and 5269 deletions
+1
View File
@@ -14,5 +14,6 @@ RUN set -ex && \
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \ echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \
chmod 0440 /etc/sudoers.d/${USERNAME} chmod 0440 /etc/sudoers.d/${USERNAME}
RUN mkdir -p /home/${USERNAME}/Documents/RTAB-Map && chown -R ${USERNAME} /home/${USERNAME}
RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
RUN echo 'source /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc RUN echo 'source /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc
+2 -1
View File
@@ -9,9 +9,10 @@
}, },
"workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind", "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind",
"workspaceFolder": "/home/vscode/rtabmap", "workspaceFolder": "/home/vscode/rtabmap",
//"mounts": ["source=${localEnv:HOME}/Documents/RTAB-Map,target=/home/vscode/Documents/RTAB-Map,type=bind,consistency=cached"],
"settings": { "settings": {
"terminal.integrated.defaultProfile.linux": "bash" "terminal.integrated.defaultProfile.linux": "bash"
}, },
"remoteUser": "vscode", "remoteUser": "vscode",
"runArgs": ["--privileged"] "runArgs": ["--privileged", "--network=host"]
} }
+2 -1
View File
@@ -14,5 +14,6 @@ RUN set -ex && \
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \ echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \
chmod 0440 /etc/sudoers.d/${USERNAME} chmod 0440 /etc/sudoers.d/${USERNAME}
RUN mkdir -p /home/${USERNAME}/Documents/RTAB-Map && chown -R ${USERNAME} /home/${USERNAME}
RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
RUN echo 'source /opt/ros/humble/setup.bash' >> /home/${USERNAME}/.bashrc RUN echo 'source /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc
+2 -2
View File
@@ -9,10 +9,10 @@
}, },
"workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind", "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind",
"workspaceFolder": "/home/vscode/rtabmap", "workspaceFolder": "/home/vscode/rtabmap",
//"mounts": ["source=${localEnv:HOME}/Documents/RTAB-Map,target=/home/vscode/Documents/RTAB-Map,type=bind,consistency=cached"],
"settings": { "settings": {
"terminal.integrated.defaultProfile.linux": "bash" "terminal.integrated.defaultProfile.linux": "bash"
}, },
"remoteUser": "vscode", "remoteUser": "vscode",
"runArgs": ["--privileged"] "runArgs": ["--privileged", "--network=host"]
} }
@@ -0,0 +1,104 @@
ARG ROS_DISTRO=jazzy
FROM osrf/ros:${ROS_DISTRO}-desktop
# Install build dependencies with Qt6 (issue: rtabmap has black window)
#RUN apt-get update && \
# apt-get install -y git software-properties-common ros-${ROS_DISTRO}-rtabmap-ros libqt6* qt6* qml6* && \
# apt-get remove -y ros-${ROS_DISTRO}-rtabmap* ros-${ROS_DISTRO}-gtsam ros-${ROS_DISTRO}-libg2o libpcl* libqt5* qt5* libvtk* libopencv* && \
# apt-get clean && rm -rf /var/lib/apt/lists/
# Install build dependencies with Qt5
RUN apt-get update && \
apt-get install -y git software-properties-common ros-${ROS_DISTRO}-rtabmap-ros && \
apt-get remove -y ros-${ROS_DISTRO}-rtabmap* ros-${ROS_DISTRO}-gtsam ros-${ROS_DISTRO}-libg2o libpcl* libvtk* libopencv* && \
apt-get clean && rm -rf /var/lib/apt/lists/
# remove ubuntu user
RUN touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu
RUN apt-get update && apt-get install -y sudo && \
apt-get clean && rm -rf /var/lib/apt/lists/
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=1000
RUN set -ex && \
groupadd --gid ${USER_GID} ${USERNAME} && \
useradd --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} && \
usermod -a -G sudo ${USERNAME} && \
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \
chmod 0440 /etc/sudoers.d/${USERNAME}
RUN mkdir -p /home/${USERNAME}/Documents/RTAB-Map
RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
WORKDIR /home/${USERNAME}/
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
# We build main dependencies from source, cleaning up the
# build directory after install but keeping the source code
# to change version/reinstall from inside the dev container
# if needed.
# Build latest VTK with Qt6
RUN git clone https://github.com/Kitware/VTK.git
RUN cd VTK && \
mkdir build && \
cd build && \
cmake -DVTK_GROUP_ENABLE_Qt=YES .. && \
make -j$(nproc) && \
make install && \
make clean
# Build latest PCL with latest VTK
# Make sure all libraries depending on Eigen are built with same CXX standard (17)
RUN git clone https://github.com/PointCloudLibrary/pcl.git
RUN cd pcl && \
mkdir build && \
cd build && \
cmake -DCMAKE_CXX_STANDARD=17 -DBUILD_tools=ON -DPCL_ENABLE_AVX=OFF -DPCL_ENABLE_MARCHNATIVE=OFF -DPCL_ENABLE_SSE=OFF .. && \
make -j$(nproc) && \
make install && \
make clean
# Build latest OpenCV
RUN git clone https://github.com/opencv/opencv.git
RUN git clone https://github.com/opencv/opencv_contrib.git
RUN cd opencv && \
mkdir build && \
cd build && \
cmake -DBUILD_opencv_python3=OFF -DBUILD_opencv_python_bindings_generator=OFF -DBUILD_opencv_python_tests=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_TESTS=OFF -DOPENCV_ENABLE_NONFREE=ON -DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules .. && \
make -j$(nproc) && \
make install && \
make clean
# Build latest gtsam
RUN git clone https://github.com/borglab/gtsam.git
RUN cd gtsam && \
mkdir build && \
cd build && \
cmake -DCMAKE_CXX_STANDARD=17 -DGTSAM_BUILD_WITH_MARCH_NATIVE=OFF -DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF -DGTSAM_BUILD_TESTS=OFF -DGTSAM_BUILD_STATIC_LIBRARY=OFF -DGTSAM_BUILD_UNSTABLE=OFF -DGTSAM_INSTALL_CPPUNILITE=OFF -DGTSAM_USE_SYSTEM_EIGEN=ON -DCMAKE_BUILD_TYPE=Release .. && \
make -j$(nproc) && \
make install && \
make clean
# Build latest g2o
RUN git clone https://github.com/RainerKuemmerle/g2o.git
RUN cd g2o && \
mkdir build && \
cd build && \
cmake -DCMAKE_CXX_STANDARD=17 -DBUILD_WITH_MARCH_NATIVE=OFF -DG2O_BUILD_APPS=OFF -DG2O_BUILD_EXAMPLES=OFF -DG2O_USE_OPENGL=OFF -DCMAKE_BUILD_TYPE=Release .. && \
make -j$(nproc) && \
make install && \
make clean
# ros2 seems not sourcing by default its multi-arch folders
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/ros/${ROS_DISTRO}/lib/x86_64-linux-gnu
RUN ldconfig
RUN chown -R ${USERNAME} /home/${USERNAME}
@@ -0,0 +1,21 @@
{
"build": {
"dockerfile": "Dockerfile",
"args": {
"ROS_DISTRO": "jazzy"
}
},
"customizations": {
"vscode": {
"extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools"]
}
},
"workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind",
"workspaceFolder": "/home/vscode/rtabmap",
//"mounts": ["source=${localEnv:HOME}/Documents/RTAB-Map,target=/home/vscode/Documents/RTAB-Map,type=bind,consistency=cached"],
"settings": {
"terminal.integrated.defaultProfile.linux": "bash"
},
"remoteUser": "vscode",
"runArgs": ["--privileged", "--network=host"]
}
+4 -1
View File
@@ -1,4 +1,6 @@
FROM introlab3it/rtabmap:noble-deps FROM introlab3it/rtabmap:noble-deps
# For devcontainer
# remove ubuntu user # remove ubuntu user
RUN touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu RUN touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu
@@ -16,5 +18,6 @@ RUN set -ex && \
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \ echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \
chmod 0440 /etc/sudoers.d/${USERNAME} chmod 0440 /etc/sudoers.d/${USERNAME}
RUN mkdir -p /home/${USERNAME}/Documents/RTAB-Map && chown -R ${USERNAME} /home/${USERNAME}
RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
RUN echo 'source /opt/ros/jazzy/setup.bash' >> /home/${USERNAME}/.bashrc RUN echo 'source /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc
+2 -1
View File
@@ -9,9 +9,10 @@
}, },
"workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind", "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind",
"workspaceFolder": "/home/vscode/rtabmap", "workspaceFolder": "/home/vscode/rtabmap",
//"mounts": ["source=${localEnv:HOME}/Documents/RTAB-Map,target=/home/vscode/Documents/RTAB-Map,type=bind,consistency=cached"],
"settings": { "settings": {
"terminal.integrated.defaultProfile.linux": "bash" "terminal.integrated.defaultProfile.linux": "bash"
}, },
"remoteUser": "vscode", "remoteUser": "vscode",
"runArgs": ["--privileged"] "runArgs": ["--privileged", "--network=host"]
} }
+2
View File
@@ -73,5 +73,7 @@ RUN set -ex && \
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \ echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${USERNAME} && \
chmod 0440 /etc/sudoers.d/${USERNAME} chmod 0440 /etc/sudoers.d/${USERNAME}
RUN mkdir -p /home/${USERNAME}/Documents/RTAB-Map && chown -R ${USERNAME} /home/${USERNAME}
RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
RUN echo 'source /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc RUN echo 'source /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc
+1
View File
@@ -9,6 +9,7 @@
}, },
"workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind", "workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind",
"workspaceFolder": "/home/vscode/rtabmap", "workspaceFolder": "/home/vscode/rtabmap",
//"mounts": ["source=${localEnv:HOME}/Documents/RTAB-Map,target=/home/vscode/Documents/RTAB-Map,type=bind,consistency=cached"],
"settings": { "settings": {
"terminal.integrated.defaultProfile.linux": "bash" "terminal.integrated.defaultProfile.linux": "bash"
}, },
+136 -73
View File
@@ -19,8 +19,8 @@ SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules")
# VERSION # VERSION
####################### #######################
SET(RTABMAP_MAJOR_VERSION 0) SET(RTABMAP_MAJOR_VERSION 0)
SET(RTABMAP_MINOR_VERSION 22) SET(RTABMAP_MINOR_VERSION 23)
SET(RTABMAP_PATCH_VERSION 0) SET(RTABMAP_PATCH_VERSION 3)
SET(RTABMAP_VERSION SET(RTABMAP_VERSION
${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}.${RTABMAP_PATCH_VERSION}) ${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}.${RTABMAP_PATCH_VERSION})
@@ -83,14 +83,6 @@ IF(MINGW)
SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import") SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-auto-import")
ENDIF(MINGW) ENDIF(MINGW)
# GCC 4 required
IF(UNIX OR MINGW)
EXEC_PROGRAM( gcc ARGS "-dumpversion" OUTPUT_VARIABLE GCC_VERSION )
IF(GCC_VERSION VERSION_LESS "4.0.0")
MESSAGE(FATAL_ERROR "GCC ${GCC_VERSION} found, but version 4.x.x minimum is required")
ENDIF(GCC_VERSION VERSION_LESS "4.0.0")
ENDIF(UNIX OR MINGW)
#The CDT Error Parser cannot handle error messages that span #The CDT Error Parser cannot handle error messages that span
#more than one line, which is the default gcc behavior. #more than one line, which is the default gcc behavior.
#In order to force gcc to generate single line error messages with no line wrapping #In order to force gcc to generate single line error messages with no line wrapping
@@ -220,10 +212,11 @@ option(WITH_ZED "Include ZED sdk support" ON)
option(WITH_ZEDOC "Include ZED Open Capture support" ON) option(WITH_ZEDOC "Include ZED Open Capture support" ON)
option(WITH_REALSENSE "Include RealSense support" ON) option(WITH_REALSENSE "Include RealSense support" ON)
option(WITH_REALSENSE_SLAM "Include RealSenseSlam support" ON) option(WITH_REALSENSE_SLAM "Include RealSenseSlam support" ON)
option(WITH_REALSENSE2 "Include RealSense support" ON) option(WITH_REALSENSE2 "Include RealSense2 support" ON)
option(WITH_MYNTEYE "Include mynteye-s support" ON) option(WITH_MYNTEYE "Include mynteye-s support" ON)
option(WITH_DEPTHAI "Include depthai-core support" OFF) option(WITH_DEPTHAI "Include depthai-core support" OFF)
option(WITH_XVSDK "Include XVisio SDK support" OFF) option(WITH_XVSDK "Include XVisio SDK support" OFF)
option(WITH_ORBBEC_SDK "Include Orbbec SDK v2 support" OFF)
option(WITH_OCTOMAP "Include OctoMap support" ON) option(WITH_OCTOMAP "Include OctoMap support" ON)
option(WITH_GRIDMAP "Include GridMap support" OFF) option(WITH_GRIDMAP "Include GridMap support" OFF)
option(WITH_CPUTSDF "Include CPUTSDF support" OFF) option(WITH_CPUTSDF "Include CPUTSDF support" OFF)
@@ -235,8 +228,9 @@ option(WITH_DVO "Include DVO support" OFF)
option(WITH_ORB_SLAM "Include ORB_SLAM2 or ORB_SLAM3 support" OFF) option(WITH_ORB_SLAM "Include ORB_SLAM2 or ORB_SLAM3 support" OFF)
option(WITH_OKVIS "Include OKVIS support" OFF) option(WITH_OKVIS "Include OKVIS support" OFF)
option(WITH_MSCKF_VIO "Include MSCKF_VIO support" OFF) option(WITH_MSCKF_VIO "Include MSCKF_VIO support" OFF)
option(WITH_VINS "Include VINS-Fusion support" OFF) option(WITH_VINS_FUSION "Include VINS-Fusion support" OFF)
option(WITH_OPENVINS "Include OpenVINS support" OFF) option(WITH_OPENVINS "Include OpenVINS support" OFF)
option(WITH_CUVSLAM "Include cuVSLAM support" OFF)
option(WITH_MADGWICK "Include Madgwick IMU filtering support" ON) option(WITH_MADGWICK "Include Madgwick IMU filtering support" ON)
option(WITH_FASTCV "Include FastCV support" ON) option(WITH_FASTCV "Include FastCV support" ON)
option(WITH_OPENMP "Include OpenMP support" ON) option(WITH_OPENMP "Include OpenMP support" ON)
@@ -413,7 +407,7 @@ IF(NOT VTK_FOUND)
ENDIF(NOT VTK_FOUND) ENDIF(NOT VTK_FOUND)
IF(WITH_TORCH) IF(WITH_TORCH)
FIND_PACKAGE(Torch QUIET) FIND_PACKAGE(Torch)
IF(TORCH_FOUND) IF(TORCH_FOUND)
MESSAGE(STATUS "Found Torch: ${TORCH_INCLUDE_DIRS}") MESSAGE(STATUS "Found Torch: ${TORCH_INCLUDE_DIRS}")
ENDIF(TORCH_FOUND) ENDIF(TORCH_FOUND)
@@ -435,14 +429,14 @@ IF(WITH_PDAL)
ENDIF(WITH_PDAL) ENDIF(WITH_PDAL)
IF(WITH_LIBLAS) IF(WITH_LIBLAS)
FIND_PACKAGE(libLAS QUIET) FIND_PACKAGE(libLAS)
IF(libLAS_FOUND) IF(libLAS_FOUND)
MESSAGE(STATUS "Found libLAS ${libLAS_VERSION}: ${libLAS_INCLUDE_DIRS}") MESSAGE(STATUS "Found libLAS ${libLAS_VERSION}: ${libLAS_INCLUDE_DIRS}")
ENDIF(libLAS_FOUND) ENDIF(libLAS_FOUND)
ENDIF(WITH_LIBLAS) ENDIF(WITH_LIBLAS)
IF(WITH_CUDASIFT) IF(WITH_CUDASIFT)
FIND_PACKAGE(CudaSift 3 QUIET) FIND_PACKAGE(CudaSift 3)
IF(CudaSift_FOUND) IF(CudaSift_FOUND)
MESSAGE(STATUS "Found CudaSift") MESSAGE(STATUS "Found CudaSift")
ENDIF(CudaSift_FOUND) ENDIF(CudaSift_FOUND)
@@ -513,35 +507,34 @@ ENDIF(WITH_DC1394)
IF(WITH_G2O) IF(WITH_G2O)
FIND_PACKAGE(g2o NO_MODULE) FIND_PACKAGE(g2o NO_MODULE)
IF(g2o_FOUND) IF(g2o_FOUND)
MESSAGE(STATUS "Found g2o (targets)") MESSAGE(STATUS "Found g2o (targets)")
SET(G2O_FOUND ${g2o_FOUND}) SET(G2O_FOUND ${g2o_FOUND})
get_target_property(G2O_INCLUDES g2o::core INTERFACE_INCLUDE_DIRECTORIES) get_target_property(G2O_INCLUDES g2o::core INTERFACE_INCLUDE_DIRECTORIES)
MESSAGE(STATUS "g2o include dir: ${G2O_INCLUDES}") MESSAGE(STATUS "g2o include dir: ${G2O_INCLUDES}")
FIND_FILE(G2O_FACTORY_FILE g2o/core/factory.h FIND_FILE(G2O_FACTORY_FILE g2o/core/factory.h
PATHS ${G2O_INCLUDES} PATHS ${G2O_INCLUDES}
NO_DEFAULT_PATH) NO_DEFAULT_PATH)
FILE(READ ${G2O_FACTORY_FILE} TMPTXT) FILE(READ ${G2O_FACTORY_FILE} TMPTXT)
STRING(FIND "${TMPTXT}" "shared_ptr" matchres) STRING(FIND "${TMPTXT}" "shared_ptr" matchres)
IF(${matchres} EQUAL -1) IF(${matchres} EQUAL -1)
MESSAGE(STATUS "Old g2o factory version detected without shared ptr (factory file: ${G2O_FACTORY_FILE}).") MESSAGE(STATUS "Old g2o factory version detected without shared ptr (factory file: ${G2O_FACTORY_FILE}).")
SET(G2O_CPP11 2) SET(G2O_CPP11 2)
ELSE() ELSE()
MESSAGE(STATUS "Latest g2o factory version detected with shared ptr (factory file: ${G2O_FACTORY_FILE}).") MESSAGE(STATUS "Latest g2o factory version detected with shared ptr (factory file: ${G2O_FACTORY_FILE}).")
SET(G2O_CPP11 1) SET(G2O_CPP11 1)
ENDIF() ENDIF()
FIND_FILE(G2O_SBA_UTILS_FILE g2o/types/sba/sba_utils.h
PATHS ${G2O_INCLUDES}
NO_DEFAULT_PATH)
IF(G2O_SBA_UTILS_FILE)
SET(G2O_WITH_SBA_UTILS 1)
ELSE()
SET(G2O_WITH_SBA_UTILS 0)
ENDIF()
ELSE() ELSE()
FIND_PACKAGE(G2O QUIET) FIND_PACKAGE(G2O QUIET)
IF(G2O_FOUND) IF(G2O_FOUND)
MESSAGE(STATUS "Found g2o: ${G2O_INCLUDE_DIRS}") MESSAGE(STATUS "Found g2o: ${G2O_INCLUDE_DIRS}")
FIND_FILE(G2O_FACTORY_FILE g2o/core/factory.h
PATHS ${G2O_INCLUDES}
NO_DEFAULT_PATH)
FILE(READ ${G2O_FACTORY_FILE} TMPTXT)
STRING(FIND "${TMPTXT}" "shared_ptr" matchres)
IF(NOT ${matchres} EQUAL -1)
MESSAGE(STATUS "Latest g2o factory version detected with shared ptr (factory file: ${G2O_FACTORY_FILE}).")
SET(G2O_CPP11 1)
ENDIF()
ENDIF(G2O_FOUND) ENDIF(G2O_FOUND)
ENDIF() ENDIF()
ENDIF(WITH_G2O) ENDIF(WITH_G2O)
@@ -549,6 +542,16 @@ ENDIF(WITH_G2O)
IF(WITH_GTSAM) IF(WITH_GTSAM)
# Force config mode to ignore PCL's findGTSAM.cmake file # Force config mode to ignore PCL's findGTSAM.cmake file
FIND_PACKAGE(GTSAM CONFIG QUIET) FIND_PACKAGE(GTSAM CONFIG QUIET)
IF(GTSAM_FOUND)
# For issue https://github.com/introlab/rtabmap/pull/1626
FIND_FILE(GTSAM_NOISE_MODEL_FACTOR_N_FILE gtsam/nonlinear/NoiseModelFactorN.h
PATHS ${GTSAM_INCLUDE_DIR}
NO_DEFAULT_PATH)
IF(GTSAM_NOISE_MODEL_FACTOR_N_FILE)
MESSAGE(STATUS "GTSAM with NoiseModelFactorN.h")
ADD_DEFINITIONS("-DGTSAM_WITH_NOISE_MODEL_FACTOR_N")
ENDIF(GTSAM_NOISE_MODEL_FACTOR_N_FILE)
ENDIF(GTSAM_FOUND)
ENDIF(WITH_GTSAM) ENDIF(WITH_GTSAM)
IF(WITH_MRPT) IF(WITH_MRPT)
@@ -567,7 +570,7 @@ IF(WITH_FLYCAPTURE2)
ENDIF(WITH_FLYCAPTURE2) ENDIF(WITH_FLYCAPTURE2)
IF(WITH_CVSBA) IF(WITH_CVSBA)
FIND_PACKAGE(cvsba QUIET) FIND_PACKAGE(cvsba)
IF(cvsba_FOUND) IF(cvsba_FOUND)
MESSAGE(STATUS "Found cvsba: ${cvsba_INCLUDE_DIRS}") MESSAGE(STATUS "Found cvsba: ${cvsba_INCLUDE_DIRS}")
ENDIF(cvsba_FOUND) ENDIF(cvsba_FOUND)
@@ -593,10 +596,14 @@ IF(WITH_POINTMATCHER)
ENDIF(WITH_POINTMATCHER) ENDIF(WITH_POINTMATCHER)
IF(libpointmatcher_FOUND OR GTSAM_FOUND) IF(libpointmatcher_FOUND OR GTSAM_FOUND)
find_package(Boost COMPONENTS thread filesystem system program_options date_time REQUIRED) find_package(Boost COMPONENTS thread filesystem program_options date_time REQUIRED)
IF(Boost_MINOR_VERSION GREATER 47) IF(Boost_MINOR_VERSION GREATER 80)
find_package(Boost COMPONENTS thread filesystem program_options date_time chrono timer serialization REQUIRED)
ELSEIF(Boost_MINOR_VERSION GREATER 47)
find_package(Boost COMPONENTS thread filesystem system program_options date_time chrono timer serialization REQUIRED) find_package(Boost COMPONENTS thread filesystem system program_options date_time chrono timer serialization REQUIRED)
ENDIF(Boost_MINOR_VERSION GREATER 47) ELSE()
find_package(Boost COMPONENTS thread filesystem system program_options date_time REQUIRED)
ENDIF()
IF(WIN32) IF(WIN32)
MESSAGE(STATUS "Boost_LIBRARY_DIRS=${Boost_LIBRARY_DIRS}") MESSAGE(STATUS "Boost_LIBRARY_DIRS=${Boost_LIBRARY_DIRS}")
link_directories(${Boost_LIBRARY_DIRS}) link_directories(${Boost_LIBRARY_DIRS})
@@ -604,7 +611,7 @@ IF(libpointmatcher_FOUND OR GTSAM_FOUND)
ENDIF(libpointmatcher_FOUND OR GTSAM_FOUND) ENDIF(libpointmatcher_FOUND OR GTSAM_FOUND)
IF(WITH_CCCORELIB) IF(WITH_CCCORELIB)
find_package(CCCoreLib QUIET) find_package(CCCoreLib)
IF(CCCoreLib_FOUND) IF(CCCoreLib_FOUND)
MESSAGE(STATUS "Found CCCoreLib: ${CCCoreLib_INCLUDE_DIRS}") MESSAGE(STATUS "Found CCCoreLib: ${CCCoreLib_INCLUDE_DIRS}")
ENDIF(CCCoreLib_FOUND) ENDIF(CCCoreLib_FOUND)
@@ -616,7 +623,7 @@ IF(WITH_OPEN3D)
ELSE() ELSE()
# Build Open3D like this to avoid linker errors in rtabmap: # Build Open3D like this to avoid linker errors in rtabmap:
# cmake -DBUILD_SHARED_LIBS=ON -DGLIBCXX_USE_CXX11_ABI=ON -DCMAKE_BUILD_TYPE=Release .. # cmake -DBUILD_SHARED_LIBS=ON -DGLIBCXX_USE_CXX11_ABI=ON -DCMAKE_BUILD_TYPE=Release ..
find_package(Open3D QUIET) find_package(Open3D)
IF(Open3D_FOUND) IF(Open3D_FOUND)
MESSAGE(STATUS "Found Open3D: ${Open3DINCLUDE_DIRS}") MESSAGE(STATUS "Found Open3D: ${Open3DINCLUDE_DIRS}")
ENDIF(Open3D_FOUND) ENDIF(Open3D_FOUND)
@@ -624,17 +631,17 @@ IF(WITH_OPEN3D)
ENDIF(WITH_OPEN3D) ENDIF(WITH_OPEN3D)
IF(WITH_LOAM) IF(WITH_LOAM)
find_package(loam_velodyne QUIET) find_package(loam_velodyne)
IF(loam_velodyne_FOUND) IF(loam_velodyne_FOUND)
MESSAGE(STATUS "Found loam_velodyne: ${loam_velodyne_INCLUDE_DIRS}") MESSAGE(STATUS "Found loam_velodyne: ${loam_velodyne_INCLUDE_DIRS}")
ENDIF(loam_velodyne_FOUND) ENDIF(loam_velodyne_FOUND)
ENDIF(WITH_LOAM) ENDIF(WITH_LOAM)
IF(WITH_FLOAM) IF(WITH_FLOAM)
find_package(floam QUIET) find_package(floam)
IF(floam_FOUND) IF(floam_FOUND)
MESSAGE(STATUS "Found floam: ${floam_INCLUDE_DIRS}") MESSAGE(STATUS "Found floam: ${floam_INCLUDE_DIRS}")
FIND_PACKAGE(Ceres QUIET REQUIRED) FIND_PACKAGE(Ceres REQUIRED)
ENDIF(floam_FOUND) ENDIF(floam_FOUND)
ENDIF(WITH_FLOAM) ENDIF(WITH_FLOAM)
@@ -701,19 +708,26 @@ IF(WITH_MYNTEYE)
ENDIF(WITH_MYNTEYE) ENDIF(WITH_MYNTEYE)
IF(WITH_DEPTHAI) IF(WITH_DEPTHAI)
FIND_PACKAGE(depthai 2.24 QUIET) FIND_PACKAGE(depthai 2.24)
IF(depthai_FOUND) IF(depthai_FOUND)
MESSAGE(STATUS "Found depthai-core (targets)") MESSAGE(STATUS "Found depthai-core (targets)")
ENDIF(depthai_FOUND) ENDIF(depthai_FOUND)
ENDIF(WITH_DEPTHAI) ENDIF(WITH_DEPTHAI)
IF(WITH_XVSDK) IF(WITH_XVSDK)
FIND_PACKAGE(xvsdk QUIET) FIND_PACKAGE(xvsdk)
IF(xvsdk_FOUND) IF(xvsdk_FOUND)
MESSAGE(STATUS "Found xvsdk (targets)") MESSAGE(STATUS "Found xvsdk (targets)")
ENDIF(xvsdk_FOUND) ENDIF(xvsdk_FOUND)
ENDIF(WITH_XVSDK) ENDIF(WITH_XVSDK)
IF(WITH_ORBBEC_SDK)
FIND_PACKAGE(OrbbecSDK 2)
IF(OrbbecSDK_FOUND)
MESSAGE(STATUS "Found OrbbecSDK v2 (targets)")
ENDIF(OrbbecSDK_FOUND)
ENDIF(WITH_ORBBEC_SDK)
IF(WITH_OCTOMAP) IF(WITH_OCTOMAP)
FIND_PACKAGE(octomap QUIET) FIND_PACKAGE(octomap QUIET)
IF(octomap_FOUND) IF(octomap_FOUND)
@@ -725,35 +739,35 @@ IF(WITH_OCTOMAP)
ENDIF(WITH_OCTOMAP) ENDIF(WITH_OCTOMAP)
IF(WITH_GRIDMAP) IF(WITH_GRIDMAP)
FIND_PACKAGE(grid_map_core QUIET) FIND_PACKAGE(grid_map_core)
IF(grid_map_core_FOUND) IF(grid_map_core_FOUND)
MESSAGE(STATUS "Found grid_map_core ${grid_map_core_VERSION}: ${grid_map_core_INCLUDE_DIRS}") MESSAGE(STATUS "Found grid_map_core ${grid_map_core_VERSION}: ${grid_map_core_INCLUDE_DIRS}")
ENDIF(grid_map_core_FOUND) ENDIF(grid_map_core_FOUND)
ENDIF(WITH_GRIDMAP) ENDIF(WITH_GRIDMAP)
IF(WITH_CPUTSDF) IF(WITH_CPUTSDF)
FIND_PACKAGE(CPUTSDF QUIET) FIND_PACKAGE(CPUTSDF)
IF(CPUTSDF_FOUND) IF(CPUTSDF_FOUND)
MESSAGE(STATUS "Found CPUTSDF: ${CPUTSDF_INCLUDE_DIRS}") MESSAGE(STATUS "Found CPUTSDF: ${CPUTSDF_INCLUDE_DIRS}")
ENDIF(CPUTSDF_FOUND) ENDIF(CPUTSDF_FOUND)
ENDIF(WITH_CPUTSDF) ENDIF(WITH_CPUTSDF)
IF(WITH_OPENCHISEL) IF(WITH_OPENCHISEL)
find_package(open_chisel QUIET) find_package(open_chisel)
if(open_chisel_FOUND) if(open_chisel_FOUND)
MESSAGE(STATUS "Found open_chisel: ${open_chisel_INCLUDE_DIRS}") MESSAGE(STATUS "Found open_chisel: ${open_chisel_INCLUDE_DIRS}")
endif(open_chisel_FOUND) endif(open_chisel_FOUND)
ENDIF(WITH_OPENCHISEL) ENDIF(WITH_OPENCHISEL)
IF(WITH_ALICE_VISION) IF(WITH_ALICE_VISION)
find_package(AliceVision CONFIG QUIET) find_package(AliceVision CONFIG)
IF(AliceVision_FOUND) IF(AliceVision_FOUND)
IF(${AliceVision_VERSION} VERSION_LESS_EQUAL "2.2") IF(${AliceVision_VERSION} VERSION_LESS_EQUAL "2.2")
find_package(Boost COMPONENTS log log_setup container REQUIRED) find_package(Boost COMPONENTS log log_setup container REQUIRED)
ENDIF(${AliceVision_VERSION} VERSION_LESS_EQUAL "2.2") ENDIF(${AliceVision_VERSION} VERSION_LESS_EQUAL "2.2")
SET(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};/usr/local/lib/cmake/modules") SET(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};/usr/local/lib/cmake/modules")
find_package(Geogram REQUIRED QUIET) find_package(Geogram REQUIRED)
find_package(assimp QUIET) find_package(assimp)
add_definitions("-DRTABMAP_ALICE_VISION_MAJOR=${AliceVision_VERSION_MAJOR}") add_definitions("-DRTABMAP_ALICE_VISION_MAJOR=${AliceVision_VERSION_MAJOR}")
add_definitions("-DRTABMAP_ALICE_VISION_MINOR=${AliceVision_VERSION_MINOR}") add_definitions("-DRTABMAP_ALICE_VISION_MINOR=${AliceVision_VERSION_MINOR}")
add_definitions("-DRTABMAP_ALICE_VISION_PATCH=${AliceVision_VERSION_PATCH}") add_definitions("-DRTABMAP_ALICE_VISION_PATCH=${AliceVision_VERSION_PATCH}")
@@ -761,28 +775,28 @@ IF(WITH_ALICE_VISION)
ENDIF(WITH_ALICE_VISION) ENDIF(WITH_ALICE_VISION)
IF(WITH_FOVIS) IF(WITH_FOVIS)
FIND_PACKAGE(libfovis QUIET) FIND_PACKAGE(libfovis)
IF(libfovis_FOUND) IF(libfovis_FOUND)
MESSAGE(STATUS "Found libfovis: ${libfovis_INCLUDE_DIRS}") MESSAGE(STATUS "Found libfovis: ${libfovis_INCLUDE_DIRS}")
ENDIF(libfovis_FOUND) ENDIF(libfovis_FOUND)
ENDIF(WITH_FOVIS) ENDIF(WITH_FOVIS)
IF(WITH_VISO2) IF(WITH_VISO2)
FIND_PACKAGE(libviso2 QUIET) FIND_PACKAGE(libviso2)
IF(libviso2_FOUND) IF(libviso2_FOUND)
MESSAGE(STATUS "Found libviso2: ${libviso2_INCLUDE_DIRS}") MESSAGE(STATUS "Found libviso2: ${libviso2_INCLUDE_DIRS}")
ENDIF(libviso2_FOUND) ENDIF(libviso2_FOUND)
ENDIF(WITH_VISO2) ENDIF(WITH_VISO2)
IF(WITH_DVO) IF(WITH_DVO)
FIND_PACKAGE(dvo_core QUIET) FIND_PACKAGE(dvo_core)
IF(dvo_core_FOUND) IF(dvo_core_FOUND)
MESSAGE(STATUS "Found dvo_core: ${dvo_core_INCLUDE_DIRS}") MESSAGE(STATUS "Found dvo_core: ${dvo_core_INCLUDE_DIRS}")
ENDIF(dvo_core_FOUND) ENDIF(dvo_core_FOUND)
ENDIF(WITH_DVO) ENDIF(WITH_DVO)
IF(WITH_OKVIS) IF(WITH_OKVIS)
FIND_PACKAGE(okvis 1.1 QUIET) FIND_PACKAGE(okvis 1.1)
IF(okvis_FOUND) IF(okvis_FOUND)
MESSAGE(STATUS "Found okvis: ${OKVIS_INCLUDE_DIRS}") MESSAGE(STATUS "Found okvis: ${OKVIS_INCLUDE_DIRS}")
find_package(brisk 2 REQUIRED) find_package(brisk 2 REQUIRED)
@@ -797,7 +811,7 @@ ENDIF(WITH_OKVIS)
# If built with okvis, we found already ceres above # If built with okvis, we found already ceres above
IF(WITH_CERES) IF(WITH_CERES)
IF(NOT okvis_FOUND AND NOT floam_FOUND) IF(NOT okvis_FOUND AND NOT floam_FOUND)
FIND_PACKAGE(Ceres QUIET) FIND_PACKAGE(Ceres)
MESSAGE(STATUS "Found ceres ${Ceres_VERSION}: ${CERES_INCLUDE_DIRS}") MESSAGE(STATUS "Found ceres ${Ceres_VERSION}: ${CERES_INCLUDE_DIRS}")
ENDIF(NOT okvis_FOUND AND NOT floam_FOUND) ENDIF(NOT okvis_FOUND AND NOT floam_FOUND)
ELSEIF(Ceres_FOUND) ELSEIF(Ceres_FOUND)
@@ -805,28 +819,33 @@ ELSEIF(Ceres_FOUND)
ENDIF() ENDIF()
IF(WITH_MSCKF_VIO) IF(WITH_MSCKF_VIO)
FIND_PACKAGE(msckf_vio QUIET) FIND_PACKAGE(msckf_vio)
IF(msckf_vio_FOUND) IF(msckf_vio_FOUND)
MESSAGE(STATUS "Found msckf_vio: ${msckf_vio_INCLUDE_DIRS}") MESSAGE(STATUS "Found msckf_vio: ${msckf_vio_INCLUDE_DIRS}")
ENDIF(msckf_vio_FOUND) ENDIF(msckf_vio_FOUND)
ENDIF(WITH_MSCKF_VIO) ENDIF(WITH_MSCKF_VIO)
IF(WITH_VINS) IF(WITH_VINS AND NOT WITH_VINS_FUSION)
FIND_PACKAGE(vins QUIET) message(DEPRECATION "The option WITH_VINS is deprecated and will be removed in a future version. Please use WITH_VINS_FUSION instead.")
set(WITH_VINS_FUSION ON)
ENDIF(WITH_VINS AND NOT WITH_VINS_FUSION)
IF(WITH_VINS_FUSION)
FIND_PACKAGE(vins)
IF(vins_FOUND) IF(vins_FOUND)
MESSAGE(STATUS "Found vins: ${vins_INCLUDE_DIRS}") MESSAGE(STATUS "Found vins-fusion: ${vins_INCLUDE_DIRS}")
IF(okvis_FOUND) IF(okvis_FOUND)
MESSAGE(WARNING "VINS and OKVIS will be both linked to project, make sure VINS has been built with against same Ceres version than OKVIS to avoid some crashes.") MESSAGE(WARNING "VINS-Fusion and OKVIS will be both linked to project, make sure VINS-Fusion has been built with against same Ceres version than OKVIS to avoid some crashes.")
ENDIF(okvis_FOUND) ENDIF(okvis_FOUND)
ENDIF(vins_FOUND) ENDIF(vins_FOUND)
ENDIF(WITH_VINS) ENDIF(WITH_VINS_FUSION)
IF(WITH_OPENVINS) IF(WITH_OPENVINS)
FIND_PACKAGE(ov_msckf QUIET) FIND_PACKAGE(ov_msckf)
# On ROS2, the indirect includes and libraries # On ROS2, the indirect includes and libraries
# are not forwarded by ov_msckf target, append them manually # are not forwarded by ov_msckf target, append them manually
FIND_PACKAGE(ov_core QUIET) FIND_PACKAGE(ov_core)
FIND_PACKAGE(ov_init QUIET) FIND_PACKAGE(ov_init)
IF(ov_msckf_FOUND AND ov_core_FOUND AND ov_init_FOUND) IF(ov_msckf_FOUND AND ov_core_FOUND AND ov_init_FOUND)
SET(ov_msckf_INCLUDE_DIRS SET(ov_msckf_INCLUDE_DIRS
${ov_msckf_INCLUDE_DIRS} ${ov_msckf_INCLUDE_DIRS}
@@ -855,12 +874,19 @@ IF(WITH_OPENGV)
ENDIF(WITH_OPENGV) ENDIF(WITH_OPENGV)
IF(WITH_ORB_SLAM AND NOT G2O_FOUND) IF(WITH_ORB_SLAM AND NOT G2O_FOUND)
FIND_PACKAGE(ORB_SLAM QUIET) FIND_PACKAGE(ORB_SLAM)
IF(ORB_SLAM_FOUND) IF(ORB_SLAM_FOUND)
MESSAGE(STATUS "Found ORB_SLAM${ORB_SLAM_VERSION}: ${ORB_SLAM_INCLUDE_DIRS}") MESSAGE(STATUS "Found ORB_SLAM${ORB_SLAM_VERSION}: ${ORB_SLAM_INCLUDE_DIRS}")
ENDIF(ORB_SLAM_FOUND) ENDIF(ORB_SLAM_FOUND)
ENDIF(WITH_ORB_SLAM AND NOT G2O_FOUND) ENDIF(WITH_ORB_SLAM AND NOT G2O_FOUND)
IF(WITH_CUVSLAM)
FIND_PACKAGE(CuVSLAM 14.0.0)
IF(CUVSLAM_FOUND)
MESSAGE(STATUS "Found cuVSLAM: ${CUVSLAM_INCLUDE_DIRS}")
ENDIF()
ENDIF(WITH_CUVSLAM)
SET(DISABLE_NEW_DTAGS_FLAG "--disable-new-dtags") SET(DISABLE_NEW_DTAGS_FLAG "--disable-new-dtags")
IF(NOT (APPLE OR WIN32) AND BUILD_WITH_RPATH_NOT_RUNPATH) IF(NOT (APPLE OR WIN32) AND BUILD_WITH_RPATH_NOT_RUNPATH)
ADD_LINK_OPTIONS(LINKER:${DISABLE_NEW_DTAGS_FLAG}) ADD_LINK_OPTIONS(LINKER:${DISABLE_NEW_DTAGS_FLAG})
@@ -955,10 +981,14 @@ ENDIF()
IF(NOT G2O_FOUND) IF(NOT G2O_FOUND)
SET(G2O "//") SET(G2O "//")
SET(G2O_CPP_CONF "//") SET(G2O_CPP_CONF "//")
SET(G2O_WITH_SBA_UTILS "//")
ELSE() ELSE()
IF(NOT G2O_CPP11) IF(NOT G2O_CPP11)
SET(G2O_CPP_CONF "//") SET(G2O_CPP_CONF "//")
ENDIF(NOT G2O_CPP11) ENDIF(NOT G2O_CPP11)
IF(NOT G2O_WITH_SBA_UTILS)
SET(G2O_WITH_SBA_UTILS_CONF "//")
ENDIF(NOT G2O_WITH_SBA_UTILS)
ENDIF() ENDIF()
IF(NOT GTSAM_FOUND) IF(NOT GTSAM_FOUND)
SET(GTSAM "//") SET(GTSAM "//")
@@ -1084,6 +1114,9 @@ IF(NOT xvsdk_FOUND)
ELSE() ELSE()
SET(CONF_WITH_XVSDK 1) SET(CONF_WITH_XVSDK 1)
ENDIF() ENDIF()
IF(NOT OrbbecSDK_FOUND)
SET(ORBBEC_SDK "//")
ENDIF(NOT OrbbecSDK_FOUND)
IF(NOT octomap_FOUND) IF(NOT octomap_FOUND)
SET(OCTOMAP "//") SET(OCTOMAP "//")
SET(CONF_WITH_OCTOMAP 0) SET(CONF_WITH_OCTOMAP 0)
@@ -1118,11 +1151,14 @@ IF(NOT msckf_vio_FOUND)
SET(MSCKF_VIO "//") SET(MSCKF_VIO "//")
ENDIF() ENDIF()
IF(NOT vins_FOUND) IF(NOT vins_FOUND)
SET(VINS "//") SET(VINSFUSION "//")
ENDIF() ENDIF()
IF(NOT ov_msckf_FOUND) IF(NOT ov_msckf_FOUND)
SET(OPENVINS "//") SET(OPENVINS "//")
ENDIF() ENDIF()
IF(NOT CUVSLAM_FOUND)
SET(CUVSLAM "//")
ENDIF()
IF(NOT ORB_SLAM_FOUND) IF(NOT ORB_SLAM_FOUND)
SET(ORB_SLAM "//") SET(ORB_SLAM "//")
ENDIF() ENDIF()
@@ -1437,15 +1473,26 @@ MESSAGE(STATUS " With ORB OcTree = NO (WITH_ORB_OCTREE=OFF)")
ENDIF() ENDIF()
IF(TORCH_FOUND) IF(TORCH_FOUND)
MESSAGE(STATUS " With SuperPoint = YES (License: GPLv3) libtorch=${Torch_VERSION}") MESSAGE(STATUS " With SuperPoint = YES (License: GPLv3) libtorch=${Torch_VERSION}")
ELSEIF(NOT WITH_TORCH) ELSEIF(NOT WITH_TORCH)
MESSAGE(STATUS " With SuperPoint = NO (WITH_TORCH=OFF)") MESSAGE(STATUS " With SuperPoint = NO (WITH_TORCH=OFF)")
ELSE() ELSE()
MESSAGE(STATUS " With SuperPoint = NO (libtorch not found)") MESSAGE(STATUS " With SuperPoint = NO (libtorch not found)")
ENDIF() ENDIF()
IF(TORCH_FOUND AND WITH_PYTHON AND Python3_FOUND)
MESSAGE(STATUS " With Superpoint Rpautrat = YES (Liscense: MIT) libtorch=${Torch_VERSION}")
ELSEIF(NOT WITH_TORCH)
MESSAGE(STATUS " With Superpoint Rpautrat = NO (WITH_TORCH=OFF)")
ELSEIF(NOT WITH_PYTHON)
MESSAGE(STATUS " With Superpoint Rpautrat = NO (WITH_PYTHON=OFF)")
ELSE()
MESSAGE(STATUS " Wtih Superpoint Rpautrat = NO (libtorch and/or python3 not found)")
ENDIF()
IF(WITH_PYTHON AND Python3_FOUND) IF(WITH_PYTHON AND Python3_FOUND)
MESSAGE(STATUS " With Python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} = YES (License: PSF)") MESSAGE(STATUS " With Python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} = YES (License: PSF)")
ELSEIF(NOT WITH_PYTHON) ELSEIF(NOT WITH_PYTHON)
MESSAGE(STATUS " With Python3 = NO (WITH_PYTHON=OFF)") MESSAGE(STATUS " With Python3 = NO (WITH_PYTHON=OFF)")
ELSE() ELSE()
@@ -1594,7 +1641,7 @@ ENDIF()
IF(grid_map_core_FOUND) IF(grid_map_core_FOUND)
MESSAGE(STATUS " With GridMap ${grid_map_core_VERSION} = YES (License: BSD)") MESSAGE(STATUS " With GridMap ${grid_map_core_VERSION} = YES (License: BSD)")
ELSEIF(NOT WITH_OCTOMAP) ELSEIF(NOT WITH_GRIDMAP)
MESSAGE(STATUS " With GridMap = NO (WITH_GRIDMAP=OFF)") MESSAGE(STATUS " With GridMap = NO (WITH_GRIDMAP=OFF)")
ELSE() ELSE()
MESSAGE(STATUS " With GridMap = NO (grid_map_core not found)") MESSAGE(STATUS " With GridMap = NO (grid_map_core not found)")
@@ -1757,6 +1804,14 @@ ELSE()
MESSAGE(STATUS " With XVisio SDK = NO (xvsdk not found)") MESSAGE(STATUS " With XVisio SDK = NO (xvsdk not found)")
ENDIF() ENDIF()
IF(OrbbecSDK_FOUND)
MESSAGE(STATUS " With Orbbec SDK ${OrbbecSDK_VERSION} = YES (License: MIT)")
ELSEIF(NOT WITH_ORBBEC_SDK)
MESSAGE(STATUS " With Orbbec SDK = NO (WITH_ORBBEC_SDK=OFF)")
ELSE()
MESSAGE(STATUS " With Orbbec SDK = NO (OrbbecSDK v2 not found)")
ENDIF()
MESSAGE(STATUS "") MESSAGE(STATUS "")
MESSAGE(STATUS " Odometry Approaches:") MESSAGE(STATUS " Odometry Approaches:")
IF(loam_velodyne_FOUND) IF(loam_velodyne_FOUND)
@@ -1818,7 +1873,7 @@ ENDIF()
IF(vins_FOUND) IF(vins_FOUND)
MESSAGE(STATUS " With VINS-Fusion = YES (License: GPLv3)") MESSAGE(STATUS " With VINS-Fusion = YES (License: GPLv3)")
ELSEIF(NOT WITH_VINS) ELSEIF(NOT WITH_VINS)
MESSAGE(STATUS " With VINS-Fusion = NO (WITH_VINS=OFF)") MESSAGE(STATUS " With VINS-Fusion = NO (WITH_VINS_FUSION=OFF)")
ELSE() ELSE()
MESSAGE(STATUS " With VINS-Fusion = NO (VINS-Fusion not found)") MESSAGE(STATUS " With VINS-Fusion = NO (VINS-Fusion not found)")
ENDIF() ENDIF()
@@ -1841,6 +1896,14 @@ ELSE()
MESSAGE(STATUS " With ORB_SLAM = NO (ORB_SLAM2 and ORB_SLAM3 not found, make sure environment variable ORB_SLAM_ROOT_DIR is set)") MESSAGE(STATUS " With ORB_SLAM = NO (ORB_SLAM2 and ORB_SLAM3 not found, make sure environment variable ORB_SLAM_ROOT_DIR is set)")
ENDIF() ENDIF()
IF(CUVSLAM_FOUND)
MESSAGE(STATUS " With cuVSLAM = YES (License: NVIDIA ISAAC ROS SOFTWARE LICENSE)")
ELSEIF(NOT WITH_CUVSLAM)
MESSAGE(STATUS " With cuVSLAM = NO (WITH_CUVSLAM=OFF)")
ELSE()
MESSAGE(STATUS " With cuVSLAM = NO (cuVSLAM not found, make sure cuVSLAM is installed)")
ENDIF()
MESSAGE(STATUS "Show all options with: cmake -LA | grep WITH_") MESSAGE(STATUS "Show all options with: cmake -LA | grep WITH_")
MESSAGE(STATUS "--------------------------------------------") MESSAGE(STATUS "--------------------------------------------")
+4 -1
View File
@@ -41,6 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@TORO@#define RTABMAP_TORO @TORO@#define RTABMAP_TORO
@G2O@#define RTABMAP_G2O @G2O@#define RTABMAP_G2O
@G2O_CPP_CONF@#define RTABMAP_G2O_CPP11 @G2O_CPP11@ @G2O_CPP_CONF@#define RTABMAP_G2O_CPP11 @G2O_CPP11@
@G2O_WITH_SBA_UTILS_CONF@#define RTABMAP_G2O_WITH_SBA_UTILS
@GTSAM@#define RTABMAP_GTSAM @GTSAM@#define RTABMAP_GTSAM
@CERES@#define RTABMAP_CERES @CERES@#define RTABMAP_CERES
@MRPT@#define RTABMAP_MRPT @MRPT@#define RTABMAP_MRPT
@@ -72,6 +73,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@MYNTEYE@#define RTABMAP_MYNTEYE @MYNTEYE@#define RTABMAP_MYNTEYE
@DEPTHAI@#define RTABMAP_DEPTHAI @DEPTHAI@#define RTABMAP_DEPTHAI
@XVSDK@#define RTABMAP_XVSDK @XVSDK@#define RTABMAP_XVSDK
@ORBBEC_SDK@#define RTABMAP_ORBBEC_SDK
@OCTOMAP@#define RTABMAP_OCTOMAP @OCTOMAP@#define RTABMAP_OCTOMAP
@GRIDMAP@#define RTABMAP_GRIDMAP @GRIDMAP@#define RTABMAP_GRIDMAP
@CPUTSDF@#define RTABMAP_CPUTSDF @CPUTSDF@#define RTABMAP_CPUTSDF
@@ -82,8 +84,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@DVO@#define RTABMAP_DVO @DVO@#define RTABMAP_DVO
@OKVIS@#define RTABMAP_OKVIS @OKVIS@#define RTABMAP_OKVIS
@MSCKF_VIO@#define RTABMAP_MSCKF_VIO @MSCKF_VIO@#define RTABMAP_MSCKF_VIO
@VINS@#define RTABMAP_VINS @VINSFUSION@#define RTABMAP_VINS_FUSION
@OPENVINS@#define RTABMAP_OPENVINS @OPENVINS@#define RTABMAP_OPENVINS
@CUVSLAM@#define RTABMAP_CUVSLAM
@ORB_SLAM@#define RTABMAP_ORB_SLAM @ORB_SLAM_VERSION@ @ORB_SLAM@#define RTABMAP_ORB_SLAM @ORB_SLAM_VERSION@
@ORB_OCTREE@#define RTABMAP_ORB_OCTREE @ORB_OCTREE@#define RTABMAP_ORB_OCTREE
@TORCH@#define RTABMAP_TORCH @TORCH@#define RTABMAP_TORCH
+2 -2
View File
@@ -1078,7 +1078,7 @@
"\"$(SRCROOT)/RTABMapApp/Libraries/include\"", "\"$(SRCROOT)/RTABMapApp/Libraries/include\"",
"\"$(SRCROOT)/RTABMapApp/Libraries/include/eigen3\"", "\"$(SRCROOT)/RTABMapApp/Libraries/include/eigen3\"",
"\"$(SRCROOT)/RTABMapApp/Libraries/include/pcl-1.15\"", "\"$(SRCROOT)/RTABMapApp/Libraries/include/pcl-1.15\"",
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.22\"", "\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.23\"",
"\"$(SRCROOT)/../android/jni/tango-gl/include\"", "\"$(SRCROOT)/../android/jni/tango-gl/include\"",
"\"$(SRCROOT)/../android/jni/third-party/include\"", "\"$(SRCROOT)/../android/jni/third-party/include\"",
"\"$(SRCROOT)/RTABMapApp/Libraries/lib/vtk.framework/Headers\"", "\"$(SRCROOT)/RTABMapApp/Libraries/lib/vtk.framework/Headers\"",
@@ -1139,7 +1139,7 @@
"\"$(SRCROOT)/RTABMapApp/Libraries/include\"", "\"$(SRCROOT)/RTABMapApp/Libraries/include\"",
"\"$(SRCROOT)/RTABMapApp/Libraries/include/eigen3\"", "\"$(SRCROOT)/RTABMapApp/Libraries/include/eigen3\"",
"\"$(SRCROOT)/RTABMapApp/Libraries/include/pcl-1.15\"", "\"$(SRCROOT)/RTABMapApp/Libraries/include/pcl-1.15\"",
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.22\"", "\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.23\"",
"\"$(SRCROOT)/../android/jni/tango-gl/include\"", "\"$(SRCROOT)/../android/jni/tango-gl/include\"",
"\"$(SRCROOT)/../android/jni/third-party/include\"", "\"$(SRCROOT)/../android/jni/third-party/include\"",
"\"$(SRCROOT)/RTABMapApp/Libraries/lib/vtk.framework/Headers\"", "\"$(SRCROOT)/RTABMapApp/Libraries/lib/vtk.framework/Headers\"",
+28 -1
View File
@@ -34,7 +34,7 @@ ENDIF()
IF(APPLE AND BUILD_AS_BUNDLE) IF(APPLE AND BUILD_AS_BUNDLE)
ADD_EXECUTABLE(rtabmap_app MACOSX_BUNDLE ${SRC_FILES}) ADD_EXECUTABLE(rtabmap_app MACOSX_BUNDLE ${SRC_FILES})
ELSEIF(WIN32 AND BUILD_AS_BUNDLE) ELSEIF(WIN32 AND BUILD_AS_BUNDLE)
ADD_EXECUTABLE(rtabmap_app WIN32 ${SRC_FILES}) ADD_EXECUTABLE(rtabmap_app ${SRC_FILES})
ELSE() ELSE()
ADD_EXECUTABLE(rtabmap_app ${SRC_FILES}) ADD_EXECUTABLE(rtabmap_app ${SRC_FILES})
ENDIF() ENDIF()
@@ -110,6 +110,33 @@ IF(BUILD_AS_BUNDLE AND (APPLE OR WIN32))
ENDIF(WIN32) ENDIF(WIN32)
ENDIF(k4a_FOUND) ENDIF(k4a_FOUND)
IF(ZED_FOUND)
# Install needed zlibwapi.dll
IF(WIN32)
file(TO_CMAKE_PATH "$ENV{ZED_SDK_ROOT_DIR}" ENV_ZED_SDK_ROOT_DIR)
INSTALL(FILES "${ENV_ZED_SDK_ROOT_DIR}/bin/zlibwapi.dll"
DESTINATION ${thirdparty_dest_dir}
COMPONENT runtime)
ENDIF(WIN32)
ENDIF(ZED_FOUND)
IF(OrbbecSDK_FOUND)
# Install needed "extensions" folder
IF(WIN32)
find_path(OrbbecSDK_BIN_DIR NAMES OrbbecSDK.dll)
IF(NOT OrbbecSDK_BIN_DIR)
MESSAGE(FATAL_ERROR "OrbbecSDK.dll not found! Verify your PATH.")
ENDIF(NOT OrbbecSDK_BIN_DIR)
MESSAGE(FATAL "OrbbecSDK_BIN_DIR=${OrbbecSDK_BIN_DIR}")
INSTALL(DIRECTORY "${OrbbecSDK_BIN_DIR}/extensions"
DESTINATION ${thirdparty_dest_dir}
COMPONENT runtime
FILES_MATCHING
PATTERN "*.lib" EXCLUDE
PATTERN "*")
ENDIF(WIN32)
ENDIF(OrbbecSDK_FOUND)
IF(Torch_FOUND) IF(Torch_FOUND)
# Install needed cudnn_ops_infer64_8.dll and cudnn_cnn_infer64_8.dll # Install needed cudnn_ops_infer64_8.dll and cudnn_cnn_infer64_8.dll
# TODO: should be a more general way to include them if version is different # TODO: should be a more general way to include them if version is different
@@ -15,7 +15,7 @@ RAMaddOverhead = 0;
% Inliers_ratio = 'Loop/Visual_inliers/' ./ 'Keypoint/Current_frame/words' % Inliers_ratio = 'Loop/Visual_inliers/' ./ 'Keypoint/Current_frame/words'
% Odometry_average = 'Memory/Distance_travelled/m'(2:end) - 'Memory/Distance_travelled/m'(1:end-1) % Odometry_average = 'Memory/Distance_travelled/m'(2:end) - 'Memory/Distance_travelled/m'(1:end-1)
statNames = {'Loop/Odom_correction_norm/m', 'Loop/Visual_inliers/', 'Inliers_ratio_%', 'Timing/Total/ms', 'Memory/RAM_usage/MB', 'Memory/RAM_estimated/MB', 'Keypoint/Current_frame/words', 'Loop/Map_id/', 'Memory/Local_graph_size/', 'Keypoint/Dictionary_size/words', 'Loop/Distance_since_last_loc/'}; % 'Odometry_average' statNames = {'Loop/Odom_correction_norm/m', 'Loop/Visual_inliers/', 'Inliers_ratio_%', 'Timing/Total/ms', 'Memory/RAM_usage/MB', 'Memory/RAM_estimated/MB', 'Keypoint/Current_frame/words', 'Loop/Map_id/', 'Memory/Local_graph_size/', 'Keypoint/Dictionary_size/words', 'Loop/Distance_since_last_loc/m'}; % 'Odometry_average'
datasets = [ 0 1 6 7 9 14 11 111 ]; % 0 1 6 7 9 12 14 11 datasets = [ 0 1 6 7 9 14 11 111 ]; % 0 1 6 7 9 12 14 11
@@ -26,7 +26,7 @@ if resultsToShow == 2
sep = [0, 1000, 3000, 5000, 7000, 9000]; sep = [0, 1000, 3000, 5000, 7000, 9000];
sepName = {'17:27', '17:54', '18:27', '18:56', '19:35'}; sepName = {'17:27', '17:54', '18:27', '18:56', '19:35'};
prefix = 'Consecutive'; prefix = 'Consecutive';
statNames = {'Loop/Distance_since_last_loc/', 'Distance_since_last_loc_under_50cm'}; statNames = {'Loop/Distance_since_last_loc/m', 'Distance_since_last_loc_under_50cm'};
endif endif
MapsN = length(sepName); MapsN = length(sepName);
@@ -52,7 +52,7 @@ if strcmp(statName,'Inliers_ratio_%')
elseif strcmp(statName, 'Odometry_average') elseif strcmp(statName, 'Odometry_average')
data = dlmread([dataDir '/' prefix num2str(datasets(d)) '-' 'Memory-Distance_travelled-m' '.txt'], '\t', 1, 0, "emptyvalue", 0); data = dlmread([dataDir '/' prefix num2str(datasets(d)) '-' 'Memory-Distance_travelled-m' '.txt'], '\t', 1, 0, "emptyvalue", 0);
elseif strcmp(statName, 'Distance_since_last_loc_under_50cm') elseif strcmp(statName, 'Distance_since_last_loc_under_50cm')
data = dlmread([dataDir '/' prefix num2str(datasets(d)) '-' 'Loop-Distance_since_last_loc-' '.txt'], '\t', 1, 0, "emptyvalue", 0); data = dlmread([dataDir '/' prefix num2str(datasets(d)) '-' 'Loop-Distance_since_last_loc-m' '.txt'], '\t', 1, 0, "emptyvalue", 0);
else else
data = dlmread([dataDir '/' prefix num2str(datasets(d)) '-' statName '.txt'], '\t', 1, 0, "emptyvalue", 0); data = dlmread([dataDir '/' prefix num2str(datasets(d)) '-' statName '.txt'], '\t', 1, 0, "emptyvalue", 0);
endif endif
@@ -13,7 +13,7 @@ source rtabmap_latest.bash
for d in "${DETECTOR[@]}" for d in "${DETECTOR[@]}"
do do
rtabmap-report --export --export_prefix "Stat$d" --loc 32 Loop/Odom_correction_norm/m Loop/Visual_inliers/ Timing/Total/ms Timing/Proximity_by_space_visual/ms Timing/Likelihood_computation/ms Timing/Posterior_computation/ms TimingMem/Keypoints_detection/ms TimingMem/Descriptors_extraction/ms TimingMem/Add_new_words/ms Loop/Map_id/ Keypoint/Current_frame/words Memory/RAM_usage/MB Memory/RAM_estimated/MB Memory/Distance_travelled/m Loop/Distance_since_last_loc/ Memory/Local_graph_size/ Keypoint/Dictionary_size/words "$DATA/$d/loc" rtabmap-report --export --export_prefix "Stat$d" --loc 32 Loop/Odom_correction_norm/m Loop/Visual_inliers/ Timing/Total/ms Timing/Proximity_by_space_visual/ms Timing/Likelihood_computation/ms Timing/Posterior_computation/ms TimingMem/Keypoints_detection/ms TimingMem/Descriptors_extraction/ms TimingMem/Add_new_words/ms Loop/Map_id/ Keypoint/Current_frame/words Memory/RAM_usage/MB Memory/RAM_estimated/MB Memory/Distance_travelled/m Loop/Distance_since_last_loc/m Memory/Local_graph_size/ Keypoint/Dictionary_size/words "$DATA/$d/loc"
rtabmap-report --export --export_prefix "Consecutive$d" --loc 32 Loop/Map_id/ Loop/Distance_since_last_loc/ "$DATA/$d/consecutive_loc" rtabmap-report --export --export_prefix "Consecutive$d" --loc 32 Loop/Map_id/ Loop/Distance_since_last_loc/m "$DATA/$d/consecutive_loc"
done done
+105
View File
@@ -0,0 +1,105 @@
# - Find cuVSLAM library (https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_visual_slam)
#
# CUVSLAM_ROOT_DIR environment variable can be set to find the library.
#
# It sets the following variables:
# CUVSLAM_FOUND - Set to false, or undefined, if cuVSLAM isn't found.
# CUVSLAM_VERSION - The version of cuVSLAM found (e.g., "14.0.0").
# CUVSLAM_INCLUDE_DIRS - The cuVSLAM include directory.
# CUVSLAM_LIBRARIES - The cuVSLAM library to link against.
find_package(CUDA REQUIRED)
find_package(Eigen3 REQUIRED)
find_path(CUVSLAM_INCLUDE_DIRS
NAMES cuvslam.h
PATHS
/usr/include
/usr/local/include
/opt/cuvslam/include
/opt/ros/humble/share/isaac_ros_nitros/cuvslam/include
$ENV{CUVSLAM_ROOT}/include
$ENV{CUVSLAM_ROOT_DIR}/include
)
find_library(CUVSLAM_LIBRARY
NAMES cuvslam
PATHS
/usr/lib
/usr/local/lib
/opt/cuvslam/lib
/opt/ros/humble/share/isaac_ros_nitros/cuvslam/lib
$ENV{CUVSLAM_ROOT}/lib
$ENV{CUVSLAM_ROOT_DIR}/lib
)
if(CUVSLAM_INCLUDE_DIRS AND CUVSLAM_LIBRARY)
# Extract version from cuvslam.h header
file(STRINGS "${CUVSLAM_INCLUDE_DIRS}/cuvslam.h" CUVSLAM_VERSION_MAJOR_LINE
REGEX "^#define CUVSLAM_API_VERSION_MAJOR")
file(STRINGS "${CUVSLAM_INCLUDE_DIRS}/cuvslam.h" CUVSLAM_VERSION_MINOR_LINE
REGEX "^#define CUVSLAM_API_VERSION_MINOR")
if(CUVSLAM_VERSION_MAJOR_LINE AND CUVSLAM_VERSION_MINOR_LINE)
string(REGEX MATCH "[0-9]+" CUVSLAM_VERSION_MAJOR "${CUVSLAM_VERSION_MAJOR_LINE}")
string(REGEX MATCH "[0-9]+" CUVSLAM_VERSION_MINOR "${CUVSLAM_VERSION_MINOR_LINE}")
set(CUVSLAM_VERSION "${CUVSLAM_VERSION_MAJOR}.${CUVSLAM_VERSION_MINOR}.0")
endif()
set(CUVSLAM_LIBRARIES
${CUVSLAM_LIBRARY}
${CUDA_LIBRARIES}
# Eigen3 is header-only, so we don't need to link to it
)
set(CUVSLAM_INCLUDE_DIRS
${CUVSLAM_INCLUDE_DIRS}
${CUDA_INCLUDE_DIRS}
${EIGEN3_INCLUDE_DIR}
)
endif()
# Version compatibility check - cuVSLAM only guarantees API compatibility within the same major version
set(CUVSLAM_VERSION_MISMATCH_REASON "")
if(CuVSLAM_FIND_VERSION AND CUVSLAM_VERSION)
string(REGEX MATCH "^[0-9]+" REQUESTED_MAJOR_VERSION "${CuVSLAM_FIND_VERSION}")
if(NOT CUVSLAM_VERSION_MAJOR EQUAL REQUESTED_MAJOR_VERSION)
set(CUVSLAM_VERSION_MISMATCH_REASON "Major version mismatch: found ${CUVSLAM_VERSION_MAJOR}.x but requested ${REQUESTED_MAJOR_VERSION}.x.\ncuVSLAM only guarantees API compatibility within the same major version.\nPlease install cuVSLAM ${REQUESTED_MAJOR_VERSION}.x or update CMakeLists.txt to request version ${CUVSLAM_VERSION_MAJOR}.0.0")
if(CuVSLAM_FIND_REQUIRED)
message(FATAL_ERROR
"cuVSLAM major version mismatch: found version ${CUVSLAM_VERSION} but version ${CuVSLAM_FIND_VERSION} is required.\n"
"cuVSLAM only guarantees API compatibility within the same major version.\n"
"Found major version ${CUVSLAM_VERSION_MAJOR} is not compatible with requested major version ${REQUESTED_MAJOR_VERSION}.\n"
"Please install cuVSLAM ${REQUESTED_MAJOR_VERSION}.x or update CMakeLists.txt to request version ${CUVSLAM_VERSION_MAJOR}.x."
)
else()
# Clear the found variables to indicate incompatibility
unset(CUVSLAM_LIBRARIES)
unset(CUVSLAM_INCLUDE_DIRS)
endif()
endif()
endif()
# Handle the QUIET and REQUIRED arguments
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CuVSLAM
FOUND_VAR CUVSLAM_FOUND
REQUIRED_VARS CUVSLAM_LIBRARIES CUVSLAM_INCLUDE_DIRS
VERSION_VAR CUVSLAM_VERSION
REASON_FAILURE_MESSAGE "${CUVSLAM_VERSION_MISMATCH_REASON}"
HANDLE_COMPONENTS
)
if(CUVSLAM_FOUND)
# Create imported target for modern CMake usage
if(NOT TARGET cuvslam::cuvslam)
add_library(cuvslam::cuvslam UNKNOWN IMPORTED)
set_target_properties(cuvslam::cuvslam PROPERTIES
IMPORTED_LOCATION "${CUVSLAM_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${CUVSLAM_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${CUVSLAM_LIBRARIES};Eigen3::Eigen"
)
endif()
endif()
mark_as_advanced(CUVSLAM_INCLUDE_DIRS CUVSLAM_LIBRARY)
+2 -2
View File
@@ -27,10 +27,10 @@
if(NOT Eigen3_FIND_VERSION) if(NOT Eigen3_FIND_VERSION)
if(NOT Eigen3_FIND_VERSION_MAJOR) if(NOT Eigen3_FIND_VERSION_MAJOR)
set(Eigen3_FIND_VERSION_MAJOR 2) set(Eigen3_FIND_VERSION_MAJOR 3)
endif() endif()
if(NOT Eigen3_FIND_VERSION_MINOR) if(NOT Eigen3_FIND_VERSION_MINOR)
set(Eigen3_FIND_VERSION_MINOR 91) set(Eigen3_FIND_VERSION_MINOR 0)
endif() endif()
if(NOT Eigen3_FIND_VERSION_PATCH) if(NOT Eigen3_FIND_VERSION_PATCH)
set(Eigen3_FIND_VERSION_PATCH 0) set(Eigen3_FIND_VERSION_PATCH 0)
+22 -11
View File
@@ -26,6 +26,10 @@ FIND_FILE(G2O_FACTORY_FILE g2o/core/factory.h
PATHS ${G2O_INCLUDE_DIR} PATHS ${G2O_INCLUDE_DIR}
NO_DEFAULT_PATH) NO_DEFAULT_PATH)
FIND_FILE(G2O_SBA_UTILS_FILE g2o/types/sba/sba_utils.h
PATHS ${G2O_INCLUDE_DIR}
NO_DEFAULT_PATH)
#ifdef G2O_NUMBER_FORMAT_STR #ifdef G2O_NUMBER_FORMAT_STR
#define G2O_CPP11 // we assume that if G2O_NUMBER_FORMAT_STR is defined, this is the new g2o code with c++11 interface #define G2O_CPP11 // we assume that if G2O_NUMBER_FORMAT_STR is defined, this is the new g2o code with c++11 interface
#endif #endif
@@ -118,22 +122,29 @@ IF(G2O_STUFF_LIBRARY AND G2O_CORE_LIBRARY AND G2O_INCLUDE_DIR AND G2O_CONFIG_FIL
${CHOLMOD_LIB}) ${CHOLMOD_LIB})
ENDIF(G2O_SOLVER_CHOLMOD) ENDIF(G2O_SOLVER_CHOLMOD)
FILE(READ ${G2O_CONFIG_FILE} TMPTXT) FILE(READ ${G2O_FACTORY_FILE} TMPTXT)
STRING(FIND "${TMPTXT}" "G2O_NUMBER_FORMAT_STR" matchres) STRING(FIND "${TMPTXT}" "shared_ptr" matchres)
IF(${matchres} EQUAL -1) IF(${matchres} EQUAL -1)
MESSAGE(STATUS "Old g2o version detected with c++03 interface (config file: ${G2O_CONFIG_FILE}).") FILE(READ ${G2O_CONFIG_FILE} TMPTXT)
SET(G2O_CPP11 0) STRING(FIND "${TMPTXT}" "G2O_NUMBER_FORMAT_STR" matchres)
ELSE()
MESSAGE(WARNING "Latest g2o version detected with c++11 interface (config file: ${G2O_CONFIG_FILE}). Make sure g2o is built with \"-DBUILD_WITH_MARCH_NATIVE=OFF\" to avoid segmentation faults caused by Eigen.")
FILE(READ ${G2O_FACTORY_FILE} TMPTXT)
STRING(FIND "${TMPTXT}" "shared_ptr" matchres)
IF(${matchres} EQUAL -1) IF(${matchres} EQUAL -1)
MESSAGE(STATUS "Old g2o version detected with c++03 interface (config file: ${G2O_CONFIG_FILE}).")
SET(G2O_CPP11 0)
ELSE()
MESSAGE(WARNING "Latest g2o version detected with c++11 interface (config file: ${G2O_CONFIG_FILE}). Make sure g2o is built with \"-DBUILD_WITH_MARCH_NATIVE=OFF\" to avoid segmentation faults caused by Eigen.")
MESSAGE(STATUS "Old g2o factory version detected without shared ptr (factory file: ${G2O_FACTORY_FILE}).") MESSAGE(STATUS "Old g2o factory version detected without shared ptr (factory file: ${G2O_FACTORY_FILE}).")
SET(G2O_CPP11 2) SET(G2O_CPP11 2)
ELSE()
MESSAGE(STATUS "Latest g2o factory version detected with shared ptr (factory file: ${G2O_FACTORY_FILE}).")
SET(G2O_CPP11 1)
ENDIF() ENDIF()
ELSE()
MESSAGE(WARNING "Latest g2o version detected with c++11 interface (config file: ${G2O_CONFIG_FILE}). Make sure g2o is built with \"-DBUILD_WITH_MARCH_NATIVE=OFF\" to avoid segmentation faults caused by Eigen.")
MESSAGE(STATUS "Latest g2o factory version detected with shared ptr (factory file: ${G2O_FACTORY_FILE}).")
SET(G2O_CPP11 1)
ENDIF()
IF(G2O_SBA_UTILS_FILE)
SET(G2O_WITH_SBA_UTILS 1)
ELSE()
SET(G2O_WITH_SBA_UTILS 0)
ENDIF() ENDIF()
SET(G2O_FOUND "YES") SET(G2O_FOUND "YES")
+4 -3
View File
@@ -11,6 +11,7 @@
find_path(ORB_SLAM_INCLUDE_DIR NAMES System.h PATHS $ENV{ORB_SLAM_ROOT_DIR}/include) find_path(ORB_SLAM_INCLUDE_DIR NAMES System.h PATHS $ENV{ORB_SLAM_ROOT_DIR}/include)
find_library(ORB_SLAM2_LIBRARY NAMES ORB_SLAM2 PATHS $ENV{ORB_SLAM_ROOT_DIR}/lib) find_library(ORB_SLAM2_LIBRARY NAMES ORB_SLAM2 PATHS $ENV{ORB_SLAM_ROOT_DIR}/lib)
find_library(ORB_SLAM3_LIBRARY NAMES ORB_SLAM3 PATHS $ENV{ORB_SLAM_ROOT_DIR}/lib) find_library(ORB_SLAM3_LIBRARY NAMES ORB_SLAM3 PATHS $ENV{ORB_SLAM_ROOT_DIR}/lib)
find_path(DBoW2_INCLUDE_DIR NAMES DBoW2/BowVector.h PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/DBoW2 NO_DEFAULT_PATH)
find_path(g2o_INCLUDE_DIR NAMES g2o/core/sparse_optimizer.h PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/g2o NO_DEFAULT_PATH) find_path(g2o_INCLUDE_DIR NAMES g2o/core/sparse_optimizer.h PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/g2o NO_DEFAULT_PATH)
find_path(sophus_INCLUDE_DIR NAMES sophus/se3.hpp PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/Sophus NO_DEFAULT_PATH) find_path(sophus_INCLUDE_DIR NAMES sophus/se3.hpp PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/Sophus NO_DEFAULT_PATH)
find_library(g2o_LIBRARY NAMES g2o PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/g2o/lib NO_DEFAULT_PATH) find_library(g2o_LIBRARY NAMES g2o PATHS $ENV{ORB_SLAM_ROOT_DIR}/Thirdparty/g2o/lib NO_DEFAULT_PATH)
@@ -22,9 +23,9 @@ IF(ORB_SLAM2_LIBRARY)
ELSEIF(ORB_SLAM3_LIBRARY) ELSEIF(ORB_SLAM3_LIBRARY)
SET(ORB_SLAM_VERSION 3) SET(ORB_SLAM_VERSION 3)
SET(ORB_SLAM_LIBRARY ${ORB_SLAM3_LIBRARY}) SET(ORB_SLAM_LIBRARY ${ORB_SLAM3_LIBRARY})
IF(g2o_INCLUDE_DIR AND sophus_INCLUDE_DIR) # ORB_SLAM3 v1 IF(g2o_INCLUDE_DIR AND sophus_INCLUDE_DIR AND DBoW2_INCLUDE_DIR) # ORB_SLAM3 v1
SET(g2o_INCLUDE_DIR ${g2o_INCLUDE_DIR} ${sophus_INCLUDE_DIR}) SET(g2o_INCLUDE_DIR ${g2o_INCLUDE_DIR} ${sophus_INCLUDE_DIR} ${DBoW2_INCLUDE_DIR} $ENV{ORB_SLAM_ROOT_DIR})
ENDIF(g2o_INCLUDE_DIR AND sophus_INCLUDE_DIR) ENDIF(g2o_INCLUDE_DIR AND sophus_INCLUDE_DIR AND DBoW2_INCLUDE_DIR)
ENDIF() ENDIF()
IF (ORB_SLAM_INCLUDE_DIR AND ORB_SLAM_LIBRARY AND DBoW2_LIBRARY AND g2o_INCLUDE_DIR AND g2o_LIBRARY) IF (ORB_SLAM_INCLUDE_DIR AND ORB_SLAM_LIBRARY AND DBoW2_LIBRARY AND g2o_INCLUDE_DIR AND g2o_LIBRARY)
+8 -2
View File
@@ -10,8 +10,6 @@
<string>${MACOSX_BUNDLE_INFO_STRING}</string> <string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string> <string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleLongVersionString</key> <key>CFBundleLongVersionString</key>
@@ -32,6 +30,14 @@
<true/> <true/>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string> <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.downloads.read-only</key>
<false/>
<key>com.apple.security.device.camera</key>
<true/>
<!-- File type associations --> <!-- File type associations -->
<key>CFBundleDocumentTypes</key> <key>CFBundleDocumentTypes</key>
+3 -2
View File
@@ -38,7 +38,7 @@ class IMUFilter;
/** /**
* Class Camera * Class Camera
* *
*/ */
class RTABMAP_CORE_EXPORT Camera : public SensorCapture class RTABMAP_CORE_EXPORT Camera : public SensorCapture
{ {
@@ -48,7 +48,7 @@ public:
SensorData takeImage(SensorCaptureInfo * info = 0) {return takeData(info);} SensorData takeImage(SensorCaptureInfo * info = 0) {return takeData(info);}
float getImageRate() const {return getFrameRate();} float getImageRate() const {return getFrameRate();}
void setImageRate(float imageRate) {setFrameRate(imageRate);} void setImageRate(float imageRate) {setFrameRate(imageRate);}
void setInterIMUPublishing(bool enabled, IMUFilter * filter = 0); // Take ownership of filter void setInterIMUPublishing(bool enabled, IMUFilter * filter = 0, bool baseFrameConversion = false); // Take ownership of filter
bool isInterIMUPublishing() const {return publishInterIMU_;} bool isInterIMUPublishing() const {return publishInterIMU_;}
bool initFromFile(const std::string & calibrationPath); bool initFromFile(const std::string & calibrationPath);
@@ -73,6 +73,7 @@ private:
private: private:
IMUFilter * imuFilter_; IMUFilter * imuFilter_;
bool publishInterIMU_; bool publishInterIMU_;
bool imuBaseFrameConversion_;
}; };
@@ -38,3 +38,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/core/camera/CameraRGBDImages.h> #include <rtabmap/core/camera/CameraRGBDImages.h>
#include <rtabmap/core/camera/CameraK4A.h> #include <rtabmap/core/camera/CameraK4A.h>
#include <rtabmap/core/camera/CameraSeerSense.h> #include <rtabmap/core/camera/CameraSeerSense.h>
#include <rtabmap/core/camera/CameraOrbbecSDK.h>
+11 -7
View File
@@ -129,6 +129,7 @@ public:
std::vector<std::vector<Eigen::Vector2f> > * texCoords = 0, std::vector<std::vector<Eigen::Vector2f> > * texCoords = 0,
#endif #endif
cv::Mat * textures = 0) const; cv::Mat * textures = 0) const;
void saveFlannIndex(const std::vector<unsigned char> & indexData) const;
public: public:
// Mutex-protected methods of abstract versions below // Mutex-protected methods of abstract versions below
@@ -161,19 +162,20 @@ public:
void executeNoResult(const std::string & sql) const; void executeNoResult(const std::string & sql) const;
// Load objects // Load objects
void load(VWDictionary * dictionary, bool lastStateOnly = true) const; void load(VWDictionary & dictionary, bool lastStateOnly = true) const;
void loadLastNodes(std::list<Signature *> & signatures) const; // returned signatures must be freed after usage void loadLastNodes(std::list<Signature *> & signatures, bool loadWordIdsOnly = false) const; // returned signatures must be freed after usage
Signature * loadSignature(int id, bool * loadedFromTrash = 0); // returned signature must be freed after usage, call loadSignatures() instead if more than one signature should be loaded Signature * loadSignature(int id, bool * loadedFromTrash = 0); // returned signature must be freed after usage, call loadSignatures() instead if more than one signature should be loaded
void loadSignatures(const std::list<int> & ids, std::list<Signature *> & signatures, std::set<int> * loadedFromTrash = 0); // returned signatures must be freed after usage void loadSignatures(const std::list<int> & ids, std::list<Signature *> & signatures, std::set<int> * loadedFromTrash = 0, bool loadWordIdsOnly = false); // returned signatures must be freed after usage
void loadWords(const std::set<int> & wordIds, std::list<VisualWord *> & vws); // returned words must be freed after usage void loadWords(const std::set<int> & wordIds, std::list<VisualWord *> & vws); // returned words must be freed after usage
// Specific queries... // Specific queries...
void loadNodeData(Signature * signature, bool images = true, bool scan = true, bool userData = true, bool occupancyGrid = true) const; void loadNodeData(Signature & signature, bool images = true, bool scan = true, bool userData = true, bool occupancyGrid = true) const;
void loadNodeData(std::list<Signature *> & signatures, bool images = true, bool scan = true, bool userData = true, bool occupancyGrid = true) const; void loadNodeData(std::list<Signature *> & signatures, bool images = true, bool scan = true, bool userData = true, bool occupancyGrid = true) const;
void getNodeData(int signatureId, SensorData & data, bool images = true, bool scan = true, bool userData = true, bool occupancyGrid = true) const; void getNodeData(int signatureId, SensorData & data, bool images = true, bool scan = true, bool userData = true, bool occupancyGrid = true) const;
bool getCalibration(int signatureId, std::vector<CameraModel> & models, std::vector<StereoCameraModel> & stereoModels) const; bool getCalibration(int signatureId, std::vector<CameraModel> & models, std::vector<StereoCameraModel> & stereoModels) const;
bool getLaserScanInfo(int signatureId, LaserScan & info) const; bool getLaserScanInfo(int signatureId, LaserScan & info) const;
bool getNodeInfo(int signatureId, Transform & pose, int & mapId, int & weight, std::string & label, double & stamp, Transform & groundTruthPose, std::vector<float> & velocity, GPS & gps, EnvSensors & sensors) const; bool getNodeInfo(int signatureId, Transform & pose, int & mapId, int & weight, std::string & label, double & stamp, Transform & groundTruthPose, std::vector<float> & velocity, GPS & gps, EnvSensors & sensors) const;
void getLocalFeatures(int signatureId, std::multimap<int, int> & words, std::vector<cv::KeyPoint> & keypoints, std::vector<cv::Point3f> & points, cv::Mat & descriptors) const;
void loadLinks(int signatureId, std::multimap<int, Link> & links, Link::Type type = Link::kUndef) const; void loadLinks(int signatureId, std::multimap<int, Link> & links, Link::Type type = Link::kUndef) const;
void getWeight(int signatureId, int & weight) const; void getWeight(int signatureId, int & weight) const;
void getLastNodeIds(std::set<int> & ids) const; void getLastNodeIds(std::set<int> & ids) const;
@@ -274,11 +276,12 @@ protected:
std::vector<std::vector<Eigen::Vector2f> > * texCoords, std::vector<std::vector<Eigen::Vector2f> > * texCoords,
#endif #endif
cv::Mat * textures) const = 0; cv::Mat * textures) const = 0;
virtual void saveFlannIndexQuery(const std::vector<unsigned char> & indexData) const = 0;
// Load objects // Load objects
virtual void loadQuery(VWDictionary * dictionary, bool lastStateOnly = true) const = 0; virtual void loadQuery(VWDictionary & dictionary, bool lastStateOnly = true) const = 0;
virtual void loadLastNodesQuery(std::list<Signature *> & signatures) const = 0; virtual void loadLastNodesQuery(std::list<Signature *> & signatures, bool loadWordIdsOnly) const = 0;
virtual void loadSignaturesQuery(const std::list<int> & ids, std::list<Signature *> & signatures) const = 0; virtual void loadSignaturesQuery(const std::list<int> & ids, std::list<Signature *> & signatures, bool loadWordIdsOnly) const = 0;
virtual void loadWordsQuery(const std::set<int> & wordIds, std::list<VisualWord *> & vws) const = 0; virtual void loadWordsQuery(const std::set<int> & wordIds, std::list<VisualWord *> & vws) const = 0;
virtual void loadLinksQuery(int signatureId, std::multimap<int, Link> & links, Link::Type type = Link::kUndef) const = 0; virtual void loadLinksQuery(int signatureId, std::multimap<int, Link> & links, Link::Type type = Link::kUndef) const = 0;
@@ -286,6 +289,7 @@ protected:
virtual bool getCalibrationQuery(int signatureId, std::vector<CameraModel> & models, std::vector<StereoCameraModel> & stereoModels) const = 0; virtual bool getCalibrationQuery(int signatureId, std::vector<CameraModel> & models, std::vector<StereoCameraModel> & stereoModels) const = 0;
virtual bool getLaserScanInfoQuery(int signatureId, LaserScan & info) const = 0; virtual bool getLaserScanInfoQuery(int signatureId, LaserScan & info) const = 0;
virtual bool getNodeInfoQuery(int signatureId, Transform & pose, int & mapId, int & weight, std::string & label, double & stamp, Transform & groundTruthPose, std::vector<float> & velocity, GPS & gps, EnvSensors & sensors) const = 0; virtual bool getNodeInfoQuery(int signatureId, Transform & pose, int & mapId, int & weight, std::string & label, double & stamp, Transform & groundTruthPose, std::vector<float> & velocity, GPS & gps, EnvSensors & sensors) const = 0;
virtual void getLocalFeaturesQuery(int signatureId, std::multimap<int, int> & words, std::vector<cv::KeyPoint> & keypoints, std::vector<cv::Point3f> & points, cv::Mat & descriptors) const = 0;
virtual void getLastNodeIdsQuery(std::set<int> & ids) const = 0; virtual void getLastNodeIdsQuery(std::set<int> & ids) const = 0;
virtual void getAllNodeIdsQuery(std::set<int> & ids, bool ignoreChildren, bool ignoreBadSignatures, bool ignoreIntermediateNodes) const = 0; virtual void getAllNodeIdsQuery(std::set<int> & ids, bool ignoreChildren, bool ignoreBadSignatures, bool ignoreIntermediateNodes) const = 0;
virtual void getAllOdomPosesQuery(std::map<int, Transform> & poses, bool ignoreChildren, bool ignoreIntermediateNodes) const = 0; virtual void getAllOdomPosesQuery(std::map<int, Transform> & poses, bool ignoreChildren, bool ignoreIntermediateNodes) const = 0;
@@ -135,10 +135,12 @@ protected:
#endif #endif
cv::Mat * textures) const; cv::Mat * textures) const;
virtual void saveFlannIndexQuery(const std::vector<unsigned char> & indexData) const;
// Load objects // Load objects
virtual void loadQuery(VWDictionary * dictionary, bool lastStateOnly = true) const; virtual void loadQuery(VWDictionary & dictionary, bool lastStateOnly = true) const;
virtual void loadLastNodesQuery(std::list<Signature *> & signatures) const; virtual void loadLastNodesQuery(std::list<Signature *> & signatures, bool loadWordIdsOnly) const;
virtual void loadSignaturesQuery(const std::list<int> & ids, std::list<Signature *> & signatures) const; virtual void loadSignaturesQuery(const std::list<int> & ids, std::list<Signature *> & signatures, bool loadWordIdsOnly) const;
virtual void loadWordsQuery(const std::set<int> & wordIds, std::list<VisualWord *> & vws) const; virtual void loadWordsQuery(const std::set<int> & wordIds, std::list<VisualWord *> & vws) const;
virtual void loadLinksQuery(int signatureId, std::multimap<int, Link> & links, Link::Type type = Link::kUndef) const; virtual void loadLinksQuery(int signatureId, std::multimap<int, Link> & links, Link::Type type = Link::kUndef) const;
@@ -146,6 +148,7 @@ protected:
virtual bool getCalibrationQuery(int signatureId, std::vector<CameraModel> & models, std::vector<StereoCameraModel> & stereoModels) const; virtual bool getCalibrationQuery(int signatureId, std::vector<CameraModel> & models, std::vector<StereoCameraModel> & stereoModels) const;
virtual bool getLaserScanInfoQuery(int signatureId, LaserScan & info) const; virtual bool getLaserScanInfoQuery(int signatureId, LaserScan & info) const;
virtual bool getNodeInfoQuery(int signatureId, Transform & pose, int & mapId, int & weight, std::string & label, double & stamp, Transform & groundTruthPose, std::vector<float> & velocity, GPS & gps, EnvSensors & sensors) const; virtual bool getNodeInfoQuery(int signatureId, Transform & pose, int & mapId, int & weight, std::string & label, double & stamp, Transform & groundTruthPose, std::vector<float> & velocity, GPS & gps, EnvSensors & sensors) const;
virtual void getLocalFeaturesQuery(int signatureId, std::multimap<int, int> & words, std::vector<cv::KeyPoint> & keypoints, std::vector<cv::Point3f> & points, cv::Mat & descriptors) const;
virtual void getLastNodeIdsQuery(std::set<int> & ids) const; virtual void getLastNodeIdsQuery(std::set<int> & ids) const;
virtual void getAllNodeIdsQuery(std::set<int> & ids, bool ignoreChildren, bool ignoreBadSignatures, bool ignoreIntermediateNodes) const; virtual void getAllNodeIdsQuery(std::set<int> & ids, bool ignoreChildren, bool ignoreBadSignatures, bool ignoreIntermediateNodes) const;
virtual void getAllOdomPosesQuery(std::map<int, Transform> & poses, bool ignoreChildren, bool ignoreIntermediateNodes) const; virtual void getAllOdomPosesQuery(std::map<int, Transform> & poses, bool ignoreChildren, bool ignoreIntermediateNodes) const;
@@ -190,6 +193,8 @@ private:
const cv::Point3f & viewpoint) const; const cv::Point3f & viewpoint) const;
private: private:
void loadWordsQuery(std::list<Signature *> & signatures) const;
void loadWordIdsQuery(std::list<Signature *> & signatures) const;
void loadLinksQuery(std::list<Signature *> & signatures) const; void loadLinksQuery(std::list<Signature *> & signatures) const;
int loadOrSaveDb(sqlite3 *pInMemory, const std::string & fileName, int isSave) const; int loadOrSaveDb(sqlite3 *pInMemory, const std::string & fileName, int isSave) const;
+30 -1
View File
@@ -104,6 +104,7 @@ namespace rtabmap {
class ORBextractor; class ORBextractor;
class SPDetector; class SPDetector;
class SPDetectorRpautrat;
class Stereo; class Stereo;
#if CV_MAJOR_VERSION < 3 #if CV_MAJOR_VERSION < 3
@@ -129,7 +130,8 @@ public:
kFeatureSurfFreak=12, //new 0.20.4 kFeatureSurfFreak=12, //new 0.20.4
kFeatureGfttDaisy=13, //new 0.20.6 kFeatureGfttDaisy=13, //new 0.20.6
kFeatureSurfDaisy=14, //new 0.20.6 kFeatureSurfDaisy=14, //new 0.20.6
kFeaturePyDetector=15}; //new 0.20.8 kFeaturePyDetector=15, //new 0.20.8
kFeatureSuperPointRpautrat=16}; // new 0.23.3
static std::string typeName(Type type) static std::string typeName(Type type)
{ {
@@ -164,6 +166,8 @@ public:
return "GFTT+Daisy"; return "GFTT+Daisy";
case kFeatureSurfDaisy: case kFeatureSurfDaisy:
return "SURF+Daisy"; return "SURF+Daisy";
case kFeatureSuperPointRpautrat:
return "SUPERPOINT-RPAUTRAT";
default: default:
return "Unknown"; return "Unknown";
} }
@@ -626,6 +630,31 @@ private:
bool cuda_; bool cuda_;
}; };
//SuperPointRpautrat
class RTABMAP_CORE_EXPORT SuperPointRpautrat : public Feature2D
{
public:
SuperPointRpautrat(const ParametersMap & parameters = ParametersMap());
virtual ~SuperPointRpautrat();
virtual void parseParameters(const ParametersMap & parameters);
virtual Feature2D::Type getType() const { return kFeatureSuperPointRpautrat; }
private:
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat());
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
cv::Ptr<SPDetectorRpautrat> superPoint_;
std::string superpointWeightsPath_;
std::string superpointModelPath_;
std::string outputDir_;
float threshold_;
bool nms_;
int minDistance_;
bool cuda_;
};
//GFTT_DAISY //GFTT_DAISY
class RTABMAP_CORE_EXPORT GFTT_DAISY : public GFTT class RTABMAP_CORE_EXPORT GFTT_DAISY : public GFTT
{ {
+30 -19
View File
@@ -37,37 +37,48 @@ namespace rtabmap {
class RTABMAP_CORE_EXPORT FlannIndex class RTABMAP_CORE_EXPORT FlannIndex
{ {
public: public:
// A forward of the internal enum, indexes should match. See src/rtflann/defines.h
enum flann_algorithm_t
{
FLANN_INDEX_LINEAR = 0,
FLANN_INDEX_KDTREE = 1,
FLANN_INDEX_KDTREE_SINGLE = 4,
FLANN_INDEX_LSH = 6,
};
FlannIndex(); FlannIndex();
virtual ~FlannIndex(); virtual ~FlannIndex();
void release(); void release();
std::vector<unsigned char> serializeIndex(bool computeChecksum = true) const;
size_t indexedFeatures() const; size_t indexedFeatures() const;
// return Bytes // return Bytes
size_t memoryUsed() const; size_t memoryUsed() const;
// Note that useDistanceL1 doesn't have any effect if LSH is used // Note that useDistanceL1 doesn't have any effect if LSH is used
void buildLinearIndex( void buildIndex(
flann_algorithm_t algorithm,
const cv::Mat & features, const cv::Mat & features,
bool useDistanceL1 = false, bool useDistanceL1 = false,
float rebalancingFactor = 2.0f); float rebalancingFactor = 2.0f);
void buildKDTreeIndex( // Return false if the indexData doesn't correspond to expected features used and parameters.
const cv::Mat & features, bool loadIndex(
int trees = 4, const std::vector<unsigned char> & indexData,
bool useDistanceL1 = false, flann_algorithm_t algorithm,
float rebalancingFactor = 2.0f); const cv::Mat & features,
void buildKDTreeSingleIndex( bool useDistanceL1 = false,
const cv::Mat & features, float rebalancingFactor = 2.0f,
int leafMaxSize = 10, std::string * errorMsg = NULL);
bool reorder = true, bool loadIndex(
bool useDistanceL1 = false, const unsigned char * indexData,
float rebalancingFactor = 2.0f); size_t indexDataSize,
void buildLSHIndex( flann_algorithm_t algorithm,
const cv::Mat & features, const cv::Mat & features,
unsigned int table_number = 12, bool useDistanceL1 = false,
unsigned int key_size = 20, float rebalancingFactor = 2.0f,
unsigned int multi_probe_level = 2, std::string * errorMsg = NULL);
float rebalancingFactor = 2.0f);
bool isBuilt(); bool isBuilt();
@@ -104,9 +115,9 @@ private:
unsigned int nextIndex_; unsigned int nextIndex_;
int featuresType_; int featuresType_;
int featuresDim_; int featuresDim_;
bool isLSH_;
bool useDistanceL1_; // true=EUCLEDIAN_L2 false=MANHATTAN_L1 bool useDistanceL1_; // true=EUCLEDIAN_L2 false=MANHATTAN_L1
float rebalancingFactor_; float rebalancingFactor_;
flann_algorithm_t algorithm_;
// keep feature in memory until the tree is rebuilt // keep feature in memory until the tree is rebuilt
// (in case the word is deleted when removed from the VWDictionary) // (in case the word is deleted when removed from the VWDictionary)
+1
View File
@@ -53,6 +53,7 @@ public:
public: public:
virtual ~GlobalMap(); virtual ~GlobalMap();
bool fullUpdateNeeded(const std::map<int, Transform> & poses) const;
bool update(const std::map<int, Transform> & poses); // return true if map has changed bool update(const std::map<int, Transform> & poses); // return true if map has changed
virtual void clear(); virtual void clear();
+1 -1
View File
@@ -56,7 +56,7 @@ bool RTABMAP_CORE_EXPORT exportPoses(
bool RTABMAP_CORE_EXPORT importPoses( bool RTABMAP_CORE_EXPORT importPoses(
const std::string & filePath, const std::string & filePath,
int format, // 0=Raw, 1=RGBD-SLAM motion capture (10=without change of coordinate frame, 11=10+ID), 2=KITTI, 3=TORO, 4=g2o, 5=NewCollege(t,x,y), 6=Malaga Urban GPS, 7=St Lucia INS, 8=Karlsruhe, 9=EuRoC MAV int format, // 0=Raw, 1=RGBD-SLAM motion capture (10=without change of coordinate frame, 11=10+ID), 2=KITTI, 3=TORO, 4=g2o, 5=NewCollege(t,x,y), 6=Malaga Urban GPS, 7=St Lucia INS, 8=Karlsruhe, 9=EuRoC MAV, 12=rgbd_bonn
std::map<int, Transform> & poses, std::map<int, Transform> & poses,
std::multimap<int, Link> * constraints = 0, // optional for formats 3 and 4 std::multimap<int, Link> * constraints = 0, // optional for formats 3 and 4
std::map<int, double> * stamps = 0); // optional for format 1 and 9 std::map<int, double> * stamps = 0); // optional for format 1 and 9
+4
View File
@@ -264,6 +264,7 @@ private:
void addSignatureToStm(Signature * signature, const cv::Mat & covariance); void addSignatureToStm(Signature * signature, const cv::Mat & covariance);
void clear(); void clear();
void loadDataFromDb(bool postInitClosingEvents); void loadDataFromDb(bool postInitClosingEvents);
void saveFlannIndex(bool postInitClosingEvents);
void moveToTrash(Signature * s, bool keepLinkedToGraph = true, std::list<int> * deletedWords = 0); void moveToTrash(Signature * s, bool keepLinkedToGraph = true, std::list<int> * deletedWords = 0);
void moveSignatureToWMFromSTM(int id, int * reducedTo = 0); void moveSignatureToWMFromSTM(int id, int * reducedTo = 0);
@@ -299,6 +300,7 @@ private:
float _similarityThreshold; float _similarityThreshold;
bool _binDataKept; bool _binDataKept;
bool _rawDescriptorsKept; bool _rawDescriptorsKept;
bool _loadVisualLocalFeaturesOnInit;
bool _saveDepth16Format; bool _saveDepth16Format;
bool _notLinkedNodesKeptInDb; bool _notLinkedNodesKeptInDb;
bool _saveIntermediateNodeData; bool _saveIntermediateNodeData;
@@ -306,6 +308,7 @@ private:
std::string _depthCompressionFormat; std::string _depthCompressionFormat;
bool _incrementalMemory; bool _incrementalMemory;
bool _localizationDataSaved; bool _localizationDataSaved;
bool _flannIndexSaved;
bool _reduceGraph; bool _reduceGraph;
int _maxStMemSize; int _maxStMemSize;
float _recentWmRatio; float _recentWmRatio;
@@ -353,6 +356,7 @@ private:
bool _linksChanged; // False by default, become true when links are modified. bool _linksChanged; // False by default, become true when links are modified.
int _signaturesAdded; int _signaturesAdded;
bool _allNodesInWM; bool _allNodesInWM;
bool _receivingOdometryFeatures;
GPS _gpsOrigin; GPS _gpsOrigin;
std::vector<CameraModel> _rectCameraModels; std::vector<CameraModel> _rectCameraModels;
std::vector<StereoCameraModel> _rectStereoCameraModels; std::vector<StereoCameraModel> _rectStereoCameraModels;
+3 -2
View File
@@ -53,10 +53,11 @@ public:
kTypeOkvis = 6, kTypeOkvis = 6,
kTypeLOAM = 7, kTypeLOAM = 7,
kTypeMSCKF = 8, kTypeMSCKF = 8,
kTypeVINS = 9, kTypeVINSFusion = 9,
kTypeOpenVINS = 10, kTypeOpenVINS = 10,
kTypeFLOAM = 11, kTypeFLOAM = 11,
kTypeOpen3D = 12 kTypeOpen3D = 12,
kTypeCuVSLAM = 13
}; };
public: public:
@@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define ODOMETRYTHREAD_H_ #define ODOMETRYTHREAD_H_
#include <rtabmap/core/rtabmap_core_export.h> #include <rtabmap/core/rtabmap_core_export.h>
#include <rtabmap/core/SensorEvent.h>
#include <rtabmap/core/SensorData.h> #include <rtabmap/core/SensorData.h>
#include <rtabmap/utilite/UThread.h> #include <rtabmap/utilite/UThread.h>
#include <rtabmap/utilite/UEventsHandler.h> #include <rtabmap/utilite/UEventsHandler.h>
@@ -55,18 +56,19 @@ private:
// MAIN LOOP // MAIN LOOP
//============================================================ //============================================================
virtual void mainLoop(); virtual void mainLoop();
void addData(const SensorData & data); void addData(const SensorEvent & data);
bool getData(SensorData & data); bool getData(SensorEvent & data);
private: private:
USemaphore _dataAdded; USemaphore _dataAdded;
UMutex _dataMutex; UMutex _dataMutex;
std::list<SensorData> _dataBuffer; std::list<SensorEvent> _dataBuffer;
std::list<SensorData> _imuBuffer; std::list<SensorData> _imuBuffer;
Odometry * _odometry; Odometry * _odometry;
unsigned int _dataBufferMaxSize; unsigned int _dataBufferMaxSize;
bool _resetOdometry; bool _resetOdometry;
Transform _resetPose; Transform _resetPose;
Transform _previousGuessPose;
double _oldestAsyncImuStamp; double _oldestAsyncImuStamp;
double _newestAsyncImuStamp; double _newestAsyncImuStamp;
}; };
+28 -15
View File
@@ -204,6 +204,7 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(Mem, ImageKept, bool, false, "Keep raw images in RAM."); RTABMAP_PARAM(Mem, ImageKept, bool, false, "Keep raw images in RAM.");
RTABMAP_PARAM(Mem, BinDataKept, bool, true, "Keep binary data in db."); RTABMAP_PARAM(Mem, BinDataKept, bool, true, "Keep binary data in db.");
RTABMAP_PARAM(Mem, RawDescriptorsKept, bool, true, "Raw descriptors kept in memory."); RTABMAP_PARAM(Mem, RawDescriptorsKept, bool, true, "Raw descriptors kept in memory.");
RTABMAP_PARAM(Mem, LoadVisualLocalFeaturesOnInit, bool, true, "Load all local visual features (keypoints, descriptors and 3D points) in RAM when loading an existing database. This can add significant time to initialize the memory but the features will be already loaded before computing loop closure transforms. If false, the features are loaded on-demand from the database when a loop closure transformation should be estimated.");
RTABMAP_PARAM(Mem, MapLabelsAdded, bool, true, "Create map labels. The first node of a map will be labeled as \"map#\" where # is the map ID."); RTABMAP_PARAM(Mem, MapLabelsAdded, bool, true, "Create map labels. The first node of a map will be labeled as \"map#\" where # is the map ID.");
RTABMAP_PARAM(Mem, SaveDepth16Format, bool, false, "Save depth image into 16 bits format to reduce memory used. Warning: values over ~65 meters are ignored (maximum 65535 millimeters)."); RTABMAP_PARAM(Mem, SaveDepth16Format, bool, false, "Save depth image into 16 bits format to reduce memory used. Warning: values over ~65 meters are ignored (maximum 65535 millimeters).");
RTABMAP_PARAM(Mem, NotLinkedNodesKept, bool, true, "Keep not linked nodes in db (rehearsed nodes and deleted nodes)."); RTABMAP_PARAM(Mem, NotLinkedNodesKept, bool, true, "Keep not linked nodes in db (rehearsed nodes and deleted nodes).");
@@ -212,8 +213,8 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM_STR(Mem, DepthCompressionFormat, ".rvl", "Depth image compression format for 16UC1 depth type. It should be \".png\" or \".rvl\". If depth type is 32FC1, \".png\" is used."); RTABMAP_PARAM_STR(Mem, DepthCompressionFormat, ".rvl", "Depth image compression format for 16UC1 depth type. It should be \".png\" or \".rvl\". If depth type is 32FC1, \".png\" is used.");
RTABMAP_PARAM(Mem, STMSize, unsigned int, 10, "Short-term memory size."); RTABMAP_PARAM(Mem, STMSize, unsigned int, 10, "Short-term memory size.");
RTABMAP_PARAM(Mem, IncrementalMemory, bool, true, "SLAM mode, otherwise it is Localization mode."); RTABMAP_PARAM(Mem, IncrementalMemory, bool, true, "SLAM mode, otherwise it is Localization mode.");
RTABMAP_PARAM(Mem, LocalizationDataSaved, bool, false, uFormat("Save localization data during localization session (when %s=false). When enabled, the database will then also grow in localization mode. This mode would be used only for debugging purpose.", kMemIncrementalMemory().c_str()).c_str()); RTABMAP_PARAM(Mem, LocalizationDataSaved, bool, false, uFormat("Save localization data during localization session (when %s=false). When enabled, the database will then also grow in localization mode. This mode would be used only for debugging purpose.", kMemIncrementalMemory().c_str()).c_str());
RTABMAP_PARAM(Mem, ReduceGraph, bool, false, "Reduce graph. Merge nodes when loop closures are added (ignoring those with user data set)."); RTABMAP_PARAM(Mem, ReduceGraph, bool, false, uFormat("Reduce graph. Merge nodes when loop closures are added (ignoring those with user data). Note that this approach assumes that 100%% of the loop closures accepted are good, so it is highly recommended to enable \"%s\" at the same time.", kRGBDOptimizeMaxError().c_str()));
RTABMAP_PARAM(Mem, RecentWmRatio, float, 0.2, "Ratio of locations after the last loop closure in WM that cannot be transferred."); RTABMAP_PARAM(Mem, RecentWmRatio, float, 0.2, "Ratio of locations after the last loop closure in WM that cannot be transferred.");
RTABMAP_PARAM(Mem, TransferSortingByWeightId, bool, false, "On transfer, signatures are sorted by weight->ID only (i.e. the oldest of the lowest weighted signatures are transferred first). If false, the signatures are sorted by weight->Age->ID (i.e. the oldest inserted in WM of the lowest weighted signatures are transferred first). Note that retrieval updates the age, not the ID."); RTABMAP_PARAM(Mem, TransferSortingByWeightId, bool, false, "On transfer, signatures are sorted by weight->ID only (i.e. the oldest of the lowest weighted signatures are transferred first). If false, the signatures are sorted by weight->Age->ID (i.e. the oldest inserted in WM of the lowest weighted signatures are transferred first). Note that retrieval updates the age, not the ID.");
RTABMAP_PARAM(Mem, RehearsalIdUpdatedToNewOne, bool, false, "On merge, update to new id. When false, no copy."); RTABMAP_PARAM(Mem, RehearsalIdUpdatedToNewOne, bool, false, "On merge, update to new id. When false, no copy.");
@@ -251,15 +252,17 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(Kp, NndrRatio, float, 0.8, "NNDR ratio (A matching pair is detected, if its distance is closer than X times the distance of the second nearest neighbor.)"); RTABMAP_PARAM(Kp, NndrRatio, float, 0.8, "NNDR ratio (A matching pair is detected, if its distance is closer than X times the distance of the second nearest neighbor.)");
#if CV_MAJOR_VERSION > 2 && !defined(HAVE_OPENCV_XFEATURES2D) #if CV_MAJOR_VERSION > 2 && !defined(HAVE_OPENCV_XFEATURES2D)
// OpenCV>2 without xFeatures2D module doesn't have BRIEF // OpenCV>2 without xFeatures2D module doesn't have BRIEF
RTABMAP_PARAM(Kp, DetectorStrategy, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector"); RTABMAP_PARAM(Kp, DetectorStrategy, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector 16=SuperPoint-Rpautrat");
#else #else
RTABMAP_PARAM(Kp, DetectorStrategy, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector"); RTABMAP_PARAM(Kp, DetectorStrategy, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector 16=SuperPoint-Rpautrat");
#endif #endif
RTABMAP_PARAM(Kp, TfIdfLikelihoodUsed, bool, true, "Use of the td-idf strategy to compute the likelihood."); RTABMAP_PARAM(Kp, TfIdfLikelihoodUsed, bool, true, "Use of the td-idf strategy to compute the likelihood.");
RTABMAP_PARAM(Kp, Parallelized, bool, true, "If the dictionary update and signature creation were parallelized."); RTABMAP_PARAM(Kp, Parallelized, bool, true, "If the dictionary update and signature creation were parallelized.");
RTABMAP_PARAM_STR(Kp, RoiRatios, "0.0 0.0 0.0 0.0", "Region of interest ratios [left, right, top, bottom]."); RTABMAP_PARAM_STR(Kp, RoiRatios, "0.0 0.0 0.0 0.0", "Region of interest ratios [left, right, top, bottom].");
RTABMAP_PARAM_STR(Kp, DictionaryPath, "", "Path of the pre-computed dictionary"); RTABMAP_PARAM_STR(Kp, DictionaryPath, "", "Path of the pre-computed dictionary");
RTABMAP_PARAM(Kp, NewWordsComparedTogether, bool, true, "When adding new words to dictionary, they are compared also with each other (to detect same words in the same signature)."); RTABMAP_PARAM(Kp, NewWordsComparedTogether, bool, true, "When adding new words to dictionary, they are compared also with each other (to detect same words in the same signature).");
RTABMAP_PARAM(Kp, FlannIndexSaved, bool, false, uFormat("Save FLANN index during localization session (when %s=false). The FLANN index will be saved to database after the first time localization mode is used, then on next sessions, the index is reloaded from the database instead of being rebuilt again. This can save significant loading time when the visual word dictionary is big (>1M words). Note that if the dictionary is modified (parameters or data), the index will be rebuilt and saved again on the next session.", kMemIncrementalMemory().c_str()).c_str());
RTABMAP_PARAM(Kp, SerializeWithChecksum, bool, true, "On serialization of the FLANN index, compute checksum of the data used by the FLANN index. This adds a slight overhead on serialization/deserialization to make sure that the dictionary data correspond to same data used when the index was built.");
RTABMAP_PARAM(Kp, SubPixWinSize, int, 3, "See cv::cornerSubPix()."); RTABMAP_PARAM(Kp, SubPixWinSize, int, 3, "See cv::cornerSubPix().");
RTABMAP_PARAM(Kp, SubPixIterations, int, 0, "See cv::cornerSubPix(). 0 disables sub pixel refining."); RTABMAP_PARAM(Kp, SubPixIterations, int, 0, "See cv::cornerSubPix(). 0 disables sub pixel refining.");
RTABMAP_PARAM(Kp, SubPixEps, double, 0.02, "See cv::cornerSubPix()."); RTABMAP_PARAM(Kp, SubPixEps, double, 0.02, "See cv::cornerSubPix().");
@@ -343,6 +346,13 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(SuperPoint, NMSRadius, int, 4, uFormat("[%s=true] Minimum distance (pixels) between keypoints.", kSuperPointNMS().c_str())); RTABMAP_PARAM(SuperPoint, NMSRadius, int, 4, uFormat("[%s=true] Minimum distance (pixels) between keypoints.", kSuperPointNMS().c_str()));
RTABMAP_PARAM(SuperPoint, Cuda, bool, true, "Use Cuda device for Torch, otherwise CPU device is used by default."); RTABMAP_PARAM(SuperPoint, Cuda, bool, true, "Use Cuda device for Torch, otherwise CPU device is used by default.");
RTABMAP_PARAM_STR(SuperPointRpautrat, WeightsPath, "", "[Required] SuperPoint weights file (*.pth).");
RTABMAP_PARAM_STR(SuperPointRpautrat, ModelPath, "", "[Required] SuperPoint python model file (superpoint_pytorch.py).");
RTABMAP_PARAM(SuperPointRpautrat, Threshold, float, 0.005, "Detector response threshold to accept keypoint.");
RTABMAP_PARAM(SuperPointRpautrat, NMS, bool, true, "If true, non-maximum suppression is applied to detected keypoints.");
RTABMAP_PARAM(SuperPointRpautrat, NMSRadius, int, 4, uFormat("[%s=true] Minimum distance (pixels) between keypoints.", kSuperPointRpautratNMS().c_str()));
RTABMAP_PARAM(SuperPointRpautrat, Cuda, bool, true, "Use Cuda device for Torch, otherwise CPU device is used by default.");
RTABMAP_PARAM_STR(PyDetector, Path, "", "Path to python script file (see available ones in rtabmap/corelib/src/python/*). See the header to see where the script should be copied."); RTABMAP_PARAM_STR(PyDetector, Path, "", "Path to python script file (see available ones in rtabmap/corelib/src/python/*). See the header to see where the script should be copied.");
RTABMAP_PARAM(PyDetector, Cuda, bool, true, "Use cuda."); RTABMAP_PARAM(PyDetector, Cuda, bool, true, "Use cuda.");
@@ -359,14 +369,14 @@ class RTABMAP_CORE_EXPORT Parameters
// RGB-D SLAM // RGB-D SLAM
RTABMAP_PARAM(RGBD, Enabled, bool, true, "Activate metric SLAM. If set to false, classic RTAB-Map loop closure detection is done using only images and without any metric information."); RTABMAP_PARAM(RGBD, Enabled, bool, true, "Activate metric SLAM. If set to false, classic RTAB-Map loop closure detection is done using only images and without any metric information.");
RTABMAP_PARAM(RGBD, LinearUpdate, float, 0.1, "Minimum linear displacement (m) to update the map. Rehearsal is done prior to this, so weights are still updated."); RTABMAP_PARAM(RGBD, LinearUpdate, float, 0.1, uFormat("Minimum linear displacement (m) to update the map. Rehearsal is done prior to this, so weights are still updated. To update the map when not moving, both %s and %s should be set to 0.", Parameters::kRGBDLinearUpdate().c_str(), Parameters::kRGBDAngularUpdate().c_str()));
RTABMAP_PARAM(RGBD, AngularUpdate, float, 0.1, "Minimum angular displacement (rad) to update the map. Rehearsal is done prior to this, so weights are still updated."); RTABMAP_PARAM(RGBD, AngularUpdate, float, 0.1, uFormat("Minimum angular displacement (rad) to update the map. Rehearsal is done prior to this, so weights are still updated. To update the map when not moving, both %s and %s should be set to 0.", Parameters::kRGBDLinearUpdate().c_str(), Parameters::kRGBDAngularUpdate().c_str()));
RTABMAP_PARAM(RGBD, LinearSpeedUpdate, float, 0.0, "Maximum linear speed (m/s) to update the map (0 means not limit)."); RTABMAP_PARAM(RGBD, LinearSpeedUpdate, float, 0.0, "Maximum linear speed (m/s) to update the map (0 means not limit).");
RTABMAP_PARAM(RGBD, AngularSpeedUpdate, float, 0.0, "Maximum angular speed (rad/s) to update the map (0 means not limit)."); RTABMAP_PARAM(RGBD, AngularSpeedUpdate, float, 0.0, "Maximum angular speed (rad/s) to update the map (0 means not limit).");
RTABMAP_PARAM(RGBD, AggressiveLoopThr, float, 0.05, uFormat("Loop closure threshold used (overriding %s) when a new mapping session is not yet linked to a map of the highest loop closure hypothesis. In localization mode, this threshold is used when there are no loop closure constraints with any map in the cache (%s). In all cases, the goal is to aggressively loop on a previous map in the database. Only used when %s is enabled. Set 1 to disable.", kRtabmapLoopThr().c_str(), kRGBDMaxOdomCacheSize().c_str(), kRGBDEnabled().c_str())); RTABMAP_PARAM(RGBD, AggressiveLoopThr, float, 0.05, uFormat("Loop closure threshold used (overriding %s) when a new mapping session is not yet linked to a map of the highest loop closure hypothesis. In localization mode, this threshold is used when there are no loop closure constraints with any map in the cache (%s). In all cases, the goal is to aggressively loop on a previous map in the database. Only used when %s is enabled. Set 1 to disable.", kRtabmapLoopThr().c_str(), kRGBDMaxOdomCacheSize().c_str(), kRGBDEnabled().c_str()));
RTABMAP_PARAM(RGBD, NewMapOdomChangeDistance, float, 0, "A new map is created if a change of odometry translation greater than X m is detected (0 m = disabled)."); RTABMAP_PARAM(RGBD, NewMapOdomChangeDistance, float, 0, "A new map is created if a change of odometry translation greater than X m is detected (0 m = disabled).");
RTABMAP_PARAM(RGBD, OptimizeFromGraphEnd, bool, false, "Optimize graph from the newest node. If false, the graph is optimized from the oldest node of the current graph (this adds an overhead computation to detect to oldest node of the current graph, but it can be useful to preserve the map referential from the oldest node). Warning when set to false: when some nodes are transferred, the first referential of the local map may change, resulting in momentary changes in robot/map position (which are annoying in teleoperation)."); RTABMAP_PARAM(RGBD, OptimizeFromGraphEnd, bool, false, "Optimize graph from the newest node. If false, the graph is optimized from the oldest node of the current graph (this adds an overhead computation to detect to oldest node of the current graph, but it can be useful to preserve the map referential from the oldest node). Warning when set to false: when some nodes are transferred, the first referential of the local map may change, resulting in momentary changes in robot/map position (which are annoying in teleoperation).");
RTABMAP_PARAM(RGBD, OptimizeMaxError, float, 3.0, uFormat("Reject loop closures if optimization error ratio is greater than this value (0=disabled). Ratio is computed as absolute error over standard deviation of each link. This will help to detect when a wrong loop closure is added to the graph. Not compatible with \"%s\" if enabled.", kOptimizerRobust().c_str())); RTABMAP_PARAM(RGBD, OptimizeMaxError, float, 3.0, uFormat("Reject loop closures if optimization error ratio is greater than this value (0=disabled). Ratio is computed as absolute error over standard deviation of each link. This will help to detect when a wrong loop closure is added to the graph. If used with \"%s\", the disabled loop closure links will be removed.", kOptimizerRobust().c_str()));
RTABMAP_PARAM(RGBD, MaxLoopClosureDistance, float, 0.0, "Reject loop closures/localizations if the distance from the map is over this distance (0=disabled)."); RTABMAP_PARAM(RGBD, MaxLoopClosureDistance, float, 0.0, "Reject loop closures/localizations if the distance from the map is over this distance (0=disabled).");
RTABMAP_PARAM(RGBD, ForceOdom3DoF, bool, true, uFormat("Force odometry pose to be 3DoF if %s=true.", kRegForce3DoF().c_str())); RTABMAP_PARAM(RGBD, ForceOdom3DoF, bool, true, uFormat("Force odometry pose to be 3DoF if %s=true.", kRegForce3DoF().c_str()));
RTABMAP_PARAM(RGBD, StartAtOrigin, bool, false, uFormat("If true, rtabmap will assume the robot is starting from origin of the map. If false, rtabmap will assume the robot is restarting from the last saved localization pose from previous session (the place where it shut down previously). Used only in localization mode (%s=false).", kMemIncrementalMemory().c_str())); RTABMAP_PARAM(RGBD, StartAtOrigin, bool, false, uFormat("If true, rtabmap will assume the robot is starting from origin of the map. If false, rtabmap will assume the robot is restarting from the last saved localization pose from previous session (the place where it shut down previously). Used only in localization mode (%s=false).", kMemIncrementalMemory().c_str()));
@@ -428,7 +438,7 @@ class RTABMAP_CORE_EXPORT Parameters
#endif #endif
#endif #endif
RTABMAP_PARAM(Optimizer, VarianceIgnored, bool, false, "Ignore constraints' variance. If checked, identity information matrix is used for each constraint. Otherwise, an information matrix is generated from the variance saved in the links."); RTABMAP_PARAM(Optimizer, VarianceIgnored, bool, false, "Ignore constraints' variance. If checked, identity information matrix is used for each constraint. Otherwise, an information matrix is generated from the variance saved in the links.");
RTABMAP_PARAM(Optimizer, Robust, bool, false, uFormat("Robust graph optimization using Vertigo (only work for g2o and GTSAM optimization strategies). Not compatible with \"%s\" if enabled.", kRGBDOptimizeMaxError().c_str())); RTABMAP_PARAM(Optimizer, Robust, bool, false, "Robust graph optimization using Vertigo (only work for g2o and GTSAM optimization strategies).");
RTABMAP_PARAM(Optimizer, PriorsIgnored, bool, true, "Ignore prior constraints (global pose or GPS) while optimizing. Currently only g2o and gtsam optimization supports this."); RTABMAP_PARAM(Optimizer, PriorsIgnored, bool, true, "Ignore prior constraints (global pose or GPS) while optimizing. Currently only g2o and gtsam optimization supports this.");
RTABMAP_PARAM(Optimizer, LandmarksIgnored, bool, false, "Ignore landmark constraints while optimizing. Currently only g2o and gtsam optimization supports this."); RTABMAP_PARAM(Optimizer, LandmarksIgnored, bool, false, "Ignore landmark constraints while optimizing. Currently only g2o and gtsam optimization supports this.");
#if defined(RTABMAP_G2O) || defined(RTABMAP_GTSAM) #if defined(RTABMAP_G2O) || defined(RTABMAP_GTSAM)
@@ -453,8 +463,8 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(GTSAM, IncRelinearizeSkip, int, 1, "Only relinearize any variables every X calls to ISAM2::update(). See GTSAM::ISAM2 doc for more info."); RTABMAP_PARAM(GTSAM, IncRelinearizeSkip, int, 1, "Only relinearize any variables every X calls to ISAM2::update(). See GTSAM::ISAM2 doc for more info.");
// Odometry // Odometry
RTABMAP_PARAM(Odom, Strategy, int, 0, "0=Frame-to-Map (F2M) 1=Frame-to-Frame (F2F) 2=Fovis 3=viso2 4=DVO-SLAM 5=ORB_SLAM2 6=OKVIS 7=LOAM 8=MSCKF_VIO 9=VINS-Fusion 10=OpenVINS 11=FLOAM 12=Open3D"); RTABMAP_PARAM(Odom, Strategy, int, 0, "0=Frame-to-Map (F2M) 1=Frame-to-Frame (F2F) 2=Fovis 3=viso2 4=DVO-SLAM 5=ORB_SLAM 6=OKVIS 7=LOAM 8=MSCKF_VIO 9=VINS-Fusion 10=OpenVINS 11=FLOAM 12=Open3D 13=cuVSLAM");
RTABMAP_PARAM(Odom, ResetCountdown, int, 0, "Automatically reset odometry after X consecutive images on which odometry cannot be computed (value=0 disables auto-reset)."); RTABMAP_PARAM(Odom, ResetCountdown, int, 0, "Automatically reset odometry after X consecutive images where odometry cannot be computed (a value of 0 disables auto-reset). When a reset occurs, odometry resumes from the last successfully computed pose with large covariance to trigger a new map. If external odometry is used, it will also be reset based on the motion estimated relative to the last computed pose but no large covariance will be received, so that a new map won't be triggered.");
RTABMAP_PARAM(Odom, Holonomic, bool, true, "If the robot is holonomic (strafing commands can be issued). If not, y value will be estimated from x and yaw values (y=x*tan(yaw))."); RTABMAP_PARAM(Odom, Holonomic, bool, true, "If the robot is holonomic (strafing commands can be issued). If not, y value will be estimated from x and yaw values (y=x*tan(yaw)).");
RTABMAP_PARAM(Odom, FillInfoData, bool, true, "Fill info with data (inliers/outliers features)."); RTABMAP_PARAM(Odom, FillInfoData, bool, true, "Fill info with data (inliers/outliers features).");
RTABMAP_PARAM(Odom, ImageBufferSize, unsigned int, 1, "Data buffer size (0 min inf)."); RTABMAP_PARAM(Odom, ImageBufferSize, unsigned int, 1, "Data buffer size (0 min inf).");
@@ -548,7 +558,7 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(OdomViso2, BucketWidth, double, 50, "Width of bucket."); RTABMAP_PARAM(OdomViso2, BucketWidth, double, 50, "Width of bucket.");
RTABMAP_PARAM(OdomViso2, BucketHeight, double, 50, "Height of bucket."); RTABMAP_PARAM(OdomViso2, BucketHeight, double, 50, "Height of bucket.");
// Odometry ORB_SLAM2 // Odometry ORB_SLAM
RTABMAP_PARAM_STR(OdomORBSLAM, VocPath, "", "Path to ORB vocabulary (*.txt)."); RTABMAP_PARAM_STR(OdomORBSLAM, VocPath, "", "Path to ORB vocabulary (*.txt).");
RTABMAP_PARAM(OdomORBSLAM, Bf, double, 0.076, "Fake IR projector baseline (m) used only when stereo is not used."); RTABMAP_PARAM(OdomORBSLAM, Bf, double, 0.076, "Fake IR projector baseline (m) used only when stereo is not used.");
RTABMAP_PARAM(OdomORBSLAM, ThDepth, double, 40.0, "Close/Far threshold. Baseline times."); RTABMAP_PARAM(OdomORBSLAM, ThDepth, double, 40.0, "Close/Far threshold. Baseline times.");
@@ -603,8 +613,8 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(OdomMSCKF, InitCovExTrans, double, 0.000025, ""); RTABMAP_PARAM(OdomMSCKF, InitCovExTrans, double, 0.000025, "");
RTABMAP_PARAM(OdomMSCKF, MaxCamStateSize, int, 20, ""); RTABMAP_PARAM(OdomMSCKF, MaxCamStateSize, int, 20, "");
// Odometry VINS // Odometry VINS-Fusion
RTABMAP_PARAM_STR(OdomVINS, ConfigPath, "", "Path of VINS config file."); RTABMAP_PARAM_STR(OdomVINSFusion, ConfigPath, "", "Path of VINS-Fusion config file.");
// Odometry OpenVINS // Odometry OpenVINS
RTABMAP_PARAM(OdomOpenVINS, UseStereo, bool, true, "If we have more than 1 camera, if we should try to track stereo constraints between pairs"); RTABMAP_PARAM(OdomOpenVINS, UseStereo, bool, true, "If we have more than 1 camera, if we should try to track stereo constraints between pairs");
@@ -672,6 +682,9 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(OdomOpen3D, MaxDepth, float, 3.0, "Maximum depth."); RTABMAP_PARAM(OdomOpen3D, MaxDepth, float, 3.0, "Maximum depth.");
RTABMAP_PARAM(OdomOpen3D, Method, int, 0, "Registration method: 0=PointToPlane, 1=Intensity, 2=Hybrid."); RTABMAP_PARAM(OdomOpen3D, Method, int, 0, "Registration method: 0=PointToPlane, 1=Intensity, 2=Hybrid.");
// Odometry cuVSLAM
RTABMAP_PARAM(OdomCuVSLAM, MulticamMode, int, 0, "cuVSLAM multicam_mode setting: 0=moderate, 1=performance, 2=precision.");
// Common registration parameters // Common registration parameters
RTABMAP_PARAM(Reg, RepeatOnce, bool, true, "Do a second registration with the output of the first registration as guess. Only done if no guess was provided for the first registration (like on loop closure). It can be useful if the registration approach used can use a guess to get better matches."); RTABMAP_PARAM(Reg, RepeatOnce, bool, true, "Do a second registration with the output of the first registration as guess. Only done if no guess was provided for the first registration (like on loop closure). It can be useful if the registration approach used can use a guess to get better matches.");
RTABMAP_PARAM(Reg, Strategy, int, 0, "0=Vis, 1=Icp, 2=VisIcp"); RTABMAP_PARAM(Reg, Strategy, int, 0, "0=Vis, 1=Icp, 2=VisIcp");
@@ -701,9 +714,9 @@ class RTABMAP_CORE_EXPORT Parameters
RTABMAP_PARAM(Vis, Iterations, int, 300, "Maximum iterations to compute the transform."); RTABMAP_PARAM(Vis, Iterations, int, 300, "Maximum iterations to compute the transform.");
#if CV_MAJOR_VERSION > 2 && !defined(HAVE_OPENCV_XFEATURES2D) #if CV_MAJOR_VERSION > 2 && !defined(HAVE_OPENCV_XFEATURES2D)
// OpenCV>2 without xFeatures2D module doesn't have BRIEF // OpenCV>2 without xFeatures2D module doesn't have BRIEF
RTABMAP_PARAM(Vis, FeatureType, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector"); RTABMAP_PARAM(Vis, FeatureType, int, 8, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector 16=SuperPoint-Rpautrat");
#else #else
RTABMAP_PARAM(Vis, FeatureType, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector"); RTABMAP_PARAM(Vis, FeatureType, int, 6, "0=SURF 1=SIFT 2=ORB 3=FAST/FREAK 4=FAST/BRIEF 5=GFTT/FREAK 6=GFTT/BRIEF 7=BRISK 8=GFTT/ORB 9=KAZE 10=ORB-OCTREE 11=SuperPoint 12=SURF/FREAK 13=GFTT/DAISY 14=SURF/DAISY 15=PyDetector 16=SuperPoint-Rpautrat");
#endif #endif
RTABMAP_PARAM(Vis, MaxFeatures, int, 1000, "0 no limits."); RTABMAP_PARAM(Vis, MaxFeatures, int, 1000, "0 no limits.");
RTABMAP_PARAM(Vis, SSC, bool, false, "If true, SSC (Suppression via Square Covering) is applied to limit keypoints."); RTABMAP_PARAM(Vis, SSC, bool, false, "If true, SSC (Suppression via Square Covering) is applied to limit keypoints.");
@@ -8,6 +8,7 @@
#ifndef CORELIB_SRC_PYTHON_PYTHONINTERFACE_H_ #ifndef CORELIB_SRC_PYTHON_PYTHONINTERFACE_H_
#define CORELIB_SRC_PYTHON_PYTHONINTERFACE_H_ #define CORELIB_SRC_PYTHON_PYTHONINTERFACE_H_
#include "rtabmap/core/rtabmap_core_export.h" // DLL export/import defines
#include <string> #include <string>
#include <rtabmap/utilite/UMutex.h> #include <rtabmap/utilite/UMutex.h>
@@ -23,7 +24,7 @@ namespace rtabmap {
* Create a single PythonInterface on main thread at * Create a single PythonInterface on main thread at
* global scope before any Python classes. * global scope before any Python classes.
*/ */
class PythonInterface class RTABMAP_CORE_EXPORT PythonInterface
{ {
public: public:
PythonInterface(); PythonInterface();
@@ -34,7 +35,7 @@ private:
pybind11::gil_scoped_release* release_; pybind11::gil_scoped_release* release_;
}; };
std::string getPythonTraceback(); std::string RTABMAP_CORE_EXPORT getPythonTraceback();
} }
@@ -107,7 +107,11 @@ public:
bool isIncrementalFlann() const {return _incrementalFlann;} bool isIncrementalFlann() const {return _incrementalFlann;}
void setIncrementalDictionary(); void setIncrementalDictionary();
void setFixedDictionary(const std::string & dictionaryPath); void setFixedDictionary(const std::string & dictionaryPath);
bool isModified() const;
std::vector<unsigned char> serializeIndex() const;
void deserializeIndex(const std::vector<unsigned char> & data);
void deserializeIndex(const unsigned char * data, size_t size);
void exportDictionary(const char * fileNameReferences, const char * fileNameDescriptors) const; void exportDictionary(const char * fileNameReferences, const char * fileNameDescriptors) const;
void clear(bool printWarningsIfNotEmpty = true); void clear(bool printWarningsIfNotEmpty = true);
@@ -137,10 +141,12 @@ private:
std::string _dictionaryPath; // a pre-computed dictionary (.txt or .db) std::string _dictionaryPath; // a pre-computed dictionary (.txt or .db)
std::string _newDictionaryPath; // a pre-computed dictionary (.txt or .db) std::string _newDictionaryPath; // a pre-computed dictionary (.txt or .db)
bool _newWordsComparedTogether; bool _newWordsComparedTogether;
bool _serializeWithChecksum;
int _lastWordId; int _lastWordId;
bool useDistanceL1_; bool useDistanceL1_;
FlannIndex * _flannIndex; FlannIndex * _flannIndex;
cv::Mat _dataTree; cv::Mat _dataTree;
bool _modified;
NNStrategy _strategy; NNStrategy _strategy;
std::map<int ,int> _mapIndexId; std::map<int ,int> _mapIndexId;
std::map<int ,int> _mapIdIndex; std::map<int ,int> _mapIdIndex;
@@ -94,14 +94,14 @@ public:
_depthFromScanFillHolesFromBorder = fillHolesFromBorder; _depthFromScanFillHolesFromBorder = fillHolesFromBorder;
} }
// Format: 0=Raw, 1=RGBD-SLAM, 2=KITTI, 3=TORO, 4=g2o, 5=NewCollege(t,x,y), 6=Malaga Urban GPS, 7=St Lucia INS, 8=Karlsruhe // 0=Raw, 1=RGBD-SLAM motion capture (10=without change of coordinate frame, 11=10+ID), 2=KITTI, 3=TORO, 4=g2o, 5=NewCollege(t,x,y), 6=Malaga Urban GPS, 7=St Lucia INS, 8=Karlsruhe, 9=EuRoC MAV, 12=rgbd_bonn
void setOdometryPath(const std::string & filePath, int format = 0) void setOdometryPath(const std::string & filePath, int format = 0)
{ {
_odometryPath = filePath; _odometryPath = filePath;
_odometryFormat = format; _odometryFormat = format;
} }
// Format: 0=Raw, 1=RGBD-SLAM, 2=KITTI, 3=TORO, 4=g2o, 5=NewCollege(t,x,y), 6=Malaga Urban GPS, 7=St Lucia INS, 8=Karlsruhe // 0=Raw, 1=RGBD-SLAM motion capture (10=without change of coordinate frame, 11=10+ID), 2=KITTI, 3=TORO, 4=g2o, 5=NewCollege(t,x,y), 6=Malaga Urban GPS, 7=St Lucia INS, 8=Karlsruhe, 9=EuRoC MAV, 12=rgbd_bonn
void setGroundTruthPath(const std::string & filePath, int format = 0) void setGroundTruthPath(const std::string & filePath, int format = 0)
{ {
_groundTruthPath = filePath; _groundTruthPath = filePath;
@@ -0,0 +1,105 @@
/*
Copyright (c) 2010-2025, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "rtabmap/core/CameraModel.h"
#include "rtabmap/core/Camera.h"
#include "rtabmap/core/Version.h"
#ifdef RTABMAP_ORBBEC_SDK
namespace ob
{
class Pipeline;
class Align;
}
#endif
namespace rtabmap
{
class RTABMAP_CORE_EXPORT CameraOrbbecSDK :
public Camera
{
public:
static bool available();
public:
// deviceId can be either an index (e.g., "0"), an UID (e.g, "2-1-2" or "gmsl-1") or a serial ("AAA6454S")
CameraOrbbecSDK(
std::string deviceId = "",
unsigned int colorWidth = 800,
unsigned int colorHeight = 600,
unsigned int depthWidth = 800,
unsigned int depthHeight = 600,
float imageRate = 0.0f,
const Transform & localTransform = Transform::getIdentity());
virtual ~CameraOrbbecSDK();
virtual bool init(const std::string & calibrationFolder = ".", const std::string & cameraName = "");
virtual bool isCalibrated() const;
virtual std::string getSerial() const;
void close();
// Should be set before initializing
void enableColorRectification(bool enabled);
void enableImu(bool enabled);
void enableDepthMM(bool enabled);
protected:
virtual SensorData captureImage(SensorCaptureInfo * info = 0);
private:
#ifdef RTABMAP_ORBBEC_SDK
std::string deviceId_;
unsigned int colorWidth_;
unsigned int colorHeight_;
unsigned int depthWidth_;
unsigned int depthHeight_;
ob::Pipeline * pipeline_;
ob::Pipeline * imuPipeline_;
ob::Align * alignFilter_;
CameraModel model_;
Transform imuLocalTransform_;
bool imuLocalTransformInitialized_;
uint64_t lastAccStamp_;
uint64_t lastImageStamp_;
bool globalTimestampAvailable_;
bool rectifyColor_;
bool convertDepthToMM_;
bool imuPublished_;
std::map<double, cv::Vec6f> imuBuffer_;
UMutex imuMutex_;
#endif
};
} // namespace rtabmap
@@ -0,0 +1,101 @@
/*
Copyright (c) 2025 Felix Toft
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ODOMETRYCUVSLAM_H_
#define ODOMETRYCUVSLAM_H_
#include <rtabmap/core/Odometry.h>
#include <memory>
#include <deque>
#include <array>
#ifdef RTABMAP_CUVSLAM
#include <cuvslam.h>
#include <ground_constraint.h>
#include <cuda_runtime.h>
#endif
namespace rtabmap {
class RTABMAP_CORE_EXPORT OdometryCuVSLAM : public Odometry
{
public:
OdometryCuVSLAM(const rtabmap::ParametersMap & parameters = rtabmap::ParametersMap());
virtual ~OdometryCuVSLAM();
virtual void reset(const Transform & initialPose = Transform::getIdentity());
virtual Odometry::Type getType() {return Odometry::kTypeCuVSLAM;}
private:
virtual Transform computeTransform(SensorData & image, const Transform & guess = Transform(), OdometryInfo * info = 0);
virtual void cleanupCuVSLAMResources();
private:
#ifdef RTABMAP_CUVSLAM
CUVSLAM_TrackerHandle cuvslam_handle_;
CUVSLAM_GroundConstraintHandle ground_constraint_handle_;
std::vector<CUVSLAM_Camera> cuvslam_cameras_;
std::vector<std::array<float, 12>> intrinsics_;
// State tracking
bool initialized_;
bool lost_;
bool tracking_;
bool planar_constraints_;
int multicam_mode_;
Transform previous_pose_;
double last_timestamp_;
// Configuration Thresholds
double velocity_ratio_threshold_high_ = 1.5; // The maximum velocity ratio of guess / estimated velocity needed to detect lost state.
double velocity_ratio_threshold_low_ = 0.5; // The minimum velocity ratio of guess / estimated velocity needed to detect lost state.
double velocity_difference_threshold_ = 0.1; // The maximum velocity difference between the guess and the estimated velocity needed to detect lost state.
double zero_estimated_velocity_threshold_ = 0.00001; // The minimum cuVSLAM estimated velocity needed to detect lost state.
double min_landmarks_threshold_ = 30; // The minimum number of landmarks needed to start tracking after an initialization.
// Forward cuVLSAM covariance directly to RTAB-Map.
// When true this disables covariance based lost detection.
bool use_raw_covariance_ = false;
//visualization
std::vector<CUVSLAM_Observation> observations_;
std::vector<CUVSLAM_Landmark> landmarks_;
// GPU memory management
std::vector<uint8_t *> gpu_left_image_data_; // pointers to all gpu images
std::vector<uint8_t *> gpu_right_image_data_;
std::vector<size_t> gpu_left_image_sizes_; // size of one image
std::vector<size_t> gpu_right_image_sizes_;
cudaStream_t cuda_stream_;
#endif
};
}
#endif /* ODOMETRYCUVSLAM_H_ */
@@ -25,39 +25,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef ODOMETRYVINS_H_ #pragma once
#define ODOMETRYVINS_H_ #pragma message("Warning: OdometryVINS.h is deprecated. Please use OdometryVINSFusion.h instead.")
#include <rtabmap/core/Odometry.h> #include "rtabmap/core/odometry/OdometryVINSFusion.h"
namespace rtabmap {
class VinsEstimator;
class RTABMAP_CORE_EXPORT OdometryVINS : public Odometry
{
public:
OdometryVINS(const rtabmap::ParametersMap & parameters = rtabmap::ParametersMap());
virtual ~OdometryVINS();
virtual void reset(const Transform & initialPose = Transform::getIdentity());
virtual Odometry::Type getType() {return Odometry::kTypeVINS;}
virtual bool canProcessRawImages() const {return true;}
virtual bool canProcessAsyncIMU() const {return true;}
private:
virtual Transform computeTransform(SensorData & image, const Transform & guess = Transform(), OdometryInfo * info = 0);
private:
#ifdef RTABMAP_VINS
VinsEstimator * vinsEstimator_;
bool initGravity_;
Transform previousPose_;
Transform previousLocalTransform_;
IMU lastImu_;
#endif
};
}
#endif /* ODOMETRYVINS_H_ */
@@ -0,0 +1,64 @@
/*
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ODOMETRYVINSFUSION_H_
#define ODOMETRYVINSFUSION_H_
#include <rtabmap/core/Odometry.h>
namespace rtabmap {
class VinsFusionEstimator;
class RTABMAP_CORE_EXPORT OdometryVINSFusion : public Odometry
{
public:
OdometryVINSFusion(const rtabmap::ParametersMap & parameters = rtabmap::ParametersMap());
virtual ~OdometryVINSFusion();
virtual void reset(const Transform & initialPose = Transform::getIdentity());
virtual Odometry::Type getType() {return Odometry::kTypeVINSFusion;}
virtual bool canProcessRawImages() const {return true;}
virtual bool canProcessAsyncIMU() const {return true;}
private:
virtual Transform computeTransform(SensorData & image, const Transform & guess = Transform(), OdometryInfo * info = 0);
private:
#ifdef RTABMAP_VINS_FUSION
VinsFusionEstimator * vinsEstimator_;
bool initGravity_;
Transform previousPose_;
Transform previousLocalTransform_;
IMU lastImu_;
double lastImuStamp_;
#endif
};
}
#endif /* ODOMETRYVINSFUSION_H_ */
+42 -8
View File
@@ -41,6 +41,7 @@ SET(SRC_FILES
camera/CameraMyntEye.cpp camera/CameraMyntEye.cpp
camera/CameraDepthAI.cpp camera/CameraDepthAI.cpp
camera/CameraSeerSense.cpp camera/CameraSeerSense.cpp
camera/CameraOrbbecSDK.cpp
EpipolarGeometry.cpp EpipolarGeometry.cpp
VisualWord.cpp VisualWord.cpp
@@ -98,9 +99,10 @@ SET(SRC_FILES
odometry/OdometryLOAM.cpp odometry/OdometryLOAM.cpp
odometry/OdometryFLOAM.cpp odometry/OdometryFLOAM.cpp
odometry/OdometryMSCKF.cpp odometry/OdometryMSCKF.cpp
odometry/OdometryVINS.cpp odometry/OdometryVINSFusion.cpp
odometry/OdometryOpenVINS.cpp odometry/OdometryOpenVINS.cpp
odometry/OdometryOpen3D.cpp odometry/OdometryOpen3D.cpp
odometry/OdometryCuVSLAM.cpp
IMU.cpp IMU.cpp
IMUThread.cpp IMUThread.cpp
@@ -163,10 +165,6 @@ IF(MSVC)
ENDIF(MSVC) ENDIF(MSVC)
SET(INCLUDE_DIRS SET(INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/../include
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/include
${ZLIB_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS}
) )
@@ -217,11 +215,21 @@ IF(TORCH_FOUND)
${SRC_FILES} ${SRC_FILES}
superpoint_torch/SuperPoint.cc superpoint_torch/SuperPoint.cc
) )
SET(INCLUDE_DIRS SET(INCLUDE_DIRS
${TORCH_INCLUDE_DIRS} ${TORCH_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/superpoint_torch ${CMAKE_CURRENT_SOURCE_DIR}/superpoint_torch
${INCLUDE_DIRS} ${INCLUDE_DIRS}
) )
IF(WITH_PYTHON AND Python3_FOUND)
SET(SRC_FILES
${SRC_FILES}
superpoint_rpautrat/SuperpointRpautrat.cpp
)
SET(INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/superpoint_rpautrat
${INCLUDE_DIRS}
)
ENDIF(WITH_PYTHON AND Python3_FOUND)
ENDIF(TORCH_FOUND) ENDIF(TORCH_FOUND)
IF(WITH_PYTHON AND Python3_FOUND) IF(WITH_PYTHON AND Python3_FOUND)
@@ -394,6 +402,13 @@ IF(xvsdk_FOUND)
) )
ENDIF(xvsdk_FOUND) ENDIF(xvsdk_FOUND)
IF(OrbbecSDK_FOUND)
SET(LIBRARIES
${LIBRARIES}
ob::OrbbecSDK
)
ENDIF(OrbbecSDK_FOUND)
IF(TARGET OpenMP::OpenMP_CXX) IF(TARGET OpenMP::OpenMP_CXX)
SET(LIBRARIES SET(LIBRARIES
${LIBRARIES} ${LIBRARIES}
@@ -768,6 +783,13 @@ IF(ORB_SLAM_FOUND)
) )
ENDIF(ORB_SLAM_FOUND) ENDIF(ORB_SLAM_FOUND)
IF(CUVSLAM_FOUND)
SET(LIBRARIES
${LIBRARIES}
cuvslam::cuvslam
)
ENDIF(CUVSLAM_FOUND)
IF(GTSAM_FOUND) IF(GTSAM_FOUND)
# Make sure GTSAM is built with system Eigen, not the included one in its package # Make sure GTSAM is built with system Eigen, not the included one in its package
IF(GTSAM_INCLUDE_DIR) IF(GTSAM_INCLUDE_DIR)
@@ -816,6 +838,7 @@ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/resources/DatabaseSchema.sql.in ${CMA
SET(RESOURCES SET(RESOURCES
${CMAKE_CURRENT_SOURCE_DIR}/resources/DatabaseSchema.sql ${CMAKE_CURRENT_SOURCE_DIR}/resources/DatabaseSchema.sql
${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_22_0.sql
${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_20_0.sql ${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_20_0.sql
${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_18_3.sql ${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_18_3.sql
${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_18_0.sql ${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_18_0.sql
@@ -825,6 +848,13 @@ SET(RESOURCES
${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_16_0.sql ${CMAKE_CURRENT_SOURCE_DIR}/resources/backward_compatibility/DatabaseSchema_0_16_0.sql
) )
IF(TORCH_FOUND AND WITH_PYTHON AND Python3_FOUND)
SET(RESOURCES
${RESOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/superpoint_rpautrat/superpoint_to_torchscript.py
)
ENDIF()
foreach(arg ${RESOURCES}) foreach(arg ${RESOURCES})
get_filename_component(filename ${arg} NAME) get_filename_component(filename ${arg} NAME)
string(REPLACE "." "_" output ${filename}) string(REPLACE "." "_" output ${filename})
@@ -857,8 +887,12 @@ generate_export_header(rtabmap_core
DEPRECATED_MACRO_NAME RTABMAP_DEPRECATED) DEPRECATED_MACRO_NAME RTABMAP_DEPRECATED)
target_include_directories(rtabmap_core PUBLIC target_include_directories(rtabmap_core PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include;${CMAKE_CURRENT_BINARY_DIR}/include;${PUBLIC_INCLUDE_DIRS};${INCLUDE_DIRS}>" "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_SOURCE_DIR}/../include;${CMAKE_CURRENT_BINARY_DIR};${CMAKE_CURRENT_BINARY_DIR}/include>"
"$<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR};${PUBLIC_INCLUDE_DIRS}>") "$<INSTALL_INTERFACE:${INSTALL_INCLUDE_DIR}>")
target_include_directories(rtabmap_core SYSTEM PUBLIC
"$<BUILD_INTERFACE:${PUBLIC_INCLUDE_DIRS};${INCLUDE_DIRS}>"
"$<INSTALL_INTERFACE:${PUBLIC_INCLUDE_DIRS}>")
TARGET_LINK_LIBRARIES(rtabmap_core TARGET_LINK_LIBRARIES(rtabmap_core
PUBLIC PUBLIC
+12 -3
View File
@@ -39,7 +39,8 @@ namespace rtabmap
Camera::Camera(float imageRate, const Transform & localTransform) : Camera::Camera(float imageRate, const Transform & localTransform) :
SensorCapture(imageRate, localTransform*CameraModel::opticalRotation()), SensorCapture(imageRate, localTransform*CameraModel::opticalRotation()),
imuFilter_(0), imuFilter_(0),
publishInterIMU_(false) publishInterIMU_(false),
imuBaseFrameConversion_(false)
{} {}
Camera::~Camera() Camera::~Camera()
@@ -52,15 +53,23 @@ bool Camera::initFromFile(const std::string & calibrationPath)
return init(UDirectory::getDir(calibrationPath), uSplit(UFile::getName(calibrationPath), '.').front()); return init(UDirectory::getDir(calibrationPath), uSplit(UFile::getName(calibrationPath), '.').front());
} }
void Camera::setInterIMUPublishing(bool enabled, IMUFilter * filter) void Camera::setInterIMUPublishing(bool enabled, IMUFilter * filter, bool baseFrameConversion)
{ {
publishInterIMU_ = enabled; publishInterIMU_ = enabled;
delete imuFilter_; delete imuFilter_;
imuFilter_ = filter; imuFilter_ = filter;
imuBaseFrameConversion_ = baseFrameConversion;
} }
void Camera::postInterIMU(const IMU & imu, double stamp) void Camera::postInterIMU(const IMU & imu_in, double stamp)
{ {
IMU imu = imu_in;
if(imuBaseFrameConversion_)
{
UASSERT(!imu.localTransform().isNull());
imu.convertToBaseFrame();
}
if(imuFilter_) if(imuFilter_)
{ {
imuFilter_->update( imuFilter_->update(
+1 -1
View File
@@ -554,7 +554,7 @@ unsigned int CameraModel::deserialize(const unsigned char * data, unsigned int d
int iR = 8; int iR = 8;
int iP = 9; int iP = 9;
int iL = 10; int iL = 10;
UDEBUG("Header: %d %d %d %d %d %d %d %d %d %d %d", header[0],header[1],header[2],header[3],header[4],header[5],header[6],header[7],header[8],header[9],header[10]); //UDEBUG("Header: %d %d %d %d %d %d %d %d %d %d %d", header[0],header[1],header[2],header[3],header[4],header[5],header[6],header[7],header[8],header[9],header[10]);
unsigned int requiredDataSize = sizeof(int)*headerSize + unsigned int requiredDataSize = sizeof(int)*headerSize +
sizeof(double)*(header[iK]+header[iD]+header[iR]+header[iP]) + sizeof(double)*(header[iK]+header[iD]+header[iR]+header[iP]) +
sizeof(float)*header[iL]; sizeof(float)*header[iL];
+55 -8
View File
@@ -383,7 +383,7 @@ void DBDriver::asyncSave(Signature * s)
{ {
if(s) if(s)
{ {
UDEBUG("s=%d", s->id()); //UDEBUG("s=%d", s->id());
_trashesMutex.lock(); _trashesMutex.lock();
{ {
_trashSignatures.insert(std::pair<int, Signature*>(s->id(), s)); _trashSignatures.insert(std::pair<int, Signature*>(s->id(), s));
@@ -531,17 +531,17 @@ void DBDriver::updateLaserScan(int nodeId, const LaserScan & scan)
_dbSafeAccessMutex.unlock(); _dbSafeAccessMutex.unlock();
} }
void DBDriver::load(VWDictionary * dictionary, bool lastStateOnly) const void DBDriver::load(VWDictionary & dictionary, bool lastStateOnly) const
{ {
_dbSafeAccessMutex.lock(); _dbSafeAccessMutex.lock();
this->loadQuery(dictionary, lastStateOnly); this->loadQuery(dictionary, lastStateOnly);
_dbSafeAccessMutex.unlock(); _dbSafeAccessMutex.unlock();
} }
void DBDriver::loadLastNodes(std::list<Signature *> & signatures) const void DBDriver::loadLastNodes(std::list<Signature *> & signatures, bool loadWordIdsOnly) const
{ {
_dbSafeAccessMutex.lock(); _dbSafeAccessMutex.lock();
this->loadLastNodesQuery(signatures); this->loadLastNodesQuery(signatures, loadWordIdsOnly);
_dbSafeAccessMutex.unlock(); _dbSafeAccessMutex.unlock();
} }
@@ -564,7 +564,8 @@ Signature * DBDriver::loadSignature(int id, bool * loadedFromTrash)
} }
void DBDriver::loadSignatures(const std::list<int> & signIds, void DBDriver::loadSignatures(const std::list<int> & signIds,
std::list<Signature *> & signatures, std::list<Signature *> & signatures,
std::set<int> * loadedFromTrash) std::set<int> * loadedFromTrash,
bool loadWordIdsOnly)
{ {
UDEBUG(""); UDEBUG("");
// look up in the trash before the database // look up in the trash before the database
@@ -609,7 +610,7 @@ void DBDriver::loadSignatures(const std::list<int> & signIds,
if(ids.size()) if(ids.size())
{ {
_dbSafeAccessMutex.lock(); _dbSafeAccessMutex.lock();
this->loadSignaturesQuery(ids, signatures); this->loadSignaturesQuery(ids, signatures, loadWordIdsOnly);
_dbSafeAccessMutex.unlock(); _dbSafeAccessMutex.unlock();
} }
} }
@@ -656,10 +657,10 @@ void DBDriver::loadWords(const std::set<int> & wordIds, std::list<VisualWord *>
} }
} }
void DBDriver::loadNodeData(Signature * signature, bool images, bool scan, bool userData, bool occupancyGrid) const void DBDriver::loadNodeData(Signature & signature, bool images, bool scan, bool userData, bool occupancyGrid) const
{ {
std::list<Signature *> signatures; std::list<Signature *> signatures;
signatures.push_back(signature); signatures.push_back(&signature);
this->loadNodeData(signatures, images, scan, userData, occupancyGrid); this->loadNodeData(signatures, images, scan, userData, occupancyGrid);
} }
@@ -823,6 +824,45 @@ bool DBDriver::getNodeInfo(
return found; return found;
} }
void DBDriver::getLocalFeatures(
int signatureId,
std::multimap<int, int> & words,
std::vector<cv::KeyPoint> & keypoints,
std::vector<cv::Point3f> & points,
cv::Mat & descriptors) const
{
bool found = false;
// look in the trash
_trashesMutex.lock();
if(uContains(_trashSignatures, signatureId))
{
const Signature * s = _trashSignatures.at(signatureId);
UASSERT(s != 0);
found = true;
if(!s->getWords().empty())
{
words = s->getWords();
if(s->getWordsKpts().empty()){
found = false; // Force checking the database in case the local features were not loaded in RAM
}
else
{
words = s->getWords();
keypoints = s->getWordsKpts();
points = s->getWords3();
descriptors = s->getWordsDescriptors().clone();
}
}
}
_trashesMutex.unlock();
if(!found)
{
UScopeMutex lock(_dbSafeAccessMutex);
getLocalFeaturesQuery(signatureId, words, keypoints, points, descriptors);
}
}
void DBDriver::loadLinks(int signatureId, std::multimap<int, Link> & links, Link::Type type) const void DBDriver::loadLinks(int signatureId, std::multimap<int, Link> & links, Link::Type type) const
{ {
bool found = false; bool found = false;
@@ -1287,6 +1327,13 @@ cv::Mat DBDriver::loadOptimizedMesh(
return cloud; return cloud;
} }
void DBDriver::saveFlannIndex(const std::vector<unsigned char> & indexData) const
{
_dbSafeAccessMutex.lock();
saveFlannIndexQuery(indexData);
_dbSafeAccessMutex.unlock();
}
void DBDriver::generateGraph( void DBDriver::generateGraph(
const std::string & fileName, const std::string & fileName,
const std::set<int> & idsInput, const std::set<int> & idsInput,
+382 -183
View File
@@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/util3d.h" #include "rtabmap/core/util3d.h"
#include "rtabmap/core/Compression.h" #include "rtabmap/core/Compression.h"
#include "DatabaseSchema_sql.h" #include "DatabaseSchema_sql.h"
#include "DatabaseSchema_0_22_0_sql.h"
#include "DatabaseSchema_0_20_0_sql.h" #include "DatabaseSchema_0_20_0_sql.h"
#include "DatabaseSchema_0_18_3_sql.h" #include "DatabaseSchema_0_18_3_sql.h"
#include "DatabaseSchema_0_18_0_sql.h" #include "DatabaseSchema_0_18_0_sql.h"
@@ -404,6 +405,7 @@ bool DBDriverSqlite3::connectDatabaseQuery(const std::string & url, bool overwri
schemas.push_back(std::make_pair("0.18.0", DATABASESCHEMA_0_18_0_SQL)); schemas.push_back(std::make_pair("0.18.0", DATABASESCHEMA_0_18_0_SQL));
schemas.push_back(std::make_pair("0.18.3", DATABASESCHEMA_0_18_3_SQL)); schemas.push_back(std::make_pair("0.18.3", DATABASESCHEMA_0_18_3_SQL));
schemas.push_back(std::make_pair("0.20.0", DATABASESCHEMA_0_20_0_SQL)); schemas.push_back(std::make_pair("0.20.0", DATABASESCHEMA_0_20_0_SQL));
schemas.push_back(std::make_pair("0.22.0", DATABASESCHEMA_0_22_0_SQL));
schemas.push_back(std::make_pair(uNumber2Str(RTABMAP_VERSION_MAJOR)+"."+uNumber2Str(RTABMAP_VERSION_MINOR), DATABASESCHEMA_SQL)); schemas.push_back(std::make_pair(uNumber2Str(RTABMAP_VERSION_MAJOR)+"."+uNumber2Str(RTABMAP_VERSION_MINOR), DATABASESCHEMA_SQL));
for(size_t i=0; i<schemas.size(); ++i) for(size_t i=0; i<schemas.size(); ++i)
{ {
@@ -1296,8 +1298,8 @@ std::map<int, std::vector<int> > DBDriverSqlite3::getAllStatisticsWmStatesQuery(
void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, bool images, bool scan, bool userData, bool occupancyGrid) const void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, bool images, bool scan, bool userData, bool occupancyGrid) const
{ {
UDEBUG("load data for %d signatures images=%d scan=%d userData=%d, grid=%d", //UDEBUG("load data for %d signatures images=%d scan=%d userData=%d, grid=%d",
(int)signatures.size(), images?1:0, scan?1:0, userData?1:0, occupancyGrid?1:0); // (int)signatures.size(), images?1:0, scan?1:0, userData?1:0, occupancyGrid?1:0);
if(!images && !scan && !userData && !occupancyGrid) if(!images && !scan && !userData && !occupancyGrid)
{ {
@@ -1445,7 +1447,7 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
{ {
UASSERT(*iter != 0); UASSERT(*iter != 0);
ULOGGER_DEBUG("Loading data for %d...", (*iter)->id()); //ULOGGER_DEBUG("Loading data for %d...", (*iter)->id());
// bind id // bind id
rc = sqlite3_bind_int(ppStmt, 1, (*iter)->id()); rc = sqlite3_bind_int(ppStmt, 1, (*iter)->id());
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str()); UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
@@ -1874,7 +1876,7 @@ void DBDriverSqlite3::loadNodeDataQuery(std::list<Signature *> & signatures, boo
// Finalize (delete) the statement // Finalize (delete) the statement
rc = sqlite3_finalize(ppStmt); rc = sqlite3_finalize(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str()); UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
ULOGGER_DEBUG("Time=%fs", timer.ticks()); //ULOGGER_DEBUG("Time=%fs", timer.ticks());
} }
} }
@@ -2391,6 +2393,23 @@ bool DBDriverSqlite3::getNodeInfoQuery(int signatureId,
return found; return found;
} }
void DBDriverSqlite3::getLocalFeaturesQuery(
int signatureId,
std::multimap<int, int> & words,
std::vector<cv::KeyPoint> & keypoints,
std::vector<cv::Point3f> & points,
cv::Mat & descriptors) const
{
Signature s(signatureId);
std::list<Signature *> ids;
ids.push_back(&s);
this->loadWordsQuery(ids);
words = ids.front()->getWords();
keypoints = ids.front()->getWordsKpts();
points = ids.front()->getWords3();
descriptors = ids.front()->getWordsDescriptors().clone();
}
void DBDriverSqlite3::getLastNodeIdsQuery(std::set<int> & ids) const void DBDriverSqlite3::getLastNodeIdsQuery(std::set<int> & ids) const
{ {
if(_ppDb) if(_ppDb)
@@ -2987,7 +3006,7 @@ void DBDriverSqlite3::getWeightQuery(int nodeId, int & weight) const
} }
//may be slower than the previous version but don't have a limit of words that can be loaded at the same time //may be slower than the previous version but don't have a limit of words that can be loaded at the same time
void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<Signature *> & nodes) const void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<Signature *> & nodes, bool loadWordIdsOnly) const
{ {
ULOGGER_DEBUG("count=%d", (int)ids.size()); ULOGGER_DEBUG("count=%d", (int)ids.size());
if(_ppDb && ids.size()) if(_ppDb && ids.size())
@@ -3151,7 +3170,7 @@ void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<
// create the node // create the node
if(id) if(id)
{ {
ULOGGER_DEBUG("Creating %d (map=%d, pose=%s)", *iter, mapId, pose.prettyPrint().c_str()); //ULOGGER_DEBUG("Creating %d (map=%d, pose=%s)", *iter, mapId, pose.prettyPrint().c_str());
Signature * s = new Signature( Signature * s = new Signature(
id, id,
mapId, mapId,
@@ -3190,175 +3209,17 @@ void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<
ULOGGER_DEBUG("Time=%fs", timer.ticks()); ULOGGER_DEBUG("Time=%fs", timer.ticks());
// Prepare the query... Get the map from signature and visual words // Prepare the query... Get the map from signature and visual words
std::stringstream query2; UDEBUG("Loading local features (ids only=%s)....", loadWordIdsOnly?"true":"false");
if(uStrNumCmp(_version, "0.13.0") >= 0) if(loadWordIdsOnly) {
{ this->loadWordIdsQuery(nodes);
query2 << "SELECT word_id, pos_x, pos_y, size, dir, response, octave, depth_x, depth_y, depth_z, descriptor_size, descriptor "
"FROM Feature "
"WHERE node_id = ? ";
} }
else if(uStrNumCmp(_version, "0.12.0") >= 0) else {
{ this->loadWordsQuery(nodes);
query2 << "SELECT word_id, pos_x, pos_y, size, dir, response, octave, depth_x, depth_y, depth_z, descriptor_size, descriptor "
"FROM Map_Node_Word "
"WHERE node_id = ? ";
} }
else if(uStrNumCmp(_version, "0.11.2") >= 0) UDEBUG("Loading local features.... done! (in %f s)", timer.ticks());
{
query2 << "SELECT word_id, pos_x, pos_y, size, dir, response, depth_x, depth_y, depth_z, descriptor_size, descriptor "
"FROM Map_Node_Word "
"WHERE node_id = ? ";
}
else
{
query2 << "SELECT word_id, pos_x, pos_y, size, dir, response, depth_x, depth_y, depth_z "
"FROM Map_Node_Word "
"WHERE node_id = ? ";
}
query2 << " ORDER BY word_id"; // Needed for fast insertion below
query2 << ";";
rc = sqlite3_prepare_v2(_ppDb, query2.str().c_str(), -1, &ppStmt, 0);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
float nanFloat = std::numeric_limits<float>::quiet_NaN ();
for(std::list<Signature*>::const_iterator iter=nodes.begin(); iter!=nodes.end(); ++iter)
{
//ULOGGER_DEBUG("Loading words of %d...", (*iter)->id());
// bind id
rc = sqlite3_bind_int(ppStmt, 1, (*iter)->id());
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
int visualWordId = 0;
int descriptorSize = 0;
const void * descriptor = 0;
int dRealSize = 0;
cv::KeyPoint kpt;
std::multimap<int, int> visualWords;
std::vector<cv::KeyPoint> visualWordsKpts;
std::vector<cv::Point3f> visualWords3;
cv::Mat descriptors;
bool allWords3NaN = true;
cv::Point3f depth(0,0,0);
// Process the result if one
rc = sqlite3_step(ppStmt);
while(rc == SQLITE_ROW)
{
int index = 0;
visualWordId = sqlite3_column_int(ppStmt, index++);
kpt.pt.x = sqlite3_column_double(ppStmt, index++);
kpt.pt.y = sqlite3_column_double(ppStmt, index++);
kpt.size = sqlite3_column_int(ppStmt, index++);
kpt.angle = sqlite3_column_double(ppStmt, index++);
kpt.response = sqlite3_column_double(ppStmt, index++);
if(uStrNumCmp(_version, "0.12.0") >= 0)
{
kpt.octave = sqlite3_column_int(ppStmt, index++);
}
if(sqlite3_column_type(ppStmt, index) == SQLITE_NULL)
{
depth.x = nanFloat;
++index;
}
else
{
depth.x = sqlite3_column_double(ppStmt, index++);
}
if(sqlite3_column_type(ppStmt, index) == SQLITE_NULL)
{
depth.y = nanFloat;
++index;
}
else
{
depth.y = sqlite3_column_double(ppStmt, index++);
}
if(sqlite3_column_type(ppStmt, index) == SQLITE_NULL)
{
depth.z = nanFloat;
++index;
}
else
{
depth.z = sqlite3_column_double(ppStmt, index++);
}
visualWordsKpts.push_back(kpt);
visualWords.insert(visualWords.end(), std::make_pair(visualWordId, visualWordsKpts.size()-1));
visualWords3.push_back(depth);
if(allWords3NaN && util3d::isFinite(depth))
{
allWords3NaN = false;
}
if(uStrNumCmp(_version, "0.11.2") >= 0)
{
descriptorSize = sqlite3_column_int(ppStmt, index++); // VisualWord descriptor size
descriptor = sqlite3_column_blob(ppStmt, index); // VisualWord descriptor array
dRealSize = sqlite3_column_bytes(ppStmt, index++);
if(descriptor && descriptorSize>0 && dRealSize>0)
{
cv::Mat d;
if(dRealSize == descriptorSize)
{
// CV_8U binary descriptors
d = cv::Mat(1, descriptorSize, CV_8U);
}
else if(dRealSize/int(sizeof(float)) == descriptorSize)
{
// CV_32F
d = cv::Mat(1, descriptorSize, CV_32F);
}
else
{
UFATAL("Saved buffer size (%d bytes) is not the same as descriptor size (%d)", dRealSize, descriptorSize);
}
memcpy(d.data, descriptor, dRealSize);
descriptors.push_back(d);
}
}
rc = sqlite3_step(ppStmt);
}
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
if(visualWords.size()==0)
{
UDEBUG("Empty signature detected! (id=%d)", (*iter)->id());
}
else
{
if(allWords3NaN)
{
visualWords3.clear();
}
(*iter)->setWords(visualWords, visualWordsKpts, visualWords3, descriptors);
ULOGGER_DEBUG("Add %d keypoints, %d 3d points and %d descriptors to node %d", (int)visualWords.size(), allWords3NaN?0:(int)visualWords3.size(), (int)descriptors.rows, (*iter)->id());
}
//reset
rc = sqlite3_reset(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
}
// Finalize (delete) the statement
rc = sqlite3_finalize(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
ULOGGER_DEBUG("Time=%fs", timer.ticks());
this->loadLinksQuery(nodes); this->loadLinksQuery(nodes);
ULOGGER_DEBUG("Time load links=%fs", timer.ticks()); ULOGGER_DEBUG("Time loading links=%fs", timer.ticks());
for(std::list<Signature*>::iterator iter = nodes.begin(); iter!=nodes.end(); ++iter) for(std::list<Signature*>::iterator iter = nodes.begin(); iter!=nodes.end(); ++iter)
{ {
@@ -3626,7 +3487,7 @@ void DBDriverSqlite3::loadSignaturesQuery(const std::list<int> & ids, std::list<
} }
} }
void DBDriverSqlite3::loadLastNodesQuery(std::list<Signature *> & nodes) const void DBDriverSqlite3::loadLastNodesQuery(std::list<Signature *> & nodes, bool loadWordIdsOnly) const
{ {
ULOGGER_DEBUG(""); ULOGGER_DEBUG("");
if(_ppDb) if(_ppDb)
@@ -3672,15 +3533,15 @@ void DBDriverSqlite3::loadLastNodesQuery(std::list<Signature *> & nodes) const
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str()); UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
ULOGGER_DEBUG("Loading %d signatures...", ids.size()); ULOGGER_DEBUG("Loading %d signatures...", ids.size());
this->loadSignaturesQuery(ids, nodes); this->loadSignaturesQuery(ids, nodes, loadWordIdsOnly);
ULOGGER_DEBUG("loaded=%d, Time=%fs", nodes.size(), timer.ticks()); ULOGGER_DEBUG("loaded=%d, Time=%fs", nodes.size(), timer.ticks());
} }
} }
void DBDriverSqlite3::loadQuery(VWDictionary * dictionary, bool lastStateOnly) const void DBDriverSqlite3::loadQuery(VWDictionary & dictionary, bool lastStateOnly) const
{ {
ULOGGER_DEBUG(""); ULOGGER_DEBUG("");
if(_ppDb && dictionary) if(_ppDb)
{ {
std::string type; std::string type;
UTimer timer; UTimer timer;
@@ -3743,11 +3604,11 @@ void DBDriverSqlite3::loadQuery(VWDictionary * dictionary, bool lastStateOnly) c
memcpy(d.data, descriptor, dRealSize); memcpy(d.data, descriptor, dRealSize);
VisualWord * vw = new VisualWord(id, d); VisualWord * vw = new VisualWord(id, d);
vw->setSaved(true); vw->setSaved(true);
dictionary->addWord(vw); dictionary.addWord(vw);
if(++count % 5000 == 0) if(++count % 5000 == 0)
{ {
ULOGGER_DEBUG("Loaded %d words...", count); //ULOGGER_DEBUG("Loaded %d words...", count);
} }
rc = sqlite3_step(ppStmt); // next result... rc = sqlite3_step(ppStmt); // next result...
} }
@@ -3758,9 +3619,50 @@ void DBDriverSqlite3::loadQuery(VWDictionary * dictionary, bool lastStateOnly) c
// Get Last word id // Get Last word id
getLastWordId(id); getLastWordId(id);
dictionary->setLastWordId(id); dictionary.setLastWordId(id);
ULOGGER_DEBUG("Time=%fs", timer.ticks()); if(uStrNumCmp(_version, "0.23.0") >= 0) {
// load dictionary index
std::stringstream query3;
query3 << "SELECT dictionary_index "
<< "FROM Admin "
<< "WHERE version='" << _version.c_str()
<<"';";
rc = sqlite3_prepare_v2(_ppDb, query3.str().c_str(), -1, &ppStmt, 0);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
// Process the result if one
rc = sqlite3_step(ppStmt);
UASSERT_MSG(rc == SQLITE_ROW, uFormat("DB error (%s): Not found first Admin row: query=\"%s\"", _version.c_str(), query3.str().c_str()).c_str());
if(rc == SQLITE_ROW)
{
const void * data = 0;
int dataSize = 0;
int index = 0;
//opt_poses
data = sqlite3_column_blob(ppStmt, index);
dataSize = sqlite3_column_bytes(ppStmt, index++);
if(dataSize>4 && data)
{
UDEBUG("A flann index was saved in the database (size=%ld).", dataSize);
dictionary.deserializeIndex((const unsigned char*)data, dataSize);
}
else {
UDEBUG("No flann index was saved in the database.");
}
rc = sqlite3_step(ppStmt); // next result...
}
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
// Finalize (delete) the statement
rc = sqlite3_finalize(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
}
ULOGGER_DEBUG("Loaded %d words... time=%fs", count, timer.ticks());
} }
} }
@@ -3860,6 +3762,262 @@ void DBDriverSqlite3::loadWordsQuery(const std::set<int> & wordIds, std::list<Vi
} }
} }
void DBDriverSqlite3::loadWordIdsQuery(std::list<Signature *> & signatures) const
{
if(_ppDb)
{
int rc = SQLITE_OK;
sqlite3_stmt * ppStmt = 0;
std::stringstream query;
if(uStrNumCmp(_version, "0.13.0") >= 0)
{
query << "SELECT word_id "
"FROM Feature "
"WHERE node_id = ? ";
}
else if(uStrNumCmp(_version, "0.12.0") >= 0)
{
query << "SELECT word_id "
"FROM Map_Node_Word "
"WHERE node_id = ? ";
}
else if(uStrNumCmp(_version, "0.11.2") >= 0)
{
query << "SELECT word_id "
"FROM Map_Node_Word "
"WHERE node_id = ? ";
}
else
{
query << "SELECT word_id "
"FROM Map_Node_Word "
"WHERE node_id = ? ";
}
query << " ORDER BY word_id"; // Needed for fast insertion below
query << ";";
rc = sqlite3_prepare_v2(_ppDb, query.str().c_str(), -1, &ppStmt, 0);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
for(std::list<Signature*>::const_iterator iter=signatures.begin(); iter!=signatures.end(); ++iter)
{
//ULOGGER_DEBUG("Loading words of %d...", (*iter)->id());
// bind id
rc = sqlite3_bind_int(ppStmt, 1, (*iter)->id());
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
int visualWordId = 0;
std::multimap<int, int> visualWords;
// Process the result if one
rc = sqlite3_step(ppStmt);
while(rc == SQLITE_ROW)
{
int index = 0;
visualWordId = sqlite3_column_int(ppStmt, index++);
visualWords.insert(visualWords.end(), std::make_pair(visualWordId, -1));
rc = sqlite3_step(ppStmt);
}
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
if(visualWords.size()==0)
{
UDEBUG("Empty signature detected! (id=%d)", (*iter)->id());
}
else
{
(*iter)->setWords(visualWords, std::vector<cv::KeyPoint>(), std::vector<cv::Point3f>(), cv::Mat());
//ULOGGER_DEBUG("Add %d keypoints, %d 3d points and %d descriptors to node %d", (int)visualWords.size(), allWords3NaN?0:(int)visualWords3.size(), (int)descriptors.rows, (*iter)->id());
}
//reset
rc = sqlite3_reset(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
}
// Finalize (delete) the statement
rc = sqlite3_finalize(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
}
}
void DBDriverSqlite3::loadWordsQuery(std::list<Signature *> & signatures) const
{
if(_ppDb)
{
int rc = SQLITE_OK;
sqlite3_stmt * ppStmt = 0;
std::stringstream query;
if(uStrNumCmp(_version, "0.13.0") >= 0)
{
query << "SELECT word_id, pos_x, pos_y, size, dir, response, octave, depth_x, depth_y, depth_z, descriptor_size, descriptor "
"FROM Feature "
"WHERE node_id = ? ";
}
else if(uStrNumCmp(_version, "0.12.0") >= 0)
{
query << "SELECT word_id, pos_x, pos_y, size, dir, response, octave, depth_x, depth_y, depth_z, descriptor_size, descriptor "
"FROM Map_Node_Word "
"WHERE node_id = ? ";
}
else if(uStrNumCmp(_version, "0.11.2") >= 0)
{
query << "SELECT word_id, pos_x, pos_y, size, dir, response, depth_x, depth_y, depth_z, descriptor_size, descriptor "
"FROM Map_Node_Word "
"WHERE node_id = ? ";
}
else
{
query << "SELECT word_id, pos_x, pos_y, size, dir, response, depth_x, depth_y, depth_z "
"FROM Map_Node_Word "
"WHERE node_id = ? ";
}
query << " ORDER BY word_id"; // Needed for fast insertion below
query << ";";
rc = sqlite3_prepare_v2(_ppDb, query.str().c_str(), -1, &ppStmt, 0);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
float nanFloat = std::numeric_limits<float>::quiet_NaN ();
for(std::list<Signature*>::const_iterator iter=signatures.begin(); iter!=signatures.end(); ++iter)
{
//ULOGGER_DEBUG("Loading words of %d...", (*iter)->id());
// bind id
rc = sqlite3_bind_int(ppStmt, 1, (*iter)->id());
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
int visualWordId = 0;
int descriptorSize = 0;
const void * descriptor = 0;
int dRealSize = 0;
cv::KeyPoint kpt;
std::multimap<int, int> visualWords;
std::vector<cv::KeyPoint> visualWordsKpts;
std::vector<cv::Point3f> visualWords3;
cv::Mat descriptors;
bool allWords3NaN = true;
cv::Point3f depth(0,0,0);
// Process the result if one
rc = sqlite3_step(ppStmt);
while(rc == SQLITE_ROW)
{
int index = 0;
visualWordId = sqlite3_column_int(ppStmt, index++);
kpt.pt.x = sqlite3_column_double(ppStmt, index++);
kpt.pt.y = sqlite3_column_double(ppStmt, index++);
kpt.size = sqlite3_column_int(ppStmt, index++);
kpt.angle = sqlite3_column_double(ppStmt, index++);
kpt.response = sqlite3_column_double(ppStmt, index++);
if(uStrNumCmp(_version, "0.12.0") >= 0)
{
kpt.octave = sqlite3_column_int(ppStmt, index++);
}
if(sqlite3_column_type(ppStmt, index) == SQLITE_NULL)
{
depth.x = nanFloat;
++index;
}
else
{
depth.x = sqlite3_column_double(ppStmt, index++);
}
if(sqlite3_column_type(ppStmt, index) == SQLITE_NULL)
{
depth.y = nanFloat;
++index;
}
else
{
depth.y = sqlite3_column_double(ppStmt, index++);
}
if(sqlite3_column_type(ppStmt, index) == SQLITE_NULL)
{
depth.z = nanFloat;
++index;
}
else
{
depth.z = sqlite3_column_double(ppStmt, index++);
}
visualWordsKpts.push_back(kpt);
visualWords.insert(visualWords.end(), std::make_pair(visualWordId, visualWordsKpts.size()-1));
visualWords3.push_back(depth);
if(allWords3NaN && util3d::isFinite(depth))
{
allWords3NaN = false;
}
if(uStrNumCmp(_version, "0.11.2") >= 0)
{
descriptorSize = sqlite3_column_int(ppStmt, index++); // VisualWord descriptor size
descriptor = sqlite3_column_blob(ppStmt, index); // VisualWord descriptor array
dRealSize = sqlite3_column_bytes(ppStmt, index++);
if(descriptor && descriptorSize>0 && dRealSize>0)
{
cv::Mat d;
if(dRealSize == descriptorSize)
{
// CV_8U binary descriptors
d = cv::Mat(1, descriptorSize, CV_8U);
}
else if(dRealSize/int(sizeof(float)) == descriptorSize)
{
// CV_32F
d = cv::Mat(1, descriptorSize, CV_32F);
}
else
{
UFATAL("Saved buffer size (%d bytes) is not the same as descriptor size (%d)", dRealSize, descriptorSize);
}
memcpy(d.data, descriptor, dRealSize);
descriptors.push_back(d);
}
}
rc = sqlite3_step(ppStmt);
}
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
if(visualWords.size()==0)
{
UDEBUG("Empty signature detected! (id=%d)", (*iter)->id());
}
else
{
if(allWords3NaN)
{
visualWords3.clear();
}
(*iter)->setWords(visualWords, visualWordsKpts, visualWords3, descriptors);
//ULOGGER_DEBUG("Add %d keypoints, %d 3d points and %d descriptors to node %d", (int)visualWords.size(), allWords3NaN?0:(int)visualWords3.size(), (int)descriptors.rows, (*iter)->id());
}
//reset
rc = sqlite3_reset(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
}
// Finalize (delete) the statement
rc = sqlite3_finalize(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
}
}
void DBDriverSqlite3::loadLinksQuery( void DBDriverSqlite3::loadLinksQuery(
int signatureId, int signatureId,
std::multimap<int, Link> & links, std::multimap<int, Link> & links,
@@ -4174,7 +4332,7 @@ void DBDriverSqlite3::loadLinksQuery(std::list<Signature *> & signatures) const
//reset //reset
rc = sqlite3_reset(ppStmt); rc = sqlite3_reset(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str()); UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
UDEBUG("time=%fs, node=%d, links.size=%d", timer.ticks(), (*iter)->id(), links.size()); //UDEBUG("time=%fs, node=%d, links.size=%d", timer.ticks(), (*iter)->id(), links.size());
} }
// Finalize (delete) the statement // Finalize (delete) the statement
@@ -5117,8 +5275,8 @@ std::map<int, Transform> DBDriverSqlite3::loadOptimizedPosesQuery(Transform * la
Transform t(serializedPoses.at<float>(i*12), serializedPoses.at<float>(i*12+1), serializedPoses.at<float>(i*12+2), serializedPoses.at<float>(i*12+3), Transform t(serializedPoses.at<float>(i*12), serializedPoses.at<float>(i*12+1), serializedPoses.at<float>(i*12+2), serializedPoses.at<float>(i*12+3),
serializedPoses.at<float>(i*12+4), serializedPoses.at<float>(i*12+5), serializedPoses.at<float>(i*12+6), serializedPoses.at<float>(i*12+7), serializedPoses.at<float>(i*12+4), serializedPoses.at<float>(i*12+5), serializedPoses.at<float>(i*12+6), serializedPoses.at<float>(i*12+7),
serializedPoses.at<float>(i*12+8), serializedPoses.at<float>(i*12+9), serializedPoses.at<float>(i*12+10), serializedPoses.at<float>(i*12+11)); serializedPoses.at<float>(i*12+8), serializedPoses.at<float>(i*12+9), serializedPoses.at<float>(i*12+10), serializedPoses.at<float>(i*12+11));
poses.insert(std::make_pair(serializedIds.at<int>(i), t)); poses.insert(poses.end(), std::make_pair(serializedIds.at<int>(i), t));
UDEBUG("Optimized pose %d: %s", serializedIds.at<int>(i), t.prettyPrint().c_str()); //UDEBUG("Optimized pose %d: %s", serializedIds.at<int>(i), t.prettyPrint().c_str());
} }
} }
@@ -5591,6 +5749,47 @@ cv::Mat DBDriverSqlite3::loadOptimizedMeshQuery(
return cloud; return cloud;
} }
void DBDriverSqlite3::saveFlannIndexQuery(const std::vector<unsigned char> & data) const
{
UDEBUG("");
if(_ppDb && uStrNumCmp(_version, "0.23.0") >= 0)
{
UTimer timer;
timer.start();
int rc = SQLITE_OK;
sqlite3_stmt * ppStmt = 0;
std::string query;
// Update table Admin
query = uFormat("UPDATE Admin SET dictionary_index=? WHERE version='%s';", _version.c_str());
rc = sqlite3_prepare_v2(_ppDb, query.c_str(), -1, &ppStmt, 0);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
int index = 1;
if(data.empty())
{
rc = sqlite3_bind_null(ppStmt, index++);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
}
else
{
rc = sqlite3_bind_blob(ppStmt, index++, data.data(), data.size(), SQLITE_STATIC);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
}
//execute query
rc=sqlite3_step(ppStmt);
UASSERT_MSG(rc == SQLITE_DONE, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
// Finalize (delete) the statement
rc = sqlite3_finalize(ppStmt);
UASSERT_MSG(rc == SQLITE_OK, uFormat("DB error (%s): %s", _version.c_str(), sqlite3_errmsg(_ppDb)).c_str());
UDEBUG("Time=%fs", timer.ticks());
}
}
std::string DBDriverSqlite3::queryStepNode() const std::string DBDriverSqlite3::queryStepNode() const
{ {
if(uStrNumCmp(_version, "0.18.0") >= 0) if(uStrNumCmp(_version, "0.18.0") >= 0)
@@ -6598,7 +6797,7 @@ void DBDriverSqlite3::stepLink(
{ {
UFATAL(""); UFATAL("");
} }
UDEBUG("Save link from %d to %d, type=%d", link.from(), link.to(), link.type()); //UDEBUG("Save link from %d to %d, type=%d", link.from(), link.to(), link.type());
// Don't save virtual links // Don't save virtual links
if(link.type()==Link::kVirtualClosure) if(link.type()==Link::kVirtualClosure)
+35 -15
View File
@@ -260,7 +260,7 @@ bool DBReader::init(
else else
{ {
Signature * s = _dbDriver->loadSignature(*_ids.begin()); Signature * s = _dbDriver->loadSignature(*_ids.begin());
_dbDriver->loadNodeData(s); _dbDriver->loadNodeData(*s);
if( s->sensorData().imageCompressed().empty() && if( s->sensorData().imageCompressed().empty() &&
s->getWords().empty() && s->getWords().empty() &&
!s->sensorData().laserScanCompressed().empty()) !s->sensorData().laserScanCompressed().empty())
@@ -510,22 +510,41 @@ SensorData DBReader::getNextData(SensorCaptureInfo * info)
} }
else else
{ {
// if localization data saved in database, covariance will be set in a prior link // In case the graph was reduced, look for forward neighbor link from previous id
_dbDriver->loadLinks(*_currentId, links, Link::kPosePrior); bool covAdded = false;
if(links.size()) if(_currentId != _ids.begin()) {
{ std::set<int>::iterator previousId = _currentId;
// assume the first is the backward neighbor, take its variance --previousId;
infMatrix = links.begin()->second.infMatrix(); std::multimap<int, Link> previousLinks;
_previousInfMatrix = infMatrix; _dbDriver->loadLinks(*previousId, previousLinks, Link::kNeighbor);
} if(previousLinks.size() && previousLinks.rbegin()->first == *_currentId)
else
{
if(_previousInfMatrix.empty())
{ {
_previousInfMatrix = cv::Mat::eye(6,6,CV_64FC1); // assume the last is the forward neighbor pointing to current ID, take its covariance
infMatrix = previousLinks.rbegin()->second.infMatrix();
_previousInfMatrix = infMatrix;
covAdded = true;
}
}
if(!covAdded) {
// if localization data saved in database, covariance will be set in a prior link
_dbDriver->loadLinks(*_currentId, links, Link::kPosePrior);
if(links.size())
{
// assume the first is the backward neighbor, take its variance
infMatrix = links.begin()->second.infMatrix();
_previousInfMatrix = infMatrix;
}
else
{
if(_previousInfMatrix.empty())
{
_previousInfMatrix = cv::Mat::eye(6,6,CV_64FC1);
}
// we have a node not linked to map, use last variance
UWARN("The node loaded (%d) doesn't have neighbor, re-using the covariance of the previous link for odometry.", s->id());
infMatrix = _previousInfMatrix;
} }
// we have a node not linked to map, use last variance
infMatrix = _previousInfMatrix;
} }
} }
} }
@@ -837,6 +856,7 @@ SensorData DBReader::getNextData(SensorCaptureInfo * info)
if(info) if(info)
{ {
info->odomPose = pose; info->odomPose = pose;
UASSERT(!infMatrix.empty());
info->odomCovariance = infMatrix.inv(); info->odomCovariance = infMatrix.inv();
info->odomVelocity = s->getVelocity(); info->odomVelocity = s->getVelocity();
UDEBUG("odom variance = %f/%f", info->odomCovariance.at<double>(0,0), info->odomCovariance.at<double>(5,5)); UDEBUG("odom variance = %f/%f", info->odomCovariance.at<double>(0,0), info->odomCovariance.at<double>(5,5));
+114 -4
View File
@@ -47,6 +47,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef RTABMAP_TORCH #ifdef RTABMAP_TORCH
#include "superpoint_torch/SuperPoint.h" #include "superpoint_torch/SuperPoint.h"
#endif #endif
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
#include "superpoint_rpautrat/SuperpointRpautrat.h"
#endif
#ifdef RTABMAP_PYTHON #ifdef RTABMAP_PYTHON
#include "python/PyDetector.h" #include "python/PyDetector.h"
@@ -730,9 +733,14 @@ Feature2D * Feature2D::create(Feature2D::Type type, const ParametersMap & parame
feature2D = new ORBOctree(parameters); feature2D = new ORBOctree(parameters);
break; break;
#ifdef RTABMAP_TORCH #ifdef RTABMAP_TORCH
case Feature2D::kFeatureSuperPointTorch: case Feature2D::kFeatureSuperPointTorch:
feature2D = new SuperPointTorch(parameters); feature2D = new SuperPointTorch(parameters);
break; break;
#endif
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
case Feature2D::kFeatureSuperPointRpautrat:
feature2D = new SuperPointRpautrat(parameters);
break;
#endif #endif
case Feature2D::kFeatureSurfFreak: case Feature2D::kFeatureSurfFreak:
feature2D = new SURF_FREAK(parameters); feature2D = new SURF_FREAK(parameters);
@@ -831,7 +839,7 @@ std::vector<cv::KeyPoint> Feature2D::generateKeypoints(const cv::Mat & image, co
cv::Rect roi(globalRoi.x + j*colSize, globalRoi.y + i*rowSize, colSize, rowSize); cv::Rect roi(globalRoi.x + j*colSize, globalRoi.y + i*rowSize, colSize, rowSize);
std::vector<cv::KeyPoint> subKeypoints; std::vector<cv::KeyPoint> subKeypoints;
subKeypoints = this->generateKeypointsImpl(image, roi, mask); subKeypoints = this->generateKeypointsImpl(image, roi, mask);
if (this->getType() != Feature2D::Type::kFeaturePyDetector) if (this->getType() != Feature2D::Type::kFeaturePyDetector && this->getType() != Feature2D::Type::kFeatureSuperPointRpautrat)
{ {
limitKeypoints(subKeypoints, maxFeatures, roi.size(), this->getSSC()); limitKeypoints(subKeypoints, maxFeatures, roi.size(), this->getSSC());
} }
@@ -2615,6 +2623,108 @@ cv::Mat SuperPointTorch::generateDescriptorsImpl(const cv::Mat & image, std::vec
} }
//////////////////////////
//SuperPointRpautrat
//////////////////////////
SuperPointRpautrat::SuperPointRpautrat(const ParametersMap & parameters) :
superpointWeightsPath_(Parameters::defaultSuperPointRpautratWeightsPath()),
superpointModelPath_(Parameters::defaultSuperPointRpautratModelPath()),
outputDir_(""),
threshold_(Parameters::defaultSuperPointRpautratThreshold()),
nms_(Parameters::defaultSuperPointRpautratNMS()),
minDistance_(Parameters::defaultSuperPointRpautratNMSRadius()),
cuda_(Parameters::defaultSuperPointRpautratCuda())
{
parseParameters(parameters);
}
SuperPointRpautrat::~SuperPointRpautrat()
{
}
void SuperPointRpautrat::parseParameters(const ParametersMap & parameters)
{
Feature2D::parseParameters(parameters);
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
std::string previousWeightsPath = superpointWeightsPath_;
std::string previousModelPath = superpointModelPath_;
bool previousCuda = cuda_;
float previousThreshold = threshold_;
bool previousNms = nms_;
int previousMinDistance = minDistance_;
Parameters::parse(parameters, Parameters::kSuperPointRpautratWeightsPath(), superpointWeightsPath_);
Parameters::parse(parameters, Parameters::kSuperPointRpautratModelPath(), superpointModelPath_);
Parameters::parse(parameters, Parameters::kSuperPointRpautratThreshold(), threshold_);
Parameters::parse(parameters, Parameters::kSuperPointRpautratNMS(), nms_);
Parameters::parse(parameters, Parameters::kSuperPointRpautratNMSRadius(), minDistance_);
Parameters::parse(parameters, Parameters::kSuperPointRpautratCuda(), cuda_);
Parameters::parse(parameters, Parameters::kRtabmapWorkingDirectory(), outputDir_);
// If working directory is not set, use the default
if(outputDir_.empty())
{
outputDir_ = Parameters::createDefaultWorkingDirectory();
}
// Reinitialize detector if model-affecting parameters changed
if(superPoint_.get() == 0 ||
superpointWeightsPath_.compare(previousWeightsPath) != 0 ||
superpointModelPath_.compare(previousModelPath) != 0 ||
previousCuda != cuda_ ||
previousThreshold != threshold_ ||
previousNms != nms_ ||
previousMinDistance != minDistance_)
{
superPoint_ = cv::Ptr<SPDetectorRpautrat>(new SPDetectorRpautrat(superpointWeightsPath_, superpointModelPath_, outputDir_, threshold_, nms_, minDistance_, cuda_, this->getMaxFeatures(), this->getSSC()));
}
else if(superPoint_.get() != 0)
{
// Update post-processing parameters without reinitializing
superPoint_->setMaxFeatures(this->getMaxFeatures());
superPoint_->setSSC(this->getSSC());
}
#else
UWARN("RTAB-Map is not built with Torch support so SuperPoint Rpautrat feature cannot be used!");
#endif
}
std::vector<cv::KeyPoint> SuperPointRpautrat::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
{
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
if(roi.x!=0 || roi.y !=0)
{
UERROR("SuperPoint Rpautrat: Not supporting ROI (%d,%d,%d,%d). Make sure %s, %s, %s, %s, %s, %s are all set to default values.",
roi.x, roi.y, roi.width, roi.height,
Parameters::kKpRoiRatios().c_str(),
Parameters::kVisRoiRatios().c_str(),
Parameters::kVisGridRows().c_str(),
Parameters::kVisGridCols().c_str(),
Parameters::kKpGridRows().c_str(),
Parameters::kKpGridCols().c_str());
return std::vector<cv::KeyPoint>();
}
return superPoint_->detect(image, mask);
#else
UWARN("RTAB-Map is not built with Torch support so SuperPoint Rpautrat feature cannot be used!");
return std::vector<cv::KeyPoint>();
#endif
}
cv::Mat SuperPointRpautrat::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
{
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
return superPoint_->compute(keypoints);
#else
UWARN("RTAB-Map is not built with Torch support so SuperPoint Rpautrat feature cannot be used!");
return cv::Mat();
#endif
}
////////////////////////// //////////////////////////
//GFTT-DAISY //GFTT-DAISY
////////////////////////// //////////////////////////
+330 -119
View File
@@ -27,8 +27,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/core/FlannIndex.h> #include <rtabmap/core/FlannIndex.h>
#include <rtabmap/utilite/ULogger.h> #include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UTimer.h>
#include <rtabmap/utilite/UConversion.h>
#include <rtabmap/core/Compression.h>
#include <rtabmap/core/Version.h>
#ifdef WIN32
#include <rtabmap/core/Parameters.h>
#endif
#include "rtflann/flann.hpp" #include "rtflann/flann.hpp"
#include <boost/crc.hpp>
namespace rtabmap { namespace rtabmap {
@@ -37,7 +45,6 @@ FlannIndex::FlannIndex():
nextIndex_(0), nextIndex_(0),
featuresType_(0), featuresType_(0),
featuresDim_(0), featuresDim_(0),
isLSH_(false),
useDistanceL1_(false), useDistanceL1_(false),
rebalancingFactor_(2.0f) rebalancingFactor_(2.0f)
{ {
@@ -49,9 +56,9 @@ FlannIndex::~FlannIndex()
void FlannIndex::release() void FlannIndex::release()
{ {
UDEBUG("");
if(index_) if(index_)
{ {
UDEBUG("Clearing flann index...");
if(featuresType_ == CV_8UC1) if(featuresType_ == CV_8UC1)
{ {
delete (rtflann::Index<rtflann::Hamming<unsigned char> >*)index_; delete (rtflann::Index<rtflann::Hamming<unsigned char> >*)index_;
@@ -72,12 +79,139 @@ void FlannIndex::release()
} }
} }
index_ = 0; index_ = 0;
UDEBUG("Clearing flann index... done!");
} }
nextIndex_ = 0; nextIndex_ = 0;
isLSH_ = false;
addedDescriptors_.clear(); addedDescriptors_.clear();
removedIndexes_.clear(); removedIndexes_.clear();
UDEBUG(""); }
#define FLANN_INDEX_HEADER_SIZE 12
std::vector<unsigned char> FlannIndex::serializeIndex(bool computeChecksum) const {
if(index_ && !addedDescriptors_.empty())
{
#ifdef WIN32
UERROR("FLANN index serialization is not yet implemented on Windows. Parameter \"%s\" cannot be used.", Parameters::kKpFlannIndexSaved().c_str());
#else
UTimer timer;
const int headerSizeBytes = sizeof(int)*FLANN_INDEX_HEADER_SIZE;
std::vector<unsigned char> indexData(1024*1024*100 + headerSizeBytes); // Max 100 MB
FILE* indexDataPtr = fmemopen(indexData.data()+headerSizeBytes, indexData.size() - headerSizeBytes, "wb");
long bytes_written = 0;
if (indexDataPtr) {
if(featuresType_ == CV_8UC1)
{
((rtflann::Index<rtflann::Hamming<unsigned char> >*)index_)->save(indexDataPtr);
}
else
{
if(useDistanceL1_)
{
((rtflann::Index<rtflann::L1<float> >*)index_)->save(indexDataPtr);;
}
else if(featuresDim_ <= 3)
{
((rtflann::Index<rtflann::L2_Simple<float> >*)index_)->save(indexDataPtr);;
}
else
{
((rtflann::Index<rtflann::L2<float> >*)index_)->save(indexDataPtr);;
}
}
bytes_written = ftell(indexDataPtr);
fclose(indexDataPtr);
}
if(bytes_written < long(indexData.size()-headerSizeBytes))
{
//Expected data size and type
int dataRows = 0;
int dataCols = 0;
int dataType = -1;
cv::Mat dataset;
std::set<int> removedDescriptors;
if(computeChecksum){
removedDescriptors.insert(removedIndexes_.begin(), removedIndexes_.end());
}
for(const auto & iter: addedDescriptors_)
{
UASSERT(!iter.second.empty());
dataRows += iter.second.rows;
if(dataCols <= 0) {
dataCols = iter.second.cols;
}
else {
UASSERT(dataCols == iter.second.cols);
}
if(dataType < 0) {
dataType = iter.second.type();
}
else {
UASSERT(dataType == iter.second.type());
}
if(computeChecksum){
if(removedDescriptors.find(iter.first) == removedDescriptors.end()) {
if(dataset.empty()) {
dataset = iter.second.clone();
}
else {
dataset.push_back(iter.second);
}
}
else {
dataRows -= iter.second.rows;
}
}
}
if(!computeChecksum) {
for(const auto & index: removedIndexes_)
{
dataRows -= addedDescriptors_.at(index).rows;
}
}
unsigned int crcValue = 0;
if(computeChecksum) {
boost::crc_32_type result;
result.process_bytes(dataset.data, dataset.total()*dataset.elemSize());
crcValue = result.checksum();
}
indexData.resize(bytes_written+headerSizeBytes);
indexData.shrink_to_fit();
int rebalancingFactorAsInt;
memcpy(&rebalancingFactorAsInt, &rebalancingFactor_, sizeof(rebalancingFactor_));
int crcValueAsInt;
memcpy(&crcValueAsInt, &crcValue, sizeof(crcValue));
int header[FLANN_INDEX_HEADER_SIZE] = {
RTABMAP_VERSION_MAJOR, RTABMAP_VERSION_MINOR, RTABMAP_VERSION_PATCH, // 0,1,2
algorithm_, // 3,
featuresDim_, // 4,
useDistanceL1_?1:0, // 5,
rebalancingFactorAsInt, // 6,
dataRows, // 7,
dataCols, // 8,
dataType, // 9,
crcValueAsInt, // 10
(int)bytes_written}; // 11
UDEBUG("Header: \"%d.%d.%d\" alg=%d dim=%d L1=%d factor=%f data(%dx%d type=%d, crc=%X) %d",
header[0],header[1],header[2],
header[3],
header[4],
header[5],
rebalancingFactor_,
header[7], header[8], header[9], crcValueAsInt,
header[11]);
memcpy(indexData.data(), header, headerSizeBytes);
return indexData;
}
else {
UERROR("Target buffer too small to serialize index, aborting.");
}
UDEBUG("Flann serialization: %fs", timer.ticks());
#endif
}
return std::vector<unsigned char>();
} }
size_t FlannIndex::indexedFeatures() const size_t FlannIndex::indexedFeatures() const
@@ -139,12 +273,13 @@ size_t FlannIndex::memoryUsed() const
return memoryUsage; return memoryUsage;
} }
void FlannIndex::buildLinearIndex( void FlannIndex::buildIndex(
flann_algorithm_t algorithm,
const cv::Mat & features, const cv::Mat & features,
bool useDistanceL1, bool useDistanceL1,
float rebalancingFactor) float rebalancingFactor)
{ {
UDEBUG(""); UDEBUG("algorithm=%d", (int)algorithm);
this->release(); this->release();
UASSERT(index_ == 0); UASSERT(index_ == 0);
UASSERT(features.type() == CV_32FC1 || features.type() == CV_8UC1); UASSERT(features.type() == CV_32FC1 || features.type() == CV_8UC1);
@@ -152,8 +287,29 @@ void FlannIndex::buildLinearIndex(
featuresDim_ = features.cols; featuresDim_ = features.cols;
useDistanceL1_ = useDistanceL1; useDistanceL1_ = useDistanceL1;
rebalancingFactor_ = rebalancingFactor; rebalancingFactor_ = rebalancingFactor;
algorithm_ = algorithm;
rtflann::LinearIndexParams params; rtflann::IndexParams params;
switch (algorithm)
{
case FLANN_INDEX_LINEAR:
params = rtflann::LinearIndexParams();
break;
case FLANN_INDEX_KDTREE:
params = rtflann::KDTreeIndexParams(4);
break;
case FLANN_INDEX_KDTREE_SINGLE:
params = rtflann::KDTreeSingleIndexParams(10, true);
break;
case FLANN_INDEX_LSH:
UASSERT(features.type() == CV_8UC1);
params = rtflann::LshIndexParams(12, 20, 2);
break;
default:
UFATAL("The flann algorithm type %d is not supported!", (int)algorithm);
break;
}
if(featuresType_ == CV_8UC1) if(featuresType_ == CV_8UC1)
{ {
@@ -199,13 +355,140 @@ void FlannIndex::buildLinearIndex(
UDEBUG(""); UDEBUG("");
} }
void FlannIndex::buildKDTreeIndex( bool FlannIndex::loadIndex(
const cv::Mat & features, const std::vector<unsigned char> & indexData,
int trees, flann_algorithm_t algorithm,
bool useDistanceL1, const cv::Mat & features,
float rebalancingFactor) bool useDistanceL1,
float rebalancingFactor,
std::string * error)
{ {
UDEBUG(""); return loadIndex(
indexData.data(),
indexData.size(),
algorithm,
features,
useDistanceL1,
rebalancingFactor),
error;
}
bool FlannIndex::loadIndex(
const unsigned char * indexData,
size_t indexDataSize,
flann_algorithm_t algorithm,
const cv::Mat & features,
bool useDistanceL1,
float rebalancingFactor,
std::string * error)
{
UASSERT(indexData!=NULL);
if(indexDataSize == 0) {
UWARN("Trying to load empty index....");
return false;
}
#ifdef WIN32
UERROR("FLANN index deserialization is not yet implemented on Windows. Index cannot be loaded from memory buffer.");
return false;
#else
// Check if the features match the expected data from the index
size_t headerSizeBytes = sizeof(int)*FLANN_INDEX_HEADER_SIZE;
if(indexDataSize < headerSizeBytes) {
if(error) {
*error = uFormat("Wrong header size detected (%ld vs expected %ld).", indexDataSize, headerSizeBytes);
}
return false;
}
const int * header = (const int *)indexData;
int savedAlgorithm = header[3];
int savedDim = header[4];
bool savedDistanceL1 = header[5]==1;
float savedRebalancingFactor;
memcpy(&savedRebalancingFactor, &header[6], sizeof(header[6]));
int savedRows = header[7];
int savedCols = header[8];
int savedType = header[9];
unsigned int savedCrc;
memcpy(&savedCrc, &header[10], sizeof(header[10]));
int savedIndexSize = header[11];
UDEBUG("Header: \"%d.%d.%d\" alg=%d dim=%d L1=%d factor=%f data(%dx%d type=%d, crc=%X) %d",
header[0],header[1],header[2],
header[3],
header[4],
header[5],
savedRebalancingFactor,
header[7], header[8], header[9], savedCrc,
header[11]);
if(savedAlgorithm != algorithm) {
if(error) {
*error = uFormat("Serialized flann algorithm (%d) doesn't match the expected one (%d).", savedAlgorithm, algorithm);
}
return false;
}
if(savedDim != features.cols) {
if(error) {
*error = uFormat("Serialized feature dimension (%d) doesn't match the expected one (%d).", savedDim, features.cols);
}
return false;
}
if(savedDistanceL1 != useDistanceL1) {
if(error) {
*error = uFormat("Serialized \"use distance L1\" (%s) doesn't match the expected one (%s).", savedDistanceL1?"true":"false", useDistanceL1?"true":"false");
}
return false;
}
if(savedRebalancingFactor != rebalancingFactor) {
if(error) {
*error = uFormat("Serialized \"rebalancing factor\" (%f) doesn't match the expected one (%f).", savedRebalancingFactor, rebalancingFactor);
}
return false;
}
if(savedRows != features.rows) {
if(error) {
*error = uFormat("Serialized feature count (%d) doesn't match the expected one (%d).", savedRows, features.rows);
}
return false;
}
if(savedCols != features.cols) {
if(error) {
*error = uFormat("Serialized feature dimension (%d) doesn't match the expected one (%d).", savedCols, features.cols);
}
return false;
}
if(savedType != features.type()) {
if(error) {
*error = uFormat("Serialized feature type (%d) doesn't match the expected one (%d).", savedType, features.type());
}
return false;
}
if(savedCrc != 0) {
// Compute checksum and compare
boost::crc_32_type result;
result.process_bytes(features.data, features.total()*features.elemSize());
if(savedCrc != result.checksum()) {
if(error) {
*error = uFormat("Serialized feature crc (%X) doesn't match the expected one (%X).", savedCrc, result.checksum());
}
return false;
}
}
if(savedIndexSize != int(indexDataSize - headerSizeBytes)) {
if(error) {
*error = uFormat("Serialized flann index size (%ld) doesn't match the expected one (%ld).", savedIndexSize, indexDataSize - headerSizeBytes);
}
return false;
}
if(savedIndexSize == 0) {
if(error) {
*error = "Serialized flann index is empty.";
}
return false;
}
this->release(); this->release();
UASSERT(index_ == 0); UASSERT(index_ == 0);
UASSERT(features.type() == CV_32FC1 || features.type() == CV_8UC1); UASSERT(features.type() == CV_32FC1 || features.type() == CV_8UC1);
@@ -213,14 +496,39 @@ void FlannIndex::buildKDTreeIndex(
featuresDim_ = features.cols; featuresDim_ = features.cols;
useDistanceL1_ = useDistanceL1; useDistanceL1_ = useDistanceL1;
rebalancingFactor_ = rebalancingFactor; rebalancingFactor_ = rebalancingFactor;
algorithm_ = algorithm;
rtflann::KDTreeIndexParams params(trees); UDEBUG("algorithm=%d", (int)algorithm);
rtflann::IndexParams params;
switch (algorithm)
{
case FLANN_INDEX_LINEAR:
params = rtflann::LinearIndexParams();
break;
case FLANN_INDEX_KDTREE:
params = rtflann::KDTreeIndexParams(4);
break;
case FLANN_INDEX_KDTREE_SINGLE:
params = rtflann::KDTreeSingleIndexParams(10, true);
break;
case FLANN_INDEX_LSH:
UASSERT(features.type() == CV_8UC1);
params = rtflann::LshIndexParams(12, 20, 2);
break;
default:
UFATAL("The flann algorithm type %d is not supported!", (int)algorithm);
break;
}
FILE* indexDataPtr = fmemopen((void*)(indexData+headerSizeBytes), indexDataSize - headerSizeBytes, "r");
if(featuresType_ == CV_8UC1) if(featuresType_ == CV_8UC1)
{ {
rtflann::Matrix<unsigned char> dataset(features.data, features.rows, features.cols); rtflann::Matrix<unsigned char> dataset(features.data, features.rows, features.cols);
index_ = new rtflann::Index<rtflann::Hamming<unsigned char> >(dataset, params); index_ = new rtflann::Index<rtflann::Hamming<unsigned char> >(dataset, params);
((rtflann::Index<rtflann::Hamming<unsigned char> >*)index_)->buildIndex(); ((rtflann::Index<rtflann::Hamming<unsigned char> >*)index_)->load_saved_index(indexDataPtr);
} }
else else
{ {
@@ -228,22 +536,24 @@ void FlannIndex::buildKDTreeIndex(
if(useDistanceL1_) if(useDistanceL1_)
{ {
index_ = new rtflann::Index<rtflann::L1<float> >(dataset, params); index_ = new rtflann::Index<rtflann::L1<float> >(dataset, params);
((rtflann::Index<rtflann::L1<float> >*)index_)->buildIndex(); ((rtflann::Index<rtflann::L1<float> >*)index_)->load_saved_index(indexDataPtr);
} }
else if(featuresDim_ <=3) else if(featuresDim_ <=3)
{ {
index_ = new rtflann::Index<rtflann::L2_Simple<float> >(dataset, params); index_ = new rtflann::Index<rtflann::L2_Simple<float> >(dataset, params);
((rtflann::Index<rtflann::L2_Simple<float> >*)index_)->buildIndex(); ((rtflann::Index<rtflann::L2_Simple<float> >*)index_)->load_saved_index(indexDataPtr);
} }
else else
{ {
index_ = new rtflann::Index<rtflann::L2<float> >(dataset, params); index_ = new rtflann::Index<rtflann::L2<float> >(dataset, params);
((rtflann::Index<rtflann::L2<float> >*)index_)->buildIndex(); ((rtflann::Index<rtflann::L2<float> >*)index_)->load_saved_index(indexDataPtr);
} }
} }
fclose(indexDataPtr);
// incremental FLANN: we should add all headers separately in case we remove // incremental FLANN: we should add all headers separately in case we remove
// some indexes (to keep underlying matrix data allocated) // some indexes (to keep underlying matrix data allocated)
if(rebalancingFactor_ > 1.0f) if(rebalancingFactor_ > 1.0f)
{ {
for(int i=0; i<features.rows; ++i) for(int i=0; i<features.rows; ++i)
@@ -257,107 +567,8 @@ void FlannIndex::buildKDTreeIndex(
addedDescriptors_.insert(std::make_pair(nextIndex_, features)); addedDescriptors_.insert(std::make_pair(nextIndex_, features));
nextIndex_ += features.rows; nextIndex_ += features.rows;
} }
UDEBUG(""); return true;
} #endif
void FlannIndex::buildKDTreeSingleIndex(
const cv::Mat & features,
int leafMaxSize,
bool reorder,
bool useDistanceL1,
float rebalancingFactor)
{
UDEBUG("");
this->release();
UASSERT(index_ == 0);
UASSERT(features.type() == CV_32FC1 || features.type() == CV_8UC1);
featuresType_ = features.type();
featuresDim_ = features.cols;
useDistanceL1_ = useDistanceL1;
rebalancingFactor_ = rebalancingFactor;
rtflann::KDTreeSingleIndexParams params(leafMaxSize, reorder);
if(featuresType_ == CV_8UC1)
{
rtflann::Matrix<unsigned char> dataset(features.data, features.rows, features.cols);
index_ = new rtflann::Index<rtflann::Hamming<unsigned char> >(dataset, params);
((rtflann::Index<rtflann::Hamming<unsigned char> >*)index_)->buildIndex();
}
else
{
rtflann::Matrix<float> dataset((float*)features.data, features.rows, features.cols);
if(useDistanceL1_)
{
index_ = new rtflann::Index<rtflann::L1<float> >(dataset, params);
((rtflann::Index<rtflann::L1<float> >*)index_)->buildIndex();
}
else if(featuresDim_ <=3)
{
index_ = new rtflann::Index<rtflann::L2_Simple<float> >(dataset, params);
((rtflann::Index<rtflann::L2_Simple<float> >*)index_)->buildIndex();
}
else
{
index_ = new rtflann::Index<rtflann::L2<float> >(dataset, params);
((rtflann::Index<rtflann::L2<float> >*)index_)->buildIndex();
}
}
// incremental FLANN: we should add all headers separately in case we remove
// some indexes (to keep underlying matrix data allocated)
if(rebalancingFactor_ > 1.0f)
{
for(int i=0; i<features.rows; ++i)
{
addedDescriptors_.insert(std::make_pair(nextIndex_++, features.row(i)));
}
}
else
{
// tree won't ever be rebalanced, so just keep only one header for the data
addedDescriptors_.insert(std::make_pair(nextIndex_, features));
nextIndex_ += features.rows;
}
UDEBUG("");
}
void FlannIndex::buildLSHIndex(
const cv::Mat & features,
unsigned int table_number,
unsigned int key_size,
unsigned int multi_probe_level,
float rebalancingFactor)
{
UDEBUG("");
this->release();
UASSERT(index_ == 0);
UASSERT(features.type() == CV_8UC1);
featuresType_ = features.type();
featuresDim_ = features.cols;
useDistanceL1_ = true;
rebalancingFactor_ = rebalancingFactor;
rtflann::Matrix<unsigned char> dataset(features.data, features.rows, features.cols);
index_ = new rtflann::Index<rtflann::Hamming<unsigned char> >(dataset, rtflann::LshIndexParams(12, 20, 2));
((rtflann::Index<rtflann::Hamming<unsigned char> >*)index_)->buildIndex();
// incremental FLANN: we should add all headers separately in case we remove
// some indexes (to keep underlying matrix data allocated)
if(rebalancingFactor_ > 1.0f)
{
for(int i=0; i<features.rows; ++i)
{
addedDescriptors_.insert(std::make_pair(nextIndex_++, features.row(i)));
}
}
else
{
// tree won't ever be rebalanced, so just keep only one header for the data
addedDescriptors_.insert(std::make_pair(nextIndex_, features));
nextIndex_ += features.rows;
}
UDEBUG("");
} }
bool FlannIndex::isBuilt() bool FlannIndex::isBuilt()
+24 -8
View File
@@ -99,15 +99,12 @@ unsigned long GlobalMap::getMemoryUsed() const
return memoryUsage; return memoryUsage;
} }
bool GlobalMap::update(const std::map<int, Transform> & poses) bool GlobalMap::fullUpdateNeeded(const std::map<int, Transform> & poses) const
{ {
UDEBUG("Update (poses=%d addedNodes_=%d)", (int)poses.size(), (int)addedNodes_.size());
// First, check of the graph has changed. If so, re-create the octree by moving all occupied nodes.
bool graphOptimized = false; // If a loop closure happened (e.g., poses are modified) bool graphOptimized = false; // If a loop closure happened (e.g., poses are modified)
bool graphChanged = addedNodes_.size()>0; // If the new map doesn't have any node from the previous map bool graphChanged = addedNodes_.size()>0; // If the new map doesn't have any node from the previous map
float updateErrorSqrd = updateError_*updateError_; float updateErrorSqrd = updateError_*updateError_;
for(std::map<int, Transform>::iterator iter=addedNodes_.begin(); iter!=addedNodes_.end(); ++iter) for(std::map<int, Transform>::const_iterator iter=addedNodes_.begin(); iter!=addedNodes_.end(); ++iter)
{ {
std::map<int, Transform>::const_iterator jter = poses.find(iter->first); std::map<int, Transform>::const_iterator jter = poses.find(iter->first);
if(jter != poses.end()) if(jter != poses.end())
@@ -125,7 +122,15 @@ bool GlobalMap::update(const std::map<int, Transform> & poses)
} }
} }
if(graphOptimized || graphChanged) return graphOptimized || graphChanged;
}
bool GlobalMap::update(const std::map<int, Transform> & poses)
{
UDEBUG("Update (poses=%d addedNodes_=%d)", (int)poses.size(), (int)addedNodes_.size());
// First, check of the graph has changed. If so, re-create the octree by moving all occupied nodes.
if(fullUpdateNeeded(poses))
{ {
// clear all but keep cache // clear all but keep cache
clear(); clear();
@@ -134,14 +139,25 @@ bool GlobalMap::update(const std::map<int, Transform> & poses)
std::list<std::pair<int, Transform> > orderedPoses; std::list<std::pair<int, Transform> > orderedPoses;
// add old poses that were not in the current map (they were just retrieved from LTM) // add old poses that were not in the current map (they were just retrieved from LTM)
int nodesNotAssembled = 0;
int nodesNotInCache = 0;
for(std::map<int, Transform>::const_iterator iter=poses.lower_bound(1); iter!=poses.end(); ++iter) for(std::map<int, Transform>::const_iterator iter=poses.lower_bound(1); iter!=poses.end(); ++iter)
{ {
if(!isNodeAssembled(iter->first)) if(!isNodeAssembled(iter->first))
{ {
UDEBUG("Pose %d not found in current added poses, it will be added to map", iter->first); if(uContains(cache(), iter->first))
orderedPoses.push_back(*iter); {
++nodesNotAssembled;
//UDEBUG("Pose %d not found in current added poses, it will be added to map", iter->first);
orderedPoses.push_back(*iter);
}
else
{
++nodesNotInCache;
}
} }
} }
UDEBUG("%d nodes will be assembled in the map and %d nodes won't (no local grids in cache for them)", nodesNotAssembled, nodesNotInCache);
// insert zero after // insert zero after
if(poses.find(0) != poses.end()) if(poses.find(0) != poses.end())
+21 -4
View File
@@ -196,7 +196,7 @@ bool exportPoses(
bool importPoses( bool importPoses(
const std::string & filePath, const std::string & filePath,
int format, // 0=Raw, 1=RGBD-SLAM motion capture (10=without change of coordinate frame, 11=10+ID), 2=KITTI, 3=TORO, 4=g2o, 5=NewCollege(t,x,y), 6=Malaga Urban GPS, 7=St Lucia INS, 8=Karlsruhe, 9=EuRoC MAV int format, // 0=Raw, 1=RGBD-SLAM motion capture (10=without change of coordinate frame, 11=10+ID), 2=KITTI, 3=TORO, 4=g2o, 5=NewCollege(t,x,y), 6=Malaga Urban GPS, 7=St Lucia INS, 8=Karlsruhe, 9=EuRoC MAV, 12=rgbd_bonn
std::map<int, Transform> & poses, std::map<int, Transform> & poses,
std::multimap<int, Link> * constraints, // optional for formats 3 and 4 std::multimap<int, Link> * constraints, // optional for formats 3 and 4
std::map<int, double> * stamps) // optional for format 1 and 9 std::map<int, double> * stamps) // optional for format 1 and 9
@@ -440,7 +440,7 @@ bool importPoses(
UERROR("Error parsing \"%s\" with NewCollege format (should have 3 values: stamp x y, found %d)", str.c_str(), (int)strList.size()); UERROR("Error parsing \"%s\" with NewCollege format (should have 3 values: stamp x y, found %d)", str.c_str(), (int)strList.size());
} }
} }
else if(format == 1 || format==10 || format==11) // rgbd-slam format else if(format == 1 || format==10 || format==11 || format==12) // rgbd-slam format
{ {
std::list<std::string> strList = uSplit(str); std::list<std::string> strList = uSplit(str);
if((strList.size() >= 8 && format!=11) || (strList.size() == 9 && format==11)) if((strList.size() >= 8 && format!=11) || (strList.size() == 9 && format==11))
@@ -451,9 +451,12 @@ bool importPoses(
} }
double stamp = uStr2Double(strList.front()); double stamp = uStr2Double(strList.front());
strList.pop_front(); strList.pop_front();
if(format==11) if(strList.size() == 8 && (format==10 || format==11 || format==12))
{ {
id = uStr2Int(strList.back()); if(format==11)
{
id = uStr2Int(strList.back());
}
strList.pop_back(); strList.pop_back();
} }
str = uJoin(strList, " "); str = uJoin(strList, " ");
@@ -481,6 +484,20 @@ bool importPoses(
1, 0, 0, 0); 1, 0, 0, 0);
pose = t*pose; pose = t*pose;
} }
else if(format == 12)
{
// See https://www.ipb.uni-bonn.de/data/rgbd-dynamic-dataset/index.html
Transform T_ros(-1, 0, 0, 0,
0, 0, 1, 0,
0, 1, 0, 0);
Transform T_m(
1.0157, 0.1828, -0.2389, 0.0113,
0.0009, -0.8431, -0.6413, -0.00980,
-0.3009, 0.6147, -0.8085, 0.0111);
// we remove the optical rotation
pose = T_ros*pose*T_ros*T_m*CameraModel::opticalRotation().inverse();
}
poses.insert(std::make_pair(id, pose)); poses.insert(std::make_pair(id, pose));
} }
} }
+2 -2
View File
@@ -64,8 +64,8 @@ void LocalGridCache::add(int nodeId,
void LocalGridCache::add(int nodeId, const LocalGrid & localGrid) void LocalGridCache::add(int nodeId, const LocalGrid & localGrid)
{ {
UDEBUG("nodeId=%d (ground=%d/%d obstacles=%d/%d empty=%d/%d)", //UDEBUG("nodeId=%d (ground=%d/%d obstacles=%d/%d empty=%d/%d)",
nodeId, localGrid.groundCells.cols, localGrid.groundCells.channels(), localGrid.obstacleCells.cols, localGrid.obstacleCells.channels(), localGrid.emptyCells.cols, localGrid.emptyCells.channels()); // nodeId, localGrid.groundCells.cols, localGrid.groundCells.channels(), localGrid.obstacleCells.cols, localGrid.obstacleCells.channels(), localGrid.emptyCells.cols, localGrid.emptyCells.channels());
if(nodeId < 0) if(nodeId < 0)
{ {
UWARN("Cannot add nodes with negative id (nodeId=%d)", nodeId); UWARN("Cannot add nodes with negative id (nodeId=%d)", nodeId);
+169 -56
View File
@@ -76,6 +76,7 @@ Memory::Memory(const ParametersMap & parameters) :
_similarityThreshold(Parameters::defaultMemRehearsalSimilarity()), _similarityThreshold(Parameters::defaultMemRehearsalSimilarity()),
_binDataKept(Parameters::defaultMemBinDataKept()), _binDataKept(Parameters::defaultMemBinDataKept()),
_rawDescriptorsKept(Parameters::defaultMemRawDescriptorsKept()), _rawDescriptorsKept(Parameters::defaultMemRawDescriptorsKept()),
_loadVisualLocalFeaturesOnInit(Parameters::defaultMemLoadVisualLocalFeaturesOnInit()),
_saveDepth16Format(Parameters::defaultMemSaveDepth16Format()), _saveDepth16Format(Parameters::defaultMemSaveDepth16Format()),
_notLinkedNodesKeptInDb(Parameters::defaultMemNotLinkedNodesKept()), _notLinkedNodesKeptInDb(Parameters::defaultMemNotLinkedNodesKept()),
_saveIntermediateNodeData(Parameters::defaultMemIntermediateNodeDataKept()), _saveIntermediateNodeData(Parameters::defaultMemIntermediateNodeDataKept()),
@@ -83,6 +84,7 @@ Memory::Memory(const ParametersMap & parameters) :
_depthCompressionFormat(Parameters::defaultMemDepthCompressionFormat()), _depthCompressionFormat(Parameters::defaultMemDepthCompressionFormat()),
_incrementalMemory(Parameters::defaultMemIncrementalMemory()), _incrementalMemory(Parameters::defaultMemIncrementalMemory()),
_localizationDataSaved(Parameters::defaultMemLocalizationDataSaved()), _localizationDataSaved(Parameters::defaultMemLocalizationDataSaved()),
_flannIndexSaved(Parameters::defaultKpFlannIndexSaved()),
_reduceGraph(Parameters::defaultMemReduceGraph()), _reduceGraph(Parameters::defaultMemReduceGraph()),
_maxStMemSize(Parameters::defaultMemSTMSize()), _maxStMemSize(Parameters::defaultMemSTMSize()),
_recentWmRatio(Parameters::defaultMemRecentWmRatio()), _recentWmRatio(Parameters::defaultMemRecentWmRatio()),
@@ -129,6 +131,7 @@ Memory::Memory(const ParametersMap & parameters) :
_linksChanged(false), _linksChanged(false),
_signaturesAdded(0), _signaturesAdded(0),
_allNodesInWM(true), _allNodesInWM(true),
_receivingOdometryFeatures(false),
_badSignRatio(Parameters::defaultKpBadSignRatio()), _badSignRatio(Parameters::defaultKpBadSignRatio()),
_tfIdfLikelihoodUsed(Parameters::defaultKpTfIdfLikelihoodUsed()), _tfIdfLikelihoodUsed(Parameters::defaultKpTfIdfLikelihoodUsed()),
_parallelized(Parameters::defaultKpParallelized()), _parallelized(Parameters::defaultKpParallelized()),
@@ -245,13 +248,13 @@ void Memory::loadDataFromDb(bool postInitClosingEvents)
if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(std::string("Loading all nodes to WM..."))); if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(std::string("Loading all nodes to WM...")));
std::set<int> ids; std::set<int> ids;
_dbDriver->getAllNodeIds(ids, true); _dbDriver->getAllNodeIds(ids, true);
_dbDriver->loadSignatures(std::list<int>(ids.begin(), ids.end()), dbSignatures); _dbDriver->loadSignatures(std::list<int>(ids.begin(), ids.end()), dbSignatures, 0, !_loadVisualLocalFeaturesOnInit);
} }
else else
{ {
// load previous session working memory // load previous session working memory
if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(std::string("Loading last nodes to WM..."))); if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(std::string("Loading last nodes to WM...")));
_dbDriver->loadLastNodes(dbSignatures); _dbDriver->loadLastNodes(dbSignatures, !_loadVisualLocalFeaturesOnInit);
} }
for(std::list<Signature*>::reverse_iterator iter=dbSignatures.rbegin(); iter!=dbSignatures.rend(); ++iter) for(std::list<Signature*>::reverse_iterator iter=dbSignatures.rbegin(); iter!=dbSignatures.rend(); ++iter)
{ {
@@ -417,20 +420,22 @@ void Memory::loadDataFromDb(bool postInitClosingEvents)
} }
else else
{ {
_dbDriver->load(_vwd, false); _dbDriver->load(*_vwd, false);
} }
} }
else else
{ {
UDEBUG("load words"); UDEBUG("load words");
// load the last dictionary // load the last dictionary
_dbDriver->load(_vwd, _vwd->isIncremental()); _dbDriver->load(*_vwd, _vwd->isIncremental());
} }
UDEBUG("%d words loaded!", _vwd->getUnusedWordsSize()); UDEBUG("%d words loaded!", _vwd->getUnusedWordsSize());
_vwd->update(); _vwd->update();
if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(uFormat("Loading dictionary, done! (%d words)", (int)_vwd->getUnusedWordsSize()))); if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(uFormat("Loading dictionary, done! (%d words)", (int)_vwd->getUnusedWordsSize())));
if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(std::string("Adding word references..."))); if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(std::string("Adding word references...")));
UDEBUG("Adding word references...");
UTimer timer;
// Enable loaded signatures // Enable loaded signatures
const std::map<int, Signature *> & signatures = this->getSignatures(); const std::map<int, Signature *> & signatures = this->getSignatures();
for(std::map<int, Signature *>::const_iterator i=signatures.begin(); i!=signatures.end(); ++i) for(std::map<int, Signature *>::const_iterator i=signatures.begin(); i!=signatures.end(); ++i)
@@ -441,7 +446,7 @@ void Memory::loadDataFromDb(bool postInitClosingEvents)
const std::multimap<int, int> & words = s->getWords(); const std::multimap<int, int> & words = s->getWords();
if(words.size()) if(words.size())
{ {
UDEBUG("node=%d, word references=%d", s->id(), words.size()); //UDEBUG("node=%d, word references=%d", s->id(), words.size());
for(std::multimap<int, int>::const_iterator iter = words.begin(); iter!=words.end(); ++iter) for(std::multimap<int, int>::const_iterator iter = words.begin(); iter!=words.end(); ++iter)
{ {
if(iter->first > 0) if(iter->first > 0)
@@ -458,7 +463,7 @@ void Memory::loadDataFromDb(bool postInitClosingEvents)
{ {
UWARN("_vwd->getUnusedWordsSize() must be empty... size=%d", _vwd->getUnusedWordsSize()); UWARN("_vwd->getUnusedWordsSize() must be empty... size=%d", _vwd->getUnusedWordsSize());
} }
UDEBUG("Total word references added = %d", _vwd->getTotalActiveReferences()); UDEBUG("Total word references added = %d (in %f s)", _vwd->getTotalActiveReferences(), timer.ticks());
if(_lastSignature == 0) if(_lastSignature == 0)
{ {
@@ -482,6 +487,37 @@ void Memory::loadDataFromDb(bool postInitClosingEvents)
UDEBUG("map ids start with %d", _idMapCount); UDEBUG("map ids start with %d", _idMapCount);
} }
void Memory::saveFlannIndex(bool postInitClosingEvents)
{
if(!_dbDriver) {
return;
}
if(uStrNumCmp(_dbDriver->getDatabaseVersion(), "0.23.0") >= 0) {
if(_flannIndexSaved && !_incrementalMemory) {
if(_vwd->isModified()) {
UINFO("Saving flann index to database... (%s=true)", Parameters::kKpFlannIndexSaved().c_str());
if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Saving flann index to database..."));
_dbDriver->saveFlannIndex(_vwd->serializeIndex());
if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Saving flann index to database, done!"));
}
else
{
UDEBUG("The dictionary didn't change since loaded, do not need to save again to database.");
}
}
else {
// clear if exists
_dbDriver->saveFlannIndex(std::vector<unsigned char>());
}
}
else if(_flannIndexSaved)
{
UWARN("Parameter %s is enabled, but database version is too old (%s < 0.23). Flann index cannot be saved.",
Parameters::kKpFlannIndexSaved().c_str(),
_dbDriver->getDatabaseVersion().c_str());
}
}
void Memory::close(bool databaseSaved, bool postInitClosingEvents, const std::string & ouputDatabasePath) void Memory::close(bool databaseSaved, bool postInitClosingEvents, const std::string & ouputDatabasePath)
{ {
UINFO("databaseSaved=%d, postInitClosingEvents=%d", databaseSaved?1:0, postInitClosingEvents?1:0); UINFO("databaseSaved=%d, postInitClosingEvents=%d", databaseSaved?1:0, postInitClosingEvents?1:0);
@@ -493,6 +529,8 @@ void Memory::close(bool databaseSaved, bool postInitClosingEvents, const std::st
databaseNameChanged = ouputDatabasePath.size() && _dbDriver->getUrl().size() && _dbDriver->getUrl().compare(ouputDatabasePath) != 0?true:false; databaseNameChanged = ouputDatabasePath.size() && _dbDriver->getUrl().size() && _dbDriver->getUrl().compare(ouputDatabasePath) != 0?true:false;
} }
UDEBUG("_memoryChanged=%d _linksChanged=%d databaseNameChanged=%d", _memoryChanged?1:0, _linksChanged?1:0, databaseNameChanged?1:0);
if(!databaseSaved || (!_memoryChanged && !_linksChanged && !databaseNameChanged)) if(!databaseSaved || (!_memoryChanged && !_linksChanged && !databaseNameChanged))
{ {
if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(uFormat("No changes added to database."))); if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(uFormat("No changes added to database.")));
@@ -500,6 +538,7 @@ void Memory::close(bool databaseSaved, bool postInitClosingEvents, const std::st
UINFO("No changes added to database."); UINFO("No changes added to database.");
if(_dbDriver) if(_dbDriver)
{ {
saveFlannIndex(postInitClosingEvents);
if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(uFormat("Closing database \"%s\"...", _dbDriver->getUrl().c_str()))); if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit(uFormat("Closing database \"%s\"...", _dbDriver->getUrl().c_str())));
_dbDriver->closeConnection(false, ouputDatabasePath); _dbDriver->closeConnection(false, ouputDatabasePath);
delete _dbDriver; delete _dbDriver;
@@ -514,11 +553,15 @@ void Memory::close(bool databaseSaved, bool postInitClosingEvents, const std::st
{ {
UINFO("Saving memory..."); UINFO("Saving memory...");
if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Saving memory...")); if(postInitClosingEvents) UEventsManager::post(new RtabmapEventInit("Saving memory..."));
if(!_memoryChanged && _linksChanged && _dbDriver) if(!_memoryChanged && _dbDriver)
{ {
// don't update the time stamps! saveFlannIndex(postInitClosingEvents);
UDEBUG("");
_dbDriver->setTimestampUpdateEnabled(false); if(_linksChanged) {
// don't update the time stamps!
UDEBUG("");
_dbDriver->setTimestampUpdateEnabled(false);
}
} }
this->clear(); this->clear();
if(_dbDriver) if(_dbDriver)
@@ -565,6 +608,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
Parameters::parse(params, Parameters::kMemBinDataKept(), _binDataKept); Parameters::parse(params, Parameters::kMemBinDataKept(), _binDataKept);
Parameters::parse(params, Parameters::kMemRawDescriptorsKept(), _rawDescriptorsKept); Parameters::parse(params, Parameters::kMemRawDescriptorsKept(), _rawDescriptorsKept);
Parameters::parse(params, Parameters::kMemLoadVisualLocalFeaturesOnInit(), _loadVisualLocalFeaturesOnInit);
Parameters::parse(params, Parameters::kMemSaveDepth16Format(), _saveDepth16Format); Parameters::parse(params, Parameters::kMemSaveDepth16Format(), _saveDepth16Format);
Parameters::parse(params, Parameters::kMemReduceGraph(), _reduceGraph); Parameters::parse(params, Parameters::kMemReduceGraph(), _reduceGraph);
Parameters::parse(params, Parameters::kMemNotLinkedNodesKept(), _notLinkedNodesKeptInDb); Parameters::parse(params, Parameters::kMemNotLinkedNodesKept(), _notLinkedNodesKeptInDb);
@@ -618,6 +662,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
Parameters::parse(params, Parameters::kMarkerVarianceAngular(), _markerAngVariance); Parameters::parse(params, Parameters::kMarkerVarianceAngular(), _markerAngVariance);
Parameters::parse(params, Parameters::kMarkerVarianceOrientationIgnored(), _markerOrientationIgnored); Parameters::parse(params, Parameters::kMarkerVarianceOrientationIgnored(), _markerOrientationIgnored);
Parameters::parse(params, Parameters::kMemLocalizationDataSaved(), _localizationDataSaved); Parameters::parse(params, Parameters::kMemLocalizationDataSaved(), _localizationDataSaved);
Parameters::parse(params, Parameters::kKpFlannIndexSaved(), _flannIndexSaved);
if(_markerAngVariance>=9999) if(_markerAngVariance>=9999)
{ {
@@ -654,10 +699,7 @@ void Memory::parseParameters(const ParametersMap & parameters)
} }
// Keypoint stuff // Keypoint stuff
if(_vwd) _vwd->parseParameters(params);
{
_vwd->parseParameters(params);
}
Parameters::parse(params, Parameters::kKpTfIdfLikelihoodUsed(), _tfIdfLikelihoodUsed); Parameters::parse(params, Parameters::kKpTfIdfLikelihoodUsed(), _tfIdfLikelihoodUsed);
Parameters::parse(params, Parameters::kKpParallelized(), _parallelized); Parameters::parse(params, Parameters::kKpParallelized(), _parallelized);
@@ -856,7 +898,7 @@ void Memory::preUpdate()
{ {
this->cleanUnusedWords(); this->cleanUnusedWords();
} }
if(_vwd && !_parallelized) if(!_parallelized)
{ {
//When parallelized, it is done in CreateSignature //When parallelized, it is done in CreateSignature
_vwd->update(); _vwd->update();
@@ -1114,10 +1156,7 @@ void Memory::addSignatureToStm(Signature * signature, const cv::Mat & covariance
} }
++_signaturesAdded; ++_signaturesAdded;
if(_vwd) UDEBUG("%d words ref for the signature %d (weight=%d)", signature->getWords().size(), signature->id(), signature->getWeight());
{
UDEBUG("%d words ref for the signature %d (weight=%d)", signature->getWords().size(), signature->id(), signature->getWeight());
}
if(signature->getWords().size()) if(signature->getWords().size())
{ {
signature->setEnabled(true); signature->setEnabled(true);
@@ -1224,16 +1263,17 @@ void Memory::moveSignatureToWMFromSTM(int id, int * reducedTo)
std::multimap<int, Link> linksCopy = links; std::multimap<int, Link> linksCopy = links;
for(std::multimap<int, Link>::iterator iter=linksCopy.begin(); iter!=linksCopy.end(); ++iter) for(std::multimap<int, Link>::iterator iter=linksCopy.begin(); iter!=linksCopy.end(); ++iter)
{ {
if(iter->second.type() == Link::kNeighbor || if(iter->second.type() == Link::kNeighborMerged)
iter->second.type() == Link::kNeighborMerged)
{ {
// Removing only merged neighbor links, we keep original neighbor
// links to be able to reprocess databases with correct odometry covariance.
s->removeLink(iter->first); s->removeLink(iter->first);
if(iter->second.type() == Link::kNeighbor) }
if(iter->second.type() == Link::kNeighbor)
{
if(_lastGlobalLoopClosureId == s->id())
{ {
if(_lastGlobalLoopClosureId == s->id()) _lastGlobalLoopClosureId = iter->first;
{
_lastGlobalLoopClosureId = iter->first;
}
} }
} }
} }
@@ -1836,13 +1876,24 @@ void Memory::clear()
UDEBUG(""); UDEBUG("");
//Get the tree root (parents) //Get the tree root (parents)
std::map<int, Signature*> mem = _signatures; if(!_dbDriver) {
for(std::map<int, Signature *>::iterator i=mem.begin(); i!=mem.end(); ++i) // We are not saving to database anyway, just delete now.
{ for(std::map<int, Signature *>::iterator iter=_signatures.begin(); iter!=_signatures.end(); ++iter)
if(i->second)
{ {
UDEBUG("deleting from the working and the short-term memory: %d", i->first); delete iter->second;
this->moveToTrash(i->second); }
_workingMem.clear();
_signatures.clear();
}
else {
std::map<int, Signature*> mem = _signatures;
for(std::map<int, Signature *>::iterator i=mem.begin(); i!=mem.end(); ++i)
{
if(i->second)
{
//UDEBUG("deleting from the working and the short-term memory: %d", i->first);
this->moveToTrash(i->second);
}
} }
} }
@@ -1866,6 +1917,7 @@ void Memory::clear()
UDEBUG(""); UDEBUG("");
_lastSignature = 0; _lastSignature = 0;
_lastGlobalLoopClosureId = 0; _lastGlobalLoopClosureId = 0;
_signaturesAdded = 0;
_idCount = kIdStart; _idCount = kIdStart;
_idMapCount = kIdStart; _idMapCount = kIdStart;
_memoryChanged = false; _memoryChanged = false;
@@ -1879,6 +1931,7 @@ void Memory::clear()
_landmarksIndex.clear(); _landmarksIndex.clear();
_landmarksSize.clear(); _landmarksSize.clear();
_allNodesInWM = true; _allNodesInWM = true;
_receivingOdometryFeatures = false;
if(_dbDriver) if(_dbDriver)
{ {
@@ -1886,14 +1939,7 @@ void Memory::clear()
cleanUnusedWords(); cleanUnusedWords();
_dbDriver->emptyTrashes(); _dbDriver->emptyTrashes();
} }
else _vwd->clear(_dbDriver!=NULL);
{
cleanUnusedWords();
}
if(_vwd)
{
_vwd->clear();
}
UDEBUG(""); UDEBUG("");
} }
@@ -2428,7 +2474,7 @@ std::list<Signature *> Memory::getRemovableSignatures(int count, const std::set<
*/ */
void Memory::moveToTrash(Signature * s, bool keepLinkedToGraph, std::list<int> * deletedWords) void Memory::moveToTrash(Signature * s, bool keepLinkedToGraph, std::list<int> * deletedWords)
{ {
UDEBUG("id=%d", s?s->id():0); //UDEBUG("id=%d", s?s->id():0);
if(s) if(s)
{ {
// Cleanup landmark indexes // Cleanup landmark indexes
@@ -2921,6 +2967,37 @@ Transform Memory::computeTransform(
_registrationPipeline->isScanRequired()?&laserBuf:0, _registrationPipeline->isScanRequired()?&laserBuf:0,
_registrationPipeline->isUserDataRequired()?&userBuf:0); _registrationPipeline->isUserDataRequired()?&userBuf:0);
// Load word descriptors and keypoints on-demand if necessary
if( !_reextractLoopClosureFeatures &&
(_registrationPipeline->isImageRequired() || guess.isNull()) &&
!fromS.getWords().empty() && fromS.getWordsKpts().empty() &&
_dbDriver)
{
// We assume "toS" has already features in RAM, so just lookup "fromS"
UDEBUG("Loading local visual features for signature %d", fromS.id());
std::multimap<int, int> words;
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::Point3f> points;
cv::Mat descriptors;
UTimer timer;
_dbDriver->getLocalFeatures(fromS.id(), words, keypoints, points, descriptors);
if(!words.empty() && !keypoints.empty()) {
UASSERT(words.size() == fromS.getWords().size());
std::map<int, int> wordsChanged = fromS.getWordsChanged();
bool wasEnabled = fromS.isEnabled();
fromS.setWords(words, keypoints, points, descriptors);
for(const auto & iter: wordsChanged) {
fromS.changeWordsRef(iter.first, iter.second);
}
fromS.setEnabled(wasEnabled);
UDEBUG("Loaded %ld local visual features for signature %d! (in %f s)", words.size(), fromS.id(), timer.ticks());
}
else
{
UDEBUG("Failed to load local visual features for signature %d.", fromS.id());
}
}
// compute transform fromId -> toId // compute transform fromId -> toId
std::vector<int> inliersV; std::vector<int> inliersV;
@@ -2980,8 +3057,10 @@ Transform Memory::computeTransform(
!_invertedReg && !_invertedReg &&
!tmpTo.getWordsDescriptors().empty() && !tmpTo.getWordsDescriptors().empty() &&
!tmpTo.getWords().empty() && !tmpTo.getWords().empty() &&
!tmpTo.getWordsKpts().empty() &&
!tmpFrom.getWordsDescriptors().empty() && !tmpFrom.getWordsDescriptors().empty() &&
!tmpFrom.getWords().empty() && !tmpFrom.getWords().empty() &&
!tmpFrom.getWordsKpts().empty() &&
!tmpFrom.getWords3().empty() && !tmpFrom.getWords3().empty() &&
fromS.hasLink(0, Link::kNeighbor)) // If doesn't have neighbors, skip bundle fromS.hasLink(0, Link::kNeighbor)) // If doesn't have neighbors, skip bundle
{ {
@@ -3015,8 +3094,12 @@ Transform Memory::computeTransform(
if(id != fromS.id() && iter->second.type() == Link::kNeighbor) // assemble only neighbors for the local feature map if(id != fromS.id() && iter->second.type() == Link::kNeighbor) // assemble only neighbors for the local feature map
{ {
const Signature * s = this->getSignature(id); const Signature * s = this->getSignature(id);
if(s && !s->getWords3().empty()) if(s)
{ {
if(s->getWordsKpts().empty() && s->getWords3().empty() && s->getWordsDescriptors().empty()) {
UDEBUG("Signature %d doesn't have features set. Cannot be added in the local feature map.", s->id());
continue;
}
const std::map<int, int> & wordsTo = uMultimapToMapUnique(s->getWords()); const std::map<int, int> & wordsTo = uMultimapToMapUnique(s->getWords());
for(std::map<int, int>::const_iterator jter=wordsTo.begin(); jter!=wordsTo.end(); ++jter) for(std::map<int, int>::const_iterator jter=wordsTo.begin(); jter!=wordsTo.end(); ++jter)
{ {
@@ -3115,6 +3198,11 @@ Transform Memory::computeTransform(
bundlePoses.insert(std::make_pair(id, iter->second.transform())); bundlePoses.insert(std::make_pair(id, iter->second.transform()));
} }
if(s->getWordsKpts().empty())
{
UDEBUG("Signature %d doesn't have features set. Keypoints won't be added in local bundle adjustment.", s->id());
continue;
}
const std::map<int,int> & words = uMultimapToMapUnique(s->getWords()); const std::map<int,int> & words = uMultimapToMapUnique(s->getWords());
for(std::map<int, int>::const_iterator jter=words.begin(); jter!=words.end(); ++jter) for(std::map<int, int>::const_iterator jter=words.begin(); jter!=words.end(); ++jter)
{ {
@@ -3590,7 +3678,7 @@ void Memory::removeAllVirtualLinks()
void Memory::removeVirtualLinks(int signatureId) void Memory::removeVirtualLinks(int signatureId)
{ {
UDEBUG(""); //UDEBUG("");
Signature * s = this->_getSignature(signatureId); Signature * s = this->_getSignature(signatureId);
if(s) if(s)
{ {
@@ -3629,10 +3717,7 @@ void Memory::dumpMemory(std::string directory) const
void Memory::dumpDictionary(const char * fileNameRef, const char * fileNameDesc) const void Memory::dumpDictionary(const char * fileNameRef, const char * fileNameDesc) const
{ {
if(_vwd) _vwd->exportDictionary(fileNameRef, fileNameDesc);
{
_vwd->exportDictionary(fileNameRef, fileNameDesc);
}
} }
void Memory::dumpSignatures(const char * fileNameSign, bool words3D) const void Memory::dumpSignatures(const char * fileNameSign, bool words3D) const
@@ -3753,10 +3838,7 @@ unsigned long Memory::getMemoryUsed() const
{ {
memoryUsage += iter->second->getMemoryUsed(true); memoryUsage += iter->second->getMemoryUsed(true);
} }
if(_vwd) memoryUsage += _vwd->getMemoryUsed();
{
memoryUsage += _vwd->getMemoryUsed();
}
memoryUsage += _stMem.size() * (sizeof(int)+sizeof(std::set<int>::iterator)) + sizeof(std::set<int>); memoryUsage += _stMem.size() * (sizeof(int)+sizeof(std::set<int>::iterator)) + sizeof(std::set<int>);
memoryUsage += _workingMem.size() * (sizeof(int)+sizeof(double)+sizeof(std::map<int, double>::iterator)) + sizeof(std::map<int, double>); memoryUsage += _workingMem.size() * (sizeof(int)+sizeof(double)+sizeof(std::map<int, double>::iterator)) + sizeof(std::map<int, double>);
memoryUsage += _groundTruths.size() * (sizeof(int)+sizeof(Transform)+12*sizeof(float) + sizeof(std::map<int, Transform>::iterator)) + sizeof(std::map<int, Transform>); memoryUsage += _groundTruths.size() * (sizeof(int)+sizeof(Transform)+12*sizeof(float) + sizeof(std::map<int, Transform>::iterator)) + sizeof(std::map<int, Transform>);
@@ -4194,6 +4276,29 @@ void Memory::getNodeWordsAndGlobalDescriptors(int nodeId,
words3 = s->getWords3(); words3 = s->getWords3();
wordsDescriptors = s->getWordsDescriptors(); wordsDescriptors = s->getWordsDescriptors();
globalDescriptors = s->sensorData().globalDescriptors(); globalDescriptors = s->sensorData().globalDescriptors();
if(!words.empty() && wordsKpts.empty() && _dbDriver)
{
std::multimap<int, int> tmpWords;
_dbDriver->getLocalFeatures(nodeId, tmpWords, wordsKpts, words3, wordsDescriptors);
if(!tmpWords.empty() && !wordsKpts.empty())
{
UASSERT(tmpWords.size() == words.size());
std::map<int, int> wordsChanged = s->getWordsChanged();
for(const auto & iter: wordsChanged) {
std::list<int> subwords = uValues(tmpWords, iter.first); // old id
if(subwords.size())
{
tmpWords.erase(iter.first);
for(std::list<int>::const_iterator jter=subwords.begin(); jter!=subwords.end(); ++jter)
{
tmpWords.insert(std::pair<int, int>(iter.second, (*jter))); // new id
}
}
}
words = tmpWords;
}
}
} }
else if(_dbDriver) else if(_dbDriver)
{ {
@@ -4765,6 +4870,11 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
{ {
meanWordsPerLocation = _vwd->getTotalActiveReferences() / (treeSize-1); // ignore virtual signature meanWordsPerLocation = _vwd->getTotalActiveReferences() / (treeSize-1); // ignore virtual signature
} }
else if(_useOdometryFeatures) {
// To not detect first image as bad signature if odometry
// is using less features than feature2D->getMaxFeatures()
meanWordsPerLocation = 0;
}
if(_parallelized && !isIntermediateNode) if(_parallelized && !isIntermediateNode)
{ {
@@ -4868,10 +4978,12 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
SensorData decimatedData; SensorData decimatedData;
UDEBUG("Received kpts=%d kpts3D=%d, descriptors=%d _useOdometryFeatures=%s", UDEBUG("Received kpts=%d kpts3D=%d, descriptors=%d _useOdometryFeatures=%s",
(int)data.keypoints().size(), (int)data.keypoints3D().size(), data.descriptors().rows, _useOdometryFeatures?"true":"false"); (int)data.keypoints().size(), (int)data.keypoints3D().size(), data.descriptors().rows, _useOdometryFeatures?"true":"false");
// TODO: do we still need the third and fouth comparisons?
// TODO: there is significant repetitive code between the if and the else, could we combine them?!
if(!_useOdometryFeatures || if(!_useOdometryFeatures ||
data.keypoints().empty() || (!_receivingOdometryFeatures && data.keypoints().empty()) ||
(int)data.keypoints().size() != data.descriptors().rows || (int)data.keypoints().size() != data.descriptors().rows ||
(_feature2D->getType() == Feature2D::kFeatureOrbOctree && data.descriptors().empty())) (!_receivingOdometryFeatures && _feature2D->getType() == Feature2D::kFeatureOrbOctree && data.descriptors().empty()))
{ {
if(_feature2D->getMaxFeatures() >= 0 && !data.imageRaw().empty() && !isIntermediateNode) if(_feature2D->getMaxFeatures() >= 0 && !data.imageRaw().empty() && !isIntermediateNode)
{ {
@@ -5208,6 +5320,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
} }
else if(_feature2D->getMaxFeatures() >= 0 && !isIntermediateNode) else if(_feature2D->getMaxFeatures() >= 0 && !isIntermediateNode)
{ {
_receivingOdometryFeatures = true;
UINFO("Use odometry features: kpts=%d 3d=%d desc=%d (dim=%d, type=%d)", UINFO("Use odometry features: kpts=%d 3d=%d desc=%d (dim=%d, type=%d)",
(int)data.keypoints().size(), (int)data.keypoints().size(),
(int)data.keypoints3D().size(), (int)data.keypoints3D().size(),
@@ -6330,7 +6443,7 @@ Signature * Memory::createSignature(const SensorData & inputData, const Transfor
void Memory::disableWordsRef(int signatureId) void Memory::disableWordsRef(int signatureId)
{ {
UDEBUG("id=%d", signatureId); //UDEBUG("id=%d", signatureId);
Signature * ss = this->_getSignature(signatureId); Signature * ss = this->_getSignature(signatureId);
if(ss && ss->isEnabled()) if(ss && ss->isEnabled())
@@ -6346,7 +6459,7 @@ void Memory::disableWordsRef(int signatureId)
count -= _vwd->getTotalActiveReferences(); count -= _vwd->getTotalActiveReferences();
ss->setEnabled(false); ss->setEnabled(false);
UDEBUG("%d words total ref removed from signature %d... (total active ref = %d)", count, ss->id(), _vwd->getTotalActiveReferences()); //UDEBUG("%d words total ref removed from signature %d... (total active ref = %d)", count, ss->id(), _vwd->getTotalActiveReferences());
} }
} }
@@ -6409,7 +6522,7 @@ void Memory::enableWordsRef(const std::list<int> & signatureIds)
UDEBUG("oldWordIds.size()=%d, getOldIds time=%fs", oldWordIds.size(), timer.ticks()); UDEBUG("oldWordIds.size()=%d, getOldIds time=%fs", oldWordIds.size(), timer.ticks());
// the words were deleted, so try to math it with an active word // the words were deleted, so try to match it with an active word
std::list<VisualWord *> vws; std::list<VisualWord *> vws;
if(oldWordIds.size() && _dbDriver) if(oldWordIds.size() && _dbDriver)
{ {
+24 -7
View File
@@ -36,9 +36,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/odometry/OdometryLOAM.h" #include "rtabmap/core/odometry/OdometryLOAM.h"
#include "rtabmap/core/odometry/OdometryFLOAM.h" #include "rtabmap/core/odometry/OdometryFLOAM.h"
#include "rtabmap/core/odometry/OdometryMSCKF.h" #include "rtabmap/core/odometry/OdometryMSCKF.h"
#include "rtabmap/core/odometry/OdometryVINS.h" #include "rtabmap/core/odometry/OdometryVINSFusion.h"
#include "rtabmap/core/odometry/OdometryOpenVINS.h" #include "rtabmap/core/odometry/OdometryOpenVINS.h"
#include "rtabmap/core/odometry/OdometryOpen3D.h" #include "rtabmap/core/odometry/OdometryOpen3D.h"
#include "rtabmap/core/odometry/OdometryCuVSLAM.h"
#include "rtabmap/core/OdometryInfo.h" #include "rtabmap/core/OdometryInfo.h"
#include "rtabmap/core/util3d.h" #include "rtabmap/core/util3d.h"
#include "rtabmap/core/util3d_mapping.h" #include "rtabmap/core/util3d_mapping.h"
@@ -103,8 +104,8 @@ Odometry * Odometry::create(Odometry::Type & type, const ParametersMap & paramet
case Odometry::kTypeMSCKF: case Odometry::kTypeMSCKF:
odometry = new OdometryMSCKF(parameters); odometry = new OdometryMSCKF(parameters);
break; break;
case Odometry::kTypeVINS: case Odometry::kTypeVINSFusion:
odometry = new OdometryVINS(parameters); odometry = new OdometryVINSFusion(parameters);
break; break;
case Odometry::kTypeOpenVINS: case Odometry::kTypeOpenVINS:
odometry = new OdometryOpenVINS(parameters); odometry = new OdometryOpenVINS(parameters);
@@ -112,6 +113,9 @@ Odometry * Odometry::create(Odometry::Type & type, const ParametersMap & paramet
case Odometry::kTypeOpen3D: case Odometry::kTypeOpen3D:
odometry = new OdometryOpen3D(parameters); odometry = new OdometryOpen3D(parameters);
break; break;
case Odometry::kTypeCuVSLAM:
odometry = new OdometryCuVSLAM(parameters);
break;
default: default:
UERROR("Unknown odometry type %d, using F2M instead...", (int)type); UERROR("Unknown odometry type %d, using F2M instead...", (int)type);
odometry = new OdometryF2M(parameters); odometry = new OdometryF2M(parameters);
@@ -625,6 +629,7 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
if(!guessIn.isNull()) if(!guessIn.isNull())
{ {
guess = guessIn; guess = guessIn;
UDEBUG("Using provided guess %s", guessIn.prettyPrint().c_str());
} }
else if(!imus_.empty()) else if(!imus_.empty())
{ {
@@ -641,12 +646,16 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
{ {
guess = guess.to3DoF(); guess = guess.to3DoF();
} }
UDEBUG("Adjusting guess from motion with IMU %s", guess.prettyPrint().c_str());
} }
else if(!imuLastTransform_.isNull()) else if(!imuLastTransform_.isNull())
{ {
UWARN("Could not find imu transform at %f", data.stamp()); UWARN("Could not find imu transform at %f", data.stamp());
} }
} }
else if(!guess.isNull()) {
UDEBUG("Using guess from motion %s", guess.prettyPrint().c_str());
}
UTimer time; UTimer time;
@@ -1011,21 +1020,28 @@ Transform Odometry::process(SensorData & data, const Transform & guessIn, Odomet
--_resetCurrentCount; --_resetCurrentCount;
if(_resetCurrentCount == 0) if(_resetCurrentCount == 0)
{ {
UWARN("Odometry automatically reset to latest pose!"); if(!guess.isNull() && !guessIn.isNull()) {
this->reset(_pose); UWARN("Odometry automatically reset to latest pose (%s) + guess (%s)!", _pose.prettyPrint().c_str(), guess.prettyPrint().c_str());
this->reset(_pose * guess);
}
else {
UWARN("Odometry automatically reset to latest pose (%s)!", _pose.prettyPrint().c_str());
this->reset(_pose);
}
_resetCurrentCount = _resetCountdown; _resetCurrentCount = _resetCountdown;
if(info) if(info)
{ {
*info = OdometryInfo(); *info = OdometryInfo();
} }
return this->computeTransform(data, Transform(), info); this->computeTransform(data, Transform(), info);
return _pose;
} }
}
previousVelocities_.clear(); previousVelocities_.clear();
velocityGuess_.setNull(); velocityGuess_.setNull();
previousStamp_ = 0; previousStamp_ = 0;
}
return Transform(); return Transform();
} }
@@ -1055,6 +1071,7 @@ void Odometry::initKalmanFilter(const Transform & initialPose, float vx, float v
0, 0, 0, 0, 0, 0.17 } }; 0, 0, 0, 0, 0, 0.17 } };
static const boost::array<double, 36> STANDARD_TWIST_COVARIANCE = static const boost::array<double, 36> STANDARD_TWIST_COVARIANCE =
{ { 0.05, 0, 0, 0, 0, 0, { { 0.05, 0, 0, 0, 0, 0,
}
0, 0.05, 0, 0, 0, 0, 0, 0.05, 0, 0, 0, 0,
0, 0, 0.05, 0, 0, 0, 0, 0, 0.05, 0, 0, 0,
0, 0, 0, 0.09, 0, 0, 0, 0, 0, 0.09, 0, 0,
+4 -4
View File
@@ -126,10 +126,10 @@ std::map<std::string, float> OdometryInfo::statistics(const Transform & pose)
stats.insert(std::make_pair("Odometry/ICPStructuralComplexity/", reg.icpStructuralComplexity)); stats.insert(std::make_pair("Odometry/ICPStructuralComplexity/", reg.icpStructuralComplexity));
stats.insert(std::make_pair("Odometry/ICPStructuralDistribution/", reg.icpStructuralDistribution)); stats.insert(std::make_pair("Odometry/ICPStructuralDistribution/", reg.icpStructuralDistribution));
stats.insert(std::make_pair("Odometry/ICPCorrespondences/", reg.icpCorrespondences)); stats.insert(std::make_pair("Odometry/ICPCorrespondences/", reg.icpCorrespondences));
stats.insert(std::make_pair("Odometry/StdDevLin/", sqrt((float)reg.covariance.at<double>(0,0)))); stats.insert(std::make_pair("Odometry/StdDevLin/", reg.covariance.empty()?0:sqrt((float)reg.covariance.at<double>(0,0))));
stats.insert(std::make_pair("Odometry/StdDevAng/", sqrt((float)reg.covariance.at<double>(5,5)))); stats.insert(std::make_pair("Odometry/StdDevAng/", reg.covariance.empty()?0:sqrt((float)reg.covariance.at<double>(5,5))));
stats.insert(std::make_pair("Odometry/VarianceLin/", (float)reg.covariance.at<double>(0,0))); stats.insert(std::make_pair("Odometry/VarianceLin/", reg.covariance.empty()?0:(float)reg.covariance.at<double>(0,0)));
stats.insert(std::make_pair("Odometry/VarianceAng/", (float)reg.covariance.at<double>(5,5))); stats.insert(std::make_pair("Odometry/VarianceAng/", reg.covariance.empty()?0:(float)reg.covariance.at<double>(5,5)));
stats.insert(std::make_pair("Odometry/TimeEstimation/ms", timeEstimation*1000.0f)); stats.insert(std::make_pair("Odometry/TimeEstimation/ms", timeEstimation*1000.0f));
stats.insert(std::make_pair("Odometry/TimeFiltering/ms", timeParticleFiltering*1000.0f)); stats.insert(std::make_pair("Odometry/TimeFiltering/ms", timeParticleFiltering*1000.0f));
stats.insert(std::make_pair("Odometry/LocalMapSize/", localMapSize)); stats.insert(std::make_pair("Odometry/LocalMapSize/", localMapSize));
+50 -27
View File
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/core/OdometryInfo.h" #include "rtabmap/core/OdometryInfo.h"
#include "rtabmap/core/OdometryEvent.h" #include "rtabmap/core/OdometryEvent.h"
#include "rtabmap/utilite/ULogger.h" #include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UTimer.h"
namespace rtabmap { namespace rtabmap {
@@ -63,7 +64,7 @@ bool OdometryThread::handleEvent(UEvent * event)
SensorEvent * sensorEvent = (SensorEvent*)event; SensorEvent * sensorEvent = (SensorEvent*)event;
if(sensorEvent->getCode() == SensorEvent::kCodeData) if(sensorEvent->getCode() == SensorEvent::kCodeData)
{ {
this->addData(sensorEvent->data()); this->addData(*sensorEvent);
} }
} }
else if(event->getClassName().compare("IMUEvent") == 0) else if(event->getClassName().compare("IMUEvent") == 0)
@@ -112,31 +113,51 @@ void OdometryThread::mainLoop()
_imuBuffer.clear(); _imuBuffer.clear();
_oldestAsyncImuStamp = 0.0; _oldestAsyncImuStamp = 0.0;
_newestAsyncImuStamp = 0.0; _newestAsyncImuStamp = 0.0;
_previousGuessPose.setNull();
} }
SensorData data; SensorEvent event;
if(getData(data)) if(getData(event))
{ {
OdometryInfo info; OdometryInfo info;
UDEBUG("Processing data..."); UDEBUG("Processing data...");
Transform pose = _odometry->process(data, &info); Transform guess;
UDEBUG("event.info().odomPose=%s", event.info().odomPose.prettyPrint().c_str());
if(!_previousGuessPose.isNull() && !event.info().odomPose.isNull()) {
guess = _previousGuessPose.inverse() * event.info().odomPose;
}
SensorData data = event.data();
Transform pose = _odometry->process(data, guess , &info);
if(!data.imageRaw().empty() || !data.laserScanRaw().empty() || (pose.isNull() && data.imu().empty())) if(!data.imageRaw().empty() || !data.laserScanRaw().empty() || (pose.isNull() && data.imu().empty()))
{ {
UDEBUG("Odom pose = %s", pose.prettyPrint().c_str()); UDEBUG("Odom pose = %s", pose.prettyPrint().c_str());
if(!pose.isNull()) {
_previousGuessPose = event.info().odomPose;
UASSERT(event.info().odomPose.isNull() || !info.reg.covariance.empty());
if(!event.info().odomPose.isNull() && info.reg.covariance.at<double>(0,0) >= 9999 &&
(pose.x() != 0.0f || pose.y() != 0.0f || pose.z() != 0.0f)) // not the first frame
{
// In case of external guess and auto reset, keep reporting lost till we
// process the second frame with valid covariance. This way it
// won't trigger a new map.
pose = Transform();
}
}
// a null pose notify that odometry could not be computed // a null pose notify that odometry could not be computed
this->post(new OdometryEvent(data, pose, info)); this->post(new OdometryEvent(data, pose, info));
} }
} }
} }
void OdometryThread::addData(const SensorData & data) void OdometryThread::addData(const SensorEvent & event)
{ {
if(data.imu().empty()) if(event.data().imu().empty())
{ {
if(dynamic_cast<OdometryMono*>(_odometry) == 0) if(dynamic_cast<OdometryMono*>(_odometry) == 0)
{ {
if((data.imageRaw().empty() || data.depthOrRightRaw().empty() || (data.cameraModels().empty() && data.stereoCameraModels().empty())) && if((event.data().imageRaw().empty() || event.data().depthOrRightRaw().empty() || (event.data().cameraModels().empty() && event.data().stereoCameraModels().empty())) &&
data.laserScanRaw().empty()) event.data().laserScanRaw().empty())
{ {
ULOGGER_ERROR("Missing some information (images/scans empty or missing calibration)!?"); ULOGGER_ERROR("Missing some information (images/scans empty or missing calibration)!?");
return; return;
@@ -145,7 +166,7 @@ void OdometryThread::addData(const SensorData & data)
else else
{ {
// Mono can accept RGB only // Mono can accept RGB only
if(data.imageRaw().empty() || (data.cameraModels().empty() && data.stereoCameraModels().empty())) if(event.data().imageRaw().empty() || (event.data().cameraModels().empty() && event.data().stereoCameraModels().empty()))
{ {
ULOGGER_ERROR("Missing some information (image empty or missing calibration)!?"); ULOGGER_ERROR("Missing some information (image empty or missing calibration)!?");
return; return;
@@ -156,30 +177,32 @@ void OdometryThread::addData(const SensorData & data)
bool notify = true; bool notify = true;
_dataMutex.lock(); _dataMutex.lock();
{ {
if( !data.imageRaw().empty() || if( !event.data().imageRaw().empty() ||
!data.imageCompressed().empty() || !event.data().imageCompressed().empty() ||
!data.laserScanRaw().isEmpty() || !event.data().laserScanRaw().isEmpty() ||
!data.laserScanCompressed().empty() || !event.data().laserScanCompressed().empty() ||
data.imu().empty()) event.data().imu().empty())
{ {
if(_oldestAsyncImuStamp > 0.0 && data.stamp() < _oldestAsyncImuStamp) { if(_oldestAsyncImuStamp > 0.0 && event.data().stamp() < _oldestAsyncImuStamp) {
UWARN("Received image/lidar with stamp (%f) older than oldest received imu " UWARN("Received image/lidar with stamp (%f) older than oldest received imu "
"(%f), skipping that frame (imu buffer size=%ld). " "(%f), skipping that frame (imu buffer size=%ld). "
"When using async IMU, make sure IMU is published faster " "When using async IMU, make sure IMU is published faster "
"than camera/lidar (assuming IMU latency is very small compared to camera/lidar).", "than camera/lidar (assuming IMU latency is very small compared to camera/lidar)."
data.stamp(), _oldestAsyncImuStamp, _imuBuffer.size()); "Current camera/lidar delay is %fs.",
event.data().stamp(), _oldestAsyncImuStamp, _imuBuffer.size(), UTimer::now() - event.data().stamp());
notify = false; notify = false;
} }
else if(_newestAsyncImuStamp > 0.0 && data.stamp()>=_newestAsyncImuStamp) { else if(_newestAsyncImuStamp > 0.0 && event.data().stamp()>=_newestAsyncImuStamp) {
UWARN("Received image/lidar with stamp (%f) newer than latest received imu " UWARN("Received image/lidar with stamp (%f) newer than latest received imu "
"(%f), skipping that frame (imu buffer size=%ld). " "(%f), skipping that frame (imu buffer size=%ld). "
"When using async IMU, make sure IMU is published faster " "When using async IMU, make sure IMU is published faster "
"than camera/lidar (assuming IMU latency is very small compared to camera/lidar).", "than camera/lidar (assuming IMU latency is very small compared to camera/lidar). "
data.stamp(), _newestAsyncImuStamp, _imuBuffer.size()); "Current camera/lidar delay is %fs.",
event.data().stamp(), _newestAsyncImuStamp, _imuBuffer.size(), UTimer::now() - event.data().stamp());
notify = false; notify = false;
} }
else { else {
_dataBuffer.push_back(data); _dataBuffer.push_back(event);
while(_dataBufferMaxSize > 0 && _dataBuffer.size() > _dataBufferMaxSize) while(_dataBufferMaxSize > 0 && _dataBuffer.size() > _dataBufferMaxSize)
{ {
UDEBUG("Data buffer is full, the oldest data is removed to add the new one."); UDEBUG("Data buffer is full, the oldest data is removed to add the new one.");
@@ -190,11 +213,11 @@ void OdometryThread::addData(const SensorData & data)
} }
else else
{ {
_imuBuffer.push_back(data); _imuBuffer.push_back(event.data());
if(_oldestAsyncImuStamp == 0) { if(_oldestAsyncImuStamp == 0) {
_oldestAsyncImuStamp = data.stamp(); _oldestAsyncImuStamp = event.data().stamp();
} }
_newestAsyncImuStamp = data.stamp(); _newestAsyncImuStamp = event.data().stamp();
} }
} }
_dataMutex.unlock(); _dataMutex.unlock();
@@ -205,7 +228,7 @@ void OdometryThread::addData(const SensorData & data)
} }
} }
bool OdometryThread::getData(SensorData & data) bool OdometryThread::getData(SensorEvent & event)
{ {
bool dataFilled = false; bool dataFilled = false;
_dataAdded.acquire(); _dataAdded.acquire();
@@ -219,12 +242,12 @@ bool OdometryThread::getData(SensorData & data)
_odometry->process(_imuBuffer.front()); _odometry->process(_imuBuffer.front());
double stamp =_imuBuffer.front().stamp(); double stamp =_imuBuffer.front().stamp();
_imuBuffer.pop_front(); _imuBuffer.pop_front();
if(stamp > _dataBuffer.front().stamp()) { if(stamp > _dataBuffer.front().data().stamp()) {
break; break;
} }
} }
data = _dataBuffer.front(); event = _dataBuffer.front();
_dataBuffer.pop_front(); _dataBuffer.pop_front();
dataFilled = true; dataFilled = true;
} }
+88 -51
View File
@@ -185,6 +185,48 @@ Optimizer * Optimizer::create(Optimizer::Type type, const ParametersMap & parame
return optimizer; return optimizer;
} }
class LinkIdKey
{
public:
LinkIdKey(int id, Link::Type type) :
id_(id),
type_(type) {}
bool operator<(const LinkIdKey & k) const
{
// landmark, sort by smallest to largest landmark id, after normal links
if(id_ < 0 && k.id_ < 0)
{
return id_ > k.id_;
}
else if(id_ < 0) {
return false;
}
else if(k.id_ < 0) {
return true;
}
if(type_ == Link::kNeighbor && k.type_ != Link::kNeighbor)
{
return true;
}
else if(type_ != Link::kNeighbor && k.type_ == Link::kNeighbor)
{
return false;
}
else if(type_ == Link::kNeighborMerged && k.type_ != Link::kNeighbor && k.type_ != Link::kNeighborMerged)
{
return true;
}
else
{
// normal link, sort by smallest to largest id
return id_ < k.id_;
}
}
int id_;
Link::Type type_;
};
void Optimizer::getConnectedGraph( void Optimizer::getConnectedGraph(
int fromId, int fromId,
const std::map<int, Transform> & posesIn, const std::map<int, Transform> & posesIn,
@@ -199,8 +241,8 @@ void Optimizer::getConnectedGraph(
posesOut.clear(); posesOut.clear();
linksOut.clear(); linksOut.clear();
std::set<int> nextPoses; std::map<LinkIdKey, Transform> nextPoses;
nextPoses.insert(fromId); nextPoses.insert(std::make_pair(LinkIdKey(fromId, Link::kUndef), posesIn.find(fromId)->second));
std::multimap<int, std::pair<int, Link::Type> > biLinks; std::multimap<int, std::pair<int, Link::Type> > biLinks;
for(std::multimap<int, Link>::const_iterator iter=linksIn.begin(); iter!=linksIn.end(); ++iter) for(std::multimap<int, Link>::const_iterator iter=linksIn.begin(); iter!=linksIn.end(); ++iter)
{ {
@@ -216,20 +258,25 @@ void Optimizer::getConnectedGraph(
while(nextPoses.size()) while(nextPoses.size())
{ {
int currentId = *nextPoses.rbegin(); // fill up all nodes before landmarks // Fill up all nodes before landmarks
nextPoses.erase(*nextPoses.rbegin()); // For nodes, fill up all neightbor nodes before loop closure ones
int currentId = nextPoses.begin()->first.id_;
Transform currentPose = nextPoses.begin()->second;
nextPoses.erase(nextPoses.begin());
if(posesOut.empty()) if(posesOut.find(currentId) != posesOut.end()) {
// Already added from priority list
continue;
}
posesOut.insert(std::make_pair(currentId, currentPose));
// add prior links
for(std::multimap<int, Link>::const_iterator pter=linksIn.find(currentId); pter!=linksIn.end() && pter->first==currentId; ++pter)
{ {
posesOut.insert(std::make_pair(currentId, posesIn.find(currentId)->second)); if(pter->second.from() == pter->second.to() && (!priorsIgnored() || pter->second.type() != Link::kPosePrior))
// add prior links
for(std::multimap<int, Link>::const_iterator pter=linksIn.find(currentId); pter!=linksIn.end() && pter->first==currentId; ++pter)
{ {
if(pter->second.from() == pter->second.to() && (!priorsIgnored() || pter->second.type() != Link::kPosePrior)) linksOut.insert(*pter);
{
linksOut.insert(*pter);
}
} }
} }
@@ -240,52 +287,42 @@ void Optimizer::getConnectedGraph(
if(posesIn.find(toId) != posesIn.end() && (!landmarksIgnored() || toId>0)) if(posesIn.find(toId) != posesIn.end() && (!landmarksIgnored() || toId>0))
{ {
std::multimap<int, Link>::const_iterator kter = graph::findLink(linksIn, currentId, toId, true, type); std::multimap<int, Link>::const_iterator kter = graph::findLink(linksIn, currentId, toId, true, type);
if(nextPoses.find(toId) == nextPoses.end()) UASSERT(kter!=linksIn.end());
if(!uContains(posesOut, toId))
{ {
if(!uContains(posesOut, toId)) const Transform & poseToIn = posesIn.at(toId);
Transform t = kter->second.from()==currentId?kter->second.transform():kter->second.transform().inverse();
Transform pose;
if(isSlam2d() && kter->second.type() == Link::kLandmark && toId>0 && (poseToIn.is3DoF() || poseToIn.is4DoF()))
{ {
const Transform & poseToIn = posesIn.at(toId); if(poseToIn.is3DoF())
Transform t = kter->second.from()==currentId?kter->second.transform():kter->second.transform().inverse();
if(isSlam2d() && kter->second.type() == Link::kLandmark && toId>0 && (poseToIn.is3DoF() || poseToIn.is4DoF()))
{ {
if(poseToIn.is3DoF()) pose = (posesOut.at(currentId) * t).to3DoF();
{
posesOut.insert(std::make_pair(toId, (posesOut.at(currentId) * t).to3DoF()));
}
else
{
posesOut.insert(std::make_pair(toId, (posesOut.at(currentId) * t).to4DoF()));
}
} }
else else
{ {
posesOut.insert(std::make_pair(toId, posesOut.at(currentId)* t)); pose = (posesOut.at(currentId) * t).to4DoF();
} }
}
// add prior links else
for(std::multimap<int, Link>::const_iterator pter=linksIn.find(toId); pter!=linksIn.end() && pter->first==toId; ++pter) {
{ pose = posesOut.at(currentId)* t;
if(pter->second.from() == pter->second.to() && (!priorsIgnored() || pter->second.type() != Link::kPosePrior))
{
linksOut.insert(*pter);
}
}
nextPoses.insert(toId);
} }
// only add unique links nextPoses.insert(std::make_pair(LinkIdKey(toId, type), pose));
if(graph::findLink(linksOut, currentId, toId, true, kter->second.type()) == linksOut.end()) }
// only add unique links
if(graph::findLink(linksOut, currentId, toId, true, kter->second.type()) == linksOut.end())
{
if(kter->second.to() < 0)
{ {
if(kter->second.to() < 0) // For landmarks, make sure fromId is the landmark
{ linksOut.insert(std::make_pair(kter->second.to(), kter->second.inverse()));
// For landmarks, make sure fromId is the landmark }
linksOut.insert(std::make_pair(kter->second.to(), kter->second.inverse())); else
} {
else linksOut.insert(*kter);
{
linksOut.insert(*kter);
}
} }
} }
} }
@@ -608,8 +645,8 @@ void Optimizer::computeBACorrespondences(
} }
} }
if(sFrom.getWords().size() && if(sFrom.getWordsKpts().size() &&
sTo.getWords().size() && sTo.getWordsKpts().size() &&
sFrom.getWords3().size()) sFrom.getWords3().size())
{ {
if(!rematchFeatures) if(!rematchFeatures)
+14 -3
View File
@@ -168,6 +168,7 @@ bool Parameters::isFeatureParameter(const std::string & parameter)
group.compare("BRISK") == 0 || group.compare("BRISK") == 0 ||
group.compare("KAZE") == 0 || group.compare("KAZE") == 0 ||
group.compare("SuperPoint") == 0 || group.compare("SuperPoint") == 0 ||
group.compare("SuperPointRpautrat") == 0 ||
group.compare("PyDetector") == 0; group.compare("PyDetector") == 0;
} }
@@ -182,6 +183,7 @@ rtabmap::ParametersMap Parameters::getDefaultOdometryParameters(bool stereo, boo
(stereo && group.compare("Stereo") == 0) || (stereo && group.compare("Stereo") == 0) ||
(icp && group.compare("Icp") == 0) || (icp && group.compare("Icp") == 0) ||
(vis && Parameters::isFeatureParameter(iter->first)) || (vis && Parameters::isFeatureParameter(iter->first)) ||
group.compare("OdomCuVSLAM") == 0 ||
group.compare("Reg") == 0 || group.compare("Reg") == 0 ||
group.compare("Optimizer") == 0 || group.compare("Optimizer") == 0 ||
group.compare("g2o") == 0 || group.compare("g2o") == 0 ||
@@ -236,6 +238,9 @@ const std::map<std::string, std::pair<bool, std::string> > & Parameters::getRemo
{ {
// removed parameters // removed parameters
// 0.23.1
removedParameters_.insert(std::make_pair("OdomVINS/ConfigPath", std::make_pair(true, Parameters::kOdomVINSFusionConfigPath())));
// 0.21.13 // 0.21.13
removedParameters_.insert(std::make_pair("Vis/ForwardEstOnly", std::make_pair(false, ""))); removedParameters_.insert(std::make_pair("Vis/ForwardEstOnly", std::make_pair(false, "")));
@@ -658,6 +663,12 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl; std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
#else #else
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl; std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
#endif
str = "With SuperPoint Rpautrat:";
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
#else
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
#endif #endif
str = "With Python3:"; str = "With Python3:";
#ifdef RTABMAP_PYTHON #ifdef RTABMAP_PYTHON
@@ -936,7 +947,7 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl; std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
#endif #endif
str = "With VINS-Fusion:"; str = "With VINS-Fusion:";
#ifdef RTABMAP_VINS #ifdef RTABMAP_VINS_FUSION
std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl; std::cout << str << std::setw(spacing - str.size()) << "true" << std::endl;
#else #else
std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl; std::cout << str << std::setw(spacing - str.size()) << "false" << std::endl;
@@ -1109,8 +1120,8 @@ ParametersMap Parameters::parseArguments(int argc, char * argv[], bool onlyParam
ignore = true; ignore = true;
} }
#endif #endif
#ifndef RTABMAP_ORBSLAM2 #ifndef RTABMAP_ORB_SLAM
if(group.compare("OdomORBSLAM2") == 0) if(group.compare("OdomORBSLAM") == 0)
{ {
ignore = true; ignore = true;
} }
+19 -11
View File
@@ -286,6 +286,10 @@ void RegistrationVis::parseParameters(const ParametersMap & parameters)
{ {
uInsert(_featureParameters, ParametersPair(Parameters::kKpGridCols(), parameters.at(Parameters::kVisGridCols()))); uInsert(_featureParameters, ParametersPair(Parameters::kKpGridCols(), parameters.at(Parameters::kVisGridCols())));
} }
if(uContains(parameters, Parameters::kRtabmapWorkingDirectory()))
{
uInsert(_featureParameters, ParametersPair(Parameters::kRtabmapWorkingDirectory(), parameters.at(Parameters::kRtabmapWorkingDirectory())));
}
delete _detectorFrom; delete _detectorFrom;
delete _detectorTo; delete _detectorTo;
@@ -329,11 +333,12 @@ Transform RegistrationVis::computeTransformationImpl(
UDEBUG("Feature Detector = %d", (int)_detectorFrom->getType()); UDEBUG("Feature Detector = %d", (int)_detectorFrom->getType());
UDEBUG("guess=%s", guess.prettyPrint().c_str()); UDEBUG("guess=%s", guess.prettyPrint().c_str());
UDEBUG("Input(%d): from=%d words, %d 3D words, %d words descriptors, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d", UDEBUG("Input(%d): from=%d words, %d 3D words, %d words descriptors, %d words kpts, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d",
fromSignature.id(), fromSignature.id(),
(int)fromSignature.getWords().size(), (int)fromSignature.getWords().size(),
(int)fromSignature.getWords3().size(), (int)fromSignature.getWords3().size(),
(int)fromSignature.getWordsDescriptors().rows, (int)fromSignature.getWordsDescriptors().rows,
(int)fromSignature.getWordsKpts().size(),
(int)fromSignature.sensorData().keypoints().size(), (int)fromSignature.sensorData().keypoints().size(),
(int)fromSignature.sensorData().keypoints3D().size(), (int)fromSignature.sensorData().keypoints3D().size(),
fromSignature.sensorData().descriptors().rows, fromSignature.sensorData().descriptors().rows,
@@ -342,11 +347,12 @@ Transform RegistrationVis::computeTransformationImpl(
(int)fromSignature.sensorData().cameraModels().size(), (int)fromSignature.sensorData().cameraModels().size(),
(int)fromSignature.sensorData().stereoCameraModels().size()); (int)fromSignature.sensorData().stereoCameraModels().size());
UDEBUG("Input(%d): to=%d words, %d 3D words, %d words descriptors, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d", UDEBUG("Input(%d): to=%d words, %d 3D words, %d words descriptors, %d words kpts, %d kpts, %d kpts3D, %d descriptors, image=%dx%d models=%d stereo=%d",
toSignature.id(), toSignature.id(),
(int)toSignature.getWords().size(), (int)toSignature.getWords().size(),
(int)toSignature.getWords3().size(), (int)toSignature.getWords3().size(),
(int)toSignature.getWordsDescriptors().rows, (int)toSignature.getWordsDescriptors().rows,
(int)toSignature.getWordsKpts().size(),
(int)toSignature.sensorData().keypoints().size(), (int)toSignature.sensorData().keypoints().size(),
(int)toSignature.sensorData().keypoints3D().size(), (int)toSignature.sensorData().keypoints3D().size(),
toSignature.sensorData().descriptors().rows, toSignature.sensorData().descriptors().rows,
@@ -375,6 +381,9 @@ Transform RegistrationVis::computeTransformationImpl(
{ {
UDEBUG(""); UDEBUG("");
// just some checks to make sure that input data are ok // just some checks to make sure that input data are ok
UASSERT(fromSignature.getWords().empty() ||
fromSignature.getWordsKpts().empty() ||
(fromSignature.getWords().size() == fromSignature.getWordsKpts().size()));
UASSERT(fromSignature.getWords().empty() || UASSERT(fromSignature.getWords().empty() ||
fromSignature.getWords3().empty() || fromSignature.getWords3().empty() ||
(fromSignature.getWords().size() == fromSignature.getWords3().size())); (fromSignature.getWords().size() == fromSignature.getWords3().size()));
@@ -382,8 +391,11 @@ Transform RegistrationVis::computeTransformationImpl(
(int)fromSignature.getWords().size() == fromSignature.getWordsDescriptors().rows || (int)fromSignature.getWords().size() == fromSignature.getWordsDescriptors().rows ||
fromSignature.sensorData().descriptors().empty() || fromSignature.sensorData().descriptors().empty() ||
fromSignature.getWordsDescriptors().empty() == 0); fromSignature.getWordsDescriptors().empty() == 0);
UASSERT((toSignature.getWords().empty() && toSignature.getWords3().empty())|| UASSERT(toSignature.getWords().empty() ||
(toSignature.getWords().size() && toSignature.getWords3().empty())|| toSignature.getWordsKpts().empty() ||
(toSignature.getWords().size() == toSignature.getWordsKpts().size()));
UASSERT(toSignature.getWords().empty() ||
toSignature.getWords3().empty() ||
(toSignature.getWords().size() == toSignature.getWords3().size())); (toSignature.getWords().size() == toSignature.getWords3().size()));
UASSERT((int)toSignature.sensorData().keypoints().size() == toSignature.sensorData().descriptors().rows || UASSERT((int)toSignature.sensorData().keypoints().size() == toSignature.sensorData().descriptors().rows ||
(int)toSignature.getWords().size() == toSignature.getWordsDescriptors().rows || (int)toSignature.getWords().size() == toSignature.getWordsDescriptors().rows ||
@@ -1660,14 +1672,10 @@ Transform RegistrationVis::computeTransformationImpl(
UINFO(msg.c_str()); UINFO(msg.c_str());
} }
} }
else if(fromSignature.getWords().size() == 0) else
{ {
msg = uFormat("No enough features (%d)", (int)fromSignature.getWords().size()); msg = uFormat("No enough features < %s=%d (from=%d to=%d)",
UWARN(msg.c_str()); Parameters::kVisMinInliers().c_str(), _minInliers, (int)fromSignature.getWords().size(), (int)toSignature.getWords().size());
}
else
{
msg = uFormat("No camera model");
UWARN(msg.c_str()); UWARN(msg.c_str());
} }
} }
+45 -52
View File
@@ -1502,8 +1502,8 @@ bool Rtabmap::process(
float angleToClosestNodeInTheGraph = 0; float angleToClosestNodeInTheGraph = 0;
if(_rgbdSlamMode) if(_rgbdSlamMode)
{ {
double linVar = odomCovariance.empty()?1.0f:uMax3(odomCovariance.at<double>(0,0), odomCovariance.at<double>(1,1)>=9999?0:odomCovariance.at<double>(1,1), odomCovariance.at<double>(2,2)>=9999?0:odomCovariance.at<double>(2,2)); double linVar = odomCovariance.empty()?0.0f:uMax3(odomCovariance.at<double>(0,0), odomCovariance.at<double>(1,1)>=9999?0:odomCovariance.at<double>(1,1), odomCovariance.at<double>(2,2)>=9999?0:odomCovariance.at<double>(2,2));
double angVar = odomCovariance.empty()?1.0f:uMax3(odomCovariance.at<double>(3,3)>=9999?0:odomCovariance.at<double>(3,3), odomCovariance.at<double>(4,4)>=9999?0:odomCovariance.at<double>(4,4), odomCovariance.at<double>(5,5)); double angVar = odomCovariance.empty()?0.0f:uMax3(odomCovariance.at<double>(3,3)>=9999?0:odomCovariance.at<double>(3,3), odomCovariance.at<double>(4,4)>=9999?0:odomCovariance.at<double>(4,4), odomCovariance.at<double>(5,5));
statistics_.addStatistic(Statistics::kMemoryOdometry_variance_lin(), (float)linVar); statistics_.addStatistic(Statistics::kMemoryOdometry_variance_lin(), (float)linVar);
statistics_.addStatistic(Statistics::kMemoryOdometry_variance_ang(), (float)angVar); statistics_.addStatistic(Statistics::kMemoryOdometry_variance_ang(), (float)angVar);
@@ -1544,9 +1544,10 @@ bool Rtabmap::process(
{ {
float x,y,z, roll,pitch,yaw; float x,y,z, roll,pitch,yaw;
t.getTranslationAndEulerAngles(x,y,z, roll,pitch,yaw); t.getTranslationAndEulerAngles(x,y,z, roll,pitch,yaw);
bool isMoving = fabs(x) > _rgbdLinearUpdate || bool isMoving = (_rgbdLinearUpdate > 0.0f && (
fabs(y) > _rgbdLinearUpdate || fabs(x) > _rgbdLinearUpdate ||
fabs(z) > _rgbdLinearUpdate || fabs(y) > _rgbdLinearUpdate ||
fabs(z) > _rgbdLinearUpdate)) ||
(_rgbdAngularUpdate>0.0f && ( (_rgbdAngularUpdate>0.0f && (
fabs(roll) > _rgbdAngularUpdate || fabs(roll) > _rgbdAngularUpdate ||
fabs(pitch) > _rgbdAngularUpdate || fabs(pitch) > _rgbdAngularUpdate ||
@@ -1607,6 +1608,7 @@ bool Rtabmap::process(
Transform t = _memory->computeTransform(oldId, signature->id(), guess, &info); Transform t = _memory->computeTransform(oldId, signature->id(), guess, &info);
if(!t.isNull()) if(!t.isNull())
{ {
UASSERT(!info.covariance.empty() && info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0);
UINFO("Odometry refining: update neighbor link (%d->%d, variance:lin=%f, ang=%f) from %s to %s", UINFO("Odometry refining: update neighbor link (%d->%d, variance:lin=%f, ang=%f) from %s to %s",
oldId, oldId,
signature->id(), signature->id(),
@@ -1614,7 +1616,6 @@ bool Rtabmap::process(
info.covariance.at<double>(5,5), info.covariance.at<double>(5,5),
guess.prettyPrint().c_str(), guess.prettyPrint().c_str(),
t.prettyPrint().c_str()); t.prettyPrint().c_str());
UASSERT(info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0);
_memory->updateLink(Link(oldId, signature->id(), signature->getLinks().begin()->second.type(), t, info.covariance.inv())); _memory->updateLink(Link(oldId, signature->id(), signature->getLinks().begin()->second.type(), t, info.covariance.inv()));
if(_optimizeFromGraphEnd) if(_optimizeFromGraphEnd)
@@ -1894,7 +1895,7 @@ bool Rtabmap::process(
*iter, *iter,
transform.prettyPrint().c_str()); transform.prettyPrint().c_str());
// Add a loop constraint // Add a loop constraint
UASSERT(info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0); UASSERT(!info.covariance.empty() && info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0);
if(_memory->addLink(Link(signature->id(), *iter, Link::kLocalTimeClosure, transform, getInformation(info.covariance)))) if(_memory->addLink(Link(signature->id(), *iter, Link::kLocalTimeClosure, transform, getInformation(info.covariance))))
{ {
++proximityDetectionsInTimeFound; ++proximityDetectionsInTimeFound;
@@ -2395,24 +2396,22 @@ bool Rtabmap::process(
distanceSoFar += _path[i-1].second.getDistance(_path[i].second); distanceSoFar += _path[i-1].second.getDistance(_path[i].second);
} }
if(distanceSoFar <= _localRadius) if(_memory->getSignature(_path[i].first) != 0)
{ {
if(_memory->getSignature(_path[i].first) != 0) if(immunizedLocations.insert(_path[i].first).second)
{ {
if(immunizedLocations.insert(_path[i].first).second) ++immunizedLocally;
{
++immunizedLocally;
}
UDEBUG("Path immunization: node %d (dist=%fm)", _path[i].first, distanceSoFar);
}
else if(retrievalLocalIds.size() < _maxLocalRetrieved)
{
UINFO("retrieval of node %d on path (dist=%fm)", _path[i].first, distanceSoFar);
retrievalLocalIds.push_back(_path[i].first);
// retrieved locations are automatically immunized
} }
UDEBUG("Path immunization: node %d (dist=%fm)", _path[i].first, distanceSoFar);
} }
else else if(retrievalLocalIds.size() < _maxLocalRetrieved)
{
UINFO("retrieval of node %d on path (dist=%fm)", _path[i].first, distanceSoFar);
retrievalLocalIds.push_back(_path[i].first);
// retrieved locations are automatically immunized
}
if(distanceSoFar > _localRadius)
{ {
UDEBUG("Stop on node %d (dist=%fm > %fm)", UDEBUG("Stop on node %d (dist=%fm > %fm)",
_path[i].first, distanceSoFar, _localRadius); _path[i].first, distanceSoFar, _localRadius);
@@ -2784,7 +2783,7 @@ bool Rtabmap::process(
signature->id(), signature->id(),
nearestId, nearestId,
transform.prettyPrint().c_str()); transform.prettyPrint().c_str());
UASSERT(info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0); UASSERT(!info.covariance.empty() && info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0);
//for statistics //for statistics
loopClosureVisualInliersMeanDist = info.inliersMeanDistance; loopClosureVisualInliersMeanDist = info.inliersMeanDistance;
@@ -2995,7 +2994,7 @@ bool Rtabmap::process(
} }
// set Identify covariance for laser scan matching only // set Identify covariance for laser scan matching only
UASSERT(info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0); UASSERT(!info.covariance.empty() && info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0);
_memory->addLink(Link(signature->id(), nearestId, Link::kLocalSpaceClosure, transform, getInformation(info.covariance)/_proximityMergedScanCovFactor, scanMatchingIds)); _memory->addLink(Link(signature->id(), nearestId, Link::kLocalSpaceClosure, transform, getInformation(info.covariance)/_proximityMergedScanCovFactor, scanMatchingIds));
loopClosureLinksAdded.push_back(std::make_pair(signature->id(), nearestId)); loopClosureLinksAdded.push_back(std::make_pair(signature->id(), nearestId));
@@ -3083,7 +3082,7 @@ bool Rtabmap::process(
if(!rejectedLoopClosure) if(!rejectedLoopClosure)
{ {
// Make the new one the parent of the old one // Make the new one the parent of the old one
UASSERT(info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0); UASSERT(!info.covariance.empty() && info.covariance.at<double>(0,0) > 0.0 && info.covariance.at<double>(5,5) > 0.0);
loopClosureLinearVariance = uMax3(info.covariance.at<double>(0,0), info.covariance.at<double>(1,1)>=9999?0:info.covariance.at<double>(1,1), info.covariance.at<double>(2,2)>=9999?0:info.covariance.at<double>(2,2)); loopClosureLinearVariance = uMax3(info.covariance.at<double>(0,0), info.covariance.at<double>(1,1)>=9999?0:info.covariance.at<double>(1,1), info.covariance.at<double>(2,2)>=9999?0:info.covariance.at<double>(2,2));
loopClosureAngularVariance = uMax3(info.covariance.at<double>(3,3)>=9999?0:info.covariance.at<double>(3,3), info.covariance.at<double>(4,4)>=9999?0:info.covariance.at<double>(4,4), info.covariance.at<double>(5,5)); loopClosureAngularVariance = uMax3(info.covariance.at<double>(3,3)>=9999?0:info.covariance.at<double>(3,3), info.covariance.at<double>(4,4)>=9999?0:info.covariance.at<double>(4,4), info.covariance.at<double>(5,5));
@@ -3158,17 +3157,7 @@ bool Rtabmap::process(
UASSERT(uContains(_optimizedPoses, signature->id())); UASSERT(uContains(_optimizedPoses, signature->id()));
UASSERT_MSG(uContains(_optimizedPoses, _path[_pathCurrentIndex].first), uFormat("id=%d", _path[_pathCurrentIndex].first).c_str()); UASSERT_MSG(uContains(_optimizedPoses, _path[_pathCurrentIndex].first), uFormat("id=%d", _path[_pathCurrentIndex].first).c_str());
Transform virtualLoop = _optimizedPoses.at(signature->id()).inverse() * _optimizedPoses.at(_path[_pathCurrentIndex].first); Transform virtualLoop = _optimizedPoses.at(signature->id()).inverse() * _optimizedPoses.at(_path[_pathCurrentIndex].first);
_memory->addLink(Link(signature->id(), _path[_pathCurrentIndex].first, Link::kVirtualClosure, virtualLoop, cv::Mat::eye(6,6,CV_64FC1)*0.01)); // set high variance
if(_localRadius == 0.0f || virtualLoop.getNorm() < _localRadius)
{
_memory->addLink(Link(signature->id(), _path[_pathCurrentIndex].first, Link::kVirtualClosure, virtualLoop, cv::Mat::eye(6,6,CV_64FC1)*0.01)); // set high variance
}
else
{
UERROR("Virtual link larger than local radius (%fm > %fm). Aborting the plan!",
virtualLoop.getNorm(), _localRadius);
this->clearPath(-1);
}
} }
} }
@@ -3269,6 +3258,7 @@ bool Rtabmap::process(
{ {
constraints.insert(std::make_pair(iter->second.from(), iter->second)); constraints.insert(std::make_pair(iter->second.from(), iter->second));
} }
cv::Mat priorInfMat = cv::Mat::eye(6,6, CV_64FC1)*_localizationPriorInf; cv::Mat priorInfMat = cv::Mat::eye(6,6, CV_64FC1)*_localizationPriorInf;
for(std::multimap<int, Link>::iterator iter=constraints.begin(); iter!=constraints.end(); ++iter) for(std::multimap<int, Link>::iterator iter=constraints.begin(); iter!=constraints.end(); ++iter)
{ {
@@ -3276,6 +3266,7 @@ bool Rtabmap::process(
if(iterPose != _optimizedPoses.end() && poses.find(iterPose->first) == poses.end()) if(iterPose != _optimizedPoses.end() && poses.find(iterPose->first) == poses.end())
{ {
poses.insert(*iterPose); poses.insert(*iterPose);
// make the poses in the map fixed // make the poses in the map fixed
constraints.insert(std::make_pair(iterPose->first, Link(iterPose->first, iterPose->first, Link::kPosePrior, iterPose->second, priorInfMat))); constraints.insert(std::make_pair(iterPose->first, Link(iterPose->first, iterPose->first, Link::kPosePrior, iterPose->second, priorInfMat)));
UDEBUG("Constraint %d->%d: %s (type=%s, var=%f)", iterPose->first, iterPose->first, iterPose->second.prettyPrint().c_str(), Link::typeName(Link::kPosePrior).c_str(), 1./_localizationPriorInf); UDEBUG("Constraint %d->%d: %s (type=%s, var=%f)", iterPose->first, iterPose->first, iterPose->second.prettyPrint().c_str(), Link::typeName(Link::kPosePrior).c_str(), 1./_localizationPriorInf);
@@ -3285,11 +3276,14 @@ bool Rtabmap::process(
std::map<int, Transform> posesOut; std::map<int, Transform> posesOut;
std::multimap<int, Link> edgeConstraintsOut; std::multimap<int, Link> edgeConstraintsOut;
bool priorsIgnored = _graphOptimizer->priorsIgnored(); bool priorsIgnored = _graphOptimizer->priorsIgnored();
UDEBUG("priorsIgnored was %s", priorsIgnored?"true":"false"); UDEBUG("priorsIgnored was %s", priorsIgnored?"true":"false");
_graphOptimizer->setPriorsIgnored(false); //temporary set false to use priors above to fix nodes of the map _graphOptimizer->setPriorsIgnored(false); //temporary set false to use priors above to fix nodes of the map
// If slam2d: get connected graph while keeping original roll,pitch,z values. // If slam2d: get connected graph while keeping original roll,pitch,z values.
_graphOptimizer->getConnectedGraph(signature->id(), poses, constraints, posesOut, edgeConstraintsOut); _graphOptimizer->getConnectedGraph(signature->id(), poses, constraints, posesOut, edgeConstraintsOut);
if(ULogger::level() == ULogger::kDebug) if(ULogger::level() == ULogger::kDebug)
{ {
for(std::map<int, Transform>::iterator iter=posesOut.begin(); iter!=posesOut.end(); ++iter) for(std::map<int, Transform>::iterator iter=posesOut.begin(); iter!=posesOut.end(); ++iter)
@@ -5644,6 +5638,7 @@ int Rtabmap::detectMoreLoopClosures(
const ProgressState * processState, const ProgressState * processState,
float clusterRadiusMin) float clusterRadiusMin)
{ {
UDEBUG("");
UASSERT(iterations>0); UASSERT(iterations>0);
if(_graphOptimizer->iterations() <= 0) if(_graphOptimizer->iterations() <= 0)
@@ -6326,6 +6321,7 @@ bool Rtabmap::addLink(const Link & link)
std::map<int, Transform> poses = _odomCachePoses; std::map<int, Transform> poses = _odomCachePoses;
std::multimap<int, Link> constraints = _odomCacheConstraints; std::multimap<int, Link> constraints = _odomCacheConstraints;
constraints.insert(std::make_pair(link.from(), link)); constraints.insert(std::make_pair(link.from(), link));
cv::Mat priorInfMat = cv::Mat::eye(6,6, CV_64FC1)*_localizationPriorInf;
for(std::multimap<int, Link>::iterator iter=constraints.begin(); iter!=constraints.end(); ++iter) for(std::multimap<int, Link>::iterator iter=constraints.begin(); iter!=constraints.end(); ++iter)
{ {
std::map<int, Transform>::iterator iterPose = _optimizedPoses.find(iter->second.to()); std::map<int, Transform>::iterator iterPose = _optimizedPoses.find(iter->second.to());
@@ -6333,7 +6329,7 @@ bool Rtabmap::addLink(const Link & link)
{ {
poses.insert(*iterPose); poses.insert(*iterPose);
// make the poses in the map fixed // make the poses in the map fixed
constraints.insert(std::make_pair(iterPose->first, Link(iterPose->first, iterPose->first, Link::kPosePrior, iterPose->second, cv::Mat::eye(6,6, CV_64FC1)*999999))); constraints.insert(std::make_pair(iterPose->first, Link(iterPose->first, iterPose->first, Link::kPosePrior, iterPose->second, priorInfMat)));
} }
} }
@@ -6927,24 +6923,24 @@ void Rtabmap::updateGoalIndex()
{ {
distanceSoFar += _path[i-1].second.getDistance(_path[i].second); distanceSoFar += _path[i-1].second.getDistance(_path[i].second);
} }
if(distanceSoFar <= _localRadius)
if(_path[i].first != _path[i-1].first)
{ {
if(_path[i].first != _path[i-1].first) const Signature * s = _memory->getSignature(_path[i].first);
if(s)
{ {
const Signature * s = _memory->getSignature(_path[i].first); if(!s->hasLink(_path[i-1].first) && _memory->getSignature(_path[i-1].first) != 0)
if(s)
{ {
if(!s->hasLink(_path[i-1].first) && _memory->getSignature(_path[i-1].first) != 0) Transform virtualLoop = _path[i].second.inverse() * _path[i-1].second;
{ _memory->addLink(Link(_path[i].first, _path[i-1].first, Link::kVirtualClosure, virtualLoop, cv::Mat::eye(6,6,CV_64FC1)*0.01)); // on the optimized path
Transform virtualLoop = _path[i].second.inverse() * _path[i-1].second; UINFO("Added Virtual link between %d and %d", _path[i-1].first, _path[i].first);
_memory->addLink(Link(_path[i].first, _path[i-1].first, Link::kVirtualClosure, virtualLoop, cv::Mat::eye(6,6,CV_64FC1)*0.01)); // on the optimized path
UINFO("Added Virtual link between %d and %d", _path[i-1].first, _path[i].first);
}
} }
} }
} }
else
if(distanceSoFar > _localRadius)
{ {
UDEBUG("Farthest goal=%d : %f m", _path[i].first, distanceSoFar);
break; break;
} }
} }
@@ -7004,11 +7000,8 @@ void Rtabmap::updateGoalIndex()
if((goalIndex == _pathCurrentIndex && i == _path.size()-1) || if((goalIndex == _pathCurrentIndex && i == _path.size()-1) ||
_pathUnreachableNodes.find(i) == _pathUnreachableNodes.end()) _pathUnreachableNodes.find(i) == _pathUnreachableNodes.end())
{ {
if(distanceFromCurrentNode <= _localRadius) goalIndex = i;
{ if(distanceFromCurrentNode > _localRadius)
goalIndex = i;
}
else
{ {
break; break;
} }
+3 -2
View File
@@ -506,9 +506,10 @@ void RtabmapThread::addData(const OdometryEvent & odomEvent)
ignoreFrame = true; ignoreFrame = true;
} }
} }
UASSERT(!odomEvent.info().reg.covariance.empty());
if(!lastPose_.isIdentity() && if(!lastPose_.isIdentity() &&
(odomEvent.pose().isIdentity() || (odomEvent.pose().isIdentity() ||
odomEvent.info().reg.covariance.at<double>(0,0)>=9999)) odomEvent.info().reg.covariance.at<double>(0,0)>=9999))
{ {
if(odomEvent.pose().isIdentity()) if(odomEvent.pose().isIdentity())
{ {
-1
View File
@@ -960,7 +960,6 @@ void SensorCaptureThread::postUpdate(SensorData * dataPtr, SensorCaptureInfo * i
{ {
UASSERT(!data.imu().localTransform().isNull()); UASSERT(!data.imu().localTransform().isNull());
imu.convertToBaseFrame(); imu.convertToBaseFrame();
} }
_imuFilter->update( _imuFilter->update(
imu.angularVelocity()[0], imu.angularVelocity()[0],
+3 -3
View File
@@ -548,7 +548,7 @@ void SensorData::setOccupancyGrid(
float cellSize, float cellSize,
const cv::Point3f & viewPoint) const cv::Point3f & viewPoint)
{ {
UDEBUG("ground=%d obstacles=%d empty=%d", ground.cols, obstacles.cols, empty.cols); //UDEBUG("ground=%d obstacles=%d empty=%d", ground.cols, obstacles.cols, empty.cols);
if((!ground.empty() && (!_groundCellsCompressed.empty() || !_groundCellsRaw.empty())) || if((!ground.empty() && (!_groundCellsCompressed.empty() || !_groundCellsRaw.empty())) ||
(!obstacles.empty() && (!_obstacleCellsCompressed.empty() || !_obstacleCellsRaw.empty())) || (!obstacles.empty() && (!_obstacleCellsCompressed.empty() || !_obstacleCellsRaw.empty())) ||
(!empty.empty() && (!_emptyCellsCompressed.empty() || !_emptyCellsRaw.empty()))) (!empty.empty() && (!_emptyCellsCompressed.empty() || !_emptyCellsRaw.empty())))
@@ -649,7 +649,7 @@ void SensorData::uncompressData(
cv::Mat * emptyCellsRaw, cv::Mat * emptyCellsRaw,
cv::Mat * depthConfidenceRaw) cv::Mat * depthConfidenceRaw)
{ {
UDEBUG("%d data(%d,%d,%d,%d,%d,%d,%d,%d)", /*UDEBUG("%d data(%d,%d,%d,%d,%d,%d,%d,%d)",
this->id(), this->id(),
imageRaw?1:0, imageRaw?1:0,
depthRaw?1:0, depthRaw?1:0,
@@ -658,7 +658,7 @@ void SensorData::uncompressData(
groundCellsRaw?1:0, groundCellsRaw?1:0,
obstacleCellsRaw?1:0, obstacleCellsRaw?1:0,
emptyCellsRaw?1:0, emptyCellsRaw?1:0,
depthConfidenceRaw?1:0); depthConfidenceRaw?1:0);*/
if(imageRaw == 0 && if(imageRaw == 0 &&
depthRaw == 0 && depthRaw == 0 &&
laserScanRaw == 0 && laserScanRaw == 0 &&
+3 -3
View File
@@ -118,7 +118,7 @@ void Signature::addLinks(const std::map<int, Link> & links)
} }
void Signature::addLink(const Link & link) void Signature::addLink(const Link & link)
{ {
UDEBUG("Add link %d to %d (type=%d/%s var=%f,%f)", link.to(), this->id(), (int)link.type(), link.typeName().c_str(), link.transVariance(), link.rotVariance()); //UDEBUG("Add link %d to %d (type=%d/%s var=%f,%f)", link.to(), this->id(), (int)link.type(), link.typeName().c_str(), link.transVariance(), link.rotVariance());
UASSERT_MSG(link.from() == this->id(), uFormat("%d->%d for signature %d (type=%d)", link.from(), link.to(), this->id(), link.type()).c_str()); UASSERT_MSG(link.from() == this->id(), uFormat("%d->%d for signature %d (type=%d)", link.from(), link.to(), this->id(), link.type()).c_str());
UASSERT_MSG((link.to() != this->id()) || link.type()==Link::kPosePrior || link.type()==Link::kGravity, uFormat("%d->%d for signature %d (type=%d)", link.from(), link.to(), this->id(), link.type()).c_str()); UASSERT_MSG((link.to() != this->id()) || link.type()==Link::kPosePrior || link.type()==Link::kGravity, uFormat("%d->%d for signature %d (type=%d)", link.from(), link.to(), this->id(), link.type()).c_str());
UASSERT_MSG(link.to() == this->id() || _links.find(link.to()) == _links.end(), uFormat("Link %d (type=%d) already added to signature %d!", link.to(), link.type(), this->id()).c_str()); UASSERT_MSG(link.to() == this->id() || _links.find(link.to()) == _links.end(), uFormat("Link %d (type=%d) already added to signature %d!", link.to(), link.type(), this->id()).c_str());
@@ -318,7 +318,7 @@ void Signature::setWords(const std::multimap<int, int> & words,
UASSERT_MSG(descriptors.empty() || descriptors.rows == (int)words.size(), uFormat("words=%d, descriptors=%d", (int)words.size(), descriptors.rows).c_str()); UASSERT_MSG(descriptors.empty() || descriptors.rows == (int)words.size(), uFormat("words=%d, descriptors=%d", (int)words.size(), descriptors.rows).c_str());
UASSERT_MSG(points.empty() || points.size() == words.size(), uFormat("words=%d, points=%d", (int)words.size(), (int)points.size()).c_str()); UASSERT_MSG(points.empty() || points.size() == words.size(), uFormat("words=%d, points=%d", (int)words.size(), (int)points.size()).c_str());
UASSERT_MSG(keypoints.empty() || keypoints.size() == words.size(), uFormat("words=%d, descriptors=%d", (int)words.size(), (int)keypoints.size()).c_str()); UASSERT_MSG(keypoints.empty() || keypoints.size() == words.size(), uFormat("words=%d, descriptors=%d", (int)words.size(), (int)keypoints.size()).c_str());
UASSERT(words.empty() || !keypoints.empty() || !points.empty() || !descriptors.empty()); //UASSERT(words.empty() || !keypoints.empty() || !points.empty() || !descriptors.empty());
_invalidWordsCount = 0; _invalidWordsCount = 0;
for(std::multimap<int, int>::const_iterator iter=words.begin(); iter!=words.end(); ++iter) for(std::multimap<int, int>::const_iterator iter=words.begin(); iter!=words.end(); ++iter)
@@ -328,7 +328,7 @@ void Signature::setWords(const std::multimap<int, int> & words,
++_invalidWordsCount; ++_invalidWordsCount;
} }
// make sure indexes are all valid! // make sure indexes are all valid!
UASSERT_MSG(iter->second >=0 && iter->second < (int)words.size(), uFormat("iter->second=%d words.size()=%d", iter->second, (int)words.size()).c_str()); UASSERT_MSG(iter->second<0 || iter->second < (int)words.size(), uFormat("iter->second=%d words.size()=%d", iter->second, (int)words.size()).c_str());
} }
_enabled = false; _enabled = false;
+187 -45
View File
@@ -51,7 +51,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <fstream> #include <fstream>
#include <string> #include <string>
#define KDTREE_SIZE 4
#define KNN_CHECKS 32 #define KNN_CHECKS 32
namespace rtabmap namespace rtabmap
@@ -69,9 +68,11 @@ VWDictionary::VWDictionary(const ParametersMap & parameters) :
_nndrRatio(Parameters::defaultKpNndrRatio()), _nndrRatio(Parameters::defaultKpNndrRatio()),
_newDictionaryPath(Parameters::defaultKpDictionaryPath()), _newDictionaryPath(Parameters::defaultKpDictionaryPath()),
_newWordsComparedTogether(Parameters::defaultKpNewWordsComparedTogether()), _newWordsComparedTogether(Parameters::defaultKpNewWordsComparedTogether()),
_serializeWithChecksum(Parameters::defaultKpSerializeWithChecksum()),
_lastWordId(0), _lastWordId(0),
useDistanceL1_(false), useDistanceL1_(false),
_flannIndex(new FlannIndex()), _flannIndex(new FlannIndex()),
_modified(true),
_strategy(kNNBruteForce) _strategy(kNNBruteForce)
{ {
this->setNNStrategy((NNStrategy)Parameters::defaultKpNNStrategy()); this->setNNStrategy((NNStrategy)Parameters::defaultKpNNStrategy());
@@ -89,6 +90,7 @@ void VWDictionary::parseParameters(const ParametersMap & parameters)
ParametersMap::const_iterator iter; ParametersMap::const_iterator iter;
Parameters::parse(parameters, Parameters::kKpNndrRatio(), _nndrRatio); Parameters::parse(parameters, Parameters::kKpNndrRatio(), _nndrRatio);
Parameters::parse(parameters, Parameters::kKpNewWordsComparedTogether(), _newWordsComparedTogether); Parameters::parse(parameters, Parameters::kKpNewWordsComparedTogether(), _newWordsComparedTogether);
Parameters::parse(parameters, Parameters::kKpSerializeWithChecksum(), _serializeWithChecksum);
Parameters::parse(parameters, Parameters::kKpIncrementalFlann(), _incrementalFlann); Parameters::parse(parameters, Parameters::kKpIncrementalFlann(), _incrementalFlann);
Parameters::parse(parameters, Parameters::kKpFlannRebalancingFactor(), _rebalancingFactor); Parameters::parse(parameters, Parameters::kKpFlannRebalancingFactor(), _rebalancingFactor);
bool byteToFloat = _byteToFloat; bool byteToFloat = _byteToFloat;
@@ -160,7 +162,7 @@ void VWDictionary::setFixedDictionary(const std::string & dictionaryPath)
DBDriver * driver = DBDriver::create(); DBDriver * driver = DBDriver::create();
if(driver->openConnection(dictionaryPath, false)) if(driver->openConnection(dictionaryPath, false))
{ {
driver->load(this, false); driver->load(*this, false);
for(std::map<int, VisualWord*>::iterator iter=_visualWords.begin(); iter!=_visualWords.end(); ++iter) for(std::map<int, VisualWord*>::iterator iter=_visualWords.begin(); iter!=_visualWords.end(); ++iter)
{ {
iter->second->setSaved(true); iter->second->setSaved(true);
@@ -289,6 +291,11 @@ void VWDictionary::setFixedDictionary(const std::string & dictionaryPath)
_newDictionaryPath = dictionaryPath; _newDictionaryPath = dictionaryPath;
} }
bool VWDictionary::isModified() const
{
return _modified;
}
bool VWDictionary::setNNStrategy(NNStrategy strategy) bool VWDictionary::setNNStrategy(NNStrategy strategy)
{ {
#if CV_MAJOR_VERSION < 3 #if CV_MAJOR_VERSION < 3
@@ -484,7 +491,13 @@ void VWDictionary::update()
if(_notIndexedWords.size() || _visualWords.size() == 0 || _removedIndexedWords.size()) if(_notIndexedWords.size() || _visualWords.size() == 0 || _removedIndexedWords.size())
{ {
if(_incrementalFlann && _modified = true;
bool firstUpdate = _removedIndexedWords.empty() && _visualWords.size() == _notIndexedWords.size();
UDEBUG("firstUpdate=%s (_removedIndexedWords=%ld, _visualWords=%ld, _notIndexedWords=%ld)",
firstUpdate?"true":"false", _removedIndexedWords.size(), _visualWords.size(), _notIndexedWords.size());
if(!firstUpdate &&
_incrementalFlann &&
_strategy < kNNBruteForce && _strategy < kNNBruteForce &&
_visualWords.size()) _visualWords.size())
{ {
@@ -501,7 +514,9 @@ void VWDictionary::update()
if(_notIndexedWords.size()) if(_notIndexedWords.size())
{ {
ULOGGER_DEBUG("Incremental FLANN: Inserting %d words...", (int)_notIndexedWords.size()); UTimer timer;
timer.start();
ULOGGER_DEBUG("Incremental FLANN: Inserting %d words...", (int)_notIndexedWords.size(), _byteToFloat?"true":"false");
for(std::set<int>::iterator iter=_notIndexedWords.begin(); iter!=_notIndexedWords.end(); ++iter) for(std::set<int>::iterator iter=_notIndexedWords.begin(); iter!=_notIndexedWords.end(); ++iter)
{ {
VisualWord* w = uValue(_visualWords, *iter, (VisualWord*)0); VisualWord* w = uValue(_visualWords, *iter, (VisualWord*)0);
@@ -528,24 +543,13 @@ void VWDictionary::update()
int index = 0; int index = 0;
if(!_flannIndex->isBuilt()) if(!_flannIndex->isBuilt())
{ {
UDEBUG("Building FLANN index..."); UDEBUG("Building FLANN index... (strategy=%s, byteToFloat=%s, useDistanceL1=%s, rebalancingFactor=%f)",
switch(_strategy) nnStrategyName(_strategy).c_str(), _byteToFloat?"true":"false", useDistanceL1_?"true":"false", _rebalancingFactor);
{ _flannIndex->buildIndex(
case kNNFlannNaive: _strategy == kNNFlannNaive ? FlannIndex::FLANN_INDEX_LINEAR:
_flannIndex->buildLinearIndex(descriptor, useDistanceL1_, _rebalancingFactor); _strategy == kNNFlannLSH ? FlannIndex::FLANN_INDEX_LSH:
break; FlannIndex::FLANN_INDEX_KDTREE, // kNNFlannKdTree
case kNNFlannKdTree: descriptor, useDistanceL1_, _rebalancingFactor);
UASSERT_MSG(descriptor.type() == CV_32F, "To use KdTree dictionary, float descriptors are required!");
_flannIndex->buildKDTreeIndex(descriptor, KDTREE_SIZE, useDistanceL1_, _rebalancingFactor);
break;
case kNNFlannLSH:
UASSERT_MSG(descriptor.type() == CV_8U, "To use LSH dictionary, binary descriptors are required!");
_flannIndex->buildLSHIndex(descriptor, 12, 20, 2, _rebalancingFactor);
break;
default:
UFATAL("Not supposed to be here!");
break;
}
UDEBUG("Building FLANN index... done!"); UDEBUG("Building FLANN index... done!");
} }
else else
@@ -561,7 +565,7 @@ void VWDictionary::update()
inserted = _mapIdIndex.insert(std::pair<int, int>(w->id(), index)); inserted = _mapIdIndex.insert(std::pair<int, int>(w->id(), index));
UASSERT(inserted.second); UASSERT(inserted.second);
} }
ULOGGER_DEBUG("Incremental FLANN: Inserting %d words... done!", (int)_notIndexedWords.size()); ULOGGER_DEBUG("Incremental FLANN: Inserting %d words... done! (in %f s)", (int)_notIndexedWords.size(), timer.ticks());
} }
} }
else if(_strategy >= kNNBruteForce && else if(_strategy >= kNNBruteForce &&
@@ -657,23 +661,13 @@ void VWDictionary::update()
ULOGGER_DEBUG("_mapIndexId.size() = %d, words.size()=%d, _dim=%d",_mapIndexId.size(), _visualWords.size(), dim); ULOGGER_DEBUG("_mapIndexId.size() = %d, words.size()=%d, _dim=%d",_mapIndexId.size(), _visualWords.size(), dim);
ULOGGER_DEBUG("copying data = %f s", timer.ticks()); ULOGGER_DEBUG("copying data = %f s", timer.ticks());
switch(_strategy) _flannIndex->buildIndex(
{ _strategy == kNNFlannNaive ? FlannIndex::FLANN_INDEX_LINEAR:
case kNNFlannNaive: _strategy == kNNFlannLSH ? FlannIndex::FLANN_INDEX_LSH:
_flannIndex->buildLinearIndex(_dataTree, useDistanceL1_, _incrementalDictionary&&_incrementalFlann?_rebalancingFactor:1); FlannIndex::FLANN_INDEX_KDTREE, // kNNFlannKdTree
break; _dataTree,
case kNNFlannKdTree: useDistanceL1_,
UASSERT_MSG(type == CV_32F, "To use KdTree dictionary, float descriptors are required!"); _incrementalDictionary&&_incrementalFlann?_rebalancingFactor:1);
_flannIndex->buildKDTreeIndex(_dataTree, KDTREE_SIZE, useDistanceL1_, _incrementalDictionary&&_incrementalFlann?_rebalancingFactor:1);
break;
case kNNFlannLSH:
UASSERT_MSG(type == CV_8U, "To use LSH dictionary, binary descriptors are required!");
_flannIndex->buildLSHIndex(_dataTree, 12, 20, 2, _incrementalDictionary&&_incrementalFlann?_rebalancingFactor:1);
break;
default:
break;
}
ULOGGER_DEBUG("Time to create kd tree = %f s", timer.ticks()); ULOGGER_DEBUG("Time to create kd tree = %f s", timer.ticks());
} }
} }
@@ -689,6 +683,146 @@ void VWDictionary::update()
UDEBUG(""); UDEBUG("");
} }
std::vector<unsigned char> VWDictionary::serializeIndex() const
{
if(_strategy >= kNNBruteForce) {
UINFO("Not flann strategy, ignoring serialization...");
return std::vector<unsigned char>();
}
if(!_flannIndex->isBuilt() || !_removedIndexedWords.empty() || !_notIndexedWords.empty() || _visualWords.empty()) {
UWARN("Flann index is not buit, or there are words not indexed, cannot do serialization.");
return std::vector<unsigned char>();
}
return _flannIndex->serializeIndex(_serializeWithChecksum);
}
void VWDictionary::deserializeIndex(const std::vector<unsigned char> & data)
{
deserializeIndex(data.data(), data.size());
}
void VWDictionary::deserializeIndex(const unsigned char * data, size_t size)
{
if(data== NULL || size == 0)
{
UWARN("Trying to deserialize empty data, aborting.");
return;
}
UDEBUG("Loading flann index... (data size=%ld bytes)", size);
if(_strategy >= kNNBruteForce) {
//ignore
return;
}
if(_flannIndex->isBuilt()) {
UERROR("Flann index is already built, cannot deserialize data!");
return;
}
if(_visualWords.empty()) {
UERROR("Descriptors should be added before deserializing flann index! See VWDictionary::addWord()");
return;
}
if(!(_removedIndexedWords.empty() && _visualWords.size() == _notIndexedWords.size())) {
UERROR("State of dictionary not as expected before deserializing. (removed words=%ld, words=%ld, not indexed=%ld)",
_removedIndexedWords.size(), _visualWords.size(), _notIndexedWords.size());
return;
}
std::map<int, int> mapIndexId;
std::map<int, int> mapIdIndex;
cv::Mat dataTree;
UTimer timer;
timer.start();
int dim = _visualWords.begin()->second->getDescriptor().cols;
int type;
if(_visualWords.begin()->second->getDescriptor().type() == CV_8U)
{
useDistanceL1_ = true;
if(_strategy == kNNFlannKdTree)
{
type = CV_32F;
if(!_byteToFloat)
{
dim *= 8;
}
}
else
{
type = _visualWords.begin()->second->getDescriptor().type();
}
}
else
{
type = _visualWords.begin()->second->getDescriptor().type();
}
UASSERT(type == CV_32F || type == CV_8U);
UASSERT(dim > 0);
// Create the data matrix
dataTree = cv::Mat(_visualWords.size(), dim, type); // SURF descriptors are CV_32F
std::map<int, VisualWord*>::const_iterator iter = _visualWords.begin();
for(unsigned int i=0; i < _visualWords.size(); ++i, ++iter)
{
cv::Mat descriptor;
if(iter->second->getDescriptor().type() == CV_8U)
{
if(_strategy == kNNFlannKdTree)
{
descriptor = convertBinTo32F(iter->second->getDescriptor(), _byteToFloat);
}
else
{
descriptor = iter->second->getDescriptor();
}
}
else
{
descriptor = iter->second->getDescriptor();
}
UASSERT_MSG(descriptor.type() == type, uFormat("%d vs %d", descriptor.type(), type).c_str());
UASSERT_MSG(descriptor.cols == dim, uFormat("%d vs %d", descriptor.cols, dim).c_str());
descriptor.copyTo(dataTree.row(i));
mapIndexId.insert(mapIndexId.end(), std::pair<int, int>(i, iter->second->id()));
mapIdIndex.insert(mapIdIndex.end(), std::pair<int, int>(iter->second->id(), i));
}
ULOGGER_DEBUG("mapIndexId.size() = %d, words.size()=%d, dim=%d", mapIndexId.size(), _visualWords.size(), dim);
ULOGGER_DEBUG("copying data = %f s", timer.ticks());
std::string errorMsg;
if(_flannIndex->loadIndex(
data,
size,
_strategy == kNNFlannNaive ? FlannIndex::FLANN_INDEX_LINEAR:
_strategy == kNNFlannLSH ? FlannIndex::FLANN_INDEX_LSH:
FlannIndex::FLANN_INDEX_KDTREE,
dataTree,
useDistanceL1_,
_incrementalDictionary && _incrementalFlann ? _rebalancingFactor:1,
&errorMsg))
{
_mapIndexId = mapIndexId;
_mapIdIndex = mapIdIndex;
_dataTree = dataTree;
_notIndexedWords.clear();
_modified = false;
}
else {
UWARN("Failed deserializing flann index data (error: %s), the index will be rebuilt on next update.", errorMsg.c_str());
_flannIndex->release(); // reset to initial state
}
ULOGGER_DEBUG("Time to load flann index = %f s", timer.ticks());
}
void VWDictionary::clear(bool printWarningsIfNotEmpty) void VWDictionary::clear(bool printWarningsIfNotEmpty)
{ {
ULOGGER_DEBUG(""); ULOGGER_DEBUG("");
@@ -718,6 +852,7 @@ void VWDictionary::clear(bool printWarningsIfNotEmpty)
_unusedWords.clear(); _unusedWords.clear();
_flannIndex->release(); _flannIndex->release();
useDistanceL1_ = false; useDistanceL1_ = false;
_modified = true;
} }
int VWDictionary::getNextId() int VWDictionary::getNextId()
@@ -784,14 +919,21 @@ std::list<int> VWDictionary::addNewWords(
type = _visualWords.begin()->second->getDescriptor().type(); type = _visualWords.begin()->second->getDescriptor().type();
UASSERT(type == CV_32F || type == CV_8U); UASSERT(type == CV_32F || type == CV_8U);
} }
static std::string moreInfo = uFormat(
"This could happen if the computer doesn't have access to same "
"feature detectors than when the database was created. This could "
"also happen if we enabled \"%s\" but the first frame received "
"was empty, thus features were re-extracted with a different detector "
"than the one used by the odometry.",
Parameters::kMemUseOdomFeatures().c_str());
if(dim && dim != descriptorsIn.cols) if(dim && dim != descriptorsIn.cols)
{ {
UERROR("Descriptors (size=%d) are not the same size as already added words in dictionary(size=%d)", descriptorsIn.cols, dim); UERROR("Descriptors (size=%d) are not the same size as already added words in dictionary (size=%d). %s", descriptorsIn.cols, dim, moreInfo.c_str());
return wordIds; return wordIds;
} }
if(type>=0 && type != descriptorsIn.type()) if(type>=0 && type != descriptorsIn.type())
{ {
UERROR("Descriptors (type=%d) are not the same type as already added words in dictionary(type=%d)", descriptorsIn.type(), type); UERROR("Descriptors (type=%d) are not the same type as already added words in dictionary (type=%d). %s", descriptorsIn.type(), type, moreInfo.c_str());
return wordIds; return wordIds;
} }
@@ -1394,15 +1536,15 @@ void VWDictionary::addWord(VisualWord * vw)
{ {
if(vw) if(vw)
{ {
_visualWords.insert(std::pair<int, VisualWord *>(vw->id(), vw)); _visualWords.insert(_visualWords.end(), std::pair<int, VisualWord *>(vw->id(), vw));
_notIndexedWords.insert(vw->id()); _notIndexedWords.insert(_notIndexedWords.end(), vw->id());
if(vw->getReferences().size()) if(vw->getReferences().size())
{ {
_totalActiveReferences += uSum(uValues(vw->getReferences())); _totalActiveReferences += uSum(uValues(vw->getReferences()));
} }
else else
{ {
_unusedWords.insert(std::pair<int, VisualWord *>(vw->id(), vw)); _unusedWords.insert(_unusedWords.end(), std::pair<int, VisualWord *>(vw->id(), vw));
} }
if(_lastWordId < vw->id()) if(_lastWordId < vw->id())
{ {
+1 -1
View File
@@ -57,7 +57,7 @@ void VisualWord::addRef(int signatureId)
} }
else else
{ {
_references.insert(std::pair<int, int>(signatureId, 1)); _references.insert(_references.end(), std::pair<int, int>(signatureId, 1));
} }
++_totalReferences; ++_totalReferences;
} }
+4 -3
View File
@@ -370,9 +370,10 @@ bool CameraDepthAI::init(const std::string & calibrationFolder, const std::strin
matrix[2][0], matrix[2][1], matrix[2][2]); matrix[2][0], matrix[2][1], matrix[2][2]);
std::vector<float> coeffs = calibHandler.getDistortionCoefficients(cameraId); std::vector<float> coeffs = calibHandler.getDistortionCoefficients(cameraId);
if(calibHandler.getDistortionModel(cameraId) == dai::CameraModel::Perspective) if(calibHandler.getDistortionModel(cameraId) == dai::CameraModel::Perspective) {
distCoeffs = (cv::Mat_<double>(1,8) << coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4], coeffs[5], coeffs[6], coeffs[7]); UASSERT(coeffs.size()>=14);
distCoeffs = (cv::Mat_<double>(1,14) << coeffs[0], coeffs[1], coeffs[2], coeffs[3], coeffs[4], coeffs[5], coeffs[6], coeffs[7], coeffs[8], coeffs[9], coeffs[10], coeffs[11], coeffs[12], coeffs[13]);
}
if(alphaScaling_>-1.0f) if(alphaScaling_>-1.0f)
newCameraMatrix = cv::getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, targetSize_, alphaScaling_); newCameraMatrix = cv::getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, targetSize_, alphaScaling_);
else else
+3 -3
View File
@@ -523,19 +523,19 @@ bool CameraImages::readPoses(
UERROR("Cannot read pose file \"%s\".", filePath.c_str()); UERROR("Cannot read pose file \"%s\".", filePath.c_str());
return false; return false;
} }
else if((format != 1 && format != 10 && format != 5 && format != 6 && format != 7 && format != 9) && poses.size() != this->imagesCount()) else if((format != 1 && format != 10 && format != 12 && format != 5 && format != 6 && format != 7 && format != 9) && poses.size() != this->imagesCount())
{ {
UERROR("The pose count is not the same as the images (%d vs %d)! Please remove " UERROR("The pose count is not the same as the images (%d vs %d)! Please remove "
"the pose file path if you don't want to use it (current file path=%s).", "the pose file path if you don't want to use it (current file path=%s).",
(int)poses.size(), this->imagesCount(), filePath.c_str()); (int)poses.size(), this->imagesCount(), filePath.c_str());
return false; return false;
} }
else if((format == 1 || format == 10 || format == 5 || format == 6 || format == 7 || format == 9) && (inOutStamps.empty() && stamps.size()!=poses.size())) else if((format == 1 || format == 10 || format == 12 || format == 5 || format == 6 || format == 7 || format == 9) && (inOutStamps.empty() && stamps.size()!=poses.size()))
{ {
UERROR("When using RGBD-SLAM, GPS, MALAGA, ST LUCIA and EuRoC MAV formats, images must have timestamps!"); UERROR("When using RGBD-SLAM, GPS, MALAGA, ST LUCIA and EuRoC MAV formats, images must have timestamps!");
return false; return false;
} }
else if(format == 1 || format == 10 || format == 5 || format == 6 || format == 7 || format == 9) else if(format == 1 || format == 10 || format == 12 || format == 5 || format == 6 || format == 7 || format == 9)
{ {
UDEBUG(""); UDEBUG("");
//Match ground truth values with images //Match ground truth values with images
+742
View File
@@ -0,0 +1,742 @@
/*
Copyright (c) 2010-2025, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <rtabmap/core/camera/CameraOrbbecSDK.h>
#include <rtabmap/core/util2d.h>
#include <rtabmap/utilite/UTimer.h>
#include <rtabmap/utilite/UStl.h>
#include <rtabmap/utilite/UThread.h>
#ifdef RTABMAP_ORBBEC_SDK
#include <libobsensor/ObSensor.hpp>
#endif
namespace rtabmap
{
#ifdef RTABMAP_ORBBEC_SDK
Transform obToRtabmap(const OBExtrinsic & t)
{
return Transform(t.rot[0], t.rot[1], t.rot[2], t.trans[0]/1000.0f,
t.rot[3], t.rot[4], t.rot[5], t.trans[1]/1000.0f,
t.rot[6], t.rot[7], t.rot[8], t.trans[2]/1000.0f);
}
cv::Mat obColorFrameToCv(const ob::VideoFrame & videoFrame)
{
cv::Mat rgb;
switch(videoFrame.getFormat()) {
case OB_FORMAT_MJPG: {
cv::Mat rawMat(1, videoFrame.getDataSize(), CV_8UC1, videoFrame.getData());
rgb = cv::imdecode(rawMat, 1);
} break;
case OB_FORMAT_NV21: {
cv::Mat rawMat(videoFrame.getHeight() * 3 / 2, videoFrame.getWidth(), CV_8UC1, videoFrame.getData());
cv::cvtColor(rawMat, rgb, cv::COLOR_YUV2BGR_NV21);
} break;
case OB_FORMAT_YUYV:
case OB_FORMAT_YUY2: {
cv::Mat rawMat(videoFrame.getHeight(), videoFrame.getWidth(), CV_8UC2, videoFrame.getData());
cv::cvtColor(rawMat, rgb, cv::COLOR_YUV2BGR_YUY2);
} break;
case OB_FORMAT_BGR: {
cv::Mat rawMat(videoFrame.getHeight(), videoFrame.getWidth(), CV_8UC3, videoFrame.getData());
cv::cvtColor(rawMat, rgb, cv::COLOR_BGR2RGB);
} break;
case OB_FORMAT_RGB: {
cv::Mat rawMat(videoFrame.getHeight(), videoFrame.getWidth(), CV_8UC3, videoFrame.getData());
cv::cvtColor(rawMat, rgb, cv::COLOR_RGB2BGR);
} break;
case OB_FORMAT_RGBA: {
cv::Mat rawMat(videoFrame.getHeight(), videoFrame.getWidth(), CV_8UC4, videoFrame.getData());
cv::cvtColor(rawMat, rgb, cv::COLOR_RGBA2BGR);
} break;
case OB_FORMAT_BGRA: {
cv::Mat rawMat(videoFrame.getHeight(), videoFrame.getWidth(), CV_8UC4, videoFrame.getData());
cv::cvtColor(rawMat, rgb, cv::COLOR_BGRA2RGB);
} break;
case OB_FORMAT_UYVY: {
cv::Mat rawMat(videoFrame.getHeight(), videoFrame.getWidth(), CV_8UC2, videoFrame.getData());
cv::cvtColor(rawMat, rgb, cv::COLOR_YUV2BGR_UYVY);
} break;
case OB_FORMAT_I420: {
cv::Mat rawMat(videoFrame.getHeight() * 3 / 2, videoFrame.getWidth(), CV_8UC1, videoFrame.getData());
cv::cvtColor(rawMat, rgb, cv::COLOR_YUV2BGR_I420);
} break;
case OB_FORMAT_Y8: {
rgb = cv::Mat(videoFrame.getHeight(), videoFrame.getWidth(), CV_8UC1, videoFrame.getData()).clone();
} break;
case OB_FORMAT_Y16: {
cv::Mat rawMat(videoFrame.getHeight(), videoFrame.getWidth(), CV_16UC1, videoFrame.getData());
rawMat.convertTo(rgb, CV_8UC1, 255.0 / 65535.0);
} break;
default:
break;
}
return rgb;
}
cv::Mat obDepthFrameToCv(const ob::DepthFrame & depthFrame)
{
cv::Mat depth;
if(depthFrame.getFormat() == OB_FORMAT_Y16 || depthFrame.getFormat() == OB_FORMAT_Z16 || depthFrame.getFormat() == OB_FORMAT_Y12C4) {
cv::Mat rawMat = cv::Mat(depthFrame.getHeight(), depthFrame.getWidth(), CV_16UC1, depthFrame.getData());
float scale = depthFrame.getValueScale() / 1000.0f;
rawMat.convertTo(depth, CV_32F, scale);
}
return depth;
}
cv::Mat obIntrinsicToK(const OBCameraIntrinsic & intrinsics)
{
cv::Mat K = cv::Mat::eye(3,3,CV_64FC1);
K.at<double>(0,0) = intrinsics.fx;
K.at<double>(1,1) = intrinsics.fy;
K.at<double>(0,2) = intrinsics.cx;
K.at<double>(1,2) = intrinsics.cy;
return K;
}
cv::Mat obIntrinsicToP(const OBCameraIntrinsic & intrinsics)
{
cv::Mat P = cv::Mat::eye(3,4,CV_64FC1);
obIntrinsicToK(intrinsics).copyTo(P.colRange(0,3));
return P;
}
cv::Mat obDistortionToD(const OBCameraDistortion & distortion)
{
cv::Mat D = cv::Mat(1,8,CV_64FC1);
D.at<double>(0,0) = distortion.k1;
D.at<double>(0,1) = distortion.k2;
D.at<double>(0,2) = distortion.p1;
D.at<double>(0,3) = distortion.p2;
D.at<double>(0,4) = distortion.k3;
D.at<double>(0,5) = distortion.k4;
D.at<double>(0,6) = distortion.k5;
D.at<double>(0,7) = distortion.k6;
if(distortion.k4 == 0 && distortion.k5 == 0 && distortion.k6 == 0)
{
D = D.colRange(0,5);
}
return D;
}
#endif
bool CameraOrbbecSDK::available()
{
#ifdef RTABMAP_ORBBEC_SDK
return true;
#else
return false;
#endif
}
CameraOrbbecSDK::CameraOrbbecSDK(
std::string deviceId,
unsigned int colorWidth,
unsigned int colorHeight,
unsigned int depthWidth,
unsigned int depthHeight,
float imageRate,
const Transform & localTransform) :
Camera(imageRate, localTransform)
#ifdef RTABMAP_ORBBEC_SDK
, deviceId_(deviceId),
colorWidth_(colorWidth),
colorHeight_(colorHeight),
depthWidth_(depthWidth),
depthHeight_(depthHeight),
pipeline_(nullptr),
imuPipeline_(nullptr),
alignFilter_(nullptr),
imuLocalTransformInitialized_(false),
lastAccStamp_(0),
lastImageStamp_(0),
globalTimestampAvailable_(false),
rectifyColor_(false),
convertDepthToMM_(true),
imuPublished_(true)
#endif
{
}
CameraOrbbecSDK::~CameraOrbbecSDK()
{
#ifdef RTABMAP_ORBBEC_SDK
this->close();
#endif
}
void CameraOrbbecSDK::close()
{
#ifdef RTABMAP_ORBBEC_SDK
if(imuPipeline_) {
imuPipeline_->stop();
delete imuPipeline_;
imuPipeline_=nullptr;
}
if(pipeline_) {
pipeline_->stop();
delete pipeline_;
pipeline_=nullptr;
}
delete alignFilter_;
alignFilter_ = nullptr;
imuLocalTransform_ = Transform();
imuLocalTransformInitialized_ = false;
lastAccStamp_ = 0;
lastImageStamp_ = 0;
globalTimestampAvailable_ = false;
model_ = CameraModel();
imuBuffer_.clear();
#endif
}
bool CameraOrbbecSDK::init(const std::string & calibrationFolder, const std::string & cameraName)
{
#ifdef RTABMAP_ORBBEC_SDK
this->close();
std::shared_ptr<ob::Device> device;
ob::Context context;
auto devices = context.queryDeviceList();
UINFO("%d device(s) found", devices->getCount());
for(uint32_t i=0; i<devices->getCount(); ++i)
{
auto currentDevice = devices->getDevice(i);
auto info = currentDevice->getDeviceInfo();
if(deviceId_.find('-') != std::string::npos)
{
// UID
if(deviceId_.compare(info->getUid()) == 0) {
device = currentDevice;
}
}
else if(uSplitNumChar(deviceId_).size() > 1)
{
// Serial
if(deviceId_.compare(info->getSerialNumber()) == 0) {
device = currentDevice;
}
}
else if((deviceId_.empty() && i==0) ||
(!deviceId_.empty() && uStr2Int(deviceId_) == (int)i)) {
// Index
device = currentDevice;
}
std::string type = "Unknown";
switch(info->getDeviceType())
{
case OB_STRUCTURED_LIGHT_MONOCULAR_CAMERA:
type = "Structured Light Monocular Camera";
break;
case OB_STRUCTURED_LIGHT_BINOCULAR_CAMERA:
type = "Structured Light Binocular Camera";
break;
case OB_TOF_CAMERA:
type = "TOF Camera";
break;
default:
break;
}
UINFO("Device %ld:", i);
UINFO(" Name: %s", info->getName());
UINFO(" Type: %s", type.c_str());
UINFO(" Serial: %s", info->getSerialNumber());
UINFO(" UID: %s", info->getUid());
UINFO(" Chip: %s", info->getAsicName());
UINFO(" Hardware version: %s", info->getHardwareVersion());
UINFO(" Firmware version: %s", info->getFirmwareVersion());
}
if(device.get() == nullptr) {
if(deviceId_.empty()) {
UERROR( "Could not find any orbbec compatible devices! Verify that the "
"camera is correctly connected and the udev rules are installed.");
}
else {
UERROR("Could not find an orbbec device with ID \"%s\"! Verify that the "
"camera is correctly connected and the udev rules are installed. "
"Unset the ID to choose the first camera found.");
}
return false;
}
bool hasGyro = false;
bool hasAccel = false;
auto sensors = device->getSensorList();
if(device->isGlobalTimestampSupported())
{
UINFO("Global (host time sync) timestamp is supported.");
device->enableGlobalTimestamp(true);
globalTimestampAvailable_ = true;
}
else
{
UWARN("Global (host time sync) timestamp is not supported! We will use device timestamp, so the camera frames won't be synchronizable with other sensors.");
}
uint32_t maxColorFps = 0;
uint32_t maxDepthFps = 0;
for(uint32_t i=0; i<sensors->getCount(); ++i)
{
if(sensors->getSensorType(i) == OB_SENSOR_GYRO)
{
hasGyro = true;
}
if(sensors->getSensorType(i) == OB_SENSOR_ACCEL)
{
hasAccel = true;
}
if( sensors->getSensorType(i) == OB_SENSOR_DEPTH ||
sensors->getSensorType(i) == OB_SENSOR_COLOR)
{
auto profiles = sensors->getSensor(i)->getStreamProfileList();
UINFO("Supported %s profiles:", sensors->getSensorType(i) == OB_SENSOR_DEPTH?"depth":"color");
for(uint32_t j=0; j<profiles->getCount(); ++j)
{
auto profile = profiles->getProfile(j)->as<ob::VideoStreamProfile>();
UINFO("Resolution: %ldx%ld, FPS: %ld, Format: %d",
profile->getWidth(), profile->getHeight(), profile->getFps(), profile->getFormat(), j==0?" (default)":"");
// Get maximum frame rate based on resolution selected
if(sensors->getSensorType(i) == OB_SENSOR_DEPTH) {
if( profile->getFps() > maxDepthFps &&
depthWidth_ == profile->getWidth() &&
depthHeight_ == profile->getHeight())
{
maxDepthFps = profile->getFps();
}
}
else
{
if( profile->getFps() > maxColorFps &&
colorWidth_ == profile->getWidth() &&
colorHeight_ == profile->getHeight())
{
maxColorFps = profile->getFps();
}
}
}
}
}
// Note that for TOF camera, we want maximum frame rate to better
// sync rgb and depth. For stereo cameras, use the specified frame rate.
if(this->getImageRate()!=0.0f && device->getDeviceInfo()->getDeviceType() != OB_TOF_CAMERA)
{
maxColorFps = maxDepthFps = (unsigned int)this->getImageRate();
this->setImageRate(0);
}
std::shared_ptr<ob::Config> imuConfig;
if(imuPublished_)
{
if(hasGyro && hasAccel)
{
imuPipeline_ = new ob::Pipeline(device);
imuConfig = std::make_shared<ob::Config>();
imuConfig->enableGyroStream();
imuConfig->enableAccelStream();
try {
UINFO("Starting imu pipeline");
imuPipeline_->start(imuConfig, [&](std::shared_ptr<ob::FrameSet> frameSet) {
if(frameSet->getCount() != 2)
{
return;
}
if(!imuLocalTransformInitialized_)
{
return;
}
UASSERT(frameSet->getFrame(OB_FRAME_ACCEL) != nullptr &&
frameSet->getFrame(OB_FRAME_GYRO) != nullptr);
auto accel = frameSet->getFrame(OB_FRAME_ACCEL)->as<const ob::AccelFrame>();
auto gyro = frameSet->getFrame(OB_FRAME_GYRO)->as<const ob::GyroFrame>();
uint64_t accelStampUs = globalTimestampAvailable_?accel->getGlobalTimeStampUs():accel->getTimeStampUs();
uint64_t gyroStampUs = globalTimestampAvailable_?gyro->getGlobalTimeStampUs():gyro->getTimeStampUs();
if(accelStampUs != gyroStampUs)
{
UWARN("Received accel and gyro frames with different timestamps (%llu vs %llu), skipping.",
accelStampUs, gyroStampUs);
return;
}
double accStamp = double(accelStampUs)/1e6;
if(accelStampUs <= lastAccStamp_) {
return;
}
lastAccStamp_ = accelStampUs;
auto accelValue = accel->getValue();
auto gyroValue = gyro->getValue();
if(isInterIMUPublishing())
{
IMU imu(cv::Vec3f(gyroValue.x, gyroValue.y, gyroValue.z), cv::Mat::eye(3,3,CV_64FC1),
cv::Vec3f(accelValue.x, accelValue.y, accelValue.z), cv::Mat::eye(3,3,CV_64FC1),
imuLocalTransform_);
this->postInterIMU(imu, accStamp);
}
else
{
UScopeMutex lock(imuMutex_);
imuBuffer_.emplace_hint(imuBuffer_.end(), accStamp, cv::Vec6f(gyroValue.x, gyroValue.y, gyroValue.z, accelValue.x, accelValue.y, accelValue.z));
if(imuBuffer_.size()>1000) {
imuBuffer_.erase(imuBuffer_.begin());
}
}
});
}
catch(const ob::Error & e) {
UERROR("Unexpected error when configuring IMU stream: %s", e.what());
}
}
else
{
UWARN("IMU option is enabled but the camera doesn't have an IMU, ignoring.");
}
}
pipeline_ = new ob::Pipeline(device);
auto config = std::make_shared<ob::Config>();
// Set highest frame rate possible to reduce color/depth sync diff
config->enableVideoStream(OB_STREAM_COLOR, colorWidth_, colorHeight_, maxColorFps, OB_FORMAT_RGB);
config->enableVideoStream(OB_STREAM_DEPTH, depthWidth_, depthHeight_, maxDepthFps, OB_FORMAT_Y16);
UINFO("Using color profile: %dx%d", colorWidth_, colorHeight_);
UINFO("Using depth profile: %dx%d", depthWidth_, depthHeight_);
config->setFrameAggregateOutputMode(OB_FRAME_AGGREGATE_OUTPUT_ALL_TYPE_FRAME_REQUIRE);
config->setAlignMode(ALIGN_DISABLE);
config->setDepthScaleRequire(true);
pipeline_->enableFrameSync();
try {
UINFO("Starting camera pipeline");
pipeline_->start(config);
auto enabledStreams = pipeline_->getConfig()->getEnabledStreamProfileList();
if(imuPipeline_ != nullptr) {
for(uint32_t i=0; i<enabledStreams->getCount() && !imuLocalTransformInitialized_; ++i)
{
if(enabledStreams->getProfile(i)->getType() == OB_STREAM_COLOR)
{
auto enabledImuStreams = imuPipeline_->getConfig()->getEnabledStreamProfileList();
for(uint32_t j=0; j<enabledImuStreams->getCount(); ++j)
{
if(enabledImuStreams->getProfile(j)->getType() == OB_STREAM_ACCEL)
{
auto extrinsics = enabledStreams->getProfile(i)->as<ob::VideoStreamProfile>()->getExtrinsicTo(enabledImuStreams->getProfile(j)->as<ob::AccelStreamProfile>());
// base -> color -> imu
imuLocalTransform_ = this->getLocalTransform() * obToRtabmap(extrinsics);
UINFO("IMU local transform: %s", imuLocalTransform_.prettyPrint().c_str());
imuLocalTransformInitialized_ = true;
break;
}
}
}
}
}
std::shared_ptr<ob::StreamProfile> colorProfile;
std::shared_ptr<ob::StreamProfile> depthProfile;
for(uint32_t i=0; i<enabledStreams->getCount(); ++i)
{
if(enabledStreams->getProfile(i)->getType() == OB_STREAM_COLOR) {
colorProfile = enabledStreams->getProfile(i);
}
else if(enabledStreams->getProfile(i)->getType() == OB_STREAM_DEPTH) {
depthProfile = enabledStreams->getProfile(i);
}
}
bool currentSelectionSupportsHwD2C = false;
auto hwD2CSupportedDepthStreamProfiles = pipeline_->getD2CDepthProfileList(colorProfile, ALIGN_D2C_HW_MODE);
if(hwD2CSupportedDepthStreamProfiles->count() == 0) {
UWARN("Current color profile selected doesn't support any hardware depth to color registration. Software registration is done instead.");
}
else
{
auto depthVsp = depthProfile->as<ob::VideoStreamProfile>();
auto count = hwD2CSupportedDepthStreamProfiles->getCount();
for(uint32_t i = 0; i < count; i++) {
auto vsp = hwD2CSupportedDepthStreamProfiles->getProfile(i)->as<ob::VideoStreamProfile>();
UINFO("Supported depth to color format: Resolution: %ldx%ld, FPS: %ld, Format: %d", vsp->getWidth(), vsp->getHeight(), vsp->getFps(), vsp->getFormat(), i==0?" (default)":"");
if(vsp->getWidth() == depthVsp->getWidth() && vsp->getHeight() == depthVsp->getHeight() && vsp->getFormat() == depthVsp->getFormat()
&& vsp->getFps() == depthVsp->getFps()) {
currentSelectionSupportsHwD2C = true;
}
}
}
if(!currentSelectionSupportsHwD2C) {
UWARN("Hardware depth to color registration cannot be done with the selected color and depth profiles. "
"Software registration is done instead, so more CPU will be needed on the host computer. "
"Set logger level to info to see comptible depth formats for the selected color profile.");
alignFilter_ = new ob::Align(OB_STREAM_COLOR);
alignFilter_->setMatchTargetResolution(false);
}
else {
UINFO("Enabling hardware depth to color registration!");
config->setAlignMode(ALIGN_D2C_HW_MODE);
config->setDepthScaleRequire(false);
pipeline_->stop();
pipeline_->start(config);
}
}
catch(const ob::Error & e)
{
UERROR("Configuration not supported! Exception: %s", e.what());
UERROR("Supported formats:");
for(uint32_t i=0; i<sensors->getCount(); ++i)
{
if( sensors->getSensorType(i) == OB_SENSOR_DEPTH ||
sensors->getSensorType(i) == OB_SENSOR_COLOR)
{
auto profiles = sensors->getSensor(i)->getStreamProfileList();
for(uint32_t j=0; j<profiles->getCount(); ++j)
{
auto profile = profiles->getProfile(j)->as<ob::VideoStreamProfile>();
UERROR("%sResolution: %ldx%ld, FPS: %ld, Format: %d",
sensors->getSensorType(i) == OB_SENSOR_DEPTH?"Depth":"Color",
profile->getWidth(),
profile->getHeight(),
profile->getFps(),
profile->getFormat(),
j==0?" (default)":"");
}
}
}
return false;
}
return true;
#else
UERROR("CameraOrbbecSDK: RTAB-Map is not built with Orbbec SDK support!");
return false;
#endif
}
bool CameraOrbbecSDK::isCalibrated() const
{
return true;
}
std::string CameraOrbbecSDK::getSerial() const
{
#ifdef RTABMAP_ORBBEC_SDK
if(pipeline_) {
return pipeline_->getDevice()->getDeviceInfo()->getSerialNumber();
}
#endif
return "";
}
void CameraOrbbecSDK::enableColorRectification(bool enabled)
{
#ifdef RTABMAP_ORBBEC_SDK
rectifyColor_ = enabled;
#endif
}
void CameraOrbbecSDK::enableImu(bool enabled)
{
#ifdef RTABMAP_ORBBEC_SDK
imuPublished_ = enabled;
#endif
}
void CameraOrbbecSDK::enableDepthMM(bool enabled)
{
#ifdef RTABMAP_ORBBEC_SDK
convertDepthToMM_ = enabled;
#endif
}
SensorData CameraOrbbecSDK::captureImage(SensorCaptureInfo * info)
{
SensorData data;
#ifdef RTABMAP_ORBBEC_SDK
if(!pipeline_) {
UERROR("Camera is not initialized!");
return data;
}
auto frameset = pipeline_->waitForFrameset();
if(frameset == nullptr || frameset->getCount() == 0) {
UWARN("No frame received!");
return data;
}
if(frameset->getCount() != 2) {
UWARN("Received %s frames, expecting 2!", frameset->getCount());
return data;
}
if(alignFilter_ != nullptr) {
// Software depth to color registration
frameset = alignFilter_->process(frameset)->as<ob::FrameSet>();
UASSERT(frameset != nullptr);
}
auto colorFrame = frameset->getFrame(OB_FRAME_COLOR);
UASSERT(colorFrame != nullptr);
auto depthFrame = frameset->getFrame(OB_FRAME_DEPTH);
UASSERT(depthFrame != nullptr);
auto colorVideoFrame = colorFrame->as<const ob::VideoFrame>();
auto depthVideoFrame = depthFrame->as<const ob::DepthFrame>();
cv::Mat rgb = obColorFrameToCv(*colorVideoFrame);
cv::Mat depth = obDepthFrameToCv(*depthVideoFrame);
if(rgb.empty()) {
UERROR("Could not convert the color frame! Type=%d Format=%d", colorFrame->getType(), colorVideoFrame->getFormat());
}
else if(depth.empty()) {
UERROR("Could not convert the depth frame! Type=%d Format=%d", depthFrame->getType(), depthVideoFrame->getFormat());
}
else if(!rgb.empty() && !depth.empty())
{
if(!model_.isValidForProjection())
{
auto streamProfile = colorFrame->getStreamProfile();
auto videoStreamProfile = streamProfile->as<ob::VideoStreamProfile>();
auto intrinsics = videoStreamProfile->getIntrinsic();
model_ = CameraModel(
getSerial(),
cv::Size(intrinsics.width, intrinsics.height),
obIntrinsicToK(intrinsics),
obDistortionToD(videoStreamProfile->getDistortion()),
cv::Mat::eye(3,3,CV_64FC1),
obIntrinsicToP(intrinsics),
this->getLocalTransform());
if(rectifyColor_ && !model_.initRectificationMap()) {
UWARN("Could not initialize rectification map, color images won't be rectified.");
}
}
if(rectifyColor_ && model_.isValidForRectification())
{
rgb = model_.rectifyImage(rgb);
}
if(convertDepthToMM_)
{
depth = util2d::cvtDepthFromFloat(depth);
}
uint64_t colorStampUs = globalTimestampAvailable_?colorFrame->getGlobalTimeStampUs():colorFrame->getTimeStampUs();
uint64_t depthStampUs = globalTimestampAvailable_?depthFrame->getGlobalTimeStampUs():depthFrame->getTimeStampUs();
double colorStamp = double(colorStampUs) / 1e6;
double depthStamp = double(depthStampUs) / 1e6;
if(fabs(colorStamp - depthStamp) > 0.018) {
// The difference seems varying between 0 and 17 ms normally
UWARN("Large timestamp difference (%fs) between color (%f) and depth (%f) frames. "
"Depth registration would be wrong on fast motion.",
colorStamp - depthStamp, colorStamp, depthStamp);
}
uint64_t stampUs = colorStampUs < depthStampUs ? colorStampUs : depthStampUs;
#ifdef WIN32
// On Windows, there is an issue that timestamps are not populated by default without following instructions from:
// https://github.com/orbbec/OrbbecSDK_v2/blob/main/scripts/env_setup/obsensor_metadata_win10.md
// Detect if the consecutive timestamps are identical, then send error!
if (stampUs <= lastImageStamp_)
{
UERROR("We detected non-consecutive timestamps, make sure you applied the fix from https://github.com/orbbec/OrbbecSDK_v2/blob/main/scripts/env_setup/obsensor_metadata_win10.md .");
}
lastImageStamp_ = stampUs;
#endif
double stamp = double(stampUs)/1e6;
data = SensorData(rgb, depth, model_, this->getNextSeqID(), stamp);
if(imuPublished_ && !imuBuffer_.empty() && !this->isInterIMUPublishing())
{
cv::Vec6f imuVec;
std::map<double, cv::Vec6f>::const_iterator iterA, iterB;
imuMutex_.lock();
int maximumTries = 10;
while(imuBuffer_.rbegin()->first < stamp && maximumTries-- > 0)
{
imuMutex_.unlock();
uSleep(1);
imuMutex_.lock();
}
if(imuBuffer_.rbegin()->first < stamp)
{
UWARN("Could not get IMU data at request image stamp %f after waiting 10 ms, latest imu stamp is %f", stamp, imuBuffer_.rbegin()->first);
imuMutex_.unlock();
}
else
{
// Interpolate imu data on image stamp
iterB = imuBuffer_.lower_bound(stamp);
iterA = iterB;
if(iterA != imuBuffer_.begin())
iterA = --iterA;
if(iterA == iterB || stamp == iterB->first)
{
imuVec = iterB->second;
}
else if(stamp > iterA->first && stamp < iterB->first)
{
float t = (stamp-iterA->first) / (iterB->first-iterA->first);
imuVec = iterA->second + t*(iterB->second - iterA->second);
}
imuBuffer_.erase(imuBuffer_.begin(), iterB);
imuMutex_.unlock();
data.setIMU(IMU(cv::Vec3d(imuVec[0], imuVec[1], imuVec[2]), cv::Mat::eye(3, 3, CV_64FC1), cv::Vec3d(imuVec[3], imuVec[4], imuVec[5]), cv::Mat::eye(3, 3, CV_64FC1), imuLocalTransform_));
}
}
}
#else
UERROR("CameraOrbbecSDK: RTAB-Map is not built with Orbbec SDK support!");
#endif
return data;
}
} // namespace rtabmap
+35 -26
View File
@@ -712,14 +712,14 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
for (auto& profile : profiles) for (auto& profile : profiles)
{ {
auto video_profile = profile.as<rs2::video_stream_profile>(); auto video_profile = profile.as<rs2::video_stream_profile>();
UINFO("%s %d %d %d %d %s type=%d", rs2_format_to_string( UINFO("%s %d %d %d %d %s type=%d",
video_profile.format()), rs2_format_to_string(profile.format()),
video_profile.width(), video_profile.get()?video_profile.width():-1,
video_profile.height(), video_profile.get()?video_profile.height():-1,
video_profile.fps(), profile.fps(),
video_profile.stream_index(), profile.stream_index(),
video_profile.stream_name().c_str(), profile.stream_name().c_str(),
video_profile.stream_type()); profile.stream_type());
} }
} }
int pi = 0; int pi = 0;
@@ -728,7 +728,8 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
auto video_profile = profile.as<rs2::video_stream_profile>(); auto video_profile = profile.as<rs2::video_stream_profile>();
if(!stereo) if(!stereo)
{ {
if( (video_profile.width() == cameraWidth_ && if( (video_profile.get() &&
video_profile.width() == cameraWidth_ &&
video_profile.height() == cameraHeight_ && video_profile.height() == cameraHeight_ &&
video_profile.fps() == cameraFps_) || video_profile.fps() == cameraFps_) ||
(strcmp(sensors[i].get_info(RS2_CAMERA_INFO_NAME), "L500 Depth Sensor")==0 && (strcmp(sensors[i].get_info(RS2_CAMERA_INFO_NAME), "L500 Depth Sensor")==0 &&
@@ -778,7 +779,7 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
} }
} }
} }
else if(video_profile.format() == RS2_FORMAT_MOTION_XYZ32F || video_profile.format() == RS2_FORMAT_6DOF) else if(profile.format() == RS2_FORMAT_MOTION_XYZ32F || profile.format() == RS2_FORMAT_6DOF)
{ {
//D435i: //D435i:
//MOTION_XYZ32F 0 0 200 (gyro) //MOTION_XYZ32F 0 0 200 (gyro)
@@ -817,6 +818,7 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
{ {
//T265: //T265:
if(!dualMode_ && if(!dualMode_ &&
video_profile.get() &&
video_profile.format() == RS2_FORMAT_Y8 && video_profile.format() == RS2_FORMAT_Y8 &&
video_profile.width() == 848 && video_profile.width() == 848 &&
video_profile.height() == 800 && video_profile.height() == 800 &&
@@ -865,7 +867,7 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
} }
added = true; added = true;
} }
else if(video_profile.format() == RS2_FORMAT_MOTION_XYZ32F || video_profile.format() == RS2_FORMAT_6DOF) else if(profile.format() == RS2_FORMAT_MOTION_XYZ32F || profile.format() == RS2_FORMAT_6DOF)
{ {
//MOTION_XYZ32F 0 0 200 //MOTION_XYZ32F 0 0 200
//MOTION_XYZ32F 0 0 62 //MOTION_XYZ32F 0 0 62
@@ -884,14 +886,14 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
for (auto& profile : profiles) for (auto& profile : profiles)
{ {
auto video_profile = profile.as<rs2::video_stream_profile>(); auto video_profile = profile.as<rs2::video_stream_profile>();
UERROR("%s %d %d %d %d %s type=%d", rs2_format_to_string( UERROR("%s %d %d %d %d %s type=%d",
video_profile.format()), rs2_format_to_string(profile.format()),
video_profile.width(), video_profile.get()?video_profile.width():-1,
video_profile.height(), video_profile.get()?video_profile.height():-1,
video_profile.fps(), profile.fps(),
video_profile.stream_index(), profile.stream_index(),
video_profile.stream_name().c_str(), profile.stream_name().c_str(),
video_profile.stream_type()); profile.stream_type());
} }
return false; return false;
} }
@@ -1075,13 +1077,13 @@ bool CameraRealSense2::init(const std::string & calibrationFolder, const std::st
{ {
auto video_profile = profilesPerSensor[i][j].as<rs2::video_stream_profile>(); auto video_profile = profilesPerSensor[i][j].as<rs2::video_stream_profile>();
UINFO("Opening: %s %d %d %d %d %s type=%d", rs2_format_to_string( UINFO("Opening: %s %d %d %d %d %s type=%d", rs2_format_to_string(
video_profile.format()), profilesPerSensor[i][j].format()),
video_profile.width(), video_profile.get()?video_profile.width():-1,
video_profile.height(), video_profile.get()?video_profile.height():-1,
video_profile.fps(), profilesPerSensor[i][j].fps(),
video_profile.stream_index(), profilesPerSensor[i][j].stream_index(),
video_profile.stream_name().c_str(), profilesPerSensor[i][j].stream_name().c_str(),
video_profile.stream_type()); profilesPerSensor[i][j].stream_type());
} }
if(globalTimeSync_ && sensors[i].supports(rs2_option::RS2_OPTION_GLOBAL_TIME_ENABLED)) if(globalTimeSync_ && sensors[i].supports(rs2_option::RS2_OPTION_GLOBAL_TIME_ENABLED))
{ {
@@ -1525,6 +1527,13 @@ SensorData CameraRealSense2::captureImage(SensorCaptureInfo * info)
else else
{ {
UERROR("Missing frames (received %d, needed=%d)", (int)frameset.size(), desiredFramesetSize); UERROR("Missing frames (received %d, needed=%d)", (int)frameset.size(), desiredFramesetSize);
if(frameset.size()>0)
{
for (auto it = frameset.begin(); it != frameset.end(); ++it)
{
UERROR("Received frame only from %s", (*it).get_profile().stream_name().c_str());
}
}
} }
} }
catch(const std::exception& ex) catch(const std::exception& ex)
File diff suppressed because it is too large Load Diff
+2
View File
@@ -176,6 +176,8 @@ Transform OdometryF2F::computeTransform(
if(info && this->isInfoDataFilled()) if(info && this->isInfoDataFilled())
{ {
std::list<std::pair<int, std::pair<int, int> > > pairs; std::list<std::pair<int, std::pair<int, int> > > pairs;
UASSERT(tmpRefFrame.getWords().size() == tmpRefFrame.getWordsKpts().size());
UASSERT(newFrame.getWords().size() == newFrame.getWordsKpts().size());
EpipolarGeometry::findPairsUnique(tmpRefFrame.getWords(), newFrame.getWords(), pairs); EpipolarGeometry::findPairsUnique(tmpRefFrame.getWords(), newFrame.getWords(), pairs);
info->refCorners.resize(pairs.size()); info->refCorners.resize(pairs.size());
info->newCorners.resize(pairs.size()); info->newCorners.resize(pairs.size());
+5
View File
@@ -793,6 +793,7 @@ Transform OdometryF2M::computeTransform(
if(!lastFrameModels.empty()) if(!lastFrameModels.empty())
{ {
UASSERT(lastFrame_->getWordsKpts().size() == lastFrame_->getWords().size());
for(std::multimap<int, int>::const_iterator iter = lastFrame_->getWords().begin(); iter!=lastFrame_->getWords().end(); ++iter) for(std::multimap<int, int>::const_iterator iter = lastFrame_->getWords().begin(); iter!=lastFrame_->getWords().end(); ++iter)
{ {
const cv::Point3f & pt = lastFrame_->getWords3()[iter->second]; const cv::Point3f & pt = lastFrame_->getWords3()[iter->second];
@@ -1559,6 +1560,10 @@ Transform OdometryF2M::computeTransform(
{ {
info->reg = regInfo.copyWithoutData(); info->reg = regInfo.copyWithoutData();
} }
if(output.isNull())
{
info->reg.covariance = cv::Mat::eye(6,6,CV_64FC1)*9999.0; // Lost
}
} }
UINFO("Odom update time = %fs lost=%s features=%d inliers=%d/%d variance:lin=%f, ang=%f local_map=%d local_scan_map=%d", UINFO("Odom update time = %fs lost=%s features=%d inliers=%d/%d variance:lin=%f, ang=%f local_map=%d local_scan_map=%d",
+54 -21
View File
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "rtabmap/utilite/UTimer.h" #include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UStl.h" #include "rtabmap/utilite/UStl.h"
#include "rtabmap/utilite/UDirectory.h" #include "rtabmap/utilite/UDirectory.h"
#include "rtabmap/utilite/UFile.h"
#include <pcl/common/transforms.h> #include <pcl/common/transforms.h>
#include <opencv2/imgproc/types_c.h> #include <opencv2/imgproc/types_c.h>
#include <rtabmap/core/odometry/OdometryORBSLAM3.h> #include <rtabmap/core/odometry/OdometryORBSLAM3.h>
@@ -116,6 +117,13 @@ bool OdometryORBSLAM3::init(const rtabmap::CameraModel & model1, const rtabmap::
} }
//Load ORB Vocabulary //Load ORB Vocabulary
vocabularyPath = uReplaceChar(vocabularyPath, '~', UDirectory::homeDir()); vocabularyPath = uReplaceChar(vocabularyPath, '~', UDirectory::homeDir());
if(!UFile::exists(vocabularyPath))
{
UERROR("ORB_SLAM vocabulary path \"%s\" doesn't exist! (Parameter name=\"%s\")",
vocabularyPath.c_str(),
rtabmap::Parameters::kOdomORBSLAMVocPath().c_str());
return false;
}
UWARN("Loading ORB Vocabulary: \"%s\". This could take a while...", vocabularyPath.c_str()); UWARN("Loading ORB Vocabulary: \"%s\". This could take a while...", vocabularyPath.c_str());
// Create configuration file // Create configuration file
@@ -240,7 +248,7 @@ bool OdometryORBSLAM3::init(const rtabmap::CameraModel & model1, const rtabmap::
//# IMU Parameters TODO: hard-coded, not used //# IMU Parameters TODO: hard-coded, not used
//#-------------------------------------------------------------------------------------------- //#--------------------------------------------------------------------------------------------
// Transformation from camera 0 to body-frame (imu) // Transformation from camera 0 to body-frame (imu)
rtabmap::Transform camImuT = model1.localTransform()*imuLocalTransform_; rtabmap::Transform camImuT = imuLocalTransform_.inverse()*model1.localTransform();
ofs << "IMU.T_b_c1: !!opencv-matrix" << std::endl; ofs << "IMU.T_b_c1: !!opencv-matrix" << std::endl;
ofs << " rows: 4" << std::endl; ofs << " rows: 4" << std::endl;
ofs << " cols: 4" << std::endl; ofs << " cols: 4" << std::endl;
@@ -340,14 +348,16 @@ bool OdometryORBSLAM3::init(const rtabmap::CameraModel & model1, const rtabmap::
ofs.close(); ofs.close();
ORB_SLAM3::System::eSensor sensor =
stereo?(withIMU?ORB_SLAM3::System::IMU_STEREO:ORB_SLAM3::System::STEREO):
(withIMU?ORB_SLAM3::System::IMU_RGBD:ORB_SLAM3::System::RGBD);
UINFO("Initializing ORB_SLAM3 system with sensor %d...", (int)sensor);
orbslam_ = new ORB_SLAM3::System( orbslam_ = new ORB_SLAM3::System(
vocabularyPath, vocabularyPath,
configPath, configPath,
stereo && withIMU?ORB_SLAM3::System::IMU_STEREO: sensor,
stereo?ORB_SLAM3::System::STEREO:
withIMU?ORB_SLAM3::System::IMU_RGBD:
ORB_SLAM3::System::RGBD,
false); false);
UINFO("Initializing ORB_SLAM3 system with sensor %d... done!", (int)sensor);
return true; return true;
#else #else
UERROR("RTAB-Map is not built with ORB_SLAM support! Select another visual odometry approach."); UERROR("RTAB-Map is not built with ORB_SLAM support! Select another visual odometry approach.");
@@ -373,6 +383,7 @@ Transform OdometryORBSLAM3::computeTransform(
{ {
if(lastImuStamp_ == 0.0 || lastImuStamp_ < data.stamp()) if(lastImuStamp_ == 0.0 || lastImuStamp_ < data.stamp())
{ {
UDEBUG("Adding IMU %f", data.stamp());
orbslamImus_.push_back(ORB_SLAM3::IMU::Point( orbslamImus_.push_back(ORB_SLAM3::IMU::Point(
data.imu().linearAcceleration().val[0], data.imu().linearAcceleration().val[0],
data.imu().linearAcceleration().val[1], data.imu().linearAcceleration().val[1],
@@ -432,6 +443,7 @@ Transform OdometryORBSLAM3::computeTransform(
if(lastImageStamp_ == 0.0) if(lastImageStamp_ == 0.0)
{ {
lastImageStamp_ = data.stamp(); lastImageStamp_ = data.stamp();
UDEBUG("Waiting for another image to initialize...");
return t; return t;
} }
@@ -457,6 +469,7 @@ Transform OdometryORBSLAM3::computeTransform(
rightMono = cv::Mat(); rightMono = cv::Mat();
cv::cvtColor(data.imageRaw(), rightMono, CV_BGR2GRAY); cv::cvtColor(data.imageRaw(), rightMono, CV_BGR2GRAY);
} }
UDEBUG("Adding Stereo Frame %f", data.stamp());
Tcw = orbslam_->TrackStereo(leftMono, rightMono, data.stamp(), orbslamImus_); Tcw = orbslam_->TrackStereo(leftMono, rightMono, data.stamp(), orbslamImus_);
orbslamImus_.clear(); orbslamImus_.clear();
} }
@@ -472,15 +485,22 @@ Transform OdometryORBSLAM3::computeTransform(
{ {
depth = util2d::cvtDepthToFloat(data.depthRaw()); depth = util2d::cvtDepthToFloat(data.depthRaw());
} }
UDEBUG("Adding RGBD Frame %f", data.stamp());
Tcw = orbslam_->TrackRGBD(data.imageRaw(), depth, data.stamp(), orbslamImus_); Tcw = orbslam_->TrackRGBD(data.imageRaw(), depth, data.stamp(), orbslamImus_);
orbslamImus_.clear(); orbslamImus_.clear();
} }
Transform previousPoseInv = previousPose_.inverse(); Transform previousPoseInv = previousPose_.inverse();
std::vector<ORB_SLAM3::MapPoint*> mapPoints = orbslam_->GetTrackedMapPoints(); std::vector<ORB_SLAM3::MapPoint*> trackedMapPoints = orbslam_->GetTrackedMapPoints();
if(orbslam_->isLost() || mapPoints.empty()) if(orbslam_->isLost() || trackedMapPoints.empty())
{ {
covariance = cv::Mat::eye(6,6,CV_64FC1)*9999.0f; covariance = cv::Mat::eye(6,6,CV_64FC1)*9999.0f;
if(!imuLocalTransform_.isNull()) {
UWARN("ORBSLAM lost tracking! If it is on initialization, try moving the sensor in a circle for a couple of seconds.");
}
else {
UWARN("ORBSLAM lost tracking!");
}
} }
else else
{ {
@@ -490,14 +510,16 @@ Transform OdometryORBSLAM3::computeTransform(
if(!p.isNull()) if(!p.isNull())
{ {
if(!localTransform.isNull()) if(!imuLocalTransform_.isNull())
{ {
if(originLocalTransform_.isNull()) // Transform p from optical-imu system (x->left, y->back and z->up) to ros system, then remove camera local transform
{ p = Transform(0,0,0,0,0,-M_PI/2) * p.inverse() * localTransform.inverse();
originLocalTransform_ = localTransform; }
} else
// transform in base frame {
p = originLocalTransform_ * p.inverse() * localTransform.inverse(); UASSERT(!localTransform.isNull());
// Transform p from optical system (x->right, y->down and z->forward) to ros system, then remove camera local transform
p = CameraModel::opticalRotation() * p.inverse() * localTransform.inverse();
} }
t = previousPoseInv*p; t = previousPoseInv*p;
} }
@@ -534,12 +556,14 @@ Transform OdometryORBSLAM3::computeTransform(
} }
} }
size_t mapPointsSize = 0;
if(info) if(info)
{ {
info->lost = t.isNull(); info->lost = t.isNull();
info->type = (int)kTypeORBSLAM; info->type = (int)kTypeORBSLAM;
info->reg.covariance = covariance; info->reg.covariance = covariance;
info->localMapSize = mapPoints.size(); std::vector<ORB_SLAM3::MapPoint*> mapPoints = orbslam_->GetAllMapPoints();
info->localMapSize = mapPointsSize = mapPoints.size();
info->localKeyFrames = 0; info->localKeyFrames = 0;
if(this->isInfoDataFilled()) if(this->isInfoDataFilled())
@@ -549,20 +573,20 @@ Transform OdometryORBSLAM3::computeTransform(
info->reg.inliersIDs.resize(kpts.size()); info->reg.inliersIDs.resize(kpts.size());
int oi = 0; int oi = 0;
UASSERT(mapPoints.size() == kpts.size()); UASSERT(trackedMapPoints.size() == kpts.size());
for (unsigned int i = 0; i < kpts.size(); ++i) for (unsigned int i = 0; i < kpts.size(); ++i)
{ {
int wordId; int wordId;
if(mapPoints[i] != 0) if(trackedMapPoints[i] != 0)
{ {
wordId = mapPoints[i]->mnId; wordId = trackedMapPoints[i]->mnId;
} }
else else
{ {
wordId = -(i+1); wordId = -(i+1);
} }
info->words.insert(std::make_pair(wordId, kpts[i])); info->words.insert(std::make_pair(wordId, kpts[i]));
if(mapPoints[i] != 0) if(trackedMapPoints[i] != 0)
{ {
info->reg.matchesIDs[oi] = wordId; info->reg.matchesIDs[oi] = wordId;
info->reg.inliersIDs[oi] = wordId; info->reg.inliersIDs[oi] = wordId;
@@ -574,7 +598,15 @@ Transform OdometryORBSLAM3::computeTransform(
info->reg.inliers = oi; info->reg.inliers = oi;
info->reg.matches = oi; info->reg.matches = oi;
Eigen::Affine3f fixRot = (this->getPose()*previousPoseInv*originLocalTransform_).toEigen3f(); Eigen::Affine3f fixRot;
if(!imuLocalTransform_.isNull())
{
fixRot = (this->getPose()*previousPoseInv*Transform(0,0,0,0,0,-M_PI/2)).toEigen3f();
}
else
{
fixRot = (this->getPose()*previousPoseInv*CameraModel::opticalRotation()).toEigen3f();
}
for (unsigned int i = 0; i < mapPoints.size(); ++i) for (unsigned int i = 0; i < mapPoints.size(); ++i)
{ {
if(mapPoints[i]) if(mapPoints[i])
@@ -587,7 +619,8 @@ Transform OdometryORBSLAM3::computeTransform(
} }
} }
UINFO("Odom update time = %fs, map points=%ld, lost=%s", timer.elapsed(), mapPoints.size(), t.isNull()?"true":"false"); UINFO("Odom update time = %fs, tracked points=%ld, map points=%ld, lost=%s",
timer.elapsed(), trackedMapPoints.size(), mapPointsSize, t.isNull()?"true":"false");
#else #else
UERROR("RTAB-Map is not built with ORB_SLAM support! Select another visual odometry approach."); UERROR("RTAB-Map is not built with ORB_SLAM support! Select another visual odometry approach.");
-516
View File
@@ -1,516 +0,0 @@
/*
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "rtabmap/core/odometry/OdometryVINS.h"
#include "rtabmap/core/OdometryInfo.h"
#include "rtabmap/core/util3d_transforms.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UStl.h"
#include "rtabmap/utilite/UThread.h"
#include "rtabmap/utilite/UDirectory.h"
#include <opencv2/imgproc/types_c.h>
#ifdef RTABMAP_VINS
#include <estimator/estimator.h>
#include <estimator/parameters.h>
#include <camodocal/camera_models/PinholeCamera.h>
#include <camodocal/camera_models/EquidistantCamera.h>
#include <utility/visualization.h>
#endif
namespace rtabmap {
#ifdef RTABMAP_VINS
class VinsEstimator: public Estimator
{
public:
VinsEstimator(
const Transform & imuLocalTransform,
const StereoCameraModel & model,
bool rectified) : Estimator()
{
MULTIPLE_THREAD = 0;
setParameter();
//overwrite camera calibration only if received model is radtan, otherwise use config
UASSERT(NUM_OF_CAM >= 1 && NUM_OF_CAM <=2);
if( (NUM_OF_CAM == 2 && model.left().D_raw().cols == 4 && model.right().D_raw().cols == 4) ||
(NUM_OF_CAM == 1 && model.left().D_raw().cols == 4))
{
UWARN("Overwriting VINS camera calibration config with received pinhole model... rectified=%d", rectified?1:0);
featureTracker.m_camera.clear();
camodocal::PinholeCameraPtr camera( new camodocal::PinholeCamera );
camodocal::PinholeCamera::Parameters params(
model.name(),
model.left().imageWidth(), model.left().imageHeight(),
rectified?0:model.left().D_raw().at<double>(0,0),
rectified?0:model.left().D_raw().at<double>(0,1),
rectified?0:model.left().D_raw().at<double>(0,2),
rectified?0:model.left().D_raw().at<double>(0,3),
rectified?model.left().fx():model.left().K_raw().at<double>(0,0),
rectified?model.left().fy():model.left().K_raw().at<double>(1,1),
rectified?model.left().cx():model.left().K_raw().at<double>(0,2),
rectified?model.left().cy():model.left().K_raw().at<double>(1,2));
camera->setParameters(params);
featureTracker.m_camera.push_back(camera);
if(NUM_OF_CAM == 2)
{
camodocal::PinholeCameraPtr camera( new camodocal::PinholeCamera );
camodocal::PinholeCamera::Parameters params(
model.name(),
model.right().imageWidth(), model.right().imageHeight(),
rectified?0:model.right().D_raw().at<double>(0,0),
rectified?0:model.right().D_raw().at<double>(0,1),
rectified?0:model.right().D_raw().at<double>(0,2),
rectified?0:model.right().D_raw().at<double>(0,3),
rectified?model.right().fx():model.right().K_raw().at<double>(0,0),
rectified?model.right().fy():model.right().K_raw().at<double>(1,1),
rectified?model.right().cx():model.right().K_raw().at<double>(0,2),
rectified?model.right().cy():model.right().K_raw().at<double>(1,2));
camera->setParameters(params);
featureTracker.m_camera.push_back(camera);
}
}
else if(rectified)
{
UWARN("Images are rectified but received calibration cannot be "
"used, make sure calibration in config file doesn't have "
"distortion or send raw images to VINS odometry.");
if(!featureTracker.m_camera.empty())
{
if(featureTracker.m_camera.front()->imageWidth() != model.left().imageWidth() ||
featureTracker.m_camera.front()->imageHeight() != model.left().imageHeight())
{
UERROR("Received images don't have same size (%dx%d) than in the config file (%dx%d)!",
model.left().imageWidth(),
model.left().imageHeight(),
featureTracker.m_camera.front()->imageWidth(),
featureTracker.m_camera.front()->imageHeight());
}
}
}
Transform imuCam0 = imuLocalTransform.inverse() * model.localTransform();
tic[0] = Vector3d(imuCam0.x(), imuCam0.y(), imuCam0.z());
ric[0] = imuCam0.toEigen4d().block<3,3>(0,0);
if(NUM_OF_CAM == 2)
{
Transform cam0cam1;
if(rectified)
{
cam0cam1 = Transform(
1, 0, 0, model.baseline(),
0, 1, 0, 0,
0, 0, 1, 0);
}
else
{
cam0cam1 = model.stereoTransform().inverse();
}
UASSERT(!cam0cam1.isNull());
Transform imuCam1 = imuCam0 * cam0cam1;
tic[1] = Vector3d(imuCam1.x(), imuCam1.y(), imuCam1.z());
ric[1] = imuCam1.toEigen4d().block<3,3>(0,0);
}
for (int i = 0; i < NUM_OF_CAM; i++)
{
cout << " exitrinsic cam " << i << endl << ric[i] << endl << tic[i].transpose() << endl;
}
f_manager.setRic(ric);
ProjectionTwoFrameOneCamFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
ProjectionTwoFrameTwoCamFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
ProjectionOneFrameTwoCamFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
td = TD;
g = G;
cout << "set g " << g.transpose() << endl;
}
// Copy of original inputImage() so that overridden processMeasurements() is used and threading is disabled.
void inputImage(double t, const cv::Mat &_img, const cv::Mat &_img1)
{
inputImageCnt++;
map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> featureFrame;
TicToc featureTrackerTime;
if(_img1.empty())
featureFrame = featureTracker.trackImage(t, _img);
else
featureFrame = featureTracker.trackImage(t, _img, _img1);
//printf("featureTracker time: %f\n", featureTrackerTime.toc());
//if(MULTIPLE_THREAD)
//{
// if(inputImageCnt % 2 == 0)
// {
// mBuf.lock();
// featureBuf.push(make_pair(t, featureFrame));
// mBuf.unlock();
// }
//}
//else
{
mBuf.lock();
featureBuf.push(make_pair(t, featureFrame));
mBuf.unlock();
TicToc processTime;
processMeasurements();
UDEBUG("VINS process time: %f", processTime.toc());
}
}
// Copy of original inputIMU() but with publisher commented
void inputIMU(double t, const Vector3d &linearAcceleration, const Vector3d &angularVelocity)
{
mBuf.lock();
accBuf.push(make_pair(t, linearAcceleration));
gyrBuf.push(make_pair(t, angularVelocity));
//printf("input imu with time %f \n", t);
mBuf.unlock();
fastPredictIMU(t, linearAcceleration, angularVelocity);
//if (solver_flag == NON_LINEAR)
// pubLatestOdometry(latest_P, latest_Q, latest_V, t);
}
// Copy of original processMeasurements() but with publishers commented and threading disabled
void processMeasurements()
{
//while (1)
{
//printf("process measurments\n");
pair<double, map<int, vector<pair<int, Eigen::Matrix<double, 7, 1> > > > > feature;
vector<pair<double, Eigen::Vector3d>> accVector, gyrVector;
if(!featureBuf.empty())
{
feature = featureBuf.front();
curTime = feature.first + td;
//while(1)
//{
if (!((!USE_IMU || IMUAvailable(feature.first + td))))
//if ((!USE_IMU || IMUAvailable(feature.first + td)))
// break;
//else
{
printf("wait for imu ... \n");
//if (! MULTIPLE_THREAD)
return;
//std::chrono::milliseconds dura(5);
//std::this_thread::sleep_for(dura);
}
//}
mBuf.lock();
if(USE_IMU)
getIMUInterval(prevTime, curTime, accVector, gyrVector);
featureBuf.pop();
mBuf.unlock();
if(USE_IMU)
{
if(!initFirstPoseFlag)
initFirstIMUPose(accVector);
UDEBUG("accVector.size() = %d", accVector.size());
for(size_t i = 0; i < accVector.size(); i++)
{
double dt;
if(i == 0)
dt = accVector[i].first - prevTime;
else if (i == accVector.size() - 1)
dt = curTime - accVector[i - 1].first;
else
dt = accVector[i].first - accVector[i - 1].first;
processIMU(accVector[i].first, dt, accVector[i].second, gyrVector[i].second);
}
}
processImage(feature.second, feature.first);
prevTime = curTime;
printStatistics(*this, 0);
//std_msgs::Header header;
//header.frame_id = "world";
//header.stamp = ros::Time(feature.first);
//pubOdometry(*this, header);
//pubKeyPoses(*this, header);
//pubCameraPose(*this, header);
//pubPointCloud(*this, header);
//pubKeyframe(*this);
//pubTF(*this, header);
}
//if (! MULTIPLE_THREAD)
// break;
//std::chrono::milliseconds dura(2);
//std::this_thread::sleep_for(dura);
}
}
};
#endif
OdometryVINS::OdometryVINS(const ParametersMap & parameters) :
Odometry(parameters)
#ifdef RTABMAP_VINS
,
vinsEstimator_(0),
initGravity_(false),
previousPose_(Transform::getIdentity())
#endif
{
#ifdef RTABMAP_VINS
// intialize
std::string configFilename;
Parameters::parse(parameters, Parameters::kOdomVINSConfigPath(), configFilename);
if(configFilename.empty())
{
UERROR("VINS config file is empty (%s=%s)!",
Parameters::kOdomVINSConfigPath().c_str(),
Parameters::kOdomVINSConfigPath().c_str());
}
else
{
readParameters(uReplaceChar(configFilename, '~', UDirectory::homeDir()));
}
#endif
}
OdometryVINS::~OdometryVINS()
{
#ifdef RTABMAP_VINS
delete vinsEstimator_;
#endif
}
void OdometryVINS::reset(const Transform & initialPose)
{
Odometry::reset(initialPose);
#ifdef RTABMAP_VINS
if(!initGravity_)
{
delete vinsEstimator_;
vinsEstimator_ = 0;
previousPose_.setIdentity();
lastImu_ = IMU();
previousLocalTransform_.setNull();
}
initGravity_ = false;
#endif
}
// return not null transform if odometry is correctly computed
Transform OdometryVINS::computeTransform(
SensorData & data,
const Transform & guess,
OdometryInfo * info)
{
Transform t;
#ifdef RTABMAP_VINS
UTimer timer;
if(USE_IMU!=0 && !data.imu().empty())
{
double t = data.stamp();
double dx = data.imu().linearAcceleration().val[0];
double dy = data.imu().linearAcceleration().val[1];
double dz = data.imu().linearAcceleration().val[2];
double rx = data.imu().angularVelocity().val[0];
double ry = data.imu().angularVelocity().val[1];
double rz = data.imu().angularVelocity().val[2];
Vector3d acc(dx, dy, dz);
Vector3d gyr(rx, ry, rz);
UDEBUG("IMU update stamp=%f", data.stamp());
if(vinsEstimator_ != 0)
{
vinsEstimator_->inputIMU(t, acc, gyr);
}
else
{
lastImu_ = data.imu();
UWARN("Waiting an image for initialization...");
}
}
if(!data.imageRaw().empty() && !data.rightRaw().empty() && data.stereoCameraModels().size() == 1 && data.stereoCameraModels()[0].isValidForProjection())
{
if(USE_IMU==1 && lastImu_.localTransform().isNull())
{
UWARN("Waiting IMU for initialization...");
return t;
}
if(vinsEstimator_ == 0)
{
// intialize
vinsEstimator_ = new VinsEstimator(
lastImu_.localTransform().isNull()?Transform::getIdentity():lastImu_.localTransform(),
data.stereoCameraModels()[0],
this->imagesAlreadyRectified());
}
UDEBUG("Image update stamp=%f", data.stamp());
cv::Mat left;
cv::Mat right;
if(data.imageRaw().type() == CV_8UC3)
{
cv::cvtColor(data.imageRaw(), left, CV_BGR2GRAY);
}
else if(data.imageRaw().type() == CV_8UC1)
{
left = data.imageRaw().clone();
}
else
{
UFATAL("Not supported color type!");
}
if(data.rightRaw().type() == CV_8UC3)
{
cv::cvtColor(data.rightRaw(), right, CV_BGR2GRAY);
}
else if(data.rightRaw().type() == CV_8UC1)
{
right = data.rightRaw().clone();
}
else
{
UFATAL("Not supported color type!");
}
vinsEstimator_->inputImage(data.stamp(), left, right);
if(vinsEstimator_->solver_flag == Estimator::NON_LINEAR)
{
Quaterniond tmp_Q;
tmp_Q = Quaterniond(vinsEstimator_->Rs[WINDOW_SIZE]);
Transform p(
vinsEstimator_->Ps[WINDOW_SIZE].x(),
vinsEstimator_->Ps[WINDOW_SIZE].y(),
vinsEstimator_->Ps[WINDOW_SIZE].z(),
tmp_Q.x(),
tmp_Q.y(),
tmp_Q.z(),
tmp_Q.w());
if(!p.isNull())
{
if(!lastImu_.localTransform().isNull())
{
p = p * lastImu_.localTransform().inverse();
}
if(this->getPose().rotation().isIdentity())
{
initGravity_ = true;
this->reset(this->getPose()*p.rotation());
}
if(previousPose_.isIdentity())
{
previousPose_ = p;
}
// make it incremental
Transform previousPoseInv = previousPose_.inverse();
t = previousPoseInv*p;
previousPose_ = p;
if(info)
{
info->type = this->getType();
info->reg.covariance = cv::Mat::eye(6,6, CV_64FC1);
info->reg.covariance *= this->framesProcessed() == 0?9999:0.0001;
// feature map
Transform fixT = this->getPose()*previousPoseInv;
for (auto &it_per_id : vinsEstimator_->f_manager.feature)
{
int used_num;
used_num = it_per_id.feature_per_frame.size();
if (!(used_num >= 2 && it_per_id.start_frame < WINDOW_SIZE - 2))
continue;
if (it_per_id.start_frame > WINDOW_SIZE * 3.0 / 4.0 || it_per_id.solve_flag != 1)
continue;
int imu_i = it_per_id.start_frame;
Vector3d pts_i = it_per_id.feature_per_frame[it_per_id.feature_per_frame.size()-1].point * it_per_id.estimated_depth;
Vector3d w_pts_i = vinsEstimator_->Rs[imu_i] * (vinsEstimator_->ric[0] * pts_i + vinsEstimator_->tic[0]) + vinsEstimator_->Ps[imu_i];
cv::Point3f p;
p.x = w_pts_i(0);
p.y = w_pts_i(1);
p.z = w_pts_i(2);
p = util3d::transformPoint(p, fixT);
info->localMap.insert(std::make_pair(it_per_id.feature_id, p));
if(this->imagesAlreadyRectified())
{
cv::Point2f pt;
data.stereoCameraModels()[0].left().reproject(pts_i(0), pts_i(1), pts_i(2), pt.x, pt.y);
info->reg.inliersIDs.push_back(info->newCorners.size());
info->newCorners.push_back(pt);
}
}
info->features = info->newCorners.size();
info->localMapSize = info->localMap.size();
}
UINFO("Odom update time = %fs p=%s", timer.elapsed(), p.prettyPrint().c_str());
}
}
else
{
UWARN("VINS not yet initialized... waiting to get enough IMU messages");
}
}
else if(!data.imageRaw().empty() && !data.depthRaw().empty())
{
UERROR("VINS-Fusion doesn't work with RGB-D data, stereo images are required!");
}
else if(!data.imageRaw().empty() && data.depthOrRightRaw().empty())
{
UERROR("VINS-Fusion requires stereo images!");
}
else if(data.imu().empty())
{
UERROR("VINS-Fusion requires stereo images (and only one stereo camera with valid calibration)!");
}
#else
UERROR("RTAB-Map is not built with VINS support! Select another visual odometry approach.");
#endif
return t;
}
} // namespace rtabmap
+581
View File
@@ -0,0 +1,581 @@
/*
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "rtabmap/core/odometry/OdometryVINSFusion.h"
#include "rtabmap/core/OdometryInfo.h"
#include "rtabmap/core/util3d_transforms.h"
#include "rtabmap/utilite/ULogger.h"
#include "rtabmap/utilite/UTimer.h"
#include "rtabmap/utilite/UStl.h"
#include "rtabmap/utilite/UThread.h"
#include "rtabmap/utilite/UDirectory.h"
#include <opencv2/imgproc/types_c.h>
#ifdef RTABMAP_VINS_FUSION
#include <estimator/estimator.h>
#include <estimator/parameters.h>
#include <camodocal/camera_models/PinholeCamera.h>
#include <camodocal/camera_models/PinholeFullCamera.h>
#include <utility/visualization.h>
#endif
namespace rtabmap {
#ifdef RTABMAP_VINS_FUSION
class VinsFusionEstimator: public Estimator
{
public:
VinsFusionEstimator() : Estimator()
{}
bool init(const Transform & imuLocalTransform,
const StereoCameraModel & model,
bool rectified)
{
MULTIPLE_THREAD = 0;
setParameter();
ROW=model.left().imageHeight();
COL=model.left().imageWidth();
//overwrite camera calibration only if received model is radtan, otherwise use config
UASSERT(NUM_OF_CAM >= 1 && NUM_OF_CAM <=2);
if( (NUM_OF_CAM == 2 && (rectified || (model.left().D_raw().cols >= 4 && model.right().D_raw().cols >= 4))) ||
(NUM_OF_CAM == 1 && (rectified || model.left().D_raw().cols >= 4)))
{
UINFO("Setting up VINS camera calibration config with received pinhole model... rectified=%d distortion coefficients=%d",
rectified?1:0, model.left().D_raw().cols);
featureTracker.m_camera.clear();
double fx = 0.0;
if(!rectified && model.left().D_raw().cols >= 8)
{
if(model.left().D_raw().cols > 8)
{
UWARN("Received %d distortion coefficients, but only the first 8 are supported, ignoring the last coefficents.",
model.left().D_raw().cols);
}
camodocal::PinholeFullCameraPtr camera( new camodocal::PinholeFullCamera );
camodocal::PinholeFullCamera::Parameters params(
model.name(),
model.left().imageWidth(), model.left().imageHeight(),
model.left().D_raw().at<double>(0,0), // k1
model.left().D_raw().at<double>(0,1), // k1
model.left().D_raw().at<double>(0,4), // k3
model.left().D_raw().at<double>(0,5), // k4
model.left().D_raw().at<double>(0,6), // k5
model.left().D_raw().at<double>(0,7), // k6
model.left().D_raw().at<double>(0,2), // p1
model.left().D_raw().at<double>(0,3), // p1
model.left().K_raw().at<double>(0,0), // fx
model.left().K_raw().at<double>(1,1), // fy
model.left().K_raw().at<double>(0,2), // cx
model.left().K_raw().at<double>(1,2)); // cy
camera->setParameters(params);
featureTracker.m_camera.push_back(camera);
fx = params.fx();
if(NUM_OF_CAM == 2)
{
UASSERT(model.left().D_raw().cols == model.right().D_raw().cols);
camodocal::PinholeFullCameraPtr camera2( new camodocal::PinholeFullCamera );
camodocal::PinholeFullCamera::Parameters params2(
model.name(),
model.right().imageWidth(), model.right().imageHeight(),
model.right().D_raw().at<double>(0,0), // k1
model.right().D_raw().at<double>(0,1), // k2
model.right().D_raw().at<double>(0,4), // k3
model.right().D_raw().at<double>(0,5), // k4
model.right().D_raw().at<double>(0,6), // k5
model.right().D_raw().at<double>(0,7), // k6
model.right().D_raw().at<double>(0,2), // p1
model.right().D_raw().at<double>(0,3), // p2
model.right().K_raw().at<double>(0,0), // fx
model.right().K_raw().at<double>(1,1), // fy
model.right().K_raw().at<double>(0,2), // cx
model.right().K_raw().at<double>(1,2)); // cy
camera2->setParameters(params2);
featureTracker.m_camera.push_back(camera2);
}
}
else
{
if(!rectified)
{
if(model.left().D_raw().cols == 6) {
UERROR("Fisheye camera model support not implemented! Provide rectified images instead (see %s).",
Parameters::kRtabmapImagesAlreadyRectified().c_str());
return false;
}
if(model.left().D_raw().cols > 4)
{
UWARN("Received %d distortion coefficients, but only 4 or 8 are supported, ignoring the last coefficents.",
model.left().D_raw().cols);
}
}
camodocal::PinholeCameraPtr camera( new camodocal::PinholeCamera );
camodocal::PinholeCamera::Parameters params(
model.name(),
model.left().imageWidth(), model.left().imageHeight(),
rectified?0:model.left().D_raw().at<double>(0,0), // k1
rectified?0:model.left().D_raw().at<double>(0,1), // k2
rectified?0:model.left().D_raw().at<double>(0,2), // p1
rectified?0:model.left().D_raw().at<double>(0,3), // p2
rectified?model.left().fx():model.left().K_raw().at<double>(0,0),
rectified?model.left().fy():model.left().K_raw().at<double>(1,1),
rectified?model.left().cx():model.left().K_raw().at<double>(0,2),
rectified?model.left().cy():model.left().K_raw().at<double>(1,2));
camera->setParameters(params);
featureTracker.m_camera.push_back(camera);
fx = params.fx();
if(NUM_OF_CAM == 2)
{
UASSERT(model.left().D_raw().cols == model.right().D_raw().cols);
camodocal::PinholeCameraPtr camera2( new camodocal::PinholeCamera );
camodocal::PinholeCamera::Parameters params2(
model.name(),
model.right().imageWidth(), model.right().imageHeight(),
rectified?0:model.right().D_raw().at<double>(0,0), // k1
rectified?0:model.right().D_raw().at<double>(0,1), // k2
rectified?0:model.right().D_raw().at<double>(0,2), // p1
rectified?0:model.right().D_raw().at<double>(0,3), // p2
rectified?model.right().fx():model.right().K_raw().at<double>(0,0),
rectified?model.right().fy():model.right().K_raw().at<double>(1,1),
rectified?model.right().cx():model.right().K_raw().at<double>(0,2),
rectified?model.right().cy():model.right().K_raw().at<double>(1,2));
camera2->setParameters(params2);
featureTracker.m_camera.push_back(camera2);
}
}
double originalParalax = MIN_PARALLAX * FOCAL_LENGTH;
// If you have compiler error about FOCAL_LENGTH being const, make sure to use the following patch for ROS1:
// https://gist.github.com/matlabbe/795ab37067367dca58bbadd8201d986c#file-vins-fusion_pull136-patch
// Use this patch for ROS2: https://gist.github.com/matlabbe/ebbb343cd744da9d6d6d6ded2e1557fd
FOCAL_LENGTH = fx;
MIN_PARALLAX = originalParalax / FOCAL_LENGTH;
ProjectionTwoFrameOneCamFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
ProjectionTwoFrameTwoCamFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
ProjectionOneFrameTwoCamFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
}
else
{
UERROR("Received stereo camera model is not compatible with VINS-Fusion.");
if(!rectified && model.left().D_raw().cols != 4) {
UERROR("When raw images are provided (%s=false), we expect 4 distortion coefficients (k1,k2,p1,p2), received %d",
Parameters::kRtabmapImagesAlreadyRectified().c_str(),
model.left().D_raw().cols);
}
return false;
}
Transform imuCam0 = imuLocalTransform.inverse() * model.localTransform();
tic[0] = TIC[0] = Vector3d(imuCam0.x(), imuCam0.y(), imuCam0.z());
ric[0] = RIC[0] = imuCam0.toEigen4d().block<3,3>(0,0);
if(NUM_OF_CAM == 2)
{
Transform cam0cam1;
if(rectified)
{
cam0cam1 = Transform(
1, 0, 0, model.baseline(),
0, 1, 0, 0,
0, 0, 1, 0);
}
else
{
cam0cam1 = model.stereoTransform().inverse();
}
UASSERT(!cam0cam1.isNull());
Transform imuCam1 = imuCam0 * cam0cam1;
tic[1] = TIC[0] = Vector3d(imuCam1.x(), imuCam1.y(), imuCam1.z());
ric[1] = RIC[0] = imuCam1.toEigen4d().block<3,3>(0,0);
}
for (int i = 0; i < NUM_OF_CAM; i++)
{
cout << " new extrinsic cam " << i << endl << ric[i] << endl << tic[i].transpose() << endl;
}
for (int i = 0; i < NUM_OF_CAM; i++)
{
cout << " new intrinsic cam " << i << endl << featureTracker.m_camera[i]->parametersToString() << endl;
}
f_manager.setRic(ric);
return true;
}
// Copy of original inputImage() so that overridden processMeasurements() is used and threading is disabled.
void inputImage(double t, const cv::Mat &_img, const cv::Mat &_img1)
{
TicToc processTime;
inputImageCnt++;
map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> featureFrame;
if(_img1.empty()) {
featureFrame = featureTracker.trackImage(t, _img);
}
else {
featureFrame = featureTracker.trackImage(t, _img, _img1);
}
mBuf.lock();
featureBuf.push(make_pair(t, featureFrame));
mBuf.unlock();
processMeasurements();
UDEBUG("VINS process time: %f", processTime.toc());
}
// Copy of original inputIMU() but with publisher commented
void inputIMU(double t, const Vector3d &linearAcceleration, const Vector3d &angularVelocity)
{
mBuf.lock();
accBuf.push(make_pair(t, linearAcceleration));
gyrBuf.push(make_pair(t, angularVelocity));
//printf("input imu with time %f \n", t);
mBuf.unlock();
if (solver_flag == NON_LINEAR)
{
mPropagate.lock();
fastPredictIMU(t, linearAcceleration, angularVelocity);
mPropagate.unlock();
}
}
// Copy of original processMeasurements() but with publishers commented and threading disabled
void processMeasurements()
{
pair<double, map<int, vector<pair<int, Eigen::Matrix<double, 7, 1> > > > > feature;
vector<pair<double, Eigen::Vector3d>> accVector, gyrVector;
if(!featureBuf.empty())
{
feature = featureBuf.front();
curTime = feature.first + td;
if (USE_IMU && !IMUAvailable(feature.first + td))
{
printf("wait for imu ... \n");
return;
}
mBuf.lock();
if(USE_IMU)
getIMUInterval(prevTime, curTime, accVector, gyrVector);
featureBuf.pop();
mBuf.unlock();
if(USE_IMU)
{
if(!initFirstPoseFlag)
initFirstIMUPose(accVector);
for(size_t i = 0; i < accVector.size(); i++)
{
double dt;
if(i == 0)
dt = accVector[i].first - prevTime;
else if (i == accVector.size() - 1)
dt = curTime - accVector[i - 1].first;
else
dt = accVector[i].first - accVector[i - 1].first;
processIMU(accVector[i].first, dt, accVector[i].second, gyrVector[i].second);
}
}
mProcess.lock();
processImage(feature.second, feature.first);
prevTime = curTime;
mProcess.unlock();
}
}
};
#endif
OdometryVINSFusion::OdometryVINSFusion(const ParametersMap & parameters) :
Odometry(parameters)
#ifdef RTABMAP_VINS_FUSION
,
vinsEstimator_(0),
initGravity_(false),
previousPose_(Transform::getIdentity())
#endif
{
#ifdef RTABMAP_VINS_FUSION
// intialize
std::string configFilename;
Parameters::parse(parameters, Parameters::kOdomVINSFusionConfigPath(), configFilename);
if(configFilename.empty())
{
UERROR("VINS config file is empty (%s)!",
Parameters::kOdomVINSFusionConfigPath().c_str());
}
else
{
UINFO("Using config file %s", configFilename.c_str());
readParameters(uReplaceChar(configFilename, '~', UDirectory::homeDir()));
}
#endif
}
OdometryVINSFusion::~OdometryVINSFusion()
{
#ifdef RTABMAP_VINS_FUSION
delete vinsEstimator_;
#endif
}
void OdometryVINSFusion::reset(const Transform & initialPose)
{
Odometry::reset(initialPose);
#ifdef RTABMAP_VINS_FUSION
if(!initGravity_)
{
delete vinsEstimator_;
vinsEstimator_ = 0;
previousPose_.setIdentity();
lastImu_ = IMU();
previousLocalTransform_.setNull();
}
initGravity_ = false;
#endif
}
// return not null transform if odometry is correctly computed
Transform OdometryVINSFusion::computeTransform(
SensorData & data,
const Transform & guess,
OdometryInfo * info)
{
Transform t;
#ifdef RTABMAP_VINS_FUSION
UTimer timer;
bool hasImage = !data.imageRaw().empty() && !data.rightRaw().empty() && data.stereoCameraModels().size() == 1 && data.stereoCameraModels()[0].isValidForProjection();
if(USE_IMU!=0 && !data.imu().empty())
{
double dx = data.imu().linearAcceleration().val[0];
double dy = data.imu().linearAcceleration().val[1];
double dz = data.imu().linearAcceleration().val[2];
double rx = data.imu().angularVelocity().val[0];
double ry = data.imu().angularVelocity().val[1];
double rz = data.imu().angularVelocity().val[2];
Vector3d acc(dx, dy, dz);
Vector3d gyr(rx, ry, rz);
UDEBUG("IMU update stamp=%f", data.stamp());
if(vinsEstimator_ != 0)
{
vinsEstimator_->inputIMU(data.stamp(), acc, gyr);
}
else
{
lastImu_ = data.imu();
lastImuStamp_ = data.stamp();
if(!hasImage) {
UWARN("Waiting an image for initialization...");
}
}
}
if(hasImage)
{
if(USE_IMU==1 && lastImu_.localTransform().isNull())
{
UWARN("Waiting IMU for initialization...");
return t;
}
if(vinsEstimator_ == 0)
{
// intialize
UINFO("Initializing with image %f", data.stamp());
vinsEstimator_ = new VinsFusionEstimator();
if(!vinsEstimator_->init(
lastImu_.localTransform().isNull()?Transform::getIdentity():lastImu_.localTransform(),
data.stereoCameraModels()[0],
this->imagesAlreadyRectified()))
{
delete vinsEstimator_;
vinsEstimator_ = 0;
return Transform();
}
if(USE_IMU) {
double dx = lastImu_.linearAcceleration().val[0];
double dy = lastImu_.linearAcceleration().val[1];
double dz = lastImu_.linearAcceleration().val[2];
double rx = lastImu_.angularVelocity().val[0];
double ry = lastImu_.angularVelocity().val[1];
double rz = lastImu_.angularVelocity().val[2];
Vector3d acc(dx, dy, dz);
Vector3d gyr(rx, ry, rz);
vinsEstimator_->inputIMU(lastImuStamp_, acc, gyr);
}
}
UDEBUG("Image update stamp=%f", data.stamp());
cv::Mat left;
cv::Mat right;
if(data.imageRaw().type() == CV_8UC3)
{
cv::cvtColor(data.imageRaw(), left, CV_BGR2GRAY);
}
else if(data.imageRaw().type() == CV_8UC1)
{
left = data.imageRaw().clone();
}
else
{
UFATAL("Not supported color type!");
}
if(data.rightRaw().type() == CV_8UC3)
{
cv::cvtColor(data.rightRaw(), right, CV_BGR2GRAY);
}
else if(data.rightRaw().type() == CV_8UC1)
{
right = data.rightRaw().clone();
}
else
{
UFATAL("Not supported color type!");
}
vinsEstimator_->inputImage(data.stamp(), left, right);
if(vinsEstimator_->solver_flag == Estimator::NON_LINEAR)
{
Quaterniond tmp_Q;
tmp_Q = Quaterniond(vinsEstimator_->Rs[WINDOW_SIZE]);
Transform p(
vinsEstimator_->Ps[WINDOW_SIZE].x(),
vinsEstimator_->Ps[WINDOW_SIZE].y(),
vinsEstimator_->Ps[WINDOW_SIZE].z(),
tmp_Q.x(),
tmp_Q.y(),
tmp_Q.z(),
tmp_Q.w());
if(!p.isNull())
{
if(!lastImu_.localTransform().isNull())
{
p = p * lastImu_.localTransform().inverse();
}
if(this->getPose().rotation().isIdentity())
{
initGravity_ = true;
this->reset(this->getPose()*p.rotation());
}
if(previousPose_.isIdentity())
{
previousPose_ = p;
}
// make it incremental
Transform previousPoseInv = previousPose_.inverse();
t = previousPoseInv*p;
previousPose_ = p;
if(info)
{
info->type = this->getType();
info->reg.covariance = cv::Mat::eye(6,6, CV_64FC1);
info->reg.covariance *= this->framesProcessed() == 0?9999:0.0001;
// feature map: based on code from pubPointCloud() of vins's visualization.cpp
for (auto &it_per_id : vinsEstimator_->f_manager.feature)
{
if(it_per_id.feature_per_frame.size() < 2) {
// feature just added but not tracked, or old feature not tracked anymore
continue;
}
int imu_i = it_per_id.start_frame;
Vector3d pts_i = it_per_id.feature_per_frame[0].point * it_per_id.estimated_depth;
Vector3d w_pts_i = vinsEstimator_->Rs[imu_i] * (vinsEstimator_->ric[0] * pts_i + vinsEstimator_->tic[0]) + vinsEstimator_->Ps[imu_i];
cv::Point3f p;
p.x = w_pts_i(0);
p.y = w_pts_i(1);
p.z = w_pts_i(2);
int featureIndex = info->localMap.size();
info->localMap.insert(std::make_pair(featureIndex, p));
FeaturePerFrame & refFrame = it_per_id.feature_per_frame[0]; // First frame it was seen
FeaturePerFrame & newFrame = it_per_id.feature_per_frame[it_per_id.feature_per_frame.size()-1]; // Last frame it was seen (not necessary in last frame)
cv::Point2f refUV(refFrame.uv[0], refFrame.uv[1]);
cv::Point2f newUV(newFrame.uv[0], newFrame.uv[1]);
info->refCorners.push_back(refUV);
info->newCorners.push_back(newUV);
info->reg.matchesIDs.push_back(featureIndex);
if(it_per_id.solve_flag > 0) {
// Feature correctly tracked
info->words.insert(std::make_pair(featureIndex, cv::KeyPoint(newUV, 3.0f)));
info->cornerInliers.push_back(featureIndex);
info->reg.inliersIDs.push_back(featureIndex);
}
++featureIndex;
}
info->features = info->localMap.size();
info->reg.inliers = info->reg.inliersIDs.size();
info->localMapSize = info->localMap.size();
}
UINFO("Odom update time = %fs p=%s", timer.elapsed(), p.prettyPrint().c_str());
}
}
else
{
UWARN("VINS-Fusion not yet initialized... needing more data.");
}
}
else if(!data.imageRaw().empty() && !data.depthRaw().empty())
{
UERROR("VINS-Fusion doesn't work with RGB-D data, stereo images are required!");
}
else if(!data.imageRaw().empty() && data.depthOrRightRaw().empty())
{
UERROR("VINS-Fusion requires stereo images!");
}
else if(data.imu().empty())
{
UERROR("VINS-Fusion requires stereo images (and only one stereo camera with valid calibration)!");
}
#else
UERROR("RTAB-Map is not built with VINS support! Select another visual odometry approach.");
#endif
return t;
}
} // namespace rtabmap
+14 -12
View File
@@ -90,14 +90,10 @@ typedef g2o::LinearSolverCSparse<SlamBlockSolver::PoseMatrixType> SlamLinearCSpa
typedef g2o::LinearSolverCholmod<SlamBlockSolver::PoseMatrixType> SlamLinearCholmodSolver; typedef g2o::LinearSolverCholmod<SlamBlockSolver::PoseMatrixType> SlamLinearCholmodSolver;
#endif #endif
// We use G2O_SRC_DIR to know we are version after December 24 2020 // We check if g2o/types/sba/sba_utils.h exists to know we use a version after December 24 2020
// where VertexSBAPointXYZ has been renamed to VertexPointXYZ // where VertexSBAPointXYZ has been renamed to VertexPointXYZ
// (g2o: 0fcccb302787e70ff19f65e70fb103a1295b33a2) // (g2o: 0fcccb302787e70ff19f65e70fb103a1295b33a2)
// #ifdef RTABMAP_G2O_WITH_SBA_UTILS
// VCPKG commented G2O_SRC_DIR from their port so we cannot use
// G2O_SRC_DIR on windows to deduce it, we then assume it is the
// latest version without VertexSBAPointXYZ
#if defined(G2O_SRC_DIR) or defined(WIN32)
namespace g2o { namespace g2o {
typedef VertexPointXYZ VertexSBAPointXYZ; typedef VertexPointXYZ VertexSBAPointXYZ;
} }
@@ -220,7 +216,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
outputCovariance = cv::Mat::eye(6,6,CV_64FC1); outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
std::map<int, Transform> optimizedPoses; std::map<int, Transform> optimizedPoses;
#ifdef RTABMAP_G2O #ifdef RTABMAP_G2O
UDEBUG("Optimizing graph..."); UDEBUG("Optimizing graph... (rootId=%d)", rootId);
#ifndef RTABMAP_VERTIGO #ifndef RTABMAP_VERTIGO
if(this->isRobust()) if(this->isRobust())
@@ -352,6 +348,9 @@ std::map<int, Transform> OptimizerG2O::optimize(
{ {
if(!priorsIgnored() && iter->second.type() == Link::kPosePrior) if(!priorsIgnored() && iter->second.type() == Link::kPosePrior)
{ {
if(rootId!=0) {
UDEBUG("Removed rootId=%d because there are priors.");
}
rootId = 0; rootId = 0;
break; break;
} }
@@ -594,7 +593,9 @@ std::map<int, Transform> OptimizerG2O::optimize(
g2o::EdgeSE2Prior * priorEdge = new g2o::EdgeSE2Prior(); g2o::EdgeSE2Prior * priorEdge = new g2o::EdgeSE2Prior();
g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1); g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
priorEdge->setVertex(0, v1); priorEdge->setVertex(0, v1);
priorEdge->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta())); auto pose = g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta());
v1->setEstimate(pose); // This will help g2o to converge faster (https://github.com/introlab/rtabmap_ros/issues/1371)
priorEdge->setMeasurement(pose);
priorEdge->setParameterId(0, PARAM_OFFSET); priorEdge->setParameterId(0, PARAM_OFFSET);
Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity(); Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
if(!isCovarianceIgnored()) if(!isCovarianceIgnored())
@@ -675,6 +676,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
Eigen::Isometry3d pose; Eigen::Isometry3d pose;
pose = a.linear(); pose = a.linear();
pose.translation() = a.translation(); pose.translation() = a.translation();
v1->setEstimate(pose); // This will help g2o to converge faster (https://github.com/introlab/rtabmap_ros/issues/1371)
priorEdge->setMeasurement(pose); priorEdge->setMeasurement(pose);
priorEdge->setParameterId(0, PARAM_OFFSET); priorEdge->setParameterId(0, PARAM_OFFSET);
Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity(); Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
@@ -1005,8 +1007,8 @@ std::map<int, Transform> OptimizerG2O::optimize(
g2o::EdgeSE3 * e = new g2o::EdgeSE3(); g2o::EdgeSE3 * e = new g2o::EdgeSE3();
g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1); g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2); g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
UASSERT(v1 != 0); UASSERT_MSG(v1 != 0, uFormat("v1=%d v2=%d", id1, id2).c_str());
UASSERT(v2 != 0); UASSERT_MSG(v2 != 0, uFormat("v1=%d v2=%d", id1, id2).c_str());
e->setVertex(0, v1); e->setVertex(0, v1);
e->setVertex(1, v2); e->setVertex(1, v2);
e->setMeasurement(constraint); e->setMeasurement(constraint);
@@ -1173,7 +1175,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
if(i>0 && optimizer.activeRobustChi2() > 1000000000000.0) if(i>0 && optimizer.activeRobustChi2() > 1000000000000.0)
{ {
UERROR("g2o: Large optimimzation error detected (%f), aborting optimization!"); UERROR("g2o: Large optimization error detected (%f), aborting optimization!");
return optimizedPoses; return optimizedPoses;
} }
@@ -1222,7 +1224,7 @@ std::map<int, Transform> OptimizerG2O::optimize(
if(optimizer.activeRobustChi2() > 1000000000000.0) if(optimizer.activeRobustChi2() > 1000000000000.0)
{ {
UERROR("g2o: Large optimimzation error detected (%f), aborting optimization!"); UERROR("g2o: Large optimization error detected (%f), aborting optimization!");
return optimizedPoses; return optimizedPoses;
} }
@@ -25,6 +25,9 @@
#pragma once #pragma once
#include <gtsam/nonlinear/NonlinearFactor.h> #include <gtsam/nonlinear/NonlinearFactor.h>
#if GTSAM_VERSION_NUMERIC >= 40300 && defined(GTSAM_WITH_NOISE_MODEL_FACTOR_N)
#include <gtsam/nonlinear/NoiseModelFactorN.h>
#endif
#include <gtsam/geometry/Pose3.h> #include <gtsam/geometry/Pose3.h>
#include <gtsam/geometry/Unit3.h> #include <gtsam/geometry/Unit3.h>
+242 -230
View File
@@ -1,230 +1,242 @@
/** /**
* Python interface for SuperGlue: https://github.com/magicleap/SuperGluePretrainedNetwork * Python interface for SuperGlue: https://github.com/magicleap/SuperGluePretrainedNetwork
*/ */
#include "PyDetector.h" #include "PyDetector.h"
#include <rtabmap/utilite/ULogger.h> #include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UDirectory.h> #include <rtabmap/utilite/UDirectory.h>
#include <rtabmap/utilite/UFile.h> #include <rtabmap/utilite/UFile.h>
#include <rtabmap/utilite/UStl.h> #include <rtabmap/utilite/UStl.h>
#include <rtabmap/utilite/UConversion.h> #include <rtabmap/utilite/UConversion.h>
#include <rtabmap/utilite/UTimer.h> #include <rtabmap/utilite/UTimer.h>
#include <pybind11/embed.h> #include <pybind11/embed.h>
#define NPY_NO_DEPRECATED_API NPY_API_VERSION #define NPY_NO_DEPRECATED_API NPY_API_VERSION
#include <numpy/arrayobject.h> #include <numpy/arrayobject.h>
namespace rtabmap namespace rtabmap
{ {
PyDetector::PyDetector(const ParametersMap & parameters) : PyDetector::PyDetector(const ParametersMap & parameters) :
pModule_(0), pModule_(0),
pFunc_(0), pFunc_(0),
path_(Parameters::defaultPyDetectorPath()), path_(Parameters::defaultPyDetectorPath()),
cuda_(Parameters::defaultPyDetectorCuda()) cuda_(Parameters::defaultPyDetectorCuda())
{ {
this->parseParameters(parameters); this->parseParameters(parameters);
UDEBUG("path = %s", path_.c_str()); UDEBUG("path = %s", path_.c_str());
if(!UFile::exists(path_) || UFile::getExtension(path_).compare("py") != 0) if(!UFile::exists(path_) || UFile::getExtension(path_).compare("py") != 0)
{ {
UERROR("Cannot initialize Python detector, the path is not valid: \"%s\"=\"%s\"", UERROR("Cannot initialize Python detector, the path is not valid: \"%s\"=\"%s\"",
Parameters::kPyDetectorPath().c_str(), path_.c_str()); Parameters::kPyDetectorPath().c_str(), path_.c_str());
return; return;
} }
pybind11::gil_scoped_acquire acquire; pybind11::gil_scoped_acquire acquire;
std::string matcherPythonDir = UDirectory::getDir(path_); std::string matcherPythonDir = UDirectory::getDir(path_);
if(!matcherPythonDir.empty()) if(!matcherPythonDir.empty())
{ {
PyRun_SimpleString("import sys"); PyRun_SimpleString("import sys");
PyRun_SimpleString(uFormat("sys.path.append(\"%s\")", matcherPythonDir.c_str()).c_str()); PyRun_SimpleString(uFormat("sys.path.append(\"%s\")", matcherPythonDir.c_str()).c_str());
} }
_import_array(); _import_array();
std::string scriptName = uSplit(UFile::getName(path_), '.').front(); std::string scriptName = uSplit(UFile::getName(path_), '.').front();
PyObject * pName = PyUnicode_FromString(scriptName.c_str()); PyObject * pName = PyUnicode_FromString(scriptName.c_str());
UDEBUG("PyImport_Import() beg"); UDEBUG("PyImport_Import() beg");
pModule_ = PyImport_Import(pName); pModule_ = PyImport_Import(pName);
UDEBUG("PyImport_Import() end"); UDEBUG("PyImport_Import() end");
Py_DECREF(pName); Py_DECREF(pName);
if(!pModule_) if(!pModule_)
{ {
UERROR("Module \"%s\" could not be imported! (File=\"%s\")", scriptName.c_str(), path_.c_str()); UERROR("Module \"%s\" could not be imported! (File=\"%s\")", scriptName.c_str(), path_.c_str());
UERROR("%s", getPythonTraceback().c_str()); UERROR("%s", getPythonTraceback().c_str());
} }
} }
PyDetector::~PyDetector() PyDetector::~PyDetector()
{ {
pybind11::gil_scoped_acquire acquire; pybind11::gil_scoped_acquire acquire;
if(pFunc_) if(pFunc_)
{ {
Py_DECREF(pFunc_); Py_DECREF(pFunc_);
} }
if(pModule_) if(pModule_)
{ {
Py_DECREF(pModule_); Py_DECREF(pModule_);
} }
} }
void PyDetector::parseParameters(const ParametersMap & parameters) void PyDetector::parseParameters(const ParametersMap & parameters)
{ {
Feature2D::parseParameters(parameters); Feature2D::parseParameters(parameters);
Parameters::parse(parameters, Parameters::kPyDetectorPath(), path_); Parameters::parse(parameters, Parameters::kPyDetectorPath(), path_);
Parameters::parse(parameters, Parameters::kPyDetectorCuda(), cuda_); Parameters::parse(parameters, Parameters::kPyDetectorCuda(), cuda_);
path_ = uReplaceChar(path_, '~', UDirectory::homeDir()); path_ = uReplaceChar(path_, '~', UDirectory::homeDir());
} }
std::vector<cv::KeyPoint> PyDetector::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask) std::vector<cv::KeyPoint> PyDetector::generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask)
{ {
UDEBUG(""); UDEBUG("");
descriptors_ = cv::Mat(); descriptors_ = cv::Mat();
UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U); UASSERT(!image.empty() && image.channels() == 1 && image.depth() == CV_8U);
std::vector<cv::KeyPoint> keypoints; std::vector<cv::KeyPoint> keypoints;
cv::Mat imgRoi(image, roi); cv::Mat imgRoi(image, roi);
UTimer timer; UTimer timer;
if(!pModule_) if(!pModule_)
{ {
UERROR("Python detector module not loaded!"); UERROR("Python detector module not loaded!");
return keypoints; return keypoints;
} }
pybind11::gil_scoped_acquire acquire; pybind11::gil_scoped_acquire acquire;
if(!pFunc_) if(!pFunc_)
{ {
PyObject * pFunc = PyObject_GetAttrString(pModule_, "init"); PyObject * pFunc = PyObject_GetAttrString(pModule_, "init");
if(pFunc) if(pFunc)
{ {
if(PyCallable_Check(pFunc)) if(PyCallable_Check(pFunc))
{ {
PyObject * result = PyObject_CallFunction(pFunc, "i", cuda_?1:0); PyObject * result = PyObject_CallFunction(pFunc, "i", cuda_?1:0);
if(result == NULL) if(result == NULL)
{ {
UERROR("Call to \"init(...)\" in \"%s\" failed!", path_.c_str()); UERROR("Call to \"init(...)\" in \"%s\" failed!", path_.c_str());
UERROR("%s", getPythonTraceback().c_str()); UERROR("%s", getPythonTraceback().c_str());
return keypoints; return keypoints;
} }
Py_DECREF(result); Py_DECREF(result);
pFunc_ = PyObject_GetAttrString(pModule_, "detect"); pFunc_ = PyObject_GetAttrString(pModule_, "detect");
if(pFunc_ && PyCallable_Check(pFunc_)) if(pFunc_ && PyCallable_Check(pFunc_))
{ {
// we are ready! // we are ready!
} }
else else
{ {
UERROR("Cannot find method \"detect(...)\" in %s", path_.c_str()); UERROR("Cannot find method \"detect(...)\" in %s", path_.c_str());
UERROR("%s", getPythonTraceback().c_str()); UERROR("%s", getPythonTraceback().c_str());
if(pFunc_) if(pFunc_)
{ {
Py_DECREF(pFunc_); Py_DECREF(pFunc_);
pFunc_ = 0; pFunc_ = 0;
} }
return keypoints; return keypoints;
} }
} }
else else
{ {
UERROR("Cannot call method \"init(...)\" in %s", path_.c_str()); UERROR("Cannot call method \"init(...)\" in %s", path_.c_str());
UERROR("%s", getPythonTraceback().c_str()); UERROR("%s", getPythonTraceback().c_str());
return keypoints; return keypoints;
} }
Py_DECREF(pFunc); Py_DECREF(pFunc);
} }
else else
{ {
UERROR("Cannot find method \"init(...)\""); UERROR("Cannot find method \"init(...)\"");
UERROR("%s", getPythonTraceback().c_str()); UERROR("%s", getPythonTraceback().c_str());
return keypoints; return keypoints;
} }
UDEBUG("init time = %fs", timer.ticks()); UDEBUG("init time = %fs", timer.ticks());
} }
if(pFunc_) if(pFunc_)
{ {
npy_intp dims[2] = {imgRoi.rows, imgRoi.cols}; npy_intp dims[2] = {imgRoi.rows, imgRoi.cols};
PyObject* pImageBuffer = PyArray_SimpleNewFromData(2, dims, NPY_UBYTE, (void*)imgRoi.data); PyObject * pImageBuffer = PyArray_SimpleNewFromData(2, dims, NPY_UBYTE, (void*)imgRoi.data);
UASSERT(pImageBuffer); UASSERT(pImageBuffer);
UDEBUG("Preparing data time = %fs", timer.ticks()); UDEBUG("Preparing data time = %fs", timer.ticks());
PyObject *pReturn = PyObject_CallFunctionObjArgs(pFunc_, pImageBuffer, NULL); PyObject * pReturn = PyObject_CallFunctionObjArgs(pFunc_, pImageBuffer, NULL);
if(pReturn == NULL) if(pReturn == NULL)
{ {
UERROR("Failed to call match() function!"); UERROR("Failed to call match() function!");
UERROR("%s", getPythonTraceback().c_str()); UERROR("%s", getPythonTraceback().c_str());
} }
else else
{ {
UDEBUG("Python detector time = %fs", timer.ticks()); UDEBUG("Python detector time = %fs", timer.ticks());
if (PyTuple_Check(pReturn) && PyTuple_GET_SIZE(pReturn) == 2) if (PyTuple_Check(pReturn) && PyTuple_GET_SIZE(pReturn) == 2)
{ {
PyObject *kptsPtr = PyTuple_GET_ITEM(pReturn, 0); PyObject * kptsPtr = PyTuple_GET_ITEM(pReturn, 0);
PyObject *descPtr = PyTuple_GET_ITEM(pReturn, 1); PyObject * descPtr = PyTuple_GET_ITEM(pReturn, 1);
if(PyArray_Check(kptsPtr) && PyArray_Check(descPtr)) if(PyArray_Check(kptsPtr) && PyArray_Check(descPtr))
{ {
PyArrayObject *arrayPtr = reinterpret_cast<PyArrayObject*>(kptsPtr); PyArrayObject *arrayPtr = reinterpret_cast<PyArrayObject*>(kptsPtr);
int nKpts = PyArray_SHAPE(arrayPtr)[0]; int nKpts = PyArray_SHAPE(arrayPtr)[0];
int kptSize = PyArray_SHAPE(arrayPtr)[1]; int kptSize = PyArray_SHAPE(arrayPtr)[1];
int type = PyArray_TYPE(arrayPtr); int type = PyArray_TYPE(arrayPtr);
UDEBUG("Kpts array %dx%d (type=%d)", nKpts, kptSize, type); UDEBUG("Kpts array %dx%d (type=%d)", nKpts, kptSize, type);
UASSERT(kptSize == 3); UASSERT(kptSize == 3);
UASSERT_MSG(type == NPY_FLOAT, uFormat("Returned matches should type FLOAT=11, received type=%d", type).c_str()); UASSERT_MSG(type == NPY_FLOAT, uFormat("Returned matches should type FLOAT=11, received type=%d", type).c_str());
float* c_out = reinterpret_cast<float*>(PyArray_DATA(arrayPtr)); float* c_out = reinterpret_cast<float*>(PyArray_DATA(arrayPtr));
keypoints.reserve(nKpts); std::vector<bool> keep_kpt(nKpts);
for (int i = 0; i < nKpts*kptSize; i+=kptSize) keypoints.reserve(nKpts);
{ for (int i = 0, kpt_idx = 0; i < nKpts*kptSize; i+=kptSize, kpt_idx++)
cv::KeyPoint kpt(c_out[i], c_out[i+1], 8, -1, c_out[i+2]); {
keypoints.push_back(kpt); // x,y in full image coordinates. Mask is in full image coordinates too.
} int full_x = (int)(c_out[i] + roi.x);
int full_y = (int)(c_out[i+1] + roi.y);
arrayPtr = reinterpret_cast<PyArrayObject*>(descPtr); keep_kpt[kpt_idx] = mask.empty() || (full_x >= 0 && full_x < mask.cols && full_y >= 0 && full_y < mask.rows && mask.at<unsigned char>(full_y, full_x) != 0);
int nDesc = PyArray_SHAPE(arrayPtr)[0]; if(keep_kpt[kpt_idx]) {
UASSERT(nDesc = nKpts); cv::KeyPoint kpt(c_out[i], c_out[i+1], 8, -1, c_out[i+2]);
int dim = PyArray_SHAPE(arrayPtr)[1]; keypoints.push_back(kpt);
type = PyArray_TYPE(arrayPtr); }
UDEBUG("Desc array %dx%d (type=%d)", nDesc, dim, type); }
UASSERT_MSG(type == NPY_FLOAT, uFormat("Returned matches should type FLOAT=11, received type=%d", type).c_str());
arrayPtr = reinterpret_cast<PyArrayObject*>(descPtr);
c_out = reinterpret_cast<float*>(PyArray_DATA(arrayPtr)); int nDesc = PyArray_SHAPE(arrayPtr)[0];
for (int i = 0; i < nDesc*dim; i+=dim) UASSERT(nDesc = nKpts);
{ int dim = PyArray_SHAPE(arrayPtr)[1];
cv::Mat descriptor = cv::Mat(1, dim, CV_32FC1, &c_out[i]).clone(); type = PyArray_TYPE(arrayPtr);
descriptors_.push_back(descriptor); UDEBUG("Desc array %dx%d (type=%d)", nDesc, dim, type);
} UASSERT_MSG(type == NPY_FLOAT, uFormat("Returned matches should type FLOAT=11, received type=%d", type).c_str());
}
} c_out = reinterpret_cast<float*>(PyArray_DATA(arrayPtr));
else for (int i = 0, kpt_idx = 0; i < nDesc*dim; i+=dim, kpt_idx++)
{ {
UWARN("Expected tuple (Kpts 3 x N, Descriptors dim x N), returning empty features."); if(keep_kpt[kpt_idx]) {
} cv::Mat descriptor = cv::Mat(1, dim, CV_32FC1, &c_out[i]).clone();
Py_DECREF(pReturn); descriptors_.push_back(descriptor);
} }
Py_DECREF(pImageBuffer); }
} }
}
return keypoints; else
} {
UWARN("Expected tuple (Kpts 3 x N, Descriptors dim x N), returning empty features.");
cv::Mat PyDetector::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const }
{ Py_DECREF(pReturn);
UASSERT((int)keypoints.size() == descriptors_.rows); }
return descriptors_; Py_DECREF(pImageBuffer);
} }
} // Apply limitKeypoints to enforce maxFeatures and SSC
this->limitKeypoints(keypoints, descriptors_, this->getMaxFeatures(), cv::Size(roi.width, roi.height), this->getSSC());
return keypoints;
}
cv::Mat PyDetector::generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const
{
UASSERT((int)keypoints.size() == descriptors_.rows);
return descriptors_;
}
}
@@ -0,0 +1,71 @@
#! /usr/bin/env python3
#
# Drop this file in the root folder of SuperPoint git: https://github.com/rpautrat/SuperPoint
# To use with rtabmap:
# --Vis/FeatureType 15 --Kp/DetectorStrategy 15 --PyDetector/Path "~/SuperPoint/rtabmap_superpoint_rpautrat.py"
#
import numpy as np
import os
import torch
from superpoint_pytorch import SuperPoint
superpoint = []
device = 'cpu'
def init(cuda):
global superpoint, device
superpoint = SuperPoint().eval()
# set up device, gpu or cpu depending on the availability and the user's choice
device = 'cuda' if torch.cuda.is_available() and cuda else 'cpu'
# Load weights directly to target device
# Get the directory where this script is located
script_dir = os.path.dirname(os.path.abspath(__file__))
weights_path = os.path.join(script_dir, 'weights', 'superpoint_v6_from_tf.pth')
# Load model weights with proper error handling
try:
state_dict = torch.load(weights_path, map_location=device, weights_only=True)
superpoint.load_state_dict(state_dict)
except Exception as e:
print(f"Error loading weights: {e}")
raise
# Move the model to the target device
superpoint.to(device)
# Ensure model is in eval mode for inference
superpoint.eval()
def detect(imageBuffer):
global superpoint, device
image = np.asarray(imageBuffer)
image = (image.astype('float32') / 255.)
try:
image_with_dims = image[None, None] # Add batch and channel dims
image_tensor = torch.from_numpy(image_with_dims).float()
image_tensor = image_tensor.to(device)
except Exception as e:
print(f"Error creating tensor: {e}")
raise
# Result: (1, 1, H, W) - PyTorch tensor on correct device (CPU or GPU).
with torch.no_grad():
pred = superpoint({'image': image_tensor})
# Extract keypoints and descriptors
keypoints = pred['keypoints'][0].cpu().numpy() # Shape: (N, 2)
keypoints_response = pred['keypoint_scores'][0].cpu().numpy()
keypoints_with_response = np.column_stack([keypoints, keypoints_response]).astype(np.float32)
# Result: (N, 3) with [x, y, response]
descriptors = pred['descriptors'][0].cpu().numpy()
# Result: (N, descriptor_dim)
desc = np.float32(descriptors).copy()
pts = np.float32(keypoints_with_response).copy()
return pts, desc
+3 -1
View File
@@ -131,7 +131,9 @@ CREATE TABLE Admin (
opt_map BLOB, -- compressed CV_8SC1 occupancy grid opt_map BLOB, -- compressed CV_8SC1 occupancy grid
opt_map_x_min FLOAT, opt_map_x_min FLOAT,
opt_map_y_min FLOAT, opt_map_y_min FLOAT,
opt_map_resolution FLOAT, opt_map_resolution FLOAT,
dictionary_index BLOB, -- serialized dictionary index
time_enter DATE time_enter DATE
); );
@@ -0,0 +1,183 @@
-- *******************************************************************
-- DatabaseSchema: Script for creating the database
-- Usage:
-- $ sqlite3 LTM.db < DatabaseSchema.sql
--
-- *******************************************************************
-- *******************************************************************
-- CLEAN
-- *******************************************************************
/*DROP TABLE Node;*/
-- *******************************************************************
-- CREATE
-- *******************************************************************
CREATE TABLE Node (
id INTEGER NOT NULL,
map_id INTEGER NOT NULL,
weight INTEGER,
stamp FLOAT,
pose BLOB, -- 3x4 float
ground_truth_pose BLOB, -- 3x4 float
velocity BLOB, -- 6 float (vx,vy,vz,vroll,vpitch,vyaw) m/s and rad/s
label TEXT,
gps BLOB, -- 1x6 double: stamp, longitude (DD), latitude (DD), altitude (m), accuracy (m), bearing (North 0->360 deg clockwise)
env_sensors BLOB, -- Variable 3xdouble: (sensorId1, value, stamp, sensorId2, value, stamp, ...)
time_enter DATE,
PRIMARY KEY (id)
);
CREATE TABLE Data (
id INTEGER NOT NULL,
image BLOB, -- compressed image (Grayscale or RGB)
depth BLOB, -- compressed image (Depth or Right image)
depth_confidence BLOB, -- compressed data (low=0 high=100)
calibration BLOB, -- fx, fy, cx, cy, [baseline,] width, height, local_transform
scan BLOB, -- compressed data (Laser scan)
scan_info BLOB, -- scan_max_pts, scan_max_range, scan_format, local_transform
ground_cells BLOB, -- compressed data (occupancy grid)
obstacle_cells BLOB, -- compressed data (occupancy grid)
empty_cells BLOB, -- compressed data (occupancy grid)
cell_size FLOAT,
view_point_x FLOAT,
view_point_y FLOAT,
view_point_z FLOAT,
user_data BLOB, -- compressed data (User data)
time_enter DATE,
PRIMARY KEY (id)
);
CREATE TABLE Link (
from_id INTEGER NOT NULL,
to_id INTEGER NOT NULL,
type INTEGER NOT NULL, -- kNeighbor=0, kGlobalClosure=1, kLocalSpaceClosure=2, kLocalTimeClosure=3, kUserClosure=4, kVirtualClosure=5, kNeighborMerged=6, kPosePrior=7, kLandmark=8
information_matrix BLOB NOT NULL, -- 6x6 double (inverse covariance)
transform BLOB, -- 3x4 float
user_data BLOB, -- compressed data (User data)
FOREIGN KEY (from_id) REFERENCES Node(id),
FOREIGN KEY (to_id) REFERENCES Node(id)
);
--
CREATE TABLE Word (
id INTEGER NOT NULL,
descriptor_size INTEGER NOT NULL,
descriptor BLOB NOT NULL,
time_enter DATE,
PRIMARY KEY (id)
);
CREATE TABLE Feature (
node_id INTEGER NOT NULL,
word_id INTEGER NOT NULL,
pos_x FLOAT NOT NULL,
pos_y FLOAT NOT NULL,
size INTEGER NOT NULL,
dir FLOAT NOT NULL,
response FLOAT NOT NULL,
octave INTEGER NOT NULL,
depth_x FLOAT,
depth_y FLOAT,
depth_z FLOAT,
descriptor_size INTEGER,
descriptor BLOB,
FOREIGN KEY (node_id) REFERENCES Node(id)
);
CREATE TABLE GlobalDescriptor (
node_id INTEGER NOT NULL,
type INTEGER NOT NULL,
info BLOB,
data BLOB NOT NULL,
FOREIGN KEY (node_id) REFERENCES Node(id)
);
--
CREATE TABLE Info (
STM_size INTEGER,
last_sign_added INTEGER,
process_mem_used INTEGER,
database_mem_used INTEGER,
dictionary_size INTEGER,
parameters TEXT,
time_enter DATE
);
CREATE TABLE Statistics (
id INTEGER NOT NULL,
stamp FLOAT,
data BLOB, -- compressed string
wm_state BLOB, -- compressed data
FOREIGN KEY (id) REFERENCES Node(id)
);
CREATE TABLE Admin (
version TEXT,
preview_image BLOB, -- compressed image
opt_cloud BLOB, -- compressed data
opt_ids BLOB, -- Node ids used to generate the optimized cloud/mesh
opt_poses BLOB, -- compressed N*3x4 float
opt_last_localization BLOB, -- 3x4 float
opt_polygons_size INTEGER, -- e.g., 3
opt_polygons BLOB, -- compressed data [length_v0, i0,i1,i3, length_v1, i0,i1,i3]
opt_tex_coords BLOB, -- compressed data [length_v0, u0,v0,u1,v1,u2,v2, length_v1, u0,v0,u1,v1,u2,v2]
opt_tex_materials BLOB, -- compressed image
opt_map BLOB, -- compressed CV_8SC1 occupancy grid
opt_map_x_min FLOAT,
opt_map_y_min FLOAT,
opt_map_resolution FLOAT,
time_enter DATE
);
-- *******************************************************************
-- TRIGGERS
-- *******************************************************************
CREATE TRIGGER insert_Feature BEFORE INSERT ON Feature
WHEN NOT EXISTS (SELECT Node.id FROM Node WHERE Node.id = NEW.node_id)
BEGIN
SELECT RAISE(ABORT, 'Foreign key constraint failed in Feature table');
END;
-- Creating a trigger for time_enter
CREATE TRIGGER insert_Node_timeEnter AFTER INSERT ON Node
BEGIN
UPDATE Node SET time_enter = DATETIME('NOW') WHERE rowid = new.rowid;
END;
CREATE TRIGGER insert_Data_timeEnter AFTER INSERT ON Data
BEGIN
UPDATE Node SET time_enter = DATETIME('NOW') WHERE rowid = new.rowid;
END;
CREATE TRIGGER insert_Word_timeEnter AFTER INSERT ON Word
BEGIN
UPDATE Word SET time_enter = DATETIME('NOW') WHERE rowid = new.rowid;
END;
CREATE TRIGGER insert_Info_timeEnter AFTER INSERT ON Info
BEGIN
UPDATE Info SET time_enter = DATETIME('NOW') WHERE rowid = new.rowid;
END;
-- *******************************************************************
-- INDEXES
-- *******************************************************************
CREATE UNIQUE INDEX IDX_Node_id on Node (id);
CREATE INDEX IDX_Feature_node_id on Feature (node_id);
CREATE INDEX IDX_GlobalDescriptor_node_id on GlobalDescriptor (node_id);
CREATE INDEX IDX_Link_from_id on Link (from_id);
CREATE UNIQUE INDEX IDX_node_label on Node (label);
CREATE UNIQUE INDEX IDX_Statistics_id on Statistics (id);
-- *******************************************************************
-- VERSION
-- *******************************************************************
INSERT INTO Admin(version) VALUES('0.22.0');
+31 -2
View File
@@ -103,7 +103,6 @@ public:
{ {
flann_algorithm_t index_type = get_param<flann_algorithm_t>(params,"algorithm"); flann_algorithm_t index_type = get_param<flann_algorithm_t>(params,"algorithm");
loaded_ = false; loaded_ = false;
if (index_type == FLANN_INDEX_SAVED) { if (index_type == FLANN_INDEX_SAVED) {
nnIndex_ = load_saved_index(features, get_param<std::string>(params,"filename"), distance); nnIndex_ = load_saved_index(features, get_param<std::string>(params,"filename"), distance);
loaded_ = true; loaded_ = true;
@@ -180,10 +179,20 @@ public:
if (fout == NULL) { if (fout == NULL) {
throw FLANNException("Cannot open file"); throw FLANNException("Cannot open file");
} }
nnIndex_->saveIndex(fout); save(fout);
fclose(fout); fclose(fout);
} }
/**
* Save index to file stream.
* Caller has to open file stream with "wb" and close it afterwards.
* @param filename
*/
void save(FILE * stream)
{
nnIndex_->saveIndex(stream);
}
/** /**
* \returns number of features in this index. * \returns number of features in this index.
*/ */
@@ -377,6 +386,26 @@ public:
return nnIndex_->radiusSearch(queries, indices, dists, radius, params); return nnIndex_->radiusSearch(queries, indices, dists, radius, params);
} }
void load_saved_index(FILE* fin)
{
if(loaded_) {
throw FLANNException("Index already loaded!");
}
if(nnIndex_->sizeAtBuild() != 0) {
throw FLANNException("Index must not be already built to load data.");
}
if (fin == NULL) {
throw FLANNException("File pointer must be valid!");
}
IndexHeader header = load_header(fin);
if (header.h.data_type != flann_datatype_value<ElementType>::value) {
throw FLANNException("Datatype of saved index is different than of the one to be loaded.");
}
rewind(fin);
nnIndex_->loadIndex(fin);
loaded_ = true;
}
private: private:
IndexType* load_saved_index(const Matrix<ElementType>& dataset, const std::string& filename, Distance distance) IndexType* load_saved_index(const Matrix<ElementType>& dataset, const std::string& filename, Distance distance)
{ {
@@ -0,0 +1,234 @@
/**
* SuperPoint implementation based on the PyTorch version by Rémi Pautrat, Paul-Edouard Sarlin
* Adapted for RTAB-Map integration
*/
#include "SuperpointRpautrat.h"
#include <rtabmap/core/Features2d.h>
#include <rtabmap/utilite/ULogger.h>
#include <rtabmap/utilite/UDirectory.h>
#include <rtabmap/utilite/UFile.h>
#include <rtabmap/utilite/UConversion.h>
#include <pybind11/embed.h>
#include <torch/torch.h>
#include <torch/script.h>
#include <opencv2/opencv.hpp>
#include <fstream>
#include <sstream>
#include "superpoint_to_torchscript_py.h"
namespace rtabmap
{
// Run the python script to export the SuperPoint model file with the desired parameters
static std::string exportSuperPointTorchScript(
const std::string & superpointWeightsPath,
const std::string & superpointModelPath,
const std::string & outputDir,
const int & width,
const int & height,
const float & threshold,
const int & nms_radius,
const bool & cuda)
{
// Validate output directory is explicitly set and exists
if(outputDir.empty())
{
UERROR("Output directory is not set.");
return std::string("");
}
if(!UDirectory::exists(outputDir))
{
UERROR("Output directory does not exist: %s", outputDir.c_str());
return std::string("");
}
// Resolve paths (no dependency on source tree)
const std::string weightsPath = superpointWeightsPath;
const std::string modelPath = superpointModelPath;
const std::string output = std::string(outputDir + "/superpoint_v6_from_tf.pt");
// Sanity checks
if(!UFile::exists(weightsPath)) {
UERROR("Weights not found: %s", weightsPath.c_str());
return "";
}
if(!UFile::exists(modelPath)) {
UERROR("Model not found: %s", modelPath.c_str());
return "";
}
// Execute the script inside the embedded Python interpreter
try
{
pybind11::gil_scoped_acquire acquire;
pybind11::dict scope;
scope["__builtins__"] = pybind11::module_::import("builtins");
// set sys.path to the location of the model definition so it can be imported
std::string model_dir = UDirectory::getDir(modelPath);
auto sys = pybind11::module_::import("sys");
pybind11::list sys_path = sys.attr("path");
sys_path.attr("insert")(0, model_dir);
try {
// execute the script to generate the model
pybind11::exec(uHex2Str(SUPERPOINT_TO_TORCHSCRIPT_PY), scope, scope);
pybind11::function generate_model = scope["generate_model"].cast<pybind11::function>();
pybind11::object result = generate_model(weightsPath, output, cuda, nms_radius, threshold, width, height);
sys_path.attr("remove")(model_dir);
}
catch(...) {
// Ensure sys.path cleanup on any exception
sys_path.attr("remove")(model_dir);
throw;
}
}
// pybind11 throws std::exception for RuntimeError
catch (const std::exception &e)
{
UERROR("Python export failed: %s", e.what());
return "";
}
return output;
}
SPDetectorRpautrat::SPDetectorRpautrat(std::string superpointWeightsPath, std::string superpointModelPath, std::string outputDir, float threshold, bool nms, int minDistance, bool cuda, int maxFeatures, bool ssc) :
device_(torch::kCPU),
superpointWeightsPath_(superpointWeightsPath),
superpointModelPath_(superpointModelPath),
outputDir_(outputDir),
threshold_(threshold),
nms_(nms),
minDistance_(minDistance),
maxFeatures_(maxFeatures),
ssc_(ssc),
detected_(false)
{
if(cuda && !torch::cuda::is_available())
{
UWARN("Cuda option is enabled but torch doesn't have cuda support on this platform, using CPU instead.");
}
cuda_ = cuda && torch::cuda::is_available();
if(!UFile::exists(superpointWeightsPath_)) {
UERROR("Superpoint weights not found: %s", superpointWeightsPath_.c_str());
}
// Update device based on cuda availability
device_ = torch::Device(cuda_ ? torch::kCUDA : torch::kCPU);
}
SPDetectorRpautrat::~SPDetectorRpautrat()
{
}
cv::Mat SPDetectorRpautrat::compute(const std::vector<cv::KeyPoint> &keypoints)
{
if(!detected_)
{
UERROR("SPDetector has been reset before extracting the descriptors! detect() should be called before compute().");
return cv::Mat();
}
if(keypoints.empty())
{
return cv::Mat();
}
// These should have the same size
UASSERT(static_cast<size_t>(desc_.rows) == keypoints.size());
return desc_;
}
std::vector<cv::KeyPoint> SPDetectorRpautrat::detect(const cv::Mat &img, const cv::Mat & mask)
{
// On first frame, run a trace of the model with the desired parameters and load the model file
if(!detected_)
{
// effectively disable nms if it is not enabled by setting radius to 0
int nms_radius = nms_ ? minDistance_ : 0;
std::string modelPath = exportSuperPointTorchScript(
superpointWeightsPath_,
superpointModelPath_,
outputDir_,
img.cols,
img.rows,
threshold_,
nms_radius,
cuda_
);
UDEBUG("Initializing SuperPoint Rpautrat detector with model: %s", modelPath.c_str());
UDEBUG("modelPath=%s thr=%f nms=%d minDistance=%d cuda=%d", modelPath.c_str(), threshold_, nms_?1:0, minDistance_, cuda_?1:0);
if(modelPath.empty())
{
UERROR("Model's path is empty! The model was not exported correctly.");
return std::vector<cv::KeyPoint>();
}
if(!UFile::exists(modelPath))
{
UERROR("Model's path \"%s\" doesn't exist!", modelPath.c_str());
return std::vector<cv::KeyPoint>();
}
// Load TorchScript model
model_ = torch::jit::load(modelPath);
model_.eval(); // put in evaluation mode
model_.to(device_);
}
// format the input tensor for the model
torch::NoGradGuard no_grad_guard;
auto x = torch::from_blob(img.data, {1, 1, img.rows, img.cols}, torch::kByte);
x = x.to(torch::kFloat) / 255;
x = x.set_requires_grad(false).to(device_);
auto outputs = model_.forward({x}).toTuple();
auto kpts_tensor = outputs->elements()[0].toTensor(); // [N, 2] keypoint coordinates
auto scores_tensor = outputs->elements()[1].toTensor(); // [N] keypoint scores
torch::Tensor desc_tensor = outputs->elements()[2].toTensor(); // [N, 256] descriptors
// Convert to CPU for processing
auto keypoints_cpu = kpts_tensor.to(torch::kCPU);
auto scores_cpu = scores_tensor.to(torch::kCPU);
std::vector<cv::KeyPoint> filtered_keypoints;
std::vector<int64_t> keep_indices_vec;
// Apply mask filtering
for(int i = 0; i < keypoints_cpu.size(0); i++) {
float score = scores_cpu[i].item<float>();
float x = keypoints_cpu[i][0].item<float>(); // x coordinate
float y = keypoints_cpu[i][1].item<float>(); // y coordinate
// Check mask if provided
if(mask.empty() || mask.at<unsigned char>((int)y, (int)x) != 0) {
keep_indices_vec.push_back(i);
filtered_keypoints.emplace_back(cv::KeyPoint(x, y, 8, -1, score));
}
}
// Filter descriptors based on mask
auto keep_indices = torch::from_blob(keep_indices_vec.data(), {(long int)keep_indices_vec.size()}, torch::kLong);
keep_indices = keep_indices.to(desc_tensor.device());
auto filtered_descriptors = desc_tensor.index_select(0, keep_indices);
// Convert descriptors to cv::Mat
auto filtered_descriptors_cpu = filtered_descriptors.to(torch::kCPU);
cv::Mat descriptors_mat(filtered_descriptors_cpu.size(0), filtered_descriptors_cpu.size(1), CV_32FC1, filtered_descriptors_cpu.data_ptr<float>());
cv::Mat descriptors_clone = descriptors_mat.clone(); // Clone to own the memory
// Apply limitKeypoints to enforce maxFeatures and SSC
Feature2D::limitKeypoints(filtered_keypoints, descriptors_clone, maxFeatures_, cv::Size(img.cols, img.rows), ssc_);
desc_ = descriptors_clone;
detected_ = true;
return filtered_keypoints;
}
} // namespace rtabmap
@@ -0,0 +1,59 @@
/**
* SuperPoint implementation based on the PyTorch version by Rémi Pautrat, Paul-Edouard Sarlin
* Adapted for RTAB-Map integration
*/
#ifndef SUPERPOINT_RPAUTRAT_H
#define SUPERPOINT_RPAUTRAT_H
#include <torch/torch.h>
#include <opencv2/opencv.hpp>
#include <vector>
#include <memory>
namespace rtabmap
{
class SPDetectorRpautrat {
public:
SPDetectorRpautrat(
std::string superpointWeightsPath,
std::string superpointModelPath,
std::string outputDir,
float threshold = 0.005f,
bool nms = true,
int nmsRadius = 4,
bool cuda = false,
int maxFeatures = 1000,
bool ssc = false
);
virtual ~SPDetectorRpautrat();
std::vector<cv::KeyPoint> detect(const cv::Mat &img, const cv::Mat & mask = cv::Mat());
cv::Mat compute(const std::vector<cv::KeyPoint> &keypoints);
// Setters for post-processing parameters that don't require model reinitialization
void setMaxFeatures(int maxFeatures) { maxFeatures_ = maxFeatures; }
void setSSC(bool ssc) { ssc_ = ssc; }
private:
torch::jit::script::Module model_;
torch::Device device_;
cv::Mat desc_;
std::string superpointWeightsPath_;
std::string superpointModelPath_;
std::string outputDir_;
float threshold_;
bool nms_;
int minDistance_;
bool cuda_;
int maxFeatures_;
bool ssc_;
bool detected_;
};
}
#endif // SUPERPOINT_RPAUTRAT_H
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
"""
Convert PyTorch weights to TorchScript format for C++ usage.
"""
import argparse
import os
import torch
import torch.nn as nn
from superpoint_pytorch import SuperPoint
def wrap_model(model: nn.Module):
"""
Simple wrapper to fix SuperPoint input format for TorchScript.
Easier to call from C++ code since the input isn't a dictionary.
"""
class Wrapper(nn.Module):
def __init__(self, net: nn.Module):
super().__init__()
self.net = net
def forward(self, x: torch.Tensor):
# SuperPoint expects {"image": tensor} but TorchScript doesn't like dict indexing
out = self.net.forward({"image": x})
# Return the format expected by C++ code: keypoints, scores, descriptors
# For single batch item, take the first (and only) element
keypoints = out["keypoints"][0] if out["keypoints"] else torch.empty(0, 2)
scores = out["keypoint_scores"][0] if out["keypoint_scores"] else torch.empty(0)
descriptors = out["descriptors"][0] if out["descriptors"] else torch.empty(0, 256)
return (keypoints, scores, descriptors)
return Wrapper(model)
def generate_model(
weights_path: str,
output_path: str,
cuda: bool,
nms_radius: int,
threshold: float,
width: int,
height: int,
):
# Check if weights are already TorchScript
try:
scripted = torch.jit.load(weights_path, map_location="cpu")
scripted.eval()
torch.jit.save(scripted, output_path)
print(f"Converted TorchScript file: {output_path}")
return
except:
pass
device = "cuda" if cuda else "cpu"
# Load SuperPoint model and weights
model = SuperPoint(
nms_radius=nms_radius,
detection_threshold=threshold,
).eval().to(device)
# Load weights without forcing CPU location to allow CUDA usage
weights = torch.load(weights_path, map_location=None)
if isinstance(weights, dict) and "state_dict" in weights:
weights = weights["state_dict"]
model.load_state_dict(weights, strict=False)
wrapped = wrap_model(model)
dummy = torch.randn(1, 1, height, width, device=device) # Dummy input, grayscale, using cuda.
# Convert to TorchScript using trace (SuperPoint has dynamic behavior that scripting can't handle)
print("Using torch.jit.trace (SuperPoint has dynamic behavior)...")
scripted = torch.jit.trace(wrapped, (dummy,), strict=False)
print("Successfully traced SuperPoint model")
# Save output
os.makedirs(os.path.dirname(output_path), exist_ok=True)
torch.jit.save(scripted, output_path)
print(f"Converted SuperPoint weights to TorchScript: {output_path}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Convert SuperPoint weights to TorchScript")
parser.add_argument("--weights", required=True, help="Path to weights file")
parser.add_argument("--output", required=True, help="Output TorchScript file")
parser.add_argument("--cuda", action="store_true", help="Use CUDA")
parser.add_argument("--width", type=int, default=1920, help="Width of the input image")
parser.add_argument("--height", type=int, default=288, help="Height of the input image")
parser.add_argument("--nms_radius", type=int, default=4, help="NMS radius")
parser.add_argument("--threshold", type=float, default=0.005, help="Confidence threshold")
args = parser.parse_args()
print(f"Generating model from weights: {args.weights} to output: {args.output}")
generate_model(
weights_path=args.weights,
output_path=args.output,
cuda=args.cuda,
nms_radius=args.nms_radius,
threshold=args.threshold,
width=args.width,
height=args.height,
)
+4 -2
View File
@@ -1048,7 +1048,8 @@ pcl::IndicesPtr cropBoxImpl(
const Transform & transform, const Transform & transform,
bool negative) bool negative)
{ {
UASSERT(min[0] < max[0] && min[1] < max[1] && min[2] < max[2]); UASSERT_MSG(min[0] < max[0] && min[1] < max[1] && min[2] <= max[2], // z can be equal in 2D case
uFormat("x=%f->%f y=%f->%f z=%f->%f", min[0], max[0], min[1], max[1], min[2], max[2]).c_str());
pcl::IndicesPtr output(new std::vector<int>); pcl::IndicesPtr output(new std::vector<int>);
pcl::CropBox<PointT> filter; pcl::CropBox<PointT> filter;
@@ -1070,7 +1071,8 @@ pcl::IndicesPtr cropBoxImpl(
pcl::IndicesPtr cropBox(const pcl::PCLPointCloud2::Ptr & cloud, const pcl::IndicesPtr & indices, const Eigen::Vector4f & min, const Eigen::Vector4f & max, const Transform & transform, bool negative) pcl::IndicesPtr cropBox(const pcl::PCLPointCloud2::Ptr & cloud, const pcl::IndicesPtr & indices, const Eigen::Vector4f & min, const Eigen::Vector4f & max, const Transform & transform, bool negative)
{ {
UASSERT(min[0] < max[0] && min[1] < max[1] && min[2] < max[2]); UASSERT_MSG(min[0] < max[0] && min[1] < max[1] && min[2] <= max[2], // z can be equal in 2D case
uFormat("x=%f->%f y=%f->%f z=%f->%f", min[0], max[0], min[1], max[1], min[2], max[2]).c_str());
pcl::IndicesPtr output(new std::vector<int>); pcl::IndicesPtr output(new std::vector<int>);
pcl::CropBox<pcl::PCLPointCloud2> filter; pcl::CropBox<pcl::PCLPointCloud2> filter;
+20 -7
View File
@@ -3,14 +3,17 @@
FROM ubuntu:24.04 FROM ubuntu:24.04
# Install build dependencies # Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends apt-utils RUN apt-get update && apt-get install -y --no-install-recommends \
apt-utils && apt-get clean && \
rm -rf /var/lib/apt/lists/
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
git unzip wget ant cmake \ git unzip wget ant cmake \
g++ lib32stdc++6 lib32z1 \ g++ lib32stdc++6 lib32z1 \
software-properties-common \ software-properties-common \
freeglut3-dev \ freeglut3-dev \
openjdk-8-jdk openjdk-8-jre \ openjdk-8-jdk openjdk-8-jre \
curl curl && \
apt-get clean && rm -rf /var/lib/apt/lists/
ENV ANDROID_HOME=/opt/android-sdk ENV ANDROID_HOME=/opt/android-sdk
ENV PATH=$PATH:/opt/android-sdk/cmdline-tools/latest/bin:/opt/android-sdk/tools:/opt/android-sdk/platform-tools:/opt/android-sdk/ndk/21.4.7075529 ENV PATH=$PATH:/opt/android-sdk/cmdline-tools/latest/bin:/opt/android-sdk/tools:/opt/android-sdk/platform-tools:/opt/android-sdk/ndk/21.4.7075529
@@ -53,6 +56,8 @@ RUN echo "Install boost..." && \
cd build && \ cd build && \
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" .. && \ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" .. && \
make -j4 && \ make -j4 && \
find . -type f -name "*.a" -print0 \
| xargs -0 $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip -g -S -d --strip-debug && \
make install && \ make install && \
cd /root && \ cd /root && \
rm -r boost_1_59_0.tar.gz boost_1_59_0 rm -r boost_1_59_0.tar.gz boost_1_59_0
@@ -80,6 +85,8 @@ RUN echo "Install flann..." && \
cd build && \ cd build && \
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DBUILD_PYTHON_BINDINGS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" .. && \ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DBUILD_PYTHON_BINDINGS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" .. && \
make -j4 && \ make -j4 && \
find . -type f -name "*.a" -print0 \
| xargs -0 $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip -g -S -d --strip-debug && \
make install && \ make install && \
cd /root && \ cd /root && \
rm -rf flann rm -rf flann
@@ -95,6 +102,8 @@ RUN echo "Install gtsam..." && \
cd build && \ cd build && \
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DMETIS_SHARED=OFF -DGTSAM_BUILD_STATIC_LIBRARY=ON -DGTSAM_BUILD_TESTS=OFF -DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF -DGTSAM_USE_SYSTEM_EIGEN=ON .. && \ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DMETIS_SHARED=OFF -DGTSAM_BUILD_STATIC_LIBRARY=ON -DGTSAM_BUILD_TESTS=OFF -DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF -DGTSAM_USE_SYSTEM_EIGEN=ON .. && \
make -j4 && \ make -j4 && \
find . -type f -name "*.a" -print0 \
| xargs -0 $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip -g -S -d --strip-debug && \
make install && \ make install && \
cd /root && \ cd /root && \
rm -rf gtsam rm -rf gtsam
@@ -108,6 +117,8 @@ RUN echo "Install g2o..." && \
cd build && \ cd build && \
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DBUILD_LGPL_SHARED_LIBS=OFF -DG2O_BUILD_APPS=OFF -DG2O_BUILD_EXAMPLES=OFF -DG2O_USE_OPENGL=OFF .. && \ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DBUILD_LGPL_SHARED_LIBS=OFF -DG2O_BUILD_APPS=OFF -DG2O_BUILD_EXAMPLES=OFF -DG2O_USE_OPENGL=OFF .. && \
make -j4 && \ make -j4 && \
find "../lib" -type f -name "*.a" -print0 \
| xargs -0 $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip -g -S -d --strip-debug && \
make install && \ make install && \
cd /root && \ cd /root && \
rm -rf g2o rm -rf g2o
@@ -117,12 +128,14 @@ RUN echo "Install VTK..." && \
git clone https://github.com/Kitware/VTK.git && \ git clone https://github.com/Kitware/VTK.git && \
cd VTK && \ cd VTK && \
git checkout tags/v8.2.0 && \ git checkout tags/v8.2.0 && \
wget https://gist.github.com/matlabbe/e217259fb8ece9ee6daf5a8f70e896a0/raw/2214b503a537d6431d764526b5b780f07d6f168d/vtk_8_2_0_android_r21_fix.patch && \ wget https://gist.github.com/matlabbe/e217259fb8ece9ee6daf5a8f70e896a0/raw/36d879f0827289e75de969afb7b6515d9a1c71d7/vtk_8_2_0_android_r21_fix.patch && \
git apply vtk_8_2_0_android_r21_fix.patch && \ git apply vtk_8_2_0_android_r21_fix.patch && \
mkdir build && \ mkdir build && \
cd build && \ cd build && \
cmake -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -DVTK_ANDROID_BUILD=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DANDROID_ARCH_ABI=arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" .. && \ cmake -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -DVTK_ANDROID_BUILD=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DANDROID_ARCH_ABI=arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" .. && \
make -j4 && \ make -j4 && \
find . -type f -name "*.a" -print0 \
| xargs -0 $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip -g -S -d --strip-debug && \
cp -r CMakeExternals/Install/vtk-android/* /opt/android/arm64-v8a/. && \ cp -r CMakeExternals/Install/vtk-android/* /opt/android/arm64-v8a/. && \
cd /root && \ cd /root && \
rm -rf VTK rm -rf VTK
@@ -140,6 +153,8 @@ RUN echo "Install pcl..." && \
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DBUILD_apps=OFF -DBUILD_examples=OFF -DBUILD_tools=OFF -DBUILD_visualization=OFF -DBUILD_tracking=OFF -DBUILD_people=OFF -DBUILD_tools=OFF -DBUILD_global_tests=OFF -DWITH_QT=OFF -DWITH_OPENGL=OFF -DWITH_VTK=ON -DPCL_SHARED_LIBS=OFF .. || true && \ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DBUILD_apps=OFF -DBUILD_examples=OFF -DBUILD_tools=OFF -DBUILD_visualization=OFF -DBUILD_tracking=OFF -DBUILD_people=OFF -DBUILD_tools=OFF -DBUILD_global_tests=OFF -DWITH_QT=OFF -DWITH_OPENGL=OFF -DWITH_VTK=ON -DPCL_SHARED_LIBS=OFF .. || true && \
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DBUILD_apps=OFF -DBUILD_examples=OFF -DBUILD_tools=OFF -DBUILD_visualization=OFF -DBUILD_tracking=OFF -DBUILD_people=OFF -DBUILD_tools=OFF -DBUILD_global_tests=OFF -DWITH_QT=OFF -DWITH_OPENGL=OFF -DWITH_VTK=ON -DPCL_SHARED_LIBS=OFF .. && \ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DBUILD_apps=OFF -DBUILD_examples=OFF -DBUILD_tools=OFF -DBUILD_visualization=OFF -DBUILD_tracking=OFF -DBUILD_people=OFF -DBUILD_tools=OFF -DBUILD_global_tests=OFF -DWITH_QT=OFF -DWITH_OPENGL=OFF -DWITH_VTK=ON -DPCL_SHARED_LIBS=OFF .. && \
make -j4 && \ make -j4 && \
find . -type f -name "*.a" -print0 \
| xargs -0 $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip -g -S -d --strip-debug && \
make install && \ make install && \
cd /root && \ cd /root && \
rm -rf pcl rm -rf pcl
@@ -161,14 +176,12 @@ RUN echo "Install OpenCV..." && \
cd build && \ cd build && \
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DOPENCV_EXTRA_MODULES_PATH=/root/opencv_contrib/modules -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DWITH_CUDA=OFF -DBUILD_opencv_structured_light=OFF -DBUILD_ANDROID_PROJECTS=OFF -DOPENCV_ENABLE_NONFREE=ON -DBUILD_ANDROID_EXAMPLES=OFF -DWITH_PROTOBUF=OFF -DBUILD_opencv_stereo=OFF -DBUILD_JAVA=OFF -DWITH_QUIRC=OFF -DBUILD_opencv_js_bindings_generator=OFF -DBUILD_opencv_objc_bindings_generator=OFF -DBUILD_opencv_objdetect=OFF -DBUILD_opencv_xobjdetect=OFF .. && \ cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_NDK=$ANDROID_NDK -DANDROID_NATIVE_API_LEVEL=23 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/android/arm64-v8a -DCMAKE_FIND_ROOT_PATH="/opt/android/arm64-v8a/bin;/opt/android/arm64-v8a;/opt/android/arm64-v8a/share" -DOPENCV_EXTRA_MODULES_PATH=/root/opencv_contrib/modules -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DWITH_CUDA=OFF -DBUILD_opencv_structured_light=OFF -DBUILD_ANDROID_PROJECTS=OFF -DOPENCV_ENABLE_NONFREE=ON -DBUILD_ANDROID_EXAMPLES=OFF -DWITH_PROTOBUF=OFF -DBUILD_opencv_stereo=OFF -DBUILD_JAVA=OFF -DWITH_QUIRC=OFF -DBUILD_opencv_js_bindings_generator=OFF -DBUILD_opencv_objc_bindings_generator=OFF -DBUILD_opencv_objdetect=OFF -DBUILD_opencv_xobjdetect=OFF .. && \
make -j4 && \ make -j4 && \
find . -type f -name "*.a" -print0 \
| xargs -0 $ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip -g -S -d --strip-debug && \
make install && \ make install && \
cd /root && \ cd /root && \
rm -rf opencv opencv_contrib rm -rf opencv opencv_contrib
RUN echo "Strip libraries..." && \
$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip -g -S -d --strip-debug --verbose /opt/android/arm64-v8a/lib/*.a && \
$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip -g -S -d --strip-debug --verbose /opt/android/arm64-v8a/sdk/native/staticlibs/arm64-v8a/*.a
RUN mkdir /opt/android/lib RUN mkdir /opt/android/lib
# tango # tango
+114 -33
View File
@@ -50,7 +50,24 @@ void showUsage()
{ {
printf("\nUsage:\n" printf("\nUsage:\n"
"rtabmap-rgbd_mapping driver\n" "rtabmap-rgbd_mapping driver\n"
" driver Driver number to use: 0=OpenNI-PCL, 1=OpenNI2, 2=Freenect, 3=OpenNI-CV, 4=OpenNI-CV-ASUS, 5=Freenect2, 6=ZED SDK, 7=RealSense, 8=RealSense2 9=Kinect for Azure SDK 10=MYNT EYE S\n\n"); " driver Driver number to use: 0=OpenNI-PCL (Kinect)\n"
" 1=OpenNI2 (Kinect and Xtion PRO Live)\n"
" 2=Freenect (Kinect)\n"
" 3=OpenNI-CV (Kinect)\n"
" 4=OpenNI-CV-ASUS (Xtion PRO Live)\n"
" 5=Freenect2 (Kinect v2)\n"
" 6=DC1394 (Bumblebee2)\n"
" 7=FlyCapture2 (Bumblebee2)\n"
" 8=ZED stereo\n"
" 9=RealSense\n"
" 10=Kinect for Windows 2 SDK\n"
" 11=RealSense2\n"
" 12=Kinect for Azure SDK\n"
" 13=MYNT EYE S\n"
" 14=ZED Open Capture\n"
" 15=depthai-core\n"
" 16=XVSDK (SeerSense)\n"
" 17=Orbbec SDK\n\n");
exit(1); exit(1);
} }
@@ -58,7 +75,7 @@ using namespace rtabmap;
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
ULogger::setType(ULogger::kTypeConsole); ULogger::setType(ULogger::kTypeConsole);
ULogger::setLevel(ULogger::kWarning); ULogger::setLevel(ULogger::kInfo);
#ifdef RTABMAP_PYTHON #ifdef RTABMAP_PYTHON
PythonInterface python; // Make sure we initialize python in main thread PythonInterface python; // Make sure we initialize python in main thread
@@ -72,92 +89,120 @@ int main(int argc, char * argv[])
else else
{ {
driver = atoi(argv[argc-1]); driver = atoi(argv[argc-1]);
if(driver < 0 || driver > 10) if(driver < 0 || driver > 17)
{ {
UERROR("driver should be between 0 and 10."); UERROR("driver should be between 0 and 17.");
showUsage(); showUsage();
} }
} }
// Here is the pipeline that we will use: // Here is the pipeline that we will use:
// CameraOpenni -> "SensorEvent" -> OdometryThread -> "OdometryEvent" -> RtabmapThread -> "RtabmapEvent" // CameraOpenni -> "SensorEvent" -> OdometryThread -> "OdometryEvent" -> RtabmapThread -> "RtabmapEvent"
// Create the OpenNI camera, it will send a SensorEvent at the rate specified.
// Set transform to camera so z is up, y is left and x going forward
Camera * camera = 0; Camera * camera = 0;
if(driver == 1) if (driver == 0)
{ {
if(!CameraOpenNI2::available()) camera = new rtabmap::CameraOpenni();
}
else if (driver == 1)
{
if (!rtabmap::CameraOpenNI2::available())
{ {
UERROR("Not built with OpenNI2 support..."); UERROR("Not built with OpenNI2 support...");
exit(-1); exit(-1);
} }
camera = new CameraOpenNI2(); camera = new rtabmap::CameraOpenNI2();
} }
else if(driver == 2) else if (driver == 2)
{ {
if(!CameraFreenect::available()) if (!rtabmap::CameraFreenect::available())
{ {
UERROR("Not built with Freenect support..."); UERROR("Not built with Freenect support...");
exit(-1); exit(-1);
} }
camera = new CameraFreenect(); camera = new rtabmap::CameraFreenect();
} }
else if(driver == 3) else if (driver == 3)
{ {
if(!CameraOpenNICV::available()) if (!rtabmap::CameraOpenNICV::available())
{ {
UERROR("Not built with OpenNI from OpenCV support..."); UERROR("Not built with OpenNI from OpenCV support...");
exit(-1); exit(-1);
} }
camera = new CameraOpenNICV(); camera = new rtabmap::CameraOpenNICV(false);
} }
else if(driver == 4) else if (driver == 4)
{ {
if(!CameraOpenNICV::available()) if (!rtabmap::CameraOpenNICV::available())
{ {
UERROR("Not built with OpenNI from OpenCV support..."); UERROR("Not built with OpenNI from OpenCV support...");
exit(-1); exit(-1);
} }
camera = new CameraOpenNICV(true); camera = new rtabmap::CameraOpenNICV(true);
} }
else if (driver == 5) else if (driver == 5)
{ {
if (!CameraFreenect2::available()) if (!rtabmap::CameraFreenect2::available())
{ {
UERROR("Not built with Freenect2 support..."); UERROR("Not built with Freenect2 support...");
exit(-1); exit(-1);
} }
camera = new CameraFreenect2(0, CameraFreenect2::kTypeColor2DepthSD); camera = new rtabmap::CameraFreenect2(0, rtabmap::CameraFreenect2::kTypeColor2DepthSD);
} }
else if (driver == 6) else if (driver == 6)
{ {
if (!CameraStereoZed::available()) if (!rtabmap::CameraStereoDC1394::available())
{ {
UERROR("Not built with ZED SDK support..."); UERROR("Not built with DC1394 support...");
exit(-1); exit(-1);
} }
camera = new CameraStereoZed(0, -1, 1, 1, 100, false); camera = new rtabmap::CameraStereoDC1394();
} }
else if (driver == 7) else if (driver == 7)
{ {
if (!CameraRealSense::available()) if (!rtabmap::CameraStereoFlyCapture2::available())
{
UERROR("Not built with FlyCapture2/Triclops support...");
exit(-1);
}
camera = new rtabmap::CameraStereoFlyCapture2();
}
else if (driver == 8)
{
if (!rtabmap::CameraStereoZed::available())
{
UERROR("Not built with ZED sdk support...");
exit(-1);
}
camera = new rtabmap::CameraStereoZed(0);
}
else if (driver == 9)
{
if (!rtabmap::CameraRealSense::available())
{ {
UERROR("Not built with RealSense support..."); UERROR("Not built with RealSense support...");
exit(-1); exit(-1);
} }
camera = new CameraRealSense(); camera = new rtabmap::CameraRealSense();
} }
else if (driver == 8) else if (driver == 10)
{ {
if (!CameraRealSense2::available()) if (!rtabmap::CameraK4W2::available())
{ {
UERROR("Not built with RealSense2 support..."); UERROR("Not built with Kinect for Windows 2 SDK support...");
exit(-1); exit(-1);
} }
camera = new CameraRealSense2(); camera = new rtabmap::CameraK4W2();
} }
else if (driver == 9) else if (driver == 11)
{
if (!rtabmap::CameraRealSense2::available())
{
UERROR("Not built with RealSense2 SDK support...");
exit(-1);
}
camera = new rtabmap::CameraRealSense2();
}
else if (driver == 12)
{ {
if (!rtabmap::CameraK4A::available()) if (!rtabmap::CameraK4A::available())
{ {
@@ -166,7 +211,7 @@ int main(int argc, char * argv[])
} }
camera = new rtabmap::CameraK4A(1); camera = new rtabmap::CameraK4A(1);
} }
else if (driver == 10) else if (driver == 13)
{ {
if (!rtabmap::CameraMyntEye::available()) if (!rtabmap::CameraMyntEye::available())
{ {
@@ -175,9 +220,45 @@ int main(int argc, char * argv[])
} }
camera = new rtabmap::CameraMyntEye(); camera = new rtabmap::CameraMyntEye();
} }
else if (driver == 14)
{
if (!rtabmap::CameraStereoZedOC::available())
{
UERROR("Not built with Zed Open Capture support...");
exit(-1);
}
camera = new rtabmap::CameraStereoZedOC(0);
}
else if (driver == 15)
{
if (!rtabmap::CameraDepthAI::available())
{
UERROR("Not built with depthai-core support...");
exit(-1);
}
camera = new rtabmap::CameraDepthAI();
}
else if (driver == 16)
{
if (!rtabmap::CameraSeerSense::available())
{
UERROR("Not built with XVisio SDK support...");
exit(-1);
}
camera = new rtabmap::CameraSeerSense();
}
else if (driver == 17)
{
if (!rtabmap::CameraOrbbecSDK::available())
{
UERROR("Not built with Orbbec SDK support...");
exit(-1);
}
camera = new rtabmap::CameraOrbbecSDK();
}
else else
{ {
camera = new rtabmap::CameraOpenni(); UFATAL("");
} }
if(!camera->init()) if(!camera->init())
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rtabmap/utilite/UEventsHandler.h> #include <rtabmap/utilite/UEventsHandler.h>
#include <QDialog> #include <QDialog>
#include <QElapsedTimer>
#include <rtabmap/core/SensorData.h> #include <rtabmap/core/SensorData.h>
#include <rtabmap/core/Parameters.h> #include <rtabmap/core/Parameters.h>
@@ -73,6 +74,9 @@ private:
QCheckBox * showScanCheckbox_; QCheckBox * showScanCheckbox_;
QCheckBox * markerCheckbox_; QCheckBox * markerCheckbox_;
MarkerDetector * markerDetector_; MarkerDetector * markerDetector_;
QElapsedTimer fpsTimer_;
double lastCapturePeriod_;
double previousCaptureStamp_;
}; };
} /* namespace rtabmap */ } /* namespace rtabmap */
+15
View File
@@ -334,6 +334,9 @@ public:
bool getPose(const std::string & id, Transform & pose); //including meshes bool getPose(const std::string & id, Transform & pose); //including meshes
bool getCloudVisibility(const std::string & id); bool getCloudVisibility(const std::string & id);
int getCloudColorIndex(const std::string & id) const;
double getCloudOpacity(const std::string & id) const;
int getCloudPointSize(const std::string & id) const;
const QMap<std::string, Transform> & getAddedClouds() const {return _addedClouds;} //including meshes const QMap<std::string, Transform> & getAddedClouds() const {return _addedClouds;} //including meshes
const QColor & getDefaultBackgroundColor() const; const QColor & getDefaultBackgroundColor() const;
@@ -399,6 +402,12 @@ public:
void setIntensityRedColormap(bool value); void setIntensityRedColormap(bool value);
void setIntensityRainbowColormap(bool value); void setIntensityRainbowColormap(bool value);
void setIntensityMax(float value); void setIntensityMax(float value);
float getCloudColorRangeMin() const;
float getCloudColorRangeMax() const;
bool isCloudColorRangeInverted() const;
void setCloudColorRangeMin(float value);
void setCloudColorRangeMax(float value);
void setCloudColorRangeInverted(bool enabled);
void buildPickingLocator(bool enable); void buildPickingLocator(bool enable);
const std::map<std::string, vtkSmartPointer<vtkOBBTree> > & getLocators() const {return _locators;} const std::map<std::string, vtkSmartPointer<vtkOBBTree> > & getLocators() const {return _locators;}
@@ -454,6 +463,10 @@ private:
QAction * _aSetIntensityRedColormap; QAction * _aSetIntensityRedColormap;
QAction * _aSetIntensityRainbowColormap; QAction * _aSetIntensityRainbowColormap;
QAction * _aSetIntensityMaximum; QAction * _aSetIntensityMaximum;
QAction * _aSetCloudColorRangeMin;
QAction * _aSetCloudColorRangeMax;
QAction * _aCloudColorRangeInverted;
QAction * _aClearCloudColorRanges;
QAction * _aSetBackgroundColor; QAction * _aSetBackgroundColor;
QAction * _aSetRenderingRate; QAction * _aSetRenderingRate;
QAction * _aSetEDLShading; QAction * _aSetEDLShading;
@@ -494,6 +507,8 @@ private:
double _renderingRate; double _renderingRate;
vtkProp * _octomapActor; vtkProp * _octomapActor;
float _intensityAbsMax; float _intensityAbsMax;
float _cloudColorRangeMin;
float _cloudColorRangeMax;
double _coordinateFrameScale; double _coordinateFrameScale;
}; };
@@ -220,6 +220,7 @@ private:
std::map<int, int> mapIds_; std::map<int, int> mapIds_;
std::map<int, int> weights_; std::map<int, int> weights_;
std::map<int, std::vector<int> > wmStates_; std::map<int, std::vector<int> > wmStates_;
std::map<int, EnvSensors> envSensors_;
QMap<int, int> idToIndex_; QMap<int, int> idToIndex_;
QList<rtabmap::Link> neighborLinks_; QList<rtabmap::Link> neighborLinks_;
QList<rtabmap::Link> loopLinks_; QList<rtabmap::Link> loopLinks_;
+8 -3
View File
@@ -77,6 +77,7 @@ public:
// Use updateNodeColorByValue() instead with valueName="Posterior". // Use updateNodeColorByValue() instead with valueName="Posterior".
RTABMAP_DEPRECATED void updatePosterior(const std::map<int, float> & posterior, float fixedMax = 0.0f, int zValueOffset = 0); RTABMAP_DEPRECATED void updatePosterior(const std::map<int, float> & posterior, float fixedMax = 0.0f, int zValueOffset = 0);
void updateNodeColorByValue(const std::string & valueName, const std::map<int, float> & values, float fixedMax = 0.0f, bool invertedColorScale = false, int zValueOffset = 0); void updateNodeColorByValue(const std::string & valueName, const std::map<int, float> & values, float fixedMax = 0.0f, bool invertedColorScale = false, int zValueOffset = 0);
void updateNodeColorByValue(const std::string & valueName, const std::map<int, float> & values, float fixedMin, float fixedMax, bool invertedColorScale = false, unsigned short hueMin=0, unsigned short hueMax=180, int zValueOffset = 0);
void updateLocalPath(const std::vector<int> & localPath); void updateLocalPath(const std::vector<int> & localPath);
void setGlobalPath(const std::vector<std::pair<int, Transform> > & globalPath); void setGlobalPath(const std::vector<std::pair<int, Transform> > & globalPath);
void setCurrentGoalID(int id, const Transform & pose = Transform()); void setCurrentGoalID(int id, const Transform & pose = Transform());
@@ -118,8 +119,9 @@ public:
bool isReferentialVisible() const; bool isReferentialVisible() const;
bool isLocalRadiusVisible() const; bool isLocalRadiusVisible() const;
float getLoopClosureOutlierThr() const {return _loopClosureOutlierThr;} float getLoopClosureOutlierThr() const {return _loopClosureOutlierThr;}
float getMaxLinkLength() const {return _maxLinkLength;} float getMinLinkLength() const {return _minLinkLength;}
bool isGraphVisible() const; bool isGraphVisible() const;
bool isNodeVisible() const;
bool isGlobalPathVisible() const; bool isGlobalPathVisible() const;
bool isLocalPathVisible() const; bool isLocalPathVisible() const;
bool isGtGraphVisible() const; bool isGtGraphVisible() const;
@@ -158,7 +160,7 @@ public:
void setReferentialVisible(bool visible); void setReferentialVisible(bool visible);
void setLocalRadiusVisible(bool visible); void setLocalRadiusVisible(bool visible);
void setLoopClosureOutlierThr(float value); void setLoopClosureOutlierThr(float value);
void setMaxLinkLength(float value); void setMinLinkLength(float value);
void setGraphVisible(bool visible); void setGraphVisible(bool visible);
void setGlobalPathVisible(bool visible); void setGlobalPathVisible(bool visible);
void setLocalPathVisible(bool visible); void setLocalPathVisible(bool visible);
@@ -184,6 +186,9 @@ protected:
virtual void mousePressEvent(QMouseEvent * event); virtual void mousePressEvent(QMouseEvent * event);
virtual void contextMenuEvent(QContextMenuEvent * event); virtual void contextMenuEvent(QContextMenuEvent * event);
private:
void setupGraphicsScene();
private: private:
QString _workingDirectory; QString _workingDirectory;
QColor _nodeColor; QColor _nodeColor;
@@ -237,7 +242,7 @@ private:
QGraphicsEllipseItem * _localRadius; QGraphicsEllipseItem * _localRadius;
QGraphicsRectItem * _odomCacheOverlay; QGraphicsRectItem * _odomCacheOverlay;
float _loopClosureOutlierThr; float _loopClosureOutlierThr;
float _maxLinkLength; float _minLinkLength;
bool _orientationENU; bool _orientationENU;
bool _mouseTracking; bool _mouseTracking;
ViewPlane _viewPlane; ViewPlane _viewPlane;
+3
View File
@@ -77,6 +77,7 @@ public:
float getDepthColorMapMinRange() const; float getDepthColorMapMinRange() const;
float getDepthColorMapMaxRange() const; float getDepthColorMapMaxRange() const;
uCvQtDepthColorMap getDepthColorMap() const; uCvQtDepthColorMap getDepthColorMap() const;
bool isDepthColorMapInCameraFrame() const;
float viewScale() const; float viewScale() const;
@@ -94,6 +95,7 @@ public:
void setDefaultMatchingLineColor(const QColor & color); void setDefaultMatchingLineColor(const QColor & color);
void setBackgroundColor(const QColor & color); void setBackgroundColor(const QColor & color);
void setDepthColorMapRange(float min, float max); void setDepthColorMapRange(float min, float max);
void setDepthColorMapInCameraFrame(bool enabled);
void setFeatures(const std::multimap<int, cv::KeyPoint> & refWords, const cv::Mat & depth = cv::Mat(), const QColor & color = Qt::yellow); void setFeatures(const std::multimap<int, cv::KeyPoint> & refWords, const cv::Mat & depth = cv::Mat(), const QColor & color = Qt::yellow);
void setFeatures(const std::vector<cv::KeyPoint> & features, const cv::Mat & depth = cv::Mat(), const QColor & color = Qt::yellow); void setFeatures(const std::vector<cv::KeyPoint> & features, const cv::Mat & depth = cv::Mat(), const QColor & color = Qt::yellow);
@@ -167,6 +169,7 @@ private:
QAction * _colorMapBlackToWhite; QAction * _colorMapBlackToWhite;
QAction * _colorMapRedToBlue; QAction * _colorMapRedToBlue;
QAction * _colorMapBlueToRed; QAction * _colorMapBlueToRed;
QAction * _colorMapInCameraFrame;
QAction * _colorMapMinRange; QAction * _colorMapMinRange;
QAction * _colorMapMaxRange; QAction * _colorMapMaxRange;
QAction * _mouseTracking; QAction * _mouseTracking;
+1
View File
@@ -178,6 +178,7 @@ protected Q_SLOTS:
void selectFreenect2(); void selectFreenect2();
void selectK4W2(); void selectK4W2();
void selectK4A(); void selectK4A();
void selectOrbbecSDK();
void selectRealSense(); void selectRealSense();
void selectRealSense2(); void selectRealSense2();
void selectRealSense2L515(); void selectRealSense2L515();
@@ -44,6 +44,7 @@ public:
void setMap(const std::map<int, Transform> & poses, const std::map<int, bool> & mask); void setMap(const std::map<int, Transform> & poses, const std::map<int, bool> & mask);
std::map<int, Transform> getVisiblePoses() const; std::map<int, Transform> getVisiblePoses() const;
bool isEmpty() const {return _poses.empty();}
void clear(); void clear();
@@ -0,0 +1,186 @@
/*
Copyright (c) 2010-2025, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef GUILIB_SRC_POINTCLOUDCOLORHANDLEINTENSITYFIELD_H_
#define GUILIB_SRC_POINTCLOUDCOLORHANDLEINTENSITYFIELD_H_
#include <pcl/visualization/point_cloud_color_handlers.h>
#include <pcl/pcl_config.h>
#include <rtabmap/core/util2d.h>
#include <rtabmap/utilite/UMath.h>
namespace rtabmap
{
class PointCloudColorHandlerIntensityField : public pcl::visualization::PointCloudColorHandler<pcl::PCLPointCloud2>
{
typedef pcl::visualization::PointCloudColorHandler<pcl::PCLPointCloud2>::PointCloud PointCloud;
typedef PointCloud::Ptr PointCloudPtr;
typedef PointCloud::ConstPtr PointCloudConstPtr;
public:
/** \brief Constructor. */
PointCloudColorHandlerIntensityField(const PointCloudConstPtr &cloud, float maxAbsIntensity = 0.0f, int colorMap = 0) : pcl::visualization::PointCloudColorHandler<pcl::PCLPointCloud2>::PointCloudColorHandler(cloud),
maxAbsIntensity_(maxAbsIntensity),
colormap_(colorMap)
{
field_idx_ = pcl::getFieldIndex(*cloud, "intensity");
if (field_idx_ != -1)
capable_ = true;
else
capable_ = false;
}
/** \brief Empty destructor */
virtual ~PointCloudColorHandlerIntensityField() {}
/** \brief Obtain the actual color for the input dataset as vtk scalars.
* \param[out] scalars the output scalars containing the color for the dataset
* \return true if the operation was successful (the handler is capable and
* the input cloud was given as a valid pointer), false otherwise
*/
#if PCL_VERSION_COMPARE(>, 1, 11, 1)
virtual vtkSmartPointer<vtkDataArray> getColor() const
{
vtkSmartPointer<vtkDataArray> scalars;
if (!capable_ || !cloud_)
return scalars;
#else
virtual bool getColor(vtkSmartPointer<vtkDataArray> &scalars) const
{
if (!capable_ || !cloud_)
return (false);
#endif
if (!scalars)
scalars = vtkSmartPointer<vtkUnsignedCharArray>::New();
scalars->SetNumberOfComponents(3);
vtkIdType nr_points = cloud_->width * cloud_->height;
// Allocate enough memory to hold all colors
float *intensities = new float[nr_points];
float intensity;
size_t point_offset = cloud_->fields[field_idx_].offset;
size_t j = 0;
// If XYZ present, check if the points are invalid
int x_idx = pcl::getFieldIndex(*cloud_, "x");
if (x_idx != -1)
{
float x_data, y_data, z_data;
size_t x_point_offset = cloud_->fields[x_idx].offset;
// Color every point
for (vtkIdType cp = 0; cp < nr_points; ++cp,
point_offset += cloud_->point_step,
x_point_offset += cloud_->point_step)
{
// Copy the value at the specified field
memcpy(&intensity, &cloud_->data[point_offset], sizeof(float));
memcpy(&x_data, &cloud_->data[x_point_offset], sizeof(float));
memcpy(&y_data, &cloud_->data[x_point_offset + sizeof(float)], sizeof(float));
memcpy(&z_data, &cloud_->data[x_point_offset + 2 * sizeof(float)], sizeof(float));
if (!std::isfinite(x_data) || !std::isfinite(y_data) || !std::isfinite(z_data))
continue;
intensities[j++] = intensity;
}
}
// No XYZ data checks
else
{
// Color every point
for (vtkIdType cp = 0; cp < nr_points; ++cp, point_offset += cloud_->point_step)
{
// Copy the value at the specified field
memcpy(&intensity, &cloud_->data[point_offset], sizeof(float));
intensities[j++] = intensity;
}
}
if (j != 0)
{
// Allocate enough memory to hold all colors
unsigned char *colors = new unsigned char[j * 3];
float min, max;
if (maxAbsIntensity_ > 0.0f)
{
max = maxAbsIntensity_;
}
else
{
uMinMax(intensities, j, min, max);
}
for (size_t k = 0; k < j; ++k)
{
colors[k * 3 + 0] = colors[k * 3 + 1] = colors[k * 3 + 2] = max > 0 ? (unsigned char)(std::min(intensities[k] / max * 255.0f, 255.0f)) : 255;
if (colormap_ == 1)
{
colors[k * 3 + 0] = 255;
colors[k * 3 + 2] = 0;
}
else if (colormap_ == 2)
{
float r, g, b;
util2d::HSVtoRGB(&r, &g, &b, colors[k * 3 + 0] * 299.0f / 255.0f, 1.0f, 1.0f);
colors[k * 3 + 0] = r * 255.0f;
colors[k * 3 + 1] = g * 255.0f;
colors[k * 3 + 2] = b * 255.0f;
}
}
reinterpret_cast<vtkUnsignedCharArray *>(&(*scalars))->SetNumberOfTuples(j);
reinterpret_cast<vtkUnsignedCharArray *>(&(*scalars))->SetArray(colors, j * 3, 0, vtkUnsignedCharArray::VTK_DATA_ARRAY_DELETE);
}
else
reinterpret_cast<vtkUnsignedCharArray *>(&(*scalars))->SetNumberOfTuples(0);
// delete [] colors;
delete[] intensities;
#if PCL_VERSION_COMPARE(>, 1, 11, 1)
return scalars;
#else
return (true);
#endif
}
protected:
/** \brief Get the name of the class. */
virtual std::string
getName() const { return ("PointCloudColorHandlerIntensityField"); }
/** \brief Get the name of the field used. */
virtual std::string
getFieldName() const { return ("intensity"); }
private:
float maxAbsIntensity_;
int colormap_; // 0=grayscale, 1=redYellow, 2=RainbowHSV
};
} /* namespace rtabmap */
#endif /* GUILIB_SRC_POINTCLOUDCOLORHANDLEINTENSITYFIELD_H_ */
@@ -0,0 +1,187 @@
/*
Copyright (c) 2010-2025, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Universite de Sherbrooke nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef GUILIB_SRC_POINTCLOUDCOLORHANDLEMINMAXGENERICFIELD_H_
#define GUILIB_SRC_POINTCLOUDCOLORHANDLEMINMAXGENERICFIELD_H_
#include <limits>
#include <pcl/visualization/point_cloud_color_handlers.h>
#include <pcl/pcl_config.h>
namespace rtabmap
{
/// Same than pcl::visualization::PointCloudColorHandlerGenericField but with min and max parameters
class PointCloudColorHandlerMinMaxGenericField : public pcl::visualization::PointCloudColorHandler<pcl::PCLPointCloud2>
{
using PointCloud = typename PointCloudColorHandler<pcl::PCLPointCloud2>::PointCloud;
using PointCloudPtr = typename PointCloud::Ptr;
using PointCloudConstPtr = typename PointCloud::ConstPtr;
public:
/** \brief Constructor. */
PointCloudColorHandlerMinMaxGenericField(const PointCloudConstPtr &cloud,
const std::string &field_name,
float min = std::numeric_limits<float>::lowest(),
float max = std::numeric_limits<float>::max(),
bool inverted_color_scale = false)
: pcl::visualization::PointCloudColorHandler<pcl::PCLPointCloud2>(cloud),
field_name_(field_name),
min_(min),
max_(max),
inverted_color_scale_(inverted_color_scale)
{
setInputCloud(cloud);
}
/** \brief Destructor. */
virtual ~PointCloudColorHandlerMinMaxGenericField() {}
/** \brief Get the name of the field used. */
virtual std::string getFieldName() const { return (field_name_); }
#if PCL_VERSION_COMPARE(>, 1, 11, 1)
virtual vtkSmartPointer<vtkDataArray> getColor() const
{
vtkSmartPointer<vtkDataArray> scalars;
if (!capable_ || !cloud_)
return scalars;
#else
virtual bool getColor(vtkSmartPointer<vtkDataArray> &scalars) const
{
if (!capable_ || !cloud_)
return (false);
#endif
if (!scalars)
scalars = vtkSmartPointer<vtkFloatArray>::New ();
scalars->SetNumberOfComponents(1);
vtkIdType nr_points = cloud_->width * cloud_->height;
scalars->SetNumberOfTuples(nr_points);
float *colors = new float[nr_points];
float field_data;
int j = 0;
int point_offset = cloud_->fields[field_idx_].offset;
// If XYZ present, check if the points are invalid
int x_idx = pcl::getFieldIndex(*cloud_, "x");
if (x_idx != -1)
{
float x_data, y_data, z_data;
int x_point_offset = cloud_->fields[x_idx].offset;
// Color every point
for (vtkIdType cp = 0; cp < nr_points; ++cp,
point_offset += cloud_->point_step,
x_point_offset += cloud_->point_step)
{
memcpy(&x_data, &cloud_->data[x_point_offset], sizeof(float));
memcpy(&y_data, &cloud_->data[x_point_offset + sizeof(float)], sizeof(float));
memcpy(&z_data, &cloud_->data[x_point_offset + 2 * sizeof(float)], sizeof(float));
if (!std::isfinite(x_data) || !std::isfinite(y_data) || !std::isfinite(z_data))
continue;
// Copy the value at the specified field
memcpy(&field_data, &cloud_->data[point_offset], pcl::getFieldSize(cloud_->fields[field_idx_].datatype));
if(field_data < min_) {
field_data = min_;
}
if(field_data > max_) {
field_data = max_;
}
colors[j] = field_data * (inverted_color_scale_?-1.0f:1.0f);
j++;
}
}
// No XYZ data checks
else
{
// Color every point
for (vtkIdType cp = 0; cp < nr_points; ++cp, point_offset += cloud_->point_step)
{
// Copy the value at the specified field
// memcpy (&field_data, &cloud_->data[point_offset], sizeof (float));
memcpy(&field_data, &cloud_->data[point_offset], pcl::getFieldSize(cloud_->fields[field_idx_].datatype));
if (!std::isfinite(field_data))
continue;
if(field_data < min_) {
field_data = min_;
}
if(field_data > max_) {
field_data = max_;
}
colors[j] = field_data * (inverted_color_scale_?-1.0f:1.0f);
j++;
}
}
reinterpret_cast<vtkFloatArray *>(&(*scalars))->SetArray(colors, j, 0, vtkFloatArray::VTK_DATA_ARRAY_DELETE);
#if PCL_VERSION_COMPARE(>, 1, 11, 1)
return scalars;
#else
return (true);
#endif
}
using PointCloudColorHandler<pcl::PCLPointCloud2>::getColor;
/** \brief Set the input cloud to be used.
* \param[in] cloud the input cloud to be used by the handler
*/
virtual void
setInputCloud(const PointCloudConstPtr &cloud)
{
PointCloudColorHandler<pcl::PCLPointCloud2>::setInputCloud(cloud);
field_idx_ = pcl::getFieldIndex(*cloud, field_name_);
capable_ = field_idx_ != -1;
if (field_idx_ != -1 && cloud_->fields[field_idx_].datatype != pcl::PCLPointField::PointFieldTypes::FLOAT32)
{
capable_ = false;
PCL_ERROR("[pcl::PointCloudColorHandlerGenericField] This currently only works with float32 fields, but field %s has a different type.\n", field_name_.c_str());
}
}
protected:
/** \brief Class getName method. */
virtual std::string
getName() const { return ("PointCloudColorHandlerMinMaxGenericField"); }
private:
/** \brief Name of the field used to create the color handler. */
std::string field_name_;
float min_;
float max_;
bool inverted_color_scale_;
};
} /* namespace rtabmap */
#endif /* GUILIB_SRC_POINTCLOUDCOLORHANDLEMINMAXGENERICFIELD_H_ */
@@ -98,6 +98,7 @@ public:
kSrcRealSense2 = 9, kSrcRealSense2 = 9,
kSrcK4A = 10, kSrcK4A = 10,
kSrcSeerSense = 11, kSrcSeerSense = 11,
kSrcOrbbecSDK = 12,
kSrcStereo = 100, kSrcStereo = 100,
kSrcDC1394 = 100, kSrcDC1394 = 100,
@@ -173,6 +174,7 @@ public:
int getOdomRegistrationApproach() const; int getOdomRegistrationApproach() const;
double getOdomF2MGravitySigma() const; double getOdomF2MGravitySigma() const;
bool isOdomDisabled() const; bool isOdomDisabled() const;
bool isOdomAsGuessEnabled() const;
bool isOdomSensorAsGt() const; bool isOdomSensorAsGt() const;
bool isGroundTruthAligned() const; bool isGroundTruthAligned() const;
@@ -367,11 +369,13 @@ private Q_SLOTS:
void changeDictionaryPath(); void changeDictionaryPath();
void changeOdometryORBSLAMVocabulary(); void changeOdometryORBSLAMVocabulary();
void changeOdometryOKVISConfigPath(); void changeOdometryOKVISConfigPath();
void changeOdometryVINSConfigPath(); void changeOdometryVINSFusionConfigPath();
void changeOdometryOpenVINSLeftMask(); void changeOdometryOpenVINSLeftMask();
void changeOdometryOpenVINSRightMask(); void changeOdometryOpenVINSRightMask();
void changeIcpPMConfigPath(); void changeIcpPMConfigPath();
void changeSuperPointModelPath(); void changeSuperPointModelPath();
void changeSuperPointRpautratWeightsPath();
void changeSuperPointRpautratModelPath();
void changePyMatcherPath(); void changePyMatcherPath();
void changePyMatcherModel(); void changePyMatcherModel();
void changePyDescriptorPath(); void changePyDescriptorPath();
+12 -12
View File
@@ -117,7 +117,7 @@ inline QImage uCvMat2QImage(
// Assume depth image (float in meters) // Assume depth image (float in meters)
const float * data = (const float *)image.data; const float * data = (const float *)image.data;
float min,max; float min,max;
if(depthMax>depthMin) if(depthMin != 0 && depthMax != 0 && depthMax > depthMin)
{ {
min = depthMin; min = depthMin;
max = depthMax; max = depthMax;
@@ -127,23 +127,23 @@ inline QImage uCvMat2QImage(
min = max = data[0]; min = max = data[0];
for(unsigned int i=1; i<image.total(); ++i) for(unsigned int i=1; i<image.total(); ++i)
{ {
if(uIsFinite(data[i]) && data[i] > 0) if(uIsFinite(data[i]) && data[i] != 0)
{ {
if(!uIsFinite(min) || (data[i] > 0 && data[i]<min)) if(!uIsFinite(min) || (data[i] != 0 && data[i]<min))
{ {
min = data[i]; min = data[i];
} }
if(!uIsFinite(max) || (data[i] > 0 && data[i]>max)) if(!uIsFinite(max) || (data[i] != 0 && data[i]>max))
{ {
max = data[i]; max = data[i];
} }
} }
} }
if(depthMax > 0 && depthMax > depthMin) if(depthMax != 0 && depthMax > depthMin)
{ {
max = depthMax; max = depthMax;
} }
if(depthMin>0 && (depthMin < depthMax || depthMin < max)) if(depthMin != 0 && (depthMin < depthMax || depthMin < max))
{ {
min = depthMin; min = depthMin;
} }
@@ -198,7 +198,7 @@ inline QImage uCvMat2QImage(
// Assume depth image (unsigned short in mm) // Assume depth image (unsigned short in mm)
const unsigned short * data = (const unsigned short *)image.data; const unsigned short * data = (const unsigned short *)image.data;
unsigned short min,max; unsigned short min,max;
if(depthMax>depthMin) if(depthMin != 0 && depthMax != 0 && depthMax > depthMin)
{ {
min = depthMin*1000; min = depthMin*1000;
max = depthMax*1000; max = depthMax*1000;
@@ -208,23 +208,23 @@ inline QImage uCvMat2QImage(
min = max = data[0]; min = max = data[0];
for(unsigned int i=1; i<image.total(); ++i) for(unsigned int i=1; i<image.total(); ++i)
{ {
if(uIsFinite(data[i]) && data[i] > 0) if(uIsFinite(data[i]) && data[i] != 0)
{ {
if(!uIsFinite(min) || (data[i] > 0 && data[i]<min)) if(!uIsFinite(min) || (data[i] != 0 && data[i]<min))
{ {
min = data[i]; min = data[i];
} }
if(!uIsFinite(max) || (data[i] > 0 && data[i]>max)) if(!uIsFinite(max) || (data[i] != 0 && data[i]>max))
{ {
max = data[i]; max = data[i];
} }
} }
} }
if(depthMax > 0 && depthMax > depthMin) if(depthMax != 0 && depthMax > depthMin)
{ {
max = depthMax*1000; max = depthMax*1000;
} }
if(depthMin>0 && (depthMin < depthMax || depthMin*1000 < max)) if(depthMin != 0 && (depthMin < depthMax || depthMin*1000 < max))
{ {
min = depthMin*1000; min = depthMin*1000;
} }
+18 -1
View File
@@ -86,6 +86,13 @@ AboutDialog::AboutDialog(QWidget * parent) :
_ui->label_sptorch->setText("No"); _ui->label_sptorch->setText("No");
_ui->label_sptorch_license->setEnabled(false); _ui->label_sptorch_license->setEnabled(false);
#endif #endif
#if defined(RTABMAP_TORCH) && defined(RTABMAP_PYTHON)
_ui->label_sprpautrat->setText("Yes");
_ui->label_sprpautrat_license->setEnabled(true);
#else
_ui->label_sprpautrat->setText("No");
_ui->label_sprpautrat_license->setEnabled(false);
#endif
#ifdef RTABMAP_PYTHON #ifdef RTABMAP_PYTHON
_ui->label_pymatcher->setText("Yes"); _ui->label_pymatcher->setText("Yes");
_ui->label_pymatcher_license->setEnabled(true); _ui->label_pymatcher_license->setEnabled(true);
@@ -179,6 +186,8 @@ AboutDialog::AboutDialog(QWidget * parent) :
_ui->label_depthai->setText(CameraDepthAI::available() ? "Yes" : "No"); _ui->label_depthai->setText(CameraDepthAI::available() ? "Yes" : "No");
_ui->label_depthai_license->setEnabled(CameraDepthAI::available()); _ui->label_depthai_license->setEnabled(CameraDepthAI::available());
_ui->label_xvsdk->setText(CameraSeerSense::available() ? "Yes" : "No"); _ui->label_xvsdk->setText(CameraSeerSense::available() ? "Yes" : "No");
_ui->label_orbbec_sdk->setText(CameraOrbbecSDK::available() ? "Yes" : "No");
_ui->label_orbbec_sdk_license->setEnabled(CameraOrbbecSDK::available());
_ui->label_toro->setText(Optimizer::isAvailable(Optimizer::kTypeTORO)?"Yes":"No"); _ui->label_toro->setText(Optimizer::isAvailable(Optimizer::kTypeTORO)?"Yes":"No");
_ui->label_toro_license->setEnabled(Optimizer::isAvailable(Optimizer::kTypeTORO)?true:false); _ui->label_toro_license->setEnabled(Optimizer::isAvailable(Optimizer::kTypeTORO)?true:false);
@@ -281,7 +290,7 @@ AboutDialog::AboutDialog(QWidget * parent) :
_ui->label_msckf_license->setEnabled(false); _ui->label_msckf_license->setEnabled(false);
#endif #endif
#ifdef RTABMAP_VINS #ifdef RTABMAP_VINS_FUSION
_ui->label_vins_fusion->setText("Yes"); _ui->label_vins_fusion->setText("Yes");
_ui->label_vins_fusion_license->setEnabled(true); _ui->label_vins_fusion_license->setEnabled(true);
#else #else
@@ -297,6 +306,14 @@ AboutDialog::AboutDialog(QWidget * parent) :
_ui->label_openvins_license->setEnabled(false); _ui->label_openvins_license->setEnabled(false);
#endif #endif
#ifdef RTABMAP_CUVSLAM
_ui->label_cuvslam->setText("Yes");
_ui->label_cuvslam_license->setEnabled(true);
#else
_ui->label_cuvslam->setText("No");
_ui->label_cuvslam_license->setEnabled(false);
#endif
} }
AboutDialog::~AboutDialog() AboutDialog::~AboutDialog()

Some files were not shown because too many files have changed in this diff Show More