mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-02 17:40:23 +08:00
Compare commits
9 Commits
master
...
track_data
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eca73a951 | ||
|
|
b99e8fa263 | ||
|
|
9f9ce646d4 | ||
|
|
b532319885 | ||
|
|
e61a45e44f | ||
|
|
4056c152f0 | ||
|
|
9cd2318e37 | ||
|
|
8f63846b66 | ||
|
|
15109dfa9e |
@@ -15,5 +15,7 @@ RUN set -ex && \
|
||||
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 /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc
|
||||
|
||||
|
||||
|
||||
@@ -15,5 +15,7 @@ RUN set -ex && \
|
||||
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 /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ RUN set -ex && \
|
||||
RUN mkdir -p /home/${USERNAME}/Documents/RTAB-Map
|
||||
|
||||
RUN echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
|
||||
RUN echo 'source /opt/ros/${ROS_DISTRO}/setup.bash' >> /home/${USERNAME}/.bashrc
|
||||
|
||||
WORKDIR /home/${USERNAME}/
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
FROM introlab3it/rtabmap:noble-kilted-deps
|
||||
# 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 echo "source /usr/share/bash-completion/completions/git" >> /home/${USERNAME}/.bashrc
|
||||
RUN echo 'source /opt/ros/kilted/setup.bash' >> /home/${USERNAME}/.bashrc
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": ["ms-vscode.cpptools-themes", "ms-vscode.cmake-tools"]
|
||||
}
|
||||
},
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/home/vscode/rtabmap,type=bind",
|
||||
"workspaceFolder": "/home/vscode/rtabmap",
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "bash"
|
||||
},
|
||||
"remoteUser": "vscode",
|
||||
"runArgs": ["--privileged"]
|
||||
}
|
||||
@@ -19,5 +19,7 @@ RUN set -ex && \
|
||||
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 /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc
|
||||
|
||||
|
||||
|
||||
@@ -77,4 +77,4 @@ RUN set -ex && \
|
||||
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 /opt/ros/$ROS_DISTRO/setup.bash' >> /home/${USERNAME}/.bashrc
|
||||
|
||||
|
||||
53
.gitattributes
vendored
53
.gitattributes
vendored
@@ -1,53 +0,0 @@
|
||||
# This repository has a historical mix of CRLF and LF files. Letting git convert
|
||||
# line endings (core.autocrlf, or a "text=auto" policy) would rewrite whole files
|
||||
# on checkout/commit and bury real changes under thousands of ending-only diff
|
||||
# lines -- and it would do so at unpredictable times, on whichever file happens
|
||||
# to be touched next. Keep every file's bytes exactly as committed.
|
||||
#
|
||||
# This also keeps LF-only files LF on Windows checkouts, which data/tests/manifest.txt
|
||||
# relies on: scripts/fetch_test_data.sh tab-splits it and a trailing CR breaks SHA
|
||||
# matching. No per-file exception is needed for that.
|
||||
#
|
||||
# Note this does not stop an editor or script from rewriting a file's endings; it
|
||||
# only stops git from doing it.
|
||||
* -text
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Migrating to the standard LF policy (a future MR, on its own)
|
||||
# -----------------------------------------------------------------------------
|
||||
# LF is what git stores natively and what essentially every C++ project on GitHub
|
||||
# uses (LLVM, OpenCV, PCL, Qt). The rule above only freezes the current mix; it is
|
||||
# a holding position, not the destination. To finish the job:
|
||||
#
|
||||
# 1. Replace the `* -text` line above with:
|
||||
#
|
||||
# * text=auto
|
||||
# # Files that must stay LF even in a Windows working tree:
|
||||
# *.sh text eol=lf
|
||||
# *.bash text eol=lf
|
||||
#
|
||||
# (`text=auto` = store LF in the repo, check out native line endings.)
|
||||
#
|
||||
# 2. Rewrite the working tree and stage it, in the same commit as step 1:
|
||||
#
|
||||
# git add --renormalize .
|
||||
#
|
||||
# 3. Verify nothing but line endings changed -- this must print nothing:
|
||||
#
|
||||
# git diff --cached --ignore-cr-at-eol
|
||||
#
|
||||
# 4. Commit alone, with no other change in the MR, then record the SHA so
|
||||
# `git blame` (and GitHub's blame view) skips over it:
|
||||
#
|
||||
# echo "<sha of the normalization commit>" >> .git-blame-ignore-revs
|
||||
#
|
||||
# Add a follow-up commit for that file; also set it locally with
|
||||
# `git config blame.ignoreRevsFile .git-blame-ignore-revs`.
|
||||
#
|
||||
# Cost, measured on the tree at the time of writing: 227 CRLF files + 7 with mixed
|
||||
# endings, ~138k of ~634k lines rewritten (22%), across 234 files. The commit
|
||||
# itself is mechanical and safe -- the real cost is every open branch that touches
|
||||
# one of those files, which will conflict over the whole file on rebase. Merge or
|
||||
# rebase those with `-X renormalize` (merge) / `--strategy-option renormalize`.
|
||||
#
|
||||
# Best moment: right after a release, when the branch backlog is smallest.
|
||||
@@ -124,7 +124,7 @@ runs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ runner.temp }}/deps-stage/depthai
|
||||
key: depthai-2.32.0-usrlocal-nocv-${{ inputs.os }}-${{ hashFiles('.github/actions/install-macos-source-deps/patches/depthai-2.32.0-hunter-macos.patch') }}-${{ steps.depver.outputs.hash }}
|
||||
key: depthai-2.32.0-usrlocal-${{ inputs.os }}-${{ hashFiles('.github/actions/install-macos-source-deps/patches/depthai-2.32.0-hunter-macos.patch') }}-${{ steps.depver.outputs.hash }}
|
||||
|
||||
- name: Build depthai
|
||||
if: steps.cache-depthai.outputs.cache-hit != 'true'
|
||||
@@ -161,7 +161,6 @@ runs:
|
||||
-DDEPTHAI_ENABLE_CURL=OFF \
|
||||
-DDEPTHAI_BUILD_TESTS=OFF \
|
||||
-DDEPTHAI_BUILD_EXAMPLES=OFF \
|
||||
-DDEPTHAI_OPENCV_SUPPORT=OFF \
|
||||
-DCMAKE_PREFIX_PATH="$(brew --prefix zlib)"
|
||||
"$CMAKE3" --build build -j$NPROC
|
||||
DESTDIR="$STAGE" "$CMAKE3" --install build
|
||||
|
||||
@@ -26,7 +26,7 @@ runs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ runner.workspace }}/vcpkg_installed
|
||||
key: ${{ runner.os }}-vcpkg-export-66c0373d-x64-vs2022-cuda130_v5
|
||||
key: ${{ runner.os }}-vcpkg-export-66c0373d-x64-vs2022-cuda130_v4
|
||||
|
||||
- name: Download and Install vcpkg
|
||||
if: steps.cache-vcpkg.outputs.cache-hit != 'true'
|
||||
|
||||
@@ -13,7 +13,7 @@ runs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ runner.workspace }}/vcpkg_installed
|
||||
key: ${{ runner.os }}-vcpkg-export-66c0373d-x64-vs2022-v7
|
||||
key: ${{ runner.os }}-vcpkg-export-66c0373d-x64-vs2022-v6
|
||||
|
||||
- name: Download and Install vcpkg
|
||||
if: steps.cache-vcpkg.outputs.cache-hit != 'true'
|
||||
|
||||
19
.github/scripts/ros-deps-env.sh
vendored
19
.github/scripts/ros-deps-env.sh
vendored
@@ -1,19 +0,0 @@
|
||||
# Puts the graph optimizers taken from the ROS 2 repo (see cmake-linux.yml) on
|
||||
# the loader path, for a shell that is about to run something built against
|
||||
# them. Sourced, not executed: it exports into the caller.
|
||||
#
|
||||
# source .github/scripts/ros-deps-env.sh /opt/ros/humble
|
||||
#
|
||||
# A missing or empty prefix is a build that took every dependency from the
|
||||
# Ubuntu archive: nothing to add.
|
||||
#
|
||||
# Both directories are needed. GTSAM and g2o install their libraries in the
|
||||
# multiarch subdirectory, while the prefix's own lib/ holds the rest, and
|
||||
# libgtsam.so carries no RUNPATH -- so the loader finds neither it nor the
|
||||
# libmetis-gtsam.so it pulls in without being told where to look.
|
||||
ros_prefix="${1:-}"
|
||||
if [ -n "$ros_prefix" ] && [ -d "$ros_prefix" ]; then
|
||||
ros_lib="$ros_prefix/lib"
|
||||
export LD_LIBRARY_PATH="${ros_lib}:${ros_lib}/$(gcc -dumpmachine)${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
|
||||
fi
|
||||
158
.github/workflows/cmake-linux.yml
vendored
158
.github/workflows/cmake-linux.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
@@ -20,129 +19,36 @@ jobs:
|
||||
build:
|
||||
name: ${{ matrix.build_name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.build_name }}
|
||||
cancel-in-progress: true
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
build_name: [ubuntu-22.04, ubuntu-24.04, ubuntu-24.04-with-opengv, ubuntu-26.04]
|
||||
include:
|
||||
# GTSAM is in no Ubuntu release, and libg2o-dev only appears in the
|
||||
# archive from noble on (jammy has no g2o at all).
|
||||
#
|
||||
# 26.04 builds GTSAM itself (gtsam_ref) rather than taking it from the
|
||||
# ROS repo: that runner is on Ubuntu's amd64v3 variant, so rtabmap and
|
||||
# the archive libraries are AVX builds where Eigen aligns to 32, while
|
||||
# the ROS binary is plain x86-64 where it aligns to 16 and uses plain
|
||||
# malloc/free. Allocating on one side and freeing on the other gives
|
||||
# "double free or corruption (out)". Forcing EIGEN_MAX_ALIGN_BYTES=16
|
||||
# on rtabmap would only move the mismatch onto PCL, g2o and Ceres.
|
||||
- build_name: ubuntu-22.04
|
||||
os: ubuntu-22.04
|
||||
extra_deps: "libunwind-dev libceres-dev"
|
||||
ros_deps: "ros-humble-libg2o ros-humble-gtsam"
|
||||
ros_prefix: "/opt/ros/humble"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON -DWITH_G2O=ON -DWITH_GTSAM=ON"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON"
|
||||
- build_name: ubuntu-24.04
|
||||
os: ubuntu-24.04
|
||||
extra_deps: "libg2o-dev libceres-dev"
|
||||
ros_deps: "ros-jazzy-gtsam"
|
||||
ros_prefix: "/opt/ros/jazzy"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON -DWITH_G2O=ON -DWITH_GTSAM=ON"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON"
|
||||
- build_name: ubuntu-24.04-with-opengv
|
||||
os: ubuntu-24.04
|
||||
extra_deps: "libg2o-dev libceres-dev"
|
||||
ros_deps: "ros-jazzy-gtsam"
|
||||
ros_prefix: "/opt/ros/jazzy"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON -DWITH_G2O=ON -DWITH_GTSAM=ON -DBUILD_OPENGV=ON"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON -DBUILD_OPENGV=ON"
|
||||
- build_name: ubuntu-26.04
|
||||
os: ubuntu-26.04
|
||||
# libboost-all-dev for the GTSAM build
|
||||
extra_deps: "libg2o-dev libceres-dev libboost-all-dev"
|
||||
ros_deps: ""
|
||||
ros_prefix: ""
|
||||
# The tag ros-lyrical-gtsam 4.3.0 is built from
|
||||
gtsam_ref: "4.3a0-ros"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON -DWITH_G2O=ON -DWITH_GTSAM=ON -DBUILD_OPENGV=ON"
|
||||
extra_deps: "libg2o-dev libceres-dev"
|
||||
extra_cmake_def: "-DWITH_CERES=ON -DWITH_PYTHON=ON -DBUILD_OPENGV=ON"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Restore test data
|
||||
id: cache-testdata
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
# SQLite DBs are binary-portable -> share the cache across
|
||||
# linux / macos / windows. Key omits runner.os on purpose so all
|
||||
# three OSes hit the same entry.
|
||||
path: data/tests/*.db
|
||||
key: testdata-${{ hashFiles('data/tests/manifest.txt') }}
|
||||
|
||||
- name: Fetch test data
|
||||
if: steps.cache-testdata.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: bash scripts/fetch_test_data.sh
|
||||
|
||||
- name: Install Linux Dependencies
|
||||
run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libopencv-dev libpcl-dev git cmake software-properties-common libyaml-cpp-dev curl gnupg lsb-release ${{ matrix.extra_deps }}
|
||||
|
||||
- name: Install Graph Optimizers From The ROS Repo
|
||||
if: matrix.ros_deps != ''
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
run: |
|
||||
sudo curl -fsSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
|
||||
-o /usr/share/keyrings/ros-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" \
|
||||
| sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ${{ matrix.ros_deps }}
|
||||
|
||||
# The compiler's x86-64 baseline, for the cache key: it decides Eigen's
|
||||
# alignment, so a GTSAM built under one must not be restored under the
|
||||
# other. v3 packages still declare Architecture: amd64, so dpkg cannot tell.
|
||||
- name: Toolchain baseline
|
||||
if: matrix.gtsam_ref != ''
|
||||
id: toolchain
|
||||
run: |
|
||||
avx=$(echo | gcc -dM -E -x c++ - | grep -c __AVX__ || true)
|
||||
echo "gcc $(gcc -dumpversion), __AVX__=$avx"
|
||||
echo "id=gcc$(gcc -dumpversion)-avx${avx}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore GTSAM
|
||||
if: matrix.gtsam_ref != ''
|
||||
id: cache-gtsam
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{github.workspace}}/gtsam-install
|
||||
key: gtsam-${{ matrix.gtsam_ref }}-${{ matrix.os }}-${{ steps.toolchain.outputs.id }}
|
||||
|
||||
- name: Build GTSAM
|
||||
if: matrix.gtsam_ref != '' && steps.cache-gtsam.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth 1 --branch ${{ matrix.gtsam_ref }} https://github.com/borglab/gtsam.git ${{runner.temp}}/gtsam-src
|
||||
# Tag-specific fixes, named after the tag.
|
||||
patch_file="${{github.workspace}}/patches/gtsam_$(echo '${{ matrix.gtsam_ref }}' | tr '.' '_').patch"
|
||||
if [ -f "$patch_file" ]; then
|
||||
echo "Applying $patch_file"
|
||||
git -C ${{runner.temp}}/gtsam-src apply "$patch_file"
|
||||
fi
|
||||
cmake -S ${{runner.temp}}/gtsam-src -B ${{runner.temp}}/gtsam-build \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=${{github.workspace}}/gtsam-install \
|
||||
-DGTSAM_USE_SYSTEM_EIGEN=ON \
|
||||
-DGTSAM_BUILD_WITH_MARCH_NATIVE=OFF \
|
||||
-DGTSAM_BUILD_WITH_WERROR=OFF \
|
||||
-DGTSAM_BUILD_TESTS=OFF \
|
||||
-DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF \
|
||||
-DGTSAM_BUILD_UNSTABLE=OFF \
|
||||
-DGTSAM_BUILD_PYTHON=OFF \
|
||||
-DBUILD_SHARED_LIBS=ON
|
||||
cmake --build ${{runner.temp}}/gtsam-build --target install -j $(nproc)
|
||||
sudo apt-get -y install libopencv-dev libpcl-dev git cmake software-properties-common libyaml-cpp-dev ${{ matrix.extra_deps }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
@@ -156,12 +62,7 @@ jobs:
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
# Both prefixes when a job has both.
|
||||
prefix_path="${{ matrix.ros_prefix }}"
|
||||
if [ -d "${{github.workspace}}/gtsam-install" ]; then
|
||||
prefix_path="${prefix_path:+$prefix_path;}${{github.workspace}}/gtsam-install"
|
||||
fi
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_PREFIX_PATH="$prefix_path" -DPython3_EXECUTABLE=$(which python3) -Dpybind11_DIR=$(python3 -m pybind11 --cmakedir) ${{ matrix.extra_cmake_def }}
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DPython3_EXECUTABLE=$(which python3) -Dpybind11_DIR=$(python3 -m pybind11 --cmakedir) ${{ matrix.extra_cmake_def }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
@@ -169,46 +70,11 @@ jobs:
|
||||
- name: Info
|
||||
working-directory: ${{github.workspace}}/build/bin
|
||||
run: |
|
||||
source ${{github.workspace}}/.github/scripts/ros-deps-env.sh "${{ matrix.ros_prefix }}"
|
||||
source ${{github.workspace}}/.github/scripts/ros-deps-env.sh "${{github.workspace}}/gtsam-install"
|
||||
./rtabmap-console --version
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
# PYTHONNOUSERSITE=1: prevent the embedded Python interpreter from
|
||||
# loading numpy / other site-packages from ~/.local that were compiled
|
||||
# against a different ABI than the build-time Python (causes numpy
|
||||
# 1.x/2.x mismatch crashes in test_pydetector / test_pydescriptor /
|
||||
# test_pymatcher).
|
||||
env:
|
||||
PYTHONNOUSERSITE: 1
|
||||
run: |
|
||||
source ${{github.workspace}}/.github/scripts/ros-deps-env.sh "${{ matrix.ros_prefix }}"
|
||||
source ${{github.workspace}}/.github/scripts/ros-deps-env.sh "${{github.workspace}}/gtsam-install"
|
||||
# Keep a core file per crashing test, for the backtrace step below: an
|
||||
# abort otherwise leaves only its message, and may not reproduce
|
||||
# elsewhere.
|
||||
ulimit -c unlimited
|
||||
sudo sysctl -w kernel.core_pattern="${{github.workspace}}/build/core.%e.%p" > /dev/null
|
||||
ctest -C ${{env.BUILD_TYPE}} -V -LE performance
|
||||
# - name: Test
|
||||
# working-directory: ${{github.workspace}}/build
|
||||
# # Execute tests defined by the CMake configuration.
|
||||
# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
# run: ctest -C ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Backtrace of crashed tests
|
||||
if: failure()
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
cores=(core.*)
|
||||
if [ ${#cores[@]} -eq 0 ]; then
|
||||
echo "No core file: the failure was an assertion or a wrong value, not a crash."
|
||||
exit 0
|
||||
fi
|
||||
sudo apt-get -y install gdb
|
||||
source ${{github.workspace}}/.github/scripts/ros-deps-env.sh "${{ matrix.ros_prefix }}"
|
||||
source ${{github.workspace}}/.github/scripts/ros-deps-env.sh "${{github.workspace}}/gtsam-install"
|
||||
for core in "${cores[@]}"; do
|
||||
exe="${core#core.}"
|
||||
exe="bin/${exe%.*}"
|
||||
echo "::group::$core ($exe)"
|
||||
gdb -batch -ex "thread apply all bt" "$exe" "$core" || true
|
||||
echo "::endgroup::"
|
||||
done
|
||||
86
.github/workflows/cmake-macos.yml
vendored
86
.github/workflows/cmake-macos.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
@@ -23,49 +22,23 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
build_name: [macos-sequoia-apple-silicon, macos-tahoe-apple-silicon, macos-tahoe-apple-silicon-cv5]
|
||||
build_name: [macos-sequoia-intel, macos-sequoia-apple-silicon, macos-tahoe-intel, macos-tahoe-apple-silicon]
|
||||
include:
|
||||
# macos-sequoia-intel temporarily disabled. Had some g2o optimizer issues.
|
||||
#- build_name: macos-sequoia-intel
|
||||
# os: macos-15-intel
|
||||
# cv: opencv@4
|
||||
- build_name: macos-sequoia-intel
|
||||
os: macos-15-intel
|
||||
- build_name: macos-sequoia-apple-silicon
|
||||
os: macos-15
|
||||
cv: opencv@4
|
||||
# - build_name: macos-tahoe-intel
|
||||
# os: macos-26-intel
|
||||
# cv: opencv@4
|
||||
- build_name: macos-tahoe-intel
|
||||
os: macos-26-intel
|
||||
- build_name: macos-tahoe-apple-silicon
|
||||
os: macos-26
|
||||
cv: opencv@4
|
||||
# - build_name: macos-tahoe-intel-cv5
|
||||
# os: macos-26-intel
|
||||
# cv: opencv
|
||||
- build_name: macos-tahoe-apple-silicon-cv5
|
||||
os: macos-26
|
||||
cv: opencv
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Restore test data
|
||||
id: cache-testdata
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
# SQLite DBs are binary-portable -> share the cache across
|
||||
# linux / macos / windows. Key omits runner.os on purpose so all
|
||||
# three OSes hit the same entry.
|
||||
path: data/tests/*.db
|
||||
key: testdata-${{ hashFiles('data/tests/manifest.txt') }}
|
||||
|
||||
- name: Fetch test data
|
||||
if: steps.cache-testdata.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: bash scripts/fetch_test_data.sh
|
||||
|
||||
- name: Install Brew Dependencies
|
||||
run: |
|
||||
# Update brew and install from Brewfile if present, or specific packages
|
||||
brew install pcl octomap pdal yaml-cpp librealsense libfreenect libusb zlib libomp suite-sparse ceres-solver ${{ matrix.cv }}
|
||||
brew install pcl opencv octomap pdal yaml-cpp librealsense libfreenect libusb zlib libomp suite-sparse ceres-solver
|
||||
|
||||
- name: Install Source Dependencies
|
||||
# Build (and per-dependency cache) the source-only deps not available from
|
||||
@@ -83,7 +56,6 @@ jobs:
|
||||
LIBOMP=$(brew --prefix libomp)
|
||||
cmake -B ${{github.workspace}}/build \
|
||||
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
|
||||
-DBUILD_TESTING=ON \
|
||||
-DBUILD_AS_BUNDLE=ON \
|
||||
-DWITH_ORBBEC_SDK=ON \
|
||||
-DWITH_DEPTHAI=ON \
|
||||
@@ -116,45 +88,9 @@ jobs:
|
||||
if-no-files-found: warn
|
||||
retention-days: ${{ github.event_name == 'pull_request' && 1 || 90 }}
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
# PYTHONNOUSERSITE=1: prevent the embedded Python interpreter from
|
||||
# loading numpy / other site-packages from the user's site that were
|
||||
# compiled against a different ABI than the build-time Python.
|
||||
env:
|
||||
PYTHONNOUSERSITE: 1
|
||||
run: |
|
||||
ctest -C ${{env.BUILD_TYPE}} -V -LE performance
|
||||
|
||||
# A ctest SEGFAULT is reported as just "SEGFAULT" with no backtrace, which
|
||||
# makes a crash inside a long integration test invisible -- the log simply
|
||||
# stops mid-test. macOS's ReportCrash writes a symbolized .ips report for
|
||||
# every crashed process, so surface any that appeared during this job.
|
||||
- name: Dump macOS crash reports on failure
|
||||
if: failure()
|
||||
run: |
|
||||
# ReportCrash can lag the process death by a few seconds.
|
||||
sleep 20
|
||||
found=0
|
||||
for d in "$HOME/Library/Logs/DiagnosticReports" /Library/Logs/DiagnosticReports; do
|
||||
[ -d "$d" ] || continue
|
||||
while IFS= read -r f; do
|
||||
found=1
|
||||
echo "::group::$f"
|
||||
cat "$f"
|
||||
echo "::endgroup::"
|
||||
done < <(find "$d" -type f \( -name '*.ips' -o -name '*.crash' \) -mtime -1 2>/dev/null | sort)
|
||||
done
|
||||
if [ "$found" = 0 ]; then
|
||||
echo "No crash reports found -- the job may have failed without a crash."
|
||||
fi
|
||||
|
||||
- name: Upload ctest log on failure
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ctest-log-${{ matrix.build_name }}
|
||||
path: build/Testing/Temporary/LastTest.log
|
||||
if-no-files-found: warn
|
||||
retention-days: 7
|
||||
# - name: Test
|
||||
# working-directory: ${{github.workspace}}/build
|
||||
# # Execute tests defined by the CMake configuration.
|
||||
# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
# run: ctest -C ${{env.BUILD_TYPE}}
|
||||
|
||||
|
||||
56
.github/workflows/cmake-ros.yml
vendored
56
.github/workflows/cmake-ros.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
@@ -20,9 +19,6 @@ jobs:
|
||||
build:
|
||||
name: ${{ matrix.ros_distribution }}
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.ros_distribution }}
|
||||
cancel-in-progress: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -43,50 +39,11 @@ jobs:
|
||||
image: osrf/ros:${{ matrix.ros_distribution }}-desktop-full
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Restore test data
|
||||
id: cache-testdata
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: data/tests/*.db
|
||||
key: testdata-${{ hashFiles('data/tests/manifest.txt') }}
|
||||
|
||||
- name: Fetch test data
|
||||
if: steps.cache-testdata.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
# The ROS images are slim; fetch_test_data.sh needs curl, and 7-Zip for
|
||||
# the assets that come as an archive. The package holding it is
|
||||
# p7zip-full up to Ubuntu 24.04 and 7zip on the newer ones.
|
||||
command -v curl >/dev/null || (apt-get update && apt-get install -y --no-install-recommends curl)
|
||||
command -v 7z >/dev/null || command -v 7zz >/dev/null || (apt-get update && \
|
||||
(apt-get install -y --no-install-recommends p7zip-full || \
|
||||
apt-get install -y --no-install-recommends 7zip))
|
||||
bash scripts/fetch_test_data.sh
|
||||
|
||||
# `${{ github.workspace }}` is expanded by the runner on the *host*
|
||||
# (/home/runner/work/...), but this job runs in a container where the runner
|
||||
# bind-mounts that directory somewhere else (/__w/...). Baking the host path
|
||||
# into RTABMAP_TEST_DATA_ROOT points every data-driven test at a directory
|
||||
# that does not exist in the container: the integration tests silently skip
|
||||
# and the ones that imread() committed fixtures abort outright.
|
||||
#
|
||||
# Resolve the path inside the container instead. A `run` step's cwd is the
|
||||
# checkout root -- the same assumption the relative `scripts/...` and
|
||||
# `data/tests/*.db` paths in the steps above already rely on -- so derive it
|
||||
# from $PWD rather than trusting a path computed outside the container. The
|
||||
# existence checks then turn that into a verified precondition: a wrong
|
||||
# mount point or a failed fetch fails here, with the path printed, instead
|
||||
# of degrading into skipped tests.
|
||||
- name: Resolve test data root in the container
|
||||
id: testdata
|
||||
shell: bash
|
||||
run: |
|
||||
root="$PWD/data"
|
||||
test -f "$root/samples/17.jpg" || { echo "::error::test data root $root is missing committed fixtures"; exit 1; }
|
||||
ls "$root"/tests/*.db >/dev/null || { echo "::error::no test databases in $root/tests"; exit 1; }
|
||||
echo "root=$root" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Newer osrf/ros images (e.g. rolling) ship "ros2-testing-apt-source", which conflicts
|
||||
# with the "ros2-apt-source" package that setup-ros installs. Remove it first so setup-ros
|
||||
# can install its apt source cleanly (no-op on images that don't have it).
|
||||
- name: Remove conflicting ros2 apt source package
|
||||
run: apt-get remove -y ros2-testing-apt-source || true
|
||||
- uses: ros-tooling/setup-ros@v0.7
|
||||
with:
|
||||
required-ros-distributions: ${{ matrix.ros_distribution }}
|
||||
@@ -96,5 +53,4 @@ jobs:
|
||||
package-name: rtabmap
|
||||
target-ros2-distro: ${{ matrix.ros_distribution }}
|
||||
rosdep-skip-keys: "${{ matrix.skip_keys }}"
|
||||
coverage-result: false
|
||||
extra-cmake-args: -DTEST_DATA_ROOT=${{ steps.testdata.outputs.root }}
|
||||
|
||||
|
||||
62
.github/workflows/cmake-windows.yml
vendored
62
.github/workflows/cmake-windows.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
@@ -37,22 +36,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Restore test data
|
||||
id: cache-testdata
|
||||
if: matrix.build_name != 'windows-2022-cuda'
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
# SQLite DBs are binary-portable -> share the cache across
|
||||
# linux / macos / windows. Key omits runner.os on purpose so all
|
||||
# three OSes hit the same entry.
|
||||
path: data/tests/*.db
|
||||
key: testdata-${{ hashFiles('data/tests/manifest.txt') }}
|
||||
|
||||
- name: Fetch test data
|
||||
if: matrix.build_name != 'windows-2022-cuda' && steps.cache-testdata.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: bash scripts/fetch_test_data.sh
|
||||
|
||||
- name: Install Windows Dependencies
|
||||
if: matrix.build_name == 'windows-2022'
|
||||
uses: ./.github/actions/install-windows-deps
|
||||
@@ -82,7 +65,6 @@ jobs:
|
||||
-DWITH_REALSENSE2=ON `
|
||||
-DWITH_CCCORELIB=ON `
|
||||
${{ matrix.extra_cmake_def }} `
|
||||
-DBUILD_TESTING=ON `
|
||||
-DVCPKG_MANIFEST_INSTALL=OFF `
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-release `
|
||||
-DVCPKG_INSTALLED_DIR="${{env.VCPKG_EXPORT_PATH}}/installed" `
|
||||
@@ -90,42 +72,7 @@ jobs:
|
||||
-DTorch_DIR=${{env.VCPKG_EXPORT_PATH}}/installed/x64-windows-release/tools/python3/Lib/site-packages/torch/share/cmake/Torch
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --target ALL_BUILD
|
||||
|
||||
- name: Test
|
||||
# Not run on the CUDA build, which is a build+package job only.
|
||||
#
|
||||
# The runner has no NVIDIA GPU or driver (see the driver-DLL note in
|
||||
# app/src/CMakeLists.txt), so no cv::cuda / CUDASIFT path can actually
|
||||
# execute there -- everything falls back to CPU and the run just repeats
|
||||
# the coverage the non-CUDA job already gives, on the same sources. It
|
||||
# used to cost more than it returned: the two cv::cuda-touching tests
|
||||
# (test_util3d_features, test_localgrid) had to be excluded because lazy
|
||||
# CUDA-init stalled them past the 300 s timeout, and once WITH_ZED=ON was
|
||||
# added to this job every remaining corelib test died in the loader with
|
||||
# 0xc0000135 (STATUS_DLL_NOT_FOUND) before reaching main(): rtabmap_core
|
||||
# links sl_zed64.dll, which imports the driver-only nvcuvid.dll /
|
||||
# nvEncodeAPI64.dll that a driver-less runner does not have.
|
||||
#
|
||||
# So this job compiles, links and packages the CUDA artifact; the
|
||||
# non-CUDA job is what verifies behaviour. Same reason the Info step
|
||||
# below is skipped here.
|
||||
if: matrix.build_name != 'windows-2022-cuda'
|
||||
working-directory: ${{github.workspace}}/build
|
||||
# PYTHONHOME points the embedded interpreter at vcpkg's bundled
|
||||
# Python install (where Lib/, DLLs/ live). Without it Python emits
|
||||
# "Could not find platform independent libraries <prefix>" at init
|
||||
# and runs without a stdlib, breaking every numpy import.
|
||||
# PYTHONNOUSERSITE=1: prevent the embedded Python interpreter from
|
||||
# loading numpy / other site-packages from %APPDATA%\Python that
|
||||
# were compiled against a different ABI than the build-time Python
|
||||
# (numpy 1.x/2.x mismatch crashes in test_pydetector /
|
||||
# test_pydescriptor / test_pymatcher).
|
||||
env:
|
||||
PYTHONHOME: ${{env.VCPKG_EXPORT_PATH}}/installed/x64-windows-release/tools/python3
|
||||
PYTHONNOUSERSITE: 1
|
||||
run: |
|
||||
ctest -C ${{env.BUILD_TYPE}} -V --timeout 300 -LE performance
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Info
|
||||
# Skipped for CUDA: the binary links ZED (sl_zed64.dll -> nvcuvid/nvEncodeAPI64),
|
||||
@@ -169,3 +116,10 @@ jobs:
|
||||
compression-level: 0
|
||||
if-no-files-found: warn
|
||||
retention-days: ${{ github.event_name == 'pull_request' && 1 || 90 }}
|
||||
|
||||
# - name: Test
|
||||
# working-directory: ${{github.workspace}}/build
|
||||
# # Execute tests defined by the CMake configuration.
|
||||
# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||
# run: ctest -C ${{env.BUILD_TYPE}}
|
||||
|
||||
|
||||
198
.github/workflows/coverage.yml
vendored
198
.github/workflows/coverage.yml
vendored
@@ -1,198 +0,0 @@
|
||||
name: Coverage
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Debug
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
name: Coverage (ubuntu-24.04)
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Restore test data
|
||||
id: cache-testdata
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
# Same key as the other workflows so all of them share one entry.
|
||||
path: data/tests/*.db
|
||||
key: testdata-${{ hashFiles('data/tests/manifest.txt') }}
|
||||
|
||||
- name: Fetch test data
|
||||
if: steps.cache-testdata.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: bash scripts/fetch_test_data.sh
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
lcov \
|
||||
libopencv-dev \
|
||||
libpcl-dev \
|
||||
git \
|
||||
cmake \
|
||||
software-properties-common \
|
||||
libyaml-cpp-dev \
|
||||
libg2o-dev \
|
||||
libceres-dev \
|
||||
curl \
|
||||
gnupg \
|
||||
lsb-release
|
||||
|
||||
# GTSAM and libpointmatcher are not in the Ubuntu archive, and
|
||||
# borglab's gtsam PPAs have no noble packages (gtsam-release-4.2 has
|
||||
# no noble suite; gtsam-develop's noble Packages index is empty). The
|
||||
# ROS 2 repo ships both for noble, which is also what the ROS CI jobs
|
||||
# test against, so take them from there.
|
||||
sudo curl -fsSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
|
||||
-o /usr/share/keyrings/ros-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" \
|
||||
| sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
ros-jazzy-gtsam \
|
||||
ros-jazzy-libpointmatcher
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -B ${{ github.workspace }}/build \
|
||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} \
|
||||
-DENABLE_COVERAGE=ON \
|
||||
-DCMAKE_PREFIX_PATH=/opt/ros/jazzy \
|
||||
-DBUILD_TESTING=ON \
|
||||
-DBUILD_APP=OFF \
|
||||
-DBUILD_TOOLS=OFF \
|
||||
-DBUILD_EXAMPLES=OFF \
|
||||
-DWITH_QT=OFF \
|
||||
-DWITH_PYTHON=OFF \
|
||||
-DWITH_CERES=ON \
|
||||
-DWITH_G2O=ON \
|
||||
-DWITH_GTSAM=ON \
|
||||
-DWITH_MRPT=OFF \
|
||||
-DWITH_CVSBA=OFF \
|
||||
-DWITH_POINTMATCHER=ON \
|
||||
-DWITH_CCCORELIB=OFF \
|
||||
-DWITH_OPEN3D=OFF \
|
||||
-DWITH_LOAM=OFF \
|
||||
-DWITH_FLOAM=OFF \
|
||||
-DWITH_LIOSAM=OFF \
|
||||
-DWITH_FLYCAPTURE2=OFF \
|
||||
-DWITH_ZED=OFF \
|
||||
-DWITH_ZEDOC=OFF \
|
||||
-DWITH_REALSENSE=OFF
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{ github.workspace }}/build -j$(nproc)
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{ github.workspace }}/build
|
||||
run: |
|
||||
# The ROS packages are not on the default loader path. Both
|
||||
# directories are needed: GTSAM and libpointmatcher land in
|
||||
# /opt/ros/jazzy/lib, but gtsam's own libmetis-gtsam.so goes to the
|
||||
# multiarch subdirectory, and libgtsam.so carries no RUNPATH, so the
|
||||
# loader can only find it through LD_LIBRARY_PATH.
|
||||
ROS_LIB=/opt/ros/jazzy/lib
|
||||
export LD_LIBRARY_PATH="${{ github.workspace }}/build/bin:${ROS_LIB}:${ROS_LIB}/$(gcc -dumpmachine)${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
find ${{ github.workspace }}/build -name '*.gcda' -delete
|
||||
# Unit tests only ("long" is the end-to-end replay suite).
|
||||
#
|
||||
# Two reasons. It costs ~90 min in this instrumented -O0 build, 94% of
|
||||
# the job's test time, for ~3 points of coverage. And running it on
|
||||
# some events but not others makes the numbers incomparable: Codecov
|
||||
# measures a pull request against master, so a unit-only PR report
|
||||
# against a full master report reads as a coverage regression on every
|
||||
# PR. Same scope everywhere keeps the comparison meaningful.
|
||||
#
|
||||
# The replays still run (and gate) in the cmake-linux / macos /
|
||||
# windows jobs; they are just not measured here.
|
||||
ctest -V -LE "long|performance"
|
||||
|
||||
- name: Generate LCOV report
|
||||
run: |
|
||||
GCOV_VER="$(gcc -dumpversion | cut -d. -f1)"
|
||||
if command -v "gcov-${GCOV_VER}" >/dev/null 2>&1; then
|
||||
GCOV_TOOL="gcov-${GCOV_VER}"
|
||||
else
|
||||
GCOV_TOOL="gcov"
|
||||
fi
|
||||
LCOV_IGNORE=(--ignore-errors gcov,source,graph,mismatch,unused)
|
||||
|
||||
# The whole of corelib/utilite, test objects included, not just their src/.
|
||||
# An inline function defined in a header is emitted in whichever
|
||||
# translation unit wins comdat folding, which is often a test one, and
|
||||
# its counters then live in that unit's .gcda: capturing only src/
|
||||
# reports such a function as uncovered however often it is called. The
|
||||
# test sources themselves are dropped below, after the counters are read.
|
||||
lcov --gcov-tool "$GCOV_TOOL" "${LCOV_IGNORE[@]}" \
|
||||
--capture \
|
||||
--directory ${{ github.workspace }}/build/corelib \
|
||||
--directory ${{ github.workspace }}/build/utilite \
|
||||
--output-file lcov.info
|
||||
|
||||
lcov "${LCOV_IGNORE[@]}" --extract lcov.info \
|
||||
'${{ github.workspace }}/*' \
|
||||
--output-file lcov.info
|
||||
# The test sources are the instrument, not the subject: a line in a
|
||||
# test counts as uncovered only when the test skipped it (a defensive
|
||||
# cleanup branch, a platform guard), which says nothing about the
|
||||
# library. They are also near-fully covered by construction, so
|
||||
# leaving them in inflates the overall number. Excluded here, before
|
||||
# the upload, so the HTML artifact, the summary below and Codecov all
|
||||
# report the same figure. Kept in sync with coverage-report.sh.
|
||||
lcov "${LCOV_IGNORE[@]}" --remove lcov.info \
|
||||
'*/sqlite3/*' \
|
||||
'*/rtflann/*' \
|
||||
'*/corelib/test/*' \
|
||||
'*/utilite/test/*' \
|
||||
'*/_deps/*' \
|
||||
--output-file lcov.info
|
||||
|
||||
lcov --summary lcov.info
|
||||
|
||||
- name: HTML coverage report
|
||||
run: genhtml --ignore-errors source,mismatch lcov.info --output-directory coverage-html --legend --demangle-cpp
|
||||
|
||||
- name: Upload HTML coverage artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-html
|
||||
path: coverage-html
|
||||
retention-days: 14
|
||||
|
||||
- name: Upload to Codecov
|
||||
if: ${{ env.CODECOV_TOKEN != '' }}
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: lcov.info
|
||||
# Upload ONLY lcov.info. By default the CLI also searches the tree and
|
||||
# runs gcov over every .gcno it finds, uploading those .gcov files
|
||||
# alongside -- which re-introduced corelib/test and utilite/test on
|
||||
# Codecov even though the lcov --remove step had dropped them (the
|
||||
# HTML artifact, built from the same lcov.info, was correctly clean).
|
||||
disable_search: true
|
||||
plugins: noop
|
||||
token: ${{ env.CODECOV_TOKEN }}
|
||||
fail_ci_if_error: false
|
||||
9
.github/workflows/docker.yml
vendored
9
.github/workflows/docker.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -23,9 +22,8 @@ jobs:
|
||||
# $ sudo apt-get upgrade qemu-user-static
|
||||
# $ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes -c yes
|
||||
# More info: https://github.com/introlab/rtabmap/issues/1454
|
||||
# Skipped on pull requests; built and pushed only from master (push or
|
||||
# manual dispatch), since it pushes the :*-deps tags to Docker Hub.
|
||||
if: github.ref == 'refs/heads/master'
|
||||
# Skipped on pull requests; built and pushed only on push to master.
|
||||
if: github.event_name != 'pull_request'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -103,8 +101,7 @@ jobs:
|
||||
docker:
|
||||
needs: docker_deps
|
||||
# Run even when docker_deps is skipped (it is, on pull requests).
|
||||
# A manual dispatch is honored only on master, the only ref we push from.
|
||||
if: ${{ !cancelled() && !failure() && (github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/master') }}
|
||||
if: ${{ !cancelled() && !failure() }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
|
||||
54
.github/workflows/docs-preview-cleanup.yml
vendored
54
.github/workflows/docs-preview-cleanup.yml
vendored
@@ -1,54 +0,0 @@
|
||||
name: Documentation preview cleanup
|
||||
|
||||
# Removes the per-PR documentation preview published by docs.yml under
|
||||
# gh-pages:preview/pr-<number>/ once the pull request is closed (merged or not).
|
||||
#
|
||||
# Deliberately has no `paths` filter: docs.yml only publishes a preview when
|
||||
# documentation-related files changed, but this must fire for every close so a
|
||||
# preview can never be orphaned on the branch.
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
name: Remove PR preview
|
||||
# Forks never get a preview (their token is read-only), so nothing to clean.
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
# Same group as docs.yml's publish job: both push to gh-pages, and two
|
||||
# concurrent pushes would collide.
|
||||
concurrency:
|
||||
group: gh-pages-publish
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout gh-pages
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: gh-pages
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Remove preview directory
|
||||
env:
|
||||
PREVIEW_DIR: preview/pr-${{ github.event.number }}
|
||||
run: |
|
||||
set -eux
|
||||
if [[ ! -d "${PREVIEW_DIR}" ]]; then
|
||||
echo "No preview at ${PREVIEW_DIR}, nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
git rm -r --quiet "${PREVIEW_DIR}"
|
||||
# Drop the parent too once the last preview is gone.
|
||||
rmdir preview 2>/dev/null || true
|
||||
git commit -m "Remove preview for PR #${{ github.event.number }}"
|
||||
git push
|
||||
218
.github/workflows/docs.yml
vendored
218
.github/workflows/docs.yml
vendored
@@ -1,218 +0,0 @@
|
||||
name: Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*'
|
||||
# Publishing commits to the gh-pages branch, so only run when something that
|
||||
# affects the documentation actually changed. Use the manual "Run workflow"
|
||||
# button if a deploy is needed outside of these paths.
|
||||
paths: &docs_paths
|
||||
- '.github/workflows/docs.yml'
|
||||
- 'CMakeLists.txt'
|
||||
- 'Doxyfile.in'
|
||||
- 'docs-report.sh'
|
||||
- 'website/**'
|
||||
- 'doxygen/**'
|
||||
- 'package.xml'
|
||||
- 'corelib/include/**'
|
||||
- 'utilite/include/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
paths: *docs_paths
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build site (ubuntu-24.04)
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
outputs:
|
||||
version: ${{ steps.site.outputs.version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
doxygen \
|
||||
graphviz \
|
||||
cmake \
|
||||
libopencv-dev \
|
||||
libpcl-dev \
|
||||
libyaml-cpp-dev \
|
||||
git
|
||||
|
||||
# Configures build-docs (CMake generates the export headers that are part
|
||||
# of Doxygen's INPUT), runs Doxygen into build-docs/api/latest/. Same
|
||||
# script used locally, so a local preview matches what gets published.
|
||||
- name: Generate C++ API documentation (Doxygen)
|
||||
run: ./docs-report.sh
|
||||
|
||||
# The theme emits absolute asset links built from site.baseurl, which
|
||||
# GitHub's own Jekyll infers from the repository (/rtabmap). A PR preview
|
||||
# is served from a deeper path, so those links would 404 there. Pin
|
||||
# baseurl to the path this particular build gets published to.
|
||||
- name: Set Jekyll baseurl
|
||||
run: |
|
||||
set -eux
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
baseurl="/${{ github.event.repository.name }}/preview/pr-${{ github.event.number }}"
|
||||
else
|
||||
baseurl="/${{ github.event.repository.name }}"
|
||||
fi
|
||||
echo "baseurl: ${baseurl}" >> website/_config.yml
|
||||
|
||||
# Landing page: built here rather than by GitHub's own Jekyll, so that
|
||||
# .nojekyll can be set below and the Doxygen output is served untouched.
|
||||
- name: Build landing page (Jekyll)
|
||||
uses: actions/jekyll-build-pages@v1
|
||||
with:
|
||||
source: ./website
|
||||
destination: ./_site
|
||||
|
||||
- name: Assemble site
|
||||
id: site
|
||||
run: |
|
||||
set -eux
|
||||
# jekyll-build-pages is a container action running as root, so _site
|
||||
# comes back owned by root and the steps below (running as the runner
|
||||
# user) cannot write into it.
|
||||
sudo chown -R "$(id -u):$(id -g)" _site
|
||||
|
||||
# Serve everything verbatim: the site is already built, and Jekyll
|
||||
# would otherwise skip Doxygen files/folders starting with '_'.
|
||||
touch _site/.nojekyll
|
||||
|
||||
mkdir -p _site/api
|
||||
cp -r build-docs/api/latest _site/api/latest
|
||||
|
||||
# On a release tag (plain X.Y.Z, not the ROS-distro variants such as
|
||||
# 0.23.7-jazzy), also publish under the version number. Older versions
|
||||
# already on the branch are preserved by keep_files when publishing.
|
||||
version=""
|
||||
if [[ "${GITHUB_REF_TYPE}" == "tag" && "${GITHUB_REF_NAME}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
version="${GITHUB_REF_NAME}"
|
||||
cp -r build-docs/api/latest "_site/api/${version}"
|
||||
fi
|
||||
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# The dropdown list is maintained by hand in the repository (one entry
|
||||
# per release) and shared by every published version -- see the header
|
||||
# comment in doxygen/versions.js.
|
||||
cp doxygen/versions.js _site/api/versions.js
|
||||
|
||||
# /api/ has no content of its own: send it to the current docs so a
|
||||
# bare .../api/ link lands somewhere useful instead of a 404. The
|
||||
# target is relative, so it works at the site root and under a
|
||||
# preview prefix alike.
|
||||
printf '%s\n' \
|
||||
'<!doctype html>' \
|
||||
'<meta charset="utf-8">' \
|
||||
'<title>RTAB-Map API documentation</title>' \
|
||||
'<meta http-equiv="refresh" content="0; url=latest/">' \
|
||||
'<link rel="canonical" href="latest/">' \
|
||||
'<p>Redirecting to the <a href="latest/">latest API documentation</a>.</p>' \
|
||||
> _site/api/index.html
|
||||
|
||||
test -f _site/index.html
|
||||
test -f _site/api/index.html
|
||||
test -f _site/api/latest/index.html
|
||||
|
||||
- name: Upload site artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: documentation-site
|
||||
path: _site
|
||||
# include-hidden-files keeps .nojekyll, which the publish job needs.
|
||||
include-hidden-files: true
|
||||
retention-days: 14
|
||||
|
||||
publish:
|
||||
name: Publish to gh-pages
|
||||
needs: build
|
||||
# Forks are skipped for pull requests: their GITHUB_TOKEN is read-only, and
|
||||
# granting write access to a workflow that builds untrusted code would be a
|
||||
# security hole.
|
||||
if: >-
|
||||
github.event_name != 'pull_request' ||
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
# Serialize every push to gh-pages, including the preview cleanup workflow,
|
||||
# and never cancel one in flight: two concurrent pushes would collide.
|
||||
concurrency:
|
||||
group: gh-pages-publish
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Download site
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: documentation-site
|
||||
path: _site
|
||||
|
||||
# Pull requests go to preview/pr-<number>/, everything else to the site
|
||||
# root. keep_files preserves what this run does not carry: the published
|
||||
# version folders (api/0.23.x/) and the other PRs' previews.
|
||||
- name: Publish
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_branch: gh-pages
|
||||
publish_dir: ./_site
|
||||
destination_dir: ${{ github.event_name == 'pull_request' && format('preview/pr-{0}', github.event.number) || '' }}
|
||||
keep_files: true
|
||||
# Do not let the action drop a .nojekyll at the *branch root*: that
|
||||
# disables Jekyll for everything on the branch, including content
|
||||
# published outside this deployment. The site we publish carries its
|
||||
# own .nojekyll inside destination_dir, which is the only place it
|
||||
# should apply.
|
||||
enable_jekyll: true
|
||||
user_name: 'github-actions[bot]'
|
||||
user_email: 'github-actions[bot]@users.noreply.github.com'
|
||||
commit_message: >-
|
||||
${{ github.event_name == 'pull_request'
|
||||
&& format('Preview for PR #{0}', github.event.number)
|
||||
|| format('Update documentation ({0})', needs.build.outputs.version || 'latest') }}
|
||||
|
||||
- name: Link the site
|
||||
run: |
|
||||
base="https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}"
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
url="${base}/preview/pr-${{ github.event.number }}"
|
||||
note="Removed automatically when this pull request is closed."
|
||||
else
|
||||
url="${base}"
|
||||
note=""
|
||||
fi
|
||||
{
|
||||
echo "### Documentation"
|
||||
echo ""
|
||||
echo "- Landing page: ${url}/"
|
||||
echo "- C++ API: ${url}/api/latest/"
|
||||
# An `if` rather than `[[ ... ]] && { ... }`: as the last command in
|
||||
# the group, a false test would make the group (and so the whole
|
||||
# step) exit 1. `note` is empty on push, which failed every master
|
||||
# run while pull requests passed.
|
||||
if [[ -n "${note}" ]]; then
|
||||
echo ""
|
||||
echo "${note}"
|
||||
fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
1
.github/workflows/ios.yml
vendored
1
.github/workflows/ios.yml
vendored
@@ -16,7 +16,6 @@ on:
|
||||
branches:
|
||||
- '**'
|
||||
paths: *ios_paths
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
|
||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -12,20 +12,6 @@ compile_flags.txt
|
||||
tags
|
||||
build_*
|
||||
*.bak
|
||||
build-*
|
||||
|
||||
# Coverage (coverage-report.sh / .github/workflows/coverage.yml)
|
||||
coverage-html/
|
||||
lcov.info
|
||||
*.gcda
|
||||
*.gcno
|
||||
|
||||
# Doxygen (docs-report.sh / .github/workflows/docs.yml)
|
||||
doc/
|
||||
# Byte-code of doxygen/generate_parameters_page.py
|
||||
__pycache__/
|
||||
# Generated from Doxyfile.in by CMake.
|
||||
Doxyfile
|
||||
|
||||
# Windows bundle: vcpkg tree, build output, and deps built from source (bundle_windows_deps.bat)
|
||||
vcpkg/
|
||||
|
||||
123
CMakeLists.txt
123
CMakeLists.txt
@@ -21,8 +21,8 @@ SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules")
|
||||
# VERSION
|
||||
#######################
|
||||
SET(RTABMAP_MAJOR_VERSION 0)
|
||||
SET(RTABMAP_MINOR_VERSION 23)
|
||||
SET(RTABMAP_PATCH_VERSION 11)
|
||||
SET(RTABMAP_MINOR_VERSION 24)
|
||||
SET(RTABMAP_PATCH_VERSION 0)
|
||||
SET(RTABMAP_VERSION
|
||||
${RTABMAP_MAJOR_VERSION}.${RTABMAP_MINOR_VERSION}.${RTABMAP_PATCH_VERSION})
|
||||
|
||||
@@ -63,6 +63,10 @@ ELSE()
|
||||
option(FLANN_KDTREE_MEM_OPT "Disable multi-threaded FLANN kd-tree to minimize memory allocations" ON)
|
||||
ENDIF()
|
||||
|
||||
IF(FLANN_KDTREE_MEM_OPT)
|
||||
ADD_DEFINITIONS("-DFLANN_KDTREE_MEM_OPT")
|
||||
ENDIF(FLANN_KDTREE_MEM_OPT)
|
||||
|
||||
IF(WIN32 AND NOT MINGW)
|
||||
ADD_DEFINITIONS("-DNOMINMAX")
|
||||
ADD_DEFINITIONS("-wd4100 -wd4127 -wd4150 -wd4191 -wd4242 -wd4244 -wd4251 -wd4305 -wd4365 -wd4512 -wd4514 -wd4548 -wd4571 -wd4619 -wd4625 -wd4626 -wd4628 -wd4668 -wd4710 -wd4711 -wd4738 -wd4820 -wd4946 -wd4986")
|
||||
@@ -81,11 +85,6 @@ endif()
|
||||
if(POLICY CMP0072)
|
||||
cmake_policy(SET CMP0072 NEW)
|
||||
endif()
|
||||
# Timestamp the files extracted from a FetchContent URL download at extraction
|
||||
# time, so a changed URL actually triggers a rebuild of what depends on them.
|
||||
if(POLICY CMP0135)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
endif()
|
||||
|
||||
IF(MINGW)
|
||||
# Hide the --enable-auto-import warning
|
||||
@@ -168,68 +167,6 @@ ENDIF(APPLE OR WIN32)
|
||||
OPTION(BUILD_APP "Build main application" ON)
|
||||
OPTION(BUILD_TOOLS "Build tools" ON)
|
||||
OPTION(BUILD_EXAMPLES "Build examples" ON)
|
||||
# Off by default: it downloads the Doxygen theme at configure time, which an
|
||||
# ordinary build has no reason to need. Set by docs-report.sh.
|
||||
OPTION(BUILD_DOCUMENTATION "Prepare the API documentation build (downloads the Doxygen theme)" OFF)
|
||||
|
||||
# The unit tests are host executables run by ctest, which can't be done from a
|
||||
# cross-compiled Android/iOS build.
|
||||
if(ANDROID OR IOS OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||
set(BUILD_TESTING OFF CACHE BOOL "Build the testing tree." FORCE)
|
||||
endif()
|
||||
|
||||
# Performance tests are benchmarks: they report times, memory and recall
|
||||
# instead of asserting on them, as those depend on the machine. They are built
|
||||
# by default so they cannot rot, but are labelled "performance" so that a run
|
||||
# opts in with `ctest -L performance` and CI opts out with `ctest -LE
|
||||
# performance`. They can also be run directly, e.g. bin/test_flann_index_perf.
|
||||
OPTION(BUILD_PERF_TESTS "Build the performance tests (see ctest -L performance)" ON)
|
||||
|
||||
include(CTest)
|
||||
if(BUILD_TESTING)
|
||||
# Add GTest using FetchContent
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
googletest
|
||||
URL https://github.com/google/googletest/archive/refs/tags/v1.16.0.zip
|
||||
)
|
||||
FetchContent_GetProperties(googletest)
|
||||
if(NOT googletest_POPULATED)
|
||||
FetchContent_Populate(googletest)
|
||||
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
|
||||
endif()
|
||||
include(GoogleTest)
|
||||
set(TEST_DATA_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/data"
|
||||
CACHE PATH "Root directory holding the test fixtures (data/tests/*.db)")
|
||||
endif()
|
||||
|
||||
OPTION(ENABLE_COVERAGE "Build with gcov/lcov coverage (GCC/Clang, Debug only; see coverage-report.sh)" OFF)
|
||||
IF(ENABLE_COVERAGE)
|
||||
IF(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
MESSAGE(FATAL_ERROR "ENABLE_COVERAGE requires GCC or Clang with gcov/lcov (current: ${CMAKE_CXX_COMPILER_ID}).")
|
||||
ENDIF()
|
||||
IF(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
MESSAGE(FATAL_ERROR "ENABLE_COVERAGE requires CMAKE_BUILD_TYPE=Debug (current: \"${CMAKE_BUILD_TYPE}\").")
|
||||
ENDIF()
|
||||
MESSAGE(STATUS "Coverage instrumentation enabled")
|
||||
ADD_COMPILE_OPTIONS(--coverage -O0 -g)
|
||||
ADD_LINK_OPTIONS(--coverage)
|
||||
ENDIF()
|
||||
|
||||
# Check UDEBUG/UINFO/UWARN/UERROR/uFormat format strings against their
|
||||
# arguments.
|
||||
# -Wno-format-zero-length: UDEBUG("") is used deliberately as a trace point
|
||||
# -Wformat-security: non-literal format strings, e.g. UERROR(uFormat(...).c_str())
|
||||
OPTION(ENABLE_FORMAT_ERRORS "Treat log/uFormat format-string mismatches as errors (GCC/Clang)" OFF)
|
||||
IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
ADD_COMPILE_OPTIONS(-Wformat -Wformat-security -Wno-format-zero-length)
|
||||
IF(ENABLE_FORMAT_ERRORS)
|
||||
MESSAGE(STATUS "Format-string mismatches are errors")
|
||||
ADD_COMPILE_OPTIONS(-Werror=format -Werror=format-security)
|
||||
ENDIF()
|
||||
ELSEIF(ENABLE_FORMAT_ERRORS)
|
||||
MESSAGE(WARNING "ENABLE_FORMAT_ERRORS has no effect on ${CMAKE_CXX_COMPILER_ID} (needs GCC/Clang)")
|
||||
ENDIF()
|
||||
|
||||
####### DEPENDENCIES #######
|
||||
IF(MOBILE_BUILD)
|
||||
@@ -304,25 +241,7 @@ option(BUILD_WITH_RPATH_NOT_RUNPATH "Explicitly disable usage of RUNPATH for the
|
||||
set(RTABMAP_QT_VERSION AUTO CACHE STRING "Force a specific Qt version.")
|
||||
set_property(CACHE RTABMAP_QT_VERSION PROPERTY STRINGS AUTO 4 5 6)
|
||||
|
||||
# OpenCV components. calib3d was split into "calib" + "geometry" in OpenCV 5.
|
||||
# These lists are reused below to generate RTABMapConfig.cmake so downstream
|
||||
# find_package(RTABMap) requests the same components this build used.
|
||||
SET(RTABMAP_OpenCV_COMPONENTS_5 core imgproc highgui stitching photo video videoio calib geometry)
|
||||
SET(RTABMAP_OpenCV_COMPONENTS_4 core imgproc highgui stitching photo video videoio calib3d)
|
||||
SET(RTABMAP_OpenCV_OPTIONAL_COMPONENTS_5 objdetect xfeatures2d nonfree gpu cudafeatures2d cudaoptflow cudaimgproc)
|
||||
SET(RTABMAP_OpenCV_OPTIONAL_COMPONENTS_4 aruco objdetect xfeatures2d nonfree gpu cudafeatures2d cudaoptflow cudaimgproc)
|
||||
|
||||
# Probe OpenCV without a version constraint first, then request the components
|
||||
# matching the detected major version. A version-constrained find that fails to
|
||||
# match (e.g. asking for 5 when only 4 is present) resets OpenCV_DIR to NOTFOUND,
|
||||
# which breaks toolchain builds that rely on a -DOpenCV_DIR hint (e.g. Android,
|
||||
# where CMAKE_FIND_ROOT_PATH restricts the search).
|
||||
FIND_PACKAGE(OpenCV REQUIRED QUIET COMPONENTS core)
|
||||
IF(OpenCV_VERSION_MAJOR GREATER 4)
|
||||
FIND_PACKAGE(OpenCV REQUIRED QUIET COMPONENTS ${RTABMAP_OpenCV_COMPONENTS_5} OPTIONAL_COMPONENTS ${RTABMAP_OpenCV_OPTIONAL_COMPONENTS_5})
|
||||
ELSE()
|
||||
FIND_PACKAGE(OpenCV REQUIRED QUIET COMPONENTS ${RTABMAP_OpenCV_COMPONENTS_4} OPTIONAL_COMPONENTS ${RTABMAP_OpenCV_OPTIONAL_COMPONENTS_4})
|
||||
ENDIF()
|
||||
FIND_PACKAGE(OpenCV REQUIRED QUIET COMPONENTS core calib3d imgproc highgui stitching photo video videoio OPTIONAL_COMPONENTS aruco objdetect xfeatures2d nonfree gpu cudafeatures2d cudaoptflow cudaimgproc)
|
||||
|
||||
IF(WITH_QT)
|
||||
FIND_PACKAGE(PCL 1.7 REQUIRED QUIET COMPONENTS common io kdtree search surface filters registration sample_consensus segmentation visualization)
|
||||
@@ -1326,15 +1245,6 @@ ENDIF()
|
||||
|
||||
CONFIGURE_FILE(Version.h.in ${CMAKE_CURRENT_BINARY_DIR}/corelib/src/include/${PROJECT_PREFIX}/core/Version.h)
|
||||
|
||||
# API documentation (Doxygen). Off by default: preparing it downloads the theme,
|
||||
# spawns Doxygen to regenerate the HTML header and runs the parameter-page
|
||||
# generator, none of which an ordinary build needs. docs-report.sh turns it on;
|
||||
# see also .github/workflows/docs.yml.
|
||||
IF(BUILD_DOCUMENTATION)
|
||||
INCLUDE(RTABMapDocumentation)
|
||||
RTABMAP_SETUP_DOCUMENTATION()
|
||||
ENDIF(BUILD_DOCUMENTATION)
|
||||
|
||||
ADD_SUBDIRECTORY( utilite )
|
||||
ADD_SUBDIRECTORY( corelib )
|
||||
|
||||
@@ -1410,18 +1320,6 @@ install(EXPORT rtabmapTargets
|
||||
####
|
||||
# Setup RTABMapConfig.cmake
|
||||
####
|
||||
IF(OpenCV_VERSION_MAJOR GREATER 4)
|
||||
SET(CONF_OPENCV_COMPONENTS ${RTABMAP_OpenCV_COMPONENTS_5})
|
||||
SET(CONF_OPENCV_OPTIONAL_COMPONENTS ${RTABMAP_OpenCV_OPTIONAL_COMPONENTS_5})
|
||||
ELSE()
|
||||
SET(CONF_OPENCV_COMPONENTS ${RTABMAP_OpenCV_COMPONENTS_4})
|
||||
SET(CONF_OPENCV_OPTIONAL_COMPONENTS ${RTABMAP_OpenCV_OPTIONAL_COMPONENTS_4})
|
||||
ENDIF()
|
||||
STRING(REPLACE ";" " " CONF_OPENCV_COMPONENTS "${CONF_OPENCV_COMPONENTS}")
|
||||
STRING(REPLACE ";" " " CONF_OPENCV_OPTIONAL_COMPONENTS "${CONF_OPENCV_OPTIONAL_COMPONENTS}")
|
||||
# Pin the OpenCV major version so downstream projects find the same major RTAB-Map was
|
||||
# built against
|
||||
SET(CONF_OPENCV_VERSION_MAJOR ${OpenCV_VERSION_MAJOR})
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
@@ -1567,10 +1465,6 @@ MESSAGE(STATUS " CMAKE_INSTALL_LIBDIR = ${CMAKE_INSTALL_LIBDIR}")
|
||||
MESSAGE(STATUS " BUILD_APP = ${BUILD_APP}")
|
||||
MESSAGE(STATUS " BUILD_TOOLS = ${BUILD_TOOLS}")
|
||||
MESSAGE(STATUS " BUILD_EXAMPLES = ${BUILD_EXAMPLES}")
|
||||
MESSAGE(STATUS " BUILD_TESTING = ${BUILD_TESTING}")
|
||||
MESSAGE(STATUS " BUILD_PERF_TESTS = ${BUILD_PERF_TESTS}")
|
||||
MESSAGE(STATUS " ENABLE_COVERAGE = ${ENABLE_COVERAGE}")
|
||||
MESSAGE(STATUS " ENABLE_FORMAT_ERRORS = ${ENABLE_FORMAT_ERRORS}")
|
||||
IF(NOT WIN32)
|
||||
# see comment above for the BUILD_SHARED_LIBS option on Windows
|
||||
MESSAGE(STATUS " BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS}")
|
||||
@@ -1592,8 +1486,7 @@ ENDIF(PCL_COMPILE_OPTIONS)
|
||||
MESSAGE(STATUS "")
|
||||
MESSAGE(STATUS "Optional dependencies ('*' affects some default parameters) :")
|
||||
IF(OpenCV_FOUND)
|
||||
IF((OpenCV_VERSION_MAJOR LESS 4 AND OPENCV_ARUCO_FOUND) OR
|
||||
(OpenCV_VERSION_MAJOR GREATER 4 AND OPENCV_OBJDETECT_FOUND))
|
||||
IF(OPENCV_ARUCO_FOUND)
|
||||
set(ARUCO_STR "YES")
|
||||
ELSE()
|
||||
set(ARUCO_STR "NO")
|
||||
|
||||
2824
Doxyfile.in
2824
Doxyfile.in
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,6 @@ rtabmap
|
||||
|
||||
[![Release][release-image]][releases]
|
||||
[![Downloads][downloads-image]][downloads]
|
||||
[](https://codecov.io/gh/introlab/rtabmap)
|
||||
[![License][license-image]][license]
|
||||
|
||||
[release-image]: https://img.shields.io/badge/release-0.23.1-green.svg?style=flat
|
||||
@@ -21,7 +20,6 @@ RTAB-Map library and standalone application.
|
||||
|
||||
* For more information (e.g., papers, major updates), visit [RTAB-Map's home page](http://introlab.github.io/rtabmap).
|
||||
* For installation instructions and examples, visit [RTAB-Map's wiki](https://github.com/introlab/rtabmap/wiki).
|
||||
* For the C++ API of the library, see the [API documentation](https://introlab.github.io/rtabmap/api/latest/), which also lists all [parameters](https://introlab.github.io/rtabmap/api/latest/parameters.html) and [command-line tools](https://introlab.github.io/rtabmap/api/latest/tools.html).
|
||||
|
||||
To use RTAB-Map under ROS, visit the [rtabmap](http://wiki.ros.org/rtabmap) page on the ROS wiki.
|
||||
|
||||
@@ -60,7 +58,7 @@ This project is supported by [IntRoLab - Intelligent / Interactive / Integrated
|
||||
<td><a href="http://build.ros.org/job/Nbin_ufv8_uFv8__rtabmap__ubuntu_focal_arm64__binary/"><img src="http://build.ros.org/buildStatus/icon?job=Nbin_ufv8_uFv8__rtabmap__ubuntu_focal_arm64__binary" alt="Build Status"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="5">ROS 2</td>
|
||||
<td rowspan="4">ROS 2</td>
|
||||
<td>Humble</td>
|
||||
<td><a href="http://build.ros2.org/job/Hbin_uJ64__rtabmap__ubuntu_jammy_amd64__binary/"><img src="http://build.ros2.org/buildStatus/icon?job=Hbin_uJ64__rtabmap__ubuntu_jammy_amd64__binary" alt="Build Status"/></td>
|
||||
</tr>
|
||||
@@ -70,10 +68,6 @@ This project is supported by [IntRoLab - Intelligent / Interactive / Integrated
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kilted</td>
|
||||
<td><a href="http://build.ros2.org/job/Lbin_uR64__rtabmap__ubuntu_resolute_amd64__binary/"><img src="http://build.ros2.org/buildStatus/icon?job=Lbin_uR64__rtabmap__ubuntu_resolute_amd64__binary" alt="Build Status"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Lyrical</td>
|
||||
<td><a href="http://build.ros2.org/job/Kbin_uN64__rtabmap__ubuntu_noble_amd64__binary/"><img src="http://build.ros2.org/buildStatus/icon?job=Kbin_uN64__rtabmap__ubuntu_noble_amd64__binary" alt="Build Status"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
# Mandatory dependencies
|
||||
find_dependency(OpenCV @CONF_OPENCV_VERSION_MAJOR@ COMPONENTS @CONF_OPENCV_COMPONENTS@ OPTIONAL_COMPONENTS @CONF_OPENCV_OPTIONAL_COMPONENTS@)
|
||||
find_dependency(OpenCV COMPONENTS core calib3d imgproc highgui stitching photo video OPTIONAL_COMPONENTS aruco objdetect xfeatures2d nonfree gpu cudafeatures2d)
|
||||
|
||||
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/RTABMap_guiTargets.cmake")
|
||||
find_dependency(PCL 1.7 COMPONENTS common io kdtree search surface filters registration sample_consensus segmentation visualization)
|
||||
|
||||
@@ -865,7 +865,7 @@ SensorData CameraTango::updateDataOnRender(Transform & pose)
|
||||
}
|
||||
|
||||
SensorData data;
|
||||
if(tangoDataReady_.acquireTry(1) == 0)
|
||||
if(tangoDataReady_.acquireTry(1))
|
||||
{
|
||||
boost::mutex::scoped_lock lock(tangoDataMutex_);
|
||||
data = tangoData_;
|
||||
|
||||
@@ -204,7 +204,7 @@ rtabmap::ParametersMap RTABMapApp::getRtabmapParameters()
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapMaxRetrieved(), "0")); // deactivate global retrieval
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRGBDMaxLocalRetrieved(), "0")); // deactivate local retrieval
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kMemMapLabelsAdded(), "false")); // don't create map labels
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapMemoryThr(), "1")); // keep the WM empty
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRtabmapMemoryThr(), "2")); // keep the WM empty
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kMemSTMSize(), "1")); // STM=1 -->
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRGBDProximityBySpace(), "false"));
|
||||
uInsert(parameters, rtabmap::ParametersPair(rtabmap::Parameters::kRGBDLinearUpdate(), "0"));
|
||||
@@ -3226,7 +3226,7 @@ int RTABMapApp::setMappingParameter(const std::string & key, const std::string &
|
||||
}
|
||||
else
|
||||
{
|
||||
UERROR("Key \"%s\" doesn't exist!", compatibleKey.c_str());
|
||||
UERROR(uFormat("Key \"%s\" doesn't exist!", compatibleKey.c_str()).c_str());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1078,7 +1078,7 @@
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/eigen3\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/pcl-1.15\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.23\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-latest\"",
|
||||
"\"$(SRCROOT)/../android/jni/tango-gl/include\"",
|
||||
"\"$(SRCROOT)/../android/jni/third-party/include\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/lib/vtk.framework/Headers\"",
|
||||
@@ -1139,7 +1139,7 @@
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/eigen3\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/pcl-1.15\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-0.23\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/include/rtabmap-latest\"",
|
||||
"\"$(SRCROOT)/../android/jni/tango-gl/include\"",
|
||||
"\"$(SRCROOT)/../android/jni/third-party/include\"",
|
||||
"\"$(SRCROOT)/RTABMapApp/Libraries/lib/vtk.framework/Headers\"",
|
||||
|
||||
@@ -303,3 +303,13 @@ cd ios
|
||||
cmake -G Xcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_SYSROOT=$sysroot -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 -DCMAKE_INSTALL_PREFIX=$prefix -DCMAKE_FIND_ROOT_PATH=$prefix -DWITH_QT=OFF -DBUILD_APP=OFF -DBUILD_TOOLS=OFF -DWITH_TORO=OFF -DWITH_VERTIGO=OFF -DWITH_MADGWICK=OFF -DWITH_ORB_OCTREE=ON -DBUILD_EXAMPLES=OFF -DWITH_LIBLAS=ON -DWITH_OPENGV=OFF ../../../../../..
|
||||
cmake --build . --config Release
|
||||
cmake --build . --config Release --target install
|
||||
|
||||
# rtabmap installs its headers into a version-stamped "rtabmap-<major>.<minor>" directory.
|
||||
# Create a version-agnostic "rtabmap-latest" symlink to the just-installed one (newest by
|
||||
# modification time) so the Xcode project's header search path does not need updating on every
|
||||
# rtabmap minor-version bump. ls -t is used instead of "sort -V" for BSD/macOS portability.
|
||||
rtabmap_versioned=$(cd "$prefix/include" && ls -td rtabmap-[0-9]* 2>/dev/null | head -n1)
|
||||
if [ -n "$rtabmap_versioned" ]; then
|
||||
ln -sfn "$rtabmap_versioned" "$prefix/include/rtabmap-latest"
|
||||
echo "Linked $prefix/include/rtabmap-latest -> $rtabmap_versioned"
|
||||
fi
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
# RTABMAP_SETUP_DOCUMENTATION()
|
||||
#
|
||||
# Prepares everything the API documentation build needs, and writes the Doxyfile:
|
||||
#
|
||||
# - fetches Doxygen Awesome, the theme of the generated HTML
|
||||
# - regenerates the Doxygen HTML header and injects our scripts in it
|
||||
# - generates the "Parameter reference" page from Parameters.h
|
||||
# - configures Doxyfile.in into ${CMAKE_BINARY_DIR}/Doxyfile
|
||||
#
|
||||
# Call it only when the documentation is actually wanted (see BUILD_DOCUMENTATION
|
||||
# in the top-level CMakeLists.txt): it needs the network, Doxygen and Python3.
|
||||
# Run the result from the source tree, whose layout the Doxyfile paths assume:
|
||||
#
|
||||
# doxygen <build dir>/Doxyfile
|
||||
#
|
||||
# The convenience script docs-report.sh does all of that, and lays the output out
|
||||
# the way the published site expects.
|
||||
|
||||
MACRO(RTABMAP_SETUP_DOCUMENTATION)
|
||||
# Doxygen Awesome, the theme of the generated HTML. Downloaded here instead of
|
||||
# being vendored, so bumping it is a one-line change; the archive is pinned by
|
||||
# hash. It requires GENERATE_TREEVIEW=YES and HTML_COLORSTYLE=LIGHT (see
|
||||
# Doxyfile.in): the theme brings its own dark mode.
|
||||
INCLUDE(FetchContent)
|
||||
FetchContent_Declare(
|
||||
doxygen-awesome-css
|
||||
URL https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v2.3.4.zip
|
||||
URL_HASH SHA256=cb684b29f6be9e63300de56f26c13a04c298af8937a15ce87390fb5a601022a3
|
||||
)
|
||||
FetchContent_MakeAvailable(doxygen-awesome-css)
|
||||
FetchContent_GetProperties(doxygen-awesome-css SOURCE_DIR AWESOME_CSS_DIR)
|
||||
|
||||
# Layout: the base theme keeps Doxygen's top tab bar. The sidebar-only variant
|
||||
# drops it and puts everything in the left navigation, which needs the wider
|
||||
# tree the theme expects (--side-nav-fixed-width).
|
||||
OPTION(BUILD_DOCUMENTATION_SIDEBAR "Use the sidebar-only layout of the Doxygen theme" OFF)
|
||||
SET(RTABMAP_DOXYGEN_STYLESHEETS "\"${AWESOME_CSS_DIR}/doxygen-awesome.css\"")
|
||||
SET(RTABMAP_DOXYGEN_TREEVIEW_WIDTH 250)
|
||||
IF(BUILD_DOCUMENTATION_SIDEBAR)
|
||||
SET(RTABMAP_DOXYGEN_TREEVIEW_WIDTH 335)
|
||||
STRING(APPEND RTABMAP_DOXYGEN_STYLESHEETS
|
||||
" \\\n \"${AWESOME_CSS_DIR}/doxygen-awesome-sidebar-only.css\""
|
||||
" \\\n \"${AWESOME_CSS_DIR}/doxygen-awesome-sidebar-only-darkmode-toggle.css\"")
|
||||
ENDIF()
|
||||
# Ours, last so it can override the theme's variables.
|
||||
STRING(APPEND RTABMAP_DOXYGEN_STYLESHEETS
|
||||
" \\\n \"${PROJECT_SOURCE_DIR}/doxygen/custom.css\"")
|
||||
|
||||
SET(RTABMAP_DOXYGEN_AWESOME_TOGGLE ${AWESOME_CSS_DIR}/doxygen-awesome-darkmode-toggle.js)
|
||||
# Loaded from the header below; init() runs in <head> so that a reader who
|
||||
# picked dark mode never sees a flash of the light theme.
|
||||
SET(RTABMAP_DOXYGEN_AWESOME_SCRIPTS
|
||||
"<script type=\"text/javascript\" src=\"$relpath^doxygen-awesome-darkmode-toggle.js\"></script>\n<script type=\"text/javascript\">DoxygenAwesomeDarkModeToggle.init();</script>\n")
|
||||
|
||||
# Doxygen HTML header carrying the version-switcher and Doxygen Awesome scripts.
|
||||
# The template is regenerated with `doxygen -w` so it always matches the locally
|
||||
# installed Doxygen (a committed header goes stale and warns on every run); we
|
||||
# only insert <script> tags before </head>. $relpath^ is Doxygen's path from the
|
||||
# current page back to this build's root, which is how the switcher locates
|
||||
# versions.js one level above (the API root) without knowing the site prefix.
|
||||
# The dark-mode toggle must be initialized before the page is rendered, so that
|
||||
# a reader who picked dark does not get a flash of the light theme.
|
||||
FIND_PROGRAM(RTABMAP_DOXYGEN_EXECUTABLE doxygen)
|
||||
SET(RTABMAP_DOXYGEN_HEADER "")
|
||||
IF(RTABMAP_DOXYGEN_EXECUTABLE)
|
||||
SET(RTABMAP_DOXYGEN_HEADER ${PROJECT_BINARY_DIR}/doxygen-header.html)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${RTABMAP_DOXYGEN_EXECUTABLE} -w html
|
||||
${RTABMAP_DOXYGEN_HEADER}
|
||||
${PROJECT_BINARY_DIR}/doxygen-footer.html
|
||||
${PROJECT_BINARY_DIR}/doxygen-stylesheet.css
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
OUTPUT_QUIET ERROR_QUIET)
|
||||
IF(EXISTS ${RTABMAP_DOXYGEN_HEADER})
|
||||
FILE(READ ${RTABMAP_DOXYGEN_HEADER} RTABMAP_DOXYGEN_HEADER_CONTENT)
|
||||
STRING(REPLACE "</head>"
|
||||
"<script type=\"text/javascript\">window.RTABMAP_DOC_ROOT=\"$relpath^\";</script>\n<script type=\"text/javascript\" src=\"$relpath^../versions.js\"></script>\n<script type=\"text/javascript\" src=\"$relpath^version-switcher.js\"></script>\n${RTABMAP_DOXYGEN_AWESOME_SCRIPTS}</head>"
|
||||
RTABMAP_DOXYGEN_HEADER_CONTENT "${RTABMAP_DOXYGEN_HEADER_CONTENT}")
|
||||
FILE(WRITE ${RTABMAP_DOXYGEN_HEADER} "${RTABMAP_DOXYGEN_HEADER_CONTENT}")
|
||||
ELSE()
|
||||
SET(RTABMAP_DOXYGEN_HEADER "")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
# "Parameter reference" page: the parameters are declared through the
|
||||
# RTABMAP_PARAM* macros, so Doxygen alone can only show 1900 bare accessors.
|
||||
# The generator reads the declarations and emits one table per group, with the
|
||||
# key, type, default and description together. Optional: without Python the
|
||||
# page is simply left out of INPUT.
|
||||
FIND_PACKAGE(Python3 COMPONENTS Interpreter QUIET)
|
||||
SET(RTABMAP_DOXYGEN_PARAMETERS_PAGE "")
|
||||
IF(Python3_Interpreter_FOUND)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${Python3_EXECUTABLE}
|
||||
${PROJECT_SOURCE_DIR}/doxygen/generate_parameters_page.py
|
||||
--input ${PROJECT_SOURCE_DIR}/corelib/include/${PROJECT_PREFIX}/core/Parameters.h
|
||||
--output ${PROJECT_BINARY_DIR}/doxygen/parameters.md
|
||||
RESULT_VARIABLE RTABMAP_DOXYGEN_PARAMETERS_RESULT
|
||||
OUTPUT_QUIET ERROR_QUIET)
|
||||
IF(RTABMAP_DOXYGEN_PARAMETERS_RESULT EQUAL 0)
|
||||
SET(RTABMAP_DOXYGEN_PARAMETERS_PAGE ${PROJECT_BINARY_DIR}/doxygen/parameters.md)
|
||||
ELSE()
|
||||
MESSAGE(WARNING "Could not generate the Doxygen parameter reference page.")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
# Doxyfile with PROJECT_NUMBER filled from RTABMAP_VERSION, so the generated
|
||||
# documentation can never drift from the actual version. Its INPUT/OUTPUT paths
|
||||
# are relative to the source tree, so run it from there:
|
||||
# doxygen <build dir>/Doxyfile
|
||||
CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/Doxyfile.in ${PROJECT_BINARY_DIR}/Doxyfile @ONLY)
|
||||
ENDMACRO(RTABMAP_SETUP_DOCUMENTATION)
|
||||
33
codecov.yml
33
codecov.yml
@@ -1,33 +0,0 @@
|
||||
# Codecov configuration -- https://docs.codecov.com/docs/codecov-yaml
|
||||
#
|
||||
# Coverage data is produced by .github/workflows/coverage.yml (lcov over a Debug
|
||||
# build with ENABLE_COVERAGE=ON) and uploaded by codecov/codecov-action; this
|
||||
# file only controls what Codecov reports back on a pull request. Nothing is
|
||||
# posted unless the Codecov GitHub App has access to the repository.
|
||||
|
||||
# Mark uncovered added lines inline in the "Files changed" tab.
|
||||
github_checks:
|
||||
annotations: true
|
||||
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: "10...90" # red/green scale: 10% is fully red, 90% fully green
|
||||
|
||||
status:
|
||||
# Catch a slow slide down without pinning an absolute number.
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1%
|
||||
|
||||
# Coverage of the lines this pull request touches. Advisory: reported, but
|
||||
# does not block the merge -- drop "informational" to make it gate.
|
||||
patch:
|
||||
default:
|
||||
informational: true
|
||||
|
||||
comment:
|
||||
layout: "condensed_header, diff, files"
|
||||
behavior: default
|
||||
require_changes: true # stay quiet when coverage doesn't move
|
||||
@@ -1,5 +1 @@
|
||||
ADD_SUBDIRECTORY( src )
|
||||
|
||||
if(BUILD_TESTING)
|
||||
ADD_SUBDIRECTORY( test )
|
||||
endif()
|
||||
@@ -32,10 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "rtabmap/utilite/UEventsHandler.h"
|
||||
#include "rtabmap/core/Parameters.h"
|
||||
|
||||
@@ -44,185 +41,45 @@ namespace rtabmap {
|
||||
class Memory;
|
||||
class Signature;
|
||||
|
||||
namespace bayes {
|
||||
class PredictionModel;
|
||||
class DensePrediction;
|
||||
class SparsePrediction;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class BayesFilter
|
||||
* @brief Recursive Bayesian filter for loop-closure hypothesis estimation in RTAB-Map.
|
||||
*
|
||||
* This class implements the prediction and update steps of a Bayes filter used to estimate
|
||||
* the posterior probability over candidate locations (signatures) in working memory. It is
|
||||
* typically called by Rtabmap after likelihood values have been computed from visual
|
||||
* word comparisons.
|
||||
*
|
||||
* The filter operates in two steps on each iteration:
|
||||
* - **Prediction**: builds a transition matrix from the memory graph and multiplies it
|
||||
* with the previous posterior to obtain the prior.
|
||||
* - **Update**: multiplies the prior by the observation likelihood and normalizes the result.
|
||||
*
|
||||
* The prediction matrix is built from neighbor relationships in @ref Memory, using a
|
||||
* Gaussian-like model configured through @ref Parameters::kBayesPredictionLC(). A virtual
|
||||
* place (negative signature id, see @ref Memory::kIdVirtual) represents the hypothesis
|
||||
* that the current observation comes from a new location.
|
||||
*
|
||||
* Related parameters (see @ref Parameters):
|
||||
* - @ref Parameters::kBayesPredictionLC() — transition probabilities per graph depth level.
|
||||
* - @ref Parameters::kBayesVirtualPlacePriorThr() — prior for the virtual place.
|
||||
* - @ref Parameters::kBayesFullPredictionUpdate() — regenerate the full prediction matrix each iteration.
|
||||
* - @ref Parameters::kBayesSparsePrediction() — keep the prediction sparse and multiply it sparsely.
|
||||
*
|
||||
* @see Memory::getNeighborsId()
|
||||
* @see Rtabmap
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT BayesFilter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a Bayes filter with default or custom parameters.
|
||||
* @param parameters Optional parameter map (Bayes group keys). Defaults are used for missing keys.
|
||||
*/
|
||||
BayesFilter(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~BayesFilter();
|
||||
|
||||
/**
|
||||
* @brief Updates internal settings from the parameter map.
|
||||
* @param parameters Map containing Bayes group keys.
|
||||
*/
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/**
|
||||
* @brief Runs one Bayes filter iteration (prediction + update).
|
||||
*
|
||||
* Given a likelihood map over signature ids, computes and stores the normalized posterior.
|
||||
* The prediction matrix is generated or updated from @ref Memory using the ids present
|
||||
* in @p likelihood.
|
||||
*
|
||||
* Read the result with @ref getPosteriorIds() and @ref getPosteriorValues().
|
||||
*
|
||||
* @param memory Working memory instance (must not be null).
|
||||
* @param likelihood Observation likelihood per signature id (must not be empty).
|
||||
* @return False on error (null memory, empty likelihood, or invalid prediction model),
|
||||
* the posterior being left unchanged.
|
||||
*/
|
||||
bool computePosterior(const Memory * memory, const std::map<int, float> & likelihood);
|
||||
|
||||
/**
|
||||
* @brief Clears posterior, prediction matrix and cached neighbor indices.
|
||||
*/
|
||||
const std::map<int, float> & computePosterior(const Memory * memory, const std::map<int, float> & likelihood);
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* @brief Sets the loop-closure prediction model from a space-separated string.
|
||||
*
|
||||
* Format: `{Vp, Lc, l1, l2, l3, ...}` where:
|
||||
* - **Vp** — virtual place probability. This is the probability to move to a new place (unvisited location).
|
||||
* - **Lc** — loop closure (depth 0) probability. This is the probability to stay at the same location.
|
||||
* - **l1, l2, ...** — probabilities for neighbors at increasing graph depth levels. This is the probability to move to a neighbor at the given depth level.
|
||||
*
|
||||
* Each value must be in [0, 1]. At least two values are required. Invalid strings are rejected
|
||||
* and the previous model is kept.
|
||||
*
|
||||
* @param prediction Space-separated list of probabilities (same format as @ref Parameters::kBayesPredictionLC()).
|
||||
*/
|
||||
//setters
|
||||
void setPredictionLC(const std::string & prediction);
|
||||
|
||||
/**
|
||||
* @brief The locations the posterior is over, ascending by id.
|
||||
*
|
||||
* The virtual place (@ref Memory::kIdVirtual) is the first of them when it is one.
|
||||
*/
|
||||
const std::vector<int> & getPosteriorIds() const {return _posteriorIds;}
|
||||
//getters
|
||||
const std::map<int, float> & getPosterior() const {return _posterior;}
|
||||
float getVirtualPlacePrior() const {return _virtualPlacePrior;}
|
||||
const std::vector<double> & getPredictionLC() const; // {Vp, Lc, l1, l2, l3, l4...}
|
||||
std::string getPredictionLCStr() const; // for convenience {Vp, Lc, l1, l2, l3, l4...}
|
||||
|
||||
/**
|
||||
* @brief The probability of each location of @ref getPosteriorIds(), in the same order.
|
||||
*/
|
||||
const std::vector<float> & getPosteriorValues() const {return _posteriorValues;}
|
||||
|
||||
/**
|
||||
* @brief Returns the virtual place prior threshold.
|
||||
* @return Value in [0, 1] used when building the virtual place row of the prediction matrix.
|
||||
*/
|
||||
float getVirtualPlacePrior() const;
|
||||
|
||||
/**
|
||||
* @brief Whether the prediction is being kept in its sparse form rather than as a matrix.
|
||||
*
|
||||
* False when @ref Parameters::kBayesSparsePrediction() is disabled, and over a model whose
|
||||
* values sum to less than 1, which leaves no zero in a column to keep out of the values.
|
||||
*/
|
||||
bool isPredictionSparse() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the loop-closure prediction model as a vector of values.
|
||||
* @return Vector in the format `{Vp, Lc, l1, l2, l3, ...}`.
|
||||
*/
|
||||
const std::vector<double> & getPredictionLC() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the loop-closure prediction model as a space-separated string.
|
||||
* @return String representation of @ref getPredictionLC().
|
||||
*/
|
||||
std::string getPredictionLCStr() const;
|
||||
|
||||
/**
|
||||
* @brief Builds or updates the prediction (transition) matrix for the given signature ids.
|
||||
*
|
||||
* Rows and columns correspond to @p ids. Neighbor links are queried from @ref Memory to fill
|
||||
* transition probabilities according to @ref getPredictionLC(). When @p ids match the
|
||||
* current posterior keys, the cached matrix may be returned without recomputation.
|
||||
*
|
||||
* When the prediction is being kept sparse, the matrix is expanded from it rather than
|
||||
* kept: it costs the memory that keeping the prediction sparse is saving, so ask for it to
|
||||
* read, dump or compare the prediction, not on every iteration.
|
||||
*
|
||||
* @param memory Working memory instance (must not be null).
|
||||
* @param ids Ordered list of signature ids (often includes @ref Memory::kIdVirtual as first element).
|
||||
* @return Square CV_32FC1 matrix of size ids.size() × ids.size().
|
||||
*/
|
||||
cv::Mat generatePrediction(const Memory * memory, const std::vector<int> & ids);
|
||||
|
||||
/**
|
||||
* @brief Estimates memory usage of this object and its internal containers.
|
||||
* @return Approximate memory footprint in bytes.
|
||||
*/
|
||||
unsigned long getMemoryUsed() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Realigns the posterior with the ids of the likelihood.
|
||||
*
|
||||
* Keeps the probability of the locations that are in both. Called only when the ids differ.
|
||||
*/
|
||||
void updatePosterior(const Memory * memory, const std::map<int, float> & likelihood);
|
||||
|
||||
/**
|
||||
* @brief Settles whether the prediction is kept sparse, from the parameter and the model.
|
||||
*
|
||||
* Called when either of the two changes rather than on every iteration, and releases the
|
||||
* sparse form when the answer is no.
|
||||
*/
|
||||
void updateKeepSparse();
|
||||
cv::Mat updatePrediction(const cv::Mat & oldPrediction,
|
||||
const Memory * memory,
|
||||
const std::vector<int> & oldIds,
|
||||
const std::vector<int> & newIds);
|
||||
void updatePosterior(const Memory * memory, const std::vector<int> & likelihoodIds);
|
||||
void normalize(cv::Mat & prediction, unsigned int index, float addedProbabilitiesSum, bool virtualPlaceUsed) const;
|
||||
|
||||
private:
|
||||
std::vector<int> _posteriorIds; ///< The locations the posterior is over, ascending by id.
|
||||
std::vector<float> _posteriorValues; ///< The probability of each of them, in the same order.
|
||||
std::vector<int> _likelihoodIds; ///< The ids of the likelihood of an iteration, in its order.
|
||||
std::vector<float> _likelihoodValues; ///< The likelihood of an iteration, in the same order.
|
||||
std::vector<float> _priorValues; ///< The prior of an iteration, in the same order.
|
||||
|
||||
bayes::PredictionModel * _model; ///< The `{Vp, Lc, l1, ...}` model and the column arithmetic of it.
|
||||
bayes::DensePrediction * _dense; ///< The prediction as a matrix, used when it is not kept sparse.
|
||||
bayes::SparsePrediction * _sparse; ///< The prediction as its values only, one column at a time.
|
||||
std::map<int, std::map<int, int> > _neighborsIndex; ///< Cached neighbor margins per signature id, for the incremental updates.
|
||||
|
||||
bool _fullPredictionUpdate; ///< If true, rebuild the whole prediction each time.
|
||||
bool _sparsePrediction; ///< Keep the prediction sparse (Bayes/SparsePrediction).
|
||||
bool _keepSparse; ///< Whether it is being kept sparse: the parameter, over a model that leaves nothing sparse to keep.
|
||||
bool _predictionChanged; ///< True when the prediction has to be built again.
|
||||
std::map<int, float> _posterior;
|
||||
cv::Mat _prediction;
|
||||
float _virtualPlacePrior;
|
||||
std::vector<double> _predictionLC; // {Vp, Lc, l1, l2, l3, l4...}
|
||||
bool _fullPredictionUpdate;
|
||||
float _totalPredictionLCValues;
|
||||
float _predictionEpsilon;
|
||||
std::map<int, std::map<int, int> > _neighborsIndex;
|
||||
};
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
@@ -35,53 +35,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class CameraModel
|
||||
* @brief Represents a pinhole camera model containing intrinsic and extrinsic
|
||||
* parameters, used for projection, rectification, and transformation.
|
||||
*
|
||||
* This class encapsulates camera calibration data, including intrinsic parameters (fx, fy, cx, cy),
|
||||
* distortion coefficients, rectification and projection matrices. It provides utility functions
|
||||
* for image rectification, projection from 2D to 3D, and vice versa.
|
||||
*
|
||||
* This class supports the 4 to 14 parameters Radial Tangential distortion model (also called Plumb Bob or
|
||||
* Brown-Conrady model) and 4 parameters Fish Eye model (also known as Equidistant model).
|
||||
*
|
||||
* @see OpenCV's calib3d module for all supported camera models.
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT CameraModel
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the default optical rotation to convert image coordinates to robot coordinates.
|
||||
*
|
||||
* Image frame: x -> right, y -> down, z -> forward
|
||||
* Robot frame: x -> forward, y -> left, z -> up
|
||||
*
|
||||
* @return Transform rotation matrix.
|
||||
* Optical rotation used to transform image coordinate frame (x->right, y->down, z->forward)
|
||||
* to robot coordinate frame (x->forward, y->left, z->up).
|
||||
*/
|
||||
static Transform opticalRotation() {return Transform(0,0,1,0, -1,0,0,0, 0,-1,0,0);}
|
||||
|
||||
public:
|
||||
|
||||
/// Default constructor.
|
||||
CameraModel();
|
||||
|
||||
/**
|
||||
* @brief Constructor using full camera parameters.
|
||||
*
|
||||
* @param name Camera name or ID.
|
||||
* @param imageSize Size of the image (width x height).
|
||||
* @param K Intrinsic matrix (3x3).
|
||||
* @param D Distortion coefficients, 1xN matrix where N is between 4 and 14 parameters:
|
||||
* k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,tx,ty]]]]).
|
||||
* To set Fish Eye / Equidistant model, it is implicitly used if we
|
||||
* provide 6 values like this: [k1,k2,0,0,k3,k4], where you only need to
|
||||
* fill "k" parameters.
|
||||
* @param R Rectification matrix (3x3).
|
||||
* @param P Projection matrix (3x4).
|
||||
* @param localTransform Local transform to apply to the camera frame.
|
||||
*/
|
||||
// K is the camera intrinsic 3x3 CV_64FC1
|
||||
// D is the distortion coefficients 1x5 CV_64FC1
|
||||
// R is the rectification matrix 3x3 CV_64FC1 (computed from stereo or Identity)
|
||||
// P is the projection matrix 3x4 CV_64FC1 (computed from stereo or equal to [K [0 0 1]'])
|
||||
CameraModel(
|
||||
const std::string & name,
|
||||
const cv::Size & imageSize,
|
||||
@@ -91,17 +59,7 @@ public:
|
||||
const cv::Mat & P,
|
||||
const Transform & localTransform = opticalRotation());
|
||||
|
||||
/**
|
||||
* @brief Minimal constructor using intrinsic parameters. This assumes the images are already rectified.
|
||||
*
|
||||
* @param fx Focal length x.
|
||||
* @param fy Focal length y.
|
||||
* @param cx Principal point x.
|
||||
* @param cy Principal point y.
|
||||
* @param localTransform Local transform to apply to the camera frame.
|
||||
* @param Tx Baseline * fx (optional). Mainly used in case of stereo pair.
|
||||
* @param imageSize Image size (optional).
|
||||
*/
|
||||
// minimal
|
||||
CameraModel(
|
||||
double fx,
|
||||
double fy,
|
||||
@@ -110,10 +68,7 @@ public:
|
||||
const Transform & localTransform = opticalRotation(),
|
||||
double Tx = 0.0f,
|
||||
const cv::Size & imageSize = cv::Size(0,0));
|
||||
|
||||
/**
|
||||
* @brief Minimal constructor with name for saving.
|
||||
*/
|
||||
// minimal to be saved
|
||||
CameraModel(
|
||||
const std::string & name,
|
||||
double fx,
|
||||
@@ -124,51 +79,13 @@ public:
|
||||
double Tx = 0.0f,
|
||||
const cv::Size & imageSize = cv::Size(0,0));
|
||||
|
||||
/// Destructor.
|
||||
virtual ~CameraModel() {}
|
||||
|
||||
/**
|
||||
* @brief Initializes the rectification maps used to undistort and rectify images.
|
||||
*
|
||||
* This function prepares the `mapX_` and `mapY_` lookup tables used for image rectification.
|
||||
* It supports both standard radial-tangential distortion and fisheye/equidistant distortion models.
|
||||
*
|
||||
* - If the distortion model is **fisheye** (indicated by `D_.cols == 6`), it uses
|
||||
* `cv::fisheye::initUndistortRectifyMap()` to create the rectification maps. This requires OpenCV ≥ 2.4.10.
|
||||
* - Otherwise, it uses the standard `cv::initUndistortRectifyMap()` for plumb bob or rational polynomial models.
|
||||
*
|
||||
* @pre The camera model must be valid for rectification:
|
||||
* - `imageSize_` must be non-zero.
|
||||
* - `D_` must be a 1-row matrix with an accepted number of columns (4, 5, 6, 8, 12, or 14).
|
||||
* - `R_` must be a 3x3 rectification matrix.
|
||||
* - `P_` must be a 3x4 projection matrix.
|
||||
*
|
||||
* @return `true` if the rectification maps were successfully initialized (`mapX_` and `mapY_` are not empty),
|
||||
* `false` otherwise.
|
||||
*
|
||||
* @see isRectificationMapInitialized(), rectifyImage(), rectifyDepth()
|
||||
*
|
||||
* @warning Requires OpenCV 2.4.10 or newer for fisheye support. If the version is older, fisheye rectification will not work.
|
||||
*/
|
||||
bool initRectificationMap();
|
||||
|
||||
/**
|
||||
* @brief Checks if the rectification map has been initialized.
|
||||
* @return True if both mapX_ and mapY_ are initialized.
|
||||
*/
|
||||
bool isRectificationMapInitialized() const {return !mapX_.empty() && !mapY_.empty();}
|
||||
|
||||
/**
|
||||
* @brief Checks if the model is valid for 2D->3D projection.
|
||||
*/
|
||||
bool isValidForProjection() const {return fx()>0.0 && fy()>0.0 && cx()>0.0 && cy()>0.0;}
|
||||
/**
|
||||
* @brief Checks if the model is valid for 3D->2D reprojection.
|
||||
*/
|
||||
bool isValidForReprojection() const {return fx()>0.0 && fy()>0.0 && cx()>0.0 && cy()>0.0 && imageWidth()>0 && imageHeight()>0;}
|
||||
/**
|
||||
* @brief Checks if the model has sufficient data for image rectification.
|
||||
*/
|
||||
bool isValidForRectification() const
|
||||
{
|
||||
return imageSize_.width>0 &&
|
||||
@@ -179,414 +96,71 @@ public:
|
||||
!P_.empty();
|
||||
}
|
||||
|
||||
/// Sets the camera name, used to set a camera name when saving to a file.
|
||||
void setName(const std::string & name) {name_=name;}
|
||||
/// Returns the camera name.
|
||||
const std::string & name() const {return name_;}
|
||||
|
||||
/// Returns focal length in x.
|
||||
double fx() const {return P_.empty()?K_.empty()?0.0:K_.at<double>(0,0):P_.at<double>(0,0);}
|
||||
/// Returns focal length in y.
|
||||
double fy() const {return P_.empty()?K_.empty()?0.0:K_.at<double>(1,1):P_.at<double>(1,1);}
|
||||
/// Returns principal point x.
|
||||
double cx() const {return P_.empty()?K_.empty()?0.0:K_.at<double>(0,2):P_.at<double>(0,2);}
|
||||
/// Returns principal point y.
|
||||
double cy() const {return P_.empty()?K_.empty()?0.0:K_.at<double>(1,2):P_.at<double>(1,2);}
|
||||
/// Returns the x translation (usually fx * baseline in case of stereo, otherwise would be 0).
|
||||
double Tx() const {return P_.empty()?0.0:P_.at<double>(0,3);}
|
||||
|
||||
/// Returns the raw intrinsic matrix (before rectification).
|
||||
cv::Mat K_raw() const {return K_;}
|
||||
/// Returns the raw distortion coefficients (before rectification).
|
||||
cv::Mat D_raw() const {return D_;}
|
||||
/// Returns the rectified camera intrinsic matrix if the projection matrix P exists, otherwise returns the raw intrinsic matrix.
|
||||
cv::Mat K() const {return !P_.empty()?P_.colRange(0,3):K_;}
|
||||
/// Returns the rectified distortion coefficients (1x5 filled with zeros) if the projection matrix P exists, otherwise returns the raw distortion coefficients.
|
||||
cv::Mat K_raw() const {return K_;} //intrinsic camera matrix (before rectification)
|
||||
cv::Mat D_raw() const {return D_;} //intrinsic distorsion matrix (before rectification)
|
||||
cv::Mat K() const {return !P_.empty()?P_.colRange(0,3):K_;} // if P exists, return rectified version
|
||||
cv::Mat D() const {return P_.empty()&&!D_.empty()?D_:cv::Mat::zeros(1,5,CV_64FC1);} // if P exists, return rectified version
|
||||
/// Returns the rectification matrix.
|
||||
cv::Mat R() const {return R_;}
|
||||
/// Returns the projection matrix.
|
||||
cv::Mat P() const {return P_;}
|
||||
cv::Mat R() const {return R_;} //rectification matrix
|
||||
cv::Mat P() const {return P_;} //projection matrix
|
||||
|
||||
/// Sets the local transform of the camera (base frame to optical frame).
|
||||
void setLocalTransform(const Transform & transform) {localTransform_ = transform;}
|
||||
/// Returns the local transform (base frame to optical frame).
|
||||
const Transform & localTransform() const {return localTransform_;}
|
||||
|
||||
/**
|
||||
* @brief Sets the image size of the camera model and updates the principal point if undefined.
|
||||
*
|
||||
* This function updates the internal image size (`imageSize_`) with the provided size.
|
||||
* If the intrinsic matrices (`K_` or `P_`) are present and the principal point coordinates
|
||||
* (`cx`, `cy`) are zero, they are set to the image center (`width/2 - 0.5`, `height/2 - 0.5`).
|
||||
*
|
||||
* This ensures the camera model remains valid and useful even when the calibration file
|
||||
* has no principal point set or the image size is updated manually.
|
||||
*
|
||||
* @param size The new image size. It must be either both dimensions zero (clearing) or both positive.
|
||||
*
|
||||
* @pre `size.width > 0 && size.height > 0` or `size.width == 0 && size.height == 0`
|
||||
* @post Updates the `imageSize_`, and adjusts `cx` and `cy` in `K_` and `P_` if they were initially zero.
|
||||
*
|
||||
* @warning If `K_` or `P_` are not initialized (`empty()`), no updates will be applied to them.
|
||||
*
|
||||
* @see imageSize(), imageWidth(), imageHeight()
|
||||
*/
|
||||
void setImageSize(const cv::Size & size);
|
||||
const cv::Size & imageSize() const {return imageSize_;}
|
||||
int imageWidth() const {return imageSize_.width;}
|
||||
int imageHeight() const {return imageSize_.height;}
|
||||
|
||||
/**
|
||||
* @brief Returns the horizontal field of view (FoV) in radians.
|
||||
*
|
||||
* The FoV is computed using the pinhole camera model as:
|
||||
* \f[
|
||||
* \text{FoV}_x = 2 \cdot \tan^{-1}\left(\frac{\text{image width}}{2 \cdot f_x}\right)
|
||||
* \f]
|
||||
*
|
||||
* @return Horizontal field of view in radians. Returns 0.0 if image width or focal length is invalid.
|
||||
*/
|
||||
double fovX() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the vertical field of view (FoV) in radians.
|
||||
*
|
||||
* The FoV is computed using the pinhole camera model as:
|
||||
* \f[
|
||||
* \text{FoV}_y = 2 \cdot \tan^{-1}\left(\frac{\text{image height}}{2 \cdot f_y}\right)
|
||||
* \f]
|
||||
*
|
||||
* @return Vertical field of view in radians. Returns 0.0 if image height or focal length is invalid.
|
||||
*/
|
||||
double fovY() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the horizontal field of view in degrees.
|
||||
*
|
||||
* Converts the result of `fovX()` from radians to degrees.
|
||||
*
|
||||
* @return Horizontal field of view in degrees. Returns 0.0 if the result is invalid.
|
||||
*/
|
||||
double horizontalFOV() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the vertical field of view in degrees.
|
||||
*
|
||||
* Converts the result of `fovY()` from radians to degrees.
|
||||
*
|
||||
* @return Vertical field of view in degrees. Returns 0.0 if the result is invalid.
|
||||
*/
|
||||
double verticalFOV() const;
|
||||
|
||||
/// Checks if the distortion model is fisheye (6 coefficients: k1,k2,0,0,k3,k4).
|
||||
double fovX() const; // in radians
|
||||
double fovY() const; // in radians
|
||||
double horizontalFOV() const; // in degrees
|
||||
double verticalFOV() const; // in degrees
|
||||
bool isFisheye() const {return D_.cols == 6;}
|
||||
|
||||
/**
|
||||
* @brief Loads the camera model parameters from a YAML calibration file.
|
||||
*
|
||||
* This method attempts to read camera intrinsic/extrinsic parameters and image size from a YAML file,
|
||||
* typically in the ROS calibration format. If the distortion model is "fisheye" or "equidistant", we expect
|
||||
* 4 coefficients, which are converted to a 6-coefficient format for internal representation.
|
||||
*
|
||||
* Fields loaded (if present):
|
||||
* - `camera_name`
|
||||
* - `image_width` (pixels)
|
||||
* - `image_height` (pixels)
|
||||
* - `camera_matrix` (K, 3x3 double matrix)
|
||||
* - `distortion_coefficients` (D, 1xN double matrix)
|
||||
* - `distortion_model` (string: name of the model)
|
||||
* - `rectification_matrix` (R, 3x3 double matrix)
|
||||
* - `projection_matrix` (P, 3x4 double matrix)
|
||||
* - `local_transform` (camera pose w.r.t robot frame)
|
||||
*
|
||||
* On success, the internal matrices and settings of the camera model are updated. If the model
|
||||
* is valid for rectification, the rectification map is initialized.
|
||||
*
|
||||
* @param filePath Absolute or relative path to the YAML file.
|
||||
* @param initRectificationMaps Set to false to skip building the (potentially large) rectification
|
||||
* maps when rectification won't be used (saves time and memory).
|
||||
* @return True if the file was successfully loaded and parsed, false otherwise.
|
||||
*
|
||||
* @warning Logs warnings if any fields are missing. If file does not exist or parsing fails, returns false.
|
||||
*
|
||||
* @see initRectificationMap()
|
||||
*/
|
||||
// Set initRectificationMaps=false to skip building the (potentially large)
|
||||
// rectification maps when rectification won't be used (saves time and memory).
|
||||
bool load(const std::string & filePath, bool initRectificationMaps = true);
|
||||
|
||||
/**
|
||||
* @brief Loads the camera model by constructing a file path from a directory and camera name.
|
||||
*
|
||||
* This is a convenience wrapper around `load(filePath)` that constructs the file path as:
|
||||
* `directory + "/" + cameraName + ".yaml"`.
|
||||
*
|
||||
* @param directory Path to the folder containing the camera YAML file.
|
||||
* @param cameraName Base name of the camera file (without extension).
|
||||
* @param initRectificationMaps Set to false to skip building the (potentially large) rectification
|
||||
* maps when rectification won't be used (saves time and memory).
|
||||
* @return True if loading from the constructed path succeeds, false otherwise.
|
||||
*/
|
||||
bool load(const std::string & directory, const std::string & cameraName, bool initRectificationMaps = true);
|
||||
|
||||
/**
|
||||
* @brief Saves the camera model parameters to a YAML calibration file in ROS format.
|
||||
*
|
||||
* The file will include the following fields if they are not empty:
|
||||
* - `camera_name`
|
||||
* - `image_width`
|
||||
* - `image_height`
|
||||
* - `camera_matrix` (K)
|
||||
* - `distortion_coefficients` (D)
|
||||
* - `distortion_model` (auto-detected based on number of distortion coefficients)
|
||||
* - `rectification_matrix` (R)
|
||||
* - `projection_matrix` (P)
|
||||
* - `local_transform` (camera pose w.r.t robot frame)
|
||||
*
|
||||
* If the distortion matrix contains 6 coefficients (used for fisheye), it is converted
|
||||
* to a standard 4-coefficient format for ROS compatibility.
|
||||
*
|
||||
* @param directory Path to the folder where the YAML file will be saved.
|
||||
* @return True if saving was successful, false otherwise.
|
||||
*
|
||||
* @note If `name_` is empty, "camera.yaml" is used as the default filename.
|
||||
* @warning Returns false and logs an error if none of the matrices are set.
|
||||
*/
|
||||
bool save(const std::string & directory) const;
|
||||
|
||||
/**
|
||||
* @brief Serializes the camera model to a binary format.
|
||||
*
|
||||
* The serialization includes the camera intrinsics (`K_`, `D_`), rectification matrix (`R_`),
|
||||
* projection matrix (`P_`), image size, and the local transform. The format is compact and suitable
|
||||
* for file storage or transmission over a network.
|
||||
*
|
||||
* Data layout:
|
||||
* - Header (11 integers):
|
||||
* - [0-2] RTAB-Map version (major, minor, patch)
|
||||
* - [3] Camera type (0 = mono, 1=stereo)
|
||||
* - [4-5] Image width, height
|
||||
* - [6-9] Element counts for K, D, R, P matrices
|
||||
* - [10] Size of localTransform (0 if null)
|
||||
* - Data section (in order): raw memory blocks for K, D, R, P (`double` values), followed by `float` values for localTransform
|
||||
*
|
||||
* @return A byte vector containing the serialized data. The format is compatible with `deserialize()`.
|
||||
*
|
||||
* @note This is a custom binary format, not meant to be human-readable.
|
||||
* @see deserialize(), StereoCameraModel
|
||||
*/
|
||||
std::vector<unsigned char> serialize() const;
|
||||
/**
|
||||
* @brief Deserializes a camera model from a byte vector.
|
||||
*
|
||||
* This is a convenience wrapper around `deserialize(const unsigned char*, unsigned int)`
|
||||
* that takes a `std::vector<unsigned char>` instead of a raw buffer.
|
||||
*
|
||||
* @param data Byte vector containing data serialized by `serialize()`.
|
||||
* @return The number of bytes successfully read and parsed. Returns 0 on failure.
|
||||
*
|
||||
* @see serialize(), deserialize(const unsigned char*, unsigned int)
|
||||
*/
|
||||
unsigned int deserialize(const std::vector<unsigned char>& data);
|
||||
|
||||
/**
|
||||
* @brief Deserializes a camera model from a raw byte buffer.
|
||||
*
|
||||
* Reads the camera intrinsics, distortion, rectification, projection matrices, image size,
|
||||
* and local transform from a serialized binary format previously created with `serialize()`.
|
||||
*
|
||||
* @param data Pointer to the binary data buffer.
|
||||
* @param dataSize Size of the data buffer in bytes.
|
||||
* @return The number of bytes successfully read. Returns 0 on error or if the format is invalid.
|
||||
*
|
||||
* @warning If the buffer format does not match the expected layout or version, an error is logged
|
||||
* and the camera model remains in a default-initialized state.
|
||||
*
|
||||
* @note Assumes little-endian architecture and strict size/type matching. The serialized format
|
||||
* must be created by `CameraModel::serialize()`. Non-mono camera types are not supported.
|
||||
* See `StereoCameraModel` to serialize/deserialize stereo models.
|
||||
*
|
||||
* @see serialize()
|
||||
*/
|
||||
unsigned int deserialize(const unsigned char * data, unsigned int dataSize);
|
||||
|
||||
/**
|
||||
* @brief Returns a new camera model with all intrinsic parameters scaled by a given factor.
|
||||
*
|
||||
* This method scales the camera's intrinsic matrix (`K_`) and projection matrix (`P_`), as well as
|
||||
* the image size, by the given `scale` factor. The distortion and rectification matrices are left unchanged.
|
||||
*
|
||||
* Only valid camera models (i.e., those for which `isValidForProjection()` returns true) are scaled.
|
||||
* If the model is invalid, a warning is issued and the original model is returned unchanged.
|
||||
*
|
||||
* @param scale Scaling factor (> 0). For example, use 0.5 to downscale or 2.0 to upscale.
|
||||
* @return A scaled copy of the camera model with updated intrinsics and image size.
|
||||
*
|
||||
* @warning If the camera model is not valid for projection, the scale operation is ignored.
|
||||
*/
|
||||
CameraModel scaled(double scale) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a new camera model adjusted for a given region of interest (ROI).
|
||||
*
|
||||
* This method shifts the principal point (`cx`, `cy`) in the intrinsic matrix (`K_`) and projection matrix (`P_`)
|
||||
* by subtracting the ROI’s top-left `(x, y)` offset. The image size is also set to the ROI size.
|
||||
*
|
||||
* Only valid camera models (i.e., those for which `isValidForProjection()` returns true) can be adjusted.
|
||||
* If the model is invalid, a warning is issued and the original model is returned unchanged.
|
||||
*
|
||||
* @param roi Region of interest defined as a rectangle (typically a subwindow of the full image).
|
||||
* @return A new camera model adapted to the ROI with adjusted intrinsics and image size.
|
||||
*
|
||||
* @warning If the camera model is not valid for projection, the ROI operation is ignored.
|
||||
*/
|
||||
CameraModel roi(const cv::Rect & roi) const;
|
||||
|
||||
/**
|
||||
* @brief Rectifies a raw image using the precomputed rectification maps.
|
||||
*
|
||||
* This function applies geometric correction (rectification) to an image using the camera model's
|
||||
* `mapX_` and `mapY_` rectification maps. It is typically used to correct lens distortion in images
|
||||
* based on the calibration parameters.
|
||||
*
|
||||
* @param raw Input raw image (e.g., from camera). Must be a valid `cv::Mat`.
|
||||
* @param interpolation Interpolation method to use. Typically `cv::INTER_LINEAR` or `cv::INTER_NEAREST`.
|
||||
*
|
||||
* @return Rectified image. If the rectification maps are not initialized, the function logs an error
|
||||
* and returns a clone of the original image.
|
||||
*
|
||||
* @pre `mapX_` and `mapY_` must be initialized using `initRectificationMap()`.
|
||||
*
|
||||
* @note Works for color and grayscale images of any valid type.
|
||||
*
|
||||
* @see initRectificationMap(), rectifyDepth()
|
||||
*/
|
||||
// For depth images, your should use cv::INTER_NEAREST
|
||||
cv::Mat rectifyImage(const cv::Mat & raw, int interpolation = cv::INTER_LINEAR) const;
|
||||
|
||||
/**
|
||||
* @brief Rectifies a raw depth image using the precomputed rectification maps.
|
||||
*
|
||||
* This function applies geometric correction (rectification) to a 16-bit unsigned depth image.
|
||||
* It performs a pixel-by-pixel bilinear interpolation, only if all neighboring pixels have valid
|
||||
* (non-zero) depth values, and the variation among them is within 1% of their average.
|
||||
*
|
||||
* The method is optimized to avoid introducing noise in regions of high depth variance.
|
||||
*
|
||||
* @param raw Input raw depth image (`CV_16UC1`). Must contain 16-bit unsigned depth values.
|
||||
*
|
||||
* @return Rectified depth image. If the rectification maps are not initialized or the input
|
||||
* image is not of type `CV_16UC1`, the function logs an error and returns a clone of the input.
|
||||
*
|
||||
* @pre Input image must be of type `CV_16UC1`. `mapX_` and `mapY_` must be initialized.
|
||||
*
|
||||
* @note Inspired by the Kinect2 CPU depth registration implementation from:
|
||||
* https://github.com/code-iai/iai_kinect2
|
||||
*
|
||||
* @warning Invalid or noisy regions are skipped in interpolation to maintain depth consistency.
|
||||
*
|
||||
* @see initRectificationMap(), rectifyImage()
|
||||
*/
|
||||
cv::Mat rectifyDepth(const cv::Mat & raw) const;
|
||||
|
||||
/**
|
||||
* @brief Projects a 2D pixel and depth value into a 3D point in the camera coordinate frame (/camera_link).
|
||||
*
|
||||
* This function uses the camera's intrinsic parameters to compute the 3D point corresponding to the given
|
||||
* 2D image coordinates and depth value.
|
||||
*
|
||||
* @param u Horizontal image coordinate (in pixels).
|
||||
* @param v Vertical image coordinate (in pixels).
|
||||
* @param depth Depth value at (u, v) in meters.
|
||||
* @param[out] x Output X coordinate in 3D space.
|
||||
* @param[out] y Output Y coordinate in 3D space.
|
||||
* @param[out] z Output Z coordinate in 3D space (equals `depth`).
|
||||
*
|
||||
* @note If `depth <= 0`, the output (x, y, z) will be set to `NaN`.
|
||||
*
|
||||
* @see reproject()
|
||||
*/
|
||||
// Project 2D pixel to 3D (in /camera_link frame)
|
||||
void project(float u, float v, float depth, float & x, float & y, float & z) const;
|
||||
|
||||
/**
|
||||
* @brief Reprojects a 3D point in the camera frame (/camera_link) into 2D image coordinates (floating-point).
|
||||
*
|
||||
* This function computes the image plane coordinates for a given 3D point using the camera's
|
||||
* intrinsic parameters.
|
||||
*
|
||||
* @param x X coordinate in camera space.
|
||||
* @param y Y coordinate in camera space.
|
||||
* @param z Z coordinate in camera space (must be non-zero).
|
||||
* @param[out] u Output horizontal image coordinate (float).
|
||||
* @param[out] v Output vertical image coordinate (float).
|
||||
*
|
||||
* @pre `z != 0`
|
||||
*
|
||||
* @see project(), reproject(int&, int&)
|
||||
*/
|
||||
// Reproject 3D point (in /camera_link frame) to pixel
|
||||
void reproject(float x, float y, float z, float & u, float & v) const;
|
||||
|
||||
/**
|
||||
* @brief Reprojects a 3D point in the camera frame (/camera_link) into 2D image coordinates (rounded to int).
|
||||
*
|
||||
* This version of `reproject()` returns integer pixel indices, computed from the 3D position.
|
||||
*
|
||||
* @param x X coordinate in camera space.
|
||||
* @param y Y coordinate in camera space.
|
||||
* @param z Z coordinate in camera space (must be non-zero).
|
||||
* @param[out] u Output horizontal image coordinate (integer pixel).
|
||||
* @param[out] v Output vertical image coordinate (integer pixel).
|
||||
*
|
||||
* @pre `z != 0`
|
||||
*
|
||||
* @see project(), reproject(float&, float&)
|
||||
*/
|
||||
void reproject(float x, float y, float z, int & u, int & v) const;
|
||||
|
||||
/**
|
||||
* @brief Checks if a given pixel coordinate lies within the image bounds.
|
||||
*
|
||||
* @param u Horizontal image coordinate (in pixels).
|
||||
* @param v Vertical image coordinate (in pixels).
|
||||
* @return `true` if the pixel is within the image dimensions, `false` otherwise.
|
||||
*
|
||||
* @note Inclusive lower bound, exclusive upper bound: `[0, width)`, `[0, height)`
|
||||
*/
|
||||
bool inFrame(int u, int v) const;
|
||||
|
||||
private:
|
||||
std::string name_; ///< Camera name.
|
||||
cv::Size imageSize_; ///< Image size.
|
||||
cv::Mat K_; ///< Intrinsic matrix.
|
||||
cv::Mat D_; ///< Distortion coefficients.
|
||||
cv::Mat R_; ///< Rectification matrix.
|
||||
cv::Mat P_; ///< Projection matrix.
|
||||
cv::Mat mapX_; ///< Rectification map X.
|
||||
cv::Mat mapY_; ///< Rectification map Y.
|
||||
Transform localTransform_; ///< Transform from camera to base link.
|
||||
std::string name_;
|
||||
cv::Size imageSize_;
|
||||
cv::Mat K_;
|
||||
cv::Mat D_;
|
||||
cv::Mat R_;
|
||||
cv::Mat P_;
|
||||
cv::Mat mapX_;
|
||||
cv::Mat mapY_;
|
||||
Transform localTransform_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Stream operator for printing a camera model to an output stream.
|
||||
*
|
||||
* This function outputs the name, image size, and camera matrices (K, D, R, P)
|
||||
* along with the local transformation.
|
||||
*
|
||||
* Example output:
|
||||
* ```
|
||||
* Name: camera1
|
||||
* Size: 640x480
|
||||
* K= [fx, 0, cx;
|
||||
* 0, fy, cy;
|
||||
* 0, 0, 1]
|
||||
* D= [...]
|
||||
* R= [...]
|
||||
* P= [...]
|
||||
* LocalTransform= [...]
|
||||
* ```
|
||||
*
|
||||
* @param os Output stream.
|
||||
* @param model Camera model to print.
|
||||
* @return The modified output stream.
|
||||
*/
|
||||
RTABMAP_CORE_EXPORT std::ostream& operator<<(std::ostream& os, const CameraModel& model);
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -37,51 +37,29 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class CompressionThread
|
||||
* @brief Background thread to compress or uncompress images and generic matrices.
|
||||
*
|
||||
* In compress mode, pass a source matrix to the constructor with an optional image
|
||||
* format (".png", ".jpg", ".rvl", or empty for zlib data). In uncompress mode, pass
|
||||
* compressed bytes and set @c isImage accordingly. Call @ref UThread::start() then
|
||||
* @ref UThread::join() to obtain the result from @ref getCompressedData() or
|
||||
* @ref getUncompressedData().
|
||||
* Compress image or data
|
||||
*
|
||||
* Example compression:
|
||||
* @code
|
||||
* cv::Mat image;
|
||||
* CompressionThread ct(image, ".png");
|
||||
* ct.start();
|
||||
* ct.join();
|
||||
* cv::Mat bytes = ct.getCompressedData();
|
||||
* @endcode
|
||||
* cv::Mat image;// an image
|
||||
* CompressionThread ct(image);
|
||||
* ct.start();
|
||||
* ct.join();
|
||||
* std::vector<unsigned char> bytes = ct.getCompressedData();
|
||||
*
|
||||
* Example uncompression:
|
||||
* @code
|
||||
* cv::Mat bytes;
|
||||
* CompressionThread ct(bytes, true);
|
||||
* ct.start();
|
||||
* ct.join();
|
||||
* cv::Mat image = ct.getUncompressedData();
|
||||
* @endcode
|
||||
* Example uncompression
|
||||
* std::vector<unsigned char> bytes;// a compressed image
|
||||
* CompressionThread ct(bytes);
|
||||
* ct.start();
|
||||
* ct.join();
|
||||
* cv::Mat image = ct.getUncompressedData();
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT CompressionThread : public UThread
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a thread in compress mode.
|
||||
* @param mat Source image or data matrix to compress.
|
||||
* @param format Image format: @c ".png", @c ".jpg", @c ".rvl", or empty for zlib (@ref compressData2).
|
||||
*/
|
||||
// format : ".png" ".jpg" "" (empty is general)
|
||||
CompressionThread(const cv::Mat & mat, const std::string & format = "");
|
||||
/**
|
||||
* @brief Constructs a thread in uncompress mode.
|
||||
* @param bytes Compressed bytes (@c CV_8UC1).
|
||||
* @param isImage If true, decode as image; otherwise decode as zlib data.
|
||||
*/
|
||||
CompressionThread(const cv::Mat & bytes, bool isImage);
|
||||
/** @return Compressed output (@c CV_8UC1), valid after compress mode completes. */
|
||||
const cv::Mat & getCompressedData() const {return compressedData_;}
|
||||
/** @return Uncompressed output, valid after uncompress mode completes. */
|
||||
cv::Mat & getUncompressedData() {return uncompressedData_;}
|
||||
protected:
|
||||
virtual void mainLoop();
|
||||
@@ -93,41 +71,24 @@ private:
|
||||
bool compressMode_;
|
||||
};
|
||||
|
||||
/** @brief Encodes @p image to a byte buffer (OpenCV @c imencode or RVL for depth). */
|
||||
std::vector<unsigned char> RTABMAP_CORE_EXPORT compressImage(const cv::Mat & image, const std::string & format = ".png");
|
||||
/** @brief Same as @ref compressImage() but returns a @c CV_8UC1 row matrix. */
|
||||
cv::Mat RTABMAP_CORE_EXPORT compressImage2(const cv::Mat & image, const std::string & format = ".png");
|
||||
|
||||
/** @brief Decodes compressed image bytes to a @cv::Mat. */
|
||||
cv::Mat RTABMAP_CORE_EXPORT uncompressImage(const cv::Mat & bytes);
|
||||
/** @brief Decodes compressed image bytes to a @cv::Mat. */
|
||||
cv::Mat RTABMAP_CORE_EXPORT uncompressImage(const std::vector<unsigned char> & bytes);
|
||||
|
||||
/** @brief Compresses a matrix with zlib; appends rows, cols and type at the end. */
|
||||
std::vector<unsigned char> RTABMAP_CORE_EXPORT compressData(const cv::Mat & data);
|
||||
/** @brief Same as @ref compressData() but returns a @c CV_8UC1 row matrix. */
|
||||
cv::Mat RTABMAP_CORE_EXPORT compressData2(const cv::Mat & data);
|
||||
|
||||
/** @brief Restores a matrix compressed with @ref compressData() or @ref compressData2(). */
|
||||
cv::Mat RTABMAP_CORE_EXPORT uncompressData(const cv::Mat & bytes);
|
||||
/** @brief Restores a matrix compressed with @ref compressData() or @ref compressData2(). */
|
||||
cv::Mat RTABMAP_CORE_EXPORT uncompressData(const std::vector<unsigned char> & bytes);
|
||||
/** @brief Restores a matrix from a raw compressed buffer. */
|
||||
cv::Mat RTABMAP_CORE_EXPORT uncompressData(const unsigned char * bytes, unsigned long size);
|
||||
|
||||
/** @brief Compresses a null-terminated string using @ref compressData2(). */
|
||||
cv::Mat RTABMAP_CORE_EXPORT compressString(const std::string & str);
|
||||
/** @brief Decompresses a string produced by @ref compressString(). */
|
||||
std::string RTABMAP_CORE_EXPORT uncompressString(const cv::Mat & bytes);
|
||||
|
||||
/**
|
||||
* @brief Detects the compression format of depth image bytes.
|
||||
* @return @c ".rvl" if the buffer has an RVL signature, otherwise @c ".png".
|
||||
*/
|
||||
std::string RTABMAP_CORE_EXPORT compressedDepthFormat(const cv::Mat & bytes);
|
||||
/** @overload */
|
||||
std::string RTABMAP_CORE_EXPORT compressedDepthFormat(const std::vector<unsigned char> & bytes);
|
||||
/** @overload */
|
||||
std::string RTABMAP_CORE_EXPORT compressedDepthFormat(const unsigned char * bytes, size_t size);
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -50,102 +50,53 @@ class Signature;
|
||||
class VWDictionary;
|
||||
class VisualWord;
|
||||
|
||||
/**
|
||||
* @class DBDriver
|
||||
* @brief Abstract database driver for RTAB-Map maps (signatures, links, words, statistics).
|
||||
*
|
||||
* DBDriver is the persistence layer used by @ref Memory, @ref DBReader and tools that
|
||||
* read or write \c .db files. The default implementation is @ref DBDriverSqlite3, created
|
||||
* by @ref create().
|
||||
*
|
||||
* The driver extends @ref UThreadNode. Pending @ref Signature and @ref VisualWord objects
|
||||
* are queued with @ref asyncSave() and flushed to the database by @ref emptyTrashes()
|
||||
* (also called from the background thread on @ref closeConnection()).
|
||||
*
|
||||
* Public methods are thread-safe where noted (mutex-protected wrappers). Subclasses
|
||||
* implement SQL-specific logic in protected \c *Query() virtual methods.
|
||||
*
|
||||
* @see DBDriverSqlite3
|
||||
* @see Memory
|
||||
*/
|
||||
// Todo This class needs a refactoring, the _dbSafeAccessMutex problem when the trash is emptying (transaction)
|
||||
// "Of course, it has always been the case and probably always will be
|
||||
//that you cannot use the same sqlite3 connection in two or more
|
||||
//threads at the same time. You can use different sqlite3 connections
|
||||
//at the same time in different threads, or you can move the same
|
||||
//sqlite3 connection across threads (subject to the constraints above)
|
||||
//but never, never try to use the same connection simultaneously in
|
||||
//two or more threads."
|
||||
//
|
||||
class RTABMAP_CORE_EXPORT DBDriver : public UThreadNode
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Factory: returns a SQLite database driver (@ref DBDriverSqlite3).
|
||||
* @param parameters Optional driver parameters (e.g. @ref Parameters::kDbTargetVersion()).
|
||||
* @return New driver instance; caller owns the pointer.
|
||||
*/
|
||||
static DBDriver * create(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
public:
|
||||
virtual ~DBDriver();
|
||||
|
||||
/** @brief Parse driver parameters from the map (e.g. target schema version). */
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
/**
|
||||
* @brief True when no database file URL was set at open time.
|
||||
* @note @ref DBDriverSqlite3 overrides this when @ref Parameters::kDbSqlite3InMemory() is enabled.
|
||||
*/
|
||||
virtual bool isInMemory() const {return _url.empty();}
|
||||
/** @return Database file path last passed to @ref openConnection(). */
|
||||
const std::string & getUrl() const {return _url;}
|
||||
/** @return Target schema version for new databases (from parameters). */
|
||||
const std::string & getTargetVersion() const {return _targetVersion;}
|
||||
|
||||
/**
|
||||
* @brief Queue a signature for deferred save; ownership is transferred.
|
||||
*
|
||||
* @note Only the *compressed* sensor buffers are written
|
||||
* (SensorData::imageCompressed(), depthOrRightCompressed(),
|
||||
* laserScanCompressed(), ...). Raw matrices are ignored, so a signature
|
||||
* carrying only raw data is stored with empty payloads. Memory compresses
|
||||
* before saving; a caller driving the driver directly should compress first
|
||||
* with @ref compressImage2() / @ref compressData2(), or pass the compressed
|
||||
* buffers to SensorData::setRGBDImage() / setLaserScan(), which treat a
|
||||
* 1-row CV_8UC1 matrix as already compressed.
|
||||
*/
|
||||
void asyncSave(Signature * s);
|
||||
/** @brief Queue a visual word for deferred save; ownership is transferred. */
|
||||
void asyncSave(VisualWord * vw);
|
||||
/**
|
||||
* @brief Flush queued signatures and visual words to the database.
|
||||
* @param async If true, signal the background thread instead of flushing synchronously.
|
||||
*/
|
||||
// Start recording changes made to the database until it is closed, then write a compact
|
||||
// delta of those changes to outputUrl (empty disables). outputUrl MUST use the ".dbu"
|
||||
// (db update) extension. Returns true if recording is active. Only the SQLite backend
|
||||
// supports it (session extension + database version >= 0.24); other backends return false.
|
||||
// NOTE: recorded changes are held in RAM until the database is closed, so only enable
|
||||
// this when the expected set of changes is small.
|
||||
virtual bool trackDatabaseChanges(const std::string & outputUrl) {(void)outputUrl; return false;}
|
||||
|
||||
void beginTransaction() const;
|
||||
void commit() const;
|
||||
|
||||
void asyncSave(Signature * s); //ownership transferred
|
||||
void asyncSave(VisualWord * vw); //ownership transferred
|
||||
void emptyTrashes(bool async = false);
|
||||
double getEmptyTrashesTime() const {return _emptyTrashesTime;}
|
||||
void setTimestampUpdateEnabled(bool enabled) {_timestampUpdate = enabled;} // used on Update Signature and Word queries
|
||||
|
||||
/**
|
||||
* @brief Export the pose graph to a Graphviz DOT file for visualization.
|
||||
*
|
||||
* Reads nodes and links from the database (and optionally from @p otherSignatures
|
||||
* not yet persisted) and writes @p fileName. Does not modify the database.
|
||||
* If @p ids is empty, all node ids are included.
|
||||
*/
|
||||
// Warning: the following functions don't look in the trash, direct database modifications
|
||||
void generateGraph(
|
||||
const std::string & fileName,
|
||||
const std::set<int> & ids = std::set<int>(),
|
||||
const std::map<int, Signature *> & otherSignatures = std::map<int, Signature *>());
|
||||
|
||||
/**
|
||||
* @name Direct database updates
|
||||
* @brief Write node or link data immediately (not via the async trash).
|
||||
* @{
|
||||
*/
|
||||
/** @brief Insert or replace a link in the database. */
|
||||
void addLink(const Link & link);
|
||||
/** @brief Remove a link between two nodes. */
|
||||
void removeLink(int from, int to);
|
||||
/** @brief Update an existing link in the database. */
|
||||
void updateLink(const Link & link);
|
||||
/**
|
||||
* @brief Update occupancy grid cells for a node.
|
||||
* @param ground Ground cells (raw @c CV_32FC2/@c CV_32FC3, or compressed @c CV_8UC1 1×N).
|
||||
* @param obstacles Obstacle cells (same formats as @p ground).
|
||||
* @param empty Empty cells (same formats as @p ground; ignored on DB schema < 0.16.0).
|
||||
* Raw mats are compressed internally via @ref SensorData::setOccupancyGrid() before writing.
|
||||
*/
|
||||
void updateOccupancyGrid(
|
||||
int nodeId,
|
||||
const cv::Mat & ground,
|
||||
@@ -153,61 +104,22 @@ public:
|
||||
const cv::Mat & empty,
|
||||
float cellSize,
|
||||
const cv::Point3f & viewpoint);
|
||||
/** @brief Update camera calibration stored for a node. */
|
||||
void updateCalibration(
|
||||
int nodeId,
|
||||
const std::vector<CameraModel> & models,
|
||||
const std::vector<StereoCameraModel> & stereoModels);
|
||||
/**
|
||||
* @brief Update the depth image stored for a node.
|
||||
* @param image Raw depth image, or pre-compressed blob (@c CV_8UC1, single row).
|
||||
* @param format Compression format when @p image is raw (e.g. @c ".png"); ignored if already compressed.
|
||||
* Uncompressed images are compressed with @ref compressImage2() before writing.
|
||||
*/
|
||||
void updateDepthImage(int nodeId, const cv::Mat & image, const std::string & format);
|
||||
/**
|
||||
* @brief Update the laser scan stored for a node.
|
||||
* @param scan Uncompressed scan, or already compressed (@ref LaserScan::isCompressed()).
|
||||
* Uncompressed data is compressed with @ref compressData2() before writing.
|
||||
*/
|
||||
void updateLaserScan(int nodeId, const LaserScan & scan);
|
||||
/** @} */
|
||||
|
||||
public:
|
||||
/**
|
||||
* @name Session exports and map artifacts
|
||||
* @brief Statistics, preview image, optimized poses, 2D map, mesh and FLANN index.
|
||||
* @{*/
|
||||
void addInfoAfterRun(int stMemSize, int lastSignAdded, int processMemUsed, int databaseMemUsed, int dictionarySize, const ParametersMap & parameters) const;
|
||||
/**
|
||||
* @brief Append a @ref Statistics record for a processed node.
|
||||
* @param statistics Metrics for @ref Statistics::refImageId() (insert skipped if id ≤ 0 or data empty).
|
||||
* @param saveWmState If true and schema ≥ 0.16.2, also store compressed working-memory state.
|
||||
*/
|
||||
void addStatistics(const Statistics & statistics, bool saveWmState) const;
|
||||
/**
|
||||
* @brief Save the map preview thumbnail in the Admin table.
|
||||
* @param image Raw image or JPEG-compressed blob (@c CV_8UC1, single row); empty clears it.
|
||||
* @note Requires database schema ≥ 0.12.0.
|
||||
*/
|
||||
void savePreviewImage(const cv::Mat & image) const;
|
||||
/** @brief Load and uncompress the map preview thumbnail from the Admin table. */
|
||||
cv::Mat loadPreviewImage() const;
|
||||
/** @brief Persist graph-optimized poses and last localization pose. */
|
||||
void saveOptimizedPoses(const std::map<int, Transform> & optimizedPoses, const Transform & lastlocalizationPose) const;
|
||||
/** @brief Load optimized poses; optional last localization pose output. */
|
||||
std::map<int, Transform> loadOptimizedPoses(Transform * lastlocalizationPose = 0) const;
|
||||
/** @brief Save the assembled 2D occupancy grid and its origin metadata. */
|
||||
void save2DMap(const cv::Mat & map, float xMin, float yMin, float cellSize) const;
|
||||
/** @brief Load the 2D map and fill origin/cell size outputs. */
|
||||
cv::Mat load2DMap(float & xMin, float & yMin, float & cellSize) const;
|
||||
/**
|
||||
* @brief Persist the global optimized 3D mesh to the database.
|
||||
* @param cloud Point cloud (@c CV_32FC1 or @c CV_32FC3).
|
||||
* @param polygons Optional per-texture polygon index lists (texture → polygon → vertex indices).
|
||||
* @param texCoords Optional UV coordinates per texture (one per polygon vertex).
|
||||
* @param textures Optional concatenated square texture images (same size per texture).
|
||||
*/
|
||||
void saveOptimizedMesh(
|
||||
const cv::Mat & cloud,
|
||||
const std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > & polygons = std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > >(), // Textures -> polygons -> vertices
|
||||
@@ -225,27 +137,15 @@ public:
|
||||
std::vector<std::vector<Eigen::Vector2f> > * texCoords = 0,
|
||||
#endif
|
||||
cv::Mat * textures = 0) const;
|
||||
/**
|
||||
* @brief Persist the visual word dictionary FLANN index (serialized blob).
|
||||
* @param indexData Serialized index from @ref VWDictionary::serializeIndex(); pass empty to clear.
|
||||
* @note Requires database schema ≥ 0.23.0. Used when @ref Parameters::kKpFlannIndexSaved() is enabled.
|
||||
*/
|
||||
void saveFlannIndex(const std::vector<unsigned char> & indexData) const;
|
||||
/** @} */
|
||||
|
||||
public:
|
||||
/**
|
||||
* @name Connection and database introspection
|
||||
* @brief Mutex-protected wrappers around protected \c *Query() methods.
|
||||
* @{*/
|
||||
/** @brief Open or create the database at @p url (empty @p url uses an in-memory database). */
|
||||
// Mutex-protected methods of abstract versions below
|
||||
|
||||
bool openConnection(const std::string & url, bool overwritten = false, bool readOnly = false);
|
||||
/** @brief Close the connection; optionally flush trashes and save in-memory DB to @p outputUrl. */
|
||||
void closeConnection(bool save = true, const std::string & outputUrl = "");
|
||||
/** @return True if a database connection is active. */
|
||||
bool isConnected() const;
|
||||
unsigned long getMemoryUsed() const; // In bytes
|
||||
/** @return Schema version string stored in the database (e.g. "0.21.0"). */
|
||||
std::string getDatabaseVersion() const;
|
||||
long getNodesMemoryUsed() const;
|
||||
long getLinksMemoryUsed() const;
|
||||
@@ -267,33 +167,11 @@ public:
|
||||
std::map<int, std::pair<std::map<std::string, float>, double> > getAllStatistics() const;
|
||||
std::map<int, std::vector<int> > getAllStatisticsWmStates() const;
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Run a SQL statement that does not return result rows.
|
||||
*
|
||||
* Executes @p sql on the open database (e.g. @c INSERT, @c UPDATE, @c DELETE,
|
||||
* @c CREATE, @c PRAGMA). Thread-safe wrapper around the backend's SQL execution.
|
||||
*
|
||||
* For reads, use the dedicated @ref loadSignature(), @ref loadLinks() and related
|
||||
* query methods instead of raw SQL.
|
||||
*
|
||||
* @param sql Complete SQL statement (SQLite syntax for @ref DBDriverSqlite3).
|
||||
* @note The connection must be open (@ref isConnected()). On failure the driver
|
||||
* aborts with an assertion (SQLite backend).
|
||||
* @warning @p sql is passed verbatim; sanitize any user-controlled values before calling.
|
||||
*/
|
||||
void executeNoResult(const std::string & sql) const;
|
||||
|
||||
/**
|
||||
* @name Load and query
|
||||
* @brief Load signatures, words, links and node metadata from the database.
|
||||
* @{*/
|
||||
|
||||
// Load objects
|
||||
void load(VWDictionary & dictionary, bool lastStateOnly = true, bool idsOnly = false) const;
|
||||
void load(VWDictionary & dictionary, bool lastStateOnly = true) const;
|
||||
void loadLastNodes(std::list<Signature *> & signatures, bool loadWordIdsOnly = false) const; // returned signatures must be freed after usage
|
||||
/** @brief Load one signature by id; caller must delete the returned pointer. */
|
||||
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, 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
|
||||
@@ -306,7 +184,6 @@ public:
|
||||
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;
|
||||
void getLocalFeatures(int signatureId, std::multimap<int, int> & words, std::vector<cv::KeyPoint> & keypoints, std::vector<cv::Point3f> & points, cv::Mat & descriptors) const;
|
||||
/** @brief Load outgoing links from @p signatureId, optionally filtered by @p type. */
|
||||
void loadLinks(int signatureId, std::multimap<int, Link> & links, Link::Type type = Link::kUndef) const;
|
||||
void getWeight(int signatureId, int & weight) const;
|
||||
void getLastNodeIds(std::set<int> & ids) const;
|
||||
@@ -320,18 +197,10 @@ public:
|
||||
void getNodesObservingLandmark(int landmarkId, std::map<int, Link> & nodes) const;
|
||||
void getNodeIdByLabel(const std::string & label, int & id) const;
|
||||
void getAllLabels(std::map<int, std::string> & labels) const;
|
||||
/** @} */
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Protected constructor for subclasses.
|
||||
*/
|
||||
DBDriver(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @name Backend implementation (subclass responsibility)
|
||||
* @brief Pure virtual SQL/backend hooks invoked by public wrappers above.
|
||||
* @{*/
|
||||
virtual bool connectDatabaseQuery(const std::string & url, bool overwritten = false, bool readOnly = false) = 0;
|
||||
virtual void disconnectDatabaseQuery(bool save = true, const std::string & outputUrl = "") = 0;
|
||||
virtual bool isConnectedQuery() const = 0;
|
||||
@@ -418,7 +287,7 @@ protected:
|
||||
virtual void saveFlannIndexQuery(const std::vector<unsigned char> & indexData) const = 0;
|
||||
|
||||
// Load objects
|
||||
virtual void loadQuery(VWDictionary & dictionary, bool lastStateOnly = true, bool idsOnly = false) const = 0;
|
||||
virtual void loadQuery(VWDictionary & dictionary, bool lastStateOnly = true) 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, bool loadWordIdsOnly) const = 0;
|
||||
virtual void loadWordsQuery(const std::set<int> & wordIds, std::list<VisualWord *> & vws) const = 0;
|
||||
@@ -438,14 +307,8 @@ protected:
|
||||
virtual void getNodesObservingLandmarkQuery(int landmarkId, std::map<int, Link> & nodes) const = 0;
|
||||
virtual void getNodeIdByLabelQuery(const std::string & label, int & id) const = 0;
|
||||
virtual void getAllLabelsQuery(std::map<int, std::string> & labels) const = 0;
|
||||
/** @} */
|
||||
|
||||
private:
|
||||
/** @brief Begin a database transaction (nested calls are serialized). */
|
||||
void beginTransaction() const;
|
||||
/** @brief Commit the current transaction. */
|
||||
void commit() const;
|
||||
|
||||
//non-abstract methods
|
||||
void saveOrUpdate(const std::vector<Signature *> & signatures);
|
||||
void saveOrUpdate(const std::vector<VisualWord *> & words) const;
|
||||
|
||||
@@ -30,130 +30,49 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "rtabmap/core/rtabmap_core_export.h" // DLL export/import defines
|
||||
#include "rtabmap/core/DBDriver.h"
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/features2d/features2d.hpp>
|
||||
#else
|
||||
#include <opencv2/features.hpp>
|
||||
#endif
|
||||
|
||||
typedef struct sqlite3_stmt sqlite3_stmt;
|
||||
typedef struct sqlite3 sqlite3;
|
||||
typedef struct sqlite3_session sqlite3_session;
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class DBDriverSqlite3
|
||||
* @brief SQLite3 implementation of @ref DBDriver for RTAB-Map map databases.
|
||||
*
|
||||
* This is the default driver returned by @ref DBDriver::create(). It stores signatures,
|
||||
* links, visual words, statistics and sensor payloads in a single \c .db file using
|
||||
* the SQLite C API.
|
||||
*
|
||||
* **Storage modes**
|
||||
* - **File-backed** (default): the database is read/written directly on disk at @ref getUrl().
|
||||
* - **In-memory**: when @ref isInMemory() is true, SQLite uses \c :memory: and the file at
|
||||
* @ref getUrl() (if any) is loaded at open and optionally saved on @ref closeConnection().
|
||||
*
|
||||
* **SQLite PRAGMA tuning** (applied on connect and when setters are called while connected):
|
||||
* - @ref setCacheSize() — page cache size in pages
|
||||
* - @ref setJournalMode() — rollback journal mode (DELETE … OFF)
|
||||
* - @ref setSynchronous() — fsync policy (OFF / NORMAL / FULL)
|
||||
* - @ref setTempStore() — storage for temporary tables and indices
|
||||
*
|
||||
* Configure via @ref Parameters::kDbSqlite3InMemory(), @ref Parameters::kDbSqlite3CacheSize(),
|
||||
* @ref Parameters::kDbSqlite3JournalMode(), @ref Parameters::kDbSqlite3Synchronous() and
|
||||
* @ref Parameters::kDbSqlite3TempStore(), or call the setters directly.
|
||||
*
|
||||
* @see DBDriver
|
||||
* @see DBDriver::create()
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT DBDriverSqlite3: public DBDriver {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct driver with optional SQLite-specific parameters.
|
||||
* @param parameters Map of parameters (see class description).
|
||||
*/
|
||||
DBDriverSqlite3(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~DBDriverSqlite3();
|
||||
|
||||
/** @brief Apply SQLite parameters from the map; forwards to @ref DBDriver::parseParameters(). */
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
/**
|
||||
* @brief True when the database runs in RAM instead of on disk.
|
||||
* @return True if @ref getUrl() is empty or @ref setDbInMemory() was used.
|
||||
*/
|
||||
virtual bool isInMemory() const {return getUrl().empty() || _dbInMemory;}
|
||||
/**
|
||||
* @brief Enable or disable in-memory mode.
|
||||
* If connected, the connection is closed and reopened with the new mode.
|
||||
*/
|
||||
void setDbInMemory(bool dbInMemory);
|
||||
/**
|
||||
* @brief Set SQLite rollback journal mode (`PRAGMA journal_mode`).
|
||||
*
|
||||
* Controls how SQLite stores the transaction journal used for atomic commit and rollback.
|
||||
* See https://www.sqlite.org/pragma.html#pragma_journal_mode
|
||||
*
|
||||
* @param journalMode Accepted values (invalid values are ignored). RTAB-Map default:
|
||||
* @ref Parameters::defaultDbSqlite3JournalMode() = **3 (MEMORY)** (@ref Parameters::kDbSqlite3JournalMode()).
|
||||
* - **0 — DELETE** (SQLite default): the journal file is deleted at the end of each transaction.
|
||||
* Good general-purpose balance of safety and speed.
|
||||
* - **1 — TRUNCATE**: the journal is truncated to zero length instead of being unlinked; can be
|
||||
* faster on some filesystems than DELETE.
|
||||
* - **2 — PERSIST**: the journal file is not deleted; only its header is zeroed after commit,
|
||||
* reducing create/delete overhead at the cost of always keeping a journal file on disk.
|
||||
* - **3 — MEMORY** (RTAB-Map default): the journal is held in RAM only (not written to disk). Faster, but the
|
||||
* database cannot be rolled back after a crash and may corrupt if the process dies mid-write.
|
||||
* - **4 — OFF**: no rollback journal. Fastest, but a crash or power loss during a write can
|
||||
* leave the database inconsistent; transactions cannot be rolled back atomically.
|
||||
*/
|
||||
void setJournalMode(int journalMode);
|
||||
|
||||
/**
|
||||
* @brief Set the number of database pages kept in SQLite's page cache (`PRAGMA cache_size`).
|
||||
*
|
||||
* A larger cache reduces disk I/O when the working set fits in memory. The effective memory
|
||||
* is approximately `cacheSize * page_size` bytes (page size is usually 4096 bytes unless
|
||||
* changed with `PRAGMA page_size`). Only positive values are used (page count); see
|
||||
* https://www.sqlite.org/pragma.html#pragma_cache_size
|
||||
*
|
||||
* @param cacheSize Number of pages to cache. RTAB-Map default:
|
||||
* @ref Parameters::defaultDbSqlite3CacheSize() = **10000** (@ref Parameters::kDbSqlite3CacheSize()).
|
||||
*/
|
||||
void setCacheSize(unsigned int cacheSize);
|
||||
|
||||
/**
|
||||
* @brief Set how aggressively SQLite syncs the database file to disk (`PRAGMA synchronous`).
|
||||
*
|
||||
* Trade-off between durability after a crash or power loss and write performance.
|
||||
* See https://www.sqlite.org/pragma.html#pragma_synchronous
|
||||
*
|
||||
* @param synchronous Accepted values (invalid values are ignored). RTAB-Map default:
|
||||
* @ref Parameters::defaultDbSqlite3Synchronous() = **0 (OFF)** (@ref Parameters::kDbSqlite3Synchronous()).
|
||||
* - **0 — OFF** (RTAB-Map default): SQLite does not wait for data to reach persistent storage. Fastest; a system
|
||||
* crash or power loss during a transaction may corrupt the database.
|
||||
* - **1 — NORMAL**: syncs at the most critical moments (SQLite default in many builds). A crash
|
||||
* may lose the last transaction but the database file structure usually stays valid.
|
||||
* - **2 — FULL**: syncs after every transaction commit. Slowest; strongest guarantee that a
|
||||
* committed transaction survives a power loss (when the OS honors fsync).
|
||||
*/
|
||||
void setSynchronous(int synchronous);
|
||||
|
||||
/**
|
||||
* @brief Set where SQLite stores temporary tables and indices (`PRAGMA temp_store`).
|
||||
*
|
||||
* Affects internal temp storage used for some queries and operations, not RTAB-Map map data.
|
||||
* See https://www.sqlite.org/pragma.html#pragma_temp_store
|
||||
*
|
||||
* @param tempStore Accepted values (invalid values are ignored). RTAB-Map default:
|
||||
* @ref Parameters::defaultDbSqlite3TempStore() = **2 (MEMORY)** (@ref Parameters::kDbSqlite3TempStore()).
|
||||
* - **0 — DEFAULT**: use SQLite's compile-time default (often FILE, i.e. on-disk temp files).
|
||||
* - **1 — FILE**: store temporary tables and indices in temporary files in the directory
|
||||
* given by `PRAGMA temp_store_directory` or the system temp folder.
|
||||
* - **2 — MEMORY** (RTAB-Map default): store temporary tables and indices in RAM. Can speed up heavy queries
|
||||
* but increases memory use; large temp structures may still spill to disk depending on build.
|
||||
*/
|
||||
void setTempStore(int tempStore);
|
||||
// Start recording changes made to the database from now until it is closed, then write
|
||||
// a compact changeset delta to outputUrl (empty disables). outputUrl MUST use the ".dbu"
|
||||
// (db update) extension. Can be called before the connection is opened or on an already-open
|
||||
// connection (e.g. after init(), so the baseline is the current content). Returns true if
|
||||
// recording is active. Only effective if the build/runtime SQLite has the session extension
|
||||
// and the database is version >= 0.24.
|
||||
// NOTE: the SQLite session extension holds ALL recorded changes in RAM until the database
|
||||
// is closed (there is no incremental spill to disk), so only enable this when the expected
|
||||
// set of changes is small (e.g. appending a few sessions), not for rewriting a whole map.
|
||||
virtual bool trackDatabaseChanges(const std::string & outputUrl);
|
||||
|
||||
// Apply a ".dbu" change delta previously written by trackDatabaseChanges() onto the database
|
||||
// at databasePath. The file (which must have the ".dbu" extension) is decompressed (same codec
|
||||
// as the other rtabmap blobs) then applied with the SQLite session extension. With rewind=false,
|
||||
// databasePath must be at the same state the delta was recorded from; with rewind=true, the
|
||||
// delta is inverted first to undo it, so databasePath must be at the post-update state. Returns
|
||||
// true on success; on failure returns false and, if errorMsg is not null, sets a human-readable
|
||||
// message. Requires a SQLite library built with the session extension (both build and runtime).
|
||||
static bool applyChangesFromFile(
|
||||
const std::string & databasePath,
|
||||
const std::string & changesetPath,
|
||||
bool rewind = false,
|
||||
std::string * errorMsg = 0);
|
||||
|
||||
protected:
|
||||
virtual bool connectDatabaseQuery(const std::string & url, bool overwritten = false, bool readOnly = false);
|
||||
@@ -243,7 +162,7 @@ protected:
|
||||
virtual void saveFlannIndexQuery(const std::vector<unsigned char> & indexData) const;
|
||||
|
||||
// Load objects
|
||||
virtual void loadQuery(VWDictionary & dictionary, bool lastStateOnly = true, bool idsOnly = false) const;
|
||||
virtual void loadQuery(VWDictionary & dictionary, bool lastStateOnly = true) const;
|
||||
virtual void loadLastNodesQuery(std::list<Signature *> & signatures, bool loadWordIdsOnly) 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;
|
||||
@@ -302,10 +221,11 @@ private:
|
||||
void loadWordIdsQuery(std::list<Signature *> & signatures) const;
|
||||
void loadLinksQuery(std::list<Signature *> & signatures) const;
|
||||
int loadOrSaveDb(sqlite3 *pInMemory, const std::string & fileName, int isSave) const;
|
||||
void startChangeTracking(); // attach the change-tracking session on the open connection
|
||||
|
||||
protected:
|
||||
sqlite3 * _ppDb; ///< Open SQLite connection (null when disconnected)
|
||||
std::string _version; ///< Schema version read from the database
|
||||
sqlite3 * _ppDb;
|
||||
std::string _version;
|
||||
|
||||
private:
|
||||
unsigned long _memoryUsedEstimate;
|
||||
@@ -314,6 +234,12 @@ private:
|
||||
int _journalMode;
|
||||
int _synchronous;
|
||||
int _tempStore;
|
||||
|
||||
// DB change tracking (SQLite session extension). Members are always present to keep
|
||||
// the class layout stable regardless of RTABMAP_WITH_SQLITE3_SESSION; the session is
|
||||
// only created/used when the feature is compiled in and enabled.
|
||||
sqlite3_session * _session;
|
||||
std::string _trackChangesOutput;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -28,67 +28,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef CORELIB_INCLUDE_RTABMAP_CORE_ENVSENSOR_H_
|
||||
#define CORELIB_INCLUDE_RTABMAP_CORE_ENVSENSOR_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class EnvSensor
|
||||
* @brief Single environmental measurement (type, value, timestamp).
|
||||
*
|
||||
* Built-in @ref Type values cover common phone/robot sensors; @ref kCustomSensor1
|
||||
* through @ref kCustomSensor9 are reserved for application-specific channels.
|
||||
* Units depend on the type (see @ref Type).
|
||||
*
|
||||
* Samples are stored per node in @ref SensorData via @ref EnvSensors (one entry
|
||||
* per type). Persisted in the database with node records (@ref DBDriver).
|
||||
*
|
||||
* @see SensorData::setEnvSensors()
|
||||
* @see SensorData::addEnvSensor()
|
||||
* @see SensorData::envSensors()
|
||||
*/
|
||||
class EnvSensor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Environmental sensor channel identifier.
|
||||
*
|
||||
* Built-in types use fixed units; custom types (≥ @ref kCustomSensor1) have
|
||||
* application-defined meaning and units.
|
||||
*/
|
||||
enum Type {
|
||||
kUndefined = 0, ///< Uninitialized / unknown channel.
|
||||
kWifiSignalStrength, ///< Wi‑Fi signal strength (dBm).
|
||||
kAmbientTemperature, ///< Ambient temperature (°C).
|
||||
kAmbientAirPressure, ///< Ambient air pressure (hPa).
|
||||
kAmbientLight, ///< Ambient illuminance (lx).
|
||||
kAmbientRelativeHumidity, ///< Relative humidity (%).
|
||||
// built-in types
|
||||
kUndefined = 0,
|
||||
kWifiSignalStrength, // dBm
|
||||
kAmbientTemperature, // Celcius
|
||||
kAmbientAirPressure, // hPa
|
||||
kAmbientLight, // lx
|
||||
kAmbientRelativeHumidity, // %
|
||||
|
||||
kCustomSensor1 = 100, ///< User-defined sensor slot 1.
|
||||
kCustomSensor2, ///< User-defined sensor slot 2.
|
||||
kCustomSensor3, ///< User-defined sensor slot 3.
|
||||
kCustomSensor4, ///< User-defined sensor slot 4.
|
||||
kCustomSensor5, ///< User-defined sensor slot 5.
|
||||
kCustomSensor6, ///< User-defined sensor slot 6.
|
||||
kCustomSensor7, ///< User-defined sensor slot 7.
|
||||
kCustomSensor8, ///< User-defined sensor slot 8.
|
||||
kCustomSensor9 ///< User-defined sensor slot 9.
|
||||
// user types
|
||||
kCustomSensor1 = 100,
|
||||
kCustomSensor2,
|
||||
kCustomSensor3,
|
||||
kCustomSensor4,
|
||||
kCustomSensor5,
|
||||
kCustomSensor6,
|
||||
kCustomSensor7,
|
||||
kCustomSensor8,
|
||||
kCustomSensor9
|
||||
};
|
||||
|
||||
public:
|
||||
/** @brief Default constructor: @ref kUndefined type, zero value and stamp. */
|
||||
EnvSensor() :
|
||||
type_(kUndefined),
|
||||
value_(0.0),
|
||||
stamp_(0.0)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a reading with the given type, value, and optional stamp.
|
||||
* @param type Sensor channel (@ref Type).
|
||||
* @param value Measurement in the units for @p type.
|
||||
* @param stamp Timestamp in seconds (0 if unknown).
|
||||
*/
|
||||
EnvSensor(const Type & type, const double & value,const double & stamp = 0) :
|
||||
type_(type),
|
||||
value_(value),
|
||||
@@ -97,11 +68,8 @@ public:
|
||||
|
||||
virtual ~EnvSensor() {}
|
||||
|
||||
/** @return Sensor channel. */
|
||||
const Type & type() const {return type_;}
|
||||
/** @return Measurement value (units depend on @ref type()). */
|
||||
const double & value() const {return value_;}
|
||||
/** @return Timestamp in seconds. */
|
||||
const double & stamp() const {return stamp_;}
|
||||
|
||||
private:
|
||||
@@ -110,7 +78,6 @@ private:
|
||||
double stamp_;
|
||||
};
|
||||
|
||||
/** @brief Map of environmental readings keyed by @ref EnvSensor::Type (at most one per type). */
|
||||
typedef std::map<EnvSensor::Type, EnvSensor> EnvSensors;
|
||||
|
||||
}
|
||||
|
||||
@@ -31,11 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "rtabmap/core/Parameters.h"
|
||||
#include "rtabmap/utilite/UStl.h"
|
||||
#include <opencv2/core/core.hpp>
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/features2d/features2d.hpp>
|
||||
#else
|
||||
#include <opencv2/features.hpp>
|
||||
#endif
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <list>
|
||||
|
||||
@@ -32,11 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/features2d/features2d.hpp>
|
||||
#else
|
||||
#include <opencv2/features.hpp>
|
||||
#endif
|
||||
#include <list>
|
||||
#include <numeric>
|
||||
#include "rtabmap/core/Parameters.h"
|
||||
@@ -74,10 +70,6 @@ class BriefDescriptorExtractor;
|
||||
class SIFT;
|
||||
#endif
|
||||
class SURF;
|
||||
#if (CV_MAJOR_VERSION == 5)
|
||||
class BRISK;
|
||||
class KAZE;
|
||||
#endif
|
||||
}
|
||||
namespace cuda {
|
||||
class FastFeatureDetector;
|
||||
@@ -97,13 +89,7 @@ typedef cv::xfeatures2d::FREAK CV_FREAK;
|
||||
typedef cv::xfeatures2d::DAISY CV_DAISY;
|
||||
typedef cv::GFTTDetector CV_GFTT;
|
||||
typedef cv::xfeatures2d::BriefDescriptorExtractor CV_BRIEF;
|
||||
#if (CV_MAJOR_VERSION < 5)
|
||||
typedef cv::BRISK CV_BRISK;
|
||||
typedef cv::KAZE CV_KAZE;
|
||||
#else
|
||||
typedef cv::xfeatures2d::BRISK CV_BRISK;
|
||||
typedef cv::xfeatures2d::KAZE CV_KAZE;
|
||||
#endif
|
||||
typedef cv::ORB CV_ORB;
|
||||
typedef cv::cuda::SURF_CUDA CV_SURF_GPU;
|
||||
typedef cv::cuda::ORB CV_ORB_GPU;
|
||||
@@ -125,26 +111,9 @@ class Stereo;
|
||||
class CV_ORB;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @class Feature2D
|
||||
* @brief Abstract 2D feature detector and descriptor extractor for visual SLAM.
|
||||
*
|
||||
* Factory @ref create() builds a concrete detector from @ref Type or from
|
||||
* **Kp/DetectorStrategy** in a @ref ParametersMap. Common tuning keys include
|
||||
* **Kp/MaxFeatures**, **Kp/GridRows**, **Kp/GridCols**, **Kp/SSC**, depth filters
|
||||
* (**Kp/MinDepth**, **Kp/MaxDepth**), ROI (**Kp/RoiRatios**), and sub-pixel refinement.
|
||||
*
|
||||
* Pipeline: @ref generateKeypoints() (grid + ROI + optional mask) then
|
||||
* @ref generateDescriptors(). Static helpers filter or cap keypoints before/after
|
||||
* matching. @ref generateKeypoints3D() projects features using stereo or depth when
|
||||
* available in @ref SensorData.
|
||||
*
|
||||
* @see Memory
|
||||
* @see RegistrationVis
|
||||
*/
|
||||
// Feature2D
|
||||
class RTABMAP_CORE_EXPORT Feature2D {
|
||||
public:
|
||||
/** @brief Built-in detector/descriptor strategy (Kp/DetectorStrategy). */
|
||||
enum Type {kFeatureUndef=-1,
|
||||
kFeatureSurf=0,
|
||||
kFeatureSift=1,
|
||||
@@ -162,10 +131,8 @@ public:
|
||||
kFeatureGfttDaisy=13, //new 0.20.6
|
||||
kFeatureSurfDaisy=14, //new 0.20.6
|
||||
kFeaturePyDetector=15, //new 0.20.8
|
||||
kFeatureSuperPointRpautrat=16, // new 0.23.3
|
||||
kFeatureEnd}; // Sentinel: always keep last. Used to iterate through types.
|
||||
kFeatureSuperPointRpautrat=16}; // new 0.23.3
|
||||
|
||||
/** @return Human-readable name for @p type (e.g. `"ORB"`, `"GFTT+BRIEF"`). */
|
||||
static std::string typeName(Type type)
|
||||
{
|
||||
switch(type){
|
||||
@@ -199,8 +166,6 @@ public:
|
||||
return "GFTT+Daisy";
|
||||
case kFeatureSurfDaisy:
|
||||
return "SURF+Daisy";
|
||||
case kFeaturePyDetector:
|
||||
return "PyDetector";
|
||||
case kFeatureSuperPointRpautrat:
|
||||
return "SUPERPOINT-RPAUTRAT";
|
||||
default:
|
||||
@@ -208,15 +173,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Creates a detector from **Kp/DetectorStrategy** in @p parameters. Caller owns the pointer. */
|
||||
static Feature2D * create(const ParametersMap & parameters = ParametersMap());
|
||||
/** @brief Creates a detector of the given @p type. Caller owns the pointer. */
|
||||
static Feature2D * create(Feature2D::Type type, const ParametersMap & parameters = ParametersMap());
|
||||
static Feature2D * create(Feature2D::Type type, const ParametersMap & parameters = ParametersMap()); // for convenience
|
||||
|
||||
/** @brief Returns true if @p type is available (RTAB-Map is built with it). */
|
||||
static bool isAvailable(Feature2D::Type type);
|
||||
|
||||
/** @brief Keeps keypoints whose depth at (u,v) is in (@p minDepth, @p maxDepth). */
|
||||
static void filterKeypointsByDepth(
|
||||
std::vector<cv::KeyPoint> & keypoints,
|
||||
const cv::Mat & depth,
|
||||
@@ -235,7 +194,6 @@ public:
|
||||
float minDepth,
|
||||
float maxDepth);
|
||||
|
||||
/** @brief Keeps keypoints with stereo disparity ≥ @p minDisparity. */
|
||||
static void filterKeypointsByDisparity(
|
||||
std::vector<cv::KeyPoint> & keypoints,
|
||||
const cv::Mat & disparity,
|
||||
@@ -246,14 +204,12 @@ public:
|
||||
const cv::Mat & disparity,
|
||||
float minDisparity);
|
||||
|
||||
/** @brief Reduces keypoint count (by response or SSC spatial distribution). */
|
||||
static void limitKeypoints(std::vector<cv::KeyPoint> & keypoints, int maxKeypoints, const cv::Size & imageSize = cv::Size(), bool ssc = false);
|
||||
static void limitKeypoints(std::vector<cv::KeyPoint> & keypoints, cv::Mat & descriptors, int maxKeypoints, const cv::Size & imageSize = cv::Size(), bool ssc = false);
|
||||
static void limitKeypoints(std::vector<cv::KeyPoint> & keypoints, std::vector<cv::Point3f> & keypoints3D, cv::Mat & descriptors, int maxKeypoints, const cv::Size & imageSize = cv::Size(), bool ssc = false);
|
||||
static void limitKeypoints(const std::vector<cv::KeyPoint> & keypoints, std::vector<bool> & inliers, int maxKeypoints, const cv::Size & imageSize = cv::Size(), bool ssc = false);
|
||||
static void limitKeypoints(const std::vector<cv::KeyPoint> & keypoints, std::vector<bool> & inliers, int maxKeypoints, const cv::Size & imageSize, int gridRows, int gridCols, bool ssc = false);
|
||||
|
||||
/** @brief ROI from **Kp/RoiRatios** string (`"left top right bottom"` fractions). */
|
||||
static cv::Rect computeRoi(const cv::Mat & image, const std::string & roiRatios);
|
||||
static cv::Rect computeRoi(const cv::Mat & image, const std::vector<float> & roiRatios);
|
||||
|
||||
@@ -267,15 +223,12 @@ public:
|
||||
public:
|
||||
virtual ~Feature2D();
|
||||
|
||||
/** @brief Detects keypoints in a grayscale @p image (CV_8UC1); optional depth or 8U mask. */
|
||||
std::vector<cv::KeyPoint> generateKeypoints(
|
||||
const cv::Mat & image,
|
||||
const cv::Mat & mask = cv::Mat());
|
||||
/** @brief Computes descriptors for @p keypoints (may shrink the list in some detectors). */
|
||||
cv::Mat generateDescriptors(
|
||||
const cv::Mat & image,
|
||||
std::vector<cv::KeyPoint> & keypoints) const;
|
||||
/** @brief Back-projects keypoints to 3D using depth or stereo in @p data. */
|
||||
std::vector<cv::Point3f> generateKeypoints3D(
|
||||
const SensorData & data,
|
||||
const std::vector<cv::KeyPoint> & keypoints) const;
|
||||
@@ -284,15 +237,6 @@ public:
|
||||
virtual const ParametersMap & getParameters() const {return parameters_;}
|
||||
virtual Feature2D::Type getType() const = 0;
|
||||
|
||||
/** @brief Returns true when a GPU/CUDA code path **could** be used by
|
||||
* this detector on this host: i.e. the build was compiled with the
|
||||
* matching GPU support AND a CUDA-capable device is detected at
|
||||
* runtime. This is a capability probe -- it does NOT reflect whether
|
||||
* the current instance is actually configured to run on GPU (that
|
||||
* depends on per-detector parameters like SURF/GpuVersion). Defaults
|
||||
* to false; subclasses with a GPU backend override it. */
|
||||
virtual bool isGpuAvailable() const {return false;}
|
||||
|
||||
protected:
|
||||
Feature2D(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
@@ -316,20 +260,19 @@ private:
|
||||
Stereo * _stereo;
|
||||
};
|
||||
|
||||
/** @brief SURF detector and descriptor (non-free / xfeatures2d depending on OpenCV build). */
|
||||
//SURF
|
||||
class RTABMAP_CORE_EXPORT SURF : public Feature2D
|
||||
{
|
||||
public:
|
||||
SURF(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~SURF();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureSurf;}
|
||||
virtual bool isGpuAvailable() const override;
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureSurf;}
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat()) override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
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;
|
||||
|
||||
private:
|
||||
double hessianThreshold_;
|
||||
@@ -344,20 +287,19 @@ private:
|
||||
cv::Ptr<CV_SURF_GPU> _gpuSurf;
|
||||
};
|
||||
|
||||
/** @brief SIFT detector and descriptor (optional GPU / CudaSift). */
|
||||
//SIFT
|
||||
class RTABMAP_CORE_EXPORT SIFT : public Feature2D
|
||||
{
|
||||
public:
|
||||
SIFT(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~SIFT();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureSift;}
|
||||
virtual bool isGpuAvailable() const override;
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureSift;}
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat()) override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
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;
|
||||
|
||||
private:
|
||||
int nOctaveLayers_;
|
||||
@@ -379,20 +321,19 @@ private:
|
||||
bool cudaSiftUpscaling_;
|
||||
};
|
||||
|
||||
/** @brief ORB detector and descriptor (optional GPU). */
|
||||
//ORB
|
||||
class RTABMAP_CORE_EXPORT ORB : public Feature2D
|
||||
{
|
||||
public:
|
||||
ORB(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~ORB();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureOrb;}
|
||||
virtual bool isGpuAvailable() const override;
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureOrb;}
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat()) override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
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;
|
||||
|
||||
private:
|
||||
float scaleFactor_;
|
||||
@@ -411,20 +352,19 @@ private:
|
||||
cv::Ptr<CV_ORB_GPU> _gpuOrb;
|
||||
};
|
||||
|
||||
/** @brief FAST corner detector only (no descriptor). */
|
||||
//FAST
|
||||
class RTABMAP_CORE_EXPORT FAST : public Feature2D
|
||||
{
|
||||
public:
|
||||
FAST(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~FAST();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureUndef;}
|
||||
virtual bool isGpuAvailable() const override;
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureUndef;}
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat()) override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat &, std::vector<cv::KeyPoint> &) const override {return cv::Mat();}
|
||||
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 &, std::vector<cv::KeyPoint> &) const {return cv::Mat();}
|
||||
|
||||
private:
|
||||
int threshold_;
|
||||
@@ -448,18 +388,18 @@ private:
|
||||
cv::Ptr<CV_FAST_GPU> _gpuFast;
|
||||
};
|
||||
|
||||
/** @brief FAST corners + BRIEF descriptors. */
|
||||
//FAST_BRIEF
|
||||
class RTABMAP_CORE_EXPORT FAST_BRIEF : public FAST
|
||||
{
|
||||
public:
|
||||
FAST_BRIEF(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~FAST_BRIEF();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureFastBrief;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureFastBrief;}
|
||||
|
||||
private:
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
|
||||
|
||||
private:
|
||||
int bytes_;
|
||||
@@ -467,18 +407,18 @@ private:
|
||||
cv::Ptr<CV_BRIEF> _brief;
|
||||
};
|
||||
|
||||
/** @brief FAST corners + FREAK descriptors. */
|
||||
//FAST_FREAK
|
||||
class RTABMAP_CORE_EXPORT FAST_FREAK : public FAST
|
||||
{
|
||||
public:
|
||||
FAST_FREAK(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~FAST_FREAK();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureFastFreak;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureFastFreak;}
|
||||
|
||||
private:
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
|
||||
|
||||
private:
|
||||
bool orientationNormalized_;
|
||||
@@ -489,18 +429,17 @@ private:
|
||||
cv::Ptr<CV_FREAK> _freak;
|
||||
};
|
||||
|
||||
/** @brief Good-features-to-track detector (Shi–Tomasi / Harris). */
|
||||
//GFTT
|
||||
class RTABMAP_CORE_EXPORT GFTT : public Feature2D
|
||||
{
|
||||
public:
|
||||
GFTT(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~GFTT();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual bool isGpuAvailable() const override;
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat()) override;
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat());
|
||||
|
||||
private:
|
||||
double _qualityLevel;
|
||||
@@ -514,18 +453,18 @@ private:
|
||||
cv::Ptr<CV_GFTT_GPU> _gpuGftt;
|
||||
};
|
||||
|
||||
/** @brief GFTT corners + BRIEF descriptors. */
|
||||
//GFTT_BRIEF
|
||||
class RTABMAP_CORE_EXPORT GFTT_BRIEF : public GFTT
|
||||
{
|
||||
public:
|
||||
GFTT_BRIEF(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~GFTT_BRIEF();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureGfttBrief;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureGfttBrief;}
|
||||
|
||||
private:
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
|
||||
|
||||
private:
|
||||
int bytes_;
|
||||
@@ -533,18 +472,18 @@ private:
|
||||
cv::Ptr<CV_BRIEF> _brief;
|
||||
};
|
||||
|
||||
/** @brief GFTT corners + FREAK descriptors. */
|
||||
//GFTT_FREAK
|
||||
class RTABMAP_CORE_EXPORT GFTT_FREAK : public GFTT
|
||||
{
|
||||
public:
|
||||
GFTT_FREAK(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~GFTT_FREAK();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureGfttFreak;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureGfttFreak;}
|
||||
|
||||
private:
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
|
||||
|
||||
private:
|
||||
bool orientationNormalized_;
|
||||
@@ -555,18 +494,18 @@ private:
|
||||
cv::Ptr<CV_FREAK> _freak;
|
||||
};
|
||||
|
||||
/** @brief SURF detector + FREAK descriptors. */
|
||||
//SURF_FREAK
|
||||
class RTABMAP_CORE_EXPORT SURF_FREAK : public SURF
|
||||
{
|
||||
public:
|
||||
SURF_FREAK(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~SURF_FREAK();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureSurfFreak;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureSurfFreak;}
|
||||
|
||||
private:
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
|
||||
|
||||
private:
|
||||
bool orientationNormalized_;
|
||||
@@ -577,36 +516,36 @@ private:
|
||||
cv::Ptr<CV_FREAK> _freak;
|
||||
};
|
||||
|
||||
/** @brief GFTT corners + ORB descriptors (common default when SURF is unavailable). */
|
||||
//GFTT_ORB
|
||||
class RTABMAP_CORE_EXPORT GFTT_ORB : public GFTT
|
||||
{
|
||||
public:
|
||||
GFTT_ORB(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~GFTT_ORB();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureGfttOrb;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureGfttOrb;}
|
||||
|
||||
private:
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
|
||||
|
||||
private:
|
||||
ORB _orb;
|
||||
};
|
||||
|
||||
/** @brief BRISK detector and descriptor. */
|
||||
//BRISK
|
||||
class RTABMAP_CORE_EXPORT BRISK : public Feature2D
|
||||
{
|
||||
public:
|
||||
BRISK(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~BRISK();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureBrisk;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureBrisk;}
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat()) override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
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;
|
||||
|
||||
private:
|
||||
int thresh_;
|
||||
@@ -616,19 +555,19 @@ private:
|
||||
cv::Ptr<CV_BRISK> brisk_;
|
||||
};
|
||||
|
||||
/** @brief KAZE detector and descriptor (OpenCV 3+). */
|
||||
//KAZE
|
||||
class RTABMAP_CORE_EXPORT KAZE : public Feature2D
|
||||
{
|
||||
public:
|
||||
KAZE(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~KAZE();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override { return kFeatureKaze; }
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const { return kFeatureKaze; }
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat()) override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
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;
|
||||
|
||||
private:
|
||||
bool extended_;
|
||||
@@ -639,23 +578,23 @@ private:
|
||||
int diffusivity_;
|
||||
|
||||
#if CV_MAJOR_VERSION > 2
|
||||
cv::Ptr<CV_KAZE> kaze_;
|
||||
cv::Ptr<cv::KAZE> kaze_;
|
||||
#endif
|
||||
};
|
||||
|
||||
/** @brief ORB with octree spatial distribution (RTAB-Map must be built with OCTREE enabled). */
|
||||
//ORB OCTREE
|
||||
class RTABMAP_CORE_EXPORT ORBOctree : public Feature2D
|
||||
{
|
||||
public:
|
||||
ORBOctree(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~ORBOctree();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureOrbOctree;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureOrbOctree;}
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat()) override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
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;
|
||||
|
||||
private:
|
||||
float scaleFactor_;
|
||||
@@ -669,20 +608,19 @@ private:
|
||||
cv::Mat descriptors_;
|
||||
};
|
||||
|
||||
/** @brief SuperPoint via LibTorch (RTAB-Map must be built with libtorch support). */
|
||||
//SuperPointTorch
|
||||
class RTABMAP_CORE_EXPORT SuperPointTorch : public Feature2D
|
||||
{
|
||||
public:
|
||||
SuperPointTorch(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~SuperPointTorch();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override { return kFeatureSuperPointTorch; }
|
||||
virtual bool isGpuAvailable() const override;
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const { return kFeatureSuperPointTorch; }
|
||||
|
||||
private:
|
||||
virtual std::vector<cv::KeyPoint> generateKeypointsImpl(const cv::Mat & image, const cv::Rect & roi, const cv::Mat & mask = cv::Mat()) override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
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<SPDetector> superPoint_;
|
||||
|
||||
@@ -693,20 +631,19 @@ private:
|
||||
bool cuda_;
|
||||
};
|
||||
|
||||
/** @brief SuperPoint (rpautrat) via Torch + Python (RTAB-Map must be built with libtorch and Python support). */
|
||||
//SuperPointRpautrat
|
||||
class RTABMAP_CORE_EXPORT SuperPointRpautrat : public Feature2D
|
||||
{
|
||||
public:
|
||||
SuperPointRpautrat(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~SuperPointRpautrat();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override { return kFeatureSuperPointRpautrat; }
|
||||
virtual bool isGpuAvailable() const override;
|
||||
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()) override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
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_;
|
||||
|
||||
@@ -719,18 +656,18 @@ private:
|
||||
bool cuda_;
|
||||
};
|
||||
|
||||
/** @brief GFTT corners + DAISY descriptors (OpenCV 3+ xfeatures2d). */
|
||||
//GFTT_DAISY
|
||||
class RTABMAP_CORE_EXPORT GFTT_DAISY : public GFTT
|
||||
{
|
||||
public:
|
||||
GFTT_DAISY(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~GFTT_DAISY();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureGfttDaisy;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureGfttDaisy;}
|
||||
|
||||
private:
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
|
||||
|
||||
private:
|
||||
bool orientationNormalized_;
|
||||
@@ -743,18 +680,18 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
/** @brief SURF detector + DAISY descriptors (OpenCV 3+ xfeatures2d). */
|
||||
//SURF_DAISY
|
||||
class RTABMAP_CORE_EXPORT SURF_DAISY : public SURF
|
||||
{
|
||||
public:
|
||||
SURF_DAISY(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~SURF_DAISY();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters) override;
|
||||
virtual Feature2D::Type getType() const override {return kFeatureSurfDaisy;}
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
virtual Feature2D::Type getType() const {return kFeatureSurfDaisy;}
|
||||
|
||||
private:
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const override;
|
||||
virtual cv::Mat generateDescriptorsImpl(const cv::Mat & image, std::vector<cv::KeyPoint> & keypoints) const;
|
||||
|
||||
private:
|
||||
bool orientationNormalized_;
|
||||
|
||||
@@ -34,117 +34,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
class NanoFlannIndex;
|
||||
|
||||
/**
|
||||
* @class FlannIndex
|
||||
* @brief Nearest neighbor index over a set of features
|
||||
*
|
||||
* Wraps the search structures of the vendored rtflann and nanoflann libraries
|
||||
* behind one interface, the structure being chosen with flann_algorithm_t at
|
||||
* build time. Used for the visual word dictionary (VWDictionary) and for the
|
||||
* 2D point searches of visual registration (RegistrationVis).
|
||||
*
|
||||
* The features are not copied: the index refers to the matrices it is given and
|
||||
* keeps them alive, cv::Mat data being reference counted, so they must not be
|
||||
* modified in place while it is in use. Every point it holds is
|
||||
* designated by an index, assigned in the order the points were added and
|
||||
* stable for the lifetime of the index: removePoint() leaves a hole rather
|
||||
* than renumbering the points after it.
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT FlannIndex
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @enum flann_algorithm_t
|
||||
* @brief The index structure built by buildIndex()
|
||||
*
|
||||
* The values under 8 are forwarded from rtflann's own enum and have to
|
||||
* match it (see src/rtflann/defines.h); the nanoflann ones are
|
||||
* rtabmap-specific and kept outside its range (0-7, 254, 255). A value is
|
||||
* written in the serialized index header and checked back on load, so none
|
||||
* of them may be renumbered.
|
||||
*
|
||||
* The nanoflann structures take float features only (nanoflann has no
|
||||
* Hamming metric) and search exactly, ignoring "checks". That makes them
|
||||
* the fastest ones for 2D and 3D points, and the wrong ones for
|
||||
* descriptors: an exact search visits more and more of the tree as the
|
||||
* dimension grows, down to being as slow as an exhaustive search. Prefer
|
||||
* the approximate rtflann kd-trees for those.
|
||||
*/
|
||||
// A forward of the internal enum, indexes should match. See src/rtflann/defines.h
|
||||
enum flann_algorithm_t
|
||||
{
|
||||
FLANN_INDEX_LINEAR = 0, ///< Exhaustive search
|
||||
FLANN_INDEX_KDTREE = 1, ///< 4 randomized kd-trees, searched approximately
|
||||
FLANN_INDEX_KDTREE_SINGLE = 4, ///< Single kd-tree, searched exactly
|
||||
FLANN_INDEX_LSH = 6, ///< Locality-Sensitive Hashing (binary descriptors)
|
||||
|
||||
/// nanoflann kd-tree. With a rebalancing factor of 1 it is built once,
|
||||
/// which is the cheapest to build and to search; over 1 it is the
|
||||
/// weight-balanced tree accepting addPoints()/removePoint(), which
|
||||
/// cannot be serialized while some of its points are removed.
|
||||
NANOFLANN_INDEX_KDTREE_SINGLE = 100,
|
||||
FLANN_INDEX_LINEAR = 0,
|
||||
FLANN_INDEX_KDTREE = 1,
|
||||
FLANN_INDEX_KDTREE_SINGLE = 4,
|
||||
FLANN_INDEX_LSH = 6,
|
||||
};
|
||||
|
||||
FlannIndex();
|
||||
virtual ~FlannIndex();
|
||||
|
||||
/** @brief Drop the index and everything it holds, back to the state of a new one. */
|
||||
void release();
|
||||
|
||||
/**
|
||||
* @brief Serialize the index, to be given back to loadIndex()
|
||||
* @param computeChecksum Add a checksum of the indexed features to the
|
||||
* data, which loadIndex() compares against the features it is given
|
||||
* @return The serialized index, empty when there is nothing to serialize or
|
||||
* when the structure in use cannot be
|
||||
*
|
||||
* The format depends on the architecture and on the versions of the
|
||||
* vendored libraries: loadIndex() refuses an index it cannot read, leaving
|
||||
* it to be rebuilt.
|
||||
*/
|
||||
std::vector<unsigned char> serializeIndex(bool computeChecksum = true) const;
|
||||
|
||||
/** @return Number of indexed features, the removed ones excluded. */
|
||||
size_t indexedFeatures() const;
|
||||
|
||||
/**
|
||||
* @return Bytes used by the index, the features themselves excluded as
|
||||
* they are only referred to.
|
||||
*/
|
||||
// return Bytes
|
||||
size_t memoryUsed() const;
|
||||
|
||||
/**
|
||||
* @brief Build the index over the given features, releasing any previous one
|
||||
* @param algorithm The structure to build
|
||||
* @param features One feature per row, CV_32FC1 or, for the rtflann
|
||||
* structures only, CV_8UC1 for binary descriptors (Hamming distance)
|
||||
* @param useDistanceL1 Search with the L1 distance instead of L2, ignored
|
||||
* by LSH and by the binary descriptors
|
||||
* @param rebalancingFactor Fraction (factor-1)/factor of the index that can
|
||||
* be left removed before it is rebuilt, e.g. half of it for 2. Set
|
||||
* to 1 to never rebuild it.
|
||||
*/
|
||||
// Note that useDistanceL1 doesn't have any effect if LSH is used
|
||||
void buildIndex(
|
||||
flann_algorithm_t algorithm,
|
||||
const cv::Mat & features,
|
||||
bool useDistanceL1 = false,
|
||||
float rebalancingFactor = 2.0f);
|
||||
|
||||
/**
|
||||
* @brief Load an index serialized by serializeIndex(), releasing any previous one
|
||||
* @param indexData The serialized index
|
||||
* @param algorithm The structure it was built with
|
||||
* @param features The very same features it was built with, in the same
|
||||
* order: the index refers to them by their row
|
||||
* @param useDistanceL1 The distance it was built with
|
||||
* @param rebalancingFactor See buildIndex(). The serialized data carries the
|
||||
* one the index was built with, which is deprecated and ignored:
|
||||
* this one is used instead.
|
||||
* @param errorMsg Filled with what didn't match when the index is refused
|
||||
* @return False if the data doesn't correspond to the given features and
|
||||
* parameters, in which case the index is left released
|
||||
*/
|
||||
// Return false if the indexData doesn't correspond to expected features used and parameters.
|
||||
bool loadIndex(
|
||||
const std::vector<unsigned char> & indexData,
|
||||
flann_algorithm_t algorithm,
|
||||
@@ -152,7 +71,6 @@ public:
|
||||
bool useDistanceL1 = false,
|
||||
float rebalancingFactor = 2.0f,
|
||||
std::string * errorMsg = NULL);
|
||||
/** @brief Load an index from a raw buffer, see the overload above. */
|
||||
bool loadIndex(
|
||||
const unsigned char * indexData,
|
||||
size_t indexDataSize,
|
||||
@@ -162,46 +80,16 @@ public:
|
||||
float rebalancingFactor = 2.0f,
|
||||
std::string * errorMsg = NULL);
|
||||
|
||||
/** @return Whether an index has been built or loaded. */
|
||||
bool isBuilt();
|
||||
|
||||
/** @return Type of the indexed features (CV_32FC1 or CV_8UC1). */
|
||||
int featuresType() const {return featuresType_;}
|
||||
/** @return Dimension of the indexed features. */
|
||||
int featuresDim() const {return featuresDim_;}
|
||||
|
||||
/**
|
||||
* @brief Add features to the index
|
||||
* @param features One feature per row, of the type and dimension the index
|
||||
* was built with
|
||||
* @return The index assigned to each of them, empty when the structure
|
||||
* doesn't accept points after it is built
|
||||
*/
|
||||
std::vector<unsigned int> addPoints(const cv::Mat & features);
|
||||
|
||||
/**
|
||||
* @brief Remove an indexed feature, by the index addPoints() gave for it
|
||||
*
|
||||
* The feature is only marked as removed: it is skipped by the searches, but
|
||||
* keeps taking memory until the index is rebuilt (see the rebalancing
|
||||
* factor of buildIndex()). Not supported by every structure.
|
||||
*/
|
||||
void removePoint(unsigned int index);
|
||||
|
||||
/**
|
||||
* @brief Search the k nearest neighbors of each query
|
||||
* @param query One feature per row, of the type and dimension the index was
|
||||
* built with
|
||||
* @param indices Neighbors found, one query per row, CV_32SC1. The
|
||||
* neighbors that couldn't be found are set to -1.
|
||||
* @param dists Their squared distances, CV_32FC1, or CV_32SC1 for the
|
||||
* Hamming distances of binary descriptors
|
||||
* @param knn Number of neighbors to search for
|
||||
* @param checks Number of leaves an approximate search visits, the exact
|
||||
* structures ignoring it
|
||||
* @param eps Search for eps-approximate neighbors
|
||||
* @param sorted Give the neighbors back by increasing distance
|
||||
*/
|
||||
// return squared distances (indices should be casted in size_t)
|
||||
void knnSearch(
|
||||
const cv::Mat & query,
|
||||
cv::Mat & indices,
|
||||
@@ -211,21 +99,7 @@ public:
|
||||
float eps = 0.0,
|
||||
bool sorted = true) const;
|
||||
|
||||
/**
|
||||
* @brief Search the neighbors of each query within a radius
|
||||
* @param query One feature per row, of the type and dimension the index was
|
||||
* built with
|
||||
* @param indices Neighbors found, one vector per query
|
||||
* @param dists Their squared distances, one vector per query
|
||||
* @param radius Search radius, squared internally: it is a distance, not a
|
||||
* squared one
|
||||
* @param maxNeighbors Maximum number of neighbors per query, the nearest
|
||||
* ones being kept. 0 for all of them.
|
||||
* @param checks Number of leaves an approximate search visits, the exact
|
||||
* structures ignoring it
|
||||
* @param eps Search for eps-approximate neighbors
|
||||
* @param sorted Give the neighbors back by increasing distance
|
||||
*/
|
||||
// return squared distances
|
||||
void radiusSearch(
|
||||
const cv::Mat & query,
|
||||
std::vector<std::vector<size_t> > & indices,
|
||||
@@ -237,8 +111,7 @@ public:
|
||||
bool sorted = true) const;
|
||||
|
||||
private:
|
||||
void * index_; // rtflann backend
|
||||
NanoFlannIndex * nanoIndex_; // nanoflann backend, only one of the two is set
|
||||
void * index_;
|
||||
unsigned int nextIndex_;
|
||||
int featuresType_;
|
||||
int featuresDim_;
|
||||
|
||||
@@ -32,21 +32,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class GPS
|
||||
* @brief WGS84 GPS fix attached to a sensor sample or graph node.
|
||||
*
|
||||
* Stores a timestamped geographic position with horizontal accuracy and bearing.
|
||||
* Values are persisted in the database as six doubles in the order
|
||||
* @c stamp, @c longitude, @c latitude, @c altitude, @c error, @c bearing.
|
||||
*
|
||||
* @see SensorData::gps()
|
||||
* @see GeodeticCoords
|
||||
*/
|
||||
class GPS
|
||||
{
|
||||
public:
|
||||
/** @brief Default-constructs a fix at the origin with zero stamp and error. */
|
||||
GPS():
|
||||
stamp_(0.0),
|
||||
longitude_(0.0),
|
||||
@@ -55,15 +43,6 @@ public:
|
||||
error_(0.0),
|
||||
bearing_(0.0)
|
||||
{}
|
||||
/**
|
||||
* @brief Constructs a GPS fix.
|
||||
* @param stamp Timestamp in seconds.
|
||||
* @param longitude Longitude in decimal degrees (DD, east positive).
|
||||
* @param latitude Latitude in decimal degrees (DD, north positive).
|
||||
* @param altitude Altitude in meters above the WGS84 ellipsoid.
|
||||
* @param error Horizontal position error radius in meters.
|
||||
* @param bearing Heading in degrees, 0 = north, increasing clockwise.
|
||||
*/
|
||||
GPS(const double & stamp,
|
||||
const double & longitude,
|
||||
const double & latitude,
|
||||
@@ -77,22 +56,13 @@ public:
|
||||
error_(error),
|
||||
bearing_(bearing)
|
||||
{}
|
||||
/** @return Timestamp in seconds. */
|
||||
const double & stamp() const {return stamp_;}
|
||||
/** @return Longitude in decimal degrees (DD). */
|
||||
const double & longitude() const {return longitude_;}
|
||||
/** @return Latitude in decimal degrees (DD). */
|
||||
const double & latitude() const {return latitude_;}
|
||||
/** @return Altitude in meters. */
|
||||
const double & altitude() const {return altitude_;}
|
||||
/** @return Horizontal position error in meters. */
|
||||
const double & error() const {return error_;}
|
||||
/** @return Bearing in degrees (north = 0, clockwise). */
|
||||
const double & bearing() const {return bearing_;}
|
||||
|
||||
/**
|
||||
* @return @ref GeodeticCoords built from @ref latitude(), @ref longitude() and @ref altitude().
|
||||
*/
|
||||
GeodeticCoords toGeodeticCoords() const {return GeodeticCoords(latitude_, longitude_, altitude_);}
|
||||
private:
|
||||
double stamp_; // in sec
|
||||
|
||||
@@ -49,61 +49,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class GeodeticCoords
|
||||
* @brief WGS84 geodetic latitude, longitude and altitude with coordinate conversions.
|
||||
*
|
||||
* Conversions follow the WGS84 reference ellipsoid (MRPT-derived implementation).
|
||||
* ENU frames use east = X, north = Y, up = Z relative to a local origin.
|
||||
*
|
||||
* @see GPS::toGeodeticCoords()
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT GeodeticCoords
|
||||
{
|
||||
public:
|
||||
/** @brief Default-constructs coordinates at (0°, 0°, 0 m). */
|
||||
GeodeticCoords();
|
||||
/**
|
||||
* @brief Constructs geodetic coordinates.
|
||||
* @param latitude Latitude in decimal degrees (DD, north positive).
|
||||
* @param longitude Longitude in decimal degrees (DD, east positive).
|
||||
* @param altitude Altitude in meters above the WGS84 ellipsoid.
|
||||
*/
|
||||
GeodeticCoords(double latitude, double longitude, double altitude);
|
||||
|
||||
/** @return Latitude in decimal degrees. */
|
||||
const double & latitude() const {return latitude_;}
|
||||
/** @return Longitude in decimal degrees. */
|
||||
const double & longitude() const {return longitude_;}
|
||||
/** @return Altitude in meters. */
|
||||
const double & altitude() const {return altitude_;}
|
||||
|
||||
/** @brief Sets latitude in decimal degrees. */
|
||||
void setLatitude(const double & value) {latitude_ = value;}
|
||||
/** @brief Sets longitude in decimal degrees. */
|
||||
void setLongitude(const double & value) {longitude_ = value;}
|
||||
/** @brief Sets altitude in meters. */
|
||||
void setAltitude(const double & value) {altitude_ = value;}
|
||||
|
||||
/** @return ECEF geocentric coordinates (meters) in the WGS84 frame. */
|
||||
cv::Point3d toGeocentric_WGS84() const;
|
||||
/**
|
||||
* @return ENU offset (meters) from @p origin to this point.
|
||||
* East = X, north = Y, up = Z.
|
||||
*/
|
||||
cv::Point3d toENU_WGS84(const GeodeticCoords & origin) const; // East=X, North=Y
|
||||
|
||||
/** @brief Sets this point from ECEF geocentric @p geocentric coordinates. */
|
||||
void fromGeocentric_WGS84(const cv::Point3d& geocentric);
|
||||
/** @brief Sets this point from an ENU offset relative to @p origin. */
|
||||
void fromENU_WGS84(const cv::Point3d & enu, const GeodeticCoords & origin);
|
||||
|
||||
/** @return ECEF geocentric coordinates of ENU point @p enu relative to @p origin. */
|
||||
static cv::Point3d ENU_WGS84ToGeocentric_WGS84(const cv::Point3d & enu, const GeodeticCoords & origin);
|
||||
/**
|
||||
* @return ENU offset from geocentric @p origin_geocentric_WGS84 to @p geocentric_WGS84.
|
||||
* @param origin Geodetic origin used to define the local ENU basis.
|
||||
*/
|
||||
static cv::Point3d Geocentric_WGS84ToENU_WGS84(
|
||||
const cv::Point3d & geocentric_WGS84,
|
||||
const cv::Point3d & origin_geocentric_WGS84,
|
||||
|
||||
@@ -37,31 +37,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class GlobalMap
|
||||
* @brief Abstract base for assembling per-node @ref LocalGrid data into a global map.
|
||||
*
|
||||
* Subclasses (@ref OccupancyGrid, @ref OctoMap, @ref GridMap, @ref CloudMap) implement
|
||||
* @ref assemble() to merge new node poses from @ref LocalGridCache into their representation.
|
||||
*
|
||||
* @ref update() decides which poses need assembly (not yet in @ref addedNodes(), present in
|
||||
* cache, id > 0) and may call @ref clear() when @ref fullUpdateNeeded() detects graph
|
||||
* optimization or a disjoint pose set (see @ref Parameters::kGridGlobalUpdateError()).
|
||||
*
|
||||
* Log-odds helpers @ref logodds() and @ref probability() convert between occupancy
|
||||
* probability and the internal log-odds representation used for global hit/miss/clamping
|
||||
* parameters (GridGlobal/Prob*).
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT GlobalMap
|
||||
{
|
||||
public:
|
||||
/** @brief Converts probability in (0, 1) to log-odds. */
|
||||
inline static float logodds(double probability)
|
||||
{
|
||||
return (float) log(probability/(1-probability));
|
||||
}
|
||||
|
||||
/** @brief Converts log-odds back to probability in (0, 1). */
|
||||
inline static double probability(double logodds)
|
||||
{
|
||||
return 1. - ( 1. / (1. + exp(logodds)));
|
||||
@@ -70,66 +53,31 @@ public:
|
||||
public:
|
||||
virtual ~GlobalMap();
|
||||
|
||||
/**
|
||||
* @brief True if the map should be rebuilt from cache (loop closure or disjoint graph).
|
||||
*
|
||||
* Compares @p poses to @ref addedNodes() using @ref getUpdateError(), which is set from
|
||||
* **Grid/GlobalUpdateError** (`Parameters::kGridGlobalUpdateError()`): if any assembled
|
||||
* node's pose moved farther than that threshold (meters), or if none of the assembled
|
||||
* nodes appear in @p poses, returns true and @ref update() will call @ref clear() first.
|
||||
*
|
||||
* @param poses Current graph poses (node id → transform).
|
||||
*/
|
||||
bool fullUpdateNeeded(const std::map<int, Transform> & poses) const;
|
||||
bool update(const std::map<int, Transform> & poses); // return true if map has changed
|
||||
|
||||
/**
|
||||
* @brief Incrementally assemble new nodes from @p poses.
|
||||
* @param poses Graph poses; ids > 0 with a cached @ref LocalGrid are candidates.
|
||||
* @return True if @ref assemble() was called (at least one new pose processed).
|
||||
*/
|
||||
bool update(const std::map<int, Transform> & poses);
|
||||
|
||||
/** @brief Clears assembled nodes and grid bounds; does not clear @ref LocalGridCache. */
|
||||
virtual void clear();
|
||||
|
||||
/** @return Grid cell size in meters (Grid/CellSize). */
|
||||
float getCellSize() const {return cellSize_;}
|
||||
/** @return Pose change threshold for full rebuild (Grid/GlobalUpdateError). */
|
||||
float getUpdateError() const {return updateError_;}
|
||||
/** @return Poses of nodes already assembled into the global map. */
|
||||
const std::map<int, Transform> & addedNodes() const {return addedNodes_;}
|
||||
|
||||
/** @brief 2D grid minimum (x, y) in meters. */
|
||||
void getGridMin(double & x, double & y) const {x=minValues_[0];y=minValues_[1];}
|
||||
/** @brief 2D grid maximum (x, y) in meters. */
|
||||
void getGridMax(double & x, double & y) const {x=maxValues_[0];y=maxValues_[1];}
|
||||
/** @brief 3D grid minimum (x, y, z) in meters. */
|
||||
void getGridMin(double & x, double & y, double & z) const {x=minValues_[0];y=minValues_[1];z=minValues_[2];}
|
||||
/** @brief 3D grid maximum (x, y, z) in meters. */
|
||||
void getGridMax(double & x, double & y, double & z) const {x=maxValues_[0];y=maxValues_[1];z=maxValues_[2];}
|
||||
|
||||
/** @brief Approximate memory used by assembled-node bookkeeping (bytes). */
|
||||
virtual unsigned long getMemoryUsed() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructs the base map; subclasses call from their constructor.
|
||||
* @param cache Non-null cache of per-node local grids.
|
||||
* @param parameters Optional Grid/ and GridGlobal/ parameter overrides.
|
||||
*/
|
||||
GlobalMap(const LocalGridCache * cache, const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/** @brief Subclass hook: merge @p newPoses into the global map; call @ref addAssembledNode(). */
|
||||
virtual void assemble(const std::list<std::pair<int, Transform> > & newPoses) = 0;
|
||||
|
||||
/** @return Cached local grids keyed by node id. */
|
||||
const std::map<int, LocalGrid> & cache() const {return cache_->localGrids();}
|
||||
|
||||
/** @return Poses of nodes already assembled (same as @ref addedNodes()). */
|
||||
const std::map<int, Transform> & assembledNodes() const {return addedNodes_;}
|
||||
/** @return True if @p id is already in the assembled set. */
|
||||
bool isNodeAssembled(int id) {return addedNodes_.find(id) != addedNodes_.end();}
|
||||
/** @brief Records a successfully assembled node (ids <= 0 are ignored). */
|
||||
void addAssembledNode(int id, const Transform & pose);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -40,106 +40,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace rtabmap {
|
||||
class Memory;
|
||||
|
||||
/**
|
||||
* @namespace rtabmap::graph
|
||||
* @brief Pose-graph I/O, trajectory metrics, link utilities, and path planning.
|
||||
*
|
||||
* Functions operate on maps of signature ids to @ref Transform poses and
|
||||
* @ref Link constraints (typically stored as `std::multimap<int, Link>` keyed by
|
||||
* the source node id).
|
||||
*
|
||||
* Main groups:
|
||||
* - **I/O:** @ref exportPoses(), @ref importPoses(), @ref exportGPS()
|
||||
* - **Evaluation:** @ref calcKittiSequenceErrors(), @ref calcRelativeErrors(),
|
||||
* @ref calcRMSE(), @ref computeMaxGraphErrors()
|
||||
* - **Links:** @ref findLink(), @ref findLinks(), @ref filterLinks(),
|
||||
* @ref filterDuplicateLinks()
|
||||
* - **Spatial queries:** @ref findNearestNode(), @ref findNearestNodes(),
|
||||
* @ref frustumPosesFiltering(), @ref radiusPosesFiltering()
|
||||
* - **Planning:** @ref computePath(), @ref computePathLength(), @ref getPaths()
|
||||
*/
|
||||
namespace graph {
|
||||
|
||||
/**
|
||||
* @brief Writes poses (and optional constraints) to disk.
|
||||
* @param filePath Output path; extension may be appended from @p format.
|
||||
* @param format Export format:
|
||||
* - `0` Raw text (`.txt`): r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz
|
||||
* - `1` RGBD-SLAM format, in motion capture frame like the ground truth of RGB-D SLAM Dataset (requires @p stamps) : stamp x y z qx qy qz qw
|
||||
* - `10` Like `1` without coordinate-frame change (i.e., in base frame) : stamp x y z qx qy qz qw
|
||||
* - `11` Like `10` with landmark ids after positive ids : stamp x y z qx qy qz qw id
|
||||
* - `2` KITTI odometry format : r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz
|
||||
* - `3` TORO graph (requires @p constraints; uses @p parameters)
|
||||
* - `4` g2o (requires @p constraints; uses @p parameters)
|
||||
* @param poses Node id → pose.
|
||||
* @param constraints Required for formats `3` and `4`.
|
||||
* @param stamps Required for formats `1`, `10`, and `11` (same size as @p poses).
|
||||
* @param parameters Optional optimizer parameters for formats `3` and `4`.
|
||||
* @return False on I/O or validation error.
|
||||
*/
|
||||
////////////////////////////////////////////
|
||||
// Graph utilities
|
||||
////////////////////////////////////////////
|
||||
|
||||
bool RTABMAP_CORE_EXPORT exportPoses(
|
||||
const std::string & filePath,
|
||||
int format,
|
||||
int format, // 0=Raw (*.txt), 1=RGBD-SLAM motion capture (*.txt) (10=without change of coordinate frame, 11=10+ID), 2=KITTI (*.txt), 3=TORO (*.graph), 4=g2o (*.g2o)
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & constraints = std::multimap<int, Link>(),
|
||||
const std::map<int, double> & stamps = std::map<int, double>(),
|
||||
const ParametersMap & parameters = ParametersMap());
|
||||
const std::multimap<int, Link> & constraints = std::multimap<int, Link>(), // required for formats 3 and 4
|
||||
const std::map<int, double> & stamps = std::map<int, double>(), // required for format 1
|
||||
const ParametersMap & parameters = ParametersMap()); // optional for formats 3 and 4
|
||||
|
||||
/**
|
||||
* @brief Loads poses (and optional constraints) from disk.
|
||||
* @param filePath Input path.
|
||||
* @param format Import format:
|
||||
* - `0` Raw text: 3×4 matrix per line (`Transform::fromString()`)
|
||||
* - `1` RGBD-SLAM motion capture: stamp x y z qw qx qy qz (applies optical-frame conversion)
|
||||
* - `2` KITTI odometry: 3×4 matrix per line (applies optical-frame conversion)
|
||||
* - `3` TORO graph (fills @p constraints)
|
||||
* - `4` g2o (not supported yet)
|
||||
* - `5` NewCollege: stamp x y (2D; first pose is origin)
|
||||
* - `6` Malaga Urban GPS: 25-field `*_GPS.txt` line (local X/Y/Z)
|
||||
* - `7` St Lucia INS: 12-field log (GPS → local ENU + roll/pitch/yaw)
|
||||
* - `8` Karlsruhe: timestamp lat lon alt x y z roll pitch yaw (first pose is origin)
|
||||
* - `9` EuRoC MAV: stamp x y z qw qx qy qz vx vy vz vr vp vy ax ay az (17 CSV fields)
|
||||
* - `10` RGBD-SLAM like `1` without coordinate-frame change
|
||||
* - `11` RGBD-SLAM like `10` with node id as 9th field: stamp x y z qw qx qy qz id
|
||||
* - `12` RGBD Bonn dynamic dataset format (stamp + pose; Bonn-specific frame conversion)
|
||||
* @param poses Output node id → pose.
|
||||
* @param constraints Optional output links (format `3` only).
|
||||
* @param stamps Optional output timestamps (formats `1`, `5`–`9`, `10`–`12` when present in file).
|
||||
* @return False on I/O or parse error.
|
||||
*/
|
||||
bool RTABMAP_CORE_EXPORT importPoses(
|
||||
const std::string & filePath,
|
||||
int format,
|
||||
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::multimap<int, Link> * constraints = 0,
|
||||
std::map<int, double> * stamps = 0);
|
||||
std::multimap<int, Link> * constraints = 0, // optional for formats 3 and 4
|
||||
std::map<int, double> * stamps = 0); // optional for format 1 and 9
|
||||
|
||||
/**
|
||||
* @brief Exports GPS samples to a PLY point cloud.
|
||||
* @param filePath Output `.ply` path.
|
||||
* @param gpsValues Node id → @ref GPS fix.
|
||||
* @param rgba Point color (default opaque white).
|
||||
*/
|
||||
bool RTABMAP_CORE_EXPORT exportGPS(
|
||||
const std::string & filePath,
|
||||
const std::map<int, GPS> & gpsValues,
|
||||
unsigned int rgba = 0xFFFFFFFF);
|
||||
|
||||
/**
|
||||
* @brief KITTI odometry benchmark error over fixed trajectory segments.
|
||||
*
|
||||
* For each start pose (every 10 frames) and segment length in
|
||||
* {100, 200, …, 800} m along @p poses_gt, compares the relative transform
|
||||
* GT vs estimate and accumulates normalized errors. The returned values are
|
||||
* the mean over all valid segments.
|
||||
*
|
||||
* @param poses_gt Ground-truth poses in temporal order (one per frame).
|
||||
* @param poses_result Estimated poses (same length and ordering as @p poses_gt).
|
||||
* @param t_err Output mean translation error (%): segment translation error (m)
|
||||
* divided by segment length, averaged, then × 100.
|
||||
* @param r_err Output mean rotation error (deg/m): segment rotation error (rad)
|
||||
* divided by segment length, averaged, then converted to deg/m.
|
||||
* @see http://www.cvlibs.net/datasets/kitti/eval_odometry.php
|
||||
* Compute translation and rotation errors for KITTI datasets.
|
||||
* See http://www.cvlibs.net/datasets/kitti/eval_odometry.php.
|
||||
* @param poses_gt, Ground Truth poses
|
||||
* @param poses_result, Estimated poses
|
||||
* @param t_err, Output translation error (%)
|
||||
* @param r_err, Output rotation error (deg/m)
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT calcKittiSequenceErrors(
|
||||
const std::vector<Transform> &poses_gt,
|
||||
@@ -148,21 +81,11 @@ void RTABMAP_CORE_EXPORT calcKittiSequenceErrors(
|
||||
float & r_err);
|
||||
|
||||
/**
|
||||
* @brief Mean frame-to-frame relative pose error (RPE-style, one step).
|
||||
*
|
||||
* For each consecutive pair `(i, i+1)`, builds the relative motion in ground
|
||||
* truth and in the estimate, then measures how much they differ:
|
||||
* - translation: Euclidean distance between the two relative transforms (m)
|
||||
* - rotation: angle between the two relative transforms (rad → deg)
|
||||
*
|
||||
* Returns the arithmetic mean over all `N-1` pairs (`N` = trajectory length).
|
||||
* Unlike @ref calcKittiSequenceErrors(), there is no fixed segment length and
|
||||
* no path-length normalization.
|
||||
*
|
||||
* @param poses_gt Ground-truth poses in temporal order (one per frame).
|
||||
* @param poses_result Estimated poses (same length and ordering as @p poses_gt).
|
||||
* @param t_err Output mean translation error over consecutive pairs (m).
|
||||
* @param r_err Output mean rotation error over consecutive pairs (deg).
|
||||
* Compute average of translation and rotation errors between each poses.
|
||||
* @param poses_gt, Ground Truth poses
|
||||
* @param poses_result, Estimated poses
|
||||
* @param t_err, Output translation error (m)
|
||||
* @param r_err, Output rotation error (deg)
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT calcRelativeErrors (
|
||||
const std::vector<Transform> &poses_gt,
|
||||
@@ -171,39 +94,12 @@ void RTABMAP_CORE_EXPORT calcRelativeErrors (
|
||||
float & r_err);
|
||||
|
||||
/**
|
||||
* @brief Absolute trajectory error (ATE) with Sim(3)-style alignment (TUM RGB-D tool).
|
||||
*
|
||||
* Only poses whose id exists in both @p groundTruth and @p poses are compared.
|
||||
* An alignment transform @c t is estimated so that per-pose error is measured after
|
||||
* bringing the estimate into the reference frame:
|
||||
* - If more than five poses match: @c t from SVD on position correspondences
|
||||
* (estimate positions → ground-truth positions; z ignored when @p align2D is true).
|
||||
* - Otherwise: @c t = groundTruth[firstId] * poses[firstId]⁻¹ using the first matched id.
|
||||
*
|
||||
* For each matched pose, after `aligned = t * poses[id]`:
|
||||
* - **Translational error:** Euclidean distance between `aligned` and `groundTruth[id]` (m).
|
||||
* - **Rotational error:** Angle between the poses' +X axes (deg).
|
||||
*
|
||||
* The eight `@p translational_*` and `@p rotational_*` outputs are statistics over those
|
||||
* per-pose errors (all matched poses). They are set to `0` when no id matches.
|
||||
*
|
||||
* @param groundTruth Reference trajectory (node id → pose).
|
||||
* @param poses Estimated trajectory; ids not in @p groundTruth are skipped.
|
||||
* @param translational_rmse Root mean square of translational errors (m).
|
||||
* @param translational_mean Arithmetic mean of translational errors (m).
|
||||
* @param translational_median Middle sample in matched-pose iteration order (m).
|
||||
* @param translational_std Standard deviation of translational errors (m).
|
||||
* @param translational_min Minimum translational error (m).
|
||||
* @param translational_max Maximum translational error (m).
|
||||
* @param rotational_rmse Root mean square of rotational errors (deg).
|
||||
* @param rotational_mean Arithmetic mean of rotational errors (deg).
|
||||
* @param rotational_median Middle sample in matched-pose iteration order (deg).
|
||||
* @param rotational_std Standard deviation of rotational errors (deg).
|
||||
* @param rotational_min Minimum rotational error (deg).
|
||||
* @param rotational_max Maximum rotational error (deg).
|
||||
* @param align2D If true, alignment uses x/y only (z set to 0 for correspondence); 3D if false.
|
||||
* @return Alignment transform @c t applied as `t * poses[id]` before error computation.
|
||||
* @see https://vision.in.tum.de/data/datasets/rgbd-dataset
|
||||
* Compute root-mean-square error (RMSE) like the TUM RGBD
|
||||
* dataset's evaluation tool (absolute trajectory error).
|
||||
* See https://vision.in.tum.de/data/datasets/rgbd-dataset
|
||||
* @param groundTruth, Ground Truth poses
|
||||
* @param poses, Estimated poses
|
||||
* @return Gt to Map transform
|
||||
*/
|
||||
Transform RTABMAP_CORE_EXPORT calcRMSE(
|
||||
const std::map<int, Transform> &groundTruth,
|
||||
@@ -222,209 +118,94 @@ Transform RTABMAP_CORE_EXPORT calcRMSE(
|
||||
float & rotational_max,
|
||||
bool align2D = false);
|
||||
|
||||
/**
|
||||
* @brief Largest pose-graph constraint violations after optimization.
|
||||
*
|
||||
* For each non-self-referenced link (`from != to`), compares the relative pose implied by @p poses to the
|
||||
* link measurement and tracks the worst linear/angular error and error/std ratios.
|
||||
*/
|
||||
struct MaxGraphErrors
|
||||
{
|
||||
float linear=-1.0f; ///< Absolute linear error (m) of the worst link.
|
||||
float angular=-1.0f; ///< Absolute angular error (rad) of the worst link.
|
||||
float linearRatio=-1.0f; ///< linear / sqrt(trans variance) of the worst link.
|
||||
float angularRatio=-1.0f; ///< angular / sqrt(rot variance) of the worst link.
|
||||
Link linearLink; ///< Link with largest @ref linearRatio.
|
||||
Link angularLink; ///< Link with largest @ref angularRatio.
|
||||
float linear=-1.0f; // absolute error (m) of the link with maximum linear error
|
||||
float angular=-1.0f; // absolute error (rad) of the link with maximum angular error
|
||||
float linearRatio=-1.0f; // Ratio = absolute error (m) / linear std (m), of the link with maximum linear error
|
||||
float angularRatio=-1.0f; // Ratio = absolute error (rad) / angular std (rad), of the link with maximum angular error
|
||||
Link linearLink; // link with maximum linear error
|
||||
Link angularLink; // link with maximum angular error
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Finds the worst pose-graph constraint residuals after optimization.
|
||||
*
|
||||
* Iterates over @p links and, for each non-self-referenced edge (`from != to`):
|
||||
* 1. Looks up `T_from` and `T_to` in @p poses (returns default @ref MaxGraphErrors if
|
||||
* any endpoint pose is missing, null, or not invertible).
|
||||
* 2. Builds the relative pose implied by the optimized poses:
|
||||
* - Normal link: `t = T_from⁻¹ · T_to`
|
||||
* - Landmark (`from < 0`): `t = T_to⁻¹ · T_from`, link measurement inverted
|
||||
* 3. Compares `t` to the link transform:
|
||||
* - **Linear error:** max |Δx|, |Δy|, and |Δz| (z ignored when @p for3DoF is true).
|
||||
* - **Angular error:** full 3D angle between `t` and the link, or yaw-only if @p for3DoF;
|
||||
* skipped for @ref Link::kLandmark when the information matrix does not constrain yaw.
|
||||
* 4. Normalizes by link uncertainty: `error / sqrt(variance)` using the link information
|
||||
* matrix (largest diagonal variance for translation/rotation).
|
||||
*
|
||||
* The returned @ref MaxGraphErrors holds the link with the highest linear and angular
|
||||
* *ratios* (not necessarily the largest absolute error).
|
||||
*
|
||||
* @param poses Optimized node poses (must contain every `from` and `to` id used).
|
||||
* @param links Graph constraints (typically `std::multimap<int, Link>` keyed by `from`).
|
||||
* @param for3DoF If true, linear error uses x/y only and angular error compares yaw only.
|
||||
* @return @ref MaxGraphErrors; fields stay `-1` when no valid link was checked or on early abort.
|
||||
*/
|
||||
MaxGraphErrors RTABMAP_CORE_EXPORT computeMaxGraphErrors(
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
bool for3DoF = false);
|
||||
|
||||
/**
|
||||
* @brief Maximum information-matrix diagonal over odometry neighbor links.
|
||||
*
|
||||
* Scans @p links of type @ref Link::kNeighbor or @ref Link::kNeighborMerged and,
|
||||
* for each dof (x, y, z, roll, pitch, yaw), keeps the largest diagonal entry of
|
||||
* the 6×6 information matrix.
|
||||
*
|
||||
* @param links Graph constraints (multimap keyed by source id).
|
||||
* @return Six maximum information values, or an empty vector if no neighbor links exist.
|
||||
*/
|
||||
std::vector<double> RTABMAP_CORE_EXPORT getMaxOdomInf(const std::multimap<int, Link> & links);
|
||||
|
||||
/**
|
||||
* @brief Finds the first link from @p from to @p to in a multimap keyed by source id.
|
||||
*
|
||||
* Iterates all entries with key @p from and matches the destination (and optionally
|
||||
* @p type). When @p checkBothWays is true, also searches key @p to for a link back
|
||||
* to @p from.
|
||||
*
|
||||
* @param links Link multimap (`key` = source node id).
|
||||
* @param from Source node id.
|
||||
* @param to Destination node id.
|
||||
* @param checkBothWays If true, also match `to → from`.
|
||||
* @param type Required link type, or @ref Link::kUndef to accept any type.
|
||||
* @return Iterator to the link, or `links.end()` if not found.
|
||||
*/
|
||||
std::multimap<int, Link>::iterator RTABMAP_CORE_EXPORT findLink(
|
||||
std::multimap<int, Link> & links,
|
||||
int from,
|
||||
int to,
|
||||
bool checkBothWays = true,
|
||||
Link::Type type = Link::kUndef);
|
||||
/** @overload
|
||||
* `std::multimap<int, std::pair<int, Link::Type>>`. */
|
||||
std::multimap<int, std::pair<int, Link::Type> >::iterator RTABMAP_CORE_EXPORT findLink(
|
||||
std::multimap<int, std::pair<int, Link::Type> > & links,
|
||||
int from,
|
||||
int to,
|
||||
bool checkBothWays = true,
|
||||
Link::Type type = Link::kUndef);
|
||||
/** @overload
|
||||
* `std::multimap<int, int>`. */
|
||||
std::multimap<int, int>::iterator RTABMAP_CORE_EXPORT findLink(
|
||||
std::multimap<int, int> & links,
|
||||
int from,
|
||||
int to,
|
||||
bool checkBothWays = true);
|
||||
/** @overload
|
||||
* Const `std::multimap<int, Link>`. */
|
||||
std::multimap<int, Link>::const_iterator RTABMAP_CORE_EXPORT findLink(
|
||||
const std::multimap<int, Link> & links,
|
||||
int from,
|
||||
int to,
|
||||
bool checkBothWays = true,
|
||||
Link::Type type = Link::kUndef);
|
||||
/** @overload
|
||||
* Const `std::multimap<int, std::pair<int, Link::Type>>`. */
|
||||
std::multimap<int, std::pair<int, Link::Type> >::const_iterator RTABMAP_CORE_EXPORT findLink(
|
||||
const std::multimap<int, std::pair<int, Link::Type> > & links,
|
||||
int from,
|
||||
int to,
|
||||
bool checkBothWays = true,
|
||||
Link::Type type = Link::kUndef);
|
||||
/** @overload
|
||||
* Const `std::multimap<int, int>`. */
|
||||
std::multimap<int, int>::const_iterator RTABMAP_CORE_EXPORT findLink(
|
||||
const std::multimap<int, int> & links,
|
||||
int from,
|
||||
int to,
|
||||
bool checkBothWays = true);
|
||||
|
||||
/**
|
||||
* @brief Lists all links incident on node @p from.
|
||||
*
|
||||
* Outgoing links (`link.from() == from`) are returned as stored; for incoming links
|
||||
* (`link.to() == from`), the inverse link is returned so the pose of @p from is always
|
||||
* the source frame.
|
||||
*
|
||||
* @param links Graph constraints.
|
||||
* @param from Node id to query.
|
||||
* @return Incident links (may be empty).
|
||||
*/
|
||||
std::list<Link> RTABMAP_CORE_EXPORT findLinks(
|
||||
const std::multimap<int, Link> & links,
|
||||
int from);
|
||||
|
||||
/**
|
||||
* @brief Removes duplicate undirected links.
|
||||
*
|
||||
* Keeps the first occurrence of each `(from, to)` or `(to, from)` pair with the same
|
||||
* @ref Link::Type (see @ref findLink() with @p checkBothWays).
|
||||
*
|
||||
* @param links Input link multimap.
|
||||
* @return Copy without duplicates.
|
||||
*/
|
||||
std::multimap<int, Link> RTABMAP_CORE_EXPORT filterDuplicateLinks(
|
||||
const std::multimap<int, Link> & links);
|
||||
|
||||
/**
|
||||
* @brief Filters links by type or self-reference.
|
||||
*
|
||||
* - If @p filteredType is @ref Link::kSelfRefLink "kSelfRefLink": exclude self-references (`from == to`),
|
||||
* or include only them when @p inverted is true.
|
||||
* - Otherwise: exclude links of @p filteredType, or keep only that type when @p inverted is true.
|
||||
*
|
||||
* @param links Input links.
|
||||
* @param filteredType Type to filter, or @ref Link::kSelfRefLink for self-reference filtering.
|
||||
* @param inverted If true, keep the filtered set instead of removing it.
|
||||
* @return Filtered link container (same structure as input).
|
||||
* Return links not of type "filteredType". If inverted=true, return links of type "filteredType".
|
||||
*/
|
||||
std::multimap<int, Link> RTABMAP_CORE_EXPORT filterLinks(
|
||||
const std::multimap<int, Link> & links,
|
||||
Link::Type filteredType,
|
||||
bool inverted = false);
|
||||
/** @overload
|
||||
* For `std::map<int, Link>`. */
|
||||
/**
|
||||
* Return links not of type "filteredType". If inverted=true, return links of type "filteredType".
|
||||
*/
|
||||
std::map<int, Link> RTABMAP_CORE_EXPORT filterLinks(
|
||||
const std::map<int, Link> & links,
|
||||
Link::Type filteredType,
|
||||
bool inverted = false);
|
||||
|
||||
/**
|
||||
* @brief Keeps poses inside (or outside) a camera frustum.
|
||||
*
|
||||
* Transforms each pose position into the frustum defined by @p cameraPose using
|
||||
* @ref util3d::frustumFiltering() (this assumes the cameraPose includes the optical rotation of the camera (X right, Y down, Z forward).
|
||||
*
|
||||
* @param poses Input poses (null poses are skipped) in base frame (X forward, Y left, Z up),
|
||||
* @param cameraPose Frustum origin and orientation including the optical rotation of the camera (X right, Y down, Z forward).
|
||||
* @param horizontalFOV Horizontal field of view (deg); see @ref CameraModel::horizontalFOV().
|
||||
* @param verticalFOV Vertical field of view (deg); see @ref CameraModel::verticalFOV().
|
||||
* @param nearClipPlaneDistance Near clipping distance (m).
|
||||
* @param farClipPlaneDistance Far clipping distance (m).
|
||||
* @param negative If false, keep poses inside the frustum; if true, keep poses outside.
|
||||
* @return Subset of @p poses passing the filter.
|
||||
*/
|
||||
//Note: This assumes a coordinate system where X is forward, * Y is up, and Z is right.
|
||||
std::map<int, Transform> RTABMAP_CORE_EXPORT frustumPosesFiltering(
|
||||
const std::map<int, Transform> & poses,
|
||||
const Transform & cameraPose,
|
||||
float horizontalFOV = 45.0f,
|
||||
float verticalFOV = 45.0f,
|
||||
float horizontalFOV = 45.0f, // in degrees, xfov = atan((image_width/2)/fx)*2
|
||||
float verticalFOV = 45.0f, // in degrees, yfov = atan((image_height/2)/fy)*2
|
||||
float nearClipPlaneDistance = 0.1f,
|
||||
float farClipPlaneDistance = 100.0f,
|
||||
bool negative = false);
|
||||
|
||||
/**
|
||||
* @brief Subsamples poses that are spatially (and optionally angularly) redundant.
|
||||
*
|
||||
* For each pose not yet processed, finds all poses within @p radius (KD-tree). When
|
||||
* @p angle > 0, only poses whose +X axis differs by at most @p angle (rad) are grouped.
|
||||
* From each group, keeps one pose: the latest in map order if @p keepLatest, otherwise
|
||||
* the earliest. The first and last poses of the input map are always kept.
|
||||
*
|
||||
* @param poses Input trajectory (map iteration order defines “latest/oldest”).
|
||||
* @param radius Clustering radius (m); if `≤ 0` or fewer than three poses, returns @p poses unchanged.
|
||||
* @param angle Max heading difference within a cluster (rad); `0` ignores orientation.
|
||||
* @param keepLatest If true, keep the latest pose per cluster; otherwise the earliest.
|
||||
* @return Subsampled poses.
|
||||
* Get only the the most recent or older poses in the defined radius.
|
||||
* @param poses The poses
|
||||
* @param radius Radius (m) of the search for near neighbors
|
||||
* @param angle Maximum angle (rad, [0,PI]) of accepted neighbor nodes in the radius (0 means ignore angle)
|
||||
* @param keepLatest keep the latest node if true, otherwise the oldest node is kept
|
||||
* @return A map containing only most recent or older poses in the the defined radius
|
||||
*/
|
||||
std::map<int, Transform> RTABMAP_CORE_EXPORT radiusPosesFiltering(
|
||||
const std::map<int, Transform> & poses,
|
||||
@@ -433,55 +214,31 @@ std::map<int, Transform> RTABMAP_CORE_EXPORT radiusPosesFiltering(
|
||||
bool keepLatest = true);
|
||||
|
||||
/**
|
||||
* @brief Radius-neighbor clustering of poses.
|
||||
*
|
||||
* For each pose, inserts `(queryId, neighborId)` into the output for every other pose
|
||||
* within @p radius (and within @p angle of the query heading when @p angle > 0).
|
||||
*
|
||||
* @param poses Input poses.
|
||||
* @param radius Search radius (m); no pairs if `≤ 0` or fewer than two poses.
|
||||
* @param angle Max heading difference (rad); `0` ignores orientation.
|
||||
* @return Multimap of pose id → neighbor id (both ids from @p poses).
|
||||
* Get all neighbor nodes in a fixed radius around each pose.
|
||||
* @param poses The poses
|
||||
* @param radius Radius (m) of the search for near neighbors
|
||||
* @param angle Maximum angle (rad, [0,PI]) of accepted neighbor nodes in the radius (0 means ignore angle)
|
||||
* @return A map between each pose id and its neighbors found in the radius
|
||||
*/
|
||||
std::multimap<int, int> RTABMAP_CORE_EXPORT radiusPosesClustering(
|
||||
const std::map<int, Transform> & poses,
|
||||
float radius,
|
||||
float angle);
|
||||
|
||||
/**
|
||||
* @brief Reduces a pose graph into hyper-nodes and hyper-links.
|
||||
*
|
||||
* **Hyper-nodes:** clusters poses connected by non-neighbor loop-closure links.
|
||||
* Clustering starts from the largest id downward; each cluster is keyed by its parent
|
||||
* (hyper-node) id.
|
||||
*
|
||||
* **Hyper-links:** for each @ref Link::kNeighbor or @ref Link::kNeighborMerged link between
|
||||
* different clusters, builds one merged @ref Link along the shortest path through
|
||||
* intra-cluster closure links (Dijkstra with unit cost).
|
||||
*
|
||||
* @param poses Input optimized poses.
|
||||
* @param links Input constraints (should be unique per directed edge for closure links).
|
||||
* @param hyperNodes Output `hyperNodeId → childPoseId` membership.
|
||||
* @param hyperLinks Output links between hyper-nodes (one per hyper-edge, most recent kept).
|
||||
*/
|
||||
void reduceGraph(
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
std::multimap<int, int> & hyperNodes,
|
||||
std::multimap<int, int> & hyperNodes, //<parent ID, child ID>
|
||||
std::multimap<int, Link> & hyperLinks);
|
||||
|
||||
/**
|
||||
* @brief A* shortest path on a pose graph with Euclidean edge costs.
|
||||
*
|
||||
* Edge cost between adjacent nodes is the Euclidean distance between their poses in
|
||||
* @p poses. Uses `costSoFar + distToEnd` where `distToEnd` is the distance to the goal pose.
|
||||
*
|
||||
* @param poses Node id → pose (must contain every node reached by @p links).
|
||||
* @param links Directed edges (`from` → `to`) keyed by source id.
|
||||
* @param from Start node id.
|
||||
* @param to Goal node id.
|
||||
* @param updateNewCosts If true, use a multimap queue that can decrease keys when a shorter path is found.
|
||||
* @return Ordered path from @p from to @p to (inclusive) with poses; empty if unreachable.
|
||||
* Perform A* path planning in the graph.
|
||||
* @param poses The graph's poses
|
||||
* @param links The graph's links (from node id -> to node id)
|
||||
* @param from initial node
|
||||
* @param to final node
|
||||
* @param updateNewCosts Keep up-to-date costs while traversing the graph.
|
||||
* @return the path ids from id "from" to id "to" including initial and final nodes.
|
||||
*/
|
||||
std::list<std::pair<int, Transform> > RTABMAP_CORE_EXPORT computePath(
|
||||
const std::map<int, rtabmap::Transform> & poses,
|
||||
@@ -491,40 +248,14 @@ std::list<std::pair<int, Transform> > RTABMAP_CORE_EXPORT computePath(
|
||||
bool updateNewCosts = false);
|
||||
|
||||
/**
|
||||
* @brief Single-source graph depth via BFS.
|
||||
*
|
||||
* Runs one breadth-first search from @p from and returns, for every reached
|
||||
* node, its depth from @p from (i.e., the number of links on the shortest
|
||||
* path, @p from itself having depth 0).
|
||||
*
|
||||
* @note The depth is a number of hops, not a distance: it counts links, and the poses
|
||||
* of the nodes play no part in it. The path it stands for is thus not the one
|
||||
* @ref computePath() returns, which minimizes the Euclidean length instead and
|
||||
* can walk more links to save meters.
|
||||
*
|
||||
* @param links Directed edges (`from` → `to`) keyed by source id.
|
||||
* @param from Start node id.
|
||||
* @param maxDepth If > 0, only nodes with depth ≤ this value are returned (the
|
||||
* frontier is not expanded further); `0` explores the whole component.
|
||||
* @return Node id → depth mapping.
|
||||
*/
|
||||
std::map<int, int> RTABMAP_CORE_EXPORT computePathDepths(
|
||||
const std::multimap<int, int> & links,
|
||||
int from,
|
||||
int maxDepth = 0);
|
||||
|
||||
/**
|
||||
* @brief Dijkstra shortest path on link constraints.
|
||||
*
|
||||
* Explores outgoing links keyed by `link.from()`. Edge cost is `1` when
|
||||
* @p useSameCostForAllLinks is true, otherwise the translation norm of the link transform.
|
||||
*
|
||||
* @param links Constraints keyed by source node id.
|
||||
* @param from Start node id.
|
||||
* @param to Goal node id.
|
||||
* @param updateNewCosts If true, allow cost improvements on open nodes.
|
||||
* @param useSameCostForAllLinks If true, unit edge cost; else use `link.transform().getNorm()`.
|
||||
* @return Node ids from @p from to @p to (inclusive); empty if unreachable.
|
||||
* Perform Dijkstra path planning in the graph.
|
||||
* @param poses The graph's poses
|
||||
* @param links The graph's links (from node id -> to node id)
|
||||
* @param from initial node
|
||||
* @param to final node
|
||||
* @param updateNewCosts Keep up-to-date costs while traversing the graph.
|
||||
* @param useSameCostForAllLinks Ignore distance between nodes
|
||||
* @return the path ids from id "from" to id "to" including initial and final nodes.
|
||||
*/
|
||||
std::list<int> RTABMAP_CORE_EXPORT computePath(
|
||||
const std::multimap<int, Link> & links,
|
||||
@@ -534,36 +265,13 @@ std::list<int> RTABMAP_CORE_EXPORT computePath(
|
||||
bool useSameCostForAllLinks = false);
|
||||
|
||||
/**
|
||||
* @brief Dijkstra path through the live @ref Memory pose graph.
|
||||
*
|
||||
* Loads links from @ref Memory (optionally from the database), chains transforms along
|
||||
* the chosen path, and returns the accumulated poses. Self-referenced links are skipped.
|
||||
*
|
||||
* By default (`linearVelocity` and `angularVelocity` ≤ 0), edge cost is translation
|
||||
* distance (m) only. When set > 0, costs are expressed in seconds of motion:
|
||||
* - @p linearVelocity adds `linkTranslation / linearVelocity` (time to drive the edge at
|
||||
* that speed). Used alone it scales every edge by the same factor, so the **shortest path
|
||||
* is unchanged**; set it to your robot’s typical forward speed (e.g. `0.5` m/s) when you
|
||||
* also use @p angularVelocity so translation and rotation costs are comparable.
|
||||
* - @p angularVelocity adds `headingMismatch / angularVelocity`, where heading mismatch is
|
||||
* the angle between the displacement to the next node and that node’s forward (+X) axis.
|
||||
* This is what changes which path is chosen: a chain followed **mostly forward** (small
|
||||
* mismatch) can beat a shorter route through loop closures that require large reorientations
|
||||
* (e.g. `angularVelocity = 1.0` rad/s with `linearVelocity = 0.5` m/s).
|
||||
* With @p angularVelocity > 0 and @p linearVelocity ≤ 0, translation is ignored and the
|
||||
* path minimizes heading mismatch only (forward-following paths, regardless of distance).
|
||||
* This can help loop-closure detection when the map was built with a forward-facing camera:
|
||||
* the path stays aligned with how places were observed while driving forward.
|
||||
*
|
||||
* @param fromId Start signature id (`≥ 0`).
|
||||
* @param toId Goal signature id (`≠ 0`).
|
||||
* @param memory Graph memory (must not be null).
|
||||
* @param lookInDatabase If true, load links from the database when not already in RAM.
|
||||
* @param updateNewCosts If true, allow cost improvements on open nodes.
|
||||
* @param linearVelocity If > 0, add `translationNorm / linearVelocity` to edge cost (m/s).
|
||||
* @param angularVelocity If > 0, add rotation time from motion direction change (rad/s).
|
||||
* @param ignoreDirectLinks If true, skip the direct edge between @p fromId and @p toId.
|
||||
* @return Path as `(nodeId, pose)` pairs; first pose is identity at @p fromId. Empty if unreachable.
|
||||
* Perform Dijkstra path planning in the graph.
|
||||
* @param fromId initial node
|
||||
* @param toId final node
|
||||
* @param memory The graph's memory
|
||||
* @param lookInDatabase check links in database
|
||||
* @param updateNewCosts Keep up-to-date costs while traversing the graph.
|
||||
* @return the path ids from id "fromId" to id "toId" including initial and final nodes (Identity pose for the first node).
|
||||
*/
|
||||
std::list<std::pair<int, Transform> > RTABMAP_CORE_EXPORT computePath(
|
||||
int fromId,
|
||||
@@ -571,19 +279,16 @@ std::list<std::pair<int, Transform> > RTABMAP_CORE_EXPORT computePath(
|
||||
const Memory * memory,
|
||||
bool lookInDatabase = true,
|
||||
bool updateNewCosts = false,
|
||||
float linearVelocity = 0.0f,
|
||||
float angularVelocity = 0.0f,
|
||||
float linearVelocity = 0.0f, // m/sec
|
||||
float angularVelocity = 0.0f, // rad/sec
|
||||
bool ignoreDirectLinks = false);
|
||||
|
||||
/**
|
||||
* @brief Id of the nearest pose to @p targetPose.
|
||||
*
|
||||
* Wrapper around @ref findNearestNodes() with `radius=0`, `k=1` (1-NN in 3D).
|
||||
*
|
||||
* @param poses Nodes to search.
|
||||
* @param targetPose Query position (x, y, z only; orientation is not used).
|
||||
* @param distance If not null, set to the squared Euclidean distance of the match.
|
||||
* @return Closest node id, or `0` if @p poses is empty.
|
||||
* Find the nearest node of the target pose
|
||||
* @param nodes the nodes to search for
|
||||
* @param targetPose the target pose to search around
|
||||
* @param distance squared distance of the nearest node found (optional)
|
||||
* @return the node id.
|
||||
*/
|
||||
int RTABMAP_CORE_EXPORT findNearestNode(
|
||||
const std::map<int, rtabmap::Transform> & poses,
|
||||
@@ -591,18 +296,12 @@ int RTABMAP_CORE_EXPORT findNearestNode(
|
||||
float * distance = 0);
|
||||
|
||||
/**
|
||||
* @brief Spatial neighbors of a node (KD-tree radius or k-NN search).
|
||||
*
|
||||
* @p nodeId is removed from the search set. Requires `radius > 0` or `k > 0`.
|
||||
* When `radius > 0`, returns all poses within @p radius (up to @p k if `k > 0`).
|
||||
* When `radius == 0`, returns the @p k nearest neighbors.
|
||||
*
|
||||
* @param nodeId Query node (must exist in @p poses); excluded from results.
|
||||
* @param poses Candidate poses.
|
||||
* @param radius Search radius (m).
|
||||
* @param angle Max +X axis angle difference (rad); `0` ignores heading.
|
||||
* @param k Max neighbors (`0` = all within radius).
|
||||
* @return Neighbor id → squared Euclidean distance.
|
||||
* Find the nearest nodes of the query pose or node
|
||||
* @param nodeId the query id
|
||||
* @param nodes the nodes to search for
|
||||
* @param radius radius to search for (m), if 0, k should be > 0.
|
||||
* @param k max nearest neighbors (0=all inside the radius)
|
||||
* @return the nodes with squared distance to query node.
|
||||
*/
|
||||
std::map<int, float> RTABMAP_CORE_EXPORT findNearestNodes(
|
||||
int nodeId,
|
||||
@@ -610,33 +309,18 @@ std::map<int, float> RTABMAP_CORE_EXPORT findNearestNodes(
|
||||
float radius,
|
||||
float angle = 0.0f,
|
||||
int k=0);
|
||||
/**
|
||||
* @brief Spatial neighbors of a pose (KD-tree radius or k-NN search).
|
||||
* @param targetPose Query pose (position used; orientation used when @p angle > 0).
|
||||
* @param poses Candidate poses (not modified).
|
||||
* @param radius Search radius (m).
|
||||
* @param angle Max +X axis angle difference (rad); `0` ignores heading.
|
||||
* @param k Max neighbors (`0` = all within radius).
|
||||
* @return Neighbor id → squared Euclidean distance.
|
||||
*/
|
||||
std::map<int, float> RTABMAP_CORE_EXPORT findNearestNodes(
|
||||
const Transform & targetPose,
|
||||
const std::map<int, Transform> & poses,
|
||||
float radius,
|
||||
float angle = 0.0f,
|
||||
int k=0);
|
||||
/**
|
||||
* @brief Like @ref findNearestNodes(int,const std::map<int,Transform>&,float,float,int)
|
||||
* but returns full @ref Transform values.
|
||||
*/
|
||||
std::map<int, Transform> RTABMAP_CORE_EXPORT findNearestPoses(
|
||||
int nodeId,
|
||||
const std::map<int, Transform> & poses,
|
||||
float radius,
|
||||
float angle = 0.0f,
|
||||
int k=0);
|
||||
/** @overload
|
||||
* Query by @ref Transform instead of node id. */
|
||||
std::map<int, Transform> RTABMAP_CORE_EXPORT findNearestPoses(
|
||||
const Transform & targetPose,
|
||||
const std::map<int, Transform> & poses,
|
||||
@@ -644,63 +328,28 @@ std::map<int, Transform> RTABMAP_CORE_EXPORT findNearestPoses(
|
||||
float angle = 0.0f,
|
||||
int k=0);
|
||||
|
||||
/** @deprecated Use @ref findNearestNodes(const Transform&,const std::map<int,Transform>&,float,float,int) with `radius=0`, `k` set. */
|
||||
// Use new findNearestNodes() interface with radius=0, angle=0.
|
||||
RTABMAP_DEPRECATED std::map<int, float> RTABMAP_CORE_EXPORT findNearestNodes(const std::map<int, rtabmap::Transform> & nodes, const rtabmap::Transform & targetPose, int k);
|
||||
/** @deprecated Use @ref findNearestNodes(int,const std::map<int,Transform>&,float,float,int). */
|
||||
// Renamed to findNearestNodes()
|
||||
RTABMAP_DEPRECATED std::map<int, float> RTABMAP_CORE_EXPORT getNodesInRadius(int nodeId, const std::map<int, Transform> & nodes, float radius);
|
||||
/** @deprecated Use @ref findNearestNodes(const Transform&,const std::map<int,Transform>&,float,float,int). */
|
||||
// Renamed to findNearestNodes()
|
||||
RTABMAP_DEPRECATED std::map<int, float> RTABMAP_CORE_EXPORT getNodesInRadius(const Transform & targetPose, const std::map<int, Transform> & nodes, float radius);
|
||||
/** @deprecated Use @ref findNearestPoses(int,const std::map<int,Transform>&,float,float,int). */
|
||||
// Renamed to findNearestNodes()
|
||||
RTABMAP_DEPRECATED std::map<int, Transform> RTABMAP_CORE_EXPORT getPosesInRadius(int nodeId, const std::map<int, Transform> & nodes, float radius, float angle = 0.0f);
|
||||
/** @deprecated Use @ref findNearestPoses(const Transform&,const std::map<int,Transform>&,float,float,int). */
|
||||
// Renamed to findNearestNodes()
|
||||
RTABMAP_DEPRECATED std::map<int, Transform> RTABMAP_CORE_EXPORT getPosesInRadius(const Transform & targetPose, const std::map<int, Transform> & nodes, float radius, float angle = 0.0f);
|
||||
|
||||
/**
|
||||
* @brief Path length along an ordered list of poses.
|
||||
*
|
||||
* Sums `path[i].second.getDistance(path[i+1].second)` for consecutive entries.
|
||||
*
|
||||
* @param path Ordered `(nodeId, pose)` pairs.
|
||||
* @return Total length (m), or `0` if fewer than two poses.
|
||||
*/
|
||||
float RTABMAP_CORE_EXPORT computePathLength(
|
||||
const std::vector<std::pair<int, Transform> > & path);
|
||||
|
||||
/**
|
||||
* @brief Path length in map iteration order.
|
||||
*
|
||||
* Sums distances between consecutive poses in ascending map key order (does not verify
|
||||
* that entries form a connected path in the graph).
|
||||
*
|
||||
* @param path Poses keyed by node id (sorted by key).
|
||||
* @return Total length (m), or `0` if fewer than two poses.
|
||||
*/
|
||||
// assuming they are all linked in map order
|
||||
float RTABMAP_CORE_EXPORT computePathLength(
|
||||
const std::map<int, Transform> & path);
|
||||
|
||||
/**
|
||||
* @brief Splits poses into chains connected only by neighbor links.
|
||||
*
|
||||
* Repeatedly builds a path starting from the lowest remaining id: adds the next pose
|
||||
* in map order only if a @ref Link::kNeighbor or @ref Link::kNeighborMerged link exists
|
||||
* from the previous pose to it. Stops at the first gap, pushes the chain, and continues
|
||||
* until @p poses is empty.
|
||||
*
|
||||
* @param poses Input poses (cleared as segments are extracted).
|
||||
* @param links Graph constraints keyed by source id.
|
||||
* @return List of pose maps, each a contiguous neighbor chain.
|
||||
*/
|
||||
std::list<std::map<int, Transform> > RTABMAP_CORE_EXPORT getPaths(
|
||||
std::map<int, Transform> poses,
|
||||
const std::multimap<int, Link> & links);
|
||||
|
||||
/**
|
||||
* @brief Axis-aligned bounding box of pose positions.
|
||||
*
|
||||
* @param poses Input poses (no effect if empty).
|
||||
* @param min Output minimum (x, y, z) in meters.
|
||||
* @param max Output maximum (x, y, z) in meters.
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT computeMinMax(const std::map<int, Transform> & poses,
|
||||
cv::Vec3f & min,
|
||||
cv::Vec3f & max);
|
||||
|
||||
@@ -1,74 +1,25 @@
|
||||
/*
|
||||
Copyright (c) 2010-2026, 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.
|
||||
*/
|
||||
* IMU.h
|
||||
*
|
||||
* Created on: 2018-03-05
|
||||
* Author: mathieu
|
||||
*/
|
||||
|
||||
#ifndef IMU_H_
|
||||
#define IMU_H_
|
||||
|
||||
#include "rtabmap/core/rtabmap_core_export.h" // DLL export/import defines
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <rtabmap/utilite/UEvent.h>
|
||||
#include <rtabmap/core/Transform.h>
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class IMU
|
||||
* @brief Inertial measurement sample (ROS @c sensor_msgs/Imu-like fields).
|
||||
*
|
||||
* Holds orientation (quaternion), angular velocity, linear acceleration, optional
|
||||
* 3×3 row-major covariance matrices (double), and an optional @ref Transform
|
||||
* expressing the IMU frame relative to the robot base.
|
||||
*
|
||||
* @ref empty() is true when @ref localTransform() is null (default constructor).
|
||||
* A sample constructed with @ref Transform::getIdentity() is not empty.
|
||||
*
|
||||
* @ref convertToBaseFrame() rotates linear/angular velocity (and orientation when
|
||||
* quaternion x/y/z are non-zero) into the base frame, then clears rotation in
|
||||
* @ref localTransform() while keeping translation.
|
||||
*
|
||||
* @see SensorData::imu()
|
||||
* @see IMUEvent
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT IMU
|
||||
|
||||
// Correspondence class to sensor_msgs/IMU
|
||||
class IMU
|
||||
{
|
||||
public:
|
||||
/** @brief Default-constructs an empty sample (null @ref localTransform()). */
|
||||
IMU() {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a sample with orientation and motion data.
|
||||
* @param orientation Unit quaternion (qx, qy, qz, qw).
|
||||
* @param orientationCovariance 3×3 row-major covariance about x, y, z (empty if unused).
|
||||
* @param angularVelocity Rad/s about x, y, z.
|
||||
* @param angularVelocityCovariance 3×3 row-major covariance (empty if unused).
|
||||
* @param linearAcceleration m/s² about x, y, z.
|
||||
* @param linearAccelerationCovariance 3×3 row-major covariance (empty if unused).
|
||||
* @param localTransform IMU frame in base coordinates (default identity).
|
||||
*/
|
||||
IMU(const cv::Vec4d & orientation, // qx qy qz qw
|
||||
const cv::Mat & orientationCovariance,
|
||||
const cv::Vec3d & angularVelocity,
|
||||
@@ -85,15 +36,6 @@ public:
|
||||
localTransform_(localTransform)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a sample without orientation (e.g. no magnetometer / no attitude).
|
||||
* @param angularVelocity Rad/s about x, y, z.
|
||||
* @param angularVelocityCovariance 3×3 row-major covariance (empty if unused).
|
||||
* @param linearAcceleration m/s² about x, y, z.
|
||||
* @param linearAccelerationCovariance 3×3 row-major covariance (empty if unused).
|
||||
* @param localTransform IMU frame in base coordinates (default identity).
|
||||
*/
|
||||
IMU(const cv::Vec3d & angularVelocity,
|
||||
const cv::Mat & angularVelocityCovariance,
|
||||
const cv::Vec3d & linearAcceleration,
|
||||
@@ -107,37 +49,21 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/** @return Orientation quaternion (qx, qy, qz, qw). */
|
||||
// qx qy qz qw
|
||||
const cv::Vec4d & orientation() const {return orientation_;}
|
||||
/** @return 3×3 orientation covariance (row-major, empty if orientation unset). */
|
||||
const cv::Mat & orientationCovariance() const {return orientationCovariance_;}
|
||||
const cv::Mat & orientationCovariance() const {return orientationCovariance_;} // 3x3 double Row major about x, y, z axes, empty if orientation is not set
|
||||
|
||||
/** @return Angular velocity (rad/s). */
|
||||
const cv::Vec3d & angularVelocity() const {return angularVelocity_;}
|
||||
/** @return 3×3 angular velocity covariance (row-major, empty if unused). */
|
||||
const cv::Mat & angularVelocityCovariance() const {return angularVelocityCovariance_;}
|
||||
const cv::Mat & angularVelocityCovariance() const {return angularVelocityCovariance_;} // 3x3 double Row major about x, y, z axes, empty if angularVelocity is not set
|
||||
|
||||
/** @return Linear acceleration (m/s²). */
|
||||
const cv::Vec3d & linearAcceleration() const {return linearAcceleration_;}
|
||||
/** @return 3×3 linear acceleration covariance (row-major, empty if unused). */
|
||||
const cv::Mat & linearAccelerationCovariance() const {return linearAccelerationCovariance_;}
|
||||
const cv::Vec3d linearAcceleration() const {return linearAcceleration_;}
|
||||
const cv::Mat & linearAccelerationCovariance() const {return linearAccelerationCovariance_;} // 3x3 double Row major x, y z, empty if linearAcceleration is not set
|
||||
|
||||
/** @return Transform from IMU frame to base frame. */
|
||||
const Transform & localTransform() const {return localTransform_;}
|
||||
|
||||
/**
|
||||
* @brief Rotate motion (and optionally orientation) into the base frame.
|
||||
*
|
||||
* Applies @ref localTransform() rotation to vectors and covariances, then sets
|
||||
* rotational part of @ref localTransform() to identity (translation unchanged).
|
||||
* No-op if @ref localTransform() is null or rotation is identity.
|
||||
* Orientation is updated only when quaternion qx, qy, qz, qw are not all zero.
|
||||
*/
|
||||
// apply local transform rotation to data, and set Identity rotation for local transform
|
||||
void convertToBaseFrame();
|
||||
|
||||
/**
|
||||
* @brief True when @ref localTransform() is null (placeholder / unset sample).
|
||||
*/
|
||||
bool empty() const
|
||||
{
|
||||
return localTransform_.isNull();
|
||||
@@ -145,43 +71,30 @@ public:
|
||||
|
||||
private:
|
||||
cv::Vec4d orientation_;
|
||||
cv::Mat orientationCovariance_;
|
||||
cv::Mat orientationCovariance_; // 3x3 double Row major about x, y, z axes, empty if orientation is not set
|
||||
|
||||
cv::Vec3d angularVelocity_;
|
||||
cv::Mat angularVelocityCovariance_;
|
||||
cv::Mat angularVelocityCovariance_; // 3x3 double Row major about x, y, z axes, empty if angularVelocity is not set
|
||||
|
||||
cv::Vec3d linearAcceleration_;
|
||||
cv::Mat linearAccelerationCovariance_;
|
||||
cv::Mat linearAccelerationCovariance_; // 3x3 double Row major x, y z, empty if linearAcceleration is not set
|
||||
|
||||
Transform localTransform_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class IMUEvent
|
||||
* @brief @ref UEvent carrying an @ref IMU sample and timestamp.
|
||||
*/
|
||||
class IMUEvent : public UEvent
|
||||
{
|
||||
public:
|
||||
/** @brief Default-constructs an event with zero stamp. */
|
||||
IMUEvent() :
|
||||
stamp_(0.0)
|
||||
{}
|
||||
/**
|
||||
* @brief Constructs an event with IMU data and stamp.
|
||||
* @param data IMU sample.
|
||||
* @param stamp Timestamp in seconds.
|
||||
*/
|
||||
IMUEvent(const IMU & data, double stamp) :
|
||||
data_(data),
|
||||
stamp_(stamp)
|
||||
{
|
||||
}
|
||||
/** @return Event type name for the utilite event system. */
|
||||
virtual std::string getClassName() const {return "IMUEvent";}
|
||||
/** @return IMU payload. */
|
||||
const IMU & getData() const {return data_;}
|
||||
/** @return Timestamp in seconds. */
|
||||
double getStamp() const {return stamp_;}
|
||||
|
||||
private:
|
||||
@@ -191,4 +104,5 @@ private:
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif /* IMU_H_ */
|
||||
|
||||
@@ -34,110 +34,47 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class IMUFilter
|
||||
* @brief Fuses gyroscope and accelerometer samples into an orientation quaternion.
|
||||
*
|
||||
* Implementations integrate angular rates and correct drift using the measured
|
||||
* gravity vector. The public @ref update() API takes timestamps and computes the
|
||||
* time step between consecutive calls.
|
||||
*
|
||||
* Factory methods @ref create() return heap-allocated instances; the caller owns
|
||||
* the pointer (e.g. @ref SensorCaptureThread, @ref IMUThread, @ref Camera).
|
||||
*
|
||||
* Output orientation from @ref getOrientation() is a unit quaternion
|
||||
* `(qx, qy, qz, qw)` in the same convention as @ref IMU (body frame).
|
||||
*
|
||||
* Filter tuning parameters are read from @ref ParametersMap (ImuFilter/... keys);
|
||||
* see @ref kComplementaryFilter and @ref kMadgwick (the latter only when RTAB-Map
|
||||
* is built with Madgwick support).
|
||||
*
|
||||
* @see IMU
|
||||
* @see SensorCaptureThread::enableIMUFiltering()
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT IMUFilter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Orientation fusion algorithm.
|
||||
*/
|
||||
enum Type {
|
||||
kMadgwick = 0, /**< Madgwick AHRS (attitude and heading reference system). RTAB-Map must be built with Madgwick support. */
|
||||
kComplementaryFilter = 1 /**< Complementary filter (always available). */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates a filter using type parsed from @p parameters.
|
||||
* @param parameters Optional ImuFilter/... tuning parameters.
|
||||
* @return New filter instance (caller owns the pointer).
|
||||
*/
|
||||
kMadgwick=0,
|
||||
kComplementaryFilter=1};
|
||||
public:
|
||||
static IMUFilter * create(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Creates a filter of the given @p type.
|
||||
* @param type Fusion algorithm; falls back to @ref kComplementaryFilter if
|
||||
* @ref kMadgwick is requested but not compiled in.
|
||||
* @param parameters Optional ImuFilter/... tuning parameters.
|
||||
* @return New filter instance (caller owns the pointer).
|
||||
*/
|
||||
static IMUFilter * create(IMUFilter::Type type, const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
virtual ~IMUFilter() {}
|
||||
|
||||
/**
|
||||
* @brief Re-reads filter parameters from @p parameters.
|
||||
* @param parameters ImuFilter/... keys (implementation-specific).
|
||||
*/
|
||||
public:
|
||||
virtual void parseParameters(const ParametersMap & parameters) {}
|
||||
virtual ~IMUFilter(){}
|
||||
|
||||
/**
|
||||
* @brief Integrates one IMU sample and updates the internal orientation estimate.
|
||||
* @param gx Gyroscope x angular rate (rad/s).
|
||||
* @param gy Gyroscope y angular rate (rad/s).
|
||||
* @param gz Gyroscope z angular rate (rad/s).
|
||||
* @param ax Accelerometer x (m/s²; magnitude ~9.81 when stationary).
|
||||
* @param ay Accelerometer y (m/s²).
|
||||
* @param az Accelerometer z (m/s²).
|
||||
* @param stamp Sample time (seconds); used with the previous stamp to compute `dt`.
|
||||
*/
|
||||
void update(
|
||||
double gx, double gy, double gz,
|
||||
double ax, double ay, double az,
|
||||
double stamp);
|
||||
|
||||
/** @return Active fusion algorithm type. */
|
||||
virtual IMUFilter::Type type() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Current orientation estimate.
|
||||
* @param qx Quaternion x
|
||||
* @param qy Quaternion y
|
||||
* @param qz Quaternion z
|
||||
* @param qw Quaternion w (scalar)
|
||||
*/
|
||||
virtual void getOrientation(double & qx, double & qy, double & qz, double & qw) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Resets internal state to the given orientation.
|
||||
* @param qx Quaternion x (default 0)
|
||||
* @param qy Quaternion y (default 0)
|
||||
* @param qz Quaternion z (default 0)
|
||||
* @param qw Quaternion w (default 1, identity)
|
||||
*/
|
||||
virtual void reset(double qx = 0.0, double qy = 0.0, double qz = 0.0, double qw = 1.0) = 0;
|
||||
|
||||
protected:
|
||||
IMUFilter(const ParametersMap & parameters = ParametersMap()) : previousStamp_(0) {}
|
||||
|
||||
private:
|
||||
// Update from accelerometer and gyroscope data.
|
||||
// [gx, gy, gz]: Angular veloctiy, in rad / s.
|
||||
// [ax, ay, az]: Normalized gravity vector.
|
||||
// dt: time delta, in seconds.
|
||||
virtual void updateImpl(
|
||||
double gx, double gy, double gz,
|
||||
double ax, double ay, double az,
|
||||
double dt) = 0;
|
||||
|
||||
private:
|
||||
double previousStamp_;
|
||||
};
|
||||
|
||||
} // namespace rtabmap
|
||||
}
|
||||
|
||||
|
||||
#endif /* CORELIB_INCLUDE_RTABMAP_CORE_IMUFILTER_H_ */
|
||||
|
||||
@@ -37,63 +37,26 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace rtabmap {
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
class IMUFilter;
|
||||
|
||||
/**
|
||||
* @class IMUThread
|
||||
* @brief Background thread that replays IMU measurements from a CSV file.
|
||||
* Class IMUThread
|
||||
*
|
||||
* Reads gyroscope and accelerometer samples from a comma-separated file (EuRoC-style
|
||||
* or epoch timestamps), optionally fuses them with @ref IMUFilter, and posts
|
||||
* @ref IMUEvent on each step.
|
||||
*
|
||||
* CSV format:
|
||||
* - First line: header (skipped).
|
||||
* - Following lines: `stamp,gx,gy,gz,ax,ay,az` (stamp in seconds with a decimal
|
||||
* point, or EuRoC nanoseconds without one).
|
||||
*
|
||||
* Playback rate is limited by @ref setRate() when `rate > 0`; otherwise samples are
|
||||
* read as fast as possible (or spaced using inter-sample timestamps after the first
|
||||
* pair). When the file ends, the thread posts an empty @ref IMUEvent and stops.
|
||||
*
|
||||
* @see IMUEvent
|
||||
* @see IMUFilter
|
||||
* @see SensorCaptureThread::enableIMUFiltering()
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT IMUThread :
|
||||
public UThread,
|
||||
public UEventsSender
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs the replay thread.
|
||||
* @param rate Target playback rate in Hz (`0` = no rate cap until timestamps apply).
|
||||
* @param localTransform IMU frame relative to the robot base (stored in each @ref IMU).
|
||||
*/
|
||||
IMUThread(int rate, const Transform & localTransform);
|
||||
virtual ~IMUThread();
|
||||
|
||||
/**
|
||||
* @brief Opens and validates an IMU CSV file.
|
||||
* @param path Path to the CSV file.
|
||||
* @return False if the file is missing or contains no data rows.
|
||||
*/
|
||||
bool init(const std::string & path);
|
||||
|
||||
/** @brief Sets the target playback rate in Hz. */
|
||||
void setRate(int rate);
|
||||
|
||||
/**
|
||||
* @brief Enables orientation fusion on replayed samples.
|
||||
* @param filteringStrategy @ref IMUFilter::Type index (`0` = Madgwick, `1` = complementary).
|
||||
* @param parameters Optional ImuFilter/... tuning parameters.
|
||||
* @param baseFrameConversion If true, rotate IMU vectors into the base frame before filtering.
|
||||
*/
|
||||
void enableIMUFiltering(int filteringStrategy = 1, const ParametersMap & parameters = ParametersMap(), bool baseFrameConversion = false);
|
||||
|
||||
/** @brief Disables fusion and deletes the internal @ref IMUFilter. */
|
||||
void enableIMUFiltering(int filteringStrategy=1, const ParametersMap & parameters = ParametersMap(), bool baseFrameConversion = false);
|
||||
void disableIMUFiltering();
|
||||
|
||||
private:
|
||||
|
||||
@@ -28,57 +28,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef CORELIB_INCLUDE_RTABMAP_CORE_LASWRITER_H_
|
||||
#define CORELIB_INCLUDE_RTABMAP_CORE_LASWRITER_H_
|
||||
|
||||
#include "rtabmap/core/rtabmap_core_export.h" // DLL export/import defines
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @brief Writes a PCL point cloud to a LAS/LAZ file (requires RTAB-Map built with libLAS).
|
||||
*
|
||||
* Output uses 1 mm XYZ scale (`0.001`). The file extension (`.las` or `.laz`) selects
|
||||
* uncompressed or compressed output when libLAS LAZ support is available.
|
||||
*
|
||||
* @param filePath Output path (`.las` or `.laz`).
|
||||
* @param cloud Input point cloud.
|
||||
* @param cameraIds Optional per-point camera/signature ids (stored as point source ID);
|
||||
* must be empty or the same length as @p cloud.
|
||||
* @return `0` on success, `1` on error (e.g. LAZ not supported).
|
||||
*/
|
||||
int RTABMAP_CORE_EXPORT saveLASFile(
|
||||
const std::string & filePath,
|
||||
const pcl::PointCloud<pcl::PointXYZ> & cloud,
|
||||
const std::vector<int> & cameraIds = std::vector<int>());
|
||||
int saveLASFile(const std::string & filePath, const pcl::PointCloud<pcl::PointXYZ> & cloud, const std::vector<int> & cameraIds = std::vector<int>());
|
||||
int saveLASFile(const std::string & filePath, const pcl::PointCloud<pcl::PointXYZRGB> & cloud, const std::vector<int> & cameraIds = std::vector<int>(), const std::vector<float> & intensities = std::vector<float>());
|
||||
int saveLASFile(const std::string & filePath, const pcl::PointCloud<pcl::PointXYZI> & cloud, const std::vector<int> & cameraIds = std::vector<int>());
|
||||
|
||||
/**
|
||||
* @brief Writes a colored point cloud to LAS/LAZ, with optional intensity and camera ids.
|
||||
* @param filePath Output path (`.las` or `.laz`).
|
||||
* @param cloud RGB point cloud (8-bit channels mapped to 16-bit LAS color).
|
||||
* @param cameraIds Optional per-point ids (same length as @p cloud or empty).
|
||||
* @param intensities Optional per-point intensities (same length as @p cloud or empty).
|
||||
* @return `0` on success, `1` on error.
|
||||
*/
|
||||
int RTABMAP_CORE_EXPORT saveLASFile(
|
||||
const std::string & filePath,
|
||||
const pcl::PointCloud<pcl::PointXYZRGB> & cloud,
|
||||
const std::vector<int> & cameraIds = std::vector<int>(),
|
||||
const std::vector<float> & intensities = std::vector<float>());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a point cloud with intensity channel to LAS/LAZ.
|
||||
* @param filePath Output path (`.las` or `.laz`).
|
||||
* @param cloud XYZI point cloud (coordinates and intensity written).
|
||||
* @param cameraIds Optional per-point ids (same length as @p cloud or empty).
|
||||
* @return `0` on success, `1` on error.
|
||||
*/
|
||||
int RTABMAP_CORE_EXPORT saveLASFile(
|
||||
const std::string & filePath,
|
||||
const pcl::PointCloud<pcl::PointXYZI> & cloud,
|
||||
const std::vector<int> & cameraIds = std::vector<int>());
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
#endif /* CORELIB_INCLUDE_RTABMAP_CORE_LASWRITER_H_ */
|
||||
|
||||
@@ -36,99 +36,65 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class Landmark
|
||||
* @brief Optimized pose of a visual landmark (e.g. ArUco/AprilTag) in the map.
|
||||
*
|
||||
* Stores a positive landmark @ref id(), optional physical @ref size() (m), world
|
||||
* @ref pose(), and a 6×6 pose covariance matrix (x, y, z, roll, pitch, yaw).
|
||||
*
|
||||
* Used in @ref Landmarks maps on @ref SensorData and in memory during SLAM.
|
||||
* Graph constraints to landmarks use @ref Link::kLandmark with a **negative**
|
||||
* landmark id on the link (`to` is typically `-id()`).
|
||||
*
|
||||
* Covariance diagonal entries must be finite and strictly positive. Use a large
|
||||
* value (e.g. `9999`) on angular axes when orientation uncertainty is unknown.
|
||||
*
|
||||
* @see SensorData::setLandmarks()
|
||||
* @see Signature::addLandmark()
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Landmark
|
||||
class Landmark
|
||||
{
|
||||
public:
|
||||
/** @brief Default-constructs an invalid landmark (`id == 0`). */
|
||||
Landmark() :
|
||||
id_(0),
|
||||
size_(0.0f)
|
||||
size_(0.0f)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Constructs a landmark with size, pose, and covariance.
|
||||
* @param id Positive landmark identifier.
|
||||
* @param size Physical size of the marker (m); `0` if unknown.
|
||||
* @param pose Landmark pose in the base frame (typically the robot frame).
|
||||
* @param covariance 6×6 `CV_64FC1` covariance (diagonal entries must be > 0).
|
||||
*/
|
||||
Landmark(const int & id, const float & size, const Transform & pose, const cv::Mat & covariance) :
|
||||
id_(id),
|
||||
size_(size),
|
||||
pose_(pose),
|
||||
covariance_(covariance)
|
||||
{
|
||||
UASSERT(id_>0);
|
||||
UASSERT(!pose_.isNull());
|
||||
UASSERT(covariance_.cols == 6 && covariance_.rows == 6 && covariance_.type() == CV_64FC1);
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(0,0)) && covariance_.at<double>(0,0)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(0,0)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(1,1)) && covariance_.at<double>(1,1)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(1,1)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(2,2)) && covariance_.at<double>(2,2)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(2,2)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(3,3)) && covariance_.at<double>(3,3)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(3,3)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(4,4)) && covariance_.at<double>(4,4)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(4,4)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(5,5)) && covariance_.at<double>(5,5)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(5,5)).c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deprecated; use the constructor with explicit @p size (`0` if unknown).
|
||||
*/
|
||||
RTABMAP_DEPRECATED Landmark(const int & id, const Transform & pose, const cv::Mat & covariance);
|
||||
Landmark(const int & id, const float & size, const Transform & pose, const cv::Mat & covariance) :
|
||||
id_(id),
|
||||
size_(size),
|
||||
pose_(pose),
|
||||
covariance_(covariance)
|
||||
{
|
||||
UASSERT(id_>0);
|
||||
UASSERT(!pose_.isNull());
|
||||
UASSERT(covariance_.cols == 6 && covariance_.rows == 6 && covariance_.type() == CV_64FC1);
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(0,0)) && covariance_.at<double>(0,0)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(0,0)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(1,1)) && covariance_.at<double>(1,1)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(1,1)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(2,2)) && covariance_.at<double>(2,2)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(2,2)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(3,3)) && covariance_.at<double>(3,3)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(3,3)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(4,4)) && covariance_.at<double>(4,4)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(4,4)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(5,5)) && covariance_.at<double>(5,5)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(5,5)).c_str());
|
||||
}
|
||||
// Use constructor with size=0 instead.
|
||||
RTABMAP_DEPRECATED Landmark(const int & id, const Transform & pose, const cv::Mat & covariance);
|
||||
|
||||
virtual ~Landmark() {}
|
||||
|
||||
/** @return Positive landmark id (map key in @ref Landmarks). */
|
||||
const int & id() const {return id_;}
|
||||
/** @return Marker size in metres (`0` if unknown). */
|
||||
const float & size() const {return size_;}
|
||||
/** @return Landmark pose in the map frame. */
|
||||
const float & size() const {return size_;}
|
||||
const Transform & pose() const {return pose_;}
|
||||
/** @return 6×6 pose covariance (`CV_64FC1`). */
|
||||
const cv::Mat & covariance() const {return covariance_;}
|
||||
|
||||
private:
|
||||
int id_;
|
||||
float size_;
|
||||
float size_;
|
||||
Transform pose_;
|
||||
cv::Mat covariance_;
|
||||
};
|
||||
|
||||
/** @brief Map of landmark id → @ref Landmark (typically positive keys). */
|
||||
typedef std::map<int, Landmark> Landmarks;
|
||||
|
||||
inline Landmark::Landmark(const int & id, const Transform & pose, const cv::Mat & covariance) :
|
||||
id_(id),
|
||||
size_(0.0f),
|
||||
pose_(pose),
|
||||
covariance_(covariance)
|
||||
id_(id),
|
||||
size_(0.0f),
|
||||
pose_(pose),
|
||||
covariance_(covariance)
|
||||
{
|
||||
UASSERT(id_>0);
|
||||
UASSERT(!pose_.isNull());
|
||||
UASSERT(covariance_.cols == 6 && covariance_.rows == 6 && covariance_.type() == CV_64FC1);
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(0,0)) && covariance_.at<double>(0,0)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(0,0)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(1,1)) && covariance_.at<double>(1,1)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(1,1)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(2,2)) && covariance_.at<double>(2,2)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(2,2)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(3,3)) && covariance_.at<double>(3,3)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(3,3)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(4,4)) && covariance_.at<double>(4,4)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(4,4)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(5,5)) && covariance_.at<double>(5,5)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(5,5)).c_str());
|
||||
UASSERT(id_>0);
|
||||
UASSERT(!pose_.isNull());
|
||||
UASSERT(covariance_.cols == 6 && covariance_.rows == 6 && covariance_.type() == CV_64FC1);
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(0,0)) && covariance_.at<double>(0,0)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(0,0)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(1,1)) && covariance_.at<double>(1,1)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(1,1)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(2,2)) && covariance_.at<double>(2,2)>0, uFormat("Linear covariance should not be null! Value=%f.", covariance_.at<double>(2,2)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(3,3)) && covariance_.at<double>(3,3)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(3,3)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(4,4)) && covariance_.at<double>(4,4)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(4,4)).c_str());
|
||||
UASSERT_MSG(uIsFinite(covariance_.at<double>(5,5)) && covariance_.at<double>(5,5)>0, uFormat("Angular covariance should not be null! Value=%f (set to 9999 if unknown).", covariance_.at<double>(5,5)).c_str());
|
||||
}
|
||||
|
||||
} // namespace rtabmap
|
||||
}
|
||||
|
||||
#endif /* CORELIB_INCLUDE_RTABMAP_CORE_LANDMARK_H_ */
|
||||
|
||||
@@ -34,42 +34,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class LaserScan
|
||||
* @brief Represents 2D or 3D laser scan data with support for multiple point data formats.
|
||||
*
|
||||
* The LaserScan class stores structured laser scan data used in SLAM, mapping, and perception.
|
||||
* It supports various formats including point coordinates, intensity, normals, RGB colors,
|
||||
* and timestamps. Utility methods are provided for format checking, cloning, and combining scans.
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT LaserScan
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Enumeration of possible formats for laser scan data.
|
||||
*
|
||||
* These values represent different combinations of point attributes
|
||||
* that can be stored in a laser scan. The format determines how each
|
||||
* point in the scan is structured.
|
||||
*/
|
||||
enum Format{
|
||||
kUnknown=0, /**< Unknown format. */
|
||||
kXY=1, /**< 2D points with X and Y coordinates. */
|
||||
kXYI=2, /**< 2D points with X, Y and intensity. */
|
||||
kXYNormal=3, /**< 2D points with X, Y and normal vectors. */
|
||||
kXYINormal=4, /**< 2D points with X, Y, intensity and normal vectors. */
|
||||
kXYZ=5, /**< 3D points with X, Y and Z coordinates. */
|
||||
kXYZI=6, /**< 3D points with X, Y, Z and intensity. */
|
||||
kXYZRGB=7, /**< 3D points with X, Y, Z and RGB color. */
|
||||
kXYZNormal=8, /**< 3D points with X, Y, Z and normal vectors. */
|
||||
kXYZINormal=9, /**< 3D points with X, Y, Z, intensity and normal vectors. */
|
||||
kXYZRGBNormal=10, /**< 3D points with X, Y, Z, RGB color and normal vectors. */
|
||||
kXYZIT=11, /**< 3D points with X, Y, Z, intensity and time. */
|
||||
kXYZIRT=12 /**< 3D points with X, Y, Z, intensity, ring and time. */
|
||||
};
|
||||
enum Format{kUnknown=0,
|
||||
kXY=1,
|
||||
kXYI=2,
|
||||
kXYNormal=3,
|
||||
kXYINormal=4,
|
||||
kXYZ=5,
|
||||
kXYZI=6,
|
||||
kXYZRGB=7,
|
||||
kXYZNormal=8,
|
||||
kXYZINormal=9,
|
||||
kXYZRGBNormal=10,
|
||||
kXYZIT=11,
|
||||
kXYZIRT=12};
|
||||
|
||||
/// @name Static Utility Functions
|
||||
/// @{
|
||||
static std::string formatName(const Format & format);
|
||||
static int channels(const Format & format);
|
||||
static bool isScan2d(const Format & format);
|
||||
@@ -78,21 +59,11 @@ public:
|
||||
static bool isScanHasIntensity(const Format & format);
|
||||
static bool isScanHasTime(const Format & format);
|
||||
static bool isScanHasRing(const Format & format);
|
||||
static float packRGB(unsigned char r, unsigned char g, unsigned char b);
|
||||
static void unpackRGB(float rgb, unsigned char & r, unsigned char & g, unsigned char & b);
|
||||
|
||||
/**
|
||||
* @brief Converts legacy scan format to a LaserScan object.
|
||||
*/
|
||||
static LaserScan backwardCompatibility(
|
||||
const cv::Mat & oldScanFormat,
|
||||
int maxPoints = 0,
|
||||
int maxRange = 0,
|
||||
const Transform & localTransform = Transform::getIdentity());
|
||||
|
||||
/**
|
||||
* @brief Converts legacy scan format with additional metadata to a LaserScan.
|
||||
*/
|
||||
static LaserScan backwardCompatibility(
|
||||
const cv::Mat & oldScanFormat,
|
||||
float minRange,
|
||||
@@ -101,19 +72,14 @@ public:
|
||||
float angleMax,
|
||||
float angleInc,
|
||||
const Transform & localTransform = Transform::getIdentity());
|
||||
/// @}
|
||||
|
||||
public:
|
||||
|
||||
/// @name Constructors
|
||||
/// @{
|
||||
LaserScan();
|
||||
LaserScan(const LaserScan & data,
|
||||
int maxPoints,
|
||||
float maxRange,
|
||||
const Transform & localTransform = Transform::getIdentity());
|
||||
|
||||
/// @deprecated Use constructor without `format` argument.
|
||||
// Use version without \"format\" argument.
|
||||
RTABMAP_DEPRECATED LaserScan(const LaserScan & data,
|
||||
int maxPoints,
|
||||
float maxRange,
|
||||
@@ -124,8 +90,7 @@ public:
|
||||
float maxRange,
|
||||
Format format,
|
||||
const Transform & localTransform = Transform::getIdentity());
|
||||
|
||||
/// @deprecated Use constructor without `format` argument.
|
||||
// Use version without \"format\" argument.
|
||||
RTABMAP_DEPRECATED LaserScan(const LaserScan & data,
|
||||
Format format,
|
||||
float minRange,
|
||||
@@ -149,10 +114,7 @@ public:
|
||||
float angleMax,
|
||||
float angleIncrement,
|
||||
const Transform & localTransform = Transform::getIdentity());
|
||||
/// @}
|
||||
|
||||
/// @name Accessors
|
||||
/// @{
|
||||
const cv::Mat & data() const {return data_;}
|
||||
Format format() const {return format_;}
|
||||
std::string formatName() const {return formatName(format_);}
|
||||
@@ -165,13 +127,10 @@ public:
|
||||
float angleIncrement() const {return angleIncrement_;}
|
||||
void setLocalTransform(const Transform & t) {localTransform_ = t;}
|
||||
Transform localTransform() const {return localTransform_;}
|
||||
/// @}
|
||||
|
||||
/// @name Status and Format Checks
|
||||
/// @{
|
||||
bool empty() const {return data_.empty();}
|
||||
bool isEmpty() const {return data_.empty();}
|
||||
int size() const {return static_cast<int>(data_.total());}
|
||||
int size() const {return data_.total();}
|
||||
int dataType() const {return data_.type();}
|
||||
bool is2d() const {return isScan2d(format_);}
|
||||
bool hasNormals() const {return isScanHasNormals(format_);}
|
||||
@@ -179,44 +138,29 @@ public:
|
||||
bool hasIntensity() const {return isScanHasIntensity(format_);}
|
||||
bool hasTime() const {return isScanHasTime(format_);}
|
||||
bool hasRing() const {return isScanHasRing(format_);}
|
||||
/** @brief True if the scan is stored compressed (single-row @c CV_8UC1 blob) instead of raw points. */
|
||||
bool isCompressed() const {return !data_.empty() && data_.type()==CV_8UC1;}
|
||||
bool isOrganized() const {return data_.rows > 1;}
|
||||
LaserScan clone() const;
|
||||
LaserScan densify() const;
|
||||
/// @}
|
||||
|
||||
/// @name Operations
|
||||
/// @{
|
||||
int getIntensityOffset() const {return hasIntensity()?(is2d()?2:3):-1;}
|
||||
int getRGBOffset() const {return hasRGB()?(is2d()?2:3):-1;}
|
||||
int getNormalsOffset() const {return hasNormals()?(2 + (is2d()?0:1) + ((hasRGB() || hasIntensity())?1:0)):-1;}
|
||||
int getRingOffset() const {return format_==kXYZIRT?4:-1;}
|
||||
int getTimeOffset() const {return format_==kXYZIT?4:(format_==kXYZIRT?5:-1);}
|
||||
|
||||
/**
|
||||
* @brief Access a specific field value of a point.
|
||||
* @param pointIndex Index of the point.
|
||||
* @param channelOffset Channel offset to access (e.g., 0=X, 1=Y, 2=Z if 2D, for other fields, use corresponding getter functions).
|
||||
* @return Reference to the field value.
|
||||
* @see getIntensityOffset() getRGBOffset() getNormalsOffset() getTimeOffset()
|
||||
*/
|
||||
float & field(unsigned int pointIndex, unsigned int channelOffset);
|
||||
|
||||
/**
|
||||
* @brief Clear the scan data.
|
||||
*/
|
||||
void clear() {data_ = cv::Mat();}
|
||||
|
||||
/**
|
||||
* @brief Concatenate scan's data (localTransform is ignored).
|
||||
* Concatenate scan's data, localTransform is ignored.
|
||||
*/
|
||||
LaserScan & operator+=(const LaserScan &);
|
||||
/**
|
||||
* @brief Concatenate scan's data (localTransform is ignored).
|
||||
* Concatenate scan's data, localTransform is ignored.
|
||||
*/
|
||||
LaserScan operator+(const LaserScan &);
|
||||
/// @}
|
||||
|
||||
private:
|
||||
void init(const cv::Mat & data,
|
||||
@@ -230,17 +174,17 @@ private:
|
||||
const Transform & localTransform = Transform::getIdentity());
|
||||
|
||||
private:
|
||||
cv::Mat data_; ///< The scan data matrix.
|
||||
Format format_; ///< The scan data format.
|
||||
int maxPoints_; ///< Maximum number of points allowed.
|
||||
float rangeMin_; ///< Minimum valid range.
|
||||
float rangeMax_; ///< Maximum valid range.
|
||||
float angleMin_; ///< Minimum angle (for 2D scans).
|
||||
float angleMax_; ///< Maximum angle (for 2D scans).
|
||||
float angleIncrement_; ///< Angular increment (for 2D scans).
|
||||
Transform localTransform_; ///< Transform from base frame to scan frame.
|
||||
cv::Mat data_;
|
||||
Format format_;
|
||||
int maxPoints_;
|
||||
float rangeMin_;
|
||||
float rangeMax_;
|
||||
float angleMin_;
|
||||
float angleMax_;
|
||||
float angleIncrement_;
|
||||
Transform localTransform_;
|
||||
};
|
||||
|
||||
} // namespace rtabmap
|
||||
}
|
||||
|
||||
#endif /* CORELIB_INCLUDE_RTABMAP_CORE_LASERSCAN_H_ */
|
||||
|
||||
@@ -35,127 +35,58 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class Link
|
||||
* @brief Directed constraint between two nodes in RTAB-Map's pose graph.
|
||||
*
|
||||
* A link connects signature @ref from() to signature @ref to() with a relative
|
||||
* @ref Transform and an information matrix (inverse covariance) used by graph
|
||||
* optimization. Links are stored on @ref Signature objects and persisted in the
|
||||
* database; they define odometry chains, loop closures, landmarks, and priors.
|
||||
*
|
||||
* The transform is expressed from the @p from node frame to the @p to node frame
|
||||
* (i.e. pose of @p to relative to @p from), unless @ref type() indicates a
|
||||
* special semantics (e.g. @ref kPosePrior, @ref kLandmark).
|
||||
*
|
||||
* @see Signature
|
||||
* @see Memory::addLink()
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Link
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Link category and filter sentinels.
|
||||
*
|
||||
* Values @ref kSelfRefLink, @ref kAllWithLandmarks, @ref kAllWithoutLandmarks and
|
||||
* @ref kUndef are also used as query filters when retrieving links from memory or
|
||||
* the database (they are not stored as link types on signatures).
|
||||
*/
|
||||
enum Type {
|
||||
kNeighbor, /**< Sequential odometry link between consecutive nodes. */
|
||||
kGlobalClosure, /**< Global loop closure added by global loop closure detection (i.e., using bags-of-words to find loop closures with other nodes in WM without using current estimated pose). */
|
||||
kLocalSpaceClosure, /**< Local loop closure added by proximity detection by space (i.e. using current estimated pose to find loop closures with nearby nodes in WM). */
|
||||
kLocalTimeClosure, /**< Local loop closure added by proximity detection by time (i.e. between nodes in STM). */
|
||||
kUserClosure, /**< User-defined loop closure constraint. */
|
||||
kVirtualClosure, /**< Virtual link added to keep the path linked to local map. */
|
||||
kNeighborMerged, /**< Merged neighbor link after graph reduction. */
|
||||
kPosePrior, /**< Absolute pose prior in the world frame (@p from == @p to). */
|
||||
kLandmark, /**< Observation of a landmark: @p from is the observer node, @p to is a negative landmark id. */
|
||||
kGravity, /**< Gravity direction constraint on the base frame (@p from == @p to). */
|
||||
kEnd, /**< Sentinel: number of link types that can be stored. */
|
||||
kSelfRefLink = 97, /**< Filter: links where @p from == @p to (e.g. @ref kPosePrior, @ref kGravity). */
|
||||
kAllWithLandmarks = 98, /**< Filter: all link types including @ref kLandmark. */
|
||||
kAllWithoutLandmarks = 99, /**< Filter: all link types except @ref kLandmark. */
|
||||
kUndef = 99 /**< Undefined type or invalid link. */
|
||||
};
|
||||
|
||||
/** @return Human-readable name for @p type (e.g. "Neighbor", "GlobalClosure"). */
|
||||
kNeighbor,
|
||||
kGlobalClosure,
|
||||
kLocalSpaceClosure,
|
||||
kLocalTimeClosure,
|
||||
kUserClosure,
|
||||
kVirtualClosure,
|
||||
kNeighborMerged,
|
||||
kPosePrior, // Absolute pose in /world frame, From == To
|
||||
kLandmark, // Transform /base_link -> /landmark, "From" is node observing the landmark "To" (landmark is negative id)
|
||||
kGravity, // Orientation of the base frame accordingly to gravity (From == To)
|
||||
kEnd,
|
||||
kSelfRefLink = 97, // Include kPosePrior and kGravity (all links where From=To)
|
||||
kAllWithLandmarks = 98,
|
||||
kAllWithoutLandmarks = 99,
|
||||
kUndef = 99};
|
||||
static std::string typeName(Type type);
|
||||
|
||||
/** @brief Default constructor; creates an invalid link (@ref kUndef). */
|
||||
Link();
|
||||
/**
|
||||
* @brief Constructs a link between two nodes.
|
||||
* @param from Source signature id.
|
||||
* @param to Target signature id (negative for landmarks when @p type is @ref kLandmark).
|
||||
* @param type Link category.
|
||||
* @param transform Relative transform from @p from to @p to.
|
||||
* @param infMatrix 6x6 information matrix (inverse covariance)
|
||||
* @param userData Optional payload; compressed automatically if not already @c CV_8UC1.
|
||||
*/
|
||||
Link(int from,
|
||||
int to,
|
||||
Type type,
|
||||
const Transform & transform,
|
||||
const cv::Mat & infMatrix = cv::Mat::eye(6,6,CV_64FC1),
|
||||
const cv::Mat & infMatrix = cv::Mat::eye(6,6,CV_64FC1), // information matrix: inverse of covariance matrix
|
||||
const cv::Mat & userData = cv::Mat());
|
||||
|
||||
/** @return True if ids, transform, and type are valid for use in the graph. */
|
||||
bool isValid() const {return from_ != 0 && to_ != 0 && !transform_.isNull() && type_!=kUndef;}
|
||||
|
||||
/** @return Source signature id. */
|
||||
int from() const {return from_;}
|
||||
/** @return Target signature id (negative for a landmark). */
|
||||
int to() const {return to_;}
|
||||
/** @return Relative transform, from the @ref from() frame to the @ref to() frame. */
|
||||
const Transform & transform() const {return transform_;}
|
||||
/** @return Link category (see @ref Type). */
|
||||
Type type() const {return type_;}
|
||||
std::string typeName() const {return typeName(type_);}
|
||||
const cv::Mat & infMatrix() const {return infMatrix_;}
|
||||
|
||||
/**
|
||||
* @brief Rotation variance derived from the information matrix diagonal (roll, pitch, yaw).
|
||||
* @param minimum If true, returns the largest diagonal entry (most uncertain axis);
|
||||
* if false, returns the smallest non-zero entry.
|
||||
*/
|
||||
double rotVariance(bool minimum = true) const;
|
||||
/**
|
||||
* @brief Translation variance derived from the information matrix diagonal (x, y, z).
|
||||
* @param minimum If true, returns the largest diagonal entry; if false, the smallest non-zero entry.
|
||||
*/
|
||||
double transVariance(bool minimum = true) const;
|
||||
|
||||
void setFrom(int from) {from_ = from;}
|
||||
void setTo(int to) {to_ = to;}
|
||||
void setTransform(const Transform & transform) {transform_ = transform;}
|
||||
void setType(Type type) {type_ = type;}
|
||||
/** @brief Sets the 6x6 information matrix (@c CV_64FC1); diagonal entries must be positive and finite. */
|
||||
void setInfMatrix(const cv::Mat & infMatrix);
|
||||
|
||||
/** @return Uncompressed user data, empty unless it was set raw or @ref uncompressUserData() was called. */
|
||||
const cv::Mat & userDataRaw() const {return _userDataRaw;}
|
||||
/** @return User data as stored/persisted, in compressed form. */
|
||||
const cv::Mat & userDataCompressed() const {return _userDataCompressed;}
|
||||
/** @brief Decompresses user data into @ref userDataRaw() if compressed data is stored. */
|
||||
void uncompressUserData();
|
||||
/** @return Uncompressed user data without modifying internal storage. */
|
||||
cv::Mat uncompressUserDataConst() const;
|
||||
|
||||
/**
|
||||
* @brief Chains this link (from → to) with @p link (to → link.to).
|
||||
* @param link Second link; must satisfy @c this->to() == link.from().
|
||||
* @param outputType Type of the merged link.
|
||||
* @return Single link from @ref from() to @p link.to() with transform
|
||||
* \(T_{ac} = T_{ab} T_{bc}\) (or null if either input transform is null).
|
||||
* Information matrix handling depends on @p outputType:
|
||||
* - @ref kNeighborMerged "kNeighborMerged": \(\Omega_{ac} = (\Omega_{ab}^{-1} + \Omega_{bc}^{-1})^{-1}\)
|
||||
* (covariances add when both legs are diagonal and independent).
|
||||
* - Other types: keeps the full information matrix of @p link unless
|
||||
* \(\Omega_{ab}(0,0) < \Omega_{bc}(0,0)\) (i.e. the smaller x information entry).
|
||||
*/
|
||||
Link merge(const Link & link, Type outputType) const;
|
||||
/** @return Link with swapped endpoints and inverted transform. */
|
||||
Link inverse() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -35,72 +35,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class LocalGrid
|
||||
* @brief Local occupancy grid cells for one map node (ground, obstacles, empty).
|
||||
*
|
||||
* Each layer is stored as a 1×N `cv::Mat` of cell coordinates in the robot/base
|
||||
* frame. Layouts match @ref LaserScan grid storage (@ref LaserScan::Format):
|
||||
* - `CV_32FC2` (2 ch): `kXY` — (x, y)
|
||||
* - `CV_32FC3` (3 ch): `kXYZ` or `kXYI` — (x, y, z) or (x, y, intensity)
|
||||
* - `CV_32FC4` (4 ch): `kXYZI` or `kXYZRGB` — (x, y, z, intensity) or (x, y, z, RGB)
|
||||
* - `CV_32FC5` (5 ch): `kXYNormal` or `kXYZIT` — 2D (x, y, nx, ny, nz) or 3D (x, y, z, intensity, time)
|
||||
* - `CV_32FC6` (6 ch): `kXYINormal`, `kXYZNormal` or `kXYZIRT`
|
||||
* - `CV_32FC7` (7 ch): `kXYZINormal` or `kXYZRGBNormal`
|
||||
*
|
||||
* @ref is3D() is true when every non-empty layer is `CV_32FC3`, `CV_32FC4` or `CV_32FC6`
|
||||
* (5-channel layers are treated as 2D by @ref OccupancyGrid). Extra fields are not used for pose/projection.
|
||||
* @ref cellSize is the grid resolution in meters (must be > 0).
|
||||
* @ref viewPoint is the sensor/view origin used when the grid was built.
|
||||
*
|
||||
* @see LaserScan
|
||||
* @see LocalGridMaker
|
||||
* @see OccupancyGrid
|
||||
* @see LocalGridCache
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT LocalGrid
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Builds a local grid from cell matrices.
|
||||
* @param ground Ground cell coordinates (may be empty).
|
||||
* @param obstacles Obstacle cell coordinates (may be empty).
|
||||
* @param empty Empty/free cell coordinates (may be empty).
|
||||
* @param cellSize Grid cell size in meters (must be > 0).
|
||||
* @param viewPoint View/sensor origin in the grid frame.
|
||||
*/
|
||||
LocalGrid(const cv::Mat & ground,
|
||||
const cv::Mat & obstacles,
|
||||
const cv::Mat & empty,
|
||||
float cellSize,
|
||||
const cv::Point3f & viewPoint = cv::Point3f(0,0,0));
|
||||
virtual ~LocalGrid() {}
|
||||
|
||||
/** @return True if every non-empty layer is `CV_32FC3`, `CV_32FC4` or `CV_32FC6` (see class doc). */
|
||||
bool is3D() const;
|
||||
|
||||
cv::Mat groundCells; ///< Ground cells (1×N, `CV_32FC2`–`CV_32FC7`, see class doc).
|
||||
cv::Mat obstacleCells; ///< Obstacle cells (same layout as @ref groundCells).
|
||||
cv::Mat emptyCells; ///< Empty/free cells (same layout as @ref groundCells).
|
||||
float cellSize; ///< Cell size in meters.
|
||||
cv::Point3f viewPoint; ///< View point used to build the grid.
|
||||
public:
|
||||
cv::Mat groundCells;
|
||||
cv::Mat obstacleCells;
|
||||
cv::Mat emptyCells;
|
||||
float cellSize;
|
||||
cv::Point3f viewPoint;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LocalGridCache
|
||||
* @brief Cache of @ref LocalGrid entries keyed by map node id.
|
||||
*
|
||||
* Used by @ref GlobalMap and its subclasses (e.g. @ref OccupancyGrid) to hold per-node
|
||||
* local grids before assembly. Node id `0` is stored as `-1` (temporary grid). Negative ids
|
||||
* passed to @ref add() are rejected.
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT LocalGridCache
|
||||
{
|
||||
public:
|
||||
LocalGridCache() {}
|
||||
virtual ~LocalGridCache() {}
|
||||
|
||||
/** @brief Inserts or replaces the grid for @p nodeId (from separate cell mats). */
|
||||
void add(int nodeId,
|
||||
const cv::Mat & ground,
|
||||
const cv::Mat & obstacles,
|
||||
@@ -108,22 +66,11 @@ public:
|
||||
float cellSize,
|
||||
const cv::Point3f & viewPoint = cv::Point3f(0,0,0));
|
||||
|
||||
/** @brief Inserts or replaces the grid for @p nodeId. */
|
||||
void add(int nodeId, const LocalGrid & localGrid);
|
||||
|
||||
/**
|
||||
* @brief Copies a grid to @p anotherCache if present here and absent there.
|
||||
* @return True if a grid was shared.
|
||||
*/
|
||||
bool shareTo(int nodeId, LocalGridCache & anotherCache) const;
|
||||
|
||||
/** @brief Approximate memory used by cached grids (bytes). */
|
||||
unsigned long getMemoryUsed() const;
|
||||
|
||||
/**
|
||||
* @brief Removes cached grids.
|
||||
* @param temporaryOnly If true, removes only entries with negative ids (e.g. `-1`).
|
||||
*/
|
||||
void clear(bool temporaryOnly = false);
|
||||
|
||||
size_t size() const {return localGrids_.size();}
|
||||
|
||||
@@ -39,61 +39,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class LocalGridMaker
|
||||
* @brief Builds per-node local occupancy grids from laser scans or depth clouds.
|
||||
*
|
||||
* Configured via `Grid/` parameters (@ref parseParameters()). Used by @ref Memory
|
||||
* to populate @ref LocalGrid cells (ground / obstacles / empty) stored on
|
||||
* @ref SensorData and cached in @ref LocalGridCache for global maps
|
||||
* (@ref OccupancyGrid, @ref OctoMap, @ref GridMap).
|
||||
*
|
||||
* **Sensor source** (`Grid/Sensor`, @ref isGridFromDepth()):
|
||||
* - `0` — laser scan only
|
||||
* - `1` — depth image(s) only (default)
|
||||
* - `2` — laser scan and depth
|
||||
*
|
||||
* **2D laser path:** projects a 2D @ref LaserScan with @ref util3d::occupancy2DFromLaserScan().
|
||||
*
|
||||
* **3D path:** segments the scan/cloud into ground and obstacles (@ref segmentCloud()),
|
||||
* then outputs @ref LocalGrid cell matrices (2D projection or 3D cells per `Grid/3D`).
|
||||
*
|
||||
* @see LocalGrid
|
||||
* @see Memory
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT LocalGridMaker
|
||||
{
|
||||
public:
|
||||
/** @brief Constructs with @ref parseParameters() on @p parameters (or defaults). */
|
||||
LocalGridMaker(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~LocalGridMaker();
|
||||
|
||||
/** @brief Updates grid settings from `Grid/` entries in @p parameters. */
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/** @return Current `Grid/CellSize` (m). */
|
||||
float getCellSize() const {return cellSize_;}
|
||||
/** @return True if occupancy is built from depth (`Grid/Sensor` is 1 or 2). */
|
||||
bool isGridFromDepth() const {return occupancySensor_;}
|
||||
/** @return True if `Grid/MapFrameProjection` is enabled. */
|
||||
bool isMapFrameProjection() const {return projMapFrame_;}
|
||||
|
||||
/**
|
||||
* @brief Segments a point cloud into ground and obstacle indices.
|
||||
*
|
||||
* Applies optional voxel filtering (`Grid/PreVoxelFiltering`, leaf size `Grid/CellSize`),
|
||||
* footprint crop, height filtering, then either normal-based ground segmentation
|
||||
* (`Grid/NormalsSegmentation`) or Z passthrough.
|
||||
*
|
||||
* @param cloud Input cloud (sensor frame, transformed internally using @p pose). Must be non-null; an empty cloud is allowed and returns empty outputs.
|
||||
* @param indices Subset of @p cloud to process (must be non-null; empty = all points when the cloud is dense).
|
||||
* @param pose Node pose (used for map-frame projection and footprint).
|
||||
* @param viewPoint Sensor origin for segmentation / ray tracing.
|
||||
* @param groundIndices Output indices of ground points.
|
||||
* @param obstaclesIndices Output indices of obstacle points.
|
||||
* @param flatObstacles Optional output for flat obstacle clusters.
|
||||
* @return Segmented cloud (voxel-downsampled when `Grid/PreVoxelFiltering` is true, using `Grid/CellSize`).
|
||||
*/
|
||||
template<typename PointT>
|
||||
typename pcl::PointCloud<PointT>::Ptr segmentCloud(
|
||||
const typename pcl::PointCloud<PointT>::Ptr & cloud,
|
||||
@@ -104,22 +60,6 @@ public:
|
||||
pcl::IndicesPtr & obstaclesIndices, // output cloud indices
|
||||
pcl::IndicesPtr * flatObstacles = 0) const; // output cloud indices
|
||||
|
||||
/**
|
||||
* @brief Creates a local grid from a @ref Signature's laser scan or depth data.
|
||||
* @param node Signature with sensor data (pose used for 3D scans).
|
||||
* @param groundCells Output ground cells (@ref LocalGrid format).
|
||||
* @param obstacleCells Output obstacle cells.
|
||||
* @param emptyCells Output empty/free cells. Behavior depends on the sensor path:
|
||||
* - **2D laser, laser-only mode** (`Grid/Sensor`=0 and a 2D @ref LaserScan):
|
||||
* uses @ref util3d::occupancy2DFromLaserScan() / @ref util3d::create2DMap().
|
||||
* Free space along each hit beam is always ray-traced (sensor → obstacle).
|
||||
* `Grid/Scan2dUnknownSpaceFilled` is separate: when true, it additionally
|
||||
* sweeps unknown angular gaps between the first and last hit (sparse FOV /
|
||||
* “holes” in coverage) out to `Grid/RangeMax` or scan max range.
|
||||
* - **Other cases** (3D scan, depth, `Grid/Sensor`=2, etc.): see
|
||||
* the @ref LaserScan overload of @ref createLocalMap().
|
||||
* @param viewPoint Output view point used for the grid.
|
||||
*/
|
||||
void createLocalMap(
|
||||
const Signature & node,
|
||||
cv::Mat & groundCells,
|
||||
@@ -127,19 +67,6 @@ public:
|
||||
cv::Mat & emptyCells,
|
||||
cv::Point3f & viewPoint);
|
||||
|
||||
/**
|
||||
* @brief Creates a local grid from a 3D (or organized) @ref LaserScan.
|
||||
* @param cloud Input scan in sensor frame.
|
||||
* @param pose Node pose in map/odom frame.
|
||||
* @param groundCells Output ground cells.
|
||||
* @param obstacleCells Output obstacle cells.
|
||||
* @param emptyCells Output empty/free cells. Filled when `Grid/RayTracing` is true:
|
||||
* 3D via OctoMap if `Grid/3D` and OctoMap support are enabled
|
||||
* (`Grid/RangeMax`, `Grid/CellSize`); otherwise 2D ray fill via
|
||||
* `occupancy2DFromLaserScan` when `Grid/3D` is false (`Grid/RangeMax`,
|
||||
* `Grid/CellSize`).
|
||||
* @param viewPointInOut View point (may be rotated if @ref isMapFrameProjection()).
|
||||
*/
|
||||
void createLocalMap(
|
||||
const LaserScan & cloud,
|
||||
const Transform & pose,
|
||||
@@ -148,26 +75,6 @@ public:
|
||||
cv::Mat & emptyCells,
|
||||
cv::Point3f & viewPointInOut) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Radius filters the points closer than @p maxRange, keeping the farther
|
||||
* points as-is (in case we want to ray trace empty space beyond max range).
|
||||
* @param cloud Input cloud.
|
||||
* @param indices Subset of @p cloud to filter. Asserted to be non-null and non-empty
|
||||
* (@ref util3d::radiusFiltering() would filter the whole cloud otherwise).
|
||||
* @param maxRange Range (m) above which points are not filtered. 0 means no limit.
|
||||
* @param radiusSearch Radius (m) used for the noise filtering.
|
||||
* @param minNeighborsInRadius Minimum neighbors required inside @p radiusSearch.
|
||||
* @return Indices of the points that passed the filtering, plus those beyond @p maxRange.
|
||||
*/
|
||||
template<typename PointT>
|
||||
static pcl::IndicesPtr noiseFilteringWithMaxRange(
|
||||
const typename pcl::PointCloud<PointT>::Ptr & cloud,
|
||||
const pcl::IndicesPtr & indices,
|
||||
float maxRange,
|
||||
float radiusSearch,
|
||||
int minNeighborsInRadius);
|
||||
|
||||
protected:
|
||||
ParametersMap parameters_;
|
||||
|
||||
@@ -205,4 +112,4 @@ protected:
|
||||
|
||||
#include <rtabmap/core/impl/LocalMapMaker.hpp>
|
||||
|
||||
#endif /* SRC_LOCAL_MAP_H_ */
|
||||
#endif /* SRC_MAP_H_ */
|
||||
|
||||
@@ -32,14 +32,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <rtabmap/core/CameraModel.h>
|
||||
#include <opencv2/opencv_modules.hpp>
|
||||
|
||||
#if (CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION==4 && CV_MINOR_VERSION >=7)) && defined(HAVE_OPENCV_OBJDETECT)
|
||||
#include <opencv2/objdetect.hpp>
|
||||
#elif defined(HAVE_OPENCV_ARUCO)
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
#include <opencv2/aruco.hpp>
|
||||
#endif
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
typedef std::map<int, Transform> MapIdPose;
|
||||
|
||||
class MarkerInfo {
|
||||
public:
|
||||
MarkerInfo(int id, float length, Transform pose) :
|
||||
@@ -69,6 +69,14 @@ public:
|
||||
virtual ~MarkerDetector();
|
||||
void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
// Use the other detect(), in which the returned map contains the length of each marker detected.
|
||||
RTABMAP_DEPRECATED
|
||||
MapIdPose detect(const cv::Mat & image,
|
||||
const CameraModel & model,
|
||||
const cv::Mat & depth = cv::Mat(),
|
||||
float * estimatedMarkerLength = 0,
|
||||
cv::Mat * imageWithDetections = 0);
|
||||
|
||||
std::map<int, MarkerInfo> detect(const cv::Mat & image,
|
||||
const std::vector<CameraModel> & models,
|
||||
const cv::Mat & depth = cv::Mat(),
|
||||
@@ -89,11 +97,8 @@ private:
|
||||
float maxRange_;
|
||||
float minRange_;
|
||||
int dictionaryId_;
|
||||
#if ((CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION==4 && CV_MINOR_VERSION >=7)) && defined(HAVE_OPENCV_OBJDETECT)) || defined(HAVE_OPENCV_ARUCO)
|
||||
#if CV_MAJOR_VERSION > 4 || (CV_MAJOR_VERSION==4 && CV_MINOR_VERSION >=7)
|
||||
cv::Ptr<cv::aruco::ArucoDetector> arucoDetector_;
|
||||
#endif
|
||||
cv::Ptr<cv::aruco::DetectorParameters> detectorParams_;
|
||||
#ifdef HAVE_OPENCV_ARUCO
|
||||
cv::Ptr<cv::aruco::DetectorParameters> detectorParams_;
|
||||
cv::Ptr<cv::aruco::Dictionary> dictionary_;
|
||||
#endif
|
||||
void * apriltagLibDetector_;
|
||||
|
||||
@@ -41,11 +41,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <set>
|
||||
#include "rtabmap/utilite/UStl.h"
|
||||
#include <opencv2/core/core.hpp>
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/features2d/features2d.hpp>
|
||||
#else
|
||||
#include <opencv2/features.hpp>
|
||||
#endif
|
||||
#include <pcl/pcl_config.h>
|
||||
|
||||
namespace rtabmap {
|
||||
@@ -65,213 +61,47 @@ class LocalGridMaker;
|
||||
class MarkerDetector;
|
||||
class GlobalDescriptorExtractor;
|
||||
|
||||
/**
|
||||
* @class Memory
|
||||
* @brief Three-tiered memory management (STM, WM, LTM) for RTAB-Map.
|
||||
*
|
||||
* Memory is the core map data structure used by @ref Rtabmap. It stores observations
|
||||
* as @ref Signature nodes connected by @ref Link edges and orchestrates their lifecycle
|
||||
* across three tiers:
|
||||
*
|
||||
* - **Short-Term Memory (STM)**: most recently added signatures, kept at a fixed size
|
||||
* (see @ref Parameters::kMemSTMSize()). Used to delay recently observed places before
|
||||
* they become candidates for loop closure.
|
||||
* - **Working Memory (WM)**: signatures available for loop-closure likelihood
|
||||
* computation in the current iteration. Older signatures are transferred from WM
|
||||
* to LTM by @ref forget() to bound iteration time.
|
||||
* - **Long-Term Memory (LTM)**: persisted in the database via @ref DBDriver. Signatures
|
||||
* can be brought back to WM with @ref reactivateSignatures() when their neighbors still
|
||||
* in WM are good loop-closure candidates.
|
||||
*
|
||||
* The class also owns a visual word dictionary (@ref VWDictionary), feature extractor
|
||||
* (@ref Feature2D) and registration pipelines (@ref Registration, @ref RegistrationVis,
|
||||
* @ref RegistrationIcp) used to compute relative transforms between signatures.
|
||||
*
|
||||
* Typical iteration: @ref update() adds a new @ref SensorData as a @ref Signature in
|
||||
* STM; @ref computeLikelihood() scores it against WM; the @ref Rtabmap caller decides
|
||||
* on loop closures with @ref BayesFilter; @ref cleanup() drops bad signatures;
|
||||
* @ref forget() transfers oldest WM signatures to LTM and @ref reactivateSignatures()
|
||||
* pulls relevant ones back.
|
||||
*
|
||||
* @see Signature
|
||||
* @see DBDriver
|
||||
* @see VWDictionary
|
||||
* @see Rtabmap
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Memory
|
||||
{
|
||||
public:
|
||||
/** @brief First valid signature id assigned to a new signature (positive integer). */
|
||||
static const int kIdStart;
|
||||
/** @brief Reserved id for the "virtual place" used by the Bayes filter (negative). */
|
||||
static const int kIdVirtual;
|
||||
/** @brief Sentinel value indicating an invalid signature id (zero). */
|
||||
static const int kIdInvalid;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a Memory instance with the given parameters.
|
||||
*
|
||||
* The database is not opened here; call @ref init() to open or create a database
|
||||
* and load persisted state. @p parameters may include any key from @ref Parameters
|
||||
* (memory, keypoint, registration, etc.); missing keys fall back to defaults.
|
||||
*/
|
||||
Memory(const ParametersMap & parameters = ParametersMap());
|
||||
virtual ~Memory();
|
||||
|
||||
/**
|
||||
* @brief Re-parses parameters and propagates them to owned sub-objects.
|
||||
*
|
||||
* Forwards the relevant subset to @ref VWDictionary, @ref Feature2D, the registration
|
||||
* pipelines and the database driver. Safe to call at runtime to change settings.
|
||||
*/
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
/** @return The most recent parameter map applied via the constructor or @ref parseParameters(). */
|
||||
virtual const ParametersMap & getParameters() const {return parameters_;}
|
||||
/**
|
||||
* @brief Adds a sensor observation to the map (overload without odometry pose).
|
||||
*
|
||||
* Equivalent to calling the full @ref update() with an identity pose and empty covariance.
|
||||
* The new signature is added to STM; oldest STM entries are promoted to WM as needed.
|
||||
*
|
||||
* @param data Sensor data (images, scan, user data, odometry features) for this frame.
|
||||
* @param stats Optional output statistics receiver for timing/diagnostic values.
|
||||
* @return True if a signature was successfully created and added, false otherwise.
|
||||
*/
|
||||
bool update(const SensorData & data,
|
||||
Statistics * stats = 0);
|
||||
/**
|
||||
* @brief Adds a sensor observation with odometry pose and velocity to the map.
|
||||
*
|
||||
* Creates a new @ref Signature, extracts visual words, links it to the previous
|
||||
* STM signature with a neighbor link, runs rehearsal against STM and promotes
|
||||
* the oldest STM signature to WM if STM is full.
|
||||
*
|
||||
* @param data Sensor data (images, scan, user data, odometry features).
|
||||
* @param pose Odometry pose at this frame (null if odometry not used).
|
||||
* @param covariance 6x6 odometry covariance (or empty if null odometry is provided).
|
||||
* @param velocity Optional 6-vector (vx, vy, vz, vroll, vpitch, vyaw).
|
||||
* @param stats Optional output statistics receiver.
|
||||
* @return True on success, false if signature creation failed.
|
||||
*/
|
||||
bool update(const SensorData & data,
|
||||
const Transform & pose,
|
||||
const cv::Mat & covariance,
|
||||
const std::vector<float> & velocity = std::vector<float>(), // vx,vy,vz,vroll,vpitch,vyaw
|
||||
Statistics * stats = 0);
|
||||
/**
|
||||
* @brief Opens or creates a database and loads existing state into WM.
|
||||
*
|
||||
* @param dbUrl Path to the database file (empty for in-memory).
|
||||
* @param dbOverwritten If true, deletes the existing file before opening.
|
||||
* @param parameters Optional parameter override applied before loading.
|
||||
* @param postInitClosingEvents If true, posts @ref RtabmapEventInit events for
|
||||
* progress reporting (used by GUI).
|
||||
* @return True on success, false if the database could not be opened.
|
||||
*/
|
||||
bool init(const std::string & dbUrl,
|
||||
bool dbOverwritten = false,
|
||||
const ParametersMap & parameters = ParametersMap(),
|
||||
bool postInitClosingEvents = false);
|
||||
/**
|
||||
* @brief Flushes pending data and closes the database connection.
|
||||
*
|
||||
* @param databaseSaved If true, persists STM/WM signatures and statistics before closing.
|
||||
* If false, in-memory state is discarded.
|
||||
* @param postInitClosingEvents If true, posts progress events while closing.
|
||||
* @param ouputDatabasePath If non-empty, the database is copied to this path on close. If a
|
||||
* database on disk was initially created/loaded on a different path, it will be updated with latest
|
||||
* changes and renamed to the output path.
|
||||
*/
|
||||
void close(bool databaseSaved = true, bool postInitClosingEvents = false, const std::string & ouputDatabasePath = "");
|
||||
/**
|
||||
* @brief Computes loop-closure likelihood of @p signature against a set of WM ids.
|
||||
*
|
||||
* Compares visual words (tf-idf if enabled) between @p signature and each id in
|
||||
* @p ids and returns a normalized likelihood per id.
|
||||
*
|
||||
* @param signature Query signature (typically the last added one).
|
||||
* @param ids Candidate signature ids in working memory.
|
||||
* @return Map from id to likelihood score.
|
||||
*/
|
||||
std::map<int, float> computeLikelihood(const Signature * signature,
|
||||
const std::list<int> & ids);
|
||||
/**
|
||||
* @brief Starts a new map id, breaking session continuity (e.g. after localization loss).
|
||||
*
|
||||
* @param reducedIds If non-null, populated with id remappings produced by graph reduction
|
||||
* triggered by the new map.
|
||||
* @return The new map id (auto-incremented).
|
||||
*/
|
||||
int incrementMapId(std::map<int, int> * reducedIds = 0);
|
||||
/**
|
||||
* @brief Refreshes the age of @p signatureId in working memory, marking it as recent.
|
||||
*
|
||||
* Used to keep loop-closure hypotheses active so they are not transferred to LTM
|
||||
* during the next @ref forget() call.
|
||||
*/
|
||||
void updateAge(int signatureId);
|
||||
|
||||
/**
|
||||
* @brief Transfers oldest signatures from WM to LTM to respect memory and/or time limits.
|
||||
*
|
||||
* Two regimes are used depending on the visual word dictionary state:
|
||||
* - **Word-count regime**: active only when mapping mode is on, the @ref VWDictionary
|
||||
* is in incremental mode, contains at least one word, and is *not* using incremental
|
||||
* FLANN. In this regime, signatures are transferred until the number of visual words
|
||||
* removed from the dictionary catches up with the number of new words indexed since
|
||||
* the previous iteration.
|
||||
* - **Signature-count regime**: used in every other case (localization mode, dictionary
|
||||
* not incremental, dictionary still empty -- e.g. lidar-only mapping or no feature
|
||||
* extraction -- or incremental FLANN, where the word count is no longer the bottleneck).
|
||||
* In this regime, at least one more signature than the count added/retrieved in the
|
||||
* previous iteration is transferred, regardless of words.
|
||||
*
|
||||
* In both regimes, candidate selection (see @c getRemovableSignatures()) honors
|
||||
* @p ignoredIds, skips intermediate nodes, and excludes WM nodes linked to STM (to
|
||||
* preserve rehearsal). Intermediate (weight==-1) nodes linked to a transferred
|
||||
* signature are dragged out with it.
|
||||
*
|
||||
* @param ignoredIds Signatures that must not be transferred (e.g. STM, retrieved ids, on the planned path).
|
||||
* @return Ids of signatures moved to LTM, in transfer order.
|
||||
*/
|
||||
std::list<int> forget(const std::set<int> & ignoredIds = std::set<int>());
|
||||
/**
|
||||
* @brief Reloads signatures from LTM into WM.
|
||||
*
|
||||
* @param ids Candidate ids; those already in WM/STM are ignored.
|
||||
* @param maxLoaded Hard cap on number of ids actually loaded (0 = unlimited).
|
||||
* @param timeDbAccess Output: time spent in the database driver (seconds).
|
||||
* @return Ids effectively brought back to WM.
|
||||
*/
|
||||
std::set<int> reactivateSignatures(const std::list<int> & ids, unsigned int maxLoaded, double & timeDbAccess);
|
||||
|
||||
/**
|
||||
* @brief Drops the last signature if flagged as bad, or any signature in localization mode.
|
||||
* @return Id of the removed signature, or 0 if none was removed.
|
||||
*/
|
||||
int cleanup();
|
||||
/** @brief Persists @p statistics to the database; @p saveWMState records the WM id list. */
|
||||
void saveStatistics(const Statistics & statistics, bool saveWMState);
|
||||
/** @brief Stores a preview image (typically a thumbnail of the last frame) in the database. */
|
||||
void savePreviewImage(const cv::Mat & image) const;
|
||||
/** @brief Loads the preview image previously written by @ref savePreviewImage(). */
|
||||
cv::Mat loadPreviewImage() const;
|
||||
/** @brief Persists an optimized pose graph and the last localization pose for next session. */
|
||||
void saveOptimizedPoses(const std::map<int, Transform> & optimizedPoses, const Transform & lastlocalizationPose) const;
|
||||
/** @brief Loads optimized poses previously written by @ref saveOptimizedPoses(). */
|
||||
std::map<int, Transform> loadOptimizedPoses(Transform * lastlocalizationPose) const;
|
||||
/** @brief Persists a 2D occupancy grid (origin @p xMin, @p yMin and resolution @p cellSize). */
|
||||
void save2DMap(const cv::Mat & map, float xMin, float yMin, float cellSize) const;
|
||||
/** @brief Loads the 2D occupancy grid previously written by @ref save2DMap(). */
|
||||
cv::Mat load2DMap(float & xMin, float & yMin, float & cellSize) const;
|
||||
/**
|
||||
* @brief Persists an optimized textured/colored mesh to the database.
|
||||
* @param cloud Point cloud (XYZRGB) of vertices.
|
||||
* @param polygons Per-texture list of polygons; each polygon is a list of vertex indices.
|
||||
* @param texCoords Per-texture list of UV coords matching @p polygons.
|
||||
* @param textures Concatenated texture images (square, equal-sized).
|
||||
*/
|
||||
void saveOptimizedMesh(
|
||||
const cv::Mat & cloud,
|
||||
const std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > & polygons = std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > >(), // Textures -> polygons -> vertices
|
||||
@@ -281,7 +111,6 @@ public:
|
||||
const std::vector<std::vector<Eigen::Vector2f> > & texCoords = std::vector<std::vector<Eigen::Vector2f> >(), // Textures -> uv coords for each vertex of the polygons
|
||||
#endif
|
||||
const cv::Mat & textures = cv::Mat()) const; // concatenated textures (assuming square textures with all same size)
|
||||
/** @brief Loads the optimized mesh previously written by @ref saveOptimizedMesh(). */
|
||||
cv::Mat loadOptimizedMesh(
|
||||
std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > * polygons = 0,
|
||||
#if PCL_VERSION_COMPARE(>=, 1, 8, 0)
|
||||
@@ -290,39 +119,12 @@ public:
|
||||
std::vector<std::vector<Eigen::Vector2f> > * texCoords = 0,
|
||||
#endif
|
||||
cv::Mat * textures = 0) const;
|
||||
/** @brief Forces the database driver to flush any queued signature/word saves. */
|
||||
void emptyTrash();
|
||||
/** @brief Blocks until the asynchronous database write thread has finished pending work. */
|
||||
void joinTrashThread();
|
||||
/**
|
||||
* @brief Adds a graph link between two signatures.
|
||||
* @param link Link to add (type, transform, covariance).
|
||||
* @param addInDatabase If true, the link is also added when one of the ids is only in LTM.
|
||||
* @return True if the link was added, false on conflict or missing nodes.
|
||||
*/
|
||||
bool addLink(const Link & link, bool addInDatabase = false);
|
||||
/** @brief Replaces an existing link with @p link (same endpoints and type). */
|
||||
void updateLink(const Link & link, bool updateInDatabase = false);
|
||||
/** @brief Removes every virtual link in WM. */
|
||||
void removeAllVirtualLinks();
|
||||
/** @brief Removes virtual links attached to @p signatureId. */
|
||||
void removeVirtualLinks(int signatureId);
|
||||
/**
|
||||
* @brief Breadth-first walk of the pose graph from @p signatureId.
|
||||
*
|
||||
* Visits neighbor and (optionally) loop-closure neighbors up to @p maxGraphDepth.
|
||||
*
|
||||
* @param signatureId Starting node.
|
||||
* @param maxGraphDepth Maximum graph distance (0 = infinite graph depth).
|
||||
* @param maxCheckedInDatabase Cap on LTM look-ups (-1 = unlimited, 0 = WM only).
|
||||
* @param incrementMarginOnLoop If true, loop-closure links count toward depth.
|
||||
* @param ignoreLoopIds If true, loop-closure neighbors are not traversed.
|
||||
* @param ignoreIntermediateNodes If true, weight==-1 intermediate nodes are skipped.
|
||||
* @param ignoreLocalSpaceLoopIds If true, only global loop closures are traversed.
|
||||
* @param nodesSet If non-empty, traversal is constrained to these ids.
|
||||
* @param dbAccessTime Output: time spent in database access (seconds).
|
||||
* @return Map from visited node id to graph depth, including @p signatureId (with graph depth of 0)
|
||||
*/
|
||||
std::map<int, int> getNeighborsId(
|
||||
int signatureId,
|
||||
int maxGraphDepth,
|
||||
@@ -333,206 +135,63 @@ public:
|
||||
bool ignoreLocalSpaceLoopIds = false,
|
||||
const std::set<int> & nodesSet = std::set<int>(),
|
||||
double * dbAccessTime = 0) const;
|
||||
/**
|
||||
* @brief Returns neighbor ids within a Euclidean radius using optimized poses.
|
||||
* @param signatureId Query node id.
|
||||
* @param radius Maximum distance from @p signatureId (meters).
|
||||
* @param optimizedPoses Pose graph after optimization (used for distances).
|
||||
* @param maxGraphDepth Maximum graph distance (in terms of nodes) to bound the search.
|
||||
* @return Map from node id to squared distance from @p signatureId.
|
||||
*/
|
||||
std::map<int, float> getNeighborsIdRadius(
|
||||
int signatureId,
|
||||
float radius,
|
||||
const std::map<int, Transform> & optimizedPoses,
|
||||
int maxGraphDepth) const;
|
||||
/** @brief Marks @p locationId as intermediate (weight = -1); excludes it from loop closure. */
|
||||
void convertToIntermediate(int locationId);
|
||||
/**
|
||||
* @brief Removes @p locationId from WM/STM and the database.
|
||||
* @param locationId Id of the signature to delete.
|
||||
* @param deletedWords Optional output: words whose reference count dropped to zero.
|
||||
* @param keepLinkedInDb If true, the location keeps its links, weight and label and
|
||||
* stays part of the graph in the database. If false (default),
|
||||
* it is unlinked first -- links removed on both sides, weight
|
||||
* invalidated, label cleared -- so at best it is kept as
|
||||
* history only and no longer appears in
|
||||
* @ref getAllSignatureIds(). An unlinked location is written
|
||||
* to the database only if it was already saved there or if
|
||||
* @ref Parameters::kMemNotLinkedNodesKept() is true (default);
|
||||
* otherwise it is discarded outright.
|
||||
*/
|
||||
void deleteLocation(int locationId, std::list<int> * deletedWords = 0, bool keepLinkedInDb = false);
|
||||
/** @brief Forces @p locationId to be flushed to the database. */
|
||||
void deleteLocation(int locationId, std::list<int> * deletedWords = 0);
|
||||
void saveLocationData(int locationId);
|
||||
/** @brief Removes any link between @p idA and @p idB (both directions). */
|
||||
void removeLink(int idA, int idB);
|
||||
/** @brief Strips raw images, scan, user data and/or occupancy grid from @p id to save memory (RAM). This doesn't clear any compressed data.*/
|
||||
void removeRawData(int id, bool image = true, bool scan = true, bool userData = true, bool occupancyGrid = true);
|
||||
/**
|
||||
* @brief Merges @p id with a close neighbor (graph reduction).
|
||||
* @param id Node to reduce.
|
||||
* @param maxDistance Maximum distance to a neighbor to allow the merge (meters).
|
||||
* @param keepLinkedInDb Same meaning as in @ref deleteLocation(): if true, the merged
|
||||
* node keeps its links, weight and label and stays part of the
|
||||
* graph in the database. If false (default), it is unlinked
|
||||
* first -- links removed on both sides, weight invalidated,
|
||||
* label cleared -- so at best it is kept as history only and no
|
||||
* longer appears in @ref getAllSignatureIds(). An unlinked node
|
||||
* is written to the database only if it was already saved there
|
||||
* or if @ref Parameters::kMemNotLinkedNodesKept() is true
|
||||
* (default); otherwise it is discarded outright.
|
||||
* @param direction Restrict merge target: 0=any, 1=previous neighbor, 2=next neighbor.
|
||||
* @return Id of the node @p id was merged into, or 0 if no reduction was performed.
|
||||
*/
|
||||
int reduceNode(int id, float maxDistance = 0.0f, bool keepLinkedInDb = false, int direction = 0);
|
||||
/**
|
||||
* @brief Enables a dummy visual word dictionary (no descriptors kept, word ids only).
|
||||
* @note Must be called before @ref init(); ignored (with an error logged) once the
|
||||
* database driver is set.
|
||||
* @note Silently disabled by @ref init() when the database holds no dictionary,
|
||||
* since there would be nothing to skip loading, and the memory would
|
||||
* otherwise be left unable to add new nodes.
|
||||
*/
|
||||
void setDummyDictionary(bool enabled);
|
||||
|
||||
/** @return Working memory as { signature id, age } (does not include STM). */
|
||||
//getters
|
||||
const std::map<int, double> & getWorkingMem() const {return _workingMem;}
|
||||
/**
|
||||
* @brief Returns the number of signatures in working memory, excluding the virtual place.
|
||||
* @param ignoreIntermediateNodes If true, intermediate nodes (weight = -1) are not counted.
|
||||
*/
|
||||
size_t getWorkingMemSize(bool ignoreIntermediateNodes = false) const;
|
||||
/** @return Number of intermediate nodes (weight = -1) currently in working memory. */
|
||||
int getWorkingMemIntermediateNodesCount() const {return _workingMemIntermediateNodesCount;}
|
||||
/** @return Number of intermediate nodes (weight = -1) currently in short-term memory. */
|
||||
int getStMemIntermediateNodesCount() const {return _stMemIntermediateNodesCount;}
|
||||
/** @return Set of signature ids currently in short-term memory. */
|
||||
const std::set<int> & getStMem() const {return _stMem;}
|
||||
/** @return Configured maximum STM size (@ref Parameters::kMemSTMSize()). */
|
||||
int getMaxStMemSize() const {return _maxStMemSize;}
|
||||
/** @brief Returns neighbor (sequential) links of @p signatureId; @p lookInDatabase also checks LTM. */
|
||||
std::multimap<int, Link> getNeighborLinks(int signatureId,
|
||||
bool lookInDatabase = false) const;
|
||||
/** @brief Returns loop-closure links of @p signatureId; @p lookInDatabase also checks LTM. */
|
||||
std::multimap<int, Link> getLoopClosureLinks(int signatureId,
|
||||
bool lookInDatabase = false) const;
|
||||
/**
|
||||
* @brief Returns all links of @p signatureId (neighbor, loop, prior, gravity, ...).
|
||||
* @param signatureId Source node id (can also be a landmark id).
|
||||
* @param lookInDatabase Also query LTM for links.
|
||||
* @param withLandmarks Include landmark links in the result.
|
||||
*/
|
||||
std::multimap<int, Link> getLinks(int signatureId,
|
||||
std::multimap<int, Link> getLinks(int signatureId, // can be also used to get links from landmarks
|
||||
bool lookInDatabase = false,
|
||||
bool withLandmarks = false) const;
|
||||
/** @brief Returns links of every signature; @p ignoreNullLinks drops empty placeholders. */
|
||||
std::multimap<int, Link> getAllLinks(bool lookInDatabase, bool ignoreNullLinks = true, bool withLandmarks = false) const;
|
||||
/** @return True if raw binary data (images, scans) is kept in memory after compression. */
|
||||
bool isBinDataKept() const {return _binDataKept;}
|
||||
/** @return Similarity threshold used by rehearsal (@ref Parameters::kMemRehearsalSimilarity()). */
|
||||
float getSimilarityThreshold() const {return _similarityThreshold;}
|
||||
/** @return Map from signature id to weight (rehearsal accumulation count) for WM and STM. */
|
||||
std::map<int, int> getWeights() const;
|
||||
/** @return Id of the most recently added signature, or 0 if none. */
|
||||
int getLastSignatureId() const;
|
||||
/**
|
||||
* @brief Returns the most recent WM signature.
|
||||
* @param ignoreIntermediateNodes If true, skips weight==-1 placeholder nodes.
|
||||
*/
|
||||
const Signature * getLastWorkingSignature(bool ignoreIntermediateNodes) const;
|
||||
/** @brief Returns all nodes observing landmark @p landmarkId, mapped to the observation link. */
|
||||
std::map<int, Link> getNodesObservingLandmark(int landmarkId, bool lookInDatabase) const;
|
||||
/** @return Signature id labeled @p label, or 0 if not found. */
|
||||
int getSignatureIdByLabel(const std::string & label, bool lookInDatabase = true) const;
|
||||
/**
|
||||
* @brief Assigns or removes a label on @p id.
|
||||
* @param id Signature id; pass 0 to remove an existing label by name.
|
||||
* @param label Label text; empty to remove.
|
||||
* @return True if the label was applied.
|
||||
*/
|
||||
bool labelSignature(int id, const std::string & label);
|
||||
/** @return Map from signature id to non-empty label (including STM+WM+LTM). */
|
||||
const std::map<int, std::string> & getAllLabels() const {return _labels;}
|
||||
/** @return Reverse landmark index: { landmark id (negative), nodes observing it }. */
|
||||
const std::map<int, std::set<int> > & getLandmarksIndex() const {return _landmarksIndex;}
|
||||
/** @return True if every persisted node (in LTM) is currently loaded in WM/STM. */
|
||||
bool allNodesInWM() const {return _allNodesInWM;}
|
||||
|
||||
/**
|
||||
* @brief Attaches user data to signature @p id, compressing it on the fly if needed.
|
||||
*
|
||||
* The format is detected automatically: a single-row @c CV_8UC1 matrix is treated
|
||||
* as already-compressed data and stored as-is; anything else is considered raw and
|
||||
* compressed before being stored.
|
||||
*
|
||||
* @note If you pass one-dimensional unsigned 8-bit raw data, transpose it so it has
|
||||
* multiple rows (not multiple columns), otherwise it will be misdetected as
|
||||
* already compressed.
|
||||
*
|
||||
* @param id Target signature id (must be in WM/STM or LTM).
|
||||
* @param data Raw or pre-compressed user data.
|
||||
* @return True if the data was attached, false if @p id was not found.
|
||||
* Set user data. Detect automatically if raw or compressed. If raw, the data is
|
||||
* compressed too. A matrix of type CV_8UC1 with 1 row is considered as compressed.
|
||||
* If you have one dimension unsigned 8 bits raw data, make sure to transpose it
|
||||
* (to have multiple rows instead of multiple columns) in order to be detected as
|
||||
* not compressed.
|
||||
*/
|
||||
bool setUserData(int id, const cv::Mat & data);
|
||||
/** @return On-disk database size in bytes. */
|
||||
int getDatabaseMemoryUsed() const;
|
||||
/** @return Schema version of the open database (e.g. "0.20.0"). */
|
||||
int getDatabaseMemoryUsed() const; // in bytes
|
||||
std::string getDatabaseVersion() const;
|
||||
/** @return File path of the open database (empty if in-memory). */
|
||||
std::string getDatabaseUrl() const;
|
||||
/** @return Last @ref emptyTrash() flush time in seconds. */
|
||||
// Record changes made to the database until it is closed, then write a compact delta
|
||||
// to outputUrl (empty disables). outputUrl MUST use the ".dbu" (db update) extension.
|
||||
// Returns true if recording started. See DBDriver.
|
||||
bool trackDatabaseChanges(const std::string & outputUrl);
|
||||
double getDbSavingTime() const;
|
||||
/** @return Map id of signature @p id; @p lookInDatabase also queries LTM. */
|
||||
int getMapId(int id, bool lookInDatabase = false) const;
|
||||
/** @return Odometry pose stored with @p signatureId (null if unknown). */
|
||||
Transform getOdomPose(int signatureId, bool lookInDatabase = false) const;
|
||||
/** @return Ground-truth pose stored with @p signatureId (null if unknown). */
|
||||
Transform getGroundTruthPose(int signatureId, bool lookInDatabase = false) const;
|
||||
/** @return Ground-truth poses for nodes currently in WM/STM. */
|
||||
const std::map<int, Transform> & getGroundTruths() const {return _groundTruths;}
|
||||
/**
|
||||
* @brief Returns a GPS fix for @p id, falling back to the nearest GPS-tagged neighbor.
|
||||
*
|
||||
* Two cases:
|
||||
* - If @p id has a GPS fix attached, @p gps is set to that fix and @p offsetENU
|
||||
* is left at identity.
|
||||
* - Otherwise, the graph is searched (via @ref getNeighborsId() with depth
|
||||
* @p maxGraphDepth, ignoring loop closures) for the closest neighbor that has a
|
||||
* GPS fix. When one is found, @p gps is set to that neighbor's fix and
|
||||
* @p offsetENU is the rigid transform from that neighbor's pose to @p id,
|
||||
* expressed in ENU coordinates (derived from the neighbor's heading/bearing).
|
||||
* Applying @p offsetENU on top of the GPS-derived pose of the neighbor yields
|
||||
* the ENU pose of @p id.
|
||||
*
|
||||
* If no GPS fix is found on @p id or any reachable neighbor, @p gps is returned
|
||||
* empty (@c gps.stamp()==0) and @p offsetENU is identity.
|
||||
*
|
||||
* @param id Query signature id.
|
||||
* @param gps Output GPS fix (empty if none found).
|
||||
* @param offsetENU Output ENU-frame offset from the GPS-tagged node to @p id
|
||||
* (identity when @p id itself carries the GPS fix or when none is found).
|
||||
* @param lookInDatabase If true, also fetch missing nodes from LTM during the search.
|
||||
* @param maxGraphDepth Maximum graph depth used to look for a GPS-tagged neighbor
|
||||
* when @p id has none (0 = no depth limit, i.e. search the whole reachable graph).
|
||||
*/
|
||||
const std::map<int, Transform> & getGroundTruths() const {return _groundTruths;} // only those in working+STM memory
|
||||
void getGPS(int id, GPS & gps, Transform & offsetENU, bool lookInDatabase, int maxGraphDepth = 0) const;
|
||||
/**
|
||||
* @brief Reads metadata of @p signatureId (no images/scan/words).
|
||||
*
|
||||
* @param signatureId Node id.
|
||||
* @param odomPose Output odometry pose (null if not set).
|
||||
* @param mapId Output map id.
|
||||
* @param weight Output rehearsal weight.
|
||||
* @param label Output label (empty if none).
|
||||
* @param stamp Output timestamp (seconds, epoch).
|
||||
* @param groundTruth Output ground-truth pose (null if not set).
|
||||
* @param velocity Output 6-vector velocity (empty if not set).
|
||||
* @param gps Output GPS fix (invalid if not set).
|
||||
* @param sensors Output environmental sensor readings.
|
||||
* @param lookInDatabase Also query LTM.
|
||||
* @return True if @p signatureId was found.
|
||||
*/
|
||||
bool getNodeInfo(int signatureId,
|
||||
Transform & odomPose,
|
||||
int & mapId,
|
||||
@@ -544,183 +203,40 @@ public:
|
||||
GPS & gps,
|
||||
EnvSensors & sensors,
|
||||
bool lookInDatabase = false) const;
|
||||
/** @return Compressed image blob for @p signatureId (empty if not stored). */
|
||||
cv::Mat getImageCompressed(int signatureId) const;
|
||||
/**
|
||||
* @brief Loads sensor data of @p locationId from WM or LTM.
|
||||
* @param images Include compressed RGB/depth images.
|
||||
* @param scan Include laser scan blob.
|
||||
* @param userData Include user data blob.
|
||||
* @param occupancyGrid Include occupancy grid cells.
|
||||
*/
|
||||
SensorData getNodeData(int locationId, bool images, bool scan, bool userData, bool occupancyGrid) const;
|
||||
/** @brief Loads the visual words, 3D points and global descriptors stored with @p nodeId. */
|
||||
void getNodeWordsAndGlobalDescriptors(int nodeId,
|
||||
std::multimap<int, int> & words,
|
||||
std::vector<cv::KeyPoint> & wordsKpts,
|
||||
std::vector<cv::Point3f> & words3,
|
||||
cv::Mat & wordsDescriptors,
|
||||
std::vector<GlobalDescriptor> & globalDescriptors) const;
|
||||
/** @brief Loads mono and/or stereo camera calibration stored with @p nodeId. */
|
||||
void getNodeCalibration(int nodeId,
|
||||
std::vector<CameraModel> & models,
|
||||
std::vector<StereoCameraModel> & stereoModels) const;
|
||||
/**
|
||||
* @brief Returns all signature ids in WM, STM and LTM.
|
||||
* @param ignoreChildren If true, nodes not linked to graph anymore are excluded
|
||||
*/
|
||||
std::set<int> getAllSignatureIds(bool ignoreChildren = true) const;
|
||||
/**
|
||||
* @brief Reports whether the in-memory map has been modified since the database was last
|
||||
* loaded or reset. @ref close() uses this flag to decide whether the database
|
||||
* needs to be rewritten.
|
||||
*
|
||||
* The flag is cleared to @c false on construction, by @ref close() and when the memory is cleared,
|
||||
* and is raised to @c true on any of the following events:
|
||||
*
|
||||
* - **@ref update() in mapping mode** (@ref Parameters::kMemIncrementalMemory() == @c true,
|
||||
* see @ref isIncremental()): every successful call sets the flag, since a new
|
||||
* @ref Signature is added to the graph and the visual word dictionary may grow.
|
||||
* - **@ref update() in localization mode** (@ref isIncremental() == @c false): the flag
|
||||
* is set only when @ref Parameters::kMemLocalizationDataSaved() is enabled
|
||||
* (see @ref isLocalizationDataSaved()), i.e. when the new node must be persisted
|
||||
* back to the database. Pure localization (the default,
|
||||
* @ref Parameters::kMemLocalizationDataSaved() == @c false) leaves the flag at
|
||||
* @c false even after many @ref update() calls, because nothing needs to be saved.
|
||||
* - **@ref reduceNode()**: merging a node into a neighbor mutates the graph and
|
||||
* marks the memory as changed (and also raises the link-changed flag).
|
||||
* - **@ref init() dictionary repair**: when @ref init() rebuilds the visual word
|
||||
* dictionary because words are missing from the database, the flag is forced to
|
||||
* @c true so the regenerated dictionary is saved back on @ref close(), even if no
|
||||
* new data was processed.
|
||||
*
|
||||
* Note that link-only modifications (e.g. @ref addLink(), @ref updateLink(),
|
||||
* @ref removeLink()) update an independent @c _linksChanged flag, not this one.
|
||||
*
|
||||
* @return True if the memory has changed and would need to be persisted.
|
||||
*/
|
||||
bool memoryChanged() const {return _memoryChanged;}
|
||||
/**
|
||||
* @return True if the memory grows on @ref update() (mapping mode), false in localization mode.
|
||||
* @see Parameters::kMemIncrementalMemory()
|
||||
*/
|
||||
bool isIncremental() const {return _incrementalMemory;}
|
||||
/**
|
||||
* @return True in localization mode when database writes are disabled
|
||||
* (i.e. @ref isIncremental() is false and @ref Parameters::kMemLocalizationReadOnly() is enabled).
|
||||
* @see Parameters::kMemIncrementalMemory()
|
||||
* @see Parameters::kMemLocalizationReadOnly()
|
||||
*/
|
||||
bool isReadOnly() const {return !_incrementalMemory && _localizationReadOnly;}
|
||||
/**
|
||||
* @return True if data added during localization is persisted to the database.
|
||||
* @see Parameters::kMemLocalizationDataSaved()
|
||||
*/
|
||||
bool isLocalizationDataSaved() const {return _localizationDataSaved;}
|
||||
/** @return Signature with @p id in WM/STM, or null if not loaded. */
|
||||
const Signature * getSignature(int id) const;
|
||||
/** @return True if @p signatureId is in short-term memory. */
|
||||
bool isInSTM(int signatureId) const {return _stMem.find(signatureId) != _stMem.end();}
|
||||
/** @return True if @p signatureId is in working memory. */
|
||||
bool isInWM(int signatureId) const {return _workingMem.find(signatureId) != _workingMem.end();}
|
||||
/** @return True if @p signatureId is not in STM/WM, so when it is in LTM or non-existing. */
|
||||
bool isInLTM(int signatureId) const {return !this->isInSTM(signatureId) && !this->isInWM(signatureId);}
|
||||
/** @return True if signature ids are auto-generated, false if taken from sensor data id. */
|
||||
bool isIDsGenerated() const {return _generateIds;}
|
||||
/** @return Id of the last accepted global loop-closure node, or 0 if none. */
|
||||
int getLastGlobalLoopClosureId() const {return _lastGlobalLoopClosureId;}
|
||||
/** @return Feature extractor used to compute visual words. */
|
||||
const Feature2D * getFeature2D() const {return _feature2D;}
|
||||
/** @return True if graph reduction is enabled (@ref Parameters::kMemReduceGraph()). */
|
||||
bool isGraphReduced() const {return _reduceGraph;}
|
||||
/**
|
||||
* @return Running per-axis maximum of the diagonal of every neighbor (odometry) link's
|
||||
* information matrix observed so far, as a 6-vector
|
||||
* (x, y, z, roll, pitch, yaw). Empty until at least one 6x6 neighbor link
|
||||
* information matrix has been seen.
|
||||
*
|
||||
* This is a runtime statistic, not a configurable parameter: it is updated on
|
||||
* @ref init() (over all loaded neighbor links) and on every @ref update() that
|
||||
* adds a new neighbor link.
|
||||
*
|
||||
* It is consumed by @ref Rtabmap::getInformation() when
|
||||
* @ref Parameters::kRGBDLoopCovLimited() is enabled, to clip loop-closure
|
||||
* information matrices so a loop never claims higher confidence than odometry
|
||||
* itself ever provided.
|
||||
*
|
||||
* @see Parameters::kRGBDLoopCovLimited()
|
||||
*/
|
||||
const std::vector<double> & getOdomMaxInf() const {return _odomMaxInf;}
|
||||
/**
|
||||
* @return True if the odometry pose orientation is used (instead of the IMU
|
||||
* orientation) as the source of each new node's gravity link.
|
||||
*
|
||||
* When enabled, every new node gets a self-loop @ref Link::kGravity holding the
|
||||
* rotation of the odometry pose passed to @ref update(). This assumes odometry is
|
||||
* already gravity-aligned (e.g. a VIO front-end). When disabled, the gravity link
|
||||
* is built from the IMU orientation in @ref SensorData::imu() if available.
|
||||
*
|
||||
* Gravity links are consumed by graph optimization only when
|
||||
* @ref Parameters::kOptimizerGravitySigma() is non-zero.
|
||||
*
|
||||
* @see Parameters::kMemUseOdomGravity()
|
||||
* @see Parameters::kOptimizerGravitySigma()
|
||||
*/
|
||||
bool isOdomGravityUsed() const {return _useOdometryGravity;}
|
||||
|
||||
/** @brief Writes a human-readable dump of WM/STM, links and weights to @p fileNameTree. */
|
||||
void dumpMemoryTree(const char * fileNameTree) const;
|
||||
/** @brief Dumps every internal map (signatures, words, dictionary) to text files in @p directory. */
|
||||
virtual void dumpMemory(std::string directory) const;
|
||||
/** @brief Dumps signatures' word ids (and 3D positions if @p words3D) to @p fileNameSign. */
|
||||
virtual void dumpSignatures(const char * fileNameSign, bool words3D) const;
|
||||
/** @brief Dumps the visual word dictionary: references to @p fileNameRef, descriptors to @p fileNameDesc. */
|
||||
void dumpDictionary(const char * fileNameRef, const char * fileNameDesc) const;
|
||||
/** @return Approximate RAM usage of the in-memory state, in bytes. */
|
||||
unsigned long getMemoryUsed() const; //Bytes
|
||||
|
||||
/**
|
||||
* @brief Writes a Graphviz DOT file of the pose graph.
|
||||
* @param fileName Output path.
|
||||
* @param ids If non-empty, restrict the graph to these node ids.
|
||||
*/
|
||||
void generateGraph(const std::string & fileName, const std::set<int> & ids = std::set<int>());
|
||||
/**
|
||||
* @brief Removes spurious obstacle points from each node's local grid using a reference 2D map.
|
||||
*
|
||||
* For every node in @p poses, the node's local **obstacle** grid is loaded, each
|
||||
* obstacle point is projected with @p poses into the reference @p map, and the
|
||||
* point is kept only if either:
|
||||
* - the reference @p map cell at its projection is not free space
|
||||
* (i.e. the cell is an obstacle or unknown, value != 0), or
|
||||
* - the reference @p map contains an obstacle cell (value == 100) within
|
||||
* @p cropRadius cells of the projection.
|
||||
*
|
||||
* Points that fall on a free cell and have no obstacle neighbor within
|
||||
* @p cropRadius are dropped. The filtered obstacle grid replaces the node's grid
|
||||
* in WM/STM and (if the node is already persisted) in the database via
|
||||
* @ref DBDriver::updateOccupancyGrid().
|
||||
*
|
||||
* **Ground and empty cells are not touched**: they are read and written back as-is.
|
||||
*
|
||||
* When @p filterScans is true, the same projection/filtering rule is also applied
|
||||
* to each node's raw laser scan, and the rewritten scan is saved back to the
|
||||
* database. This is useful to remove dynamic objects from the stored scans before
|
||||
* re-meshing or re-exporting.
|
||||
*
|
||||
* @param poses Optimized poses used to project the local grids/scans into @p map.
|
||||
* @param map Reference 2D occupancy grid (cell values: 0 free, 100 occupied,
|
||||
* anything else unknown).
|
||||
* @param xMin Reference map origin x in world coordinates (meters).
|
||||
* @param yMin Reference map origin y in world coordinates (meters).
|
||||
* @param cellSize Reference map resolution (meters/cell); must match the nodes' grid cell size.
|
||||
* @param cropRadius Search radius (in cells) around each projected point used to
|
||||
* accept points near an obstacle in @p map.
|
||||
* @param filterScans If true, also filter and rewrite the raw laser scan attached
|
||||
* to each node, using the same rule as for obstacle cells.
|
||||
* @return Number of (node, grid or scan) modifications performed, or -1 on error
|
||||
* (no database loaded, empty @p poses or empty @p map).
|
||||
*/
|
||||
int cleanupLocalGrids(
|
||||
const std::map<int, Transform> & poses,
|
||||
const cv::Mat & map,
|
||||
@@ -730,20 +246,10 @@ public:
|
||||
int cropRadius = 1,
|
||||
bool filterScans = false);
|
||||
|
||||
/** @return Visual word dictionary used for tf-idf likelihood and feature matching. */
|
||||
//keypoint stuff
|
||||
const VWDictionary * getVWDictionary() const;
|
||||
|
||||
/**
|
||||
* @brief Extracts a sub-graph (poses + links) for a set of node ids.
|
||||
*
|
||||
* Used by graph optimization callers to retrieve constraints for a region of interest.
|
||||
*
|
||||
* @param ids Ids to include.
|
||||
* @param poses Output: odometry poses for @p ids.
|
||||
* @param links Output: links between the nodes (and to landmarks if @p landmarksAdded).
|
||||
* @param lookInDatabase If true, fetch missing data from LTM.
|
||||
* @param landmarksAdded If true, also include landmark constraints.
|
||||
*/
|
||||
// RGB-D stuff
|
||||
void getMetricConstraints(
|
||||
const std::set<int> & ids,
|
||||
std::map<int, Transform> & poses,
|
||||
@@ -751,31 +257,9 @@ public:
|
||||
bool lookInDatabase = false,
|
||||
bool landmarksAdded = false);
|
||||
|
||||
/**
|
||||
* @brief Computes the relative transform from @p fromS to @p toS using the registration pipeline.
|
||||
* @param fromS Source signature (will be modified to cache extracted data).
|
||||
* @param toS Target signature.
|
||||
* @param guess Initial transform estimate (null if unknown).
|
||||
* @param info Optional output with inlier counts, variance and diagnostics.
|
||||
* @param useKnownCorrespondencesIfPossible If true, reuses existing word-id correspondences.
|
||||
* @return The estimated transform, or a null @ref Transform on failure.
|
||||
*/
|
||||
Transform computeTransform(Signature & fromS, Signature & toS, Transform guess, RegistrationInfo * info = 0, bool useKnownCorrespondencesIfPossible = false) const;
|
||||
/** @brief Convenience overload: loads signatures by id and forwards to the @ref Signature variant. */
|
||||
Transform computeTransform(int fromId, int toId, Transform guess, RegistrationInfo * info = 0, bool useKnownCorrespondencesIfPossible = false);
|
||||
/**
|
||||
* @brief Refines a transform using ICP alignment of laser scans only.
|
||||
* @return The refined transform, or a null @ref Transform on failure.
|
||||
*/
|
||||
Transform computeIcpTransform(const Signature & fromS, const Signature & toS, Transform guess, RegistrationInfo * info = 0) const;
|
||||
/**
|
||||
* @brief ICP registration of one node against an assembled cloud from multiple neighbors.
|
||||
* @param newId New (query) node id.
|
||||
* @param oldId Reference node id.
|
||||
* @param poses Neighbor poses used to assemble the reference cloud.
|
||||
* @param info Optional output with inlier counts and diagnostics.
|
||||
* @return The estimated transform, or a null @ref Transform on failure.
|
||||
*/
|
||||
Transform computeIcpTransformMulti(
|
||||
int newId,
|
||||
int oldId,
|
||||
@@ -796,6 +280,7 @@ private:
|
||||
std::list<Signature *> getRemovableSignatures(int count,
|
||||
const std::set<int> & ignoredIds = std::set<int>());
|
||||
int getNextId();
|
||||
void initCountId();
|
||||
void rehearsal(Signature * signature, Statistics * stats = 0);
|
||||
bool rehearsalMerge(int oldId, int newId);
|
||||
bool canBeReduced(const Link & link, float maxDistance, int direction);
|
||||
@@ -815,7 +300,6 @@ private:
|
||||
int getNi(int signatureId) const;
|
||||
|
||||
protected:
|
||||
/** @brief Database driver owning the persistent storage (created by @ref init()). */
|
||||
DBDriver * _dbDriver;
|
||||
|
||||
private:
|
||||
@@ -880,8 +364,6 @@ private:
|
||||
bool _memoryChanged; // False by default, become true only when Memory::update() is called.
|
||||
bool _linksChanged; // False by default, become true when links are modified.
|
||||
int _signaturesAdded;
|
||||
int _workingMemIntermediateNodesCount; // number of nodes with weight==-1 currently in _workingMem
|
||||
int _stMemIntermediateNodesCount; // number of nodes with weight==-1 currently in _stMem
|
||||
bool _allNodesInWM;
|
||||
bool _receivingOdometryFeatures;
|
||||
GPS _gpsOrigin;
|
||||
@@ -913,8 +395,6 @@ private:
|
||||
MarkerDetector * _markerDetector;
|
||||
|
||||
GlobalDescriptorExtractor * _globalDescriptorExtractor;
|
||||
|
||||
bool _dummyDictionary;
|
||||
};
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
@@ -39,115 +39,55 @@ namespace rtabmap {
|
||||
class OdometryInfo;
|
||||
class ParticleFilter;
|
||||
|
||||
/**
|
||||
* @class Odometry
|
||||
* @brief Abstract base class for visual, lidar and visual-inertial odometry backends.
|
||||
*
|
||||
* Odometry estimates the incremental motion between consecutive @ref SensorData frames.
|
||||
* Concrete implementations override @c computeTransform(); the public @ref process()
|
||||
* pipeline handles IMU caching, optional motion guesses, filtering (Kalman or particle),
|
||||
* image decimation, deskewing and pose integration.
|
||||
*
|
||||
* Use @ref create() to instantiate a backend from @ref Parameters::kOdomStrategy().
|
||||
*
|
||||
* @see OdometryThread
|
||||
* @see OdometryInfo
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Odometry
|
||||
{
|
||||
public:
|
||||
/** @brief Odometry backend selected by @ref Parameters::kOdomStrategy(). */
|
||||
enum Type {
|
||||
kTypeUndef = -1, /**< Undefined / invalid type. */
|
||||
kTypeF2M = 0, /**< Frame-to-map (default). */
|
||||
kTypeF2F = 1, /**< Frame-to-frame. */
|
||||
kTypeFovis = 2, /**< FOVIS stereo visual odometry. */
|
||||
kTypeViso2 = 3, /**< libviso2. */
|
||||
kTypeDVO = 4, /**< Dense visual odometry. */
|
||||
kTypeORBSLAM = 5, /**< ORB-SLAM 2/3. */
|
||||
kTypeOkvis = 6, /**< OKVIS. */
|
||||
kTypeLOAM = 7, /**< LOAM lidar odometry. */
|
||||
kTypeMSCKF = 8, /**< MSCKF visual-inertial. */
|
||||
kTypeVINSFusion = 9,/**< VINS-Fusion. */
|
||||
kTypeOpenVINS = 10, /**< OpenVINS. */
|
||||
kTypeFLOAM = 11, /**< FLOAM lidar odometry. */
|
||||
kTypeOpen3D = 12, /**< Open3D RGB-D odometry. */
|
||||
kTypeCuVSLAM = 13, /**< cuVSLAM. */
|
||||
kTypeLIOSAM = 14 /**< LIO-SAM. */
|
||||
kTypeUndef = -1,
|
||||
kTypeF2M = 0,
|
||||
kTypeF2F = 1,
|
||||
kTypeFovis = 2,
|
||||
kTypeViso2 = 3,
|
||||
kTypeDVO = 4,
|
||||
kTypeORBSLAM = 5,
|
||||
kTypeOkvis = 6,
|
||||
kTypeLOAM = 7,
|
||||
kTypeMSCKF = 8,
|
||||
kTypeVINSFusion = 9,
|
||||
kTypeOpenVINS = 10,
|
||||
kTypeFLOAM = 11,
|
||||
kTypeOpen3D = 12,
|
||||
kTypeCuVSLAM = 13,
|
||||
kTypeLIOSAM = 14
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Creates an odometry instance from @ref Parameters::kOdomStrategy() in @p parameters.
|
||||
* @param parameters RTAB-Map parameters (odometry strategy and related options).
|
||||
* @return New odometry object (caller owns the pointer). Falls back to @ref kTypeF2M if the type is unknown.
|
||||
*/
|
||||
public:
|
||||
static Odometry * create(const ParametersMap & parameters = ParametersMap());
|
||||
/**
|
||||
* @brief Creates an odometry instance of a given @p type.
|
||||
* @param type In/out odometry type; updated to @ref kTypeF2M if @p type is unknown.
|
||||
* @param parameters RTAB-Map parameters passed to the concrete backend.
|
||||
*/
|
||||
static Odometry * create(Type & type, const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
public:
|
||||
virtual ~Odometry();
|
||||
/**
|
||||
* @brief Processes a sensor frame and updates the integrated pose.
|
||||
* @param data Input sensor data (must have @c id() >= 0). May be modified in place (decompression, deskewing).
|
||||
* @param info Optional output statistics and debug data.
|
||||
* @return Updated integrated pose (@ref getPose()) after the frame is processed,
|
||||
* or a null transform if odometry is lost. The incremental transform is
|
||||
* available in @c OdometryInfo::transform when @p info is provided.
|
||||
*/
|
||||
Transform process(SensorData & data, OdometryInfo * info = 0);
|
||||
/**
|
||||
* @brief Processes a sensor frame with an external motion guess.
|
||||
* @param data Input sensor data.
|
||||
* @param guess Optional prior on the incremental transform (used by the backend when supported).
|
||||
* @param info Optional output statistics and debug data.
|
||||
*/
|
||||
Transform process(SensorData & data, const Transform & guess, OdometryInfo * info = 0);
|
||||
/**
|
||||
* @brief Resets internal state and sets the initial pose.
|
||||
* @param initialPose Starting pose (must not be null). Z/roll/pitch may be cleared if @ref Parameters::kRegForce3DoF() is enabled.
|
||||
*/
|
||||
virtual void reset(const Transform & initialPose = Transform::getIdentity());
|
||||
/** @return Concrete odometry backend type. */
|
||||
virtual Odometry::Type getType() = 0;
|
||||
/** @return True if the backend can process unrectified camera images. */
|
||||
virtual bool canProcessRawImages() const {return false;}
|
||||
/** @return True if the backend processes IMU asynchronously outside @ref process(). */
|
||||
virtual bool canProcessAsyncIMU() const {return false;}
|
||||
|
||||
/** @return Current integrated odometry pose. */
|
||||
//getters
|
||||
const Transform & getPose() const {return _pose;}
|
||||
/** @return True if @ref OdometryInfo debug/statistics fields are filled in @ref process(). */
|
||||
bool isInfoDataFilled() const {return _fillInfoData;}
|
||||
/** @deprecated Use @ref getVelocityGuess() instead. */
|
||||
// Use getVelocityGuess() instead.
|
||||
RTABMAP_DEPRECATED const Transform & previousVelocityTransform() const;
|
||||
/** @return Last estimated velocity used for motion guessing (may be null). */
|
||||
const Transform & getVelocityGuess() const {return velocityGuess_;}
|
||||
/** @return Timestamp of the previously processed frame. */
|
||||
double previousStamp() const {return previousStamp_;}
|
||||
/** @return Number of frames processed since the last @ref reset(). */
|
||||
unsigned int framesProcessed() const {return framesProcessed_;}
|
||||
/** @return True if input images are already rectified (see @ref Parameters::kRtabmapImagesAlreadyRectified()). */
|
||||
bool imagesAlreadyRectified() const {return _imagesAlreadyRectified;}
|
||||
|
||||
protected:
|
||||
/** @return IMU orientations cached from recent frames (stamp → transform). */
|
||||
const std::map<double, Transform> & imus() const {return imus_;}
|
||||
|
||||
/** @brief Constructs the base odometry state from RTAB-Map parameters. */
|
||||
Odometry(const rtabmap::ParametersMap & parameters);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Computes the incremental transform for one frame (implemented by subclasses).
|
||||
* @param data Sensor data for this frame (may already be decimated or deskewed).
|
||||
* @param guess Motion prior from the base class or the caller.
|
||||
* @param info Optional debug/statistics output.
|
||||
* @return Incremental transform, or null if tracking failed.
|
||||
*/
|
||||
virtual Transform computeTransform(SensorData & data, const Transform & guess = Transform(), OdometryInfo * info = 0) = 0;
|
||||
|
||||
void initKalmanFilter(const Transform & initialPose = Transform::getIdentity(), float vx=0.0f, float vy=0.0f, float vz=0.0f, float vroll=0.0f, float vpitch=0.0f, float vyaw=0.0f);
|
||||
@@ -189,6 +129,9 @@ private:
|
||||
std::vector<StereoCameraModel> stereoModels_;
|
||||
std::vector<CameraModel> models_;
|
||||
std::map<double, Transform> imus_;
|
||||
|
||||
protected:
|
||||
Odometry(const rtabmap::ParametersMap & parameters);
|
||||
};
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -36,40 +36,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class OdometryEvent
|
||||
* @brief One processed frame with the pose the odometry integrated for it.
|
||||
*
|
||||
* Posted by @ref OdometryThread for every frame it processes, and consumed by
|
||||
* @ref RtabmapThread, which passes the data and the pose to
|
||||
* @ref Rtabmap::process(). A null @ref pose() means **odometry is lost** on this
|
||||
* frame; RtabmapThread reads that as a reset and starts a new map.
|
||||
*
|
||||
* The covariance is always 6x6 @c CV_64FC1 with positive finite diagonal terms
|
||||
* (identity when the front-end did not provide one, i.e. "unknown but valid").
|
||||
* A value ≥ 9999 on the first diagonal term is the convention for a lost estimate.
|
||||
*
|
||||
* @see OdometryThread
|
||||
* @see OdometryInfo
|
||||
* @see RtabmapThread
|
||||
*/
|
||||
class OdometryEvent : public UEvent
|
||||
{
|
||||
public:
|
||||
/** @brief Creates an empty event, with an identity covariance. */
|
||||
OdometryEvent()
|
||||
{
|
||||
_info.reg.covariance = cv::Mat::eye(6,6,CV_64FC1);
|
||||
}
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param data The frame that was processed.
|
||||
* @param pose Integrated odometry pose, null if odometry is lost.
|
||||
* @param info Everything else the iteration produced.
|
||||
*
|
||||
* An empty covariance in @p info is replaced by identity; otherwise it must be
|
||||
* 6x6 @c CV_64FC1 with finite, strictly positive diagonal terms.
|
||||
*/
|
||||
OdometryEvent(
|
||||
const SensorData & data,
|
||||
const Transform & pose,
|
||||
@@ -93,19 +66,10 @@ public:
|
||||
virtual ~OdometryEvent() {}
|
||||
virtual std::string getClassName() const {return "OdometryEvent";}
|
||||
|
||||
/** @return The processed frame, modifiable (e.g. to attach user data). */
|
||||
SensorData & data() {return _data;}
|
||||
/** @return The processed frame. */
|
||||
const SensorData & data() const {return _data;}
|
||||
/** @return Integrated odometry pose, null if odometry was lost on this frame. */
|
||||
const Transform & pose() const {return _pose;}
|
||||
/** @return 6x6 covariance of the motion estimate (@c CV_64FC1). */
|
||||
const cv::Mat & covariance() const {return _info.reg.covariance;}
|
||||
/**
|
||||
* @brief Linear and angular velocity, from the motion and the frame interval.
|
||||
* @return (vx, vy, vz, vroll, vpitch, vyaw) in m/s and rad/s, or an empty
|
||||
* vector when the interval is unknown.
|
||||
*/
|
||||
std::vector<float> velocity() const {
|
||||
if(_info.interval>0.0)
|
||||
{
|
||||
@@ -122,7 +86,6 @@ public:
|
||||
}
|
||||
return std::vector<float>();
|
||||
}
|
||||
/** @return Quality indicators, timings and intermediate data of the iteration. */
|
||||
const OdometryInfo & info() const {return _info;}
|
||||
|
||||
private:
|
||||
@@ -131,24 +94,12 @@ private:
|
||||
OdometryInfo _info;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class OdometryResetEvent
|
||||
* @brief Asks @ref OdometryThread to restart the odometry from a given pose.
|
||||
*
|
||||
* The buffered frames and IMU samples are dropped, and the integration starts
|
||||
* over from @ref getPose(). Handled even before the thread is started.
|
||||
*
|
||||
* @see OdometryThread
|
||||
* @see Odometry::reset()
|
||||
*/
|
||||
class OdometryResetEvent : public UEvent
|
||||
{
|
||||
public:
|
||||
/** @param pose Pose to restart from (identity by default). */
|
||||
OdometryResetEvent(const Transform & pose = Transform::getIdentity()){_pose = pose;}
|
||||
virtual ~OdometryResetEvent() {}
|
||||
virtual std::string getClassName() const {return "OdometryResetEvent";}
|
||||
/** @return The pose the odometry should restart from. */
|
||||
const Transform & getPose() const {return _pose;}
|
||||
private:
|
||||
Transform _pose;
|
||||
|
||||
@@ -34,86 +34,58 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "rtabmap/core/RegistrationInfo.h"
|
||||
#include "rtabmap/core/CameraModel.h"
|
||||
#include "rtabmap/core/LaserScan.h"
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/features2d/features2d.hpp>
|
||||
#else
|
||||
#include <opencv2/features.hpp>
|
||||
#endif
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class OdometryInfo
|
||||
* @brief What one @ref Odometry iteration produced, beyond the pose.
|
||||
*
|
||||
* Filled by @ref Odometry::process() when a pointer is passed to it, and carried
|
||||
* to the rest of the application by @ref OdometryEvent. It holds the incremental
|
||||
* motion, the quality indicators used to decide whether the estimate can be
|
||||
* trusted, the timings, and the intermediate data a viewer needs to draw what
|
||||
* the front-end sees (local map, matched features).
|
||||
*
|
||||
* Which fields are filled depends on the front-end: the feature-related ones
|
||||
* come from @ref OdometryF2M and @ref OdometryF2F, and an ICP-only or external
|
||||
* strategy leaves them empty.
|
||||
*
|
||||
* @see Odometry
|
||||
* @see OdometryEvent
|
||||
* @see RegistrationInfo
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT OdometryInfo
|
||||
{
|
||||
public:
|
||||
OdometryInfo();
|
||||
/** @brief A copy without the heavy members (features, local map, scan). */
|
||||
OdometryInfo copyWithoutData() const;
|
||||
/**
|
||||
* @brief Formats the content as the `Odometry/...` statistics.
|
||||
* @param pose Current pose, added to the output when not null.
|
||||
* @return Statistic name (with its unit) to value, as published in @ref Statistics.
|
||||
*/
|
||||
std::map<std::string, float> statistics(const Transform & pose = Transform());
|
||||
|
||||
bool lost; ///< True when the motion could not be estimated on this frame (@ref transform is then null).
|
||||
RegistrationInfo reg; ///< Registration result: matches, inliers, covariance, ICP indicators and timings.
|
||||
int features; ///< Number of features extracted in the current frame.
|
||||
int localMapSize; ///< Number of 3D points in the local feature map (F2M).
|
||||
int localScanMapSize; ///< Number of points in the local scan map (F2M).
|
||||
int localKeyFrames; ///< Number of key frames forming the local map (F2M).
|
||||
int localBundleOutliers; ///< Features rejected by the last local bundle adjustment.
|
||||
int localBundleConstraints; ///< Feature observations kept by the last local bundle adjustment.
|
||||
float localBundleTime; ///< Time spent in the local bundle adjustment (s).
|
||||
std::map<int, Transform> localBundlePoses; ///< Key frame poses optimized by the local bundle adjustment.
|
||||
std::map<int, std::vector<CameraModel> > localBundleModels; ///< Camera models of @ref localBundlePoses.
|
||||
float localBundleAvgInlierDistance; ///< Average distance of the bundle adjustment inliers (m).
|
||||
int localBundleMaxKeyFramesForInlier; ///< Highest number of key frames observing a same inlier.
|
||||
std::vector<int> localBundleOutliersPerCam; ///< Outliers of the last local bundle adjustment, per camera.
|
||||
bool keyFrameAdded; ///< True if this frame became a key frame of the local map.
|
||||
float timeDeskewing; ///< Time spent deskewing the laser scan (s).
|
||||
float timeEstimation; ///< Time spent estimating the motion (s).
|
||||
float timeParticleFiltering; ///< Time spent in the particle filter (s), when enabled.
|
||||
double stamp; ///< Stamp of the processed frame.
|
||||
double interval; ///< Time since the previous processed frame (s); the divisor for velocities.
|
||||
Transform transform; ///< Motion since the previous frame, null when @ref lost.
|
||||
Transform transformFiltered; ///< @ref transform after Kalman or particle filtering, when enabled.
|
||||
Transform transformGroundTruth; ///< Ground truth motion since the previous frame, when the data provides it.
|
||||
Transform guessVelocity; ///< @deprecated Use @ref guess and @ref interval instead.
|
||||
Transform guess; ///< Motion guess given to the front-end (from the velocity model, an external pose or an IMU).
|
||||
float distanceTravelled; ///< Distance travelled since the odometry was last reset (m).
|
||||
int memoryUsage; ///< Process memory used (MB), only with @ref Parameters::kRtabmapPublishRAMUsage().
|
||||
double gravityRollError; ///< Absolute roll difference between the estimated pose and the IMU gravity (rad).
|
||||
double gravityPitchError;///< Absolute pitch difference between the estimated pose and the IMU gravity (rad).
|
||||
bool lost;
|
||||
RegistrationInfo reg;
|
||||
int features;
|
||||
int localMapSize;
|
||||
int localScanMapSize;
|
||||
int localKeyFrames;
|
||||
int localBundleOutliers;
|
||||
int localBundleConstraints;
|
||||
float localBundleTime;
|
||||
std::map<int, Transform> localBundlePoses;
|
||||
std::map<int, std::vector<CameraModel> > localBundleModels;
|
||||
float localBundleAvgInlierDistance;
|
||||
int localBundleMaxKeyFramesForInlier;
|
||||
std::vector<int> localBundleOutliersPerCam;
|
||||
bool keyFrameAdded;
|
||||
float timeDeskewing;
|
||||
float timeEstimation;
|
||||
float timeParticleFiltering;
|
||||
double stamp;
|
||||
double interval;
|
||||
Transform transform;
|
||||
Transform transformFiltered;
|
||||
Transform transformGroundTruth;
|
||||
Transform guessVelocity; // deprecated, will be removed. Use guess and interval instead.
|
||||
Transform guess;
|
||||
float distanceTravelled;
|
||||
int memoryUsage; //MB
|
||||
double gravityRollError;
|
||||
double gravityPitchError;
|
||||
|
||||
int type; ///< Odometry strategy that produced this, see @ref Parameters::kOdomStrategy().
|
||||
int type;
|
||||
|
||||
// F2M
|
||||
std::multimap<int, cv::KeyPoint> words; ///< Keypoints of the current frame, by word id (F2M).
|
||||
std::map<int, cv::Point3f> localMap; ///< Local feature map in the odometry frame, by word id (F2M).
|
||||
LaserScan localScanMap; ///< Local scan map in the odometry frame (F2M).
|
||||
std::multimap<int, cv::KeyPoint> words;
|
||||
std::map<int, cv::Point3f> localMap;
|
||||
LaserScan localScanMap;
|
||||
|
||||
// F2F
|
||||
std::vector<cv::Point2f> refCorners; ///< Corners of the reference frame (F2F).
|
||||
std::vector<cv::Point2f> newCorners; ///< Where those corners were found in the current frame (F2F).
|
||||
std::vector<int> cornerInliers; ///< Indices in @ref refCorners / @ref newCorners kept as inliers (F2F).
|
||||
std::vector<cv::Point2f> refCorners;
|
||||
std::vector<cv::Point2f> newCorners;
|
||||
std::vector<int> cornerInliers;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -39,67 +39,13 @@ namespace rtabmap {
|
||||
|
||||
class Odometry;
|
||||
|
||||
/**
|
||||
* @class OdometryThread
|
||||
* @brief Runs an @ref Odometry front-end in its own thread, driven by events.
|
||||
*
|
||||
* The thread owns the @ref Odometry object and calls @ref Odometry::process() on
|
||||
* the frames it receives, so that a slow odometry update does not block the
|
||||
* sensor thread. It sits in the middle of the event-based pipeline:
|
||||
* @ref SensorCaptureThread → OdometryThread → @ref RtabmapThread.
|
||||
*
|
||||
* **Input events** (handled in @ref handleEvent(), i.e. in the caller's thread):
|
||||
* - @ref SensorEvent — a frame to process. It is rejected with an error if
|
||||
* it carries neither a laser scan nor an image with its calibration (an
|
||||
* @ref OdometryMono front-end accepts RGB alone).
|
||||
* - @ref IMUEvent — an IMU sample, kept in a separate buffer.
|
||||
* - @ref OdometryResetEvent — resets the odometry to the pose it carries
|
||||
* (identity if null) and drops everything buffered. Unlike the others, it is
|
||||
* handled even before the thread is started.
|
||||
*
|
||||
* **Output event**: one @ref OdometryEvent per processed frame, carrying the
|
||||
* data, the integrated pose and the @ref OdometryInfo. A null pose means
|
||||
* odometry is lost; that is what @ref RtabmapThread reads to start a new map.
|
||||
*
|
||||
* **Buffering.** The frame buffer holds @p dataBufferMaxSize frames and drops
|
||||
* the oldest when full, so with the default size of 1 the odometry always works
|
||||
* on the freshest frame rather than falling behind. IMU samples are buffered
|
||||
* apart and fed to the odometry up to the stamp of the frame about to be
|
||||
* processed, so that tightly-coupled back-ends see them in order. A frame whose
|
||||
* stamp falls outside the buffered IMU window is skipped with a warning: with an
|
||||
* asynchronous IMU, it must be published faster (less delay) than the camera or lidar.
|
||||
*
|
||||
* When the incoming @ref SensorEvent already carries an odometry pose (a robot
|
||||
* publishing its own odometry), the motion between two consecutive such poses is
|
||||
* passed to @ref Odometry::process() as a guess.
|
||||
*
|
||||
* @see Odometry
|
||||
* @see RtabmapThread
|
||||
* @see SensorCaptureThread
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT OdometryThread : public UThread, public UEventsHandler {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param odometry The odometry to run; must not be null. The thread takes
|
||||
* ownership and deletes it in the destructor.
|
||||
* @param dataBufferMaxSize Maximum number of frames waiting to be processed
|
||||
* (0 = unlimited). Beyond that the oldest frame is
|
||||
* dropped, keeping the odometry on recent data.
|
||||
*/
|
||||
// take ownership of Odometry
|
||||
OdometryThread(Odometry * odometry, unsigned int dataBufferMaxSize = 1);
|
||||
virtual ~OdometryThread();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Receives the events listed in the class description.
|
||||
*
|
||||
* Runs in the posting thread: frames and IMU samples are only buffered here,
|
||||
* the odometry itself runs in the thread's main loop. Data events are ignored
|
||||
* until the thread is started, an @ref OdometryResetEvent is not.
|
||||
*
|
||||
* @return Always false, so the event keeps being dispatched to other handlers.
|
||||
*/
|
||||
virtual bool handleEvent(UEvent * event);
|
||||
|
||||
private:
|
||||
|
||||
@@ -32,22 +32,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <rtabmap/core/Link.h>
|
||||
#include <rtabmap/core/Parameters.h>
|
||||
#include <rtabmap/core/Signature.h>
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class FeatureBA
|
||||
* @brief A single bundle adjustment feature observation: one keypoint seen in one frame.
|
||||
*
|
||||
* Used as the per-frame value in the @c wordReferences map (`<wordId, <frameId, FeatureBA>>`)
|
||||
* passed to @ref Optimizer::optimizeBA(). @ref depth is in meters when available (RGB-D / stereo
|
||||
* with disparity) or 0 if unknown (monocular). @ref descriptor and @ref cameraIndex are optional;
|
||||
* @ref cameraIndex selects which @ref CameraModel of a multi-camera rig the keypoint belongs to.
|
||||
*/
|
||||
class FeatureBA
|
||||
{
|
||||
public:
|
||||
@@ -59,74 +49,31 @@ public:
|
||||
{
|
||||
//UDEBUG("kpt=(%f,%f) depth=%f, camIndex=%d", kpt.pt.x, kpt.pt.y, depth, cameraIndex);
|
||||
}
|
||||
cv::KeyPoint kpt; ///< 2D image keypoint.
|
||||
float depth; ///< Depth at @ref kpt in meters, or 0 if unknown (monocular).
|
||||
cv::Mat descriptor; ///< Optional descriptor for the keypoint (used when re-matching is enabled).
|
||||
int cameraIndex; ///< Index into the frame's camera model list for multi-camera rigs.
|
||||
cv::KeyPoint kpt;
|
||||
float depth;
|
||||
cv::Mat descriptor;
|
||||
int cameraIndex;
|
||||
};
|
||||
|
||||
typedef std::map<int, std::set<int> > BAOutliers; // <word ID, rejected pose IDs>, matching wordReferences
|
||||
|
||||
/**
|
||||
* @class Optimizer
|
||||
* @brief Abstract base for pose-graph and bundle-adjustment optimizers.
|
||||
*
|
||||
* Optimizer is a factory + interface in front of several third-party back-ends
|
||||
* (@ref kTypeTORO "TORO", @ref kTypeG2O "g2o", @ref kTypeGTSAM "GTSAM",
|
||||
* @ref kTypeCeres "Ceres", @ref kTypeCVSBA "cvsba"). Use @ref create() to instantiate one
|
||||
* based on the @c Optimizer/Strategy parameter; use @ref isAvailable() to check whether a
|
||||
* given back-end was compiled in.
|
||||
*
|
||||
* Two families of methods are exposed:
|
||||
* - **Pose-graph optimization** (@ref optimize / @ref optimizeIncremental) — refines poses given
|
||||
* relative-pose constraints. Subclasses override @ref optimize() with covariance output.
|
||||
* - **Bundle adjustment** (@ref optimizeBA) — jointly refines poses and 3D points using
|
||||
* reprojection error. Subclasses override the lowest-level overload; the others are
|
||||
* convenience wrappers that fill in models/correspondences from @ref Signature data.
|
||||
*
|
||||
* Common knobs (iterations, robust kernels, 2D-vs-3D, etc.) are configured through
|
||||
* @ref parseParameters() or per-attribute setters and apply to whichever back-end is selected.
|
||||
*/
|
||||
////////////////////////////////////////////
|
||||
// Graph optimizers
|
||||
////////////////////////////////////////////
|
||||
class RTABMAP_CORE_EXPORT Optimizer
|
||||
{
|
||||
public:
|
||||
/** @brief Graph-optimizer back-end identifier. */
|
||||
enum Type {
|
||||
kTypeUndef = -1, ///< Unspecified / invalid.
|
||||
kTypeTORO = 0, ///< TORO (tree-based relaxation).
|
||||
kTypeG2O = 1, ///< g2o (general graph optimization, supports BA).
|
||||
kTypeGTSAM = 2, ///< GTSAM (factor graphs, iSAM2-style incremental).
|
||||
kTypeCeres = 3, ///< Ceres Solver (nonlinear least squares, supports BA).
|
||||
kTypeCVSBA = 4 ///< cvsba (sparse bundle adjustment only).
|
||||
kTypeUndef = -1,
|
||||
kTypeTORO = 0,
|
||||
kTypeG2O = 1,
|
||||
kTypeGTSAM = 2,
|
||||
kTypeCeres = 3,
|
||||
kTypeCVSBA = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Returns whether @p type was compiled in (its third-party dependency was found).
|
||||
*
|
||||
* @ref kTypeUndef is treated as unavailable. @ref create() falls back through this check
|
||||
* to pick the first available back-end when the requested one is missing.
|
||||
*/
|
||||
static bool isAvailable(Optimizer::Type type);
|
||||
|
||||
/**
|
||||
* @brief Factory: build an optimizer from a @ref ParametersMap.
|
||||
*
|
||||
* Reads @c Optimizer/Strategy from @p parameters; falls back to the default strategy if
|
||||
* the chosen back-end isn't compiled in. Caller owns the returned pointer.
|
||||
*/
|
||||
static Optimizer * create(const ParametersMap & parameters);
|
||||
|
||||
/** @brief Factory: build an optimizer of a specific @p type. Caller owns the result. */
|
||||
static Optimizer * create(Optimizer::Type type, const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Extracts the connected component reachable from @p fromId.
|
||||
*
|
||||
* Walks @p linksIn breadth-first starting at @p fromId and copies every visited pose
|
||||
* (from @p posesIn) and every traversed link into @p posesOut / @p linksOut. Use this to
|
||||
* isolate the subgraph that actually affects @p fromId before calling @ref optimize(),
|
||||
* since the back-ends require a single connected component.
|
||||
*/
|
||||
// Get connected poses and constraints from a set of links
|
||||
void getConnectedGraph(
|
||||
int fromId,
|
||||
const std::map<int, Transform> & posesIn,
|
||||
@@ -137,23 +84,19 @@ public:
|
||||
public:
|
||||
virtual ~Optimizer() {}
|
||||
|
||||
/** @brief Returns the concrete back-end identifier (one of @ref Type). */
|
||||
virtual Type type() const = 0;
|
||||
|
||||
/// @name Getters for the settings shared across back-ends.
|
||||
/// @{
|
||||
int iterations() const {return iterations_;} ///< Max solver iterations.
|
||||
bool isSlam2d() const {return slam2d_;} ///< True if optimizing in SE(2) instead of SE(3).
|
||||
bool isCovarianceIgnored() const {return covarianceIgnored_;} ///< If true, all edges share an identity information matrix.
|
||||
double epsilon() const {return epsilon_;} ///< Convergence threshold on cost decrease.
|
||||
bool isRobust() const {return robust_;} ///< If true, use a robust kernel / switchable factors against bad loop closures.
|
||||
bool priorsIgnored() const {return priorsIgnored_;} ///< If true, unary priors on poses are dropped.
|
||||
bool landmarksIgnored() const {return landmarksIgnored_;}///< If true, landmark/marker observations are dropped.
|
||||
float gravitySigma() const {return gravitySigma_;} ///< Std-dev (rad) of the gravity prior on roll/pitch; 0 disables it.
|
||||
/// @}
|
||||
// getters
|
||||
int iterations() const {return iterations_;}
|
||||
bool isSlam2d() const {return slam2d_;}
|
||||
bool isCovarianceIgnored() const {return covarianceIgnored_;}
|
||||
double epsilon() const {return epsilon_;}
|
||||
bool isRobust() const {return robust_;}
|
||||
bool priorsIgnored() const {return priorsIgnored_;}
|
||||
bool landmarksIgnored() const {return landmarksIgnored_;}
|
||||
float gravitySigma() const {return gravitySigma_;}
|
||||
|
||||
/// @name Setters mirroring the corresponding getters.
|
||||
/// @{
|
||||
// setters
|
||||
void setIterations(int iterations) {iterations_ = iterations;}
|
||||
void setSlam2d(bool enabled) {slam2d_ = enabled;}
|
||||
void setCovarianceIgnored(bool enabled) {covarianceIgnored_ = enabled;}
|
||||
@@ -162,33 +105,9 @@ public:
|
||||
void setPriorsIgnored(bool enabled) {priorsIgnored_ = enabled;}
|
||||
void setLandmarksIgnored(bool enabled) {landmarksIgnored_ = enabled;}
|
||||
void setGravitySigma(float value) {gravitySigma_ = value;}
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* @brief Reads shared knobs from @p parameters and applies them to this instance.
|
||||
*
|
||||
* Subclasses override to additionally read back-end-specific keys (e.g. @c g2o/Solver,
|
||||
* @c GTSAM/Optimizer); they should call this base implementation first.
|
||||
*/
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/**
|
||||
* @brief Pose-graph optimization that grows the graph one node at a time.
|
||||
*
|
||||
* Inserts poses in @p poses iteration order, propagating odometry edges (@c Neighbor /
|
||||
* @c NeighborMerged) directly and triggering a call to @ref optimize() whenever a loop
|
||||
* closure is added. A final full @ref optimize() pass anchors at @p rootId. Useful for
|
||||
* warm-starting heavily-deformed initial guesses where a single-shot @ref optimize()
|
||||
* can diverge.
|
||||
*
|
||||
* @param rootId Pose whose absolute transform is held fixed.
|
||||
* @param poses Initial poses keyed by id.
|
||||
* @param constraints Relative-pose constraints (see @ref Link::Type).
|
||||
* @param intermediateGraphes Optional: appended per outer iteration for debug/visualization.
|
||||
* @param finalError Optional: written with the solver's final cost.
|
||||
* @param iterationsDone Optional: written with the solver's actual iteration count.
|
||||
* @return Refined poses (same key set as @p poses), or empty on failure.
|
||||
*/
|
||||
std::map<int, Transform> optimizeIncremental(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
@@ -197,12 +116,6 @@ public:
|
||||
double * finalError = 0,
|
||||
int * iterationsDone = 0);
|
||||
|
||||
/**
|
||||
* @brief Pose-graph optimization (single shot).
|
||||
*
|
||||
* Convenience overload that discards the output covariance. See the covariance-returning
|
||||
* overload below for parameter docs.
|
||||
*/
|
||||
std::map<int, Transform> optimize(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
@@ -211,22 +124,7 @@ public:
|
||||
double * finalError = 0,
|
||||
int * iterationsDone = 0);
|
||||
|
||||
/**
|
||||
* @brief Pose-graph optimization with marginal covariance of @p rootId.
|
||||
*
|
||||
* This is the primary back-end entry point — concrete subclasses override it. The base
|
||||
* implementation just emits an error.
|
||||
*
|
||||
* @param rootId Pose to hold fixed during optimization.
|
||||
* @param poses Initial pose estimates.
|
||||
* @param constraints Relative-pose constraints between pose ids (and optionally to landmarks).
|
||||
* @param outputCovariance Output: 6x6 covariance of the last optimized pose w.r.t. @p rootId
|
||||
* (3x3 for 2D mode). Filled only if the back-end supports it.
|
||||
* @param intermediateGraphes Optional: appended at each iteration for debug/visualization.
|
||||
* @param finalError Optional: written with the solver's final cost.
|
||||
* @param iterationsDone Optional: written with the solver's actual iteration count.
|
||||
* @return Refined poses, or empty on failure.
|
||||
*/
|
||||
// inherited classes should implement one of these methods
|
||||
virtual std::map<int, Transform> optimize(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
@@ -235,57 +133,25 @@ public:
|
||||
std::list<std::map<int, Transform> > * intermediateGraphes = 0,
|
||||
double * finalError = 0,
|
||||
int * iterationsDone = 0);
|
||||
|
||||
/**
|
||||
* @brief Bundle adjustment: jointly refine poses and 3D points (back-end-level entry point).
|
||||
*
|
||||
* Concrete subclasses (g2o, Ceres, cvsba) override this; the base implementation errors out.
|
||||
* The other @ref optimizeBA() overloads ultimately funnel here.
|
||||
*
|
||||
* @param rootId Pose to hold fixed. If negative, ALL poses other than the (positive) lowest id
|
||||
* are held fixed.
|
||||
* @param poses Initial pose estimates keyed by frame id.
|
||||
* @param links Edges used to define the BA problem topology.
|
||||
* @param models Camera model(s) per frame; for stereo, @c Tx must be set on the model
|
||||
* (= -baseline*fx). Multi-camera rigs have multiple entries per frame.
|
||||
* @param points3DMap In/out: world 3D points keyed by word id; refined on return.
|
||||
* @param wordReferences Observations: `<wordId, <frameId, FeatureBA>>`. See @ref FeatureBA.
|
||||
* @param outliers Optional output: word ids dropped by the back-end's outlier rejection.
|
||||
* @return Refined poses, or empty on failure.
|
||||
*/
|
||||
virtual std::map<int, Transform> optimizeBA(
|
||||
int rootId,
|
||||
int rootId, // if negative, all other poses are fixed
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
const std::map<int, std::vector<CameraModel> > & models,
|
||||
const std::map<int, std::vector<CameraModel> > & models, // in case of stereo, Tx should be set
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences,
|
||||
BAOutliers * outliers = 0);
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences, // <ID words, IDs frames + keypoint/depth/descriptor>
|
||||
std::set<int> * outliers = 0);
|
||||
|
||||
/**
|
||||
* @brief BA wrapper that derives camera models and correspondences from signatures.
|
||||
*
|
||||
* Builds @c models per frame from each @ref Signature's @ref SensorData (mono or stereo,
|
||||
* with stereo-baseline encoded in Tx), calls @ref computeBACorrespondences() to populate
|
||||
* @p points3DMap / @p wordReferences from the signatures' words, then delegates to the
|
||||
* back-end overload above.
|
||||
*
|
||||
* @param rematchFeatures If true, re-match descriptors across linked frames before
|
||||
* building correspondences (more accurate, more expensive).
|
||||
* @param registrationParameters Forwarded to @ref RegistrationVis when re-matching.
|
||||
*/
|
||||
std::map<int, Transform> optimizeBA(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
const std::map<int, Signature> & signatures,
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
std::map<int, std::map<int, FeatureBA> > & wordReferences,
|
||||
std::map<int, std::map<int, FeatureBA> > & wordReferences, // <ID words, IDs frames + keypoint/depth/descriptor>
|
||||
bool rematchFeatures = false,
|
||||
const ParametersMap & registrationParameters = ParametersMap());
|
||||
|
||||
/** @brief BA convenience wrapper: like the overload above but ignores the
|
||||
* refined 3D points and observation map. */
|
||||
std::map<int, Transform> optimizeBA(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
@@ -294,41 +160,19 @@ public:
|
||||
bool rematchFeatures = false,
|
||||
const ParametersMap & registrationParameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Refine a single two-frame link via BA.
|
||||
*
|
||||
* Sets the @c from frame at identity and the @c to frame at @c link.transform(), then runs
|
||||
* BA over the supplied 3D points and observations. Returns the refined relative transform,
|
||||
* or @c link.transform() unchanged on failure.
|
||||
*/
|
||||
Transform optimizeBA(
|
||||
const Link & link,
|
||||
const CameraModel & model,
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences,
|
||||
BAOutliers * outliers = 0);
|
||||
std::set<int> * outliers = 0);
|
||||
|
||||
/**
|
||||
* @brief Build BA correspondences (3D points + per-frame observations) from signatures.
|
||||
*
|
||||
* For each link, matches words between the two signatures with @ref RegistrationVis,
|
||||
* triangulates / lifts depth into world coordinates using the frames' initial poses, and
|
||||
* populates @p points3DMap (one entry per word id) and @p wordReferences (one entry per
|
||||
* (word, frame) observation).
|
||||
*
|
||||
* @param rematchFeatures If true, descriptors are re-matched between frames instead of
|
||||
* relying on pre-existing word ids — more robust to feature drift.
|
||||
* @param useLinkTransformAsGuess If true, the link's transform seeds the PnP guess
|
||||
* instead of estimating it from scratch.
|
||||
* @param registrationParameters Forwarded to @ref RegistrationVis (estimation type,
|
||||
* inlier counts, NNDR, etc.).
|
||||
*/
|
||||
void computeBACorrespondences(
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
const std::map<int, Signature> & signatures,
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
std::map<int, std::map<int, FeatureBA > > & wordReferences,
|
||||
std::map<int, std::map<int, FeatureBA > > & wordReferences, // <ID words, IDs frames + keypoint/depth/descriptor>
|
||||
bool rematchFeatures = false,
|
||||
bool useLinkTransformAsGuess = false,
|
||||
ParametersMap registrationParameters = ParametersMap());
|
||||
|
||||
@@ -40,9 +40,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
/** @brief Parameter keys mapped to their values, as used by every configurable class (see @ref Parameters). */
|
||||
typedef std::map<std::string, std::string> ParametersMap; // Key, value
|
||||
/** @brief A single parameter key/value pair, the entry type of @ref ParametersMap. */
|
||||
typedef std::pair<std::string, std::string> ParametersPair;
|
||||
|
||||
/**
|
||||
@@ -65,11 +63,8 @@ typedef std::pair<std::string, std::string> ParametersPair;
|
||||
*/
|
||||
#define RTABMAP_PARAM(PREFIX, NAME, TYPE, DEFAULT_VALUE, DESCRIPTION) \
|
||||
public: \
|
||||
/** @brief Key of parameter PREFIX##/##NAME: DESCRIPTION Default value: DEFAULT_VALUE (TYPE). */ \
|
||||
static std::string k##PREFIX##NAME() {return std::string(#PREFIX "/" #NAME);} \
|
||||
/** @brief Default value of parameter PREFIX##/##NAME: DEFAULT_VALUE. */ \
|
||||
static TYPE default##PREFIX##NAME() {return (TYPE)DEFAULT_VALUE;} \
|
||||
/** @brief Type of parameter PREFIX##/##NAME, as a string: TYPE. */ \
|
||||
static std::string type##PREFIX##NAME() {return std::string(#TYPE);} \
|
||||
private: \
|
||||
class Dummy##PREFIX##NAME { \
|
||||
@@ -102,11 +97,8 @@ typedef std::pair<std::string, std::string> ParametersPair;
|
||||
*/
|
||||
#define RTABMAP_PARAM_STR(PREFIX, NAME, DEFAULT_VALUE, DESCRIPTION) \
|
||||
public: \
|
||||
/** @brief Key of parameter PREFIX##/##NAME: DESCRIPTION Default value: DEFAULT_VALUE (string). */ \
|
||||
static std::string k##PREFIX##NAME() {return std::string(#PREFIX "/" #NAME);} \
|
||||
/** @brief Default value of parameter PREFIX##/##NAME: DEFAULT_VALUE. */ \
|
||||
static std::string default##PREFIX##NAME() {return DEFAULT_VALUE;} \
|
||||
/** @brief Type of parameter PREFIX##/##NAME, as a string: string. */ \
|
||||
static std::string type##PREFIX##NAME() {return std::string("string");} \
|
||||
private: \
|
||||
class Dummy##PREFIX##NAME { \
|
||||
@@ -138,11 +130,8 @@ typedef std::pair<std::string, std::string> ParametersPair;
|
||||
*/
|
||||
#define RTABMAP_PARAM_COND(PREFIX, NAME, TYPE, COND, DEFAULT_VALUE1, DEFAULT_VALUE2, DESCRIPTION) \
|
||||
public: \
|
||||
/** @brief Key of parameter PREFIX##/##NAME: DESCRIPTION Default value: DEFAULT_VALUE1 if COND, DEFAULT_VALUE2 otherwise (TYPE). */ \
|
||||
static std::string k##PREFIX##NAME() {return std::string(#PREFIX "/" #NAME);} \
|
||||
/** @brief Default value of parameter PREFIX##/##NAME: DEFAULT_VALUE1 if COND, DEFAULT_VALUE2 otherwise. */ \
|
||||
static TYPE default##PREFIX##NAME() {return COND?DEFAULT_VALUE1:DEFAULT_VALUE2;} \
|
||||
/** @brief Type of parameter PREFIX##/##NAME, as a string: TYPE. */ \
|
||||
static std::string type##PREFIX##NAME() {return std::string(#TYPE);} \
|
||||
private: \
|
||||
class Dummy##PREFIX##NAME { \
|
||||
@@ -176,8 +165,7 @@ typedef std::pair<std::string, std::string> ParametersPair;
|
||||
* std::string strValue = Util::value(Parameters::getDefaultParameters(), theKey); // strValue = "640"
|
||||
* @endcode
|
||||
* @see getDefaultParameters()
|
||||
* @see The @ref parameters "Parameter reference" page, which lists every parameter
|
||||
* with its type, default value and description.
|
||||
* TODO Add a detailed example with simple classes
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Parameters
|
||||
{
|
||||
@@ -252,10 +240,10 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(Mem, RotateImagesUpsideUp, bool, false, "Rotate images so that upside is up if they are not already. This can be useful in case the robots don't have all same camera orientation but are using the same map, so that not rotation-invariant visual features can still be used across the fleet.");
|
||||
|
||||
// KeypointMemory (Keypoint-based)
|
||||
RTABMAP_PARAM(Kp, NNStrategy, int, 1, "FLANN Linear=0, FLANN KdTree=1, FLANN LSH=2, Brute Force=3, Brute Force GPU=4, FLANN KdTree Single=5, NanoFLANN KdTree=6");
|
||||
RTABMAP_PARAM(Kp, NNStrategy, int, 1, "kNNFlannNaive=0, kNNFlannKdTree=1, kNNFlannLSH=2, kNNBruteForce=3, kNNBruteForceGPU=4");
|
||||
RTABMAP_PARAM(Kp, IncrementalDictionary, bool, true, "");
|
||||
RTABMAP_PARAM(Kp, IncrementalFlann, bool, true, uFormat("When using FLANN based strategy, add/remove points to its index without always rebuilding the index (the index is only rebuilt when too many of its features have been removed, see \"%s\").", kKpFlannRebalancingFactor().c_str()));
|
||||
RTABMAP_PARAM(Kp, FlannRebalancingFactor, float, 2.0, uFormat("Rebuild the incremental FLANN index (see \"%s\") once the ratio (factor-1)/factor of its features has been removed, e.g. half of them for a factor of 2. Rebuilding frees the memory of the removed features and speeds up the searches. Features are mostly removed when memory management is enabled (\"%s\" or \"%s\"). Set to 1 to never rebuild, which also uses less memory as the features don't have to be referenced one by one.", kKpIncrementalFlann().c_str(), kRtabmapTimeThr().c_str(), kRtabmapMemoryThr().c_str()));
|
||||
RTABMAP_PARAM(Kp, IncrementalFlann, bool, true, uFormat("When using FLANN based strategy, add/remove points to its index without always rebuilding the index (the index is built only when the dictionary increases of the factor \"%s\" in size).", kKpFlannRebalancingFactor().c_str()));
|
||||
RTABMAP_PARAM(Kp, FlannRebalancingFactor, float, 2.0, uFormat("Factor used when rebuilding the incremental FLANN index (see \"%s\"). Set <=1 to disable.", kKpIncrementalFlann().c_str()));
|
||||
RTABMAP_PARAM(Kp, ByteToFloat, bool, false, uFormat("For %s=1, binary descriptors are converted to float by converting each byte to float instead of converting each bit to float. When converting bytes instead of bits, less memory is used and search is faster at the cost of slightly less accurate matching.", kKpNNStrategy().c_str()));
|
||||
RTABMAP_PARAM(Kp, MaxDepth, float, 0, "Filter extracted keypoints by depth (0=inf).");
|
||||
RTABMAP_PARAM(Kp, MinDepth, float, 0, "Filter extracted keypoints by depth.");
|
||||
@@ -284,26 +272,10 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
|
||||
//Database
|
||||
RTABMAP_PARAM(DbSqlite3, InMemory, bool, false, "Using database in the memory instead of a file on the hard disk.");
|
||||
RTABMAP_PARAM(DbSqlite3, CacheSize, unsigned int, 10000,
|
||||
"PRAGMA cache_size: number of database pages kept in SQLite's page cache (approx. cacheSize * page_size bytes, often ~4 KiB per page). "
|
||||
"Larger values reduce disk I/O when the working set fits in RAM. SQLite built-in default is typically 2000 pages.");
|
||||
RTABMAP_PARAM(DbSqlite3, JournalMode, int, 3,
|
||||
"PRAGMA journal_mode: rollback journal storage. See sqlite.org/pragma.html#pragma_journal_mode for more details. "
|
||||
"0=DELETE (SQLite default): journal file deleted after each commit. "
|
||||
"1=TRUNCATE: journal truncated to zero length. "
|
||||
"2=PERSIST: journal file kept, header zeroed after commit. "
|
||||
"3=MEMORY: journal in RAM only; faster, weaker crash safety. "
|
||||
"4=OFF: no journal; fastest, risk of corruption on crash.");
|
||||
RTABMAP_PARAM(DbSqlite3, Synchronous, int, 0,
|
||||
"PRAGMA synchronous: how aggressively SQLite syncs the database to disk. See sqlite.org/pragma.html#pragma_synchronous for more details. "
|
||||
"0=OFF: no wait for persistent storage; fastest, corruption possible on power loss. "
|
||||
"1=NORMAL: sync at critical moments (common SQLite default with WAL). "
|
||||
"2=FULL (SQLite safest default): sync after every commit; slowest.");
|
||||
RTABMAP_PARAM(DbSqlite3, TempStore, int, 2,
|
||||
"PRAGMA temp_store: where SQLite stores temporary tables and indices. See sqlite.org/pragma.html#pragma_temp_store for more details. "
|
||||
"0=DEFAULT: SQLite compile-time default (often on-disk temp files). "
|
||||
"1=FILE: temporary files in the system temp directory. "
|
||||
"2=MEMORY: temporary data in RAM when possible.");
|
||||
RTABMAP_PARAM(DbSqlite3, CacheSize, unsigned int, 10000, "Sqlite cache size (default is 2000).");
|
||||
RTABMAP_PARAM(DbSqlite3, JournalMode, int, 3, "0=DELETE, 1=TRUNCATE, 2=PERSIST, 3=MEMORY, 4=OFF (see sqlite3 doc : \"PRAGMA journal_mode\")");
|
||||
RTABMAP_PARAM(DbSqlite3, Synchronous, int, 0, "0=OFF, 1=NORMAL, 2=FULL (see sqlite3 doc : \"PRAGMA synchronous\")");
|
||||
RTABMAP_PARAM(DbSqlite3, TempStore, int, 2, "0=DEFAULT, 1=FILE, 2=MEMORY (see sqlite3 doc : \"PRAGMA temp_store\")");
|
||||
RTABMAP_PARAM_STR(Db, TargetVersion, "", "Target database version for backward compatibility purpose. Only Major and minor versions are used and should be set (e.g., 0.19 vs 0.20 or 1.0 vs 2.0). Patch version is ignored (e.g., 0.20.1 and 0.20.3 will generate a 0.20 database).");
|
||||
|
||||
// Keypoints descriptors/detectors
|
||||
@@ -387,10 +359,9 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(PyDetector, Cuda, bool, true, "Use cuda.");
|
||||
|
||||
// BayesFilter
|
||||
RTABMAP_PARAM(Bayes, VirtualPlacePriorThr, float, 0.9, "Virtual place prior. Considering that we are at a new place, this is the prior probability to move again to a new place (unvisited location). The prior probability to move to a previously visited location is 1 - VirtualPlacePriorThr (split equally against all previously visited locations).");
|
||||
RTABMAP_PARAM_STR(Bayes, PredictionLC, "0.1 0.36 0.30 0.16 0.062 0.0151 0.00255 0.000324 2.5e-05 1e-06 4.8e-08 1.2e-09 1.9e-11 2.2e-13 1.7e-15 8.5e-18 2.9e-20 6.9e-23", "Prediction of loop closures (Gaussian-like, here with sigma=1.6) - Format: {VirtualPlaceProb, LoopClosureProb, NeighborLvl1, NeighborLvl2, ...}. Considering we are at a previously visited location, the first value is the probability to move to a new place (unvisited location), the second value is the probability to stay at the same location, the third value is the probability to move to a neighbor or loop closure at the first depth level, the fourth value is the probability to move to a neighbor or loop closure at the second depth level, etc. If the sum of the values is not 1, the difference is normalized against all remaining visited locations. Normally, the sum of these values should be 1.");
|
||||
RTABMAP_PARAM(Bayes, VirtualPlacePriorThr, float, 0.9, "Virtual place prior");
|
||||
RTABMAP_PARAM_STR(Bayes, PredictionLC, "0.1 0.36 0.30 0.16 0.062 0.0151 0.00255 0.000324 2.5e-05 1.3e-06 4.8e-08 1.2e-09 1.9e-11 2.2e-13 1.7e-15 8.5e-18 2.9e-20 6.9e-23", "Prediction of loop closures (Gaussian-like, here with sigma=1.6) - Format: {VirtualPlaceProb, LoopClosureProb, NeighborLvl1, NeighborLvl2, ...}.");
|
||||
RTABMAP_PARAM(Bayes, FullPredictionUpdate, bool, false, "Regenerate all the prediction matrix on each iteration (otherwise only removed/added ids are updated).");
|
||||
RTABMAP_PARAM(Bayes, SparsePrediction, bool, true, uFormat("Use a sparse representation of the prediction instead of a dense matrix, which significantly reduces memory usage and processing time on large maps. Ignored when the values of %s sum to less than 1, as the prediction is then not sparse.", kBayesPredictionLC().c_str()).c_str());
|
||||
|
||||
// Verify hypotheses
|
||||
RTABMAP_PARAM(VhEp, Enabled, bool, false, uFormat("Verify visual loop closure hypothesis by computing a fundamental matrix. This is done prior to transformation computation when %s is enabled.", kRGBDEnabled().c_str()));
|
||||
@@ -485,11 +456,9 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(g2o, Solver, int, 0, "0=csparse 1=pcg 2=cholmod 3=Eigen");
|
||||
#endif
|
||||
RTABMAP_PARAM(g2o, Optimizer, int, 0, "0=Levenberg 1=GaussNewton");
|
||||
|
||||
RTABMAP_PARAM(Optimizer, Baseline, double, 0.075, "When doing bundle adjustment with RGB-D data (mono camera + depth), set a fake baseline (m) so the BA backend treats depth as stereo disparity. Applies to all BA-capable backends (g2o, GTSAM, Ceres). Set to 0 to keep the problem mono (depth observations are ignored). For real stereo data the baseline in the calibration (Tx) is used directly.");
|
||||
RTABMAP_PARAM(Optimizer, PixelVariance, double, 1.0, "Pixel variance used on the u/v axes of every bundle adjustment reprojection edge. Applies to all BA-capable backends (g2o, GTSAM, Ceres). Should approximate the squared 1-sigma keypoint localization error in pixels. Set higher (e.g. 4-9) if features are noisy (low texture, motion blur, low light, or large detector scale). Set lower (e.g. 0.01-0.1) if features are sub-pixel refined (Lucas-Kanade tracking, parabolic peak interpolation). Intuition: the lower the pixel variance, the more the optimizer trusts the keypoint positions.");
|
||||
RTABMAP_PARAM(Optimizer, DisparityVariance, double, 1.0, "Disparity variance used on the disparity axis (u - u_right) of stereo / RGB-D bundle adjustment edges. Applies to all BA-capable backends (g2o, GTSAM, Ceres). Defaults to the same value as PixelVariance for backward compatibility. Set higher (e.g. 2-4) if your depth source is noisier than your feature detector's u/v precision (typical for stereo block matchers / SGM at long range). Set lower (e.g. 0.01-0.1) if your depth source is more accurate than the u/v detector (typical for ToF / LiDAR-fused depth where range is measured directly rather than triangulated). Intuition: the lower the disparity variance, the more the optimizer trusts the depth measurements. Geometric note: wider baseline and/or higher image resolution improve a block matcher's effective disparity precision (larger disparity magnitudes and finer sub-pixel refinement), so wide-baseline high-resolution stereo pairs can usually afford a lower disparity variance (e.g. 0.1-0.5); narrow-baseline low-resolution pairs should keep it higher (e.g. 1-4).");
|
||||
RTABMAP_PARAM(Optimizer, RobustKernelDelta, double, 8, "Robust kernel delta used for bundle adjustment (0 means don't use robust kernel). Applies to all BA-capable backends (g2o, GTSAM, Ceres). Observations with chi2 over this threshold will be ignored in the second optimization pass.");
|
||||
RTABMAP_PARAM(g2o, PixelVariance, double, 1.0, "Pixel variance used for bundle adjustment.");
|
||||
RTABMAP_PARAM(g2o, RobustKernelDelta, double, 8, "Robust kernel delta used for bundle adjustment (0 means don't use robust kernel). Observations with chi2 over this threshold will be ignored in the second optimization pass.");
|
||||
RTABMAP_PARAM(g2o, Baseline, double, 0.075, "When doing bundle adjustment with RGB-D data, we can set a fake baseline (m) to do stereo bundle adjustment (if 0, mono bundle adjustment is done). For stereo data, the baseline in the calibration is used directly.");
|
||||
|
||||
RTABMAP_PARAM(GTSAM, Optimizer, int, 1, "0=Levenberg 1=GaussNewton 2=Dogleg");
|
||||
RTABMAP_PARAM(GTSAM, Incremental, bool, false, uFormat("Do graph optimization incrementally (iSAM2) to increase optimization speed on loop closures. Note that only GaussNewton and Dogleg optimization algorithms are supported (%s) in this mode.", kGTSAMOptimizer().c_str()));
|
||||
@@ -530,9 +499,9 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(OdomF2M, ScanRange, float, 0, "[Geometry] Distance Range used to filter points of local map (when > 0). 0 means local map is updated using time and not range.");
|
||||
RTABMAP_PARAM(OdomF2M, ValidDepthRatio, float, 0.75, "If a new frame has points without valid depth, they are added to local feature map only if points with valid depth on total points is over this ratio. Setting to 1 means no points without valid depth are added to local feature map.");
|
||||
#if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM)
|
||||
RTABMAP_PARAM(OdomF2M, BundleAdjustment, int, 1, uFormat("Local bundle adjustment. Value matches the %s parameter: 0=disabled (TORO is not BA-capable), 1=g2o, 2=GTSAM, 3=Ceres, 4=cvsba.", kOptimizerStrategy().c_str()));
|
||||
RTABMAP_PARAM(OdomF2M, BundleAdjustment, int, 1, "Local bundle adjustment: 0=disabled, 1=g2o, 2=cvsba, 3=Ceres.");
|
||||
#else
|
||||
RTABMAP_PARAM(OdomF2M, BundleAdjustment, int, 0, uFormat("Local bundle adjustment. Value matches the %s parameter: 0=disabled (TORO is not BA-capable), 1=g2o, 2=GTSAM, 3=Ceres, 4=cvsba.", kOptimizerStrategy().c_str()));
|
||||
RTABMAP_PARAM(OdomF2M, BundleAdjustment, int, 0, "Local bundle adjustment: 0=disabled, 1=g2o, 2=cvsba, 3=Ceres.");
|
||||
#endif
|
||||
RTABMAP_PARAM(OdomF2M, BundleAdjustmentMaxFrames, int, 10, "Maximum frames used for bundle adjustment (0=inf or all current frames in the local map).");
|
||||
RTABMAP_PARAM(OdomF2M, BundleAdjustmentMinMotion, float, 0.0, "To create a new keyframe with bundle adjustment, a minimum motion (in pixels) can be required. The motion is computed by the average distance between inliers of the previous keyframe and new frame.");
|
||||
@@ -781,7 +750,7 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(Vis, GridRows, int, 1, uFormat("Number of rows of the grid used to extract uniformly \"%s / grid cells\" features from each cell.", kVisMaxFeatures().c_str()));
|
||||
RTABMAP_PARAM(Vis, GridCols, int, 1, uFormat("Number of columns of the grid used to extract uniformly \"%s / grid cells\" features from each cell.", kVisMaxFeatures().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorType, int, 0, "Correspondences computation approach: 0=Features Matching, 1=Optical Flow");
|
||||
RTABMAP_PARAM(Vis, CorNNType, int, 1, uFormat("[%s=0] FLANN Linear=0, FLANN KdTree=1, FLANN LSH=2, Brute Force=3, Brute Force GPU=4, Brute Force Cross Check=5, SuperGlue=6, GMS=7, FLANN KdTree Single=8, NanoFLANN KdTree=9. Used for features matching approach.", kVisCorType().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorNNType, int, 1, uFormat("[%s=0] kNNFlannNaive=0, kNNFlannKdTree=1, kNNFlannLSH=2, kNNBruteForce=3, kNNBruteForceGPU=4, BruteForceCrossCheck=5, SuperGlue=6, GMS=7. Used for features matching approach.", kVisCorType().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorNNDR, float, 0.8, uFormat("[%s=0] NNDR: nearest neighbor distance ratio. Used for knn features matching approach.", kVisCorType().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorGuessWinSize, int, 40, uFormat("[%s=0] Matching window size (pixels) around projected points when a guess transform is provided to find correspondences. 0 means disabled.", kVisCorType().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorGuessMatchToProjection, bool, false, uFormat("[%s=0] Match frame's corners to source's projected points (when guess transform is provided) instead of projected points to frame's corners.", kVisCorType().c_str()));
|
||||
@@ -794,9 +763,9 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(Vis, CorFlowErrorThreshold, float, 20, uFormat("[%s=false] Filter out features with error greater than this threshold.", kVisCorFlowUseMinEigenVals().c_str()));
|
||||
RTABMAP_PARAM(Vis, CorFlowGpu, bool, false, uFormat("[%s=1] Enable GPU version of the optical flow approach (only available if OpenCV is built with CUDA). Note that %s is not used in the GPU implementation.", kVisCorType().c_str(), kVisCorFlowUseMinEigenVals().c_str()));
|
||||
#if defined(RTABMAP_G2O) || defined(RTABMAP_ORB_SLAM)
|
||||
RTABMAP_PARAM(Vis, BundleAdjustment, int, 1, uFormat("Optimization with bundle adjustment. Value matches the %s parameter: 0=disabled (TORO is not BA-capable), 1=g2o, 2=GTSAM, 3=Ceres, 4=cvsba.", kOptimizerStrategy().c_str()));
|
||||
RTABMAP_PARAM(Vis, BundleAdjustment, int, 1, "Optimization with bundle adjustment: 0=disabled, 1=g2o, 2=cvsba, 3=Ceres.");
|
||||
#else
|
||||
RTABMAP_PARAM(Vis, BundleAdjustment, int, 0, uFormat("Optimization with bundle adjustment. Value matches the %s parameter: 0=disabled (TORO is not BA-capable), 1=g2o, 2=GTSAM, 3=Ceres, 4=cvsba.", kOptimizerStrategy().c_str()));
|
||||
RTABMAP_PARAM(Vis, BundleAdjustment, int, 0, "Optimization with bundle adjustment: 0=disabled, 1=g2o, 2=cvsba, 3=Ceres.");
|
||||
#endif
|
||||
|
||||
// Features matching approaches
|
||||
@@ -846,9 +815,8 @@ class RTABMAP_CORE_EXPORT Parameters
|
||||
RTABMAP_PARAM(Icp, PointToPlaneRadius, float, 0.0, "Search radius to compute normals for point to plane if the cloud doesn't have already normals.");
|
||||
RTABMAP_PARAM(Icp, PointToPlaneGroundNormalsUp, float, 0.0, "Invert normals on ground if they are pointing down (useful for ring-like 3D LiDARs). 0 means disabled, 1 means only normals perfectly aligned with -z axis. This is only done with 3D scans.");
|
||||
RTABMAP_PARAM(Icp, PointToPlaneMinComplexity, float, 0.02, uFormat("Minimum structural complexity (0.0=low, 1.0=high) of the scan to do PointToPlane registration, otherwise PointToPoint registration is done instead and strategy from %s is used. This check is done only when %s=true.", kIcpPointToPlaneLowComplexityStrategy().c_str(), kIcpPointToPlane().c_str()));
|
||||
RTABMAP_PARAM(Icp, PointToPlaneComplexityCentered, bool, false, uFormat("If false (default), the complexity metric uses the uncentered second-moment matrix (1/N) * sum(n_i * n_i^T), whose smallest eigenvalue directly measures how well the surface normals span R^N. If true, uses centered PCA (cv::PCA covariance) for backwards compatibility -- but the centered metric is known to mis-classify perpendicular-surface scenes as degenerate when normals are consistently viewpoint-flipped (only N distinct directions in N-D collapse to rank N-1 after centering). For true degeneracies (parallel surfaces, e.g. corridors) the two metrics agree because the normal mean is zero. The %s threshold of 0.02 works under either setting.", kIcpPointToPlaneMinComplexity().c_str()));
|
||||
RTABMAP_PARAM(Icp, PointToPlaneLowComplexityStrategy, int, 1, uFormat("If structural complexity is below %s: set to 0 so that the transform is automatically rejected, set to 1 (default, legacy) to recompute the transform with PointToPoint and limit its correction in axes with most constraints (e.g., for a corridor-like environment, the resulting transform will be limited in y and yaw, x will taken from the guess), set to 2 to recompute the transform with PointToPoint and accept it \"as is\", set to 3 to keep the PointToPlane transform and apply the same axis-constrained projection as strategy 1.", kIcpPointToPlaneMinComplexity().c_str()));
|
||||
RTABMAP_PARAM(Icp, OutlierRatio, float, 0.85, uFormat("Outlier ratio. For libpointmatcher (%s=1), sets TrimmedDistOutlierFilter/ratio for convenience when configuration file is not set. For CCCoreLib (%s=2), sets \"finalOverlapRatio\". For PCL (%s=0), if 0<value<1, installs a RANSAC correspondence rejector with inlier threshold = value * %s. The value should be between 0 and 1.", kIcpStrategy().c_str(), kIcpStrategy().c_str(), kIcpStrategy().c_str(), kIcpMaxCorrespondenceDistance().c_str()));
|
||||
RTABMAP_PARAM(Icp, PointToPlaneLowComplexityStrategy, int, 1, uFormat("If structural complexity is below %s: set to 0 to so that the transform is automatically rejected, set to 1 to limit ICP correction in axes with most constraints (e.g., for a corridor-like environment, the resulting transform will be limited in y and yaw, x will taken from the guess), set to 2 to accept \"as is\" the transform computed by PointToPoint.", kIcpPointToPlaneMinComplexity().c_str()));
|
||||
RTABMAP_PARAM(Icp, OutlierRatio, float, 0.85, uFormat("Outlier ratio used with %s>0. For libpointmatcher, this parameter set TrimmedDistOutlierFilter/ratio for convenience when configuration file is not set. For CCCoreLib, this parameter set the \"finalOverlapRatio\". The value should be between 0 and 1.", kIcpStrategy().c_str()));
|
||||
RTABMAP_PARAM_STR(Icp, DebugExportFormat, "", "Export scans used for ICP in the specified format (a warning on terminal will be shown with the file paths used). Supported formats are \"pcd\", \"ply\" or \"vtk\". If logger level is debug, from and to scans will stamped, so previous files won't be overwritten.");
|
||||
|
||||
// libpointmatcher
|
||||
|
||||
@@ -69,7 +69,7 @@ std::vector<double> resample(const std::vector<double> & p, // particles
|
||||
std::vector<double> np; //new particles
|
||||
if(p.size() != w.size() || p.size() == 0)
|
||||
{
|
||||
UERROR("particles (%d) and weights (%d) are not the same size", (int)p.size(), (int)w.size());
|
||||
UERROR("particles (%d) and weights (%d) are not the same size", p.size(), w.size());
|
||||
return np;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,24 +21,16 @@ class gil_scoped_release;
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* Process-wide singleton owning the embedded Python interpreter.
|
||||
* Call PythonInterface::instance() from the main thread (typically near
|
||||
* the top of main()) before any Python-using class is constructed.
|
||||
* Create a single PythonInterface on main thread at
|
||||
* global scope before any Python classes.
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT PythonInterface
|
||||
{
|
||||
public:
|
||||
// Pass a caller tag (e.g. class name) so the main-thread assertion
|
||||
// can report who triggered the first construction.
|
||||
static PythonInterface & instance(const std::string & caller = "");
|
||||
|
||||
PythonInterface(const PythonInterface &) = delete;
|
||||
PythonInterface & operator=(const PythonInterface &) = delete;
|
||||
PythonInterface();
|
||||
virtual ~PythonInterface();
|
||||
|
||||
private:
|
||||
explicit PythonInterface(const std::string & caller);
|
||||
~PythonInterface();
|
||||
|
||||
pybind11::scoped_interpreter* guard_;
|
||||
pybind11::gil_scoped_release* release_;
|
||||
};
|
||||
|
||||
@@ -36,90 +36,52 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class Registration
|
||||
* @brief Abstract base for registering two observations (visual, ICP, or both).
|
||||
*
|
||||
* Factory @ref create() builds @ref RegistrationVis, @ref RegistrationIcp, or
|
||||
* the two chained (@ref kTypeVisIcp) from **Reg/Strategy** in a @ref ParametersMap.
|
||||
*
|
||||
* @ref computeTransformation() wraps signatures and delegates to
|
||||
* @ref computeTransformationMod(), which calls @ref computeTransformationImpl()
|
||||
* on the concrete strategy, optionally chains a child registration (e.g. ICP
|
||||
* after visual), applies **Reg/Force3DoF**, and may repeat once with the first
|
||||
* result as guess when **Reg/RepeatOnce** is enabled.
|
||||
*
|
||||
* Requirement queries (@ref isImageRequired(), @ref isScanRequired(), etc.) OR-combine
|
||||
* with the child when the parent returns false. Minimum correspondence thresholds
|
||||
* take the maximum between parent and child.
|
||||
*
|
||||
* @see RegistrationVis
|
||||
* @see RegistrationIcp
|
||||
* @see RegistrationInfo
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Registration
|
||||
{
|
||||
public:
|
||||
/** @brief Registration strategy selected by **Reg/Strategy**. */
|
||||
enum Type {
|
||||
kTypeUndef = -1, /**< Undefined / invalid strategy. */
|
||||
kTypeVis = 0, /**< Visual registration only (@ref RegistrationVis). */
|
||||
kTypeIcp = 1, /**< Geometric registration only (@ref RegistrationIcp). */
|
||||
kTypeVisIcp = 2 /**< Visual registration refined by ICP (@ref RegistrationVis chained with @ref RegistrationIcp). */
|
||||
kTypeUndef = -1,
|
||||
kTypeVis = 0,
|
||||
kTypeIcp = 1,
|
||||
kTypeVisIcp = 2
|
||||
};
|
||||
/** @brief Minimum diagonal value for linear covariance (m²). */
|
||||
static double COVARIANCE_LINEAR_EPSILON;
|
||||
/** @brief Minimum diagonal value for angular covariance (rad²). */
|
||||
static double COVARIANCE_ANGULAR_EPSILON;
|
||||
|
||||
public:
|
||||
/** @brief Creates a registration from **Reg/Strategy** in @p parameters. Caller owns the pointer. */
|
||||
static Registration * create(const ParametersMap & parameters);
|
||||
/** @brief Creates a registration of @p type (may be adjusted, e.g. to @ref kTypeVis). Caller owns the pointer. */
|
||||
static Registration * create(Type & type, const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
public:
|
||||
virtual ~Registration();
|
||||
/** @brief Parses **Reg/RepeatOnce**, **Reg/Force3DoF** and forwards to the child registration if set. */
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/** @brief True if images are needed (parent or child). */
|
||||
bool isImageRequired() const;
|
||||
/** @brief True if laser scans are needed (parent or child). */
|
||||
bool isScanRequired() const;
|
||||
/** @brief True if user data is needed (parent or child). */
|
||||
bool isUserDataRequired() const;
|
||||
|
||||
/** @brief True if the strategy can refine an initial guess (parent or child). */
|
||||
bool canUseGuess() const;
|
||||
|
||||
/** @brief Minimum visual inliers required (max of parent and child thresholds). */
|
||||
int getMinVisualCorrespondences() const;
|
||||
/** @brief Minimum geometry inlier ratio required (max of parent and child thresholds). */
|
||||
float getMinGeometryCorrespondencesRatio() const;
|
||||
|
||||
/** @return Value of **Reg/RepeatOnce**. */
|
||||
bool repeatOnce() const {return repeatOnce_;}
|
||||
/** @return Value of **Reg/Force3DoF**. */
|
||||
bool force3DoF() const {return force3DoF_;}
|
||||
|
||||
/** @brief Replaces the current child registration; takes ownership of @p child. */
|
||||
// take ownership!
|
||||
void setChildRegistration(Registration * child);
|
||||
|
||||
/** @brief Registers @p from to @p to using immutable signatures (copied internally). */
|
||||
Transform computeTransformation(
|
||||
const Signature & from,
|
||||
const Signature & to,
|
||||
Transform guess = Transform::getIdentity(),
|
||||
RegistrationInfo * info = 0) const;
|
||||
/** @brief Registers two @ref SensorData observations (wrapped as signatures). */
|
||||
Transform computeTransformation(
|
||||
const SensorData & from,
|
||||
const SensorData & to,
|
||||
Transform guess = Transform::getIdentity(),
|
||||
RegistrationInfo * info = 0) const;
|
||||
|
||||
/** @brief Registers @p from to @p to; signatures may be modified by the implementation. */
|
||||
Transform computeTransformationMod(
|
||||
Signature & from,
|
||||
Signature & to,
|
||||
@@ -127,14 +89,11 @@ public:
|
||||
RegistrationInfo * info = 0) const;
|
||||
|
||||
protected:
|
||||
/** @brief @p child is owned and deleted in the destructor. */
|
||||
// take ownership of child
|
||||
Registration(const ParametersMap & parameters = ParametersMap(), Registration * child = 0);
|
||||
|
||||
/**
|
||||
* @brief Strategy-specific registration.
|
||||
*
|
||||
* May modify @p from and @p to; a child registration reuses those changes.
|
||||
*/
|
||||
// It is safe to modify the signatures in the implementation, if so, the
|
||||
// child registration will use these modifications.
|
||||
virtual Transform computeTransformationImpl(
|
||||
Signature & from,
|
||||
Signature & to,
|
||||
|
||||
@@ -39,33 +39,8 @@ namespace rtabmap {
|
||||
class RTABMAP_CORE_EXPORT RegistrationIcp : public Registration
|
||||
{
|
||||
public:
|
||||
enum IcpStrategy {
|
||||
kIcpUndef = -1, /**< Undefined / invalid type. */
|
||||
kIcpPCL = 0, /**< Point Cloud Library. */
|
||||
kIcpPointMatcher = 1, /**< libpointmatcher. */
|
||||
kIcpCCCoreLib = 2, /**< CCCoreLib (Cloud Compare). */
|
||||
kIcpEnd = 3 /**< Sentinel: always keep last. Used to iterate through strategies. */
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Returns true if @p strategy is built into this rtabmap binary.
|
||||
*
|
||||
* @ref kIcpPCL is always available; @ref kIcpPointMatcher requires the
|
||||
* RTABMAP_POINTMATCHER build flag; @ref kIcpCCCoreLib requires
|
||||
* RTABMAP_CCCORELIB.
|
||||
*/
|
||||
static bool available(IcpStrategy strategy);
|
||||
|
||||
/**
|
||||
* @brief Human-readable name for @p strategy ("PCL", "libpointmatcher",
|
||||
* "CCCoreLib", or "Unknown").
|
||||
*/
|
||||
static const char * strategyName(IcpStrategy strategy);
|
||||
|
||||
// take ownership of child
|
||||
RegistrationIcp(const ParametersMap & parameters = ParametersMap(), Registration * child = 0);
|
||||
RegistrationIcp(IcpStrategy strategy, const ParametersMap & parameters = ParametersMap(), Registration * child = 0);
|
||||
virtual ~RegistrationIcp();
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
@@ -81,7 +56,7 @@ protected:
|
||||
virtual float getMinGeometryCorrespondencesRatioImpl() const {return _correspondenceRatio;}
|
||||
|
||||
private:
|
||||
IcpStrategy _strategy;
|
||||
int _strategy;
|
||||
float _maxTranslation;
|
||||
float _maxRotation;
|
||||
float _voxelSize;
|
||||
@@ -100,7 +75,6 @@ private:
|
||||
float _pointToPlaneRadius;
|
||||
float _pointToPlaneGroundNormalsUp;
|
||||
float _pointToPlaneMinComplexity;
|
||||
bool _pointToPlaneComplexityCentered;
|
||||
int _pointToPlaneLowComplexityStrategy;
|
||||
std::string _libpointmatcherConfig;
|
||||
int _libpointmatcherKnn;
|
||||
|
||||
@@ -31,19 +31,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class RegistrationInfo
|
||||
* @brief Statistics and diagnostics returned by @ref Registration.
|
||||
*
|
||||
* Filled by @ref RegistrationVis and/or @ref RegistrationIcp during
|
||||
* @ref Registration::computeTransformation(). The 6×6 @ref covariance matrix
|
||||
* is clamped to @ref Registration::COVARIANCE_LINEAR_EPSILON and
|
||||
* @ref Registration::COVARIANCE_ANGULAR_EPSILON on the diagonal when empty or
|
||||
* too small.
|
||||
*
|
||||
* Visual fields are set by @ref RegistrationVis; ICP fields by @ref RegistrationIcp.
|
||||
* In a combined Vis+ICP pipeline, both sets may be populated in the same object.
|
||||
*/
|
||||
class RegistrationInfo
|
||||
{
|
||||
public:
|
||||
@@ -61,13 +48,11 @@ public:
|
||||
icpStructuralComplexity(0.0f),
|
||||
icpStructuralDistribution(0.0f),
|
||||
icpCorrespondences(0),
|
||||
icpRMS(0),
|
||||
icpIterations(-1)
|
||||
icpRMS(0)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
/** @brief Copies scalar metrics and @ref covariance; omits correspondence ID vectors. */
|
||||
RegistrationInfo copyWithoutData() const
|
||||
{
|
||||
RegistrationInfo output;
|
||||
@@ -88,36 +73,34 @@ public:
|
||||
output.icpStructuralDistribution = icpStructuralDistribution;
|
||||
output.icpCorrespondences = icpCorrespondences;
|
||||
output.icpRMS = icpRMS;
|
||||
output.icpIterations = icpIterations;
|
||||
return output;
|
||||
}
|
||||
|
||||
cv::Mat covariance; /**< 6×6 registration uncertainty (CV_64FC1). */
|
||||
std::string rejectedMsg; /**< Reason the registration was rejected, if any. */
|
||||
double totalTime; /**< Total registration time (seconds). */
|
||||
cv::Mat covariance;
|
||||
std::string rejectedMsg;
|
||||
double totalTime;
|
||||
|
||||
// RegistrationVis
|
||||
int inliers; /**< Number of visual inliers. */
|
||||
float inliersRatio; /**< Ratio of inliers to matches. */
|
||||
float inliersMeanDistance; /**< Mean reprojection or descriptor distance of inliers. */
|
||||
float inliersDistribution; /**< Spatial spread of inliers in the image. */
|
||||
std::vector<int> inliersIDs; /**< Indices of inlier keypoints. */
|
||||
int matches; /**< Total visual matches before outlier rejection. */
|
||||
float variance; /**< Estimated variance of the visual constraint. */
|
||||
std::vector<int> matchesIDs; /**< Indices of all matches. */
|
||||
std::vector<int> projectedIDs; /**< Source ("from") feature IDs used in projection. */
|
||||
std::vector<int> inliersPerCam; /**< Inlier count per camera (multi-camera). */
|
||||
std::vector<int> matchesPerCam; /**< Match count per camera (multi-camera). */
|
||||
int inliers;
|
||||
float inliersRatio;
|
||||
float inliersMeanDistance;
|
||||
float inliersDistribution;
|
||||
std::vector<int> inliersIDs;
|
||||
int matches;
|
||||
float variance;
|
||||
std::vector<int> matchesIDs;
|
||||
std::vector<int> projectedIDs; // "From" IDs
|
||||
std::vector<int> inliersPerCam;
|
||||
std::vector<int> matchesPerCam;
|
||||
|
||||
// RegistrationIcp
|
||||
float icpInliersRatio; /**< Ratio of ICP inlier correspondences. */
|
||||
float icpTranslation; /**< Translation component of the ICP correction (m). */
|
||||
float icpRotation; /**< Rotation component of the ICP correction (rad). */
|
||||
float icpStructuralComplexity; /**< Structural complexity of the scan overlap. */
|
||||
float icpStructuralDistribution; /**< Distribution of structural features in the overlap. */
|
||||
int icpCorrespondences; /**< Number of ICP point correspondences. */
|
||||
float icpRMS; /**< Root-mean-square error of ICP correspondences (m). */
|
||||
int icpIterations; /**< Number of ICP iterations actually performed (-1 if unavailable from backend). */
|
||||
float icpInliersRatio;
|
||||
float icpTranslation;
|
||||
float icpRotation;
|
||||
float icpStructuralComplexity;
|
||||
float icpStructuralDistribution;
|
||||
int icpCorrespondences;
|
||||
float icpRMS;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -41,53 +41,23 @@ class Feature2D;
|
||||
class PyMatcher;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @class RegistrationVis
|
||||
* @brief Visual registration between two signatures using features and geometry.
|
||||
*
|
||||
* Extracts and matches features (@ref Feature2D), then estimates motion with
|
||||
* **Vis/EstimationType** (3D→3D, 3D→2D PnP, or 2D→2D epipolar). Supports RGB-D
|
||||
* (@ref CameraModel + depth), stereo (@ref StereoCameraModel), optical flow, GMS,
|
||||
* and optional Python matchers when built with Python support.
|
||||
*
|
||||
* Can chain a child registration (e.g. @ref RegistrationIcp in
|
||||
* @ref Registration::kTypeVisIcp). Feature-related Vis/ parameters are mapped
|
||||
* to Kp/ for @ref Feature2D::create().
|
||||
*
|
||||
* @see Registration
|
||||
* @see RegistrationInfo
|
||||
*/
|
||||
// Visual registration
|
||||
class RTABMAP_CORE_EXPORT RegistrationVis : public Registration
|
||||
{
|
||||
public:
|
||||
/** @brief @p child is owned and deleted in the destructor. */
|
||||
// take ownership of child
|
||||
RegistrationVis(const ParametersMap & parameters = ParametersMap(), Registration * child = 0);
|
||||
virtual ~RegistrationVis();
|
||||
|
||||
/** @brief Parses Vis/ and feature parameters; recreates feature detectors. */
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/** @return **Vis/InlierDistance** (m). */
|
||||
float getInlierDistance() const {return _inlierDistance;}
|
||||
/** @return **Vis/Iterations** (RANSAC iterations). */
|
||||
int getIterations() const {return _iterations;}
|
||||
/** @return **Vis/MinInliers**. */
|
||||
int getMinInliers() const {return _minInliers;}
|
||||
/** @return **Vis/CorNNType** nearest-neighbor strategy. */
|
||||
int getNNType() const {return _nnType;}
|
||||
/** @return Name of the **Vis/CorNNType** nearest-neighbor strategy in use. */
|
||||
std::string getNNTypeName() const {return getNNTypeName(_nnType);}
|
||||
|
||||
/**
|
||||
* @brief Name of a Vis/CorNNType value
|
||||
*/
|
||||
static std::string getNNTypeName(int nnType);
|
||||
/** @return **Vis/CorNNDR** ratio test threshold. */
|
||||
float getNNDR() const {return _nndr;}
|
||||
/** @return **Vis/EstimationType** (0: 3D→3D, 1: PnP, 2: epipolar). */
|
||||
int getEstimationType() const {return _estimationType;}
|
||||
|
||||
/** @return Feature detector used for the “from” signature. */
|
||||
const Feature2D * getDetector() const {return _detectorFrom;}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -50,175 +50,24 @@ class Memory;
|
||||
class BayesFilter;
|
||||
class Signature;
|
||||
class Optimizer;
|
||||
class PythonInterface;
|
||||
|
||||
/**
|
||||
* @class Rtabmap
|
||||
* @brief Top-level RTAB-Map SLAM pipeline (mapping, localization and loop closure).
|
||||
*
|
||||
* Rtabmap orchestrates the full SLAM iteration. Each new sensor observation passed to
|
||||
* @ref process() goes through the steps described below.
|
||||
*
|
||||
*
|
||||
* @par 1. Memory update
|
||||
*
|
||||
* Done via @ref Memory::update(): a new @ref Signature is added to STM, the oldest
|
||||
* STM entry is promoted to WM if STM is full, and rehearsal compares the new
|
||||
* signature to the previous STM signature.
|
||||
*
|
||||
* When the robot barely moved since the previous frame (odometry displacement below
|
||||
* @ref Parameters::kRGBDLinearUpdate() and @ref Parameters::kRGBDAngularUpdate()), the
|
||||
* iteration is flagged as a "small displacement":
|
||||
* - If a loop closure or localization was already accepted on a recent iteration,
|
||||
* appearance-based global loop-closure detection and proximity detection by space
|
||||
* are both skipped to avoid wasting work while the robot is stationary at an
|
||||
* already-known location (only retrieval runs).
|
||||
* - Otherwise (no recent loop closure / localization), both still run normally so
|
||||
* a first-time loop closure can still be detected from a standstill.
|
||||
*
|
||||
* In either case, at the end of the iteration, if no loop closure, proximity
|
||||
* detection or landmark observation latched onto the new node, it is deleted from
|
||||
* @ref Memory so the map does not grow while the robot is idle.
|
||||
*
|
||||
* Rehearsal still runs first, so visually similar consecutive idle frames may also be
|
||||
* merged into the previous STM signature (its weight is incremented and the new
|
||||
* signature is discarded) when the similarity exceeds
|
||||
* @ref Parameters::kMemRehearsalSimilarity().
|
||||
*
|
||||
*
|
||||
* @par 2. Loop-closure hypothesis
|
||||
*
|
||||
* Scored via @ref Memory::computeLikelihood() and the recursive @ref BayesFilter
|
||||
* (prior + observation update).
|
||||
*
|
||||
*
|
||||
* @par 3. Hypothesis selection
|
||||
*
|
||||
* The highest posterior is compared against the loop-closure threshold
|
||||
* (@ref Parameters::kRtabmapLoopThr()); if accepted, the loop-closure link is added
|
||||
* and the pose graph is re-optimized by @ref Optimizer.
|
||||
*
|
||||
* In **RGB-D mode**, two extra checks must pass before the link is committed:
|
||||
* - a valid geometric transform must be computed between the two candidate nodes by
|
||||
* the registration pipeline (visual + optional ICP, see
|
||||
* @ref Memory::computeTransform());
|
||||
* - the resulting transform must not be rejected by the graph-optimization
|
||||
* consistency check (see @ref Parameters::kRGBDOptimizeMaxError()).
|
||||
*
|
||||
* The optimization used by the consistency check depends on the operating mode:
|
||||
* - In **mapping mode**, the local map is re-optimized
|
||||
* around the current signature including the new link, then
|
||||
* @ref graph::computeMaxGraphErrors() measures the worst per-link residual / its
|
||||
* standard deviation. If the ratio exceeds @ref Parameters::kRGBDOptimizeMaxError(),
|
||||
* the loop closure(s) added this iteration are removed from @ref Memory.
|
||||
* - In **localization mode**, optimization is run on a sub-graph composed of the
|
||||
* odometry cache (@ref Parameters::kRGBDMaxOdomCacheSize()), the newly added
|
||||
* localization link and pose priors fixing the map nodes (with the variance set
|
||||
* from @ref Parameters::kRGBDLocalizationPriorError()). The same error-ratio check is
|
||||
* applied; on failure the localization is rejected for this iteration but the
|
||||
* persisted map and its links are left untouched.
|
||||
*
|
||||
* In both modes, if the same link is rejected twice in a row, a graph repair may
|
||||
* also be attempted (within @ref Parameters::kRGBDOptimizeMaxErrorRepairRadius()) to
|
||||
* drop the offending link instead of the new candidate.
|
||||
*
|
||||
* If either RGB-D check fails, the candidate is discarded and no link is added.
|
||||
*
|
||||
*
|
||||
* @par 4. Retrieval
|
||||
*
|
||||
* Once a loop-closure hypothesis is selected, neighbors of the matched node are
|
||||
* brought back from LTM into WM via @ref Memory::reactivateSignatures(), so the next
|
||||
* iteration can compare against them too. Up to @ref Parameters::kRtabmapMaxRetrieved()
|
||||
* nodes are pulled per iteration; nodes around the current path or local pose may
|
||||
* also be retrieved (capped by @ref Parameters::kRGBDMaxLocalRetrieved()).
|
||||
*
|
||||
* Retrieval (and the related node immunization) is only active when memory management
|
||||
* is enabled, i.e. when @ref Parameters::kRtabmapTimeThr() or
|
||||
* @ref Parameters::kRtabmapMemoryThr() is non-zero.
|
||||
*
|
||||
*
|
||||
* @par 5. Proximity detection (RGB-D mode)
|
||||
*
|
||||
* Visual and scan-based local matches to nearby nodes, used in addition to the
|
||||
* appearance-based loop closure.
|
||||
*
|
||||
* Candidate proximity links go through the same two RGB-D gates as loop closures
|
||||
* above: a valid geometric transform must be computed by the registration pipeline,
|
||||
* and the transform must not be rejected by the graph-optimization consistency check.
|
||||
*
|
||||
*
|
||||
* @par 6. Transfer (WM to LTM)
|
||||
*
|
||||
* At the end of the iteration, if the iteration exceeded the configured time budget
|
||||
* (@ref Parameters::kRtabmapTimeThr()) or WM exceeded its size budget
|
||||
* (@ref Parameters::kRtabmapMemoryThr()), @ref Memory::forget() moves the oldest
|
||||
* low-frequency signatures from WM to LTM (immunized nodes -- retrieved neighbors,
|
||||
* the last localization node, etc. -- are kept in WM).
|
||||
*
|
||||
* Transfer is skipped when both thresholds are 0 (memory management disabled).
|
||||
*
|
||||
*
|
||||
* @par 7. Map / localization output
|
||||
*
|
||||
* Optimized poses, current map correction and statistics are made available to
|
||||
* callers via the getters below.
|
||||
*
|
||||
*
|
||||
* @par Operating modes
|
||||
*
|
||||
* Selected by @ref Parameters::kMemIncrementalMemory() (see @ref Memory::isIncremental()):
|
||||
* - **Mapping**: STM and WM grow; loop closures update the optimized graph.
|
||||
* - **Localization**: STM/WM are frozen; the current node is matched against the
|
||||
* persisted map and only @ref getLastLocalizationPose() is updated.
|
||||
*
|
||||
*
|
||||
* @par Path planning
|
||||
*
|
||||
* Rtabmap also exposes basic graph-based path planning in RGB-D mode
|
||||
* (@ref computePath(), @ref getPath(), @ref getPathStatus()), used by the GUI to
|
||||
* navigate between mapped locations.
|
||||
*
|
||||
* When memory management is enabled (see step 4 Retrieval and step 6 Transfer), the
|
||||
* retrieval step also pulls nodes along the currently planned path back from LTM into
|
||||
* WM (capped by @ref Parameters::kRGBDMaxLocalRetrieved()) so the robot is able to
|
||||
* re-localize against upcoming waypoints as it follows the path, even when those
|
||||
* nodes had been transferred out of WM earlier.
|
||||
*
|
||||
*
|
||||
* @see Memory
|
||||
* @see BayesFilter
|
||||
* @see Optimizer
|
||||
* @see Parameters
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Rtabmap
|
||||
{
|
||||
public:
|
||||
/** @brief Loop-closure verification strategy. */
|
||||
enum VhStrategy {
|
||||
kVhNone, ///< No verification: the highest hypothesis above threshold is accepted.
|
||||
kVhEpipolar, ///< Epipolar geometry verification (mostly historical, RGB-only mode).
|
||||
kVhUndef ///< Sentinel -- undefined.
|
||||
};
|
||||
enum VhStrategy {kVhNone, kVhEpipolar, kVhUndef};
|
||||
|
||||
public:
|
||||
Rtabmap();
|
||||
virtual ~Rtabmap();
|
||||
|
||||
/**
|
||||
* @brief Main RTAB-Map iteration: ingests one sensor frame and updates the map.
|
||||
*
|
||||
* Adds @p data to @ref Memory, runs the Bayes filter on the current likelihood,
|
||||
* selects a loop-closure hypothesis if any, performs proximity detection,
|
||||
* re-optimizes the graph as needed, and refreshes @ref getStatistics() and
|
||||
* @ref getLastLocalizationPose().
|
||||
*
|
||||
* @param data Sensor data for this frame (images, scan, user data, ...).
|
||||
* @param odomPose Odometry pose; must be non-null in RGB-D SLAM mode.
|
||||
* Pass a null @ref Transform to fall back to appearance-only mode.
|
||||
* @param odomCovariance 6x6 odometry covariance (default: identity).
|
||||
* @param odomVelocity Optional 6-vector (vx, vy, vz, vroll, vpitch, vyaw).
|
||||
* @param externalStats Extra named statistics to record in the database for this iteration.
|
||||
* @return True if @p data was added to the map (i.e. the memory update succeeded).
|
||||
* @brief Main loop of rtabmap.
|
||||
* @param data Sensor data to process.
|
||||
* @param odomPose Odometry pose, should be non-null for RGB-D SLAM mode.
|
||||
* @param covariance Odometry covariance.
|
||||
* @param externalStats External statistics to be saved in the database for convenience
|
||||
* @return true if data has been added to map.
|
||||
*/
|
||||
bool process(
|
||||
const SensorData & data,
|
||||
@@ -226,13 +75,7 @@ public:
|
||||
const cv::Mat & odomCovariance = cv::Mat::eye(6,6,CV_64FC1),
|
||||
const std::vector<float> & odomVelocity = std::vector<float>(),
|
||||
const std::map<std::string, float> & externalStats = std::map<std::string, float>());
|
||||
/**
|
||||
* @brief Convenience overload: builds a diagonal covariance from scalar variances.
|
||||
*
|
||||
* The 6x6 odometry covariance is constructed as
|
||||
* @c diag(odomLinearVariance, odomLinearVariance, odomLinearVariance,
|
||||
* odomAngularVariance, odomAngularVariance, odomAngularVariance).
|
||||
*/
|
||||
// for convenience
|
||||
bool process(
|
||||
const SensorData & data,
|
||||
Transform odomPose,
|
||||
@@ -240,366 +83,121 @@ public:
|
||||
float odomAngularVariance,
|
||||
const std::vector<float> & odomVelocity = std::vector<float>(),
|
||||
const std::map<std::string, float> & externalStats = std::map<std::string, float>());
|
||||
/**
|
||||
* @brief Appearance-only convenience overload (loop-closure detection without odometry).
|
||||
*
|
||||
* Equivalent to processing @p image alone, with no odometry pose. Useful for offline
|
||||
* loop-closure benchmarking on image sequences.
|
||||
*
|
||||
* @param image RGB or grayscale frame.
|
||||
* @param id Optional frame id (0 = auto-generated).
|
||||
* @param externalStats Extra named statistics to record in the database for this iteration.
|
||||
*/
|
||||
// for convenience, loop closure detection only
|
||||
bool process(
|
||||
const cv::Mat & image,
|
||||
int id=0, const std::map<std::string, float> & externalStats = std::map<std::string, float>());
|
||||
|
||||
/**
|
||||
* @brief Initializes Rtabmap with parameters and a database.
|
||||
*
|
||||
* Initialize Rtabmap with parameters and a database
|
||||
* @param parameters Parameters overriding default parameters and database parameters
|
||||
* (see @p loadDatabaseParameters).
|
||||
* @param databasePath Database input/output path. If empty, an in-memory database is
|
||||
* used. If set and the file does not exist, it is created empty;
|
||||
* if it exists, nodes and the visual word vocabulary are loaded
|
||||
* into working memory.
|
||||
* @param loadDatabaseParameters If true and an existing database is opened, the
|
||||
* parameters stored inside the database are loaded and
|
||||
* applied to this Rtabmap instance (then overridden by
|
||||
* @p parameters).
|
||||
* (@see loadDatabaseParameters)
|
||||
* @param databasePath The database input/output path. If not set, an
|
||||
* empty database is used in RAM. If set and the file doesn't exist,
|
||||
* it will be created empty. If the database exists, nodes and
|
||||
* vocabulary will be loaded in working memory.
|
||||
* @param loadDatabaseParameters If an existing database is used (@see databasePath),
|
||||
* the parameters inside are loaded and set to current
|
||||
* Rtabmap instance.
|
||||
*/
|
||||
void init(const ParametersMap & parameters, const std::string & databasePath = "", bool loadDatabaseParameters = false);
|
||||
/**
|
||||
* @brief Initializes Rtabmap from a configuration file and a database.
|
||||
*
|
||||
* @param configFile Configuration file (*.ini) overriding default parameters and
|
||||
* database parameters (see @p loadDatabaseParameters).
|
||||
* @param databasePath Database input/output path; same semantics as the other @ref init().
|
||||
* @param loadDatabaseParameters If true and an existing database is opened, the
|
||||
* parameters stored inside the database are loaded and
|
||||
* applied first, then overridden by values from @p configFile.
|
||||
* Initialize Rtabmap with parameters from a configuration file and a database
|
||||
* @param configFile Configuration file (*.ini) overriding default parameters and database parameters
|
||||
* (@see loadDatabaseParameters)
|
||||
* @param databasePath The database input/output path. If not set, an
|
||||
* empty database is used in RAM. If set and the file doesn't exist,
|
||||
* it will be created empty. If the database exists, nodes and
|
||||
* vocabulary will be loaded in working memory.
|
||||
* @param loadDatabaseParameters If an existing database is used (@see databasePath),
|
||||
* the parameters inside are loaded and set to current
|
||||
* Rtabmap instance.
|
||||
*/
|
||||
void init(const std::string & configFile = "", const std::string & databasePath = "", bool loadDatabaseParameters = false);
|
||||
|
||||
/**
|
||||
* @brief Closes Rtabmap and releases the underlying @ref Memory.
|
||||
*
|
||||
* @param databaseSaved If true, the in-memory state is flushed to the database;
|
||||
* if false, in-memory changes are discarded.
|
||||
* @param ouputDatabasePath If non-empty, the database is copied to this path on
|
||||
* close. If a database on disk was initially created/loaded on
|
||||
* a different path, it will be updated with the latest changes
|
||||
* and renamed to the output path.
|
||||
* Close rtabmap. This will delete rtabmap object if set.
|
||||
* @param databaseSaved true=database saved, false=database discarded.
|
||||
* @param databasePath output database file name, ignored if
|
||||
* Db/Sqlite3InMemory=false (opened database is
|
||||
* then overwritten).
|
||||
*/
|
||||
void close(bool databaseSaved = true, const std::string & ouputDatabasePath = "");
|
||||
|
||||
/** @return Working directory used for dumps, log files and temporary outputs. */
|
||||
const std::string & getWorkingDir() const {return _wDir;}
|
||||
/** @return True if RGB-D SLAM mode is enabled (@ref Parameters::kRGBDEnabled()). */
|
||||
bool isRGBDMode() const { return _rgbdSlamMode; }
|
||||
/** @return Id of the loop-closure hypothesis accepted at the last @ref process() iteration, or 0 if none. */
|
||||
int getLoopClosureId() const {return _loopClosureHypothesis.first;}
|
||||
/** @return Posterior probability of the accepted loop-closure hypothesis, or 0 if none. */
|
||||
float getLoopClosureValue() const {return _loopClosureHypothesis.second;}
|
||||
/** @return Id of the highest-posterior hypothesis at the last iteration (whether or not it was accepted). */
|
||||
int getHighestHypothesisId() const {return _highestHypothesis.first;}
|
||||
/** @return Posterior of the highest-posterior hypothesis at the last iteration. */
|
||||
float getHighestHypothesisValue() const {return _highestHypothesis.second;}
|
||||
/** @return Id of the last non-intermediate signature added to the map (0 if none). */
|
||||
int getLastLocationId() const;
|
||||
/** @return Working memory ids ordered as in @ref Memory::getWorkingMem(). */
|
||||
std::list<int> getWM() const;
|
||||
/** @return Short-term memory ids. */
|
||||
std::set<int> getSTM() const;
|
||||
/** @return Working memory size (number of WM signatures). */
|
||||
int getWMSize() const;
|
||||
/** @return Short-term memory size (number of STM signatures). */
|
||||
int getSTMSize() const;
|
||||
/** @return Per-signature weights (rehearsal counts) for WM and STM. */
|
||||
std::list<int> getWM() const; // working memory
|
||||
std::set<int> getSTM() const; // short-term memory
|
||||
int getWMSize() const; // working memory size
|
||||
int getSTMSize() const; // short-term memory size
|
||||
std::map<int, int> getWeights() const;
|
||||
/** @return Total number of signatures across WM, STM and LTM. */
|
||||
int getTotalMemSize() const;
|
||||
/** @return Wall-clock duration of the last @ref process() call, in seconds. */
|
||||
double getLastProcessTime() const {return _lastProcessTime;};
|
||||
/** @return True if @p locationId is currently in short-term memory. */
|
||||
bool isInSTM(int locationId) const;
|
||||
/** @return True if signature ids are auto-generated (vs. taken from @ref SensorData::id()). */
|
||||
bool isIDsGenerated() const;
|
||||
/** @return Statistics produced by the last @ref process() iteration. */
|
||||
const Statistics & getStatistics() const;
|
||||
/** @return Optimized poses of the current local map (last graph optimization result). */
|
||||
const std::map<int, Transform> & getLocalOptimizedPoses() const {return _optimizedPoses;}
|
||||
/** @return Constraints (links) of the current local map. */
|
||||
const std::multimap<int, Link> & getLocalConstraints() const {return _constraints;}
|
||||
/**
|
||||
* @return Optimized pose of @p locationId in the current local map (identity if not present).
|
||||
*/
|
||||
Transform getPose(int locationId) const;
|
||||
/**
|
||||
* @return Transform mapping odometry frame to the optimized map frame.
|
||||
*
|
||||
* This is the correction applied to incoming odometry poses so they align with the
|
||||
* latest graph optimization output. Updated whenever a loop closure or proximity
|
||||
* detection re-optimizes the graph.
|
||||
*
|
||||
* In ROS terms, this corresponds to the standard @c /map -> @c /odom TF transform
|
||||
* published by SLAM systems: composing it with the live odometry pose
|
||||
* (@c /odom -> @c /base_link) yields the robot pose in the map frame.
|
||||
*/
|
||||
Transform getMapCorrection() const {return _mapCorrection;}
|
||||
/** @return Owned @ref Memory (may be null before @ref init()). */
|
||||
const Memory * getMemory() const {return _memory;}
|
||||
/** @return Radius (meters) under which the current path goal is considered reached. */
|
||||
// Record changes made to the working database from now until it is closed, then write a
|
||||
// compact delta to outputUrl (empty disables). outputUrl MUST use the ".dbu" (db update)
|
||||
// extension. Call after init(), so the delta reflects only what is added/modified afterwards.
|
||||
// Returns true if recording started (requires the SQLite session extension and a database
|
||||
// version >= 0.24). See DBDriver.
|
||||
// NOTE: recorded changes are held in RAM until the database is closed, so only enable this
|
||||
// when the expected set of changes is small (e.g. appending a few sessions).
|
||||
bool trackDatabaseChanges(const std::string & outputUrl);
|
||||
float getGoalReachedRadius() const {return _goalReachedRadius;}
|
||||
/** @return Local radius (meters) used by proximity detection and path planning queries. */
|
||||
float getLocalRadius() const {return _localRadius;}
|
||||
/**
|
||||
* @return Last localized pose in the map frame.
|
||||
*
|
||||
* In **localization mode**, this is the corrected odometry pose of the last
|
||||
* processed frame. In **mapping mode**, this is the last pose returned by
|
||||
* @ref getLocalOptimizedPoses().
|
||||
*/
|
||||
const Transform & getLastLocalizationPose() const {return _lastLocalizationPose;}
|
||||
|
||||
/**
|
||||
* @return Maximum allowed processing time per @ref process() call, in milliseconds.
|
||||
* @see Parameters::kRtabmapTimeThr()
|
||||
*/
|
||||
float getTimeThreshold() const {return _maxTimeAllowed;}
|
||||
/**
|
||||
* @brief Sets the per-iteration time budget (ms).
|
||||
*
|
||||
* Drives how aggressively WM is transferred to LTM to keep iterations under the
|
||||
* threshold. 0 disables the time bound.
|
||||
*
|
||||
* @note This setting and @ref setMemoryThreshold() are the only two switches that
|
||||
* enable RTAB-Map's memory management (WM-to-LTM transfer, retrieval and node
|
||||
* immunization). When both are 0, memory management is disabled and all
|
||||
* signatures stay in working memory.
|
||||
*
|
||||
* @see Parameters::kRtabmapTimeThr()
|
||||
*/
|
||||
void setTimeThreshold(float maxTimeAllowed);
|
||||
/**
|
||||
* @return Maximum allowed WM size (number of signatures).
|
||||
* @see Parameters::kRtabmapMemoryThr()
|
||||
*/
|
||||
int getMemoryThreshold() const {return _maxMemoryAllowed;}
|
||||
/**
|
||||
* @brief Sets the maximum number of signatures kept in WM (0 = unbounded).
|
||||
*
|
||||
* @note This setting and @ref setTimeThreshold() are the only two switches that
|
||||
* enable RTAB-Map's memory management (WM-to-LTM transfer, retrieval and node
|
||||
* immunization). When both are 0, memory management is disabled and all
|
||||
* signatures stay in working memory.
|
||||
*
|
||||
* @see Parameters::kRtabmapMemoryThr()
|
||||
*/
|
||||
void setMemoryThreshold(int maxMemoryAllowed);
|
||||
float getTimeThreshold() const {return _maxTimeAllowed;} // in ms
|
||||
void setTimeThreshold(float maxTimeAllowed); // in ms
|
||||
int getMemoryThreshold() const {return _maxMemoryAllowed;} // in nodes
|
||||
void setMemoryThreshold(int maxMemoryAllowed); // in nodes
|
||||
|
||||
/**
|
||||
* @brief Sets the localization prior pose used to seed the next @ref process() call
|
||||
* (localization mode only).
|
||||
*
|
||||
* Tells RTAB-Map where the robot is currently located in the map frame, so that the
|
||||
* very next call to @ref process() can align incoming odometry with the persisted
|
||||
* map without waiting for a loop closure. Typical use cases: restoring localization
|
||||
* after a session restart, applying an external pose estimate (e.g. from a GPS or
|
||||
* a known starting point), or recovering from "kidnapped robot" situations.
|
||||
*
|
||||
* This call only stages state; it does **not** itself produce a non-identity
|
||||
* @ref getMapCorrection(). The alignment between the odometry frame and the map
|
||||
* frame is performed on the next @ref process() call, which consumes
|
||||
* @p initialPose together with the incoming odometry pose. Two branches are taken
|
||||
* depending on @ref Parameters::kRGBDOptimizeFromGraphEnd():
|
||||
* - **false (default)**: @ref getMapCorrection() is set so that the live odometry
|
||||
* pose is shifted to land on @p initialPose in the map frame (the optimized map
|
||||
* is left untouched).
|
||||
* - **true**: every optimized node pose is rigidly transformed so that the map
|
||||
* itself moves to align with @p initialPose (the map correction stays close to
|
||||
* identity).
|
||||
*
|
||||
* The transform applied is restricted by SLAM dimensionality: 3-DoF (x, y, yaw)
|
||||
* for 2D SLAM, 4-DoF (x, y, z, yaw) when gravity is available
|
||||
* (IMU orientation or @ref Memory::isOdomGravityUsed()) and
|
||||
* @ref Parameters::kOptimizerGravitySigma() is non-zero, full 6-DoF otherwise.
|
||||
*
|
||||
* Side effects on the staged state:
|
||||
* - @ref getLastLocalizationPose() is replaced by @p initialPose; the localization
|
||||
* covariance, the last localization node id and the odometry cache used for
|
||||
* loop-closure rejection are all cleared.
|
||||
* - @ref getMapCorrection() is reset to identity and any backup is cleared.
|
||||
* - If the current map has not been optimized yet (no entries in
|
||||
* @ref getLocalOptimizedPoses()) and a last working signature exists, the map
|
||||
* is optimized around that signature so the next @ref process() has something
|
||||
* to localize against.
|
||||
*
|
||||
* After the next @ref process() consumes the prior, the nearest optimized node to
|
||||
* @p initialPose is recorded as the last localization node.
|
||||
*
|
||||
* @param initialPose Robot pose in the map frame.
|
||||
*
|
||||
* @note No-op (with warning) in mapping mode.
|
||||
*
|
||||
* @see Parameters::kMemIncrementalMemory()
|
||||
* @see Parameters::kRGBDOptimizeFromGraphEnd()
|
||||
*/
|
||||
void setInitialPose(const Transform & initialPose);
|
||||
/**
|
||||
* @brief Starts a new map session (next @ref process() will create a fresh map id).
|
||||
*
|
||||
* In **mapping mode**, this increments the map id, clears the local optimized graph
|
||||
* and resets the Bayes filter.
|
||||
* In **localization mode**, it resets the map correction, the localization node and
|
||||
* the odometry cache; if @ref Parameters::kRGBDStartAtOrigin() is enabled, the
|
||||
* last localization pose is reset to identity.
|
||||
*
|
||||
* @return The new map id (mapping mode), or -1 (localization mode).
|
||||
*/
|
||||
int triggerNewMap();
|
||||
/**
|
||||
* @brief Assigns or clears a label on signature @p id.
|
||||
* @return True if the label was applied.
|
||||
*/
|
||||
bool labelLocation(int id, const std::string & label);
|
||||
/**
|
||||
* @brief Attaches user data to signature @p id, compressing it on the fly if needed.
|
||||
*
|
||||
* The format is detected automatically: a single-row @c CV_8UC1 matrix is treated
|
||||
* as already-compressed data and stored as-is; anything else is considered raw and
|
||||
* compressed before being stored.
|
||||
*
|
||||
* @note If you pass one-dimensional unsigned 8-bit raw data, transpose it so it has
|
||||
* multiple rows (not multiple columns), otherwise it will be misdetected as
|
||||
* already compressed.
|
||||
*
|
||||
* @param id Target signature id (must be in WM/STM or LTM).
|
||||
* @param data Raw or pre-compressed user data.
|
||||
* @return True if the data was attached.
|
||||
* Set user data. Detect automatically if raw or compressed. If raw, the data is
|
||||
* compressed too. A matrix of type CV_8UC1 with 1 row is considered as compressed.
|
||||
* If you have one dimension unsigned 8 bits raw data, make sure to transpose it
|
||||
* (to have multiple rows instead of multiple columns) in order to be detected as
|
||||
* not compressed.
|
||||
*/
|
||||
bool setUserData(int id, const cv::Mat & data);
|
||||
/**
|
||||
* @brief Writes a Graphviz DOT file of the pose graph.
|
||||
* @param path Output file path.
|
||||
* @param id If non-zero, root the graph at @p id; otherwise use the last signature.
|
||||
* @param margin Maximum graph depth around @p id to include.
|
||||
*/
|
||||
void generateDOTGraph(const std::string & path, int id=0, int margin=5);
|
||||
/**
|
||||
* @brief Exports the current pose graph to a text file.
|
||||
*
|
||||
* Forwards to @ref graph::exportPoses() after collecting either the optimized or
|
||||
* the raw odometry poses (and the matching constraints when needed).
|
||||
*
|
||||
* @param path Output file path.
|
||||
* @param optimized If true, export optimized poses; otherwise raw odometry poses.
|
||||
* @param global If true, include nodes from LTM as well; otherwise only WM/STM.
|
||||
* @param format Output format code; see @ref graph::exportPoses() for the full
|
||||
* list of supported values (raw, RGBD-SLAM/TUM, KITTI, TORO, g2o, ...).
|
||||
*
|
||||
* @see graph::exportPoses()
|
||||
*/
|
||||
void exportPoses(
|
||||
const std::string & path,
|
||||
bool optimized,
|
||||
bool global,
|
||||
int format
|
||||
int format // 0=raw, 1=rgbd-slam format, 2=KITTI format, 3=TORO, 4=g2o
|
||||
);
|
||||
/**
|
||||
* @brief Clears all in-memory state and resets the database.
|
||||
*
|
||||
* In incremental mode, also clears the persisted map. In read-only memory mode,
|
||||
* resets the in-memory state but leaves the database untouched.
|
||||
*/
|
||||
void resetMemory();
|
||||
/** @brief Dumps the Bayes-filter prediction matrix to a file in the working directory. */
|
||||
void dumpPrediction() const;
|
||||
/** @brief Dumps the @ref Memory state (signatures, words, dictionary) to the working directory. */
|
||||
void dumpData() const;
|
||||
/**
|
||||
* @brief Re-parses parameters and propagates them to owned sub-objects
|
||||
* (@ref Memory, @ref BayesFilter, @ref Optimizer, ...).
|
||||
*/
|
||||
void parseParameters(const ParametersMap & parameters);
|
||||
/** @return Current effective parameter map. */
|
||||
const ParametersMap & getParameters() const {return _parameters;}
|
||||
/**
|
||||
* @brief Sets the working directory used for dumps, logs and temporary files.
|
||||
*
|
||||
* Can also be configured through @ref Parameters::kRtabmapWorkingDirectory() in the
|
||||
* parameter map passed to @ref init() or @ref parseParameters().
|
||||
*
|
||||
* @see Parameters::kRtabmapWorkingDirectory()
|
||||
*/
|
||||
void setWorkingDirectory(std::string path);
|
||||
/**
|
||||
* @brief Removes the loop-closure link added at the last @ref process() iteration.
|
||||
*
|
||||
* Looks at the last non-intermediate signature in STM and erases any
|
||||
* @ref Link::kGlobalClosure, @ref Link::kLocalSpaceClosure, @ref Link::kLocalTimeClosure
|
||||
* or @ref Link::kUserClosure attached to it. The current optimized map is updated
|
||||
* accordingly.
|
||||
*/
|
||||
void rejectLastLoopClosure();
|
||||
/**
|
||||
* @brief Deletes the most recent (non-intermediate) location from the map.
|
||||
*
|
||||
* Used by tools to undo the very last @ref process() iteration. In mapping mode,
|
||||
* the optimized graph is recomputed without the deleted node.
|
||||
*
|
||||
* @note Locations whose neighbors include intermediate nodes are not supported.
|
||||
*/
|
||||
void deleteLastLocation();
|
||||
/**
|
||||
* @brief Replaces the current optimized poses and constraints with externally
|
||||
* provided ones.
|
||||
*
|
||||
* Useful when graph optimization is performed outside of Rtabmap.
|
||||
*
|
||||
* @warning No consistency check is performed against the current @ref Memory state:
|
||||
* @p poses and @p constraints overwrite the internal containers verbatim.
|
||||
* The caller is responsible for ensuring that every id in @p poses (and
|
||||
* every endpoint of every link in @p constraints) belongs to a signature
|
||||
* currently in STM or WM (see @ref Memory::isInSTM() / @ref Memory::isInWM()).
|
||||
* Passing poses for ids that are no longer loaded will leave dangling
|
||||
* entries that may confuse subsequent @ref process() calls.
|
||||
*/
|
||||
void setOptimizedPoses(const std::map<int, Transform> & poses, const std::multimap<int, Link> & constraints);
|
||||
/**
|
||||
* @brief Returns a copy of signature @p id with optional payloads attached.
|
||||
*
|
||||
* Loads from WM/STM if present, otherwise from LTM. Selectively populates the
|
||||
* returned @ref Signature with images, scan, user data, occupancy grid, visual
|
||||
* words and global descriptors.
|
||||
*/
|
||||
Signature getSignatureCopy(int id, bool images, bool scan, bool userData, bool occupancyGrid, bool withWords, bool withGlobalDescriptors) const;
|
||||
/**
|
||||
* @brief Deprecated: use @ref getGraph() instead with @c withImages=true,
|
||||
* @c withScan=true, @c withUserData=true and @c withGrid=true.
|
||||
*/
|
||||
// Use getGraph() instead with withImages=true, withScan=true, withUserData=true and withGrid=true.
|
||||
RTABMAP_DEPRECATED
|
||||
void get3DMap(std::map<int, Signature> & signatures,
|
||||
std::map<int, Transform> & poses,
|
||||
std::multimap<int, Link> & constraints,
|
||||
bool optimized,
|
||||
bool global) const;
|
||||
/**
|
||||
* @brief Extracts a full snapshot of the current pose graph.
|
||||
*
|
||||
* @param poses Output: pose for every selected node.
|
||||
* @param constraints Output: links between selected nodes.
|
||||
* @param optimized If true, return optimized poses; otherwise raw odometry poses.
|
||||
* @param global If true, include nodes from LTM as well; otherwise only WM/STM.
|
||||
* @param signatures Optional output: a copy of each node's @ref Signature (with the
|
||||
* payloads requested by the @p with* flags).
|
||||
* @param withImages Attach compressed RGB/depth images to @p signatures.
|
||||
* @param withScan Attach laser scan blob.
|
||||
* @param withUserData Attach user data blob.
|
||||
* @param withGrid Attach occupancy grid cells.
|
||||
* @param withWords Attach visual words (id, keypoints, 3D points, descriptors).
|
||||
* @param withGlobalDescriptors Attach global descriptors.
|
||||
*/
|
||||
void getGraph(std::map<int, Transform> & poses,
|
||||
std::multimap<int, Link> & constraints,
|
||||
bool optimized,
|
||||
@@ -611,55 +209,8 @@ public:
|
||||
bool withGrid = false,
|
||||
bool withWords = true,
|
||||
bool withGlobalDescriptors = true) const;
|
||||
/**
|
||||
* @brief Returns optimized poses within a metric radius of @p pose.
|
||||
*
|
||||
* @param pose Query pose in the map frame.
|
||||
* @param radius Search radius in meters (0 falls back to @ref Parameters::kRGBDLocalRadius()).
|
||||
* @param k If non-zero, also cap the result to the @p k nearest neighbors.
|
||||
* @param distsSqr Optional output: per-id squared distance to @p pose.
|
||||
* @return Nodes (and possibly landmarks) within the radius, mapped to their pose.
|
||||
* Landmarks have a negative id.
|
||||
*/
|
||||
std::map<int, Transform> getNodesInRadius(const Transform & pose, float radius, int k=0, std::map<int, float> * distsSqr=0);
|
||||
/**
|
||||
* @brief Returns optimized poses within a metric radius of node @p nodeId.
|
||||
*
|
||||
* @param nodeId Query node id. Pass 0 to query around the latest node. A negative
|
||||
* id requests neighbors of the corresponding landmark.
|
||||
* @param radius Search radius in meters (0 falls back to @ref Parameters::kRGBDLocalRadius()).
|
||||
* @param k If non-zero, cap the result to the @p k nearest neighbors.
|
||||
* @param distsSqr Optional output: per-id squared distance to @p nodeId.
|
||||
* @return Nodes (and possibly landmarks) within the radius.
|
||||
*/
|
||||
std::map<int, Transform> getNodesInRadius(int nodeId, float radius, int k=0, std::map<int, float> * distsSqr=0);
|
||||
/**
|
||||
* @brief Post-processing: searches for additional loop closures over the existing graph.
|
||||
*
|
||||
* Clusters nearby optimized poses and runs registration between candidates that are
|
||||
* not yet linked. New links are added to @ref Memory and the graph is re-optimized.
|
||||
*
|
||||
* @note The registration approach used here is the one configured in @ref Memory via
|
||||
* @ref Parameters::kRegStrategy() (0=Vis, 1=Icp, 2=VisIcp), so the quality and
|
||||
* sensor requirements of this pass mirror the live loop-closure pipeline.
|
||||
*
|
||||
* @note Candidate cluster pairs whose ids differ by less than
|
||||
* @ref Parameters::kMemSTMSize(), or that are already reachable from each
|
||||
* other within that many graph hops, are filtered out. This prevents trivial
|
||||
* "loop closures" between temporally or topologically adjacent nodes.
|
||||
*
|
||||
* @param clusterRadiusMax Maximum metric distance (m) between two candidate nodes.
|
||||
* @param clusterAngle Maximum angular distance (rad) between two candidate nodes.
|
||||
* @param iterations Number of refinement passes.
|
||||
* @param intraSession Include loop closures within the same map session.
|
||||
* @param interSession Include loop closures between different map sessions.
|
||||
* @param state Optional progress sink; cancellation requests are honored.
|
||||
* @param clusterRadiusMin Minimum metric distance (m); pairs closer than this are
|
||||
* considered already linked through neighbor links.
|
||||
* @param toFromMapId If >=0, restrict candidate pairs to nodes belonging to this map id.
|
||||
* @return Number of loop closures added, or -1 on error
|
||||
* (e.g. not in RGB-D mode, no optimizer iterations).
|
||||
*/
|
||||
std::map<int, Transform> getNodesInRadius(const Transform & pose, float radius, int k=0, std::map<int, float> * distsSqr=0); // If radius=0 and k=0, RGBD/LocalRadius is used. Can return landmarks.
|
||||
std::map<int, Transform> getNodesInRadius(int nodeId, float radius, int k=0, std::map<int, float> * distsSqr=0); // If nodeId==0, return poses around latest node. If radius=0 and k=0, RGBD/LocalRadius is used. Can return landmarks and use landmark id (negative) as request.
|
||||
int detectMoreLoopClosures(
|
||||
float clusterRadiusMax = 0.5f,
|
||||
float clusterAngle = M_PI/6.0f,
|
||||
@@ -669,31 +220,11 @@ public:
|
||||
const ProgressState * state = 0,
|
||||
float clusterRadiusMin = 0.0f,
|
||||
int toFromMapId = -1);
|
||||
/**
|
||||
* @brief Runs a global bundle adjustment over the optimized graph.
|
||||
*
|
||||
* @param optimizerType Backend optimizer (e.g. 1=g2o); availability depends on
|
||||
* what RTAB-Map was built with.
|
||||
* @param rematchFeatures If true, re-match visual features between connected nodes
|
||||
* before BA (otherwise reuse existing word-id correspondences).
|
||||
* @param iterations Solver iterations (0 falls back to @ref Parameters::kOptimizerIterations()).
|
||||
* @param pixelVariance Pixel reprojection variance used by the cost (0 falls back
|
||||
* to @ref Parameters::kOptimizerPixelVariance()).
|
||||
* @return True if BA was run and improved poses were stored.
|
||||
*/
|
||||
bool globalBundleAdjustment(
|
||||
int optimizerType = 1 /*g2o*/,
|
||||
bool rematchFeatures = true,
|
||||
int iterations = 0,
|
||||
float pixelVariance = 0.0f);
|
||||
/**
|
||||
* @brief Filters spurious obstacles from every node's local grid using a reference 2D map.
|
||||
*
|
||||
* Thin wrapper around @ref Memory::cleanupLocalGrids(); see that method for the
|
||||
* exact filtering rule and the meaning of @p cropRadius and @p filterScans.
|
||||
*
|
||||
* @return Number of (node, grid or scan) modifications, or -1 on error.
|
||||
*/
|
||||
int cleanupLocalGrids(
|
||||
const std::map<int, Transform> & mapPoses,
|
||||
const cv::Mat & map,
|
||||
@@ -702,212 +233,28 @@ public:
|
||||
float cellSize,
|
||||
int cropRadius = 1,
|
||||
bool filterScans = false);
|
||||
/**
|
||||
* @brief Re-runs registration on every link of the current graph and updates the
|
||||
* ones that converge.
|
||||
*
|
||||
* Useful after parameter changes to refresh stored transforms.
|
||||
*
|
||||
* @note The registration approach is the one configured in @ref Memory via
|
||||
* @ref Parameters::kRegStrategy() (0=Vis, 1=Icp, 2=VisIcp). For each link,
|
||||
* the link's existing relative transform (the constraint produced by the
|
||||
* current optimized local graph) is passed as the initial guess to
|
||||
* @ref Memory::computeTransform(), so links already close to convergence
|
||||
* are refined locally rather than re-estimated from scratch.
|
||||
*
|
||||
* @return Number of links refined, or -1 if not in RGB-D mode.
|
||||
*/
|
||||
int refineLinks();
|
||||
/**
|
||||
* @brief Adds an external link to the map.
|
||||
*
|
||||
* The link's "from" and "to" endpoints must exist in memory (incremental mode) or
|
||||
* in the optimized poses (localization mode). RGB-D mode only.
|
||||
*
|
||||
* @return True if the link was added.
|
||||
*/
|
||||
bool addLink(const Link & link);
|
||||
/**
|
||||
* @brief Converts an odometry covariance into an information matrix, clipping by
|
||||
* @ref Memory::getOdomMaxInf() when @ref Parameters::kRGBDLoopCovLimited()
|
||||
* is enabled.
|
||||
*/
|
||||
cv::Mat getInformation(const cv::Mat & covariance) const;
|
||||
/**
|
||||
* @brief Marks node ids whose data should be re-emitted on the next @ref process().
|
||||
*
|
||||
* The requested signatures are attached to the @ref Statistics object produced by
|
||||
* the next @ref process() call (via the same mechanism as the regular "last signature
|
||||
* data"), so consumers reading @ref getStatistics() pick them up alongside the
|
||||
* normal output. Up to @ref Parameters::kRtabmapMaxRepublished() ids are emitted
|
||||
* per iteration; any leftover ids stay queued for subsequent iterations until they
|
||||
* are republished or fall out of the current graph.
|
||||
*
|
||||
* Pass an empty vector to clear the request set. Requires
|
||||
* @ref Parameters::kRtabmapMaxRepublished() > 0 and
|
||||
* @ref Parameters::kRtabmapPublishLastSignature() = true.
|
||||
*/
|
||||
void addNodesToRepublish(const std::vector<int> & ids);
|
||||
/**
|
||||
* @brief Loads the visual word dictionary as ids only, without descriptors.
|
||||
*
|
||||
* With a dummy dictionary, @ref init() populates @ref VWDictionary with placeholder
|
||||
* @ref VisualWord objects carrying an empty descriptor, and the dictionary update
|
||||
* (FLANN index construction) is skipped. This makes opening a large database much
|
||||
* faster and lighter in RAM when the word descriptors are not needed, e.g. to inspect
|
||||
* or post-process an existing map rather than to localize in it.
|
||||
*
|
||||
* The dummy dictionary is silently disabled if the database has no words, or if the
|
||||
* dictionary has to be rebuilt from the nodes because it was not saved properly.
|
||||
*
|
||||
* @param enabled True to load ids only, false to load the full dictionary (default).
|
||||
*
|
||||
* @note Must be called before @ref init(); an error is logged and the call ignored
|
||||
* once the memory exists.
|
||||
* @warning Incompatible with mapping: adding new nodes asserts in
|
||||
* @c Memory::createSignature(). Loop closure detection also cannot match
|
||||
* new observations against a descriptor-less dictionary.
|
||||
*
|
||||
* @see Memory::setDummyDictionary()
|
||||
*/
|
||||
void setDummyDictionary(bool enabled = true);
|
||||
|
||||
/** @return Current path status: -1 = failed, 0 = idle / executing, 1 = success. */
|
||||
int getPathStatus() const {return _pathStatus;}
|
||||
/**
|
||||
* @brief Clears the current path and sets its terminal status.
|
||||
* @param status -1 = failed, 0 = idle / executing, 1 = success.
|
||||
*/
|
||||
void clearPath(int status);
|
||||
/**
|
||||
* @brief Plans a path from the current location to node @p targetNode.
|
||||
*
|
||||
* RGB-D mode only (requires @ref Parameters::kRGBDEnabled() = true).
|
||||
*
|
||||
* @param targetNode Destination node id (positive) or landmark id (negative).
|
||||
* @param global If true, also search nodes in LTM; otherwise only the current
|
||||
* optimized map.
|
||||
* @return True if a path was computed; the result is available via @ref getPath().
|
||||
*
|
||||
* @see Parameters::kRGBDEnabled()
|
||||
*/
|
||||
int getPathStatus() const {return _pathStatus;} // -1=failed 0=idle/executing 1=success
|
||||
void clearPath(int status); // -1=failed 0=idle/executing 1=success
|
||||
bool computePath(int targetNode, bool global);
|
||||
/**
|
||||
* @brief Plans a path in the current optimized map toward a metric goal pose.
|
||||
*
|
||||
* @param targetPose Goal pose in the map frame.
|
||||
* @param tolerance Goal-acceptance tolerance (meters). A negative value falls back
|
||||
* to @ref Parameters::kRGBDLocalRadius(); 0 means infinite tolerance.
|
||||
* @return True if a path was computed.
|
||||
*/
|
||||
bool computePath(const Transform & targetPose, float tolerance = -1.0f);
|
||||
/** @return The currently planned path as a sequence of (node id, pose) waypoints. */
|
||||
bool computePath(const Transform & targetPose, float tolerance = -1.0f); // only in current optimized map, tolerance (m) < 0 means RGBD/LocalRadius, 0 means infinite
|
||||
const std::vector<std::pair<int, Transform> > & getPath() const {return _path;}
|
||||
/** @return Upcoming waypoints (from the current path index onward). */
|
||||
std::vector<std::pair<int, Transform> > getPathNextPoses() const;
|
||||
/** @return Upcoming node ids (from the current path index onward). */
|
||||
std::vector<int> getPathNextNodes() const;
|
||||
/** @return Id of the current intermediate path goal (the node currently being chased). */
|
||||
int getPathCurrentGoalId() const;
|
||||
/** @return Index of the current waypoint in @ref getPath(). */
|
||||
unsigned int getPathCurrentIndex() const {return _pathCurrentIndex;}
|
||||
/** @return Index of the current intermediate goal in @ref getPath(). */
|
||||
unsigned int getPathCurrentGoalIndex() const {return _pathGoalIndex;}
|
||||
/** @return Transform from the final waypoint pose to the requested goal pose. */
|
||||
const Transform & getPathTransformToGoal() const {return _pathTransformToGoal;}
|
||||
|
||||
/**
|
||||
* @brief Returns optimized poses of WM nodes located in front of @p fromId.
|
||||
*
|
||||
* Candidates are first gathered around @p fromId, then STM nodes are excluded, the
|
||||
* survivors are cropped to a forward-facing box of width @p radius (1 m behind,
|
||||
* @p radius ahead, +/-@p radius laterally), and a KdTree radius search keeps the
|
||||
* @p maxNearestNeighbors closest poses in that box.
|
||||
*
|
||||
* @note Mapping vs. localization mode differs only in how the initial candidate
|
||||
* set is built:
|
||||
* - In **mapping mode** (incremental), candidates are produced by a
|
||||
* graph-radius walk from @p fromId: nodes reachable within @p maxDiffID
|
||||
* graph hops AND within @p radius meters in the optimized poses.
|
||||
* - In **localization mode**, the graph-hop restriction is ignored: every
|
||||
* optimized pose within @p radius meters of @p fromId is considered.
|
||||
* The forward-box crop and KdTree radius search that follow are identical
|
||||
* in both modes.
|
||||
*
|
||||
* @param fromId Reference node (must be in @ref Memory and @ref getLocalOptimizedPoses()).
|
||||
* @param maxNearestNeighbors Cap on the number of nodes returned.
|
||||
* @param radius Maximum metric distance from @p fromId (meters).
|
||||
* @param maxDiffID Maximum graph depth from @p fromId in mapping mode (0 = unlimited).
|
||||
* Ignored in localization mode.
|
||||
*/
|
||||
std::map<int, Transform> getForwardWMPoses(int fromId, int maxNearestNeighbors, float radius, int maxDiffID) const;
|
||||
/**
|
||||
* @brief Segments a set of optimized poses into paths connected by neighbor links.
|
||||
*
|
||||
* Designed to be called on the result of a radius search around @p target (a set
|
||||
* of @p poses already constrained to be metrically close to the goal). Within that
|
||||
* radius, the method partitions the @p poses into one or more "paths" where each
|
||||
* path is a connected component reachable from its starting node using **only
|
||||
* neighbor (sequential) links** -- loop-closure links, landmark links and
|
||||
* intermediate nodes are not used to traverse between members. Paths are produced
|
||||
* one at a time, each starting from the still-unclaimed pose nearest to @p target;
|
||||
* a candidate is added to the current path only if it has at least one neighbor
|
||||
* link to a node already in the path.
|
||||
*
|
||||
* Used internally by proximity detection by space (see
|
||||
* @ref Parameters::kRGBDProximityBySpace()) in two independent stages, each
|
||||
* iterating over the segmented paths:
|
||||
* - **One-to-one** (visual registration): runs registration between the current
|
||||
* node and at most one node per neighbor-connected path, avoiding redundant
|
||||
* attempts against nearby members of the same local trajectory.
|
||||
* - **One-to-many** (scan matching, enabled when
|
||||
* @ref Parameters::kRGBDProximityPathMaxNeighbors() > 0): on each path,
|
||||
* neighboring nodes are assembled around the nearest pose on the path (up to
|
||||
* the configured count, walked forward and backward) and their laser scans are
|
||||
* merged for an ICP registration against the current scan. The
|
||||
* neighbor-link-only structure of each path is what makes this assembly
|
||||
* geometrically consistent.
|
||||
*
|
||||
* @param poses Candidate nodes with their optimized poses (typically pre-filtered
|
||||
* to a radius around @p target).
|
||||
* @param target Reference pose used to order paths: each path's starting node is
|
||||
* the still-unclaimed pose closest to @p target.
|
||||
* @param maxGraphDepth Maximum graph depth traversed from the starting node when
|
||||
* gathering candidates for a path (0 = unlimited).
|
||||
* @return Map from the starting node id of each path to its (node id -> pose) chain.
|
||||
*/
|
||||
std::map<int, std::map<int, Transform> > getPaths(const std::map<int, Transform> & poses, const Transform & target, int maxGraphDepth = 0) const;
|
||||
/**
|
||||
* @brief Applies the standard RTAB-Map likelihood adjustment.
|
||||
*
|
||||
* Normalizes raw likelihoods using mean and standard deviation across non-null
|
||||
* values. Real-place entries with @c value <= @c mean + @c stdDev are clamped to
|
||||
* @c 1.0; only entries above that threshold are scaled. The virtual place (the
|
||||
* first key in @p likelihood, representing the "new place" hypothesis) is then
|
||||
* set so that its likelihood reflects how peaked the real distribution is.
|
||||
*
|
||||
* The exact formulas are selected by @ref Parameters::kRtabmapVirtualPlaceLikelihoodRatio()
|
||||
* (default 0, Angeli PhD formulation):
|
||||
*
|
||||
* - **Ratio = 0** (mean / std-dev formulation):
|
||||
* - Real place above threshold: @c (value - (stdDev - epsilon)) / mean
|
||||
* - Virtual place: @c mean / stdDev + 1 (when @c stdDev is non-trivial and a
|
||||
* maximum exists; otherwise 2).
|
||||
* The virtual place "wins" when the real-place distribution is flat
|
||||
* (small @c stdDev relative to @c mean).
|
||||
*
|
||||
* - **Ratio != 0** (z-score formulation):
|
||||
* - Real place above threshold: @c (value - mean) / stdDev (i.e. the z-score).
|
||||
* - Virtual place: @c stdDev / (max - mean) + 1 (when @c max > @c mean;
|
||||
* otherwise 2). The virtual place "wins" when no real candidate stands out
|
||||
* far above the mean.
|
||||
*
|
||||
* In both formulations a low virtual-place likelihood favors a real-place loop
|
||||
* closure on the next Bayes update; a high one favors the "new place" hypothesis.
|
||||
*
|
||||
* @see Parameters::kRtabmapVirtualPlaceLikelihoodRatio()
|
||||
*/
|
||||
void adjustLikelihood(std::map<int, float> & likelihood) const;
|
||||
std::pair<int, float> selectHypothesis(const std::map<int, float> & posterior,
|
||||
const std::map<int, float> & likelihood) const;
|
||||
|
||||
private:
|
||||
void optimizeCurrentMap(int id,
|
||||
@@ -950,8 +297,8 @@ private:
|
||||
bool _publishRAMUsage;
|
||||
bool _computeRMSE;
|
||||
bool _saveWMState;
|
||||
float _maxTimeAllowed; ///< Per-iteration time budget (ms).
|
||||
unsigned int _maxMemoryAllowed; ///< Maximum number of signatures kept in WM.
|
||||
float _maxTimeAllowed; // in ms
|
||||
unsigned int _maxMemoryAllowed; // signatures count in WM
|
||||
float _loopThr;
|
||||
float _loopRatio;
|
||||
float _aggressiveLoopThr;
|
||||
@@ -992,7 +339,7 @@ private:
|
||||
float _optimizationMaxErrorRepairRadius;
|
||||
bool _startNewMapOnLoopClosure;
|
||||
bool _startNewMapOnGoodSignature;
|
||||
float _goalReachedRadius; ///< Path-goal acceptance radius (meters).
|
||||
float _goalReachedRadius; // meters
|
||||
bool _goalsSavedInUserData;
|
||||
int _pathStuckIterations;
|
||||
float _pathLinearVelocity;
|
||||
@@ -1038,16 +385,16 @@ private:
|
||||
std::map<int, Transform> _optimizedPoses;
|
||||
std::multimap<int, Link> _constraints;
|
||||
Transform _mapCorrection;
|
||||
Transform _mapCorrectionBackup; ///< Used in localization mode when odometry is lost.
|
||||
Transform _lastLocalizationPose; ///< Corrected odometry pose; in mapping mode, last pose of getLocalOptimizedPoses().
|
||||
int _lastLocalizationNodeId; ///< Last localization node id (localization mode).
|
||||
Transform _mapCorrectionBackup; // used in localization mode when odom is lost
|
||||
Transform _lastLocalizationPose; // Corrected odometry pose. In mapping mode, this corresponds to last pose return by getLocalOptimizedPoses().
|
||||
int _lastLocalizationNodeId; // for localization mode
|
||||
cv::Mat _localizationCovariance;
|
||||
std::map<int, std::pair<cv::Point3d, Transform> > _gpsGeocentricCache;
|
||||
bool _currentSessionHasGPS;
|
||||
LaserScan _globalScanMap;
|
||||
std::map<int, Transform> _globalScanMapPoses;
|
||||
std::map<int, Transform> _odomCachePoses; ///< Odometry cache used to reject loop closures (localization mode).
|
||||
std::multimap<int, Link> _odomCacheConstraints; ///< Odometry cache constraints (localization mode).
|
||||
std::map<int, Transform> _odomCachePoses; // used in localization mode to reject loop closures
|
||||
std::multimap<int, Link> _odomCacheConstraints; // used in localization mode to reject loop closures
|
||||
std::map<int, Transform> _markerPriors;
|
||||
std::pair<int, int> _lastRejectedLoopClosureIds;
|
||||
|
||||
@@ -1063,7 +410,10 @@ private:
|
||||
int _pathStuckCount;
|
||||
float _pathStuckDistance;
|
||||
|
||||
bool _dummyDictionary;
|
||||
#ifdef RTABMAP_PYTHON
|
||||
PythonInterface * _python;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
@@ -54,41 +54,30 @@ private:
|
||||
Statistics _stats;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class RtabmapEventCmd
|
||||
* @brief Command sent to @ref RtabmapThread, with its parameters.
|
||||
*
|
||||
* Posting one to @ref UEventsManager is how the map is controlled from another
|
||||
* thread: initializing or closing it, resetting the memory, requesting the graph,
|
||||
* setting a goal, labelling a node... The arguments listed for each @ref Cmd are
|
||||
* passed as @p value1 / @p value2 to the constructor, and parameter maps through
|
||||
* @p parameters.
|
||||
*/
|
||||
class RtabmapEventCmd : public UEvent
|
||||
{
|
||||
public:
|
||||
enum dummy {d}; // Hack, to fix Eclipse complaining about not defined Cmd enum ?!
|
||||
/** @brief What the receiving @ref RtabmapThread should do. */
|
||||
enum Cmd {
|
||||
kCmdUndef, /**< No command. */
|
||||
kCmdInit, /**< Initialize the map. Params: [string] database path + ParametersMap. */
|
||||
kCmdResetMemory, /**< Clear the map and start over from an empty memory. */
|
||||
kCmdClose, /**< Close the map. Params: [bool] database saved (default true), [string] output database path (empty=use same database to save, only work when Db/Sqlite3InMemory=true). */
|
||||
kCmdUpdateParams, /**< Apply new parameters. Params: ParametersMap. */
|
||||
kCmdDumpMemory, /**< Dump the memory content to text files in the working directory. */
|
||||
kCmdDumpPrediction, /**< Dump the Bayes filter prediction matrix to a text file. */
|
||||
kCmdGenerateDOTGraph, /**< Write the graph as a DOT file. Params: [bool] global, [string] path, if global=false: [int] id, [int] margin. */
|
||||
kCmdExportPoses, /**< Export the poses to a file. Params: [bool] global, [bool] optimized, [string] path, [int] type (0=raw format, 1=RGBD-SLAM format, 2=KITTI format, 3=TORO, 4=g2o). */
|
||||
kCmdCleanDataBuffer, /**< Drop the frames waiting in the thread's buffer. */
|
||||
kCmdPublish3DMap, /**< Ask for the map, answered with a @ref RtabmapEvent3DMap. Params: [bool] global, [bool] optimized, [bool] graphOnly. */
|
||||
kCmdRepublishData, /**< Republish the data of some nodes. Params: [vector<int>] ids. */
|
||||
kCmdTriggerNewMap, /**< Start a new map session (see @ref Rtabmap::triggerNewMap()). */
|
||||
kCmdPause, /**< Stop consuming incoming data. */
|
||||
kCmdResume, /**< Resume after a @ref kCmdPause. */
|
||||
kCmdGoal, /**< Plan a path to a node. Params: [string] label or [int] location ID. */
|
||||
kCmdCancelGoal, /**< Abandon the current goal. */
|
||||
kCmdLabel, /**< Label a node. Params: [string] label, [int] location ID. */
|
||||
kCmdRemoveLabel /**< Remove a label. Params: [string] label. */
|
||||
kCmdUndef,
|
||||
kCmdInit, // params: [string] database path + ParametersMap
|
||||
kCmdResetMemory,
|
||||
kCmdClose, // params: [bool] database saved (default true), [string] output database path (empty=use same database to save, only work when Db/Sqlite3InMemory=true)
|
||||
kCmdUpdateParams, // params: ParametersMap
|
||||
kCmdDumpMemory,
|
||||
kCmdDumpPrediction,
|
||||
kCmdGenerateDOTGraph, // params: [bool] global, [string] path, if global=false: [int] id, [int] margin
|
||||
kCmdExportPoses, // params: [bool] global, [bool] optimized, [string] path, [int] type (0=raw format, 1=RGBD-SLAM format, 2=KITTI format, 3=TORO, 4=g2o)
|
||||
kCmdCleanDataBuffer,
|
||||
kCmdPublish3DMap, // params: [bool] global, [bool] optimized, [bool] graphOnly
|
||||
kCmdRepublishData, // params: [vector<int>] ids
|
||||
kCmdTriggerNewMap,
|
||||
kCmdPause,
|
||||
kCmdResume,
|
||||
kCmdGoal, // params: [string] label or [int] location ID
|
||||
kCmdCancelGoal,
|
||||
kCmdLabel, // params: [string] label, [int] location ID
|
||||
kCmdRemoveLabel // params: [string] label
|
||||
};
|
||||
public:
|
||||
RtabmapEventCmd(Cmd cmd, const ParametersMap & parameters = ParametersMap()) :
|
||||
|
||||
@@ -48,138 +48,40 @@ namespace rtabmap {
|
||||
|
||||
class Rtabmap;
|
||||
|
||||
/**
|
||||
* @class RtabmapThread
|
||||
* @brief Runs a @ref Rtabmap instance in its own thread, driven by events.
|
||||
*
|
||||
* The thread owns the @ref Rtabmap object and calls @ref Rtabmap::process() on the
|
||||
* data it receives, so that mapping never blocks the sensor or odometry threads.
|
||||
* It is the last stage of the event-based pipeline:
|
||||
* @ref SensorCaptureThread → @ref OdometryThread → RtabmapThread.
|
||||
*
|
||||
* **Input events** (handled in @ref handleEvent(), i.e. in the caller's thread):
|
||||
* - @ref OdometryEvent and @ref SensorEvent -- the frame and its odometry pose are
|
||||
* buffered for the main loop. A null pose means odometry is lost and the frame
|
||||
* is dropped (unless the memory is in localization mode).
|
||||
* - @ref UserDataEvent -- user data attached to the next processed frame.
|
||||
* - @ref RtabmapEventCmd -- a command (see @ref RtabmapEventCmd::Cmd), such as
|
||||
* initializing or closing the map, triggering a new map, pausing, setting a
|
||||
* goal, labelling a node or requesting the map.
|
||||
* - @ref ParamEvent -- applies new parameters, equivalent to a
|
||||
* @ref RtabmapEventCmd::kCmdUpdateParams command.
|
||||
*
|
||||
* **Output events**, posted from the thread:
|
||||
* - @ref RtabmapEvent with the @ref Statistics of an iteration that added a node.
|
||||
* - @ref RtabmapEvent3DMap in answer to a map request.
|
||||
* - @ref RtabmapGlobalPathEvent and @ref RtabmapGoalStatusEvent for path planning.
|
||||
* - @ref RtabmapLabelErrorEvent when a label could not be set.
|
||||
*
|
||||
* **Input regulation**, on top of what @ref Rtabmap does:
|
||||
* - Frames are queued in a buffer of @ref Parameters::kRtabmapImageBufferSize()
|
||||
* elements; when it is full the oldest frame is dropped, so a slow map update
|
||||
* never delays the live pipeline.
|
||||
* - Frames arriving faster than @ref Parameters::kRtabmapDetectionRate() are
|
||||
* skipped, which is the rate limiting that @ref Rtabmap::process() itself does
|
||||
* not do. With @ref Parameters::kRtabmapCreateIntermediateNodes() enabled they
|
||||
* are kept instead, and added as intermediate nodes (odometry-only, not used
|
||||
* for loop closure detection).
|
||||
* - An identity odometry pose, or a covariance ≥ 9999, is read as an odometry
|
||||
* reset and triggers a new map id on the next processed frame. The largest
|
||||
* covariance seen between two processed frames is the one passed on, so the
|
||||
* link weight does not depend on the odometry frame rate.
|
||||
*
|
||||
* The object registers itself as a @ref UEventsHandler, so posting an event to
|
||||
* @ref UEventsManager is enough to feed it:
|
||||
* @code
|
||||
* rtabmap::RtabmapThread rtabmapThread(new rtabmap::Rtabmap()); // takes ownership
|
||||
* rtabmapThread.start();
|
||||
* UEventsManager::addHandler(&rtabmapThread);
|
||||
* UEventsManager::createPipe(&odomThread, &rtabmapThread, "OdometryEvent");
|
||||
* @endcode
|
||||
*
|
||||
* @see Rtabmap
|
||||
* @see OdometryThread
|
||||
* @see SensorCaptureThread
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT RtabmapThread :
|
||||
public UThreadNode,
|
||||
public UEventsHandler
|
||||
{
|
||||
public:
|
||||
/** @brief What the main loop does on its next wake-up. */
|
||||
enum State {
|
||||
kStateDetecting, /**< Process the next buffered frame. */
|
||||
kStateProcessCommand /**< Execute the next queued @ref RtabmapEventCmd. */
|
||||
kStateDetecting,
|
||||
kStateProcessCommand
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param rtabmap The map to run; must not be null. The thread takes ownership
|
||||
* and deletes it in @ref close(), which the destructor calls.
|
||||
*
|
||||
* The buffer size, detection rate and intermediate node settings are taken
|
||||
* from the default parameters here, then updated by every
|
||||
* @ref RtabmapEventCmd::kCmdInit and @ref RtabmapEventCmd::kCmdUpdateParams
|
||||
* command.
|
||||
*/
|
||||
// take ownership
|
||||
RtabmapThread(Rtabmap * rtabmap);
|
||||
virtual ~RtabmapThread();
|
||||
|
||||
/** @brief Drops the buffered frames, the pending user data and the odometry state. */
|
||||
void clearBufferedData();
|
||||
/**
|
||||
* @brief Sets the maximum rate at which frames are processed (Hz, 0 = unlimited).
|
||||
*
|
||||
* Same setting as @ref Parameters::kRtabmapDetectionRate(). Frames arriving
|
||||
* faster are dropped, or added as intermediate nodes if
|
||||
* @ref createIntermediateNodes() is enabled.
|
||||
*/
|
||||
void setDetectorRate(float rate);
|
||||
/**
|
||||
* @brief Sets how many frames may wait in the buffer (0 = unlimited).
|
||||
*
|
||||
* Same setting as @ref Parameters::kRtabmapImageBufferSize(). Once full, the
|
||||
* oldest frame is dropped to make room for the new one.
|
||||
*/
|
||||
void setDataBufferSize(unsigned int bufferSize);
|
||||
/**
|
||||
* @brief Keeps the frames skipped by @ref setDetectorRate() as intermediate nodes.
|
||||
*
|
||||
* Same setting as @ref Parameters::kRtabmapCreateIntermediateNodes(). Those
|
||||
* nodes carry the odometry link but take no part in loop closure detection.
|
||||
*/
|
||||
void createIntermediateNodes(bool enabled);
|
||||
|
||||
/** @return Maximum processing rate in Hz (0 = unlimited). */
|
||||
float getDetectorRate() const {return _rate;}
|
||||
/** @return Maximum number of buffered frames (0 = unlimited). */
|
||||
unsigned int getDataBufferSize() const {return _dataBufferMaxSize;}
|
||||
/** @return True if skipped frames are added as intermediate nodes. */
|
||||
bool getCreateIntermediateNodes() const {return _createIntermediateNodes;}
|
||||
|
||||
/**
|
||||
* @brief Joins the thread and closes rtabmap. This will delete rtabmap object if set.
|
||||
* Close rtabmap. This will delete rtabmap object if set.
|
||||
* @param databaseSaved true=database saved, false=database discarded.
|
||||
* @param databasePath output database file name, ignored if
|
||||
* Db/Sqlite3InMemory=false (opened database is
|
||||
* then overwritten).
|
||||
*
|
||||
* Called by the destructor with @p databaseSaved true. The object cannot be
|
||||
* used afterwards: the @ref Rtabmap instance it owned is gone.
|
||||
*/
|
||||
void close(bool databaseSaved, const std::string & databasePath = "");
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Receives the events listed in the class description.
|
||||
*
|
||||
* Runs in the posting thread: data events are only queued here, the work
|
||||
* happens in the thread's main loop. Events are ignored until the thread is
|
||||
* started.
|
||||
*
|
||||
* @return Always false, so the event keeps being dispatched to other handlers.
|
||||
*/
|
||||
virtual bool handleEvent(UEvent * anEvent);
|
||||
|
||||
private:
|
||||
|
||||
@@ -43,231 +43,50 @@ namespace rtabmap
|
||||
{
|
||||
|
||||
/**
|
||||
* @class SensorCapture
|
||||
* @brief Abstract base class for sensor data capture (cameras, lidars, etc.)
|
||||
* Class Camera
|
||||
*
|
||||
* SensorCapture provides a unified interface for capturing sensor data from various
|
||||
* sensor types including cameras (RGB-D, stereo, mono) and lidars. It handles frame
|
||||
* rate control, local transform management, and provides a common API for sensor
|
||||
* initialization and data capture.
|
||||
*
|
||||
* The class implements the Template Method pattern:
|
||||
* - `takeData()` handles frame rate control and timing, then calls the pure virtual
|
||||
* `captureData()` method implemented by derived classes
|
||||
* - Derived classes must implement `captureData()` to perform the actual sensor capture
|
||||
*
|
||||
* Key features:
|
||||
* - **Frame rate control**: Automatic throttling to maintain target frame rate
|
||||
* - **Local transform**: Transform from robot base frame to sensor frame
|
||||
* - **Capture timing**: Tracks capture time and provides it via SensorCaptureInfo
|
||||
* - **Sequence IDs**: Automatic sequence number generation for captured data
|
||||
*
|
||||
* Derived classes include:
|
||||
* - **Camera**: Base class for all camera types (RGB-D, stereo, mono, file readers, etc.)
|
||||
* - **Lidar**: Base class for lidar sensors
|
||||
*
|
||||
* @note This is an abstract class. Use concrete implementations like CameraRGBD,
|
||||
* CameraStereo, LidarVLP16, etc., or create custom derived classes.
|
||||
*
|
||||
* @see Camera
|
||||
* @see Lidar
|
||||
* @see SensorData
|
||||
* @see SensorCaptureInfo
|
||||
* @see SensorCaptureThread
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT SensorCapture
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~SensorCapture();
|
||||
|
||||
/**
|
||||
* @brief Captures sensor data with frame rate control
|
||||
*
|
||||
* This method handles frame rate throttling and timing, then calls the
|
||||
* pure virtual `captureData()` method to perform the actual capture.
|
||||
*
|
||||
* The method:
|
||||
* - Enforces the target frame rate by sleeping if necessary
|
||||
* - Measures capture time and stores it in SensorCaptureInfo
|
||||
* - Assigns sequence IDs to captured data
|
||||
* - Warns if the target frame rate cannot be reached
|
||||
*
|
||||
* @param info Optional pointer to SensorCaptureInfo to fill with capture metadata
|
||||
* (ID, timestamp, capture time). If null, no info is filled.
|
||||
* @return SensorData containing the captured sensor data
|
||||
*
|
||||
* @note If frame rate is 0, data is captured as fast as possible without throttling.
|
||||
* @note The returned SensorData should have rectified images if calibration was loaded.
|
||||
*
|
||||
* @see captureData()
|
||||
*/
|
||||
SensorData takeData(SensorCaptureInfo * info = 0);
|
||||
|
||||
/**
|
||||
* @brief Initializes the sensor
|
||||
*
|
||||
* Pure virtual method that must be implemented by derived classes to initialize
|
||||
* the sensor hardware or data source. This typically involves:
|
||||
* - Opening device connections or file streams
|
||||
* - Loading camera calibration parameters
|
||||
* - Configuring sensor settings
|
||||
*
|
||||
* @param calibrationFolder Directory path where calibration files are located
|
||||
* (default: current directory ".")
|
||||
* @param cameraName Base name of the camera for loading calibration files
|
||||
* (default: empty string)
|
||||
* @return True if initialization was successful, false otherwise
|
||||
*
|
||||
* @note Must be called before calling takeData() or captureData()
|
||||
*/
|
||||
virtual bool init(const std::string & calibrationFolder = ".", const std::string & cameraName = "") = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the sensor's serial number or unique identifier
|
||||
*
|
||||
* Pure virtual method that must be implemented by derived classes to return
|
||||
* a unique identifier for the sensor (e.g., device serial number, file path,
|
||||
* or other identifier).
|
||||
*
|
||||
* @return String identifier for the sensor
|
||||
*/
|
||||
virtual std::string getSerial() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Checks if the sensor provides odometry poses
|
||||
*
|
||||
* Some sensors (e.g., visual-inertial cameras) can provide pose estimates
|
||||
* directly. This method indicates whether the sensor supports pose queries.
|
||||
*
|
||||
* @return True if the sensor provides odometry poses, false otherwise
|
||||
*
|
||||
* @note Default implementation returns false. Derived classes should override
|
||||
* if they support pose estimation.
|
||||
*
|
||||
* @see getPose()
|
||||
*/
|
||||
virtual bool odomProvided() const { return false; }
|
||||
|
||||
/**
|
||||
* @brief Gets the sensor's pose estimate at a specific timestamp
|
||||
*
|
||||
* Queries the sensor for its pose estimate at the given timestamp. This is
|
||||
* typically used for sensors that provide visual-inertial odometry or other
|
||||
* pose estimation capabilities.
|
||||
*
|
||||
* @param stamp Timestamp in seconds for which to query the pose
|
||||
* @param[out] pose Output transform representing the sensor pose
|
||||
* @param[out] covariance Output covariance matrix (6x6) representing twist uncertainty
|
||||
* @param maxWaitTime Maximum time in seconds to wait for pose data (default: 0.06)
|
||||
* @return True if pose was successfully retrieved, false otherwise
|
||||
*
|
||||
* @note Default implementation returns false. Derived classes should override
|
||||
* if they support pose estimation.
|
||||
*
|
||||
* @see odomProvided()
|
||||
*/
|
||||
virtual bool getPose(double stamp, Transform & pose, cv::Mat & covariance, double maxWaitTime = 0.06) { return false; }
|
||||
|
||||
// Getters
|
||||
|
||||
/**
|
||||
* @brief Returns the target frame rate
|
||||
* @return Frame rate in Hz (0 = unlimited, capture as fast as possible)
|
||||
*/
|
||||
//getters
|
||||
float getFrameRate() const {return _frameRate;}
|
||||
|
||||
/**
|
||||
* @brief Returns the local transform from base frame to sensor frame
|
||||
* @return Const reference to the local transform
|
||||
*/
|
||||
const Transform & getLocalTransform() const {return _localTransform;}
|
||||
|
||||
// Setters
|
||||
|
||||
/**
|
||||
* @brief Sets the target frame rate
|
||||
*
|
||||
* Controls how often `takeData()` will capture data. The method will throttle
|
||||
* captures to maintain the target rate.
|
||||
*
|
||||
* @param frameRate Target frame rate in Hz (0 = unlimited, capture as fast as possible)
|
||||
*
|
||||
* @note Setting frame rate to 0 disables throttling and captures as fast as possible.
|
||||
*/
|
||||
//setters
|
||||
void setFrameRate(float frameRate) {_frameRate = frameRate;}
|
||||
|
||||
/**
|
||||
* @brief Sets the local transform from base frame to sensor frame
|
||||
*
|
||||
* The local transform represents the pose of the sensor relative to the robot's
|
||||
* base frame. This is used to transform sensor data into the robot's coordinate system.
|
||||
*
|
||||
* @param localTransform Transform from base frame to sensor frame
|
||||
*/
|
||||
void setLocalTransform(const Transform & localTransform) {_localTransform= localTransform;}
|
||||
|
||||
/**
|
||||
* @brief Resets the frame rate timer
|
||||
*
|
||||
* Resets the internal timer used for frame rate control. This is useful when
|
||||
* starting a new capture session or after a pause.
|
||||
*/
|
||||
void resetTimer();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Protected constructor
|
||||
* Constructor
|
||||
*
|
||||
* Creates a SensorCapture instance with the specified frame rate and local transform.
|
||||
* This constructor is protected because SensorCapture is an abstract base class
|
||||
* and should not be instantiated directly.
|
||||
*
|
||||
* @param frameRate Target frame rate in Hz (0 = unlimited, capture as fast as possible)
|
||||
* @param localTransform Transform from base frame to sensor frame (default: identity)
|
||||
* @param frameRate the frame rate (Hz), 0 for fast as the sensor can
|
||||
* @param localTransform the transform from base frame to sensor frame
|
||||
*/
|
||||
SensorCapture(float frameRate = 0, const Transform & localTransform = Transform::getIdentity());
|
||||
|
||||
/**
|
||||
* @brief Pure virtual method to capture sensor data
|
||||
*
|
||||
* This method must be implemented by derived classes to perform the actual
|
||||
* sensor data capture. It is called by `takeData()` after frame rate throttling.
|
||||
*
|
||||
* The returned SensorData should:
|
||||
* - Have rectified images if calibration was loaded during `init()`
|
||||
* - Include proper timestamps
|
||||
* - Contain valid sensor data (images, depth, laser scans, etc.)
|
||||
*
|
||||
* @param info Optional pointer to SensorCaptureInfo to fill with capture metadata.
|
||||
* The base class will fill ID, timestamp, and capture time, but derived
|
||||
* classes can add additional information.
|
||||
* @return SensorData containing the captured sensor data
|
||||
*
|
||||
* @note If capture fails, return an empty SensorData (id=0, stamp=0.0).
|
||||
* @note RGB and depth images should be already rectified if calibration was loaded.
|
||||
*
|
||||
* @see takeData()
|
||||
* returned rgb and depth images should be already rectified if calibration was loaded
|
||||
*/
|
||||
virtual SensorData captureData(SensorCaptureInfo * info = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief Gets the next sequence ID
|
||||
*
|
||||
* Returns and increments the internal sequence counter. This is used to assign
|
||||
* unique sequence numbers to captured data.
|
||||
*
|
||||
* @return Next sequence ID (starts at 1, increments with each call)
|
||||
*/
|
||||
int getNextSeqID() {return ++_seq;}
|
||||
|
||||
private:
|
||||
float _frameRate; ///< Target frame rate in Hz (0 = unlimited)
|
||||
Transform _localTransform; ///< Transform from base frame to sensor frame
|
||||
UTimer * _frameRateTimer; ///< Timer for frame rate control
|
||||
int _seq; ///< Sequence counter for captured data
|
||||
float _frameRate;
|
||||
Transform _localTransform;
|
||||
UTimer * _frameRateTimer;
|
||||
int _seq;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -33,50 +33,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
/**
|
||||
* @class SensorCaptureInfo
|
||||
* @brief Metadata structure for sensor data capture and processing
|
||||
*
|
||||
* SensorCaptureInfo contains metadata about sensor data capture, including timing
|
||||
* information for various processing steps, camera identification, and odometry
|
||||
* information. This information is typically attached to SensorEvent objects to
|
||||
* provide context about how the sensor data was captured and processed.
|
||||
*
|
||||
* The class tracks timing for various processing stages:
|
||||
* - **Capture**: Time to capture data from the sensor
|
||||
* - **Processing**: Time for various image processing steps (deskewing, disparity,
|
||||
* mirroring, exposure compensation, decimation, histogram equalization, etc.)
|
||||
* - **Depth processing**: Time for depth-related operations (scan from depth,
|
||||
* undistort depth, bilateral filtering)
|
||||
* - **Total**: Total processing time
|
||||
*
|
||||
* Odometry information includes:
|
||||
* - **Pose**: The odometry pose at the time of capture
|
||||
* - **Covariance**: Uncertainty of the odometry twist (6x6 matrix)
|
||||
* - **Velocity**: 6DOF velocity vector [vx, vy, vz, vroll, vpitch, vyaw]
|
||||
*
|
||||
* @note All timing values are in seconds (float).
|
||||
* @note The odometry covariance matrix is initialized to identity by default.
|
||||
*
|
||||
* @see SensorEvent
|
||||
* @see SensorData
|
||||
*/
|
||||
class SensorCaptureInfo
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*
|
||||
* Initializes all fields to default values:
|
||||
* - cameraName: empty string
|
||||
* - id: 0
|
||||
* - stamp: 0.0
|
||||
* - All timing fields: 0.0f
|
||||
* - odomPose: null transform
|
||||
* - odomCovariance: 6x6 identity matrix
|
||||
* - odomVelocity: empty vector
|
||||
*/
|
||||
SensorCaptureInfo() :
|
||||
cameraName(""),
|
||||
id(0),
|
||||
@@ -95,41 +55,28 @@ public:
|
||||
odomCovariance(cv::Mat::eye(6,6,CV_64FC1))
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~SensorCaptureInfo() {}
|
||||
|
||||
std::string cameraName; ///< Camera/sensor name identifier
|
||||
int id; ///< Capture ID (typically matches SensorData ID)
|
||||
double stamp; ///< Timestamp in seconds (typically matches SensorData stamp)
|
||||
|
||||
// Timing information (all in seconds)
|
||||
float timeCapture; ///< Time to capture data from the sensor (seconds)
|
||||
float timeDeskewing; ///< Time for laser scan deskewing (seconds)
|
||||
float timeDisparity; ///< Time to compute stereo disparity (seconds)
|
||||
float timeMirroring; ///< Time for image mirroring/flipping (seconds)
|
||||
float timeStereoExposureCompensation; ///< Time for stereo exposure compensation (seconds)
|
||||
float timeImageDecimation; ///< Time for image decimation/downsampling (seconds)
|
||||
float timeHistogramEqualization; ///< Time for histogram equalization (seconds)
|
||||
float timeScanFromDepth; ///< Time to convert depth image to laser scan (seconds)
|
||||
float timeUndistortDepth; ///< Time to undistort depth image (seconds)
|
||||
float timeBilateralFiltering; ///< Time for bilateral filtering of depth (seconds)
|
||||
float timeTotal; ///< Total processing time (seconds)
|
||||
|
||||
// Odometry information
|
||||
Transform odomPose; ///< Odometry pose at the time of capture (in odometry coordinate frame)
|
||||
cv::Mat odomCovariance; ///< Odometry twist covariance matrix (6x6, CV_64FC1). Default: identity matrix
|
||||
std::vector<float> odomVelocity; ///< 6DOF odometry velocity [vx, vy, vz, vroll, vpitch, vyaw] (m/s, rad/s)
|
||||
std::string cameraName;
|
||||
int id;
|
||||
double stamp;
|
||||
float timeCapture;
|
||||
float timeDeskewing;
|
||||
float timeDisparity;
|
||||
float timeMirroring;
|
||||
float timeStereoExposureCompensation;
|
||||
float timeImageDecimation;
|
||||
float timeHistogramEqualization;
|
||||
float timeScanFromDepth;
|
||||
float timeUndistortDepth;
|
||||
float timeBilateralFiltering;
|
||||
float timeTotal;
|
||||
Transform odomPose;
|
||||
cv::Mat odomCovariance;
|
||||
std::vector<float> odomVelocity;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use SensorCaptureInfo instead
|
||||
* @brief Backward compatibility typedef
|
||||
*
|
||||
* CameraInfo is deprecated. Use SensorCaptureInfo instead.
|
||||
*/
|
||||
//backward compatibility
|
||||
RTABMAP_DEPRECATED typedef SensorCaptureInfo CameraInfo;
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
@@ -52,86 +52,23 @@ class IMUFilter;
|
||||
class Feature2D;
|
||||
|
||||
/**
|
||||
* @class SensorCaptureThread
|
||||
* @brief Thread-based sensor data capture and event posting for RTAB-Map
|
||||
* Class CameraThread
|
||||
*
|
||||
* SensorCaptureThread is a multi-threaded class that continuously captures sensor
|
||||
* data from cameras and/or lidars in a background thread. It processes the data,
|
||||
* optionally applies filtering and transformations, and posts SensorEvent events
|
||||
* to registered handlers through RTAB-Map's event system.
|
||||
*
|
||||
* The class supports various sensor configurations:
|
||||
* - **Camera-only**: Single or multiple cameras for RGB-D or stereo imaging
|
||||
* - **Lidar-only**: Single lidar for 3D point cloud capture
|
||||
* - **Camera + Lidar**: Combined visual and range sensing
|
||||
* - **With odometry**: Optional odometry sensor for pose estimation and deskewing
|
||||
*
|
||||
* Key features:
|
||||
* - **Automatic data synchronization**: Synchronizes camera and lidar data by timestamp
|
||||
* - **Deskewing**: Corrects lidar scans using odometry poses
|
||||
* - **Image processing**: Supports mirroring, decimation, histogram equalization, stereo-to-depth conversion
|
||||
* - **Depth filtering**: Optional bilateral filtering for depth images
|
||||
* - **IMU filtering**: Optional IMU data filtering and fusion
|
||||
* - **Feature detection**: Optional automatic feature detection on captured images
|
||||
* - **Frame rate control**: Configurable capture rate
|
||||
* - **Event-driven architecture**: Posts SensorEvent events for downstream processing
|
||||
*
|
||||
* The class inherits from UThread (for threading) and UEventsSender (for event posting).
|
||||
*
|
||||
* @note Ownership of Camera, Lidar, and SensorCapture pointers is transferred to this class.
|
||||
* @note The thread must be started with start() and stopped with kill() or join().
|
||||
* @warning **Thread Safety**: All configuration parameters (setters) should be called
|
||||
* **before** starting the thread with start(). Changing configuration after the
|
||||
* thread is started is not thread-safe and may lead to race conditions or
|
||||
* undefined behavior. If you need to change parameters at runtime, stop the
|
||||
* thread first, modify the configuration, then restart it.
|
||||
*
|
||||
* @see Camera
|
||||
* @see Lidar
|
||||
* @see SensorCapture
|
||||
* @see SensorEvent
|
||||
* @see SensorData
|
||||
* @see UThread
|
||||
* @see UEventsSender
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT SensorCaptureThread :
|
||||
public UThread,
|
||||
public UEventsSender
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor for camera-only capture
|
||||
*
|
||||
* Creates a SensorCaptureThread that captures images from a single camera.
|
||||
* The camera can be RGB-D, stereo, or mono.
|
||||
*
|
||||
* @param camera Pointer to the camera to capture from (ownership transferred)
|
||||
* @param parameters Optional parameters map for configuration
|
||||
*
|
||||
* @note The camera pointer is owned by this class and will be deleted on destruction.
|
||||
*/
|
||||
// ownership transferred
|
||||
SensorCaptureThread(
|
||||
Camera * camera,
|
||||
const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Constructor for camera with odometry sensor
|
||||
*
|
||||
* Creates a SensorCaptureThread that captures images from a camera and uses
|
||||
* an odometry sensor for pose estimation. The odometry sensor can be the same
|
||||
* as the camera or a different sensor.
|
||||
*
|
||||
* @param camera Pointer to the camera to capture images from (ownership transferred)
|
||||
* @param odomSensor Pointer to the odometry sensor for pose estimation (can be the same as camera)
|
||||
* @param extrinsics Static transform from odometry sensor's left lens frame to camera's left lens frame
|
||||
* (without optical rotation applied)
|
||||
* @param poseTimeOffset Time offset in seconds to add to data timestamp when querying pose (default: 0.0)
|
||||
* @param poseScaleFactor Scale factor to apply to pose translation (default: 1.0, no scaling)
|
||||
* @param poseWaitTime Maximum time in seconds to wait for pose data (default: 0.1)
|
||||
* @param parameters Optional parameters map for configuration
|
||||
*
|
||||
* @note If odomAsGt is set to true via setOdomAsGroundTruth(), the odometry pose
|
||||
* will be used as ground truth instead of odometry.
|
||||
* @param camera the camera to take images from
|
||||
* @param odomSensor an odometry sensor to get a pose (can be again the camera)
|
||||
* @param odomAsGt set odometry sensor pose as ground truth instead of odometry
|
||||
* @param extrinsics the static transform between odometry sensor's left lens frame to camera's left lens frame (without optical rotation)
|
||||
*/
|
||||
SensorCaptureThread(
|
||||
Camera * camera,
|
||||
@@ -141,55 +78,23 @@ public:
|
||||
float poseScaleFactor = 1.0f,
|
||||
double poseWaitTime = 0.1,
|
||||
const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Constructor for lidar-only capture
|
||||
*
|
||||
* Creates a SensorCaptureThread that captures 3D point clouds from a lidar.
|
||||
*
|
||||
* @param lidar Pointer to the lidar to capture scans from (ownership transferred)
|
||||
* @param parameters Optional parameters map for configuration
|
||||
*
|
||||
* @note The lidar pointer is owned by this class and will be deleted on destruction.
|
||||
* @param lidar the lidar to take scans from
|
||||
*/
|
||||
SensorCaptureThread(
|
||||
Lidar * lidar,
|
||||
const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Constructor for lidar with camera
|
||||
*
|
||||
* Creates a SensorCaptureThread that captures both lidar scans and camera images.
|
||||
* If the camera provides pose information, it can be used for deskewing lidar scans.
|
||||
*
|
||||
* @param lidar Pointer to the lidar to capture scans from (ownership transferred)
|
||||
* @param camera Pointer to the camera to capture images from (ownership transferred).
|
||||
* If the camera provides pose, it can be used for deskewing.
|
||||
* @param parameters Optional parameters map for configuration
|
||||
* @param lidar the lidar to take scans from
|
||||
* @param camera the camera to take images from. If the camera is providing a pose, it can be used for deskewing
|
||||
*/
|
||||
SensorCaptureThread(
|
||||
Lidar * lidar,
|
||||
Camera * camera,
|
||||
const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Constructor for lidar with odometry sensor
|
||||
*
|
||||
* Creates a SensorCaptureThread that captures lidar scans and uses an odometry
|
||||
* sensor for pose estimation and deskewing. The odometry sensor can be the same
|
||||
* as the lidar or a different sensor.
|
||||
*
|
||||
* @param lidar Pointer to the lidar to capture scans from (ownership transferred)
|
||||
* @param odomSensor Pointer to the odometry sensor for pose estimation and deskewing
|
||||
* (can be the same as lidar)
|
||||
* @param poseTimeOffset Time offset in seconds to add to data timestamp when querying pose (default: 0.0)
|
||||
* @param poseScaleFactor Scale factor to apply to pose translation (default: 1.0, no scaling)
|
||||
* @param poseWaitTime Maximum time in seconds to wait for pose data (default: 0.1)
|
||||
* @param parameters Optional parameters map for configuration
|
||||
*
|
||||
* @note Deskewing is enabled by default when an odometry sensor is provided.
|
||||
* Use setScanParameters() to configure deskewing behavior.
|
||||
* @note The lidar's local transform should be set as the extrinsics between odometry sensor's frame and lidar's frame.
|
||||
* @param lidar the lidar to take scans from
|
||||
* @param odomSensor an odometry sensor to get a pose and used for deskewing (can be again the lidar)
|
||||
*/
|
||||
SensorCaptureThread(
|
||||
Lidar * lidar,
|
||||
@@ -198,28 +103,11 @@ public:
|
||||
float poseScaleFactor = 1.0f,
|
||||
double poseWaitTime = 0.1,
|
||||
const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Constructor for lidar with camera and odometry sensor
|
||||
*
|
||||
* Creates a SensorCaptureThread that captures both lidar scans and camera images,
|
||||
* and uses an odometry sensor for pose estimation and deskewing. This is the most
|
||||
* comprehensive configuration, supporting multi-modal sensing with pose correction.
|
||||
*
|
||||
* @param lidar Pointer to the lidar to capture scans from (ownership transferred)
|
||||
* @param camera Pointer to the camera to capture images from (ownership transferred)
|
||||
* @param odomSensor Pointer to the odometry sensor for pose estimation and deskewing
|
||||
* (can be the same as camera or lidar)
|
||||
* @param extrinsics Static transform from odometry frame to camera frame
|
||||
* (without optical rotation applied)
|
||||
* @param poseTimeOffset Time offset in seconds to add to data timestamp when querying pose (default: 0.0)
|
||||
* @param poseScaleFactor Scale factor to apply to pose translation (default: 1.0, no scaling)
|
||||
* @param poseWaitTime Maximum time in seconds to wait for pose data (default: 0.1)
|
||||
* @param parameters Optional parameters map for configuration
|
||||
*
|
||||
* @note The camera and lidar data are synchronized by timestamp, with the camera
|
||||
* frame being at least as recent as the lidar frame.
|
||||
* @note The lidar's local transform should be set as the extrinsics between odometry sensor's frame and lidar's frame.
|
||||
* @param lidar the lidar to take scans from
|
||||
* @param camera the camera to take images from
|
||||
* @param odomSensor an odometry sensor to get a pose and used for deskewing (can be again the camera or lidar)
|
||||
* @param extrinsics the static transform between odometry frame to camera frame (without optical rotation)
|
||||
*/
|
||||
SensorCaptureThread(
|
||||
Lidar * lidar,
|
||||
@@ -230,182 +118,26 @@ public:
|
||||
float poseScaleFactor = 1.0f,
|
||||
double poseWaitTime = 0.1,
|
||||
const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*
|
||||
* Stops the capture thread if running and deletes owned sensor pointers.
|
||||
*/
|
||||
virtual ~SensorCaptureThread();
|
||||
|
||||
/**
|
||||
* @brief Enables or disables image mirroring (horizontal flip)
|
||||
* @param enabled True to enable mirroring, false to disable
|
||||
*/
|
||||
void setMirroringEnabled(bool enabled) {_mirroring = enabled;}
|
||||
|
||||
/**
|
||||
* @brief Enables or disables stereo exposure compensation
|
||||
*
|
||||
* When enabled, adjusts exposure between left and right stereo cameras
|
||||
* to improve matching quality.
|
||||
*
|
||||
* @param enabled True to enable exposure compensation, false to disable
|
||||
*/
|
||||
void setStereoExposureCompensation(bool enabled) {_stereoExposureCompensation = enabled;}
|
||||
|
||||
/**
|
||||
* @brief Sets whether to capture color images only (skip depth)
|
||||
* @param colorOnly True to capture color only, false to capture both color and depth
|
||||
*/
|
||||
void setColorOnly(bool colorOnly) {_colorOnly = colorOnly;}
|
||||
|
||||
/**
|
||||
* @brief Sets image decimation factor
|
||||
*
|
||||
* Decimation reduces image resolution by skipping pixels. A decimation of N
|
||||
* means every Nth pixel is kept (e.g., 2 = half resolution, 4 = quarter resolution).
|
||||
*
|
||||
* @param decimation Decimation factor (1 = no decimation, 2 = half resolution, etc.)
|
||||
*/
|
||||
void setImageDecimation(int decimation) {_imageDecimation = decimation;}
|
||||
|
||||
/**
|
||||
* @brief Sets histogram equalization method
|
||||
*
|
||||
* Controls the type of histogram equalization applied to captured images.
|
||||
* Histogram equalization improves image contrast by redistributing pixel intensities.
|
||||
*
|
||||
* @param histogramMethod Histogram equalization method:
|
||||
* - 0 = None (disabled)
|
||||
* - 1 = Standard histogram equalization (cv::equalizeHist)
|
||||
* - 2 = CLAHE - Contrast Limited Adaptive Histogram Equalization (cv::createCLAHE)
|
||||
*
|
||||
* @note For color images, histogram equalization is applied only to the luminance channel (Y in YCrCb color space).
|
||||
* @note For stereo images, histogram equalization is applied to both left and right images.
|
||||
*/
|
||||
void setHistogramMethod(int histogramMethod) {_histogramMethod = histogramMethod;}
|
||||
|
||||
/**
|
||||
* @brief Enables or disables stereo-to-depth conversion
|
||||
*
|
||||
* When enabled, converts stereo images to depth images using dense stereo matching.
|
||||
*
|
||||
* @param enabled True to enable stereo-to-depth conversion, false to disable
|
||||
*/
|
||||
void setStereoToDepth(bool enabled) {_stereoToDepth = enabled;}
|
||||
|
||||
/**
|
||||
* @brief Sets the target frame rate for capture
|
||||
*
|
||||
* Controls how often the capture loop runs. A frame rate of 0 means capture
|
||||
* as fast as possible.
|
||||
*
|
||||
* @param frameRate Target frame rate in Hz (0 = unlimited)
|
||||
*/
|
||||
void setFrameRate(float frameRate);
|
||||
|
||||
/**
|
||||
* @deprecated Use setFrameRate() instead
|
||||
* @brief Sets the target image capture rate (deprecated)
|
||||
*/
|
||||
RTABMAP_DEPRECATED void setImageRate(float frameRate) {setFrameRate(frameRate);}
|
||||
|
||||
/**
|
||||
* @brief Sets the depth distortion model file path
|
||||
*
|
||||
* Loads a discrete depth distortion model from the specified file path.
|
||||
* This is used to correct systematic depth errors at longer ranges using
|
||||
* the CLAMS (Calibration, Localization, And Mapping System) approach.
|
||||
*
|
||||
* The distortion model is typically created using RTAB-Map's depth calibration
|
||||
* tool, which uses visual odometry and 3D mapping to generate ground truth
|
||||
* depth for calibration.
|
||||
*
|
||||
* @param path Path to the distortion model file
|
||||
*
|
||||
* @see https://github.com/introlab/rtabmap/wiki/Depth-Calibration
|
||||
* for detailed instructions on how to create and use depth distortion models
|
||||
*/
|
||||
void setDistortionModel(const std::string & path);
|
||||
|
||||
/**
|
||||
* @brief Sets whether odometry poses should be treated as ground truth
|
||||
*
|
||||
* When enabled, odometry poses from the odometry sensor are stored as ground
|
||||
* truth poses instead of odometry poses in the SensorData.
|
||||
*
|
||||
* @param enabled True to use odometry as ground truth, false to use as odometry
|
||||
*/
|
||||
void setOdomAsGroundTruth(bool enabled) {_odomAsGt = enabled;}
|
||||
|
||||
/**
|
||||
* @brief Enables bilateral filtering for depth images
|
||||
*
|
||||
* Bilateral filtering reduces noise in depth images while preserving edges.
|
||||
*
|
||||
* @param sigmaS Spatial standard deviation (pixels)
|
||||
* @param sigmaR Range standard deviation (depth units)
|
||||
*/
|
||||
void enableBilateralFiltering(float sigmaS, float sigmaR);
|
||||
|
||||
/**
|
||||
* @brief Disables bilateral filtering
|
||||
*/
|
||||
void disableBilateralFiltering() {_bilateralFiltering = false;}
|
||||
|
||||
/**
|
||||
* @brief Enables IMU data filtering
|
||||
*
|
||||
* Enables filtering and fusion of IMU data from the sensor. The filtering
|
||||
* strategy determines how IMU data is processed to estimate orientation.
|
||||
*
|
||||
* @param filteringStrategy Filtering strategy:
|
||||
* - 0 = Madgwick filter (requires RTAB-Map built with RTABMAP_MADGWICK option)
|
||||
* - 1 = Complementary filter (default)
|
||||
* @param parameters Optional parameters map for IMU filter configuration
|
||||
* @param baseFrameConversion If true, converts IMU data to base frame
|
||||
*
|
||||
* @note If Madgwick filter is requested but RTAB-Map is not built with the option enabled,
|
||||
* the Complementary filter will be used instead.
|
||||
*
|
||||
* @see IMUFilter
|
||||
*/
|
||||
void enableIMUFiltering(int filteringStrategy=1, const ParametersMap & parameters = ParametersMap(), bool baseFrameConversion = false);
|
||||
|
||||
/**
|
||||
* @brief Disables IMU data filtering
|
||||
*/
|
||||
void disableIMUFiltering();
|
||||
|
||||
/**
|
||||
* @brief Enables automatic feature detection on captured images
|
||||
*
|
||||
* When enabled, automatically detects and extracts visual features (keypoints,
|
||||
* descriptors) from captured images.
|
||||
*
|
||||
* @param parameters Optional parameters map for feature detector configuration.
|
||||
* Parameters from the "Vis/" group (e.g., Vis/FeatureType, Vis/MaxFeatures)
|
||||
* are automatically converted to "Kp/" parameters internally.
|
||||
* If both Vis/ and Kp/ parameters are provided, Vis/ parameters take precedence.
|
||||
*
|
||||
* @note The function converts Vis/ parameters to Kp/ parameters before creating the Feature2D detector.
|
||||
*/
|
||||
void enableFeatureDetection(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Disables automatic feature detection
|
||||
*/
|
||||
void disableFeatureDetection();
|
||||
|
||||
/**
|
||||
* @deprecated Use the new version with groundNormalsUp parameter instead
|
||||
* @brief Sets lidar scan processing parameters (deprecated)
|
||||
*
|
||||
* Use the new version with groundNormalsUp parameter:
|
||||
* - groundNormalsUp=0.8 for forceGroundNormalsUp=True
|
||||
* - groundNormalsUp=0.0 for forceGroundNormalsUp=False
|
||||
*/
|
||||
// Use new version of this function with groundNormalsUp=0.8 for forceGroundNormalsUp=True and groundNormalsUp=0.0 for forceGroundNormalsUp=False.
|
||||
RTABMAP_DEPRECATED void setScanParameters(
|
||||
bool fromDepth,
|
||||
int downsampleStep, // decimation of the depth image in case the scan is from depth image
|
||||
@@ -416,27 +148,6 @@ public:
|
||||
float normalsRadius,
|
||||
bool forceGroundNormalsUp,
|
||||
bool deskewing);
|
||||
|
||||
/**
|
||||
* @brief Sets lidar scan processing parameters
|
||||
*
|
||||
* Configures how lidar scans are processed, including conversion from depth images,
|
||||
* filtering, normal estimation, and deskewing.
|
||||
*
|
||||
* @param fromDepth If true, generate scans from depth images instead of raw lidar data
|
||||
* @param downsampleStep Downsampling step:
|
||||
* - If fromDepth is true: Image decimation step for depth images
|
||||
* (1 = no decimation, 2 = every other pixel in both dimensions, etc.)
|
||||
* - If fromDepth is false: Point skipping step for laser scan data
|
||||
* (1 = no skipping, 2 = every other point, etc.)
|
||||
* @param rangeMin Minimum range in meters (points closer are filtered out, 0 = no minimum)
|
||||
* @param rangeMax Maximum range in meters (points farther are filtered out, 0 = no maximum)
|
||||
* @param voxelSize Voxel size in meters for downsampling (0 = no voxelization)
|
||||
* @param normalsK Number of neighbors for K-nearest neighbors normal estimation (0 = no normals)
|
||||
* @param normalsRadius Radius in meters for radius-based normal estimation (0 = use K-nearest)
|
||||
* @param groundNormalsUp Threshold for forcing ground normals upward (0.0-1.0, 0.8 = strong, 0.0 = disabled)
|
||||
* @param deskewing If true, apply deskewing using odometry poses
|
||||
*/
|
||||
void setScanParameters(
|
||||
bool fromDepth,
|
||||
int downsampleStep=1, // decimation of the depth image in case the scan is from depth image
|
||||
@@ -448,120 +159,58 @@ public:
|
||||
float groundNormalsUp = 0.0f,
|
||||
bool deskewing = false);
|
||||
|
||||
/**
|
||||
* @brief Post-processing hook called before posting SensorEvent
|
||||
*
|
||||
* This method is called after all processing is complete but before the
|
||||
* SensorEvent is posted. Derived classes can override this to perform
|
||||
* additional processing or modifications to the data.
|
||||
*
|
||||
* @param data Pointer to the processed sensor data (can be modified)
|
||||
* @param info Pointer to the sensor capture info (can be modified, may be null)
|
||||
*/
|
||||
void postUpdate(SensorData * data, SensorCaptureInfo * info = 0) const;
|
||||
|
||||
// Getters
|
||||
|
||||
/**
|
||||
* @brief Checks if the capture thread is paused
|
||||
* @return True if paused (not running), false if running
|
||||
*/
|
||||
//getters
|
||||
bool isPaused() const {return !this->isRunning();}
|
||||
|
||||
/**
|
||||
* @brief Checks if the capture thread is actively capturing
|
||||
* @return True if capturing (running), false if not running
|
||||
*/
|
||||
bool isCapturing() const {return this->isRunning();}
|
||||
|
||||
/**
|
||||
* @brief Checks if odometry is provided by the sensors
|
||||
* @return True if an odometry sensor is configured, false otherwise
|
||||
*/
|
||||
bool odomProvided() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the camera pointer
|
||||
* @return Pointer to the camera (null if not set). Valid until SensorCaptureThread is deleted.
|
||||
*/
|
||||
Camera * camera() {return _camera;}
|
||||
|
||||
/**
|
||||
* @brief Returns the odometry sensor pointer
|
||||
* @return Pointer to the odometry sensor (null if not set). Valid until SensorCaptureThread is deleted.
|
||||
*/
|
||||
SensorCapture * odomSensor() {return _odomSensor;}
|
||||
|
||||
/**
|
||||
* @brief Returns the lidar pointer
|
||||
* @return Pointer to the lidar (null if not set). Valid until SensorCaptureThread is deleted.
|
||||
*/
|
||||
Lidar * lidar() {return _lidar;}
|
||||
Camera * camera() {return _camera;} // return null if not set, valid until CameraThread is deleted
|
||||
SensorCapture * odomSensor() {return _odomSensor;} // return null if not set, valid until CameraThread is deleted
|
||||
Lidar * lidar() {return _lidar;} // return null if not set, valid until CameraThread is deleted
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Called once when the thread starts (before mainLoop)
|
||||
*
|
||||
* Initializes resources needed for the capture loop.
|
||||
*/
|
||||
virtual void mainLoopBegin();
|
||||
|
||||
/**
|
||||
* @brief Main capture loop executed in the thread
|
||||
*
|
||||
* Continuously captures sensor data, processes it, and posts SensorEvent events.
|
||||
* This method runs until the thread is killed.
|
||||
*/
|
||||
virtual void mainLoop();
|
||||
|
||||
/**
|
||||
* @brief Called when the thread is being killed
|
||||
*
|
||||
* Performs cleanup and ensures proper shutdown of sensors.
|
||||
*/
|
||||
virtual void mainLoopKill();
|
||||
|
||||
private:
|
||||
Camera * _camera; ///< Camera for image capture (owned, null if not used)
|
||||
SensorCapture * _odomSensor; ///< Odometry sensor for pose estimation (owned, null if not used)
|
||||
Lidar * _lidar; ///< Lidar for scan capture (owned, null if not used)
|
||||
Transform _extrinsicsOdomToCamera; ///< Static transform from odometry frame to camera frame
|
||||
bool _odomAsGt; ///< If true, use odometry poses as ground truth instead of odometry
|
||||
double _poseTimeOffset; ///< Time offset in seconds when querying poses
|
||||
float _poseScaleFactor; ///< Scale factor to apply to pose translation
|
||||
double _poseWaitTime; ///< Maximum time to wait for pose data
|
||||
bool _mirroring; ///< Enable horizontal image mirroring
|
||||
bool _stereoExposureCompensation; ///< Enable stereo exposure compensation
|
||||
bool _colorOnly; ///< Capture color images only (skip depth)
|
||||
int _imageDecimation; ///< Image decimation factor (1 = no decimation)
|
||||
int _histogramMethod; ///< Histogram equalization method
|
||||
bool _stereoToDepth; ///< Convert stereo images to depth using dense matching
|
||||
bool _scanDeskewing; ///< Enable lidar scan deskewing using odometry
|
||||
bool _scanFromDepth; ///< Generate scans from depth images instead of raw lidar
|
||||
int _scanDownsampleStep; ///< Decimation step for depth-to-scan conversion
|
||||
float _scanRangeMin; ///< Minimum scan range in meters (0 = no minimum)
|
||||
float _scanRangeMax; ///< Maximum scan range in meters (0 = no maximum)
|
||||
float _scanVoxelSize; ///< Voxel size for scan downsampling (0 = no voxelization)
|
||||
int _scanNormalsK; ///< K-nearest neighbors for normal estimation (0 = disabled)
|
||||
float _scanNormalsRadius; ///< Radius for normal estimation (0 = use K-nearest)
|
||||
float _scanForceGroundNormalsUp; ///< Threshold for forcing ground normals upward (0.0-1.0)
|
||||
StereoDense * _stereoDense; ///< Dense stereo matcher for stereo-to-depth conversion (owned)
|
||||
clams::DiscreteDepthDistortionModel * _distortionModel; ///< Depth distortion correction model (owned)
|
||||
bool _bilateralFiltering; ///< Enable bilateral filtering for depth images
|
||||
float _bilateralSigmaS; ///< Spatial standard deviation for bilateral filtering
|
||||
float _bilateralSigmaR; ///< Range standard deviation for bilateral filtering
|
||||
IMUFilter * _imuFilter; ///< IMU data filter (owned, null if disabled)
|
||||
bool _imuBaseFrameConversion; ///< Convert IMU data to base frame
|
||||
Feature2D * _featureDetector; ///< Feature detector for automatic feature extraction (owned, null if disabled)
|
||||
bool _depthAsMask; ///< Use depth as mask for feature detection
|
||||
Camera * _camera;
|
||||
SensorCapture * _odomSensor;
|
||||
Lidar * _lidar;
|
||||
Transform _extrinsicsOdomToCamera;
|
||||
bool _odomAsGt;
|
||||
double _poseTimeOffset;
|
||||
float _poseScaleFactor;
|
||||
double _poseWaitTime;
|
||||
bool _mirroring;
|
||||
bool _stereoExposureCompensation;
|
||||
bool _colorOnly;
|
||||
int _imageDecimation;
|
||||
int _histogramMethod;
|
||||
bool _stereoToDepth;
|
||||
bool _scanDeskewing;
|
||||
bool _scanFromDepth;
|
||||
int _scanDownsampleStep;
|
||||
float _scanRangeMin;
|
||||
float _scanRangeMax;
|
||||
float _scanVoxelSize;
|
||||
int _scanNormalsK;
|
||||
float _scanNormalsRadius;
|
||||
float _scanForceGroundNormalsUp;
|
||||
StereoDense * _stereoDense;
|
||||
clams::DiscreteDepthDistortionModel * _distortionModel;
|
||||
bool _bilateralFiltering;
|
||||
float _bilateralSigmaS;
|
||||
float _bilateralSigmaR;
|
||||
IMUFilter * _imuFilter;
|
||||
bool _imuBaseFrameConversion;
|
||||
Feature2D * _featureDetector;
|
||||
bool _depthAsMask;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use SensorCaptureThread instead
|
||||
* @brief Backward compatibility typedef
|
||||
*
|
||||
* CameraThread is deprecated. Use SensorCaptureThread instead.
|
||||
*/
|
||||
//backward compatibility
|
||||
RTABMAP_DEPRECATED typedef SensorCaptureThread CameraThread;
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,57 +34,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
/**
|
||||
* @class SensorEvent
|
||||
* @brief Event class for sensor data communication in RTAB-Map's event system
|
||||
*
|
||||
* SensorEvent is an event class that extends UEvent and is used to communicate
|
||||
* sensor data (images, depth, laser scans, etc.) through RTAB-Map's event-driven
|
||||
* architecture. It is typically posted by sensor capture threads and handled by
|
||||
* event handlers (e.g., RtabmapThread, DataRecorder, CalibrationDialog).
|
||||
*
|
||||
* The class supports two event codes:
|
||||
* - **kCodeData**: Indicates that new sensor data is available
|
||||
* - **kCodeNoMoreImages**: Indicates that the sensor stream has ended (end of file, camera disconnected, etc.)
|
||||
*
|
||||
* SensorEvent carries:
|
||||
* - **SensorData**: The actual sensor data (images, depth, scans, camera models, etc.)
|
||||
* - **SensorCaptureInfo**: Metadata about the capture (camera name, timing information, odometry pose, etc.)
|
||||
*
|
||||
* @note This class is part of RTAB-Map's event system. Handlers should check the event code
|
||||
* and class name before processing the event.
|
||||
*
|
||||
* @see UEvent
|
||||
* @see SensorData
|
||||
* @see SensorCaptureInfo
|
||||
* @see UEventsHandler
|
||||
* @see UEventsManager
|
||||
*/
|
||||
class SensorEvent :
|
||||
public UEvent
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @enum Code
|
||||
* @brief Event codes for SensorEvent
|
||||
*/
|
||||
enum Code {
|
||||
kCodeData, ///< Event code indicating new sensor data is available
|
||||
kCodeNoMoreImages ///< Event code indicating the sensor stream has ended
|
||||
kCodeData,
|
||||
kCodeNoMoreImages
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor from a single image
|
||||
*
|
||||
* Creates a SensorEvent with kCodeData containing a single image.
|
||||
* This is a convenience constructor for simple image-only sensor data.
|
||||
*
|
||||
* @param image The image to include in the sensor data
|
||||
* @param seq Sequence number (default: 0)
|
||||
* @param stamp Timestamp in seconds (default: 0.0)
|
||||
* @param cameraName Optional camera name identifier (default: empty)
|
||||
*/
|
||||
SensorEvent(const cv::Mat & image, int seq=0, double stamp = 0.0, const std::string & cameraName = std::string()) :
|
||||
UEvent(kCodeData),
|
||||
data_(image, seq, stamp)
|
||||
@@ -92,57 +51,23 @@ public:
|
||||
sensorCaptureInfo_.cameraName = cameraName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor for end-of-stream event
|
||||
*
|
||||
* Creates a SensorEvent with kCodeNoMoreImages to signal that the sensor
|
||||
* stream has ended (e.g., end of file, camera disconnected).
|
||||
*
|
||||
* @note This constructor does not include any sensor data.
|
||||
*/
|
||||
SensorEvent() :
|
||||
UEvent(kCodeNoMoreImages)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor from SensorData
|
||||
*
|
||||
* Creates a SensorEvent with kCodeData containing the provided SensorData.
|
||||
*
|
||||
* @param data The sensor data to include in the event
|
||||
*/
|
||||
SensorEvent(const SensorData & data) :
|
||||
UEvent(kCodeData),
|
||||
data_(data)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor from SensorData with camera name
|
||||
*
|
||||
* Creates a SensorEvent with kCodeData containing the provided SensorData
|
||||
* and camera name.
|
||||
*
|
||||
* @param data The sensor data to include in the event
|
||||
* @param cameraName Camera name identifier
|
||||
*/
|
||||
SensorEvent(const SensorData & data, const std::string & cameraName) :
|
||||
UEvent(kCodeData),
|
||||
data_(data)
|
||||
{
|
||||
sensorCaptureInfo_.cameraName = cameraName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor from SensorData with full capture info
|
||||
*
|
||||
* Creates a SensorEvent with kCodeData containing the provided SensorData
|
||||
* and complete sensor capture information (camera name, timing, odometry, etc.).
|
||||
*
|
||||
* @param data The sensor data to include in the event
|
||||
* @param sensorCaptureInfo Complete sensor capture information
|
||||
*/
|
||||
SensorEvent(const SensorData & data, const SensorCaptureInfo & sensorCaptureInfo) :
|
||||
UEvent(kCodeData),
|
||||
data_(data),
|
||||
@@ -150,60 +75,20 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the sensor data contained in this event
|
||||
*
|
||||
* Returns the SensorData object containing images, depth, laser scans,
|
||||
* camera models, and other sensor information.
|
||||
*
|
||||
* @return Const reference to the sensor data
|
||||
*/
|
||||
// Image or descriptors
|
||||
const SensorData & data() const {return data_;}
|
||||
|
||||
/**
|
||||
* @brief Returns the camera name
|
||||
*
|
||||
* Returns the camera name identifier from the sensor capture info.
|
||||
*
|
||||
* @return Const reference to the camera name string
|
||||
*/
|
||||
const std::string & cameraName() const {return sensorCaptureInfo_.cameraName;}
|
||||
|
||||
/**
|
||||
* @brief Returns the sensor capture information
|
||||
*
|
||||
* Returns the complete sensor capture information including camera name,
|
||||
* timing information, odometry pose, covariance, and other metadata.
|
||||
*
|
||||
* @return Const reference to the sensor capture info
|
||||
*/
|
||||
const SensorCaptureInfo & info() const {return sensorCaptureInfo_;}
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~SensorEvent() {}
|
||||
|
||||
/**
|
||||
* @brief Returns the class name for event identification
|
||||
*
|
||||
* Returns "SensorEvent" to allow event handlers to identify this event type.
|
||||
*
|
||||
* @return Class name string "SensorEvent"
|
||||
*/
|
||||
virtual std::string getClassName() const {return std::string("SensorEvent");}
|
||||
|
||||
private:
|
||||
SensorData data_; ///< Sensor data (images, depth, scans, camera models, etc.)
|
||||
SensorCaptureInfo sensorCaptureInfo_; ///< Sensor capture metadata (camera name, timing, odometry, etc.)
|
||||
SensorData data_;
|
||||
SensorCaptureInfo sensorCaptureInfo_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use SensorEvent instead
|
||||
* @brief Backward compatibility typedef
|
||||
*
|
||||
* CameraEvent is deprecated. Use SensorEvent instead.
|
||||
*/
|
||||
//backward compatibility
|
||||
RTABMAP_DEPRECATED typedef SensorEvent CameraEvent;
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
@@ -31,11 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <pcl/point_types.h>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/features2d/features2d.hpp>
|
||||
#else
|
||||
#include <opencv2/features.hpp>
|
||||
#endif
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
#include <map>
|
||||
#include <list>
|
||||
@@ -49,66 +45,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
/**
|
||||
* @class Signature
|
||||
* @brief Represents a node in RTAB-Map's pose graph
|
||||
*
|
||||
* The Signature class is a fundamental building block of RTAB-Map's graph-based
|
||||
* mapping system. Each signature represents a unique location or pose in the map
|
||||
* and contains:
|
||||
*
|
||||
* - **Visual words**: Bag-of-words representation using visual features (keypoints,
|
||||
* descriptors, 3D points) for place recognition
|
||||
* - **Links**: Connections to other signatures (neighbors, loop closures, landmarks)
|
||||
* - **Pose information**: Current pose, ground truth pose, velocity
|
||||
* - **Sensor data**: Images, depth, laser scans, etc. captured at this location
|
||||
* - **Metadata**: ID, map ID, timestamp, label, weight, modification flags
|
||||
*
|
||||
* Signatures are used for:
|
||||
* - **Place recognition**: Comparing signatures to detect loop closures
|
||||
* - **Graph construction**: Building the pose graph through links
|
||||
* - **Localization**: Matching current observations to stored signatures
|
||||
* - **Mapping**: Storing sensor data, visual features and local occupancy grid for each location
|
||||
*
|
||||
* The class tracks modification state to optimize database updates, and provides
|
||||
* methods to manage visual words, links, landmarks, and pose information.
|
||||
*
|
||||
* @note Signatures are typically managed by the Memory class, which handles
|
||||
* storage, retrieval, and graph operations.
|
||||
*
|
||||
* @see Link
|
||||
* @see SensorData
|
||||
* @see Memory
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Signature
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*
|
||||
* Creates an empty signature with invalid ID (0), default map ID (-1),
|
||||
* and all fields initialized to default values.
|
||||
*/
|
||||
Signature();
|
||||
|
||||
/**
|
||||
* @brief Constructor with explicit parameters
|
||||
*
|
||||
* Creates a signature with the specified parameters. The sensor data ID
|
||||
* will be automatically set to match the signature ID if not already set.
|
||||
*
|
||||
* @param id Unique signature ID (must be > 0 for valid signature)
|
||||
* @param mapId Map ID this signature belongs to (default: -1 for no map)
|
||||
* @param weight Weight/importance of this signature (default: 0)
|
||||
* @param stamp Timestamp in seconds (default: 0.0)
|
||||
* @param label Optional label/name for this signature (default: empty)
|
||||
* @param pose Current pose of the signature (default: identity null transform)
|
||||
* @param groundTruthPose Ground truth pose for evaluation (default: null transform)
|
||||
* @param sensorData Sensor data captured at this location (default: empty)
|
||||
*
|
||||
* @note The signature is marked as modified and not saved by default.
|
||||
*/
|
||||
Signature(int id,
|
||||
int mapId = -1,
|
||||
int weight = 0,
|
||||
@@ -117,430 +58,67 @@ public:
|
||||
const Transform & pose = Transform(),
|
||||
const Transform & groundTruthPose = Transform(),
|
||||
const SensorData & sensorData = SensorData());
|
||||
|
||||
/**
|
||||
* @brief Constructor from sensor data
|
||||
*
|
||||
* Creates a signature from sensor data. The signature ID is taken from
|
||||
* the sensor data ID, and the ground truth pose is extracted from the
|
||||
* sensor data if available.
|
||||
*
|
||||
* @param data Sensor data to create signature from
|
||||
*
|
||||
* @note The signature pose is initialized to identity transform.
|
||||
*/
|
||||
Signature(const SensorData & data);
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~Signature();
|
||||
|
||||
/**
|
||||
* @brief Compares this signature to another signature
|
||||
*
|
||||
* Computes a similarity score between this signature and another signature
|
||||
* based on their visual words. The comparison uses bag-of-words matching
|
||||
* to determine how similar the two locations are.
|
||||
*
|
||||
* @param signature The signature to compare against
|
||||
* @return Similarity score between 0.0 and 1.0, where 1.0 means 100% similarity
|
||||
*
|
||||
* @note This method is used for loop closure detection and place recognition.
|
||||
* Must return a value between >=0 and <=1 (1 means 100% similarity).
|
||||
*/
|
||||
float compareTo(const Signature & signature) const;
|
||||
|
||||
/**
|
||||
* @brief Checks if this signature is considered "bad"
|
||||
*
|
||||
* A signature is considered bad if it has no valid visual words (all words
|
||||
* are invalid or the signature has no words at all). Bad signatures cannot
|
||||
* be used for place recognition.
|
||||
*
|
||||
* @return True if the signature has no valid visual words, false otherwise
|
||||
*/
|
||||
bool isBadSignature() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the signature ID
|
||||
* @return The unique signature ID (0 if invalid/uninitialized)
|
||||
*/
|
||||
int id() const {return _id;}
|
||||
|
||||
/**
|
||||
* @brief Returns the map ID
|
||||
* @return The map ID this signature belongs to (-1 if no map assigned)
|
||||
*/
|
||||
int mapId() const {return _mapId;}
|
||||
|
||||
/**
|
||||
* @brief Sets the weight/importance of this signature
|
||||
*
|
||||
* The weight represents the importance or priority of this signature,
|
||||
* typically set by Rehearsal mechanism (see Memory::rehearsal).
|
||||
* Higher weights may be used to prioritize certain signatures during
|
||||
* memory management or loop closure detection.
|
||||
*
|
||||
* @param weight The weight value
|
||||
*
|
||||
* @note Marks the signature as modified if the weight changes. A signature already
|
||||
* flagged as modified stays modified.
|
||||
*/
|
||||
void setWeight(int weight) {_modified=_modified || _weight!=weight;_weight = weight;}
|
||||
|
||||
/**
|
||||
* @brief Returns the weight/importance of this signature
|
||||
* @return The weight value
|
||||
*/
|
||||
void setWeight(int weight) {_modified=_weight!=weight;_weight = weight;}
|
||||
int getWeight() const {return _weight;}
|
||||
|
||||
/**
|
||||
* @brief Sets a label/name for this signature
|
||||
*
|
||||
* Labels are optional text identifiers that can be used to tag or
|
||||
* categorize signatures (e.g., "room1", "corridor", "entrance").
|
||||
*
|
||||
* @param label The label string
|
||||
*
|
||||
* @note Marks the signature as modified if the label changes. A signature already
|
||||
* flagged as modified stays modified.
|
||||
*/
|
||||
void setLabel(const std::string & label) {_modified=_modified || _label.compare(label)!=0;_label = label;}
|
||||
|
||||
/**
|
||||
* @brief Returns the label/name of this signature
|
||||
* @return The label string (empty if not set)
|
||||
*/
|
||||
void setLabel(const std::string & label) {_modified=_label.compare(label)!=0;_label = label;}
|
||||
const std::string & getLabel() const {return _label;}
|
||||
|
||||
/**
|
||||
* @brief Returns the timestamp of this signature
|
||||
* @return Timestamp in seconds (typically seconds since epoch)
|
||||
*/
|
||||
double getStamp() const {return _stamp;}
|
||||
|
||||
/**
|
||||
* @brief Adds multiple links from a list
|
||||
*
|
||||
* Adds all links from the provided list to this signature's link collection.
|
||||
*
|
||||
* @param links List of links to add
|
||||
*
|
||||
* @note Each link must have `from()` equal to this signature's ID.
|
||||
* @note Automatically marks links as modified.
|
||||
* @see addLink()
|
||||
*/
|
||||
void addLinks(const std::list<Link> & links);
|
||||
|
||||
/**
|
||||
* @brief Adds multiple links from a map
|
||||
*
|
||||
* Adds all links from the provided map to this signature's link collection.
|
||||
*
|
||||
* @param links Map of target IDs to links
|
||||
*
|
||||
* @note Each link must have `from()` equal to this signature's ID.
|
||||
* @note The map's keys are the target IDs (`to()` values of the links).
|
||||
* @note Automatically marks links as modified.
|
||||
* @see addLink()
|
||||
*/
|
||||
void addLinks(const std::map<int, Link> & links);
|
||||
|
||||
/**
|
||||
* @brief Adds a single link to this signature
|
||||
*
|
||||
* Adds a link connecting this signature to another signature. The link
|
||||
* represents a constraint in the pose graph (e.g., neighbor, loop closure).
|
||||
*
|
||||
* @param link The link to add
|
||||
*
|
||||
* @note The link's `from()` must equal this signature's ID.
|
||||
* @note The link's `to()` must be different from this signature's ID
|
||||
* (except for self-referring links like pose priors or gravity).
|
||||
* @note Duplicate links to the same target are not allowed (assertion failure).
|
||||
* @note Automatically marks links as modified.
|
||||
*/
|
||||
void addLink(const Link & link);
|
||||
|
||||
/**
|
||||
* @brief Checks if this signature has a link to a target signature
|
||||
*
|
||||
* @param idTo Target signature ID (0 to check for any link of the specified type)
|
||||
* @param type Link type to check for (default: Link::kUndef to check any type)
|
||||
* @return True if a matching link exists, false otherwise
|
||||
*/
|
||||
bool hasLink(int idTo, Link::Type type = Link::kUndef) const;
|
||||
|
||||
/**
|
||||
* @brief Changes all link target IDs from one value to another
|
||||
*
|
||||
* Updates all links that point to `idFrom` to point to `idTo` instead.
|
||||
* This is useful when merging or reorganizing the graph.
|
||||
*
|
||||
* @param idFrom Original target ID
|
||||
* @param idTo New target ID
|
||||
*
|
||||
* @note Automatically marks links as modified.
|
||||
*/
|
||||
void changeLinkIds(int idFrom, int idTo);
|
||||
|
||||
/**
|
||||
* @brief Removes all links from this signature
|
||||
*
|
||||
* @param keepSelfReferringLinks If true, preserves self-referring links
|
||||
* (pose priors, gravity) that have from() == to()
|
||||
*
|
||||
* @note Automatically marks links as modified.
|
||||
*/
|
||||
void removeLinks(bool keepSelfReferringLinks = false);
|
||||
|
||||
/**
|
||||
* @brief Removes a specific link to a target signature
|
||||
*
|
||||
* @param idTo Target signature ID of the link to remove
|
||||
*
|
||||
* @note Automatically marks links as modified.
|
||||
*/
|
||||
void removeLink(int idTo);
|
||||
|
||||
/**
|
||||
* @brief Removes all virtual links from this signature
|
||||
*
|
||||
* Virtual links are links of type Link::kVirtualClosure.
|
||||
*
|
||||
* @note Automatically marks links as modified.
|
||||
*/
|
||||
void removeVirtualLinks();
|
||||
|
||||
/**
|
||||
* @brief Adds a landmark observation to this signature
|
||||
*
|
||||
* Landmarks are persistent features in the environment that can be observed
|
||||
* from multiple locations. This method adds a link representing the observation
|
||||
* of a landmark from this signature's location.
|
||||
*
|
||||
* @param landmark Link representing the landmark observation
|
||||
*
|
||||
* @note Landmark IDs are typically negative to distinguish them from regular nodes.
|
||||
*/
|
||||
void addLandmark(const Link & landmark);
|
||||
|
||||
/**
|
||||
* @brief Returns all landmark observations
|
||||
* @return Const reference to the map of landmark IDs to landmark links
|
||||
*/
|
||||
const std::map<int, Link> & getLandmarks() const {return _landmarks;}
|
||||
|
||||
/**
|
||||
* @brief Removes all landmark observations
|
||||
*/
|
||||
void removeLandmarks();
|
||||
|
||||
/**
|
||||
* @brief Removes a specific landmark observation
|
||||
* @param landmarkId The landmark ID to remove
|
||||
*/
|
||||
void removeLandmark(int landmarkId);
|
||||
|
||||
/**
|
||||
* @brief Sets whether this signature has been saved to the database
|
||||
* @param saved True if saved, false otherwise
|
||||
*/
|
||||
void setSaved(bool saved) {_saved = saved;}
|
||||
|
||||
/**
|
||||
* @brief Sets the modification flag for this signature
|
||||
*
|
||||
* Marks the signature as modified (or unmodified). When set to true,
|
||||
* also marks links as modified.
|
||||
*
|
||||
* @param modified True if modified, false otherwise
|
||||
*/
|
||||
void setModified(bool modified) {_modified = modified; _linksModified = modified;}
|
||||
|
||||
/**
|
||||
* @brief Returns all links from this signature
|
||||
* @return Const reference to the multimap of target IDs to links
|
||||
*/
|
||||
const std::multimap<int, Link> & getLinks() const {return _links;}
|
||||
|
||||
/**
|
||||
* @brief Checks if this signature has been saved to the database
|
||||
* @return True if saved, false otherwise
|
||||
*/
|
||||
bool isSaved() const {return _saved;}
|
||||
|
||||
/**
|
||||
* @brief Checks if this signature has been modified
|
||||
*
|
||||
* Returns true if either the signature data or links have been modified.
|
||||
*
|
||||
* @return True if modified, false otherwise
|
||||
*/
|
||||
bool isModified() const {return _modified || _linksModified;}
|
||||
|
||||
/**
|
||||
* @brief Checks if the links have been modified
|
||||
* @return True if links are modified, false otherwise
|
||||
*/
|
||||
bool isLinksModified() const {return _linksModified;}
|
||||
|
||||
// Visual words management
|
||||
|
||||
/**
|
||||
* @brief Removes all visual words from this signature
|
||||
*
|
||||
* Clears all visual words, keypoints, 3D points, and descriptors.
|
||||
* The signature will be disabled after this operation.
|
||||
*/
|
||||
//visual words stuff
|
||||
void removeAllWords();
|
||||
|
||||
/**
|
||||
* @brief Changes visual word references from one word ID to another
|
||||
*
|
||||
* Updates all occurrences of `oldWordId` to `activeWordId` in the visual words.
|
||||
* This is used when words are merged or reorganized in the dictionary.
|
||||
*
|
||||
* @param oldWordId The original word ID to replace
|
||||
* @param activeWordId The new word ID to use
|
||||
*
|
||||
* @note Tracks word ID changes in `_wordsChanged` for reference.
|
||||
*/
|
||||
void changeWordsRef(int oldWordId, int activeWordId);
|
||||
|
||||
/**
|
||||
* @brief Sets all visual words for this signature
|
||||
*
|
||||
* Sets the complete visual word representation including word IDs, keypoints,
|
||||
* 3D points, and descriptors. All arrays must have matching sizes.
|
||||
*
|
||||
* @param words Multimap of word IDs to keypoint indices (allows duplicate words). The keypoint indices match the keypoints, points and descriptors.
|
||||
* @param keypoints Vector of 2D keypoints in image coordinates. The keypoints must be in the same order as the words.
|
||||
* @param words3 Vector of 3D points in base_link frame (with localTransform applied). The points must be in the same order as the words.
|
||||
* @param descriptors Feature descriptors matrix (one row per word). The descriptors must be in the same order as the words.
|
||||
*
|
||||
* @note The signature is disabled after setting words (must be explicitly enabled).
|
||||
* @note Invalid words (ID <= 0) are counted in `_invalidWordsCount`.
|
||||
* @note All arrays must have the same size (number of words).
|
||||
*/
|
||||
void setWords(const std::multimap<int, int> & words, const std::vector<cv::KeyPoint> & keypoints, const std::vector<cv::Point3f> & words3, const cv::Mat & descriptors);
|
||||
|
||||
/**
|
||||
* @brief Checks if this signature is enabled
|
||||
*
|
||||
* Enabled signatures can be used for place recognition and loop closure detection.
|
||||
* More explicitly, it means that the signature is registered to the VWDictionary (see Memory::enableWordsRef).
|
||||
*
|
||||
* @return True if enabled, false otherwise
|
||||
*/
|
||||
bool isEnabled() const {return _enabled;}
|
||||
|
||||
/**
|
||||
* @brief Sets whether this signature is enabled
|
||||
* @param enabled True to enable, false to disable
|
||||
*/
|
||||
void setEnabled(bool enabled) {_enabled = enabled;}
|
||||
|
||||
/**
|
||||
* @brief Returns the visual words map
|
||||
*
|
||||
* Returns a multimap of word IDs to keypoint indices. The multimap allows
|
||||
* duplicate word IDs (a word can appear multiple times in the signature).
|
||||
*
|
||||
* @return Const reference to the words multimap
|
||||
*/
|
||||
const std::multimap<int, int> & getWords() const {return _words;}
|
||||
|
||||
/**
|
||||
* @brief Returns the keypoints associated with visual words
|
||||
* @return Const reference to the vector of 2D keypoints
|
||||
*/
|
||||
const std::vector<cv::KeyPoint> & getWordsKpts() const {return _wordsKpts;}
|
||||
|
||||
/**
|
||||
* @brief Returns the count of invalid visual words
|
||||
*
|
||||
* Invalid words are those with ID <= 0. These words cannot be used
|
||||
* for place recognition. However, they are still used for transform
|
||||
* estimation after loop closures are detected with the valid words.
|
||||
*
|
||||
* @return Number of invalid words
|
||||
*/
|
||||
int getInvalidWordsCount() const {return _invalidWordsCount;}
|
||||
|
||||
/**
|
||||
* @brief Returns the word ID change mapping
|
||||
*
|
||||
* Returns a map of old word IDs to new word IDs, tracking changes
|
||||
* made through `changeWordsRef()`.
|
||||
*
|
||||
* @return Const reference to the word ID change map
|
||||
*/
|
||||
const std::map<int, int> & getWordsChanged() const {return _wordsChanged;}
|
||||
|
||||
/**
|
||||
* @brief Returns the feature descriptors for visual words
|
||||
* @return Const reference to the descriptors matrix (one row per word)
|
||||
*/
|
||||
const cv::Mat & getWordsDescriptors() const {return _wordsDescriptors;}
|
||||
|
||||
/**
|
||||
* @brief Sets the feature descriptors for visual words
|
||||
*
|
||||
* Updates the descriptors matrix. The number of rows must match
|
||||
* the number of visual words.
|
||||
*
|
||||
* @param descriptors Descriptors matrix (one row per word)
|
||||
*/
|
||||
void setWordsDescriptors(const cv::Mat & descriptors);
|
||||
|
||||
// Pose and metric information
|
||||
|
||||
/**
|
||||
* @brief Sets the pose of this signature
|
||||
*
|
||||
* The pose represents the position and orientation of this signature
|
||||
* in the odometry coordinate frame. To get the pose in map coordinate frame,
|
||||
* use the map correction transform computed by graph optimization and apply it to this pose.
|
||||
*
|
||||
* @param pose The transform representing the pose in the odometry coordinate frame.
|
||||
*
|
||||
* @code
|
||||
* // Get pose in map coordinate frame
|
||||
* Statistics stats;
|
||||
* Transform poseInMapFrame = stats.mapCorrection() * signature.getPose();
|
||||
* // poseInMapFrame is the pose in map coordinate frame
|
||||
* // signature.getPose() returns the pose in odometry coordinate frame
|
||||
* @endcode
|
||||
*
|
||||
* @see Statistics::mapCorrection()
|
||||
* @see getPose()
|
||||
*/
|
||||
//metric stuff
|
||||
void setPose(const Transform & pose) {_pose = pose;}
|
||||
|
||||
/**
|
||||
* @brief Sets the ground truth pose for evaluation
|
||||
*
|
||||
* Ground truth poses are used for evaluating localization accuracy
|
||||
* and comparing against the estimated pose.
|
||||
*
|
||||
* @param pose The ground truth transform
|
||||
*/
|
||||
void setGroundTruthPose(const Transform & pose) {_groundTruthPose = pose;}
|
||||
|
||||
/**
|
||||
* @brief Sets the velocity of this signature
|
||||
*
|
||||
* Sets the 6DOF velocity (linear and angular) at the time this signature
|
||||
* was captured.
|
||||
*
|
||||
* @param vx Linear velocity in x direction (m/s)
|
||||
* @param vy Linear velocity in y direction (m/s)
|
||||
* @param vz Linear velocity in z direction (m/s)
|
||||
* @param vroll Angular velocity around x axis (rad/s)
|
||||
* @param vpitch Angular velocity around y axis (rad/s)
|
||||
* @param vyaw Angular velocity around z axis (rad/s)
|
||||
*/
|
||||
void setVelocity(float vx, float vy, float vz, float vroll, float vpitch, float vyaw) {
|
||||
_velocity = std::vector<float>(6,0);
|
||||
_velocity[0]=vx;
|
||||
@@ -551,100 +129,45 @@ public:
|
||||
_velocity[5]=vyaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the 3D points of visual words
|
||||
*
|
||||
* Returns the 3D coordinates of visual words in the base_link frame
|
||||
* (with localTransform applied).
|
||||
*
|
||||
* @return Const reference to the vector of 3D points
|
||||
*/
|
||||
const std::vector<cv::Point3f> & getWords3() const {return _words3;}
|
||||
|
||||
/**
|
||||
* @brief Returns the pose of this signature
|
||||
* @return Const reference to the pose transform in the odometry coordinate frame.
|
||||
* @see setPose() for an example of how to get the pose in map coordinate frame.
|
||||
*/
|
||||
const Transform & getPose() const {return _pose;}
|
||||
|
||||
/**
|
||||
* @brief Gets the pose covariance matrix
|
||||
*
|
||||
* Gets the covariance matrix representing the uncertainty
|
||||
* between the current pose and the previous pose (e.g, from the odometry link with the previous signature).
|
||||
*
|
||||
* @return 6x6 covariance matrix (3x3 for translation, 3x3 for rotation)
|
||||
*/
|
||||
cv::Mat getPoseCovariance() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the ground truth pose
|
||||
* @return Const reference to the ground truth transform
|
||||
*/
|
||||
const Transform & getGroundTruthPose() const {return _groundTruthPose;}
|
||||
|
||||
/**
|
||||
* @brief Returns the velocity of this signature
|
||||
* @return Const reference to the velocity vector [vx, vy, vz, vroll, vpitch, vyaw]
|
||||
*/
|
||||
const std::vector<float> & getVelocity() const {return _velocity;}
|
||||
|
||||
/**
|
||||
* @brief Returns mutable access to the sensor data
|
||||
* @return Reference to the sensor data
|
||||
*/
|
||||
SensorData & sensorData() {return _sensorData;}
|
||||
|
||||
/**
|
||||
* @brief Returns const access to the sensor data
|
||||
* @return Const reference to the sensor data
|
||||
*/
|
||||
const SensorData & sensorData() const {return _sensorData;}
|
||||
|
||||
/**
|
||||
* @brief Computes the memory usage of this signature
|
||||
*
|
||||
* Calculates the approximate memory footprint of this signature, including
|
||||
* visual words, links, sensor data, and other internal structures.
|
||||
*
|
||||
* @param withSensorData If true, includes sensor data in the calculation
|
||||
* @return Memory usage in bytes
|
||||
*/
|
||||
unsigned long getMemoryUsed(bool withSensorData=true) const;
|
||||
unsigned long getMemoryUsed(bool withSensorData=true) const; // Return memory usage in Bytes
|
||||
|
||||
private:
|
||||
int _id; ///< Unique signature ID (0 if invalid)
|
||||
int _mapId; ///< Map ID this signature belongs to (-1 if no map)
|
||||
double _stamp; ///< Timestamp in seconds
|
||||
std::multimap<int, Link> _links; ///< Links to other signatures (target ID -> Link, allows multiple links per target)
|
||||
std::map<int, Link> _landmarks; ///< Landmark observations (landmark ID -> Link)
|
||||
int _weight; ///< Weight/importance of this signature
|
||||
std::string _label; ///< Optional label/name for this signature
|
||||
bool _saved; ///< Flag indicating if signature is saved to database
|
||||
bool _modified; ///< Flag indicating if signature data has been modified
|
||||
bool _linksModified; ///< Flag indicating if links have been modified (optimization for database updates)
|
||||
int _id;
|
||||
int _mapId;
|
||||
double _stamp;
|
||||
std::multimap<int, Link> _links; // id, transform
|
||||
std::map<int, Link> _landmarks;
|
||||
int _weight;
|
||||
std::string _label;
|
||||
bool _saved; // If it's saved to bd
|
||||
bool _modified;
|
||||
bool _linksModified; // Optimization when updating signatures in database
|
||||
|
||||
/**
|
||||
* @brief Visual words representation
|
||||
*
|
||||
* Contains all visual words for this signature. Words can be duplicates
|
||||
* (a word can appear multiple times in the signature). Words match with
|
||||
* the keypoints and descriptors arrays.
|
||||
*/
|
||||
std::multimap<int, int> _words; ///< Visual words: word ID -> keypoint index (multimap allows duplicates)
|
||||
std::vector<cv::KeyPoint> _wordsKpts; ///< 2D keypoints in image coordinates
|
||||
std::vector<cv::Point3f> _words3; ///< 3D points in base_link frame (with localTransform applied)
|
||||
cv::Mat _wordsDescriptors; ///< Feature descriptors matrix (one row per word)
|
||||
std::map<int, int> _wordsChanged; ///< Word ID change tracking: old ID -> new ID
|
||||
bool _enabled; ///< Flag indicating if signature is enabled for place recognition
|
||||
int _invalidWordsCount; ///< Count of invalid words (ID <= 0)
|
||||
// Contains all words (Some can be duplicates -> if a word appears 2
|
||||
// times in the signature, it will be 2 times in this list)
|
||||
// Words match with the CvSeq keypoints and descriptors
|
||||
std::multimap<int, int> _words; // word <id, keypoint index>
|
||||
std::vector<cv::KeyPoint> _wordsKpts;
|
||||
std::vector<cv::Point3f> _words3; // in base_link frame (localTransform applied))
|
||||
cv::Mat _wordsDescriptors;
|
||||
std::map<int, int> _wordsChanged; // <oldId, newId>
|
||||
bool _enabled;
|
||||
int _invalidWordsCount;
|
||||
|
||||
Transform _pose; ///< Current pose in map coordinate frame
|
||||
Transform _groundTruthPose; ///< Ground truth pose for evaluation
|
||||
std::vector<float> _velocity; ///< 6DOF velocity [vx, vy, vz, vroll, vpitch, vyaw]
|
||||
Transform _pose;
|
||||
Transform _groundTruthPose;
|
||||
std::vector<float> _velocity;
|
||||
|
||||
SensorData _sensorData; ///< Sensor data captured at this location (images, depth, scans, etc.)
|
||||
SensorData _sensorData;
|
||||
};
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
@@ -31,41 +31,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "rtabmap/core/rtabmap_core_export.h" // DLL export/import defines
|
||||
|
||||
#include <opencv2/core/core.hpp>
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/features2d/features2d.hpp>
|
||||
#else
|
||||
#include <opencv2/features.hpp>
|
||||
#endif
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <rtabmap/core/Signature.h>
|
||||
#include <rtabmap/core/Link.h>
|
||||
#include <rtabmap/utilite/UStl.h>
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @def RTABMAP_STATS(PREFIX, NAME, UNIT)
|
||||
* @brief Macro to define a statistic with automatic name generation and default initialization
|
||||
*
|
||||
* This macro generates:
|
||||
* - A static method `k##PREFIX##NAME()` that returns the statistic name in the format "PREFIX/NAME/UNIT"
|
||||
* - A dummy class that initializes the default data map with the statistic name and a default value of 0.0
|
||||
*
|
||||
* @param PREFIX The group/category prefix (e.g., "Loop", "Timing", "Memory")
|
||||
* @param NAME The statistic name (e.g., "Id", "Total", "Working_memory_size")
|
||||
* @param UNIT The unit of measurement (e.g., "ms", "m", "deg", or empty string "")
|
||||
*
|
||||
* @note The generated static method can be used to get the standardized statistic name:
|
||||
* @code
|
||||
* std::string statName = Statistics::kTimingTotal(); // Returns "Timing/Total/ms"
|
||||
* statistics.addStatistic(statName, 500.0f);
|
||||
* @endcode
|
||||
*
|
||||
* @note The default value (0.0) is automatically added to the default data map when a Statistics
|
||||
* object is first constructed.
|
||||
*/
|
||||
#define RTABMAP_STATS(PREFIX, NAME, UNIT) \
|
||||
public: \
|
||||
static std::string k##PREFIX##NAME() {return #PREFIX "/" #NAME "/" #UNIT;} \
|
||||
@@ -76,34 +50,6 @@ namespace rtabmap {
|
||||
}; \
|
||||
Dummy##PREFIX##NAME dummy##PREFIX##NAME
|
||||
|
||||
/**
|
||||
* @class Statistics
|
||||
* @brief Collects and manages runtime statistics for RTAB-Map
|
||||
*
|
||||
* The Statistics class provides a comprehensive system for collecting, storing, and managing
|
||||
* various runtime statistics about RTAB-Map's operation. Statistics are organized into groups
|
||||
* such as:
|
||||
* - **Loop**: Loop closure detection, hypothesis validation, optimization errors
|
||||
* - **Proximity**: Proximity-based detection statistics
|
||||
* - **Memory**: Working memory size, database usage, signature management
|
||||
* - **Timing**: Performance measurements for various operations
|
||||
* - **Keypoint**: Visual word dictionary and feature statistics
|
||||
* - **Gt**: Ground truth comparison statistics
|
||||
*
|
||||
* Statistics are stored in a map with keys in the format "Group/Name/Unit" (e.g., "Timing/Total time/ms").
|
||||
* This hierarchical naming allows for easy categorization and plotting.
|
||||
*
|
||||
* The class supports two modes:
|
||||
* - **Basic mode** (`extended() == false`): Only stores loop closure and last signature ID fields
|
||||
* - **Extended mode** (`extended() == true`): Stores all available statistics including poses,
|
||||
* constraints, likelihoods, posteriors, and detailed timing information
|
||||
*
|
||||
* Statistics can be serialized to/from strings for storage in databases or transmission over networks.
|
||||
*
|
||||
* @note Statistics are typically created by RTAB-Map's core components (Rtabmap, Memory, etc.)
|
||||
* and can be retrieved for analysis, visualization, or debugging purposes.
|
||||
*
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Statistics
|
||||
{
|
||||
RTABMAP_STATS(Loop, Id,); // Combined loop or proximity detection
|
||||
@@ -191,9 +137,7 @@ class RTABMAP_CORE_EXPORT Statistics
|
||||
RTABMAP_STATS(NeighborLinkRefining, Pts,);
|
||||
|
||||
RTABMAP_STATS(Memory, Working_memory_size,);
|
||||
RTABMAP_STATS(Memory, Working_memory_inter_size,);
|
||||
RTABMAP_STATS(Memory, Short_time_memory_size,);
|
||||
RTABMAP_STATS(Memory, Short_time_memory_inter_size,);
|
||||
RTABMAP_STATS(Memory, Database_memory_used, MB);
|
||||
RTABMAP_STATS(Memory, Signatures_removed,);
|
||||
RTABMAP_STATS(Memory, Immunized_globally,);
|
||||
@@ -281,521 +225,123 @@ class RTABMAP_CORE_EXPORT Statistics
|
||||
RTABMAP_STATS(Gt, Localization_angular_error, deg);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the default statistics data map
|
||||
*
|
||||
* Returns a map containing all predefined statistics with their default values (0.0).
|
||||
* This map is initialized when the first Statistics object is constructed.
|
||||
*
|
||||
* @return Const reference to the default statistics data map
|
||||
*/
|
||||
static const std::map<std::string, float> & defaultData();
|
||||
|
||||
/**
|
||||
* @brief Serializes a statistics data map to a string
|
||||
*
|
||||
* Converts a statistics data map into a serialized string format suitable for storage
|
||||
* or transmission. The format is: "key1:value1;key2:value2;..." where values are
|
||||
* formatted as numbers with dots (not commas) as decimal separators (independent of the system's locale).
|
||||
*
|
||||
* @param data The statistics data map to serialize
|
||||
* @return Serialized string representation of the data
|
||||
*
|
||||
* @note Empty maps result in empty strings
|
||||
* @see deserializeData()
|
||||
*/
|
||||
static std::string serializeData(const std::map<std::string, float> & data);
|
||||
|
||||
/**
|
||||
* @brief Deserializes a statistics data map from a string
|
||||
*
|
||||
* Parses a serialized statistics string back into a data map. The string format
|
||||
* should be: "key1:value1;key2:value2;..." as produced by serializeData().
|
||||
*
|
||||
* @param data The serialized string to parse
|
||||
* @return Deserialized statistics data map
|
||||
*
|
||||
* @note Invalid entries (missing colons, malformed values) are silently skipped
|
||||
* @see serializeData()
|
||||
*/
|
||||
static std::map<std::string, float> deserializeData(const std::string & data);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*
|
||||
* Creates a new Statistics object in basic mode (extended = false).
|
||||
* Initializes all ID fields to 0 or -1, and timestamp to 0.0.
|
||||
*
|
||||
* @note The first Statistics object constructed will initialize the default data map.
|
||||
*/
|
||||
Statistics();
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~Statistics();
|
||||
|
||||
/**
|
||||
* @brief Adds a statistic value to the data map
|
||||
*
|
||||
* Adds or updates a statistic in the internal data map. The name should follow
|
||||
* the format "Group/Name/Unit" (e.g., "Timing/Total time/ms").
|
||||
*
|
||||
* @param name The statistic name in the format "Group/Name/Unit"
|
||||
* @param value The statistic value to store
|
||||
*
|
||||
* @note If a statistic with the same name already exists, it will be overwritten.
|
||||
* @note Use the static methods generated by RTABMAP_STATS() to get standardized names:
|
||||
* @code
|
||||
* statistics.addStatistic(Statistics::kTimingTotal(), 500.0f);
|
||||
* @endcode
|
||||
*/
|
||||
// name format = "Grp/Name/unit"
|
||||
void addStatistic(const std::string & name, float value);
|
||||
|
||||
// setters
|
||||
|
||||
/**
|
||||
* @brief Sets whether extended statistics mode is enabled
|
||||
*
|
||||
* In basic mode (extended = false), only loop closure and last signature ID fields are filled.
|
||||
* In extended mode (extended = true), all available statistics including poses, constraints,
|
||||
* likelihoods, posteriors, and detailed timing information are stored.
|
||||
*
|
||||
* @param extended True to enable extended mode, false for basic mode
|
||||
*/
|
||||
void setExtended(bool extended) {_extended = extended;}
|
||||
|
||||
/**
|
||||
* @brief Sets the reference image ID (current/last processed signature ID)
|
||||
* @param id The signature ID
|
||||
*/
|
||||
void setRefImageId(int id) {_refImageId = id;}
|
||||
|
||||
/**
|
||||
* @brief Sets the reference image map ID
|
||||
* @param id The map ID associated with the reference image
|
||||
*/
|
||||
void setRefImageMapId(int id) {_refImageMapId = id;}
|
||||
|
||||
/**
|
||||
* @brief Sets the loop closure detection ID
|
||||
* @param id The signature ID where a loop closure was detected (0 if none)
|
||||
*/
|
||||
void setLoopClosureId(int id) {_loopClosureId = id;}
|
||||
|
||||
/**
|
||||
* @brief Sets the loop closure map ID
|
||||
* @param id The map ID associated with the loop closure
|
||||
*/
|
||||
void setLoopClosureMapId(int id) {_loopClosureMapId = id;}
|
||||
|
||||
/**
|
||||
* @brief Sets the proximity detection ID
|
||||
* @param id The signature ID where proximity was detected (0 if none)
|
||||
*/
|
||||
void setProximityDetectionId(int id) {_proximiyDetectionId = id;}
|
||||
|
||||
/**
|
||||
* @brief Sets the proximity detection map ID
|
||||
* @param id The map ID associated with the proximity detection
|
||||
*/
|
||||
void setProximityDetectionMapId(int id) {_proximiyDetectionMapId = id;}
|
||||
|
||||
/**
|
||||
* @brief Sets the timestamp for these statistics
|
||||
* @param stamp The timestamp (typically in seconds since epoch)
|
||||
*/
|
||||
void setStamp(double stamp) {_stamp = stamp;}
|
||||
|
||||
/**
|
||||
* @deprecated Use addSignatureData() instead
|
||||
* @brief Sets the last signature data (deprecated)
|
||||
*
|
||||
* This method is deprecated. Use addSignatureData() instead, which allows
|
||||
* storing multiple signatures.
|
||||
*/
|
||||
// Use addSignatureData() instead.
|
||||
RTABMAP_DEPRECATED void setLastSignatureData(const Signature & data);
|
||||
|
||||
/**
|
||||
* @brief Adds signature data to the statistics
|
||||
*
|
||||
* Adds a signature to the internal signatures data map. Multiple signatures
|
||||
* can be stored, indexed by their ID.
|
||||
*
|
||||
* @param data The signature to add
|
||||
*
|
||||
* @note If a signature with the same ID already exists, it will be overwritten.
|
||||
*/
|
||||
void addSignatureData(const Signature & data) {uInsert(_signaturesData, std::make_pair(data.id(), data));}
|
||||
|
||||
/**
|
||||
* @brief Sets all signature data at once
|
||||
* @param data Map of signature IDs to Signature objects
|
||||
*/
|
||||
void addSignatureData(const Signature & data) {_signaturesData.insert(std::make_pair(data.id(), data));}
|
||||
void setSignaturesData(const std::map<int, Signature> & data) {_signaturesData = data;}
|
||||
|
||||
/**
|
||||
* @brief Sets the pose graph (node poses)
|
||||
*
|
||||
* Sets the complete pose graph, mapping node IDs to their Transform poses.
|
||||
* Used in extended mode for visualization and analysis.
|
||||
*
|
||||
* @param poses Map of node IDs to their poses
|
||||
*/
|
||||
void setPoses(const std::map<int, Transform> & poses) {_poses = poses;}
|
||||
|
||||
/**
|
||||
* @brief Sets the constraint graph (links between nodes)
|
||||
*
|
||||
* Sets the complete constraint graph, mapping node IDs to their Link constraints.
|
||||
* Used in extended mode for visualization and analysis.
|
||||
*
|
||||
* @param constraints Multimap of node IDs to their links (a node can have multiple links)
|
||||
*/
|
||||
void setConstraints(const std::multimap<int, Link> & constraints) {_constraints = constraints;}
|
||||
|
||||
/**
|
||||
* @brief Sets the map correction transform
|
||||
*
|
||||
* The map correction transform represents the transformation from the map fixed frame
|
||||
* to the odometry fixed frame. This transform is typically updated after graph optimization
|
||||
* to transform the odometry pose in map frame.
|
||||
*
|
||||
* @param mapCorrection The pose of odometry frame in map coordinate system
|
||||
*/
|
||||
void setMapCorrection(const Transform & mapCorrection) {_mapCorrection = mapCorrection;}
|
||||
|
||||
/**
|
||||
* @brief Sets the loop closure transform
|
||||
*
|
||||
* The transform between the current pose and the loop closure pose.
|
||||
*
|
||||
* @param loopClosureTransform The loop closure transform
|
||||
*/
|
||||
void setLoopClosureTransform(const Transform & loopClosureTransform) {_loopClosureTransform = loopClosureTransform;}
|
||||
|
||||
/**
|
||||
* @brief Sets the localization covariance matrix
|
||||
*
|
||||
* The covariance matrix representing the uncertainty in the current localization estimate.
|
||||
*
|
||||
* @param covariance The covariance matrix (typically 6x6 for 3D pose)
|
||||
*/
|
||||
void setLocalizationCovariance(const cv::Mat & covariance) {_localizationCovariance = covariance;}
|
||||
|
||||
/**
|
||||
* @brief Sets node labels
|
||||
* @param labels Map of node IDs to their string labels
|
||||
*/
|
||||
void setLabels(const std::map<int, std::string> & labels) {_labels = labels;}
|
||||
|
||||
/**
|
||||
* @brief Sets node weights
|
||||
* @param weights Map of node IDs to their integer weights
|
||||
*/
|
||||
void setWeights(const std::map<int, int> & weights) {_weights = weights;}
|
||||
|
||||
/**
|
||||
* @brief Sets posterior probabilities for loop closure hypotheses
|
||||
*
|
||||
* @param posterior Map of node IDs to their posterior probabilities
|
||||
*/
|
||||
void setPosterior(const std::map<int, float> & posterior) {_posterior = posterior;}
|
||||
|
||||
/**
|
||||
* @brief Sets likelihood values for loop closure hypotheses
|
||||
*
|
||||
* Likelihood values represent how well each hypothesis matches the current observation.
|
||||
*
|
||||
* @param likelihood Map of node IDs to their likelihood values
|
||||
*/
|
||||
void setLikelihood(const std::map<int, float> & likelihood) {_likelihood = likelihood;}
|
||||
|
||||
/**
|
||||
* @brief Sets raw likelihood values (before normalization)
|
||||
* @param rawLikelihood Map of node IDs to their raw likelihood values
|
||||
*/
|
||||
void setRawLikelihood(const std::map<int, float> & rawLikelihood) {_rawLikelihood = rawLikelihood;}
|
||||
|
||||
/**
|
||||
* @brief Sets the local path (sequence of node IDs)
|
||||
*
|
||||
* The local path represents the sequence of next nodes to visit for path planning.
|
||||
*
|
||||
* @param localPath Vector of node IDs in order of next nodes to visit
|
||||
*/
|
||||
void setLocalPath(const std::vector<int> & localPath) {_localPath=localPath;}
|
||||
|
||||
/**
|
||||
* @brief Sets the current goal node ID
|
||||
* @param goal The goal node ID (0 if no goal)
|
||||
*/
|
||||
void setCurrentGoalId(int goal) {_currentGoalId=goal;}
|
||||
|
||||
/**
|
||||
* @brief Sets the reduced IDs mapping
|
||||
*
|
||||
* Maps original node IDs to reduced IDs, used for memory optimization.
|
||||
*
|
||||
* @param reducedIds Map of original IDs to reduced IDs
|
||||
*/
|
||||
void setReducedIds(const std::map<int, int> & reducedIds) {_reducedIds = reducedIds;}
|
||||
|
||||
/**
|
||||
* @brief Sets the working memory state
|
||||
*
|
||||
* The working memory state is a vector of node IDs currently in working memory.
|
||||
*
|
||||
* @param state Vector of node IDs in working memory
|
||||
*/
|
||||
void setWmState(const std::vector<int> & state) {_wmState = state;}
|
||||
|
||||
/**
|
||||
* @brief Sets odometry cache poses
|
||||
*
|
||||
* Cached odometry poses during localization mode.
|
||||
*
|
||||
* @param poses Map of node IDs to their cached odometry poses
|
||||
*/
|
||||
void setOdomCachePoses(const std::map<int, Transform> & poses) {_odomCachePoses = poses;}
|
||||
|
||||
/**
|
||||
* @brief Sets odometry cache constraints
|
||||
*
|
||||
* Cached odometry constraints (links) between nodes.
|
||||
*
|
||||
* @param constraints Multimap of node IDs to their cached odometry links
|
||||
*/
|
||||
void setOdomCacheConstraints(const std::multimap<int, Link> & constraints) {_odomCacheConstraints = constraints;}
|
||||
|
||||
// getters
|
||||
|
||||
/**
|
||||
* @brief Returns whether extended statistics mode is enabled
|
||||
* @return True if extended mode, false if basic mode
|
||||
*/
|
||||
bool extended() const {return _extended;}
|
||||
|
||||
/**
|
||||
* @brief Returns the reference image ID
|
||||
* @return The signature ID (0 if not set)
|
||||
*/
|
||||
int refImageId() const {return _refImageId;}
|
||||
|
||||
/**
|
||||
* @brief Returns the reference image map ID
|
||||
* @return The map ID (-1 if not set)
|
||||
*/
|
||||
int refImageMapId() const {return _refImageMapId;}
|
||||
|
||||
/**
|
||||
* @brief Returns the loop closure detection ID
|
||||
* @return The signature ID where loop closure was detected (0 if none)
|
||||
*/
|
||||
int loopClosureId() const {return _loopClosureId;}
|
||||
|
||||
/**
|
||||
* @brief Returns the loop closure map ID
|
||||
* @return The map ID associated with the loop closure (-1 if not set)
|
||||
*/
|
||||
int loopClosureMapId() const {return _loopClosureMapId;}
|
||||
|
||||
/**
|
||||
* @brief Returns the proximity detection ID
|
||||
* @return The signature ID where proximity was detected (0 if none)
|
||||
*/
|
||||
int proximityDetectionId() const {return _proximiyDetectionId;}
|
||||
|
||||
/**
|
||||
* @brief Returns the proximity detection map ID
|
||||
* @return The map ID associated with the proximity detection (-1 if not set)
|
||||
*/
|
||||
int proximityDetectionMapId() const {return _proximiyDetectionMapId;}
|
||||
|
||||
/**
|
||||
* @brief Returns the timestamp
|
||||
* @return The timestamp (0.0 if not set)
|
||||
*/
|
||||
double stamp() const {return _stamp;}
|
||||
|
||||
/**
|
||||
* @brief Returns the last signature data
|
||||
*
|
||||
* Returns the most recently added signature (by ID). If no signatures are stored,
|
||||
* returns a dummy empty signature.
|
||||
*
|
||||
* @return Reference to the last signature data
|
||||
*/
|
||||
const Signature & getLastSignatureData() const {return _signaturesData.empty()?_dummyEmptyData:_signaturesData.rbegin()->second;}
|
||||
|
||||
/**
|
||||
* @brief Returns all signature data
|
||||
* @return Const reference to the map of signature IDs to Signature objects
|
||||
*/
|
||||
const std::map<int, Signature> & getSignaturesData() const {return _signaturesData;}
|
||||
|
||||
/**
|
||||
* @brief Returns the pose graph
|
||||
* @return Const reference to the map of node IDs to poses
|
||||
*/
|
||||
const std::map<int, Transform> & poses() const {return _poses;}
|
||||
|
||||
/**
|
||||
* @brief Returns the constraint graph
|
||||
* @return Const reference to the multimap of node IDs to links
|
||||
*/
|
||||
const std::multimap<int, Link> & constraints() const {return _constraints;}
|
||||
|
||||
/**
|
||||
* @brief Returns the map correction transform
|
||||
*
|
||||
* Returns the transform from the map fixed frame to the odometry fixed frame.
|
||||
*
|
||||
* @return Const reference to the map correction transform
|
||||
*/
|
||||
const Transform & mapCorrection() const {return _mapCorrection;}
|
||||
|
||||
/**
|
||||
* @brief Returns the loop closure transform
|
||||
* @return Const reference to the loop closure transform
|
||||
*/
|
||||
const Transform & loopClosureTransform() const {return _loopClosureTransform;}
|
||||
|
||||
/**
|
||||
* @brief Returns the localization covariance matrix
|
||||
* @return Const reference to the covariance matrix (may be empty)
|
||||
*/
|
||||
const cv::Mat & localizationCovariance() const {return _localizationCovariance;}
|
||||
|
||||
/**
|
||||
* @brief Returns node labels
|
||||
* @return Const reference to the map of node IDs to labels
|
||||
*/
|
||||
const std::map<int, std::string> & labels() const {return _labels;}
|
||||
|
||||
/**
|
||||
* @brief Returns node weights
|
||||
* @return Const reference to the map of node IDs to weights
|
||||
*/
|
||||
const std::map<int, int> & weights() const {return _weights;}
|
||||
|
||||
/**
|
||||
* @brief Returns posterior probabilities
|
||||
* @return Const reference to the map of node IDs to posterior probabilities
|
||||
*/
|
||||
const std::map<int, float> & posterior() const {return _posterior;}
|
||||
|
||||
/**
|
||||
* @brief Returns likelihood values
|
||||
* @return Const reference to the map of node IDs to likelihood values
|
||||
*/
|
||||
const std::map<int, float> & likelihood() const {return _likelihood;}
|
||||
|
||||
/**
|
||||
* @brief Returns raw likelihood values
|
||||
* @return Const reference to the map of node IDs to raw likelihood values
|
||||
*/
|
||||
const std::map<int, float> & rawLikelihood() const {return _rawLikelihood;}
|
||||
|
||||
/**
|
||||
* @brief Returns the local path
|
||||
* @return Const reference to the vector of node IDs
|
||||
*/
|
||||
const std::vector<int> & localPath() const {return _localPath;}
|
||||
|
||||
/**
|
||||
* @brief Returns the current goal node ID
|
||||
* @return The goal node ID (0 if no goal)
|
||||
*/
|
||||
int currentGoalId() const {return _currentGoalId;}
|
||||
|
||||
/**
|
||||
* @brief Returns the reduced IDs mapping
|
||||
* @return Const reference to the map of original IDs to reduced IDs
|
||||
*/
|
||||
const std::map<int, int> & reducedIds() const {return _reducedIds;}
|
||||
|
||||
/**
|
||||
* @brief Returns the working memory state
|
||||
* @return Const reference to the vector of node IDs in working memory
|
||||
*/
|
||||
const std::vector<int> & wmState() const {return _wmState;}
|
||||
|
||||
/**
|
||||
* @brief Returns odometry cache poses
|
||||
* @return Const reference to the map of node IDs to cached odometry poses
|
||||
*/
|
||||
const std::map<int, Transform> & odomCachePoses() const {return _odomCachePoses;}
|
||||
|
||||
/**
|
||||
* @brief Returns odometry cache constraints
|
||||
* @return Const reference to the multimap of node IDs to cached odometry links
|
||||
*/
|
||||
const std::multimap<int, Link> & odomCacheConstraints() const {return _odomCacheConstraints;}
|
||||
|
||||
/**
|
||||
* @brief Returns the statistics data map
|
||||
*
|
||||
* Returns the map containing all plottable statistics in the format "Group/Name/Unit" -> value.
|
||||
* This is the main data structure for storing numeric statistics.
|
||||
*
|
||||
* @return Const reference to the statistics data map
|
||||
*
|
||||
* @note Use addStatistic() to add values to this map
|
||||
* @note Use serializeData() to convert this map to a string for storage
|
||||
*/
|
||||
const std::map<std::string, float> & data() const {return _data;}
|
||||
|
||||
private:
|
||||
bool _extended; ///< Extended mode flag: false = only loop closure and last signature ID, true = all statistics
|
||||
bool _extended; // 0 -> only loop closure and last signature ID fields are filled
|
||||
|
||||
int _refImageId; ///< Reference image ID (current/last processed signature)
|
||||
int _refImageMapId; ///< Reference image map ID
|
||||
int _loopClosureId; ///< Loop closure detection ID (0 if none)
|
||||
int _loopClosureMapId; ///< Loop closure map ID
|
||||
int _proximiyDetectionId; ///< Proximity detection ID (0 if none)
|
||||
int _proximiyDetectionMapId; ///< Proximity detection map ID
|
||||
double _stamp; ///< Timestamp for these statistics
|
||||
int _refImageId;
|
||||
int _refImageMapId;
|
||||
int _loopClosureId;
|
||||
int _loopClosureMapId;
|
||||
int _proximiyDetectionId;
|
||||
int _proximiyDetectionMapId;
|
||||
double _stamp;
|
||||
|
||||
std::map<int, Signature> _signaturesData; ///< Map of signature IDs to Signature objects
|
||||
Signature _dummyEmptyData; ///< Dummy empty signature returned when no signatures are stored
|
||||
std::map<int, Signature> _signaturesData;
|
||||
Signature _dummyEmptyData;
|
||||
|
||||
std::map<int, Transform> _poses; ///< Pose graph: node IDs to poses
|
||||
std::multimap<int, Link> _constraints; ///< Constraint graph: node IDs to links (multimap allows multiple links per node)
|
||||
Transform _mapCorrection; ///< Transform from map fixed frame to odometry fixed frame (typically updated after optimization)
|
||||
Transform _loopClosureTransform; ///< Loop closure transform
|
||||
cv::Mat _localizationCovariance; ///< Localization covariance matrix
|
||||
std::map<int, Transform> _poses;
|
||||
std::multimap<int, Link> _constraints;
|
||||
Transform _mapCorrection;
|
||||
Transform _loopClosureTransform;
|
||||
cv::Mat _localizationCovariance;
|
||||
|
||||
std::map<int, std::string> _labels; ///< Node labels
|
||||
std::map<int, int> _weights; ///< Node weights
|
||||
std::map<int, float> _posterior; ///< Posterior probabilities for loop closure hypotheses
|
||||
std::map<int, float> _likelihood; ///< Likelihood values for loop closure hypotheses
|
||||
std::map<int, float> _rawLikelihood; ///< Raw likelihood values (before normalization)
|
||||
std::map<int, std::string> _labels;
|
||||
std::map<int, int> _weights;
|
||||
std::map<int, float> _posterior;
|
||||
std::map<int, float> _likelihood;
|
||||
std::map<int, float> _rawLikelihood;
|
||||
|
||||
std::vector<int> _localPath; ///< Local path (sequence of node IDs)
|
||||
int _currentGoalId; ///< Current goal node ID (0 if no goal)
|
||||
std::vector<int> _localPath;
|
||||
int _currentGoalId;
|
||||
|
||||
std::map<int, int> _reducedIds; ///< Mapping of original IDs to reduced/compressed IDs
|
||||
std::map<int, int> _reducedIds;
|
||||
|
||||
std::vector<int> _wmState; ///< Working memory state (vector of node IDs in working memory)
|
||||
std::vector<int> _wmState;
|
||||
|
||||
std::map<int, Transform> _odomCachePoses; ///< Cached odometry poses in localization mode
|
||||
std::multimap<int, Link> _odomCacheConstraints; ///< Cached odometry constraints/links
|
||||
std::map<int, Transform> _odomCachePoses;
|
||||
std::multimap<int, Link> _odomCacheConstraints;
|
||||
|
||||
/**
|
||||
* @brief Statistics data map (plottable statistics)
|
||||
*
|
||||
* Format: {"Group/Name/Unit", value}
|
||||
* Example: {"Timing/Total time/ms", 500.0f}
|
||||
*
|
||||
* All plottable numeric statistics are stored in this map with hierarchical names
|
||||
* that allow for easy categorization and visualization.
|
||||
*/
|
||||
// Format for statistics (Plottable statistics must go in that map) :
|
||||
// {"Group/Name/Unit", value}
|
||||
// Example : {"Timing/Total time/ms", 500.0f}
|
||||
std::map<std::string, float> _data;
|
||||
|
||||
static std::map<std::string, float> _defaultData; ///< Static default data map (initialized on first Statistics construction)
|
||||
static bool _defaultDataInitialized; ///< Flag indicating if default data has been initialized
|
||||
static std::map<std::string, float> _defaultData;
|
||||
static bool _defaultDataInitialized;
|
||||
// end extended data
|
||||
};
|
||||
|
||||
|
||||
@@ -35,111 +35,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class Stereo
|
||||
* @brief Sparse stereo matching using block matching
|
||||
*
|
||||
* This class implements sparse stereo matching to find corresponding feature points
|
||||
* between stereo image pairs using block matching with a search window. Unlike dense
|
||||
* stereo matching, this class works with sparse feature points rather than computing
|
||||
* disparity for every pixel.
|
||||
*
|
||||
* The algorithm uses a pyramidal approach for efficiency, searching for correspondences
|
||||
* within a specified disparity range using either SAD (Sum of Absolute Differences) or
|
||||
* SSD (Sum of Squared Differences) as the matching cost.
|
||||
*
|
||||
* @note Input images must be grayscale (CV_8UC1).
|
||||
* @see StereoOpticalFlow for an alternative implementation using optical flow
|
||||
* @see StereoDense for dense stereo matching
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Stereo {
|
||||
public:
|
||||
/**
|
||||
* @brief Factory method to create a Stereo instance
|
||||
*
|
||||
* Creates a Stereo instance based on the stereo optical flow parameter
|
||||
* in the provided parameters map. If optical flow is enabled, creates a
|
||||
* StereoOpticalFlow instance; otherwise, creates a standard Stereo instance.
|
||||
*
|
||||
* @param parameters Parameters map containing configuration values.
|
||||
* The Parameters::kStereoOpticalFlow() parameter determines
|
||||
* which implementation to create.
|
||||
* @return Pointer to the created Stereo instance (caller owns the memory).
|
||||
* Returns StereoOpticalFlow if optical flow is enabled, otherwise Stereo.
|
||||
*/
|
||||
static Stereo * create(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* Initializes a Stereo instance with default parameter values and then
|
||||
* parses the provided parameters map to override defaults.
|
||||
*
|
||||
* @param parameters Optional parameters map containing configuration values.
|
||||
* If empty, default values are used.
|
||||
*/
|
||||
Stereo(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~Stereo() {}
|
||||
|
||||
/**
|
||||
* @brief Parse parameters from a parameters map
|
||||
*
|
||||
* Updates the algorithm's configuration based on the provided parameters map.
|
||||
* Supported parameters:
|
||||
* - Parameters::kStereoWinWidth() - Search window width
|
||||
* - Parameters::kStereoWinHeight() - Search window height
|
||||
* - Parameters::kStereoIterations() - Number of iterations
|
||||
* - Parameters::kStereoMaxLevel() - Maximum pyramid level
|
||||
* - Parameters::kStereoMinDisparity() - Minimum disparity value
|
||||
* - Parameters::kStereoMaxDisparity() - Maximum disparity value
|
||||
* - Parameters::kStereoSSD() - Use SSD instead of SAD
|
||||
*
|
||||
* @param parameters Parameters map containing configuration values
|
||||
*/
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/**
|
||||
* @brief Compute stereo correspondences using block matching
|
||||
*
|
||||
* Finds corresponding points in the right stereo image for the given
|
||||
* points in the left stereo image using block matching with a search window.
|
||||
* The algorithm uses a pyramidal approach for efficiency.
|
||||
*
|
||||
* @param leftImage Left stereo image (must be CV_8UC1 grayscale)
|
||||
* @param rightImage Right stereo image (must be CV_8UC1 grayscale)
|
||||
* @param leftCorners Input vector of feature points in the left image
|
||||
* @param status Output vector indicating which correspondences are valid (1) or invalid (0).
|
||||
* The size matches leftCorners.size().
|
||||
* @return Vector of corresponding points in the right image. The size matches leftCorners.size().
|
||||
* Invalid correspondences may have coordinates outside the image bounds.
|
||||
* @note Both input images must be grayscale (CV_8UC1).
|
||||
* @note The algorithm searches for correspondences within the disparity range
|
||||
* [minDisparity(), maxDisparity()] and uses a search window of size winSize().
|
||||
*/
|
||||
virtual std::vector<cv::Point2f> computeCorrespondences(
|
||||
const cv::Mat & leftImage,
|
||||
const cv::Mat & rightImage,
|
||||
const std::vector<cv::Point2f> & leftCorners,
|
||||
std::vector<unsigned char> & status) const;
|
||||
#ifdef HAVE_OPENCV_CUDEV
|
||||
/**
|
||||
* @brief Compute stereo correspondences using GPU (not implemented)
|
||||
*
|
||||
* GPU version of computeCorrespondences. Currently not implemented for the
|
||||
* standard Stereo class. Use StereoOpticalFlow with GPU enabled for GPU acceleration.
|
||||
*
|
||||
* @param leftImage Left stereo image on GPU (must be CV_8UC1 grayscale)
|
||||
* @param rightImage Right stereo image on GPU (must be CV_8UC1 grayscale)
|
||||
* @param leftCorners Input vector of feature points in the left image
|
||||
* @param status Output vector indicating which correspondences are valid
|
||||
* @return Empty vector (GPU support not implemented for this class)
|
||||
* @note This method always returns an empty vector and logs an error.
|
||||
*/
|
||||
virtual std::vector<cv::Point2f> computeCorrespondences(
|
||||
const cv::cuda::GpuMat & leftImage,
|
||||
const cv::cuda::GpuMat & rightImage,
|
||||
@@ -147,125 +57,30 @@ public:
|
||||
std::vector<unsigned char> & status) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get the search window size
|
||||
* @return Search window size as cv::Size(winWidth_, winHeight_)
|
||||
*/
|
||||
cv::Size winSize() const {return cv::Size(winWidth_, winHeight_);}
|
||||
|
||||
/**
|
||||
* @brief Get the number of iterations
|
||||
* @return Number of iterations for the matching algorithm
|
||||
*/
|
||||
int iterations() const {return iterations_;}
|
||||
|
||||
/**
|
||||
* @brief Get the maximum pyramid level
|
||||
* @return Maximum pyramid level used in the pyramidal approach
|
||||
*/
|
||||
int maxLevel() const {return maxLevel_;}
|
||||
|
||||
/**
|
||||
* @brief Get the minimum disparity value
|
||||
* @return Minimum disparity value to search (in pixels)
|
||||
*/
|
||||
float minDisparity() const {return minDisparity_;}
|
||||
|
||||
/**
|
||||
* @brief Get the maximum disparity value
|
||||
* @return Maximum disparity value to search (in pixels)
|
||||
*/
|
||||
float maxDisparity() const {return maxDisparity_;}
|
||||
|
||||
/**
|
||||
* @brief Check if SSD (Sum of Squared Differences) is used
|
||||
* @return true if SSD is used, false if SAD (Sum of Absolute Differences) is used
|
||||
*/
|
||||
bool winSSD() const {return winSSD_;}
|
||||
|
||||
/**
|
||||
* @brief Check if GPU acceleration is enabled
|
||||
* @return Always returns false for the base Stereo class
|
||||
*/
|
||||
virtual bool isGpuEnabled() const {return false;}
|
||||
|
||||
private:
|
||||
int winWidth_; ///< Search window width (default: from Parameters::defaultStereoWinWidth())
|
||||
int winHeight_; ///< Search window height (default: from Parameters::defaultStereoWinHeight())
|
||||
int iterations_; ///< Number of iterations for matching (default: from Parameters::defaultStereoIterations())
|
||||
int maxLevel_; ///< Maximum pyramid level (default: from Parameters::defaultStereoMaxLevel())
|
||||
float minDisparity_; ///< Minimum disparity value to search (default: from Parameters::defaultStereoMinDisparity())
|
||||
float maxDisparity_; ///< Maximum disparity value to search (default: from Parameters::defaultStereoMaxDisparity())
|
||||
bool winSSD_; ///< Use SSD instead of SAD for matching cost (default: from Parameters::defaultStereoSSD())
|
||||
int winWidth_;
|
||||
int winHeight_;
|
||||
int iterations_;
|
||||
int maxLevel_;
|
||||
float minDisparity_;
|
||||
float maxDisparity_;
|
||||
bool winSSD_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class StereoOpticalFlow
|
||||
* @brief Sparse stereo matching using optical flow
|
||||
*
|
||||
* This class implements sparse stereo matching using optical flow (Lucas-Kanade)
|
||||
* to find corresponding feature points between stereo image pairs. It extends the
|
||||
* base Stereo class with optical flow-based matching, which can be more robust
|
||||
* than simple block matching, especially for textured regions.
|
||||
*
|
||||
* The algorithm uses pyramidal Lucas-Kanade optical flow to track feature points
|
||||
* from the left image to the right image, then filters the results based on
|
||||
* disparity constraints.
|
||||
*
|
||||
* @note Input images must be grayscale (CV_8UC1).
|
||||
* @note GPU acceleration is available if RTAB-Map is built with OpenCV CUDA support.
|
||||
* @see Stereo for block matching-based implementation
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT StereoOpticalFlow : public Stereo {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*
|
||||
* Initializes a StereoOpticalFlow instance with default parameter values
|
||||
* and then parses the provided parameters map to override defaults.
|
||||
*
|
||||
* @param parameters Optional parameters map containing configuration values.
|
||||
* If empty, default values are used.
|
||||
*/
|
||||
StereoOpticalFlow(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~StereoOpticalFlow() {}
|
||||
|
||||
/**
|
||||
* @brief Parse parameters from a parameters map
|
||||
*
|
||||
* Updates the algorithm's configuration based on the provided parameters map.
|
||||
* First calls the base class parseParameters(), then parses optical flow-specific
|
||||
* parameters:
|
||||
* - Parameters::kStereoEps() - Convergence threshold for optical flow
|
||||
* - Parameters::kStereoGpu() - Enable GPU acceleration (requires OpenCV CUDA)
|
||||
*
|
||||
* @param parameters Parameters map containing configuration values
|
||||
* @note If GPU is enabled but RTAB-Map is not built with OpenCV CUDA support,
|
||||
* GPU will be automatically disabled and an error message will be logged.
|
||||
*/
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/**
|
||||
* @brief Compute stereo correspondences using optical flow
|
||||
*
|
||||
* Finds corresponding points in the right stereo image for the given
|
||||
* points in the left stereo image using pyramidal Lucas-Kanade optical flow.
|
||||
* The results are filtered based on disparity constraints (minDisparity to maxDisparity).
|
||||
*
|
||||
* @param leftImage Left stereo image (must be CV_8UC1 grayscale)
|
||||
* @param rightImage Right stereo image (must be CV_8UC1 grayscale)
|
||||
* @param leftCorners Input vector of feature points in the left image
|
||||
* @param status Output vector indicating which correspondences are valid (1) or invalid (0).
|
||||
* The size matches leftCorners.size().
|
||||
* @return Vector of corresponding points in the right image. The size matches leftCorners.size().
|
||||
* Invalid correspondences may have coordinates outside the image bounds.
|
||||
* @note Both input images must be grayscale (CV_8UC1).
|
||||
* @note If GPU is enabled, the GPU version of this method is called automatically.
|
||||
*/
|
||||
virtual std::vector<cv::Point2f> computeCorrespondences(
|
||||
const cv::Mat & leftImage,
|
||||
const cv::Mat & rightImage,
|
||||
@@ -273,21 +88,6 @@ public:
|
||||
std::vector<unsigned char> & status) const;
|
||||
|
||||
#ifdef HAVE_OPENCV_CUDEV
|
||||
/**
|
||||
* @brief Compute stereo correspondences using GPU-accelerated optical flow
|
||||
*
|
||||
* GPU-accelerated version of computeCorrespondences using CUDA-optimized
|
||||
* sparse pyramidal Lucas-Kanade optical flow. This method is automatically
|
||||
* called when GPU is enabled.
|
||||
*
|
||||
* @param leftImage Left stereo image on GPU (must be CV_8UC1 grayscale)
|
||||
* @param rightImage Right stereo image on GPU (must be CV_8UC1 grayscale)
|
||||
* @param leftCorners Input vector of feature points in the left image
|
||||
* @param status Output vector indicating which correspondences are valid
|
||||
* @return Vector of corresponding points in the right image
|
||||
* @note Requires RTAB-Map to be built with OpenCV CUDA support (HAVE_OPENCV_CUDAOPTFLOW).
|
||||
* @note The results are filtered based on disparity constraints after GPU computation.
|
||||
*/
|
||||
virtual std::vector<cv::Point2f> computeCorrespondences(
|
||||
const cv::cuda::GpuMat & leftImage,
|
||||
const cv::cuda::GpuMat & rightImage,
|
||||
@@ -295,41 +95,13 @@ public:
|
||||
std::vector<unsigned char> & status) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get the convergence threshold (epsilon)
|
||||
* @return Convergence threshold for optical flow iteration termination
|
||||
*/
|
||||
float epsilon() const {return epsilon_;}
|
||||
|
||||
/**
|
||||
* @brief Check if GPU acceleration is enabled
|
||||
*
|
||||
* Returns whether GPU acceleration is currently enabled for optical flow computation.
|
||||
* GPU acceleration requires OpenCV CUDA support to be compiled in.
|
||||
*
|
||||
* @return true if GPU is enabled and available, false otherwise
|
||||
*/
|
||||
virtual bool isGpuEnabled() const;
|
||||
|
||||
bool usingMinEigenVals() const {return useMinEigenVals_;}
|
||||
float minEigThreshold() const {return minEigThreshold_;}
|
||||
float errorThreshold() const {return errorThreshold_;}
|
||||
virtual bool isGpuEnabled() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Update status vector based on disparity constraints
|
||||
*
|
||||
* Filters the correspondence results by checking if the computed disparity
|
||||
* (leftCorners[i].x - rightCorners[i].x) falls within the valid range
|
||||
* [minDisparity(), maxDisparity()]. Points outside this range are marked
|
||||
* as invalid in the status vector.
|
||||
*
|
||||
* @param leftCorners Input feature points in the left image
|
||||
* @param rightCorners Corresponding points in the right image
|
||||
* @param status Status vector to update (1 = valid, 0 = invalid)
|
||||
* @note This method is called automatically after optical flow computation
|
||||
* to filter results based on disparity constraints.
|
||||
*/
|
||||
void updateStatus(
|
||||
const std::vector<cv::Point2f> & leftCorners,
|
||||
const std::vector<cv::Point2f> & rightCorners,
|
||||
@@ -337,11 +109,11 @@ private:
|
||||
std::vector<float> err = {}) const;
|
||||
|
||||
private:
|
||||
float epsilon_; ///< Convergence threshold for optical flow (default: from Parameters::defaultStereoEps())
|
||||
float epsilon_;
|
||||
bool useMinEigenVals_;
|
||||
float minEigThreshold_;
|
||||
float errorThreshold_;
|
||||
bool gpu_; ///< Enable GPU acceleration (default: from Parameters::defaultStereoGpu(), requires OpenCV CUDA)
|
||||
bool gpu_;
|
||||
};
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -32,63 +32,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class StereoCameraModel
|
||||
* @brief A class representing a calibrated stereo camera system.
|
||||
*
|
||||
* This class encapsulates the calibration data and operations associated with a stereo camera setup,
|
||||
* including intrinsic and extrinsic parameters for both left and right cameras, stereo rectification,
|
||||
* and methods for computing depth or disparity from stereo images.
|
||||
*
|
||||
* It relies internally on two `CameraModel` instances for the left and right cameras.
|
||||
*
|
||||
* Typical uses include:
|
||||
* - Stereo rectification
|
||||
* - Stereo disparity-to-depth conversion
|
||||
* - Saving and loading stereo camera calibration data
|
||||
* - Projecting or reprojecting points
|
||||
*
|
||||
* @see CameraModel
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT StereoCameraModel
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor. Creates an empty stereo model with default suffixes ("left", "right").
|
||||
*/
|
||||
StereoCameraModel() : leftSuffix_("left"), rightSuffix_("right") {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a StereoCameraModel from detailed intrinsic and extrinsic parameters for both cameras.
|
||||
*
|
||||
* Initializes the stereo camera model by specifying the calibration parameters for the left and right cameras,
|
||||
* along with the stereo extrinsic parameters.
|
||||
*
|
||||
* @param name Name identifier for the stereo camera.
|
||||
* @param imageSize1 Image size (width, height) of the left camera.
|
||||
* @param K1 Intrinsic camera matrix (3x3, CV_64FC1) for the left camera.
|
||||
* @param D1 Distortion coefficients for the left camera.
|
||||
* @param R1 Rectification matrix (3x3, CV_64FC1) for the left camera.
|
||||
* @param P1 Projection matrix (3x4, CV_64FC1) for the left camera.
|
||||
* @param imageSize2 Image size (width, height) of the right camera.
|
||||
* @param K2 Intrinsic camera matrix (3x3, CV_64FC1) for the right camera.
|
||||
* @param D2 Distortion coefficients for the right camera.
|
||||
* @param R2 Rectification matrix (3x3, CV_64FC1) for the right camera.
|
||||
* @param P2 Projection matrix (3x4, CV_64FC1) for the right camera.
|
||||
* @param R Rotation matrix (3x3, CV_64FC1) representing the rotation from left to right camera coordinate system.
|
||||
* Can be empty if unknown.
|
||||
* @param T Translation vector (3x1, CV_64FC1) representing the translation from left to right camera coordinate system.
|
||||
* Can be empty if unknown.
|
||||
* @param E Essential matrix (3x3, CV_64FC1) encoding the stereo camera epipolar geometry.
|
||||
* Can be empty if unknown.
|
||||
* @param F Fundamental matrix (3x3, CV_64FC1) encoding the stereo camera epipolar constraints.
|
||||
* Can be empty if unknown.
|
||||
* @param localTransform The local transform associated with the stereo camera model.
|
||||
*
|
||||
* @note All matrices must have correct sizes and types as specified.
|
||||
* The rectification and projection matrices (R1, P1, R2, P2) are used to define the stereo rectification parameters.
|
||||
* The rotation and translation (R, T) define the relative pose between the cameras.
|
||||
*/
|
||||
StereoCameraModel(
|
||||
const std::string & name,
|
||||
const cv::Size & imageSize1,
|
||||
@@ -98,29 +45,7 @@ public:
|
||||
const cv::Mat & R, const cv::Mat & T, const cv::Mat & E, const cv::Mat & F,
|
||||
const Transform & localTransform = Transform(0,0,1,0, -1,0,0,0, 0,-1,0,0));
|
||||
|
||||
/**
|
||||
* @brief Constructs a StereoCameraModel from two individual camera models and optional stereo extrinsic parameters.
|
||||
*
|
||||
* This constructor initializes the stereo camera model by assigning the provided left and right camera models.
|
||||
* If the stereo extrinsics (`R`, `T`) are provided and valid, stereo rectification will be attempted—provided both
|
||||
* cameras are valid for rectification and their image dimensions match.
|
||||
*
|
||||
* Each camera model will automatically have its name updated using the `name` parameter and default suffixes ("left", "right").
|
||||
*
|
||||
* @param name The base name for the stereo camera model.
|
||||
* @param leftCameraModel The camera model representing the left camera.
|
||||
* @param rightCameraModel The camera model representing the right camera.
|
||||
* @param R (Optional) Rotation matrix of the left camera relative to the right camera coordinate system (3x3, CV_64FC1).
|
||||
* @param T (Optional) Translation vector of the left camera relative to the right camera coordinate system (3x1, CV_64FC1).
|
||||
* @param E (Optional) Essential matrix between the two cameras (3x3, CV_64FC1).
|
||||
* @param F (Optional) Fundamental matrix between the two cameras (3x3, CV_64FC1).
|
||||
*
|
||||
* @throws UException if any of the provided matrices (`R`, `T`, `E`, `F`) are non-empty and not of the expected type/shape.
|
||||
* @throws UException if `R` and `T` are provided but the camera models are not valid for rectification.
|
||||
*
|
||||
* @note Stereo rectification is only attempted if both `R` and `T` are non-empty, the cameras are valid, and their image sizes match.
|
||||
* @see updateStereoRectification()
|
||||
*/
|
||||
// if R and T are not null, left and right camera models should be valid to be rectified.
|
||||
StereoCameraModel(
|
||||
const std::string & name,
|
||||
const CameraModel & leftCameraModel,
|
||||
@@ -129,54 +54,14 @@ public:
|
||||
const cv::Mat & T = cv::Mat(),
|
||||
const cv::Mat & E = cv::Mat(),
|
||||
const cv::Mat & F = cv::Mat());
|
||||
|
||||
/**
|
||||
* @brief Constructs a StereoCameraModel from two camera models and an extrinsic Transform between them.
|
||||
*
|
||||
* This constructor sets up a stereo camera model using the given left and right camera models along with
|
||||
* an optional 3D transform (`extrinsics`) representing the pose of the left camera relative to the right camera coordinate system.
|
||||
*
|
||||
* If a valid (non-null) transform is provided, the corresponding rotation and translation matrices are extracted
|
||||
* and stored as the stereo extrinsic parameters. Stereo rectification will be attempted if both camera models
|
||||
* are valid for rectification and their image sizes match.
|
||||
*
|
||||
* Each camera model will be renamed using the provided `name` and default suffixes ("left", "right").
|
||||
*
|
||||
* @param name Base name for the stereo camera model.
|
||||
* @param leftCameraModel Camera model for the left camera.
|
||||
* @param rightCameraModel Camera model for the right camera.
|
||||
* @param extrinsics (Optional) Transform of the left camera relative to the right camera coordinate system. If null, no extrinsics are used.
|
||||
*
|
||||
* @throws UException if `extrinsics` is not null and either camera model is not valid for rectification.
|
||||
*
|
||||
* @note Stereo rectification is performed only when `extrinsics` is valid and both camera models are rectifiable
|
||||
* with matching image dimensions.
|
||||
* @see updateStereoRectification()
|
||||
*/
|
||||
// if extrinsics transform is not null, left and right camera models should be valid to be rectified.
|
||||
StereoCameraModel(
|
||||
const std::string & name,
|
||||
const CameraModel & leftCameraModel,
|
||||
const CameraModel & rightCameraModel,
|
||||
const Transform & extrinsics);
|
||||
|
||||
/**
|
||||
* @brief Minimal constructor using focal lengths and baseline only.
|
||||
*
|
||||
* Creates a simplified stereo camera model using only the essential intrinsic parameters
|
||||
* and baseline. This constructor assumes the images are already rectified and both cameras
|
||||
* have the same intrinsic parameters.
|
||||
*
|
||||
* @param fx Focal length in x direction (pixels).
|
||||
* @param fy Focal length in y direction (pixels).
|
||||
* @param cx Principal point x coordinate (pixels).
|
||||
* @param cy Principal point y coordinate (pixels).
|
||||
* @param baseline Stereo baseline distance in meters.
|
||||
* @param localTransform Local transform from camera to robot base frame (default: optical rotation).
|
||||
* @param imageSize Image size (width, height). Optional, can be set later.
|
||||
*
|
||||
* @note This constructor creates a simplified model suitable for rectified stereo pairs.
|
||||
* For full calibration with distortion, use the constructors that accept camera matrices.
|
||||
*/
|
||||
//minimal
|
||||
StereoCameraModel(
|
||||
double fx,
|
||||
double fy,
|
||||
@@ -185,24 +70,7 @@ public:
|
||||
double baseline,
|
||||
const Transform & localTransform = Transform(0,0,1,0, -1,0,0,0, 0,-1,0,0),
|
||||
const cv::Size & imageSize = cv::Size(0,0));
|
||||
|
||||
/**
|
||||
* @brief Minimal constructor that also sets a name, required if we want to save it to a file.
|
||||
*
|
||||
* Same as the minimal constructor but also sets the camera name, which is required
|
||||
* when saving the calibration to disk.
|
||||
*
|
||||
* @param name Camera name identifier (used for saving calibration files).
|
||||
* @param fx Focal length in x direction (pixels).
|
||||
* @param fy Focal length in y direction (pixels).
|
||||
* @param cx Principal point x coordinate (pixels).
|
||||
* @param cy Principal point y coordinate (pixels).
|
||||
* @param baseline Stereo baseline distance in meters.
|
||||
* @param localTransform Local transform from camera to robot base frame (default: optical rotation).
|
||||
* @param imageSize Image size (width, height). Optional, can be set later.
|
||||
*
|
||||
* @note Use this constructor when you plan to save the calibration to a file.
|
||||
*/
|
||||
//minimal to be saved
|
||||
StereoCameraModel(
|
||||
const std::string & name,
|
||||
double fx,
|
||||
@@ -212,347 +80,68 @@ public:
|
||||
double baseline,
|
||||
const Transform & localTransform = Transform(0,0,1,0, -1,0,0,0, 0,-1,0,0),
|
||||
const cv::Size & imageSize = cv::Size(0,0));
|
||||
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*/
|
||||
virtual ~StereoCameraModel() {}
|
||||
|
||||
/**
|
||||
* @brief Returns true if both left and right models are valid for projection and the baseline is positive.
|
||||
*/
|
||||
bool isValidForProjection() const {return left_.isValidForProjection() && right_.isValidForProjection() && baseline() > 0.0;}
|
||||
|
||||
/**
|
||||
* @brief Returns true if both left and right models are valid for rectification.
|
||||
*/
|
||||
bool isValidForRectification() const {return left_.isValidForRectification() && right_.isValidForRectification();}
|
||||
|
||||
/**
|
||||
* @brief Initializes the rectification maps for both cameras.
|
||||
*/
|
||||
void initRectificationMap() {left_.initRectificationMap(); right_.initRectificationMap();}
|
||||
|
||||
/**
|
||||
* @brief Returns true if rectification maps are initialized.
|
||||
*/
|
||||
bool isRectificationMapInitialized() const {return left_.isRectificationMapInitialized() && right_.isRectificationMapInitialized();}
|
||||
|
||||
/**
|
||||
* @brief Sets the camera name and optional image suffixes for the left and right cameras.
|
||||
*
|
||||
* Updates the stereo camera model name and the suffixes used for identifying left and right
|
||||
* camera calibration files. The suffixes are used when loading/saving calibration data from disk.
|
||||
*
|
||||
* @param name Base name for the stereo camera model.
|
||||
* @param leftSuffix Suffix for the left camera (default: "left"). Used in filenames like "cameraName_left.yaml".
|
||||
* @param rightSuffix Suffix for the right camera (default: "right"). Used in filenames like "cameraName_right.yaml".
|
||||
*
|
||||
* @note The suffixes are used by load() and save() methods to construct filenames for each camera.
|
||||
*/
|
||||
void setName(const std::string & name, const std::string & leftSuffix = "left", const std::string & rightSuffix = "right");
|
||||
|
||||
/**
|
||||
* @brief Gets the camera name.
|
||||
*/
|
||||
const std::string & name() const {return name_;}
|
||||
|
||||
/**
|
||||
* @brief Sets the image size for both left and right cameras.
|
||||
*/
|
||||
// backward compatibility
|
||||
void setImageSize(const cv::Size & size) {left_.setImageSize(size); right_.setImageSize(size);}
|
||||
|
||||
/**
|
||||
* @brief Loads stereo camera calibration data from disk.
|
||||
*
|
||||
* This method loads the intrinsic parameters for both the left and right cameras from files in the specified directory,
|
||||
* using the provided camera name and internal suffixes. If `ignoreStereoTransform` is false, it also attempts to load
|
||||
* the stereo extrinsic parameters (rotation, translation, essential, and fundamental matrices) from a YAML file.
|
||||
*
|
||||
* The stereo extrinsics are expected in the file:
|
||||
* `directory/cameraName_pose.yaml`, following the ROS calibration format.
|
||||
*
|
||||
* @param directory The directory where the calibration files are located.
|
||||
* @param cameraName The base name of the stereo camera (used to derive filenames).
|
||||
* @param ignoreStereoTransform If true, skips loading stereo extrinsic parameters.
|
||||
* @param initRectificationMaps Set to false to skip building the (potentially large) left/right
|
||||
* rectification maps when rectification won't be used (saves time and memory).
|
||||
* @return true if loading is successful, false otherwise.
|
||||
*
|
||||
* @see save(), saveStereoTransform(), CameraModel::initRectificationMap()
|
||||
*/
|
||||
// Set initRectificationMaps=false to skip building the (potentially large) left/right
|
||||
// rectification maps when rectification won't be used (saves time and memory).
|
||||
bool load(const std::string & directory, const std::string & cameraName, bool ignoreStereoTransform = true, bool initRectificationMaps = true);
|
||||
|
||||
/**
|
||||
* @brief Saves stereo camera calibration data to disk.
|
||||
*
|
||||
* This method saves the intrinsic parameters of both left and right cameras to the specified directory.
|
||||
* If `ignoreStereoTransform` is false, it also saves the stereo extrinsic parameters (rotation, translation,
|
||||
* essential, and fundamental matrices) in a ROS-compatible YAML file named `cameraName_pose.yaml`.
|
||||
*
|
||||
* @param directory The directory where calibration files should be saved.
|
||||
* @param ignoreStereoTransform If true, skips saving stereo extrinsic parameters.
|
||||
* @return true if saving was successful, false otherwise.
|
||||
*
|
||||
* @see load(), saveStereoTransform()
|
||||
*/
|
||||
bool save(const std::string & directory, bool ignoreStereoTransform = true) const;
|
||||
|
||||
/**
|
||||
* @brief Saves stereo extrinsic parameters to a YAML file in ROS format.
|
||||
*
|
||||
* This method exports the stereo transform, including rotation, translation, essential, and fundamental matrices,
|
||||
* into a YAML file named `cameraName_pose.yaml` located in the specified directory.
|
||||
* The file format is compatible with ROS camera calibration tools.
|
||||
*
|
||||
* @param directory The target directory for saving the calibration file.
|
||||
* @return true if saving was successful, false if required matrices are missing or invalid.
|
||||
*
|
||||
* @warning If extrinsics (`R_`, `T_`, `E_`, `F_`) are empty or invalid, nothing will be saved and a warning is printed.
|
||||
*
|
||||
* @see load(), save()
|
||||
*/
|
||||
bool saveStereoTransform(const std::string & directory) const;
|
||||
|
||||
/**
|
||||
* @brief Serializes the stereo camera model into a byte vector.
|
||||
*
|
||||
* This method serializes the left and right camera models along with the stereo extrinsic parameters
|
||||
* (rotation matrix R_, translation vector T_, essential matrix E_, and fundamental matrix F_) into a
|
||||
* contiguous byte array. The serialization format starts with a fixed-size integer header containing
|
||||
* version info, stereo type, matrix sizes, and serialized data sizes, followed by the actual matrices and
|
||||
* serialized camera data.
|
||||
*
|
||||
* The serialized data can later be restored using the corresponding `deserialize()` method.
|
||||
*
|
||||
* @return A vector of unsigned char containing the serialized stereo camera data.
|
||||
*/
|
||||
std::vector<unsigned char> serialize() const;
|
||||
|
||||
/**
|
||||
* @brief Deserializes stereo camera model data from a byte vector.
|
||||
*
|
||||
* This method wraps the pointer-based `deserialize()` and attempts to restore the stereo camera
|
||||
* model from the given serialized byte vector.
|
||||
*
|
||||
* @param data The vector of bytes containing previously serialized stereo camera model data.
|
||||
* @return The number of bytes read from the data if successful, 0 otherwise.
|
||||
*
|
||||
* @see deserialize(const unsigned char*, unsigned int)
|
||||
*/
|
||||
unsigned int deserialize(const std::vector<unsigned char>& data);
|
||||
|
||||
/**
|
||||
* @brief Deserializes stereo camera model data from a raw byte array.
|
||||
*
|
||||
* This method reconstructs the stereo camera model from the provided serialized data buffer.
|
||||
* It expects the data format to match the one produced by `serialize()`, including a header with
|
||||
* version info, matrix sizes, and data sizes, followed by the serialized extrinsic matrices and
|
||||
* serialized left and right camera data.
|
||||
*
|
||||
* The method performs various sanity checks on data sizes and matrix dimensions and will fail if
|
||||
* the data format or sizes are inconsistent.
|
||||
*
|
||||
* @param data Pointer to the raw serialized data buffer.
|
||||
* @param dataSize Size in bytes of the data buffer.
|
||||
* @return The number of bytes consumed during deserialization if successful, or 0 on failure.
|
||||
*
|
||||
* @warning The stereo camera model is reset to a default empty state before deserialization.
|
||||
* @warning If the serialized data type is not stereo (type != 1), deserialization will fail.
|
||||
*
|
||||
* @see serialize()
|
||||
*/
|
||||
unsigned int deserialize(const unsigned char * data, unsigned int dataSize);
|
||||
|
||||
/**
|
||||
* @brief Returns the stereo baseline in meters.
|
||||
*
|
||||
* Computes the baseline distance between the left and right cameras using the projection
|
||||
* matrices. The baseline is calculated as the difference in x-translation (Tx) normalized
|
||||
* by the focal length.
|
||||
*
|
||||
* @return The baseline distance in meters. Returns 0.0 if focal lengths are invalid or zero.
|
||||
*
|
||||
* @note The baseline is a physical distance and is essential for depth computation from disparity.
|
||||
*/
|
||||
double baseline() const {return right_.fx()!=0.0 && left_.fx() != 0.0 ? left_.Tx() / left_.fx() - right_.Tx()/right_.fx():0.0;}
|
||||
|
||||
/**
|
||||
* @brief Computes the depth (Z coordinate) from a given disparity value.
|
||||
*
|
||||
* Uses the stereo camera model parameters to convert disparity to depth using the formula:
|
||||
* \f[
|
||||
* \text{depth} = \frac{\text{baseline} \times f_x}{\text{disparity} + (c_{x_{right}} - c_{x_{left}})}
|
||||
* \f]
|
||||
* where \( f_x \) is the focal length of the left camera and \( c_x \) are principal points.
|
||||
*
|
||||
* @param disparity The disparity value (difference in pixel coordinates between left and right images).
|
||||
* @return The computed depth in the same unit as the baseline (typically meters).
|
||||
* Returns 0 if disparity is zero or if the model is not valid for projection.
|
||||
*
|
||||
* @note This function requires the stereo camera to be valid for projection (i.e., calibrated and rectified).
|
||||
*/
|
||||
float computeDepth(float disparity) const;
|
||||
|
||||
/**
|
||||
* @brief Computes the disparity value from a given depth.
|
||||
*
|
||||
* Converts depth back to disparity using the inverse formula:
|
||||
* \f[
|
||||
* \text{disparity} = \frac{\text{baseline} \times f_x}{\text{depth}} - (c_{x_{right}} - c_{x_{left}})
|
||||
* \f]
|
||||
*
|
||||
* @param depth Depth value in the same unit as the baseline (typically meters).
|
||||
* @return The computed disparity in pixels.
|
||||
* Returns 0 if depth is zero or if the model is not valid for projection.
|
||||
*
|
||||
* @note This function requires the stereo camera to be valid for projection (i.e., calibrated and rectified).
|
||||
*/
|
||||
float computeDisparity(float depth) const; // m
|
||||
|
||||
/**
|
||||
* @brief Computes the disparity value from a depth given in unsigned short format (millimeters).
|
||||
*
|
||||
* Converts depth expressed as an unsigned short (in millimeters) to disparity.
|
||||
* The depth is first converted to meters before computing disparity using the formula:
|
||||
* \f[
|
||||
* \text{disparity} = \frac{\text{baseline} \times f_x}{\text{depth (meters)}} - (c_{x_{right}} - c_{x_{left}})
|
||||
* \f]
|
||||
*
|
||||
* @param depth Depth value in millimeters as an unsigned short.
|
||||
* @return The computed disparity in pixels.
|
||||
* Returns 0 if depth is zero or if the model is not valid for projection.
|
||||
*
|
||||
* @note This function requires the stereo camera to be valid for projection (i.e., calibrated and rectified).
|
||||
*/
|
||||
float computeDisparity(unsigned short depth) const; // mm
|
||||
|
||||
const cv::Mat & R() const {return R_;} ///< Stereo extrinsic rotation matrix.
|
||||
const cv::Mat & T() const {return T_;} ///< Stereo extrinsic translation vector.
|
||||
const cv::Mat & E() const {return E_;} ///< Essential matrix.
|
||||
const cv::Mat & F() const {return F_;} ///< Fundamental matrix
|
||||
const cv::Mat & R() const {return R_;} //extrinsic rotation matrix
|
||||
const cv::Mat & T() const {return T_;} //extrinsic translation matrix
|
||||
const cv::Mat & E() const {return E_;} //extrinsic essential matrix
|
||||
const cv::Mat & F() const {return F_;} //extrinsic fundamental matrix
|
||||
|
||||
/**
|
||||
* @brief Scales both cameras' calibration by a factor.
|
||||
*
|
||||
* Scales the intrinsic parameters (focal lengths, principal points) and image sizes
|
||||
* of both left and right cameras by the given scale factor. This is useful when working
|
||||
* with downscaled or upscaled images.
|
||||
*
|
||||
* @param scale Scaling factor (> 0). For example, use 0.5 to downscale or 2.0 to upscale.
|
||||
*
|
||||
* @note The baseline is not scaled, as it represents a physical distance between cameras.
|
||||
* @note Only valid camera models are scaled. Invalid models are left unchanged.
|
||||
*/
|
||||
void scale(double scale);
|
||||
|
||||
/**
|
||||
* @brief Applies region-of-interest (ROI) cropping to both cameras.
|
||||
*
|
||||
* Adjusts both camera models for a region of interest by shifting the principal points
|
||||
* and updating the image sizes. This is useful when working with cropped or subwindowed images.
|
||||
*
|
||||
* @param roi Region of interest rectangle. The top-left corner defines the offset for principal points.
|
||||
*
|
||||
* @note The principal points (cx, cy) are adjusted by subtracting the ROI's top-left coordinates.
|
||||
* @note The image size is set to the ROI size.
|
||||
* @note Only valid camera models are adjusted. Invalid models are left unchanged.
|
||||
*/
|
||||
void roi(const cv::Rect & roi);
|
||||
|
||||
/**
|
||||
* @brief Sets the local transform from left camera to robot base.
|
||||
*/
|
||||
void setLocalTransform(const Transform & transform) {left_.setLocalTransform(transform);}
|
||||
|
||||
/**
|
||||
* @brief Gets the local transform from left camera to robot base.
|
||||
*/
|
||||
const Transform & localTransform() const {return left_.localTransform();}
|
||||
|
||||
/**
|
||||
* @brief Returns the stereo transform (left camera relative to right camera coordinate system).
|
||||
*
|
||||
* The stereo transform brings points given in the
|
||||
* first (left) camera's coordinate system to points in the second (right) camera's coordinate
|
||||
* system. In more technical terms, it performs a change of basis from the
|
||||
* first camera's coordinate system to the second camera's coordinate system. Due to its duality,
|
||||
* it is equivalent to the position of the first camera with respect to the second
|
||||
* camera coordinate system.
|
||||
*
|
||||
* @return Transform from left camera to right camera coordinate system. Returns identity if R_ or T_ are empty.
|
||||
*
|
||||
* @note The transform is constructed from the stereo extrinsic parameters R_ and T_.
|
||||
*
|
||||
* @par Example:
|
||||
* For a stereo camera with a baseline of 15 cm, where the right camera is positioned to the
|
||||
* right of the left camera, the x value of the returned Transform would be -0.15 (negative
|
||||
* because it represents the position of the left camera in the right camera's coordinate system).
|
||||
* @code
|
||||
* StereoCameraModel stereo(...);
|
||||
* Transform transform = stereo.stereoTransform();
|
||||
* // If baseline is 0.15 m, transform.x() would be approximately -0.15
|
||||
* @endcode
|
||||
*/
|
||||
Transform stereoTransform() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the left camera model.
|
||||
*/
|
||||
const CameraModel & left() const {return left_;}
|
||||
/**
|
||||
* @brief Returns the right camera model.
|
||||
*/
|
||||
const CameraModel & right() const {return right_;}
|
||||
|
||||
/**
|
||||
* @brief Gets the suffix used for the left camera calibration file.
|
||||
*/
|
||||
const std::string & getLeftSuffix() const {return leftSuffix_;}
|
||||
/**
|
||||
* @brief Gets the suffix used for the right camera calibration file.
|
||||
*/
|
||||
const std::string & getRightSuffix() const {return rightSuffix_;}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Updates stereo rectification parameters for both cameras.
|
||||
*
|
||||
* This private method computes the rectification and projection matrices for both left and right
|
||||
* cameras based on the stereo extrinsic parameters (R_, T_). It is called automatically when
|
||||
* constructing a StereoCameraModel with valid extrinsics.
|
||||
*
|
||||
* @note Requires both R_ and T_ to be non-empty and valid.
|
||||
* @note Both camera models must be valid for rectification.
|
||||
*/
|
||||
void updateStereoRectification();
|
||||
|
||||
private:
|
||||
std::string leftSuffix_; ///< Suffix for the left calibration file.
|
||||
std::string rightSuffix_; ///< Suffix for the right calibration file.
|
||||
CameraModel left_; ///< Left camera model.
|
||||
CameraModel right_; ///< Right camera model.
|
||||
std::string name_; ///< Model name or ID.
|
||||
|
||||
cv::Mat R_; ///< Rotation matrix between cameras.
|
||||
cv::Mat T_; ///< Translation vector between cameras.
|
||||
cv::Mat E_; ///< Essential matrix.
|
||||
cv::Mat F_; ///< Fundamental matrix.
|
||||
std::string leftSuffix_;
|
||||
std::string rightSuffix_;
|
||||
CameraModel left_;
|
||||
CameraModel right_;
|
||||
std::string name_;
|
||||
cv::Mat R_;
|
||||
cv::Mat T_;
|
||||
cv::Mat E_;
|
||||
cv::Mat F_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Outputs a textual representation of the StereoCameraModel to the given output stream.
|
||||
*
|
||||
* This operator prints the details of the stereo camera model including:
|
||||
* - The left camera parameters.
|
||||
* - The right camera parameters.
|
||||
* - The stereo extrinsic matrices: Rotation (R), Translation (T), Essential (E), and Fundamental (F).
|
||||
* - The baseline distance between the two cameras.
|
||||
*
|
||||
* @param os The output stream to write to.
|
||||
* @param model The StereoCameraModel instance to output.
|
||||
* @return A reference to the output stream after writing the model information.
|
||||
*/
|
||||
RTABMAP_CORE_EXPORT std::ostream& operator<<(std::ostream& os, const StereoCameraModel& model);
|
||||
|
||||
} // rtabmap
|
||||
|
||||
@@ -35,97 +35,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class StereoDense
|
||||
* @brief Abstract base class for dense stereo matching algorithms
|
||||
*
|
||||
* This class provides the interface for dense stereo matching algorithms that
|
||||
* compute disparity maps from stereo image pairs. It uses a factory pattern
|
||||
* to create specific implementations (Block Matching or Semi-Global Block Matching).
|
||||
*
|
||||
* The disparity map is a 16-bit signed integer image (CV_16SC1) where each pixel
|
||||
* value represents the horizontal displacement between corresponding pixels in
|
||||
* the left and right stereo images. Higher disparity values indicate closer objects.
|
||||
*
|
||||
* @note Both input images must have the same size and be either grayscale (CV_8UC1)
|
||||
* or color (CV_8UC3). Color images are automatically converted to grayscale.
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT StereoDense {
|
||||
public:
|
||||
/**
|
||||
* @enum Type
|
||||
* @brief Enumeration of available stereo matching algorithm types
|
||||
*/
|
||||
enum Type {
|
||||
kTypeBM = 0, ///< Block Matching algorithm (faster, less accurate)
|
||||
kTypeSGBM = 1 ///< Semi-Global Block Matching algorithm (slower, more accurate)
|
||||
kTypeBM = 0,
|
||||
kTypeSGBM = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Factory method to create a StereoDense instance from parameters
|
||||
*
|
||||
* Creates a StereoDense instance based on the stereo dense strategy parameter
|
||||
* in the provided parameters map. The strategy is determined by the
|
||||
* Parameters::kStereoDenseStrategy() parameter.
|
||||
*
|
||||
* @param parameters Parameters map containing configuration values
|
||||
* @return Pointer to the created StereoDense instance (caller owns the memory)
|
||||
* @see create(Type, const ParametersMap&)
|
||||
*/
|
||||
static StereoDense * create(const ParametersMap & parameters);
|
||||
|
||||
/**
|
||||
* @brief Factory method to create a StereoDense instance of a specific type
|
||||
*
|
||||
* Creates a StereoDense instance of the specified type with the given parameters.
|
||||
*
|
||||
* @param type The type of stereo matching algorithm to create
|
||||
* @param parameters Optional parameters map containing configuration values
|
||||
* @return Pointer to the created StereoDense instance (caller owns the memory)
|
||||
* @see create(const ParametersMap&)
|
||||
*/
|
||||
static StereoDense * create(StereoDense::Type type, const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~StereoDense() {}
|
||||
|
||||
/**
|
||||
* @brief Parse parameters from a parameters map
|
||||
*
|
||||
* Updates the algorithm's configuration based on the provided parameters map.
|
||||
* Derived classes should override this method to parse their specific parameters.
|
||||
*
|
||||
* @param parameters Parameters map containing configuration values
|
||||
*/
|
||||
virtual void parseParameters(const ParametersMap & parameters) {}
|
||||
|
||||
/**
|
||||
* @brief Compute disparity map from stereo image pair
|
||||
*
|
||||
* Computes a disparity map from the given left and right stereo images.
|
||||
* The images must have the same size and be either grayscale (CV_8UC1) or
|
||||
* color (CV_8UC3). Color images are automatically converted to grayscale.
|
||||
*
|
||||
* @param leftImage Left stereo image (CV_8UC1 or CV_8UC3)
|
||||
* @param rightImage Right stereo image (CV_8UC1 or CV_8UC3), must have same size as leftImage
|
||||
* @return Disparity map as a 16-bit signed integer image (CV_16SC1)
|
||||
* @note The disparity values are stored as fixed-point numbers with 4 fractional bits.
|
||||
* To get the actual disparity, divide by 16.
|
||||
* @note Invalid disparities are typically represented by negative values or zero.
|
||||
*/
|
||||
virtual cv::Mat computeDisparity(
|
||||
const cv::Mat & leftImage,
|
||||
const cv::Mat & rightImage) const = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Protected constructor
|
||||
*
|
||||
* @param parameters Optional parameters map for initialization
|
||||
*/
|
||||
StereoDense(const ParametersMap & parameters = ParametersMap()) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -38,62 +38,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class Transform
|
||||
* @brief Represents a 3D rigid body transformation (rotation + translation).
|
||||
*
|
||||
* This class provides an abstraction over 3D transformations using a 3x4 matrix representation,
|
||||
* with utilities for conversion to/from Eigen and OpenCV formats, interpolation, inversion,
|
||||
* DoF reduction, and distance calculations. It is fundamental to pose estimation and motion handling
|
||||
* within the RTAB-Map framework.
|
||||
*
|
||||
* The underlying data is stored in a `cv::Mat` (3x4, CV_32FC1), representing a rotation matrix and translation vector.
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT Transform
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Default constructor. Initializes to a null (all zeros) transform.
|
||||
*/
|
||||
// Zero by default
|
||||
Transform();
|
||||
/**
|
||||
* @brief Constructor from rotation matrix elements and translation components.
|
||||
* @param r11...r33 Rotation matrix components.
|
||||
* @param o14,o24,o34 Translation vector components.
|
||||
*/
|
||||
// rotation matrix r## and origin o##
|
||||
Transform(float r11, float r12, float r13, float o14,
|
||||
float r21, float r22, float r23, float o24,
|
||||
float r31, float r32, float r33, float o34);
|
||||
/**
|
||||
* @brief Constructs a Transform from a 3x4 OpenCV matrix.
|
||||
* @param transformationMatrix A 3x4 CV_32FC1 matrix.
|
||||
*/
|
||||
// should have 3 rows, 4 cols and type CV_32FC1
|
||||
Transform(const cv::Mat & transformationMatrix);
|
||||
/**
|
||||
* @brief Constructs a Transform from position and Euler angles (in radians).
|
||||
* @param x, y, z Translation components.
|
||||
* @param roll, pitch, yaw Euler rotation angles (ZYX-convention).
|
||||
*/
|
||||
// x,y,z, roll,pitch,yaw
|
||||
Transform(float x, float y, float z, float roll, float pitch, float yaw);
|
||||
/**
|
||||
* @brief Constructs a Transform from position and quaternion.
|
||||
* @param x, y, z Translation components.
|
||||
* @param qx, qy, qz, qw Quaternion rotation components.
|
||||
*/
|
||||
// x,y,z, qx,qy,qz,qw
|
||||
Transform(float x, float y, float z, float qx, float qy, float qz, float qw);
|
||||
/**
|
||||
* @brief Constructs a 2D Transform (x, y, theta).
|
||||
*/
|
||||
// x,y, theta
|
||||
Transform(float x, float y, float theta);
|
||||
|
||||
/**
|
||||
* @brief Returns a deep copy of the transform.
|
||||
*/
|
||||
Transform clone() const;
|
||||
|
||||
// --- Accessors (rotation matrix elements, translation) ---
|
||||
float r11() const {return data()[0];} //!< Rotation matrix element at row 1, col 1.
|
||||
float r11() const {return data()[0];}
|
||||
float r12() const {return data()[1];}
|
||||
float r13() const {return data()[2];}
|
||||
float r21() const {return data()[4];}
|
||||
@@ -103,157 +69,64 @@ public:
|
||||
float r32() const {return data()[9];}
|
||||
float r33() const {return data()[10];}
|
||||
|
||||
float o14() const {return data()[3];} //!< Translation x
|
||||
float o24() const {return data()[7];} //!< Translation y
|
||||
float o34() const {return data()[11];} //!< Translation z
|
||||
float o14() const {return data()[3];}
|
||||
float o24() const {return data()[7];}
|
||||
float o34() const {return data()[11];}
|
||||
|
||||
float & operator[](int index) {return data()[index];}
|
||||
const float & operator[](int index) const {return data()[index];}
|
||||
float & operator()(int row, int col) {return data()[row*4 + col];}
|
||||
const float & operator()(int row, int col) const {return data()[row*4 + col];}
|
||||
|
||||
/**
|
||||
* @brief Checks whether the transform is null (all zeros).
|
||||
*/
|
||||
bool isNull() const;
|
||||
/**
|
||||
* @brief Checks whether the transform is identity.
|
||||
*/
|
||||
bool isIdentity() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the transform to null (zero matrix).
|
||||
*/
|
||||
void setNull();
|
||||
/**
|
||||
* @brief Sets the transform to the identity transform.
|
||||
*/
|
||||
void setIdentity();
|
||||
|
||||
/**
|
||||
* @brief Returns the internal OpenCV matrix (3x4).
|
||||
*/
|
||||
const cv::Mat & dataMatrix() const {return data_;}
|
||||
/**
|
||||
* @brief Returns a pointer to the raw data (12 floats).
|
||||
*/
|
||||
const float * data() const {return (const float *)data_.data;}
|
||||
float * data() {return (float *)data_.data;}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of float elements (always 12).
|
||||
*/
|
||||
int size() const {return 12;}
|
||||
|
||||
/// Translation getters/setters
|
||||
float & x() {return data()[3];} //!< Translation x
|
||||
float & y() {return data()[7];} //!< Translation y
|
||||
float & z() {return data()[11];} //!< Translation z
|
||||
float & x() {return data()[3];}
|
||||
float & y() {return data()[7];}
|
||||
float & z() {return data()[11];}
|
||||
const float & x() const {return data()[3];}
|
||||
const float & y() const {return data()[7];}
|
||||
const float & z() const {return data()[11];}
|
||||
|
||||
/**
|
||||
* @brief Returns 2D orientation (theta) in radians.
|
||||
*/
|
||||
float theta() const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether the transform is invertible.
|
||||
*/
|
||||
bool isInvertible() const;
|
||||
/**
|
||||
* @brief Returns the inverse of the transform.
|
||||
*/
|
||||
Transform inverse() const;
|
||||
/**
|
||||
* @brief Returns only the rotation component.
|
||||
*/
|
||||
Transform rotation() const;
|
||||
/**
|
||||
* @brief Returns only the translation component.
|
||||
*/
|
||||
Transform translation() const;
|
||||
/**
|
||||
* @brief Converts to 3 DoF (x, y, theta).
|
||||
*/
|
||||
Transform to3DoF() const;
|
||||
/**
|
||||
* @brief Converts to 4 DoF (x, y, z, yaw).
|
||||
*/
|
||||
Transform to4DoF() const;
|
||||
/**
|
||||
* @brief Checks if the transform is 3 DoF (no pitch/roll).
|
||||
*/
|
||||
bool is3DoF() const;
|
||||
/**
|
||||
* @brief Checks if the transform is 4 DoF (no pitch).
|
||||
*/
|
||||
bool is4DoF() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the 3x3 rotation matrix (cv::Mat).
|
||||
*/
|
||||
cv::Mat rotationMatrix() const;
|
||||
/**
|
||||
* @brief Returns the 3x1 translation matrix (cv::Mat).
|
||||
*/
|
||||
cv::Mat translationMatrix() const;
|
||||
|
||||
/**
|
||||
* @brief Extracts translation and Euler angles (radians).
|
||||
*/
|
||||
void getTranslationAndEulerAngles(float & x, float & y, float & z, float & roll, float & pitch, float & yaw) const;
|
||||
/**
|
||||
* @brief Extracts Euler angles (roll, pitch, yaw).
|
||||
*/
|
||||
void getEulerAngles(float & roll, float & pitch, float & yaw) const;
|
||||
/**
|
||||
* @brief Extracts translation only.
|
||||
*/
|
||||
void getTranslation(float & x, float & y, float & z) const;
|
||||
/**
|
||||
* @brief Returns angular difference (in radians) with another transform.
|
||||
*/
|
||||
float getAngle(const Transform & t) const;
|
||||
/**
|
||||
* @brief Returns the Euclidean norm of the translation vector.
|
||||
*/
|
||||
float getNorm() const;
|
||||
/**
|
||||
* @brief Returns the squared norm of the translation vector.
|
||||
*/
|
||||
float getNormSquared() const;
|
||||
/**
|
||||
* @brief Returns the Euclidean distance to another transform.
|
||||
*/
|
||||
float getDistance(const Transform & t) const;
|
||||
/**
|
||||
* @brief Returns the squared distance to another transform.
|
||||
*/
|
||||
float getDistanceSquared(const Transform & t) const;
|
||||
/**
|
||||
* @brief Interpolates between this and another transform.
|
||||
* @param t Interpolation factor [0, 1].
|
||||
* @param other Target transform.
|
||||
*/
|
||||
Transform interpolate(float t, const Transform & other) const;
|
||||
/**
|
||||
* @brief Normalizes the rotation matrix.
|
||||
*/
|
||||
void normalizeRotation();
|
||||
/**
|
||||
* @brief Returns a string representation of the transform.
|
||||
*/
|
||||
std::string prettyPrint() const;
|
||||
|
||||
/// Operator overloads
|
||||
Transform operator*(const Transform & t) const;
|
||||
Transform & operator*=(const Transform & t);
|
||||
bool operator==(const Transform & t) const;
|
||||
bool operator!=(const Transform & t) const;
|
||||
|
||||
// --- Eigen conversions ---
|
||||
Eigen::Matrix4f toEigen4f() const;
|
||||
Eigen::Matrix4d toEigen4d() const;
|
||||
Eigen::Affine3f toEigen3f() const;
|
||||
@@ -263,14 +136,7 @@ public:
|
||||
Eigen::Quaterniond getQuaterniond() const;
|
||||
|
||||
public:
|
||||
// --- Static helpers ---
|
||||
|
||||
/**
|
||||
* @brief Returns identity transform.
|
||||
*/
|
||||
static Transform getIdentity();
|
||||
|
||||
/// Converts from Eigen representations
|
||||
static Transform fromEigen4f(const Eigen::Matrix4f & matrix);
|
||||
static Transform fromEigen4d(const Eigen::Matrix4d & matrix);
|
||||
static Transform fromEigen3f(const Eigen::Affine3f & matrix);
|
||||
@@ -280,75 +146,40 @@ public:
|
||||
static Transform fromEigen3f(const Eigen::Matrix<float, 3, 4> & matrix);
|
||||
static Transform fromEigen3d(const Eigen::Matrix<double, 3, 4> & matrix);
|
||||
|
||||
/**
|
||||
* @brief Returns the transform from RTAB-Map to OpenGL coordinate system.
|
||||
* @note Coordinate systems:
|
||||
* - OpenGL: x → right, y → up, z → out of the screen (toward viewer).
|
||||
* - RTAB-Map: x → forward, y → left, z → up.
|
||||
*/
|
||||
static Transform opengl_T_rtabmap() {return Transform(
|
||||
0.0f, -1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
-1.0f, 0.0f, 0.0f, 0.0f);}
|
||||
/**
|
||||
* @brief Returns the transform from OpenGL to RTAB-Map coordinate system.
|
||||
* @note Coordinate systems:
|
||||
* - OpenGL: x → right, y → up, z → out of the screen (toward viewer).
|
||||
* - RTAB-Map: x → forward, y → left, z → up.
|
||||
*/
|
||||
static Transform rtabmap_T_opengl() {return Transform(
|
||||
0.0f, 0.0f,-1.0f, 0.0f,
|
||||
-1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f);}
|
||||
|
||||
/**
|
||||
* @brief Parses a transform from a string representation.
|
||||
* Supported formats:
|
||||
* - (3 values) "x y z"
|
||||
* - (6 values) "x y z roll pitch yaw" (ZYX-Euler convention)
|
||||
* - (7 values) "x y z qx qy qz qw"
|
||||
* - (9 values, 3x3 rotation) "r11 r12 r13 r21 r22 r23 r31 r32 r33"
|
||||
* - (12 values, 3x4 rotation) "r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz"
|
||||
*/
|
||||
* Format (3 values): x y z
|
||||
* Format (6 values): x y z roll pitch yaw
|
||||
* Format (7 values): x y z qx qy qz qw
|
||||
* Format (9 values, 3x3 rotation): r11 r12 r13 r21 r22 r23 r31 r32 r33
|
||||
* Format (12 values, 3x4 transform): r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz
|
||||
*/
|
||||
static Transform fromString(const std::string & string);
|
||||
/**
|
||||
* @brief Checks if a string can be parsed into a transform.
|
||||
*/
|
||||
static bool canParseString(const std::string & string);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the transform to a given timestamp.
|
||||
* @param tfBuffer Buffer of timestamped transforms.
|
||||
* @param stamp Requested timestamp.
|
||||
* @return transform at the requested timestamp, interpolated if it doesn't fall on a transform
|
||||
* with the exact same timestamp in the buffer. If the timestamp is older
|
||||
* than the oldest timestamp or newer than the latest timestamp in the buffer, a
|
||||
* null transform is returned and a warning message is generated.
|
||||
*/
|
||||
static Transform getTransform(
|
||||
const std::map<double, Transform> & tfBuffer,
|
||||
const double & stamp);
|
||||
/**
|
||||
* @deprecated Use getTransform() instead.
|
||||
*/
|
||||
// Use Transform::getTransform() instead to get always accurate transforms.
|
||||
RTABMAP_DEPRECATED static Transform getClosestTransform(
|
||||
const std::map<double, Transform> & tfBuffer,
|
||||
const double & stamp,
|
||||
double * stampDiff);
|
||||
|
||||
private:
|
||||
cv::Mat data_; ///< 3x4 float matrix (rotation + translation)
|
||||
cv::Mat data_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Output stream operator for Transform.
|
||||
*/
|
||||
RTABMAP_CORE_EXPORT std::ostream& operator<<(std::ostream& os, const Transform& s);
|
||||
|
||||
/**
|
||||
* @class TransformStamped
|
||||
* @brief Associates a transform with a timestamp.
|
||||
*/
|
||||
class TransformStamped
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -31,11 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/features2d/features2d.hpp>
|
||||
#else
|
||||
#include <opencv2/features.hpp>
|
||||
#endif
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include "rtabmap/core/Parameters.h"
|
||||
@@ -47,59 +43,18 @@ class DBDriver;
|
||||
class VisualWord;
|
||||
class FlannIndex;
|
||||
|
||||
/**
|
||||
* @class VWDictionary
|
||||
* @brief Manages a dictionary of visual words for visual place recognition and loop closure detection.
|
||||
*
|
||||
* The VWDictionary class maintains a collection of visual words (feature descriptors) and provides
|
||||
* efficient nearest neighbor search capabilities. It supports both incremental and fixed dictionary modes:
|
||||
* - Incremental mode: New visual words are added dynamically as new images are processed
|
||||
* - Fixed mode: A pre-computed dictionary is loaded from a file
|
||||
*
|
||||
* The class uses various nearest neighbor search strategies (FLANN, brute force, GPU-accelerated)
|
||||
* to match descriptors efficiently. It tracks word references to signatures (images) and manages
|
||||
* unused words for memory optimization.
|
||||
*
|
||||
* @note Visual words are identified by unique integer IDs starting from VWDictionary::ID_START
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT VWDictionary
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @enum NNStrategy
|
||||
* @brief Nearest neighbor search strategies for descriptor matching
|
||||
*
|
||||
* The values are those of the Kp/NNStrategy parameter, saved in user
|
||||
* configurations and databases: append, never renumber. The Vis/CorNNType
|
||||
* parameter has strategies of its own, its values are mapped to these ones
|
||||
* by RegistrationVis::nnStrategyFromCorNNType().
|
||||
*/
|
||||
enum NNStrategy{
|
||||
kNNFlannNaive, ///< FLANN naive search (exhaustive)
|
||||
kNNFlannKdTree, ///< FLANN kd-tree index (fast for high-dimensional descriptors)
|
||||
kNNFlannLSH, ///< FLANN Locality-Sensitive Hashing (ideal for binary descriptors)
|
||||
kNNBruteForce, ///< Brute force CPU search
|
||||
kNNBruteForceGPU, ///< Brute force GPU-accelerated search (requires CUDA)
|
||||
kNNFlannKdTreeSingle, ///< FLANN single exact kd-tree index (rebuilt whenever a word is added, for an index built once)
|
||||
kNNNanoFlannKdTree, ///< nanoflann kd-tree index (float descriptors only, incremental)
|
||||
kNNUndef ///< Undefined strategy
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Starting ID for visual words (typically 1)
|
||||
*/
|
||||
kNNFlannNaive,
|
||||
kNNFlannKdTree,
|
||||
kNNFlannLSH,
|
||||
kNNBruteForce,
|
||||
kNNBruteForceGPU,
|
||||
kNNUndef};
|
||||
static const int ID_START;
|
||||
|
||||
/**
|
||||
* @brief Invalid visual word ID (typically 0)
|
||||
*/
|
||||
static const int ID_INVALID;
|
||||
|
||||
/**
|
||||
* @brief Get the name of a nearest neighbor strategy
|
||||
* @param strategy The strategy enum value
|
||||
* @return String representation of the strategy name
|
||||
*/
|
||||
static std::string nnStrategyName(NNStrategy strategy)
|
||||
{
|
||||
switch(strategy) {
|
||||
@@ -113,475 +68,91 @@ public:
|
||||
return "BRUTE FORCE";
|
||||
case kNNBruteForceGPU:
|
||||
return "BRUTE FORCE GPU";
|
||||
case kNNNanoFlannKdTree:
|
||||
return "NANOFLANN KD-TREE";
|
||||
case kNNFlannKdTreeSingle:
|
||||
return "FLANN KD-TREE SINGLE";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param parameters Optional parameters map to configure the dictionary
|
||||
*/
|
||||
VWDictionary(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*
|
||||
* Clears all visual words and releases resources.
|
||||
*/
|
||||
virtual ~VWDictionary();
|
||||
|
||||
/**
|
||||
* @brief Parse and apply parameters from a parameters map
|
||||
* @param parameters Parameters map containing configuration values
|
||||
*/
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/**
|
||||
* @brief Update the search index with newly added words
|
||||
*
|
||||
* This method rebuilds the nearest neighbor search index (FLANN, etc.)
|
||||
* to include any words that were added but not yet indexed.
|
||||
*/
|
||||
virtual void update();
|
||||
|
||||
/**
|
||||
* @brief Add new visual words from descriptors
|
||||
* @param descriptors Matrix of descriptors (one row per descriptor)
|
||||
* @param signatureId ID of the signature (image) these descriptors belong to
|
||||
* @return List of visual word IDs that were added or matched
|
||||
*
|
||||
* For each descriptor, either matches it to an existing visual word
|
||||
* (using nearest neighbor search with NNDR ratio) or creates a new visual word.
|
||||
*
|
||||
* @note If the dictionary is not incremental (fixed), NNDR is not applied
|
||||
* and the closest existing visual word ID is always returned. If the dictionary is
|
||||
* incremental, a new visual word is created if NNDR validation
|
||||
* passes; otherwise, a reference to an existing visual word is added.
|
||||
*/
|
||||
virtual std::list<int> addNewWords(
|
||||
const cv::Mat & descriptors,
|
||||
int signatureId);
|
||||
|
||||
/**
|
||||
* @brief Add an existing visual word to the dictionary
|
||||
* @param vw Pointer to the visual word to add (ownership is transferred)
|
||||
*
|
||||
* @note The dictionary takes ownership of the VisualWord object
|
||||
*/
|
||||
virtual void addWord(VisualWord * vw);
|
||||
|
||||
/**
|
||||
* @brief Find nearest neighbor visual word IDs for a list of visual words
|
||||
* @param vws List of visual words to match
|
||||
* @return Vector of visual word IDs (one per input visual word)
|
||||
*
|
||||
* @note If the dictionary is not incremental (fixed), NNDR is not applied
|
||||
* and the closest visual word ID is always returned. If the dictionary is
|
||||
* incremental, a valid visual word ID is returned only if NNDR validation passes.
|
||||
*/
|
||||
std::vector<int> findNN(const std::list<VisualWord *> & vws) const;
|
||||
|
||||
/**
|
||||
* @brief Find nearest neighbor visual word IDs for descriptors
|
||||
* @param descriptors Matrix of descriptors (one row per descriptor)
|
||||
* @return Vector of visual word IDs (one per descriptor)
|
||||
*
|
||||
* @note If the dictionary is not incremental (fixed), NNDR is not applied
|
||||
* and the closest visual word ID is always returned. If the dictionary is
|
||||
* incremental, a valid visual word ID is returned only if NNDR validation passes.
|
||||
*/
|
||||
std::vector<int> findNN(const cv::Mat & descriptors) const;
|
||||
|
||||
/**
|
||||
* @brief Add a reference from a visual word to a signature
|
||||
* @param wordId ID of the visual word
|
||||
* @param signatureId ID of the signature (image)
|
||||
* @return true if the word exists in the dictionary and the reference has been added, false otherwise
|
||||
*
|
||||
* Tracks which signatures use which visual words. If the word was unused,
|
||||
* it is removed from the unused words list.
|
||||
*/
|
||||
bool addWordRef(int wordId, int signatureId);
|
||||
|
||||
/**
|
||||
* @brief Remove all references from a visual word to a signature
|
||||
* @param wordId ID of the visual word
|
||||
* @param signatureId ID of the signature (image)
|
||||
*
|
||||
* If the word has no more references after this operation,
|
||||
* it is added to the unused words list.
|
||||
*/
|
||||
void removeAllWordRef(int wordId, int signatureId);
|
||||
|
||||
/**
|
||||
* @brief Get a visual word by ID
|
||||
* @param id Visual word ID
|
||||
* @return Pointer to the visual word, or nullptr if not found
|
||||
*/
|
||||
const VisualWord * getWord(int id) const;
|
||||
|
||||
/**
|
||||
* @brief Get an unused visual word by ID
|
||||
* @param id Visual word ID
|
||||
* @return Pointer to the unused visual word, or nullptr if not found or not unused
|
||||
*
|
||||
* @note Ownership of the returned visual word still belongs to the dictionary.
|
||||
* To transfer ownership to the caller, removeWords() must be called on this word.
|
||||
*/
|
||||
VisualWord * getUnusedWord(int id) const;
|
||||
|
||||
/**
|
||||
* @brief Set the last word ID (used when loading from database)
|
||||
* @param id Last word ID
|
||||
*/
|
||||
void setLastWordId(int id) {_lastWordId = id;}
|
||||
|
||||
/**
|
||||
* @brief Get all visual words
|
||||
* @return Map of visual word ID to VisualWord pointer
|
||||
*/
|
||||
const std::map<int, VisualWord *> & getVisualWords() const {return _visualWords;}
|
||||
|
||||
/**
|
||||
* @brief Get the Nearest Neighbor Distance Ratio (NNDR) threshold
|
||||
* @return NNDR ratio value
|
||||
*
|
||||
* The NNDR ratio is used to determine if a descriptor matches an existing
|
||||
* visual word. If the ratio of distances to the first and second nearest
|
||||
* neighbors is below this threshold, a match is accepted.
|
||||
*
|
||||
* @note The NNDR method was introduced in "Distinctive Image Features
|
||||
* from Scale-Invariant Keypoints" by David Lowe (IJCV 2004).
|
||||
*/
|
||||
float getNndrRatio() const {return _nndrRatio;}
|
||||
|
||||
/**
|
||||
* @brief Get the count of words not yet indexed in the search tree
|
||||
* @return Number of words waiting to be indexed
|
||||
*/
|
||||
unsigned int getNotIndexedWordsCount() const {return (int)_notIndexedWords.size();}
|
||||
|
||||
/**
|
||||
* @brief Get the ID of the last indexed word
|
||||
* @return Last indexed word ID, or 0 if no words are indexed
|
||||
*/
|
||||
int getLastIndexedWordId() const;
|
||||
|
||||
/**
|
||||
* @brief Get the total number of active word-to-signature references
|
||||
* @return Total count of active references
|
||||
*/
|
||||
int getTotalActiveReferences() const {return _totalActiveReferences;}
|
||||
|
||||
/**
|
||||
* @brief Get the count of words currently indexed in the search tree
|
||||
* @return Number of indexed words
|
||||
*/
|
||||
unsigned int getIndexedWordsCount() const;
|
||||
|
||||
/**
|
||||
* @brief Get the memory used by the search index
|
||||
* @return Memory usage in kilobytes
|
||||
*/
|
||||
unsigned int getIndexMemoryUsed() const; // KB
|
||||
|
||||
/**
|
||||
* @brief Get the total memory used by the dictionary
|
||||
* @return Memory usage in bytes
|
||||
*/
|
||||
unsigned long getMemoryUsed() const; //Bytes
|
||||
|
||||
/**
|
||||
* @brief Set the nearest neighbor search strategy
|
||||
* @param strategy The strategy to use
|
||||
* @return true if the search tree was re-initialized (strategy changed), false otherwise
|
||||
*
|
||||
* Changing the strategy will rebuild the search index if words are already indexed.
|
||||
*/
|
||||
bool setNNStrategy(NNStrategy strategy);
|
||||
|
||||
/**
|
||||
* @brief Get the current nearest neighbor search strategy
|
||||
* @return The current NNStrategy
|
||||
*/
|
||||
NNStrategy getNNStrategy() const {return _strategy;}
|
||||
|
||||
/**
|
||||
* @brief Check if the dictionary is in incremental mode
|
||||
* @return true if incremental, false if fixed
|
||||
*/
|
||||
bool setNNStrategy(NNStrategy strategy); // Return true if the search tree has been re-initialized
|
||||
bool isIncremental() const {return _incrementalDictionary;}
|
||||
|
||||
/**
|
||||
* @brief Check if FLANN index is updated incrementally
|
||||
* @return true if incremental FLANN updates are enabled
|
||||
*/
|
||||
bool isIncrementalFlann() const {return _incrementalFlann;}
|
||||
|
||||
/**
|
||||
* @brief Set the dictionary to incremental mode
|
||||
*
|
||||
* In incremental mode, new visual words can be added dynamically.
|
||||
* This cannot be called if a fixed dictionary is already loaded.
|
||||
*/
|
||||
void setIncrementalDictionary();
|
||||
|
||||
/**
|
||||
* @brief Set the dictionary to fixed mode and load from file
|
||||
* @param dictionaryPath Path to the dictionary file (.txt or .db format)
|
||||
*
|
||||
* Loads a pre-computed dictionary from a file. The dictionary file format in txt format
|
||||
* should be: one line per visual word, with word ID followed by descriptor values.
|
||||
* This cannot be called if words are already in the dictionary.
|
||||
*/
|
||||
void setFixedDictionary(const std::string & dictionaryPath);
|
||||
|
||||
/**
|
||||
* @brief Check if the dictionary has been modified since last save
|
||||
* @return true if modified, false otherwise
|
||||
*/
|
||||
bool isModified() const;
|
||||
// Re-index all the words from scratch. The index then contains the words in
|
||||
// the same order than the one built by update() on a dictionary freshly
|
||||
// loaded from a database, which is required to serialize it (the serialized
|
||||
// index is matched against the words in that order when deserialized).
|
||||
void rebuildIndex();
|
||||
|
||||
/**
|
||||
* @brief Serialize the search index to a byte vector
|
||||
* @return Serialized index data
|
||||
*/
|
||||
std::vector<unsigned char> serializeIndex() const;
|
||||
|
||||
/**
|
||||
* @brief Deserialize the search index from a byte vector
|
||||
* @param data Serialized index data
|
||||
* @return true if deserialization was successful, false otherwise
|
||||
*/
|
||||
bool deserializeIndex(const std::vector<unsigned char> & data);
|
||||
|
||||
/**
|
||||
* @brief Deserialize the search index from raw bytes
|
||||
* @param data Pointer to serialized index data
|
||||
* @param size Size of the data in bytes
|
||||
* @return true if deserialization was successful, false otherwise
|
||||
*/
|
||||
bool deserializeIndex(const unsigned char * data, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Export the dictionary to files
|
||||
* @param fileNameReferences Path to file for word-to-signature references
|
||||
* @param fileNameDescriptors Path to file for visual word descriptors
|
||||
*
|
||||
* Exports the dictionary in a format that can be loaded later.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief Clear all visual words and reset the dictionary
|
||||
* @param printWarningsIfNotEmpty If true, print warnings if dictionary is not empty
|
||||
*
|
||||
* Deletes all visual words and releases all resources.
|
||||
*/
|
||||
void clear(bool printWarningsIfNotEmpty = true);
|
||||
|
||||
/**
|
||||
* @brief Get all unused visual words
|
||||
* @return Vector of pointers to unused visual words
|
||||
*
|
||||
* Unused words are visual words that have no references to any signatures.
|
||||
*
|
||||
* @note Ownership of the returned visual words still belongs to the dictionary.
|
||||
* To transfer ownership to the caller, removeWords() must be called on these words.
|
||||
*/
|
||||
std::vector<VisualWord *> getUnusedWords() const;
|
||||
|
||||
/**
|
||||
* @brief Get IDs of all unused visual words
|
||||
* @return Vector of unused word IDs
|
||||
*/
|
||||
std::vector<int> getUnusedWordIds() const;
|
||||
|
||||
/**
|
||||
* @brief Get the count of unused visual words
|
||||
* @return Number of unused words
|
||||
*/
|
||||
unsigned int getUnusedWordsSize() const {return (int)_unusedWords.size();}
|
||||
|
||||
/**
|
||||
* @brief Remove words from the dictionary
|
||||
* @param words Vector of visual word pointers to remove
|
||||
*
|
||||
* @note The caller is responsible for deleting the VisualWord objects
|
||||
*/
|
||||
void removeWords(const std::vector<VisualWord*> & words); // caller must delete the words
|
||||
|
||||
/**
|
||||
* @brief Delete all unused visual words
|
||||
*
|
||||
* Removes and deletes visual words that have no references to any signatures.
|
||||
*/
|
||||
void deleteUnusedWords();
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Convert binary descriptors to 32-bit float format
|
||||
* @param descriptorsIn Input descriptors (CV_8UC1 for binary, or CV_32FC1 for float)
|
||||
* @param byteToFloat Conversion mode:
|
||||
* - If true: Simple type conversion from CV_8UC1 to CV_32FC1 using OpenCV's convertTo().
|
||||
* Each byte value becomes a float value (output dimensions unchanged).
|
||||
* - If false: Bit-by-bit expansion for binary descriptors (e.g., ORB, BRIEF).
|
||||
* Each input byte (8 bits) is expanded into 8 float values (0.0f or 1.0f),
|
||||
* one per bit. Output has 8x the number of columns (e.g., 32 bytes -> 256 floats).
|
||||
* @return Descriptors in 32-bit float format (CV_32FC1)
|
||||
*/
|
||||
static cv::Mat convertBinTo32F(const cv::Mat & descriptorsIn, bool byteToFloat = true);
|
||||
|
||||
/**
|
||||
* @brief Convert 32-bit float descriptors to binary format
|
||||
* @param descriptorsIn Input descriptors (CV_32FC1)
|
||||
* @param byteToFloat Conversion mode:
|
||||
* - If true: Simple type conversion from CV_32FC1 to CV_8UC1 using OpenCV's convertTo().
|
||||
* Each float value becomes a byte value (output dimensions unchanged).
|
||||
* - If false: Bit-by-bit packing for binary descriptors.
|
||||
* Each group of 8 float values (0.0f or 1.0f) is packed into 1 byte (8 bits),
|
||||
* one bit per float. Input must have columns divisible by 8.
|
||||
* Output has 1/8 the number of columns (e.g., 256 floats -> 32 bytes).
|
||||
* @return Descriptors in binary format (CV_8UC1)
|
||||
*/
|
||||
static cv::Mat convert32FToBin(const cv::Mat & descriptorsIn, bool byteToFloat = true);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Get the next available visual word ID
|
||||
* @return Next unique word ID
|
||||
*/
|
||||
int getNextId();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Map of visual word ID to VisualWord pointer
|
||||
* @note All visual words (used and unused) are stored here
|
||||
*/
|
||||
std::map<int, VisualWord *> _visualWords; //<id,VisualWord*>
|
||||
|
||||
/**
|
||||
* @brief Total count of active word-to-signature references
|
||||
* @note Used to track all references for updating common signatures
|
||||
*/
|
||||
int _totalActiveReferences;
|
||||
int _totalActiveReferences; // keep track of all references for updating the common signature
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Whether the dictionary is in incremental mode
|
||||
*/
|
||||
bool _incrementalDictionary;
|
||||
|
||||
/**
|
||||
* @brief Whether FLANN index is updated incrementally
|
||||
*/
|
||||
bool _incrementalFlann;
|
||||
|
||||
/**
|
||||
* @brief Rebalancing factor for FLANN index updates
|
||||
*/
|
||||
float _rebalancingFactor;
|
||||
|
||||
/**
|
||||
* @brief Whether to convert descriptors from byte to float format
|
||||
*/
|
||||
bool _byteToFloat;
|
||||
|
||||
/**
|
||||
* @brief Nearest Neighbor Distance Ratio threshold
|
||||
*
|
||||
* @note The NNDR method was introduced in "Distinctive Image Features
|
||||
* from Scale-Invariant Keypoints" by David Lowe (IJCV 2004).
|
||||
*/
|
||||
float _nndrRatio;
|
||||
|
||||
/**
|
||||
* @brief Path to the pre-computed dictionary file (.txt or .db)
|
||||
*/
|
||||
std::string _dictionaryPath; // a pre-computed dictionary (.txt or .db)
|
||||
|
||||
/**
|
||||
* @brief Path to a new dictionary file to load
|
||||
*/
|
||||
std::string _newDictionaryPath; // a pre-computed dictionary (.txt or .db)
|
||||
|
||||
/**
|
||||
* @brief Whether new words should be compared together before adding
|
||||
*/
|
||||
bool _newWordsComparedTogether;
|
||||
|
||||
/**
|
||||
* @brief Whether to include checksum when serializing index
|
||||
*/
|
||||
bool _serializeWithChecksum;
|
||||
|
||||
/**
|
||||
* @brief ID of the last visual word added
|
||||
*/
|
||||
int _lastWordId;
|
||||
|
||||
/**
|
||||
* @brief Whether to use L1 distance metric instead of L2
|
||||
*/
|
||||
bool useDistanceL1_;
|
||||
|
||||
/**
|
||||
* @brief FLANN index for fast nearest neighbor search
|
||||
*/
|
||||
FlannIndex * _flannIndex;
|
||||
|
||||
/**
|
||||
* @brief Data matrix for the search tree
|
||||
*/
|
||||
cv::Mat _dataTree;
|
||||
|
||||
/**
|
||||
* @brief Whether the dictionary has been modified since last save
|
||||
*/
|
||||
bool _modified;
|
||||
|
||||
/**
|
||||
* @brief Current nearest neighbor search strategy
|
||||
*/
|
||||
NNStrategy _strategy;
|
||||
|
||||
/**
|
||||
* @brief Map from search index position to visual word ID
|
||||
*/
|
||||
std::map<int ,int> _mapIndexId;
|
||||
|
||||
/**
|
||||
* @brief Map from visual word ID to search index position
|
||||
*/
|
||||
std::map<int ,int> _mapIdIndex;
|
||||
|
||||
/**
|
||||
* @brief Map of unused visual words (words with no references)
|
||||
* @note These words remain in _visualWords but are marked as unused
|
||||
*/
|
||||
std::map<int, VisualWord*> _unusedWords; //<id,VisualWord*>
|
||||
/**
|
||||
* @brief Set of word IDs that are not yet indexed in the search tree
|
||||
*/
|
||||
std::set<int> _notIndexedWords;
|
||||
|
||||
/**
|
||||
* @brief Set of word IDs that were removed from dictionary but still indexed
|
||||
*/
|
||||
std::set<int> _removedIndexedWords;
|
||||
std::map<int, VisualWord*> _unusedWords; //<id,VisualWord*>, note that these words stay in _visualWords
|
||||
std::set<int> _notIndexedWords; // Words that are not indexed in the dictionary
|
||||
std::set<int> _removedIndexedWords; // Words not anymore in the dictionary but still indexed in the dictionary
|
||||
};
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
@@ -35,95 +35,32 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
/**
|
||||
* @class VisualWord
|
||||
* @brief Represents a visual word (feature descriptor) used in the bag-of-words (BoW) model.
|
||||
*
|
||||
* A VisualWord holds a unique identifier, its descriptor (usually a feature vector from an image),
|
||||
* and a record of where (in which signatures/images) it was observed. This is a key component in
|
||||
* loop closure detection and visual place recognition in RTAB-Map.
|
||||
*
|
||||
* The word keeps track of references (signature IDs) where it appears, and how many times it was observed
|
||||
* in each signature. It also tracks whether the word has been saved to a database.
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT VisualWord
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param id Unique ID of the visual word.
|
||||
* @param descriptor The descriptor associated with the word (e.g., ORB, SIFT).
|
||||
* @param signatureId Optional signature ID where the word is first observed.
|
||||
*/
|
||||
VisualWord(int id, const cv::Mat & descriptor, int signatureId = 0);
|
||||
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*/
|
||||
~VisualWord();
|
||||
|
||||
/**
|
||||
* @brief Adds a reference to this word for the given signature ID.
|
||||
* @param signatureId ID of the signature in which this word is observed.
|
||||
*/
|
||||
void addRef(int signatureId);
|
||||
|
||||
/**
|
||||
* @brief Removes all references to this word for the given signature ID.
|
||||
* @param signatureId ID of the signature from which references will be removed.
|
||||
* @return Number of references removed.
|
||||
*/
|
||||
int removeAllRef(int signatureId);
|
||||
|
||||
/**
|
||||
* @brief Estimates the memory used by this visual word in bytes.
|
||||
* @return Size in bytes.
|
||||
*/
|
||||
unsigned long getMemoryUsed() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the total number of references (across all signatures).
|
||||
* @return Number of total references.
|
||||
*/
|
||||
int getTotalReferences() const {return _totalReferences;}
|
||||
|
||||
/**
|
||||
* @brief Returns the ID of this visual word.
|
||||
* @return Word ID.
|
||||
*/
|
||||
int id() const {return _id;}
|
||||
|
||||
/**
|
||||
* @brief Returns the feature descriptor of this word.
|
||||
* @return The OpenCV matrix descriptor.
|
||||
*/
|
||||
const cv::Mat & getDescriptor() const {return _descriptor;}
|
||||
const std::map<int, int> & getReferences() const {return _references;} // (signature id , occurrence in the signature)
|
||||
|
||||
/**
|
||||
* @brief Returns the references of this word.
|
||||
* @return A map of (signature ID, occurrence count).
|
||||
*/
|
||||
const std::map<int, int> & getReferences() const {return _references;}
|
||||
|
||||
/**
|
||||
* @brief Checks if the word has been saved to the database.
|
||||
* @return True if saved, false otherwise.
|
||||
*/
|
||||
bool isSaved() const {return _saved;}
|
||||
|
||||
/**
|
||||
* @brief Sets the saved flag for the word.
|
||||
* @param saved True if the word has been saved to the database.
|
||||
*/
|
||||
void setSaved(bool saved) {_saved = saved;}
|
||||
|
||||
private:
|
||||
int _id; ///< Unique ID of the visual word.
|
||||
cv::Mat _descriptor; ///< Feature descriptor (e.g., ORB, SIFT).
|
||||
bool _saved; ///< Whether the word is saved to the database.
|
||||
int _id;
|
||||
cv::Mat _descriptor;
|
||||
bool _saved; // If it's saved to db
|
||||
|
||||
int _totalReferences; ///< Total reference count across all signatures.
|
||||
std::map<int, int> _references; ///< Active references: map of (signature ID, occurrence count).
|
||||
int _totalReferences;
|
||||
std::map<int, int> _references; // (signature id , occurrence in the signature)
|
||||
std::map<int, int> _oldReferences; // (signature id , occurrence in the signature)
|
||||
};
|
||||
|
||||
} // namespace rtabmap
|
||||
|
||||
@@ -51,9 +51,7 @@ class Grabber;
|
||||
|
||||
namespace rtabmap
|
||||
{
|
||||
/**
|
||||
* @brief OpenNI driver for cameras like Kinect for Xbox 360
|
||||
*/
|
||||
|
||||
class RTABMAP_CORE_EXPORT CameraOpenni :
|
||||
public Camera
|
||||
{
|
||||
|
||||
@@ -29,41 +29,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#define CORELIB_INCLUDE_RTABMAP_CORE_IMPL_LOCALMAP_HPP_
|
||||
|
||||
#include <rtabmap/core/util3d_mapping.h>
|
||||
#include <rtabmap/core/util3d_filtering.h>
|
||||
#include <rtabmap/core/util3d_transforms.h>
|
||||
#include <rtabmap/utilite/ULogger.h>
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
template<typename PointT>
|
||||
pcl::IndicesPtr LocalGridMaker::noiseFilteringWithMaxRange(
|
||||
const typename pcl::PointCloud<PointT>::Ptr & cloud,
|
||||
const pcl::IndicesPtr & indices,
|
||||
float maxRange,
|
||||
float radiusSearch,
|
||||
int minNeighborsInRadius)
|
||||
{
|
||||
// util3d::radiusFiltering() would filter the whole cloud if the indices are empty
|
||||
UASSERT(indices.get() && !indices->empty());
|
||||
pcl::IndicesPtr closeIndices = indices;
|
||||
pcl::IndicesPtr farIndices;
|
||||
if(maxRange != 0.0f)
|
||||
{
|
||||
// Don't filter points farther than maximum range, in case we want to ray trace empty space
|
||||
util3d::rangeSplitFiltering(cloud, indices, maxRange, closeIndices, farIndices);
|
||||
}
|
||||
if(closeIndices->size())
|
||||
{
|
||||
// Note: radiusFiltering() would filter the whole cloud if indices are empty
|
||||
closeIndices = util3d::radiusFiltering(cloud, closeIndices, radiusSearch, minNeighborsInRadius);
|
||||
}
|
||||
if(farIndices.get())
|
||||
{
|
||||
closeIndices = util3d::concatenate(closeIndices, farIndices);
|
||||
}
|
||||
return closeIndices;
|
||||
}
|
||||
|
||||
template<typename PointT>
|
||||
typename pcl::PointCloud<PointT>::Ptr LocalGridMaker::segmentCloud(
|
||||
const typename pcl::PointCloud<PointT>::Ptr & cloudIn,
|
||||
@@ -81,13 +51,6 @@ typename pcl::PointCloud<PointT>::Ptr LocalGridMaker::segmentCloud(
|
||||
flatObstacles->reset(new std::vector<int>);
|
||||
}
|
||||
|
||||
UASSERT(cloudIn.get());
|
||||
UASSERT(indicesIn.get());
|
||||
if(cloudIn->empty())
|
||||
{
|
||||
return cloudIn;
|
||||
}
|
||||
|
||||
typename pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
|
||||
pcl::IndicesPtr indices(new std::vector<int>);
|
||||
|
||||
@@ -119,11 +82,6 @@ typename pcl::PointCloud<PointT>::Ptr LocalGridMaker::segmentCloud(
|
||||
}
|
||||
}
|
||||
|
||||
if(cloud->empty())
|
||||
{
|
||||
return cloud;
|
||||
}
|
||||
|
||||
// add pose rotation without yaw
|
||||
float roll, pitch, yaw;
|
||||
pose.getEulerAngles(roll, pitch, yaw);
|
||||
@@ -217,15 +175,51 @@ typename pcl::PointCloud<PointT>::Ptr LocalGridMaker::segmentCloud(
|
||||
noiseFilteringMinNeighbors_);
|
||||
if(groundIndices->size())
|
||||
{
|
||||
groundIndices = noiseFilteringWithMaxRange<PointT>(cloud, groundIndices, rangeMax_, noiseFilteringRadius_, noiseFilteringMinNeighbors_);
|
||||
pcl::IndicesPtr farIndices;
|
||||
if(rangeMax_!=0)
|
||||
{
|
||||
// Don't filter points farther than maximum range, in case we want to ray trace empty space
|
||||
pcl::IndicesPtr closeIndices;
|
||||
rtabmap::util3d::rangeSplitFiltering(cloud, groundIndices, rangeMax_, closeIndices, farIndices);
|
||||
groundIndices = closeIndices;
|
||||
}
|
||||
groundIndices = rtabmap::util3d::radiusFiltering(cloud, groundIndices, noiseFilteringRadius_, noiseFilteringMinNeighbors_);
|
||||
if(farIndices.get())
|
||||
{
|
||||
groundIndices = rtabmap::util3d::concatenate(groundIndices, farIndices);
|
||||
}
|
||||
}
|
||||
if(obstaclesIndices->size())
|
||||
{
|
||||
obstaclesIndices = noiseFilteringWithMaxRange<PointT>(cloud, obstaclesIndices, rangeMax_, noiseFilteringRadius_, noiseFilteringMinNeighbors_);
|
||||
pcl::IndicesPtr farIndices;
|
||||
if(rangeMax_!=0)
|
||||
{
|
||||
// Don't filter points farther than maximum range, in case we want to ray trace empty space
|
||||
pcl::IndicesPtr closeIndices;
|
||||
rtabmap::util3d::rangeSplitFiltering(cloud, obstaclesIndices, rangeMax_, closeIndices, farIndices);
|
||||
obstaclesIndices = closeIndices;
|
||||
}
|
||||
obstaclesIndices = rtabmap::util3d::radiusFiltering(cloud, obstaclesIndices, noiseFilteringRadius_, noiseFilteringMinNeighbors_);
|
||||
if(farIndices.get())
|
||||
{
|
||||
obstaclesIndices = rtabmap::util3d::concatenate(obstaclesIndices, farIndices);
|
||||
}
|
||||
}
|
||||
if(flatObstacles && (*flatObstacles)->size())
|
||||
{
|
||||
*flatObstacles = noiseFilteringWithMaxRange<PointT>(cloud, *flatObstacles, rangeMax_, noiseFilteringRadius_, noiseFilteringMinNeighbors_);
|
||||
pcl::IndicesPtr farIndices;
|
||||
if(rangeMax_!=0)
|
||||
{
|
||||
// Don't filter points farther than maximum range, in case we want to ray trace empty space
|
||||
pcl::IndicesPtr closeIndices;
|
||||
rtabmap::util3d::rangeSplitFiltering(cloud, *flatObstacles, rangeMax_, closeIndices, farIndices);
|
||||
*flatObstacles = closeIndices;
|
||||
}
|
||||
*flatObstacles = rtabmap::util3d::radiusFiltering(cloud, *flatObstacles, noiseFilteringRadius_, noiseFilteringMinNeighbors_);
|
||||
if(farIndices.get())
|
||||
{
|
||||
*flatObstacles = rtabmap::util3d::concatenate(*flatObstacles, farIndices);
|
||||
}
|
||||
}
|
||||
UDEBUG("Radius filtering end (%ld ground %ld obstacles)",
|
||||
groundIndices->size(),
|
||||
|
||||
@@ -85,8 +85,7 @@ void segmentObstaclesFromGround(
|
||||
viewPoint,
|
||||
groundNormalsUp);
|
||||
|
||||
if(flatSurfaces->size() &&
|
||||
(segmentFlatObstacles || maxGroundHeight != 0.0f || minClusterSize>1))
|
||||
if(segmentFlatObstacles && flatSurfaces->size())
|
||||
{
|
||||
int biggestFlatSurfaceIndex;
|
||||
std::vector<pcl::IndicesPtr> clusteredFlatSurfaces = extractClusters(
|
||||
@@ -136,9 +135,7 @@ void segmentObstaclesFromGround(
|
||||
{
|
||||
Eigen::Vector4f centroid(0,0,0,1);
|
||||
pcl::compute3DCentroid(*cloud, *clusteredFlatSurfaces.at(i), centroid);
|
||||
if((maxGroundHeight!=0.0f && centroid[2] <= maxGroundHeight) ||
|
||||
centroid[2] <= biggestSurfaceMax[2] ||
|
||||
(maxGroundHeight==0.0f && !segmentFlatObstacles))
|
||||
if(maxGroundHeight==0.0f || centroid[2] <= maxGroundHeight || centroid[2] <= biggestSurfaceMax[2]) // epsilon
|
||||
{
|
||||
ground = util3d::concatenate(ground, clusteredFlatSurfaces.at(i));
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
const std::map<int, std::vector<CameraModel> > & models,
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences, // <ID words, IDs frames + keypoint(x,y,depth)>
|
||||
BAOutliers * outliers = 0);
|
||||
std::set<int> * outliers = 0);
|
||||
};
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -43,18 +43,13 @@ public:
|
||||
bool slam2d = Parameters::defaultRegForce3DoF(),
|
||||
bool covarianceIgnored = Parameters::defaultOptimizerVarianceIgnored(),
|
||||
double epsilon = Parameters::defaultOptimizerEpsilon()) :
|
||||
Optimizer(iterations, slam2d, covarianceIgnored, epsilon),
|
||||
pixelVariance_(Parameters::defaultOptimizerPixelVariance()),
|
||||
disparityVariance_(Parameters::defaultOptimizerDisparityVariance()),
|
||||
robustKernelDelta_(Parameters::defaultOptimizerRobustKernelDelta()),
|
||||
baseline_(Parameters::defaultOptimizerBaseline()) {}
|
||||
OptimizerCeres(const ParametersMap & parameters);
|
||||
Optimizer(iterations, slam2d, covarianceIgnored, epsilon) {}
|
||||
OptimizerCeres(const ParametersMap & parameters) :
|
||||
Optimizer(parameters) {}
|
||||
virtual ~OptimizerCeres() {}
|
||||
|
||||
virtual Type type() const {return kTypeCeres;}
|
||||
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
virtual std::map<int, Transform> optimize(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
@@ -71,13 +66,7 @@ public:
|
||||
const std::map<int, std::vector<CameraModel> > & models, // in case of stereo, Tx should be set
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences, // <ID words, IDs frames + keypoint(x,y,depth)>
|
||||
BAOutliers * outliers = 0);
|
||||
|
||||
private:
|
||||
double pixelVariance_;
|
||||
double disparityVariance_;
|
||||
double robustKernelDelta_;
|
||||
double baseline_;
|
||||
std::set<int> * outliers = 0);
|
||||
};
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
const std::map<int, std::vector<CameraModel> > & models, // in case of stereo, Tx should be set
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences, // <ID words, IDs frames + keypoint(x,y,depth)>
|
||||
BAOutliers * outliers = 0);
|
||||
std::set<int> * outliers = 0);
|
||||
|
||||
bool saveGraph(
|
||||
const std::string & fileName,
|
||||
@@ -82,7 +82,6 @@ private:
|
||||
int solver_;
|
||||
int optimizer_;
|
||||
double pixelVariance_;
|
||||
double disparityVariance_;
|
||||
double robustKernelDelta_;
|
||||
double baseline_;
|
||||
};
|
||||
|
||||
@@ -58,21 +58,8 @@ public:
|
||||
double * finalError = 0,
|
||||
int * iterationsDone = 0);
|
||||
|
||||
virtual std::map<int, Transform> optimizeBA(
|
||||
int rootId,
|
||||
const std::map<int, Transform> & poses,
|
||||
const std::multimap<int, Link> & links,
|
||||
const std::map<int, std::vector<CameraModel> > & models,
|
||||
std::map<int, cv::Point3f> & points3DMap,
|
||||
const std::map<int, std::map<int, FeatureBA> > & wordReferences,
|
||||
BAOutliers * outliers = 0);
|
||||
|
||||
private:
|
||||
int internalOptimizerType_;
|
||||
double pixelVariance_;
|
||||
double disparityVariance_;
|
||||
double robustKernelDelta_;
|
||||
double baseline_;
|
||||
|
||||
gtsam::ISAM2 * isam2_;
|
||||
struct ConstraintToFactor {
|
||||
|
||||
@@ -34,88 +34,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class StereoBM
|
||||
* @brief Block Matching algorithm for dense stereo matching
|
||||
*
|
||||
* This class implements the Block Matching (BM) algorithm for computing disparity
|
||||
* maps from stereo image pairs.
|
||||
*
|
||||
* @note This class wraps OpenCV's cv::StereoBM implementation.
|
||||
* @see StereoSGBM for a more accurate but slower alternative
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT StereoBM : public StereoDense {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor with explicit block size and number of disparities
|
||||
*
|
||||
* @param blockSize Size of the block window for matching (must be odd, typically 5-21)
|
||||
* @param numDisparities Number of disparity levels to search (must be divisible by 16)
|
||||
*/
|
||||
StereoBM(int blockSize, int numDisparities);
|
||||
|
||||
/**
|
||||
* @brief Constructor with parameters map
|
||||
*
|
||||
* Creates a StereoBM instance and initializes it from the provided parameters map.
|
||||
*
|
||||
* @param parameters Parameters map containing configuration values
|
||||
*/
|
||||
StereoBM(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~StereoBM() {}
|
||||
|
||||
/**
|
||||
* @brief Parse parameters from a parameters map
|
||||
*
|
||||
* Updates the algorithm's configuration based on the provided parameters map.
|
||||
* Supported parameters:
|
||||
* - Parameters::kStereoBMBlockSize()
|
||||
* - Parameters::kStereoBMMinDisparity()
|
||||
* - Parameters::kStereoBMNumDisparities()
|
||||
* - Parameters::kStereoBMPreFilterSize()
|
||||
* - Parameters::kStereoBMPreFilterCap()
|
||||
* - Parameters::kStereoBMUniquenessRatio()
|
||||
* - Parameters::kStereoBMTextureThreshold()
|
||||
* - Parameters::kStereoBMSpeckleWindowSize()
|
||||
* - Parameters::kStereoBMSpeckleRange()
|
||||
* - Parameters::kStereoBMDisp12MaxDiff()
|
||||
*
|
||||
* @param parameters Parameters map containing configuration values
|
||||
*/
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/**
|
||||
* @brief Compute disparity map from stereo image pair
|
||||
*
|
||||
* Computes a disparity map using the Block Matching algorithm. The images
|
||||
* must have the same size and be either grayscale (CV_8UC1) or color (CV_8UC3).
|
||||
* Color images are automatically converted to grayscale.
|
||||
*
|
||||
* @param leftImage Left stereo image (CV_8UC1 or CV_8UC3)
|
||||
* @param rightImage Right stereo image (CV_8UC1 or CV_8UC3), must have same size as leftImage
|
||||
* @return Disparity map as a 16-bit signed integer image (CV_16SC1)
|
||||
* @note The disparity values are stored as fixed-point numbers with 4 fractional bits.
|
||||
* To get the actual disparity, divide by 16.
|
||||
*/
|
||||
virtual cv::Mat computeDisparity(
|
||||
const cv::Mat & leftImage,
|
||||
const cv::Mat & rightImage) const;
|
||||
|
||||
private:
|
||||
int blockSize_; ///< Size of the block window for matching (default: 15, must be odd)
|
||||
int minDisparity_; ///< Minimum disparity value to search (default: 0)
|
||||
int numDisparities_; ///< Number of disparity levels to search (default: 64, must be divisible by 16)
|
||||
int preFilterSize_; ///< Size of the prefiltering window (default: 9, must be odd)
|
||||
int preFilterCap_; ///< Prefiltering cap value (default: 31)
|
||||
int uniquenessRatio_; ///< Uniqueness ratio for matching (default: 15)
|
||||
int textureThreshold_; ///< Texture threshold for filtering (default: 10)
|
||||
int speckleWindowSize_; ///< Maximum size of smooth disparity regions to consider as speckles (default: 100)
|
||||
int speckleRange_; ///< Maximum disparity variation within each connected component (default: 4)
|
||||
int disp12MaxDiff_; ///< Maximum allowed difference in left-right disparity check (default: -1, disabled)
|
||||
int blockSize_; //15
|
||||
int minDisparity_; //0
|
||||
int numDisparities_; //64
|
||||
int preFilterSize_; //9
|
||||
int preFilterCap_; //31
|
||||
int uniquenessRatio_; //15
|
||||
int textureThreshold_; //10
|
||||
int speckleWindowSize_; //100
|
||||
int speckleRange_; //4
|
||||
int disp12MaxDiff_; //-1
|
||||
};
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -34,86 +34,28 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
namespace rtabmap {
|
||||
|
||||
/**
|
||||
* @class StereoSGBM
|
||||
* @brief Semi-Global Block Matching algorithm for dense stereo matching
|
||||
*
|
||||
* This class implements the Semi-Global Block Matching (SGBM) algorithm for
|
||||
* computing disparity maps from stereo image pairs.
|
||||
*
|
||||
* SGBM generally produces more accurate results than Block Matching, especially
|
||||
* in textureless regions and areas with repetitive patterns, but is computationally
|
||||
* more expensive.
|
||||
*
|
||||
* @note This class wraps OpenCV's cv::StereoSGBM implementation.
|
||||
* @see StereoBM for a faster but less accurate alternative
|
||||
*/
|
||||
class RTABMAP_CORE_EXPORT StereoSGBM : public StereoDense {
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor with parameters map
|
||||
*
|
||||
* Creates a StereoSGBM instance and initializes it from the provided parameters map.
|
||||
*
|
||||
* @param parameters Parameters map containing configuration values
|
||||
*/
|
||||
StereoSGBM(const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Virtual destructor
|
||||
*/
|
||||
virtual ~StereoSGBM() {}
|
||||
|
||||
/**
|
||||
* @brief Parse parameters from a parameters map
|
||||
*
|
||||
* Updates the algorithm's configuration based on the provided parameters map.
|
||||
* Supported parameters:
|
||||
* - Parameters::kStereoSGBMBlockSize()
|
||||
* - Parameters::kStereoSGBMMinDisparity()
|
||||
* - Parameters::kStereoSGBMNumDisparities()
|
||||
* - Parameters::kStereoSGBMPreFilterCap()
|
||||
* - Parameters::kStereoSGBMUniquenessRatio()
|
||||
* - Parameters::kStereoSGBMSpeckleWindowSize()
|
||||
* - Parameters::kStereoSGBMSpeckleRange()
|
||||
* - Parameters::kStereoSGBMP1()
|
||||
* - Parameters::kStereoSGBMP2()
|
||||
* - Parameters::kStereoSGBMDisp12MaxDiff()
|
||||
* - Parameters::kStereoSGBMMode()
|
||||
*
|
||||
* @param parameters Parameters map containing configuration values
|
||||
*/
|
||||
virtual void parseParameters(const ParametersMap & parameters);
|
||||
|
||||
/**
|
||||
* @brief Compute disparity map from stereo image pair
|
||||
*
|
||||
* Computes a disparity map using the Semi-Global Block Matching algorithm.
|
||||
* The images must have the same size and be either grayscale (CV_8UC1) or
|
||||
* color (CV_8UC3). Color images are automatically converted to grayscale.
|
||||
*
|
||||
* @param leftImage Left stereo image (CV_8UC1 or CV_8UC3)
|
||||
* @param rightImage Right stereo image (CV_8UC1 or CV_8UC3), must have same size as leftImage
|
||||
* @return Disparity map as a 16-bit signed integer image (CV_16SC1)
|
||||
* @note The disparity values are stored as fixed-point numbers with 4 fractional bits.
|
||||
* To get the actual disparity, divide by 16.
|
||||
*/
|
||||
virtual cv::Mat computeDisparity(
|
||||
const cv::Mat & leftImage,
|
||||
const cv::Mat & rightImage) const;
|
||||
|
||||
private:
|
||||
int blockSize_; ///< Size of the block window for matching (default: 15, must be odd)
|
||||
int minDisparity_; ///< Minimum disparity value to search (default: 0)
|
||||
int numDisparities_; ///< Number of disparity levels to search (default: 64, must be divisible by 16)
|
||||
int preFilterCap_; ///< Prefiltering cap value (default: 31)
|
||||
int uniquenessRatio_; ///< Uniqueness ratio for matching (default: 15)
|
||||
int speckleWindowSize_; ///< Maximum size of smooth disparity regions to consider as speckles (default: 100)
|
||||
int speckleRange_; ///< Maximum disparity variation within each connected component (default: 4)
|
||||
int P1_; ///< Penalty parameter for disparity changes of 1 pixel (default: 0, typically 8*blockSize*blockSize)
|
||||
int P2_; ///< Penalty parameter for disparity changes of more than 1 pixel (default: 0, typically 32*blockSize*blockSize)
|
||||
int disp12MaxDiff_; ///< Maximum allowed difference in left-right disparity check (default: 0)
|
||||
int mode_; ///< Algorithm mode: 0=cv::StereoSGBM::MODE_SGBM, 1=cv::StereoSGBM::MODE_HH (default: 0)
|
||||
int blockSize_; //15
|
||||
int minDisparity_; //0
|
||||
int numDisparities_; //64
|
||||
int preFilterCap_; //31
|
||||
int uniquenessRatio_; //15
|
||||
int speckleWindowSize_; //100
|
||||
int speckleRange_; //4
|
||||
int P1_; //0
|
||||
int P2_; //0
|
||||
int disp12MaxDiff_; //0
|
||||
int mode_; //0=cv::StereoSGBM::MODE_SGBM;
|
||||
};
|
||||
|
||||
} /* namespace rtabmap */
|
||||
|
||||
@@ -32,15 +32,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#ifndef CORELIB_SRC_OPENCV_STEREORECTIFYFISHEYE_H_
|
||||
#define CORELIB_SRC_OPENCV_STEREORECTIFYFISHEYE_H_
|
||||
|
||||
// This header relies on the OpenCV C API (cvRodrigues2, cvProjectPoints2, ...)
|
||||
// which was removed in OpenCV 5. Pull in only the version macros (available in
|
||||
// all OpenCV versions) so we can fail early with a clear message rather than
|
||||
// with cryptic errors from the includes below.
|
||||
#include <opencv2/core/version.hpp>
|
||||
#if CV_MAJOR_VERSION >= 5
|
||||
#error "stereoRectifyFisheye.h is not supported with OpenCV 5 or later (it uses the removed OpenCV C API). Use cv::fisheye::stereoRectify() instead, or guard your include with '#if CV_MAJOR_VERSION < 5'."
|
||||
#endif
|
||||
|
||||
#include <opencv2/calib3d/calib3d.hpp>
|
||||
#if CV_MAJOR_VERSION >= 3
|
||||
#include <opencv2/calib3d/calib3d_c.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2010-2025, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -39,89 +39,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
namespace rtabmap
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief This namespace contains 2D image processing utilities.
|
||||
*/
|
||||
/**
|
||||
* @namespace rtabmap::util2d
|
||||
* @brief 2D image utilities: disparity and depth conversion, interpolation,
|
||||
* decimation, sub-pixel refinement and similarity measures.
|
||||
*/
|
||||
namespace util2d
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Computes the Sum of Squared Differences (SSD) between two image patches.
|
||||
*
|
||||
* This function calculates the pixel-wise squared differences between corresponding elements
|
||||
* in two input windows and accumulates the result into a single score. It supports grayscale
|
||||
* 8-bit, 32-bit float, and 16-bit 2-channel short images (e.g., optical flow or stereo blocks).
|
||||
*
|
||||
* @param windowLeft Left input image patch.
|
||||
* @param windowRight Right input image patch (must be the same size and type as windowLeft).
|
||||
* @return The SSD score (a lower score indicates higher similarity).
|
||||
*
|
||||
* @throws Assertion failure if the input types or dimensions don't match.
|
||||
*
|
||||
* Supported types:
|
||||
* - CV_8UC1 (grayscale image)
|
||||
* - CV_32FC1 (floating-point grayscale)
|
||||
* - CV_16SC2 (2-channel signed short vectors; average of both channels used)
|
||||
*/
|
||||
// SSD: Sum of Squared Differences
|
||||
float RTABMAP_CORE_EXPORT ssd(const cv::Mat & windowLeft, const cv::Mat & windowRight);
|
||||
|
||||
/**
|
||||
* @brief Computes the Sum of Absolute Differences (SAD) between two image patches.
|
||||
*
|
||||
* This function calculates the absolute pixel intensity difference between two windows
|
||||
* and accumulates the result. It supports grayscale 8-bit, 32-bit float, and 16-bit
|
||||
* 2-channel short images.
|
||||
*
|
||||
* @param windowLeft Left input image patch.
|
||||
* @param windowRight Right input image patch (must match windowLeft in size and type).
|
||||
* @return The SAD score (a lower score indicates greater similarity).
|
||||
*
|
||||
* @throws Assertion failure if the input types or dimensions are incompatible.
|
||||
*
|
||||
* Supported types:
|
||||
* - CV_8UC1 (grayscale image)
|
||||
* - CV_32FC1 (floating-point grayscale)
|
||||
* - CV_16SC2 (2-channel signed short vectors; average of both channels used)
|
||||
*/
|
||||
// SAD: Sum of Absolute intensity Differences
|
||||
float RTABMAP_CORE_EXPORT sad(const cv::Mat & windowLeft, const cv::Mat & windowRight);
|
||||
|
||||
/**
|
||||
* @brief Computes stereo correspondences between left and right images using a pyramidal window-based matching approach.
|
||||
*
|
||||
* This function estimates the right image positions of a set of corners detected in the left image using block matching.
|
||||
* It supports both Sum of Absolute Differences (SAD) and Sum of Squared Differences (SSD) as the matching criteria,
|
||||
* and uses a coarse-to-fine strategy over an image pyramid for robustness and subpixel accuracy.
|
||||
*
|
||||
* @param leftImage The left image (grayscale 8 bits or CV_8UC1).
|
||||
* @param rightImage The right image (same type and size as `leftImage`).
|
||||
* @param leftCorners The list of 2D points in the left image for which correspondences are to be found.
|
||||
* @param[out] status Output status vector indicating the success of correspondence for each point.
|
||||
* (1: valid correspondence found, 0: no valid match)
|
||||
* @param winSize The size of the search window (should be odd). Will be made odd internally if not.
|
||||
* Minimum size is 3.
|
||||
* @param maxLevel The maximum level of the image pyramid to use for coarse-to-fine search.
|
||||
* @param iterations Number of iterations for subpixel refinement (gradient descent-like search).
|
||||
* @param minDisparityF Minimum allowed disparity (float). Defines search range.
|
||||
* @param maxDisparityF Maximum allowed disparity (float). Defines search range.
|
||||
* @param ssdApproach If true, uses SSD (Sum of Squared Differences) as matching cost.
|
||||
* If false, uses SAD (Sum of Absolute Differences).
|
||||
*
|
||||
* @return A vector of 2D points corresponding to `leftCorners` but in the right image.
|
||||
* The size of the output matches the input `leftCorners`. Invalid or rejected points are flagged in `status`.
|
||||
*
|
||||
* @note - Both input images must be rectified (i.e., correspondences lie along epipolar lines).
|
||||
* - This function modifies the search window to ensure it is odd-sized (required for accurate matching).
|
||||
* - Subpixel accuracy is achieved using iterative matching and local refinement.
|
||||
* - The disparity range is adaptive and refined at each pyramid level.
|
||||
* - For accurate results, ensure good quality corner detection and well-rectified input images.
|
||||
*
|
||||
* @see cv::buildOpticalFlowPyramid, cv::getRectSubPix
|
||||
*/
|
||||
std::vector<cv::Point2f> RTABMAP_CORE_EXPORT calcStereoCorrespondences(
|
||||
const cv::Mat & leftImage,
|
||||
const cv::Mat & rightImage,
|
||||
@@ -132,47 +57,9 @@ std::vector<cv::Point2f> RTABMAP_CORE_EXPORT calcStereoCorrespondences(
|
||||
int iterations = 5,
|
||||
float minDisparity = 0.0f,
|
||||
float maxDisparity = 64.0f,
|
||||
bool ssdApproach = true);
|
||||
bool ssdApproach = true); // SSD by default, otherwise it is SAD
|
||||
|
||||
/**
|
||||
* @brief Computes sparse optical flow using a pyramidal Lucas-Kanade method constrained to the x-axis.
|
||||
*
|
||||
* This function is a customized version of OpenCV's `cv::calcOpticalFlowPyrLK`, modified specifically
|
||||
* for stereo matching scenarios. It assumes that the `prevImg` is the left stereo image and `nextImg`
|
||||
* is the right stereo image. The optical flow is computed only along the x-axis (i.e., horizontal direction),
|
||||
* which is typically valid in rectified stereo image pairs.
|
||||
*
|
||||
* @note
|
||||
* The key modification to the original Lucas-Kanade implementation is the following:
|
||||
* Instead of computing the flow in both x and y directions, the y-displacement is **forced to zero**:
|
||||
* @code
|
||||
* // Original:
|
||||
* cv::Point2f delta((float)((A12*b2 - A22*b1) * D), (float)((A12*b1 - A11*b2) * D));
|
||||
*
|
||||
* // Modified:
|
||||
* cv::Point2f delta((float)((A12*b2 - A22*b1) * D), 0);
|
||||
* @endcode
|
||||
* This ensures that flow estimation is constrained along the epipolar lines (x-direction only).
|
||||
*
|
||||
* @param _prevImg Input image from the previous frame (or left stereo image). Supports pyramid or raw image.
|
||||
* @param _nextImg Input image from the next frame (or right stereo image). Supports pyramid or raw image.
|
||||
* @param _prevPts Vector of 2D points for which the flow needs to be found (in `prevImg`).
|
||||
* @param _nextPts Output vector of 2D points containing the calculated new positions (in `nextImg`).
|
||||
* If `OPTFLOW_USE_INITIAL_FLOW` is passed, it should contain initial guesses.
|
||||
* @param _status Output status vector. Each element is set to 1 if flow for the corresponding features
|
||||
* has been found, 0 otherwise.
|
||||
* @param _err Optional output vector. Contains error or min eigenvalue values (depending on flags).
|
||||
* @param winSize Size of the search window at each pyramid level.
|
||||
* @param maxLevel 0-based maximal pyramid level number. If set to 0, pyramids are not used (single level).
|
||||
* @param criteria Termination criteria for iterative search algorithm (maxCount and/or epsilon).
|
||||
* @param flags Operation flags:
|
||||
* - `OPTFLOW_USE_INITIAL_FLOW`: Use initial `nextPts` values.
|
||||
* - `OPTFLOW_LK_GET_MIN_EIGENVALS`: Output minimum eigenvalues instead of error.
|
||||
* @param minEigThreshold Minimum eigenvalue threshold for rejecting unstable flow vectors.
|
||||
*
|
||||
* @see cv::calcOpticalFlowPyrLK
|
||||
* @see https://github.com/opencv/opencv/blob/4.x/modules/video/src/lkpyramid.cpp
|
||||
*/
|
||||
// exactly as cv::calcOpticalFlowPyrLK but it should be called with pyramid (from cv::buildOpticalFlowPyramid()) and delta drops the y error.
|
||||
void RTABMAP_CORE_EXPORT calcOpticalFlowPyrLKStereo( cv::InputArray _prevImg, cv::InputArray _nextImg,
|
||||
cv::InputArray _prevPts, cv::InputOutputArray _nextPts,
|
||||
cv::OutputArray _status, cv::OutputArray _err,
|
||||
@@ -180,86 +67,16 @@ void RTABMAP_CORE_EXPORT calcOpticalFlowPyrLKStereo( cv::InputArray _prevImg, cv
|
||||
cv::TermCriteria criteria = cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, 30, 0.01),
|
||||
int flags = 0, double minEigThreshold = 1e-4 );
|
||||
|
||||
/**
|
||||
* @brief Computes the disparity map from a pair of stereo images.
|
||||
*
|
||||
* This function calculates a dense disparity map from the provided left and right stereo images.
|
||||
* It assumes that the stereo pair is rectified and of the same size. The left image can be
|
||||
* either grayscale (CV_8UC1) or color (CV_8UC3), while the right image must be grayscale (CV_8UC1).
|
||||
*
|
||||
* If the left image is in color, it is first converted to grayscale before disparity computation.
|
||||
* The actual disparity computation is delegated to a `StereoDense` object created using the provided parameters.
|
||||
*
|
||||
* @param leftImage The left image of the stereo pair. Can be grayscale or BGR color.
|
||||
* @param rightImage The right image of the stereo pair. Must be grayscale.
|
||||
* @param parameters A map of parameters used to configure the stereo matching algorithm.
|
||||
*
|
||||
* @return A `cv::Mat` representing the computed disparity map. Some algorithms, like
|
||||
* StereoBM or StereoSGBM compute 16-bit fixed-point disparity map (CV_16SC1) (where each
|
||||
* disparity value has 4 fractional bits), whereas other algorithms output 32-bit
|
||||
* floating-point (CV_32FC1) disparity map.
|
||||
*
|
||||
* @throws Assertion failure if:
|
||||
* - Either image is empty.
|
||||
* - Image sizes do not match.
|
||||
* - Image types are incompatible.
|
||||
*
|
||||
* @note The returned disparity map contains disparity values for each pixel in the left image.
|
||||
* Pixels with no match may be set to 0 or a negative value depending on the stereo algorithm.
|
||||
*
|
||||
* @see rtabmap::StereoDense
|
||||
*/
|
||||
|
||||
cv::Mat RTABMAP_CORE_EXPORT disparityFromStereoImages(
|
||||
const cv::Mat & leftImage,
|
||||
const cv::Mat & rightImage,
|
||||
const ParametersMap & parameters = ParametersMap());
|
||||
|
||||
/**
|
||||
* @brief Converts a disparity map to a depth map using stereo camera parameters.
|
||||
*
|
||||
* This function takes a disparity map and computes a corresponding depth map using the formula:
|
||||
* \f[ \text{depth} = \frac{\text{baseline} \times \text{focal length}}{\text{disparity}} \f]
|
||||
*
|
||||
* - Disparity values of 0 or less are ignored (depth is left as 0).
|
||||
* - For CV_16UC1 depth type, depth is scaled to millimeters (i.e., multiplied by 1000).
|
||||
* - Values that exceed the 16-bit unsigned max value (65535) are counted and ignored.
|
||||
*
|
||||
* @param disparity The input disparity map (CV_32FC1 or CV_16SC1).
|
||||
* @param fx The focal length in pixels (typically from camera intrinsic parameters).
|
||||
* @param baseline The distance between the stereo cameras in meters.
|
||||
* @param type The desired output depth image type: CV_32FC1 (meters) or CV_16UC1 (millimeters).
|
||||
* @return A depth image of the same resolution as the input disparity map.
|
||||
*
|
||||
* @warning Logs a warning if any computed depth values exceed the maximum allowed by the CV_16UC1 format (65535 mm).
|
||||
*
|
||||
* @throws Assertion failure if the disparity map is empty, has unsupported type, or output type is invalid.
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT depthFromDisparity(const cv::Mat & disparity,
|
||||
float fx, float baseline,
|
||||
int type = CV_32FC1); // CV_32FC1 or CV_16UC1
|
||||
|
||||
/**
|
||||
* @brief Computes a depth map from stereo image pairs using optical flow tracking.
|
||||
*
|
||||
* This function estimates the depth of features tracked between the left and right rectified stereo images
|
||||
* by computing sparse optical flow (via Lucas-Kanade) between provided feature points in the left image.
|
||||
* It uses stereo triangulation based on the tracked correspondences and known camera intrinsics.
|
||||
*
|
||||
* @param leftImage Grayscale rectified left image (CV_8UC1).
|
||||
* @param rightImage Grayscale rectified right image (CV_8UC1), must be same size as leftImage.
|
||||
* @param leftCorners Feature points (e.g., corners) detected in the left image.
|
||||
* @param fx Focal length of the camera in pixels (must be > 0).
|
||||
* @param baseline Distance between the left and right camera centers in meters (must be > 0).
|
||||
* @param flowWinSize Window size used for optical flow (e.g., 15 for 15x15).
|
||||
* @param flowMaxLevel Maximum pyramid level for optical flow.
|
||||
* @param flowIterations Maximum number of iterations for the iterative search algorithm in optical flow.
|
||||
* @param flowEps Desired accuracy for optical flow termination criteria.
|
||||
*
|
||||
* @return A depth map (CV_32FC1) with the same size as the input images. Pixels corresponding to successfully
|
||||
* tracked features have valid depth values, while others are zero.
|
||||
*
|
||||
* @see cv::calcOpticalFlowPyrLK, rtabmap::util2d::depthFromStereoCorrespondences
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT depthFromStereoImages(
|
||||
const cv::Mat & leftImage,
|
||||
const cv::Mat & rightImage,
|
||||
@@ -271,58 +88,12 @@ cv::Mat RTABMAP_CORE_EXPORT depthFromStereoImages(
|
||||
int flowIterations = 20,
|
||||
double flowEps = 0.02);
|
||||
|
||||
/**
|
||||
* @brief Computes a disparity map from stereo correspondences between two images.
|
||||
*
|
||||
* This function calculates the disparity map based on the given stereo correspondences
|
||||
* (the left and right corners of features) and stores the resulting disparity values in
|
||||
* a matrix. The disparity for each point is computed as the horizontal difference
|
||||
* between the corresponding points in the left and right images.
|
||||
* The function also accepts a mask to specify which points to include in the disparity computation.
|
||||
*
|
||||
* @param[in] disparitySize The size of the output disparity map.
|
||||
* @param[in] leftCorners The list of points in the left image where features are detected.
|
||||
* @param[in] rightCorners The list of points in the right image corresponding to the points in `leftCorners`.
|
||||
* @param[in] mask A vector of flags indicating which correspondences to use for the disparity computation.
|
||||
* An empty vector means all correspondences are included.
|
||||
*
|
||||
* @return A `cv::Mat` of type `CV_32FC1` representing the computed disparity map. Each pixel value corresponds
|
||||
* to the disparity (horizontal difference between left and right image points) at that location.
|
||||
*
|
||||
* @note The disparity is computed as the horizontal difference between corresponding points in the left and right
|
||||
* images, specifically the difference in the x-coordinates of the points.
|
||||
* @note The disparity map is returned in floating point format, where the value represents the disparity in pixels.
|
||||
*
|
||||
* @warning The function checks that all points are within the bounds of the disparity map.
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT disparityFromStereoCorrespondences(
|
||||
const cv::Size & disparitySize,
|
||||
const std::vector<cv::Point2f> & leftCorners,
|
||||
const std::vector<cv::Point2f> & rightCorners,
|
||||
const std::vector<unsigned char> & mask);
|
||||
|
||||
/**
|
||||
* @brief Computes a sparse depth map from corresponding stereo feature points.
|
||||
*
|
||||
* This function uses known corresponding 2D feature points from rectified stereo images
|
||||
* to estimate depth via triangulation, using the disparity between matched points.
|
||||
* The computed depth values are placed into a depth map at the locations of the left image points.
|
||||
*
|
||||
* @param leftImage The left rectified grayscale image (used for image size reference).
|
||||
* @param leftCorners Feature points detected in the left image.
|
||||
* @param rightCorners Corresponding feature points in the right image (same size as leftCorners).
|
||||
* @param mask Optional binary mask indicating which correspondences are valid (1 = valid).
|
||||
* If empty, all correspondences are considered valid.
|
||||
* @param fx Focal length of the camera in pixels (must be > 0).
|
||||
* @param baseline Distance between the stereo cameras in meters (must be > 0).
|
||||
*
|
||||
* @return A sparse depth map (CV_32FC1) of the same size as the input image.
|
||||
* Pixels corresponding to valid matches will contain depth values (in meters),
|
||||
* while all others remain zero.
|
||||
*
|
||||
* @note It assumes stereo images are rectified and only x-axis disparity is present.
|
||||
* @see rtabmap::util2d::depthFromStereoImages
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT depthFromStereoCorrespondences(
|
||||
const cv::Mat & leftImage,
|
||||
const std::vector<cv::Point2f> & leftCorners,
|
||||
@@ -330,73 +101,9 @@ cv::Mat RTABMAP_CORE_EXPORT depthFromStereoCorrespondences(
|
||||
const std::vector<unsigned char> & mask,
|
||||
float fx, float baseline);
|
||||
|
||||
/**
|
||||
* @brief Converts a 32-bit float depth image (in meters) to a 16-bit unsigned depth image (in millimeters).
|
||||
*
|
||||
* This function converts each valid depth value from meters to millimeters
|
||||
* (by multiplying by 1000.0) and stores it as an unsigned 16-bit integer.
|
||||
* Depth values outside the valid range (greater than 65535 mm) are clipped
|
||||
* to zero and counted. A warning is printed if such values are found.
|
||||
*
|
||||
* @param depth32F Input depth image of type CV_32FC1, where depth is in meters.
|
||||
* May be empty.
|
||||
*
|
||||
* @return A 16-bit unsigned depth image (CV_16UC1) in millimeters. Returns an
|
||||
* empty matrix if input is empty.
|
||||
*
|
||||
* @warning If depth values exceed 65535 mm, they are ignored and a warning is issued.
|
||||
*
|
||||
* @note It's assumed that the input image is in meters (commonly used format
|
||||
* for 32-bit depth maps). If it’s already in millimeters, do not use this function.
|
||||
*
|
||||
* @see rtabmap::util2d::cvtDepthToFloat()
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT cvtDepthFromFloat(const cv::Mat & depth32F);
|
||||
|
||||
/**
|
||||
* @brief Converts a 16-bit unsigned depth image (in millimeters) to a 32-bit float depth image (in meters).
|
||||
*
|
||||
* This function converts each depth value from millimeters to meters by dividing
|
||||
* by 1000.0. Useful when working with floating point depth operations or to
|
||||
* standardize depth formats for computation or storage.
|
||||
*
|
||||
* @param depth16U Input depth image of type CV_16UC1, where depth is in millimeters.
|
||||
* May be empty.
|
||||
*
|
||||
* @return A 32-bit float depth image (CV_32FC1) with depth values in meters.
|
||||
* Returns an empty matrix if input is empty.
|
||||
*
|
||||
* @note Use this function when you want to convert from 16-bit mm format to floating point meter format.
|
||||
*
|
||||
* @see rtabmap::util2d::cvtDepthFromFloat()
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT cvtDepthToFloat(const cv::Mat & depth16U);
|
||||
|
||||
/**
|
||||
* @brief Retrieves a depth value from a depth image at a subpixel coordinate.
|
||||
*
|
||||
* This function samples the depth value from a depth image (either in 16-bit unsigned integers
|
||||
* representing millimeters or 32-bit floats representing meters) at a floating-point (x, y)
|
||||
* coordinate. The value can be optionally smoothed using a weighted neighborhood, and fallback
|
||||
* estimation from neighbors is possible if the depth at the target pixel is invalid or zero.
|
||||
*
|
||||
* @param depthImage Input depth image. Must be of type CV_16UC1 (depth in mm) or CV_32FC1 (depth in meters).
|
||||
* @param x The subpixel X-coordinate in the image.
|
||||
* @param y The subpixel Y-coordinate in the image.
|
||||
* @param smoothing If true, apply a weighted 3x3 smoothing around the pixel.
|
||||
* @param depthErrorRatio Maximum acceptable depth difference ratio used during smoothing and fallback estimation.
|
||||
* @param estWithNeighborsIfNull If true, and the target pixel has an invalid depth, estimate it from valid neighboring pixels.
|
||||
*
|
||||
* @return The depth value at the given (x, y) location (in meters), or 0 if it cannot be determined.
|
||||
*
|
||||
* @note
|
||||
* - The function applies bounds checking on the input coordinates.
|
||||
* - If `smoothing` is enabled, a weighted average using a 3x3 kernel is computed.
|
||||
* - If `estWithNeighborsIfNull` is enabled and the pixel has no valid depth, the value is estimated
|
||||
* from 4-connected neighbors using consistency constraints based on `depthErrorRatio`.
|
||||
* - Pixels with zero or invalid (NaN/Inf) depth are ignored in estimation and smoothing.
|
||||
*
|
||||
*/
|
||||
float RTABMAP_CORE_EXPORT getDepth(
|
||||
const cv::Mat & depthImage,
|
||||
float x, float y,
|
||||
@@ -404,114 +111,15 @@ float RTABMAP_CORE_EXPORT getDepth(
|
||||
float depthErrorRatio = 0.02f, //ratio
|
||||
bool estWithNeighborsIfNull = false);
|
||||
|
||||
/**
|
||||
* @defgroup RoiComputation Region of Interest (ROI) Computation
|
||||
* Functions to compute the ROI in an image using ratio-based cropping.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Computes a region of interest (ROI) in the image using string-defined ratios.
|
||||
*
|
||||
* @param image Input image.
|
||||
* @param roiRatios A string of 4 space-separated float values representing the ROI ratios:
|
||||
* - left, right, top, bottom.
|
||||
* - Each ratio should be between 0 and 1.
|
||||
* - For example, "0.1 0.1 0.2 0.2" removes 10% from the left/right and 20% from the top/bottom.
|
||||
* @return A cv::Rect defining the ROI, or an empty rectangle if input is invalid.
|
||||
*/
|
||||
cv::Rect RTABMAP_CORE_EXPORT computeRoi(const cv::Mat & image, const std::string & roiRatios);
|
||||
|
||||
/**
|
||||
* @brief Computes a region of interest (ROI) from an image size and a string of ratios.
|
||||
*
|
||||
* @param imageSize The size of the image (width x height).
|
||||
* @param roiRatios A string with 4 space-separated float values indicating cropping ratios:
|
||||
* - left, right, top, bottom.
|
||||
* @return The computed ROI rectangle.
|
||||
*/
|
||||
cv::Rect RTABMAP_CORE_EXPORT computeRoi(const cv::Size & imageSize, const std::string & roiRatios);
|
||||
|
||||
/**
|
||||
* @brief Computes a region of interest (ROI) in the image using float vector-defined ratios.
|
||||
*
|
||||
* @param image Input image.
|
||||
* @param roiRatios A vector of 4 float values (left, right, top, bottom) representing the ROI.
|
||||
* - Each value should be between 0 and 1.
|
||||
* @return A cv::Rect defining the cropped region.
|
||||
*/
|
||||
cv::Rect RTABMAP_CORE_EXPORT computeRoi(const cv::Mat & image, const std::vector<float> & roiRatios);
|
||||
|
||||
/**
|
||||
* @brief Computes a region of interest (ROI) using float ratios and the image size.
|
||||
*
|
||||
* @param imageSize The size of the image (width x height).
|
||||
* @param roiRatios A vector of 4 float values representing ratios for cropping:
|
||||
* - [left, right, top, bottom].
|
||||
* - Each must be in the range [0, 1), and valid (i.e., left + right < 1, top + bottom < 1).
|
||||
* @return The computed ROI rectangle.
|
||||
*/
|
||||
cv::Rect RTABMAP_CORE_EXPORT computeRoi(const cv::Size & imageSize, const std::vector<float> & roiRatios);
|
||||
|
||||
/** @} */ // end of RoiComputation group
|
||||
|
||||
/**
|
||||
* @brief Downsamples an image by a given decimation factor.
|
||||
*
|
||||
* If the image is a depth image (CV_32FC1 or CV_16UC1), it ensures that
|
||||
* decimation is done precisely without interpolation. For other types, OpenCV's
|
||||
* `resize` with `INTER_AREA` is used.
|
||||
*
|
||||
* @param image The input image to decimate.
|
||||
* @param decimation The downsampling factor (must be >= 1).
|
||||
* @return The decimated image. If the decimation factor is 1 or the image is empty, the original image is returned.
|
||||
*
|
||||
* @note For depth images, the image size must be divisible by the decimation factor.
|
||||
* @throw Assertion failure if decimation is < 1 or size mismatch for depth images.
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT decimate(const cv::Mat & image, int d);
|
||||
|
||||
/**
|
||||
* @brief Upsamples a depth image using bilinear interpolation with depth consistency check.
|
||||
*
|
||||
* Performs a depth-aware interpolation for CV_32FC1 or CV_16UC1 types. It checks whether
|
||||
* the surrounding four corner values are consistent within a `depthErrorRatio`, and if so,
|
||||
* performs bilinear interpolation. For other image types, OpenCV's `resize` is used.
|
||||
*
|
||||
* @param image The input image to interpolate.
|
||||
* @param factor The interpolation factor (must be >= 1).
|
||||
* @param depthErrorRatio Acceptable ratio of depth difference to allow interpolation.
|
||||
* @return The interpolated image. If the factor is 1 or the image is empty, the original image is returned.
|
||||
*
|
||||
* @note This function is intended for depth images. If corners have invalid or inconsistent depth values,
|
||||
* interpolation is skipped at that patch.
|
||||
* @throw Assertion failure if factor < 1 or invalid parameters.
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT interpolate(const cv::Mat & image, int factor, float depthErrorRatio = 0.02f);
|
||||
|
||||
/**
|
||||
* @brief Registers a depth image to a different camera frame (typically RGB).
|
||||
*
|
||||
* This function aligns the given depth image to the coordinate frame of an RGB camera
|
||||
* using the intrinsic parameters of both cameras and the extrinsic transformation between them.
|
||||
* The output is a depth image aligned to the RGB image dimensions and field of view.
|
||||
*
|
||||
* The function assumes the depth is either in meters (`CV_32FC1`) or in millimeters (`CV_16UC1`),
|
||||
* and it returns a registered depth image in the same format.
|
||||
*
|
||||
* @param depth The input depth image (type `CV_16UC1` in mm or `CV_32FC1` in meters).
|
||||
* @param depthK Intrinsic matrix of the depth camera (3x3, type `CV_64FC1`).
|
||||
* @param colorSize Size of the target RGB image (the output will match this size).
|
||||
* @param colorK Intrinsic matrix of the RGB camera (3x3, type `CV_64FC1`).
|
||||
* @param transform Transform from the RGB camera frame to depth camera frame.
|
||||
*
|
||||
* @return A depth image registered to the RGB image space, with the same type as the input depth.
|
||||
*
|
||||
* @throw Assertion failure if input validation fails (e.g., empty image, incorrect types or sizes).
|
||||
*
|
||||
* @note If multiple depth points project to the same RGB pixel, the closest one is kept.
|
||||
* This helps with occlusion handling when registering sparse/depth data.
|
||||
*/
|
||||
// Registration Depth to RGB (return registered depth image)
|
||||
cv::Mat RTABMAP_CORE_EXPORT registerDepth(
|
||||
const cv::Mat & depth,
|
||||
const cv::Mat & depthK,
|
||||
@@ -527,130 +135,27 @@ cv::Mat RTABMAP_CORE_EXPORT registerDepth(
|
||||
const rtabmap::Transform & transform,
|
||||
cv::Mat & registeredConfidence);
|
||||
|
||||
/**
|
||||
* @brief Fills holes in the depth image using linear interpolation.
|
||||
*
|
||||
* This function iterates through the depth image and fills in holes (missing depth values) by interpolating from
|
||||
* surrounding valid depth values. It considers both horizontal and vertical neighbors to interpolate missing data.
|
||||
* The maximum hole size and the error ratio are used to control the filling process. The function works with
|
||||
* both 16-bit (mm) and 32-bit (meters) depth images.
|
||||
*
|
||||
* @param depth The input depth image (CV_16UC1 or CV_32FC1).
|
||||
* @param maximumHoleSize The maximum size of a hole to be filled, in pixels.
|
||||
* @param errorRatio The ratio used to calculate the allowed depth error for interpolation.
|
||||
*
|
||||
* @return A new depth image with holes filled.
|
||||
*
|
||||
* @note The input depth image must be of type CV_16UC1 (depth in millimeters) or CV_32FC1 (depth in meters).
|
||||
* The filled output is of the same type as the input.
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT fillDepthHoles(
|
||||
const cv::Mat & depth,
|
||||
int maximumHoleSize = 1,
|
||||
float errorRatio = 0.02f);
|
||||
|
||||
/**
|
||||
* @brief Fill holes in a registered depth image using linear interpolation.
|
||||
*
|
||||
* This function attempts to fill invalid (zero-valued) pixels in a registered
|
||||
* depth image by looking at neighboring pixels in vertical and/or horizontal
|
||||
* directions. Optionally, it can also fill "double holes" (gaps of two consecutive
|
||||
* pixels) if `fillDoubleHoles` is enabled.
|
||||
*
|
||||
* The interpolation is only performed if the depth difference between the
|
||||
* neighbors is within 1% of their average, to avoid introducing invalid depth values.
|
||||
*
|
||||
* @param[in,out] registeredDepth The input/output registered depth image (CV_16UC1).
|
||||
* Modified in-place to fill in missing depth values.
|
||||
* @param vertical If true, the function tries to fill holes in vertical direction.
|
||||
* @param horizontal If true, the function tries to fill holes in horizontal direction.
|
||||
* @param fillDoubleHoles If true, the function also attempts to fill two-pixel wide holes
|
||||
* by linearly interpolating between values spaced by two pixels.
|
||||
*
|
||||
* @note This function assumes that the depth image contains unsigned 16-bit values,
|
||||
* where a value of 0 represents an invalid or missing depth value. Pixels on the
|
||||
* contour are not interpolated.
|
||||
*
|
||||
* @warning The input matrix must be of type CV_16UC1, or the function will trigger
|
||||
* an assertion failure.
|
||||
*
|
||||
* @see rtabmap::util2d::registerDepth(), rtabmap::util2d::fillDepthHoles()
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT fillRegisteredDepthHoles(
|
||||
cv::Mat & depthRegistered,
|
||||
bool vertical,
|
||||
bool horizontal,
|
||||
bool fillDoubleHoles = false);
|
||||
|
||||
/**
|
||||
* @brief Applies a 2D fast bilateral filter to a depth image.
|
||||
*
|
||||
* This function is a 2D adaptation of the pcl::FastBilateralFiltering algorithm.
|
||||
* It processes a depth image (either CV_32FC1 or CV_16UC1) using a bilateral
|
||||
* filter with spatial and range standard deviations `sigmaS` and `sigmaR`.
|
||||
* The method includes optimizations such as early division and efficient
|
||||
* 3D grid accumulation with smoothing.
|
||||
*
|
||||
* @param depth Input depth image. Must be of type CV_32FC1 (meters) or CV_16UC1 (millimeters).
|
||||
* @param sigmaS Spatial standard deviation. Controls the amount of smoothing in the image plane.
|
||||
* @param sigmaR Range standard deviation. Controls the amount of smoothing in the depth (z) dimension.
|
||||
* @param earlyDivision If true, applies early normalization to improve performance.
|
||||
*
|
||||
* @return Filtered depth image as a CV_32FC1 Mat. If the input image is empty or contains no valid depth,
|
||||
* an empty Mat is returned.
|
||||
*
|
||||
* @note This implementation relies on an auxiliary 3D data structure and uses trilinear interpolation
|
||||
* for reconstructing smoothed values. Pixels with non-finite or invalid depths are ignored.
|
||||
*
|
||||
* @warning The result is always a CV_32FC1 image, even if the input is CV_16UC1. If input depth's valid pixels have
|
||||
* all exact same value, the result will be retruned with all zeros (issue from the original implementation).
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT fastBilateralFiltering(
|
||||
const cv::Mat & depth,
|
||||
float sigmaS = 15.0f,
|
||||
float sigmaR = 0.05f,
|
||||
bool earlyDivision = false);
|
||||
|
||||
/**
|
||||
* @brief Filters out depth bleeding artifacts in a depth image.
|
||||
*
|
||||
* This function sets depth values to zero (invalid) if their value significantly differs
|
||||
* from both neighboring pixels in either horizontal or vertical direction. It works on
|
||||
* depth images of type `CV_32FC1` (32-bit float, in meters) or `CV_16UC1` (16-bit unsigned int, in millimeters).
|
||||
*
|
||||
* The function also ignores the image border by setting the first and last rows and columns to zero.
|
||||
*
|
||||
* @param depth Input/output depth image. Must be of type `CV_32FC1` or `CV_16UC1`.
|
||||
* The filtering is done in-place.
|
||||
* @param maxDepthError Maximum allowed depth difference between a pixel and its neighbors
|
||||
* before it is considered invalid and filtered out. For `CV_32FC1`,
|
||||
* this value is in meters; for `CV_16UC1`, it's converted to millimeters.
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT depthBleedingFiltering(
|
||||
cv::Mat & depth,
|
||||
float maxDepthError);
|
||||
|
||||
/**
|
||||
* @brief Automatic brightness and contrast optimization with optional histogram clipping.
|
||||
*
|
||||
* This function automatically adjusts the brightness and contrast of the input image based on
|
||||
* its histogram. It optionally clips a percentage of the darkest and brightest parts of the histogram
|
||||
* to reduce the influence of outliers (similar to "auto levels" in photo editors).
|
||||
*
|
||||
* @param[in] src Input image. Must be of type CV_8UC1 (grayscale), CV_8UC3 (BGR), or CV_8UC4 (BGRA).
|
||||
* @param[in] mask Optional mask. Only non-zero mask pixels are considered in histogram computation.
|
||||
* @param[in] clipLowHistPercent Percentage of the lowest histogram range to clip. Use 0 to disable.
|
||||
* @param[in] clipHighHistPercent Percentage of the highest histogram range to clip. Use 0 to disable.
|
||||
* @param[out] alphaOut Optional pointer to store the computed alpha (contrast scale factor).
|
||||
* @param[out] betaOut Optional pointer to store the computed beta (brightness shift factor).
|
||||
*
|
||||
* @return A new image with automatically adjusted brightness and contrast.
|
||||
* The image will have the same size and number of channels as the input.
|
||||
*
|
||||
* @note For BGRA input images, the alpha (transparency) channel is preserved and not modified.
|
||||
*
|
||||
* @see Original discussion: https://answers.opencv.org/question/75510/how-to-make-auto-adjustmentsbrightness-and-contrast-for-image-android-opencv-image-correction/
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT brightnessAndContrastAuto(
|
||||
const cv::Mat & src,
|
||||
const cv::Mat & mask,
|
||||
@@ -659,139 +164,42 @@ cv::Mat RTABMAP_CORE_EXPORT brightnessAndContrastAuto(
|
||||
float * alphaOut = 0,
|
||||
float * betaOut = 0);
|
||||
|
||||
/**
|
||||
* @brief Performs exposure fusion on a set of input images.
|
||||
*
|
||||
* This function blends multiple images with different exposures into a single
|
||||
* well-exposed image using the Mertens exposure fusion algorithm. It leverages
|
||||
* OpenCV's `createMergeMertens()` method (available in OpenCV 3 and above).
|
||||
*
|
||||
* @param images A vector of input images (typically CV_8UC3) to be fused. All images
|
||||
* should have the same size and type.
|
||||
*
|
||||
* @return A fused color image (CV_8UC3) with enhanced exposure. If OpenCV version is
|
||||
* below 3, the function returns a clone of the first image in the input vector.
|
||||
*
|
||||
* @note The output image is normalized to 8-bit color (0–255). Exposure fusion requires
|
||||
* OpenCV 3.0 or later.
|
||||
*
|
||||
* @warning If OpenCV version is lower than 3, exposure fusion is not performed and a warning is issued.
|
||||
*/
|
||||
cv::Mat RTABMAP_CORE_EXPORT exposureFusion(
|
||||
const std::vector<cv::Mat> & images);
|
||||
|
||||
/**
|
||||
* @brief Converts a color from HSV (Hue, Saturation, Value) to RGB.
|
||||
*
|
||||
* This function takes HSV color values and converts them to their corresponding
|
||||
* RGB representation using standard sector-based color conversion.
|
||||
*
|
||||
* @param[out] r Pointer to a float where the resulting red component (0.0–1.0) will be stored.
|
||||
* @param[out] g Pointer to a float where the resulting green component (0.0–1.0) will be stored.
|
||||
* @param[out] b Pointer to a float where the resulting blue component (0.0–1.0) will be stored.
|
||||
* @param[in] h Hue angle in degrees (0–360). Defines the color type.
|
||||
* @param[in] s Saturation (0.0–1.0). 0 is grayscale, 1 is full color.
|
||||
* @param[in] v Value (brightness) (0.0–1.0). 0 is black, 1 is full brightness.
|
||||
*
|
||||
* @note This function assumes `h` is in degrees. If `s` is 0, the resulting color is grayscale,
|
||||
* with R=G=B=V.
|
||||
*
|
||||
* @warning The output RGB values are in the 0.0 to 1.0 range, not 0–255.
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT HSVtoRGB( float *r, float *g, float *b, float h, float s, float v );
|
||||
|
||||
/**
|
||||
* @brief Applies Non-Maximum Suppression (NMS) to a set of keypoints.
|
||||
*
|
||||
* This function filters a set of input keypoints by applying a grid-based non-maximum suppression
|
||||
* (NMS) algorithm. It retains only the strongest keypoints (based on response value) while
|
||||
* ensuring that no two retained points are within a certain distance from each other.
|
||||
*
|
||||
* @param[in] ptsIn Input vector of keypoints.
|
||||
* @param[in] descriptorsIn Corresponding descriptors for the input keypoints. Can be empty.
|
||||
* @param[out] ptsOut Output vector of keypoints after NMS filtering.
|
||||
* @param[out] descriptorsOut Output descriptors corresponding to the filtered keypoints.
|
||||
* @param[in] dist_thresh Minimum allowed distance between retained keypoints (suppression radius).
|
||||
* @param[in] img_width Width of the image on which the keypoints are based.
|
||||
* @param[in] img_height Height of the image on which the keypoints are based.
|
||||
*
|
||||
* @note Keypoints are suppressed if they are within `dist_thresh` pixels of a stronger keypoint.
|
||||
* @note If `descriptorsIn` is empty, descriptor output will remain empty.
|
||||
* @note Assumes all keypoints lie within the image dimensions provided.
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT NMS(
|
||||
const std::vector<cv::KeyPoint> & ptsIn,
|
||||
const cv::Mat & descriptorsIn,
|
||||
std::vector<cv::KeyPoint> & ptsOut,
|
||||
cv::Mat & descriptorsOut,
|
||||
int dist_thresh, int img_width, int img_height);
|
||||
int border, int dist_thresh, int img_width, int img_height);
|
||||
|
||||
/**
|
||||
* @brief Applies the SSC (Suppression via Square Covering) algorithm to spatially select keypoints.
|
||||
*
|
||||
* This function selects a subset of keypoints that are uniformly distributed across the image
|
||||
* using a square covering method and binary search optimization to achieve a desired number of keypoints.
|
||||
*
|
||||
* @param[in] keypoints Input vector of keypoints to select from.
|
||||
* @param[in] maxKeypoints Desired upper bound on the number of output keypoints. The internal target is
|
||||
* first reduced by `round(maxKeypoints * tolerance)` so the result is always
|
||||
* less than or equal to this value.
|
||||
* @param[in] tolerance Relative tolerance applied to the reduced target (e.g., 0.1 allows ±10% of the
|
||||
* reduced target, not of `maxKeypoints`).
|
||||
* @param[in] cols Width of the image in pixels.
|
||||
* @param[in] rows Height of the image in pixels.
|
||||
* @param[in] indx Optional vector of indices to use instead of the original keypoints ordering.
|
||||
* If provided, should be the same size as `keypoints`. This allows for applying
|
||||
* SSC to a pre-sorted subset (e.g., top-N keypoints).
|
||||
*
|
||||
* @return A vector of indices corresponding to the selected keypoints in the input `keypoints` vector.
|
||||
*
|
||||
* @note The algorithm operates by covering the image with a grid of cells and retaining the most confident
|
||||
* keypoint in each uncovered cell while suppressing nearby keypoints within a computed square radius.
|
||||
* @note Uses binary search to find the optimal suppression radius so the number of selected keypoints is
|
||||
* within [effectiveMax * (1 - tolerance), effectiveMax * (1 + tolerance)], where
|
||||
* effectiveMax = maxKeypoints - round(maxKeypoints * tolerance).
|
||||
* @note Works best when `keypoints` are pre-sorted by response strength (e.g., strongest first).
|
||||
* @note If the `indx` vector is provided, the returned indices refer to the original list, not just `indx`.
|
||||
*/
|
||||
std::vector<int> RTABMAP_CORE_EXPORT SSC(
|
||||
const std::vector<cv::KeyPoint> & keypoints,
|
||||
int maxKeypoints,
|
||||
float tolerance,
|
||||
int cols,
|
||||
int rows,
|
||||
const std::vector<int> & indx = {});
|
||||
const std::vector<cv::KeyPoint> & keypoints, int maxKeypoints, float tolerance, int cols, int rows, const std::vector<int> & indx = {});
|
||||
|
||||
/**
|
||||
* @brief Rotates the input RGB and depth images to make them appear upright based on the camera's roll angle.
|
||||
* @brief Rotate images and camera model so that the top of the image is up.
|
||||
*
|
||||
* This function uses the camera's extrinsic parameters to determine if the captured image is rotated
|
||||
* (e.g., sideways or upside-down), and rotates it appropriately (by 90°, 180°, or 270°) to correct orientation.
|
||||
* It also updates the associated camera model to reflect the new transformation and adjusted image size.
|
||||
* The roll value of local transform of the camera model is used to estimate
|
||||
* if the images have to be rotated. If there is a pitch value higher than
|
||||
* 45 deg, the original images and camera model will be returned (no rotation will happen).
|
||||
* The return local transform of the camera model is updated accordingly. The distortion
|
||||
* model is ignored and won't be transfered to modified camera model, so this function
|
||||
* expects already rectified images.
|
||||
*
|
||||
* @param[in,out] model The camera model associated with the images. It will be updated to reflect the new orientation.
|
||||
* @param[in,out] rgb The RGB image to be rotated if necessary.
|
||||
* @param[in,out] depth The depth image to be rotated if necessary.
|
||||
*
|
||||
* @return True if the images were rotated, false if no rotation was needed or the pitch angle is too large for a reliable decision.
|
||||
*
|
||||
* @note The function:
|
||||
* - Ignores rotation if pitch > π/4 (too ambiguous to determine "up").
|
||||
* - Assumes roll is responsible for rotation (i.e., sideways capture).
|
||||
* - Applies necessary rotation and updates the camera intrinsics accordingly.
|
||||
* - Supports image types: RGB and depth must be valid OpenCV `cv::Mat`.
|
||||
* - Respects image transparency and depth values during rotation.
|
||||
*
|
||||
* @warning This function assumes that the camera's local transform includes the standard optical rotation.
|
||||
*
|
||||
* @see rtabmap::CameraModel, cv::transpose, cv::flip
|
||||
* @param model a valid camera model
|
||||
* @param rgb a rgb/grayscale image (set cv::Mat() if not used)
|
||||
* @param depth a depth image (set cv::Mat() if not used)
|
||||
* @return true if the model/images have been rotated, false otherwise
|
||||
*/
|
||||
bool RTABMAP_CORE_EXPORT rotateImagesUpsideUpIfNecessary(
|
||||
CameraModel & model,
|
||||
cv::Mat & rgb,
|
||||
cv::Mat & depth);
|
||||
|
||||
} // namespace util2d
|
||||
} // namespace util3d
|
||||
} // namespace rtabmap
|
||||
|
||||
#endif /* UTIL2D_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,12 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
#include <opencv2/core/version.hpp>
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/features2d/features2d.hpp>
|
||||
#else
|
||||
#include <opencv2/features.hpp>
|
||||
#endif
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <list>
|
||||
@@ -48,81 +43,39 @@ namespace rtabmap
|
||||
namespace util3d
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Extracts 3D point correspondences between two sets of labeled 3D points.
|
||||
*
|
||||
* This function identifies common point IDs (keys) between two multimap structures containing
|
||||
* `pcl::PointXYZ` points. For each shared key that appears **exactly once** in both input maps,
|
||||
* and where both corresponding points are finite, the matched points are added to two output point clouds.
|
||||
*
|
||||
* The resulting `cloud1` and `cloud2` point clouds will contain points with a one-to-one correspondence,
|
||||
* useful for geometric registration (e.g., ICP).
|
||||
*
|
||||
* @param words1 Input multimap of point ID to 3D point for the first dataset.
|
||||
* @param words2 Input multimap of point ID to 3D point for the second dataset.
|
||||
* @param cloud1 Output point cloud (corresponding to points from `words1`).
|
||||
* @param cloud2 Output point cloud (corresponding to points from `words2`).
|
||||
*
|
||||
* @note Only keys that appear exactly once in both `words1` and `words2`, and whose associated
|
||||
* `pcl::PointXYZ` entries are finite, will be included in the output clouds.
|
||||
*/
|
||||
|
||||
void RTABMAP_CORE_EXPORT findCorrespondences(
|
||||
const std::multimap<int, cv::KeyPoint> & wordsA,
|
||||
const std::multimap<int, cv::KeyPoint> & wordsB,
|
||||
std::list<std::pair<cv::Point2f, cv::Point2f> > & pairs);
|
||||
|
||||
void RTABMAP_CORE_EXPORT findCorrespondences(
|
||||
const std::multimap<int, cv::Point3f> & words1,
|
||||
const std::multimap<int, cv::Point3f> & words2,
|
||||
std::vector<cv::Point3f> & inliers1,
|
||||
std::vector<cv::Point3f> & inliers2,
|
||||
float maxDepth,
|
||||
std::vector<int> * uniqueCorrespondences = 0);
|
||||
|
||||
void RTABMAP_CORE_EXPORT findCorrespondences(
|
||||
const std::map<int, cv::Point3f> & words1,
|
||||
const std::map<int, cv::Point3f> & words2,
|
||||
std::vector<cv::Point3f> & inliers1,
|
||||
std::vector<cv::Point3f> & inliers2,
|
||||
float maxDepth,
|
||||
std::vector<int> * correspondences = 0);
|
||||
|
||||
// remove depth by z axis
|
||||
void RTABMAP_CORE_EXPORT extractXYZCorrespondences(const std::multimap<int, pcl::PointXYZ> & words1,
|
||||
const std::multimap<int, pcl::PointXYZ> & words2,
|
||||
pcl::PointCloud<pcl::PointXYZ> & cloud1,
|
||||
pcl::PointCloud<pcl::PointXYZ> & cloud2);
|
||||
|
||||
/**
|
||||
* @brief Extracts reliable 3D point correspondences between two sets of labeled 3D points using RANSAC filtering.
|
||||
*
|
||||
* This function finds correspondences between `words1` and `words2` based on shared unique keys. For each common key
|
||||
* that appears exactly once in both maps, and where the corresponding 3D points are finite, a candidate correspondence
|
||||
* is formed. If more than 7 such pairs exist, RANSAC is used via OpenCV’s `cv::findFundamentalMat` to reject outliers
|
||||
* based on the geometric consistency of the 2D projections.
|
||||
*
|
||||
* Only the inlier correspondences determined by RANSAC are returned in the output point clouds `cloud1` and `cloud2`.
|
||||
*
|
||||
* @param words1 Input multimap of point ID to `pcl::PointXYZ` for the first set of 3D features.
|
||||
* @param words2 Input multimap of point ID to `pcl::PointXYZ` for the second set of 3D features.
|
||||
* @param cloud1 Output point cloud containing inlier points from `words1`.
|
||||
* @param cloud2 Output point cloud containing inlier points from `words2`.
|
||||
*
|
||||
* @note At least 8 valid point correspondences are required for RANSAC to compute a fundamental matrix.
|
||||
* If fewer than 8 valid matches exist, the function does not modify the output clouds.
|
||||
*
|
||||
* @warning Only 2D `(x, y)` components of the 3D points are used for RANSAC filtering.
|
||||
*
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT extractXYZCorrespondencesRANSAC(const std::multimap<int, pcl::PointXYZ> & words1,
|
||||
const std::multimap<int, pcl::PointXYZ> & words2,
|
||||
pcl::PointCloud<pcl::PointXYZ> & cloud1,
|
||||
pcl::PointCloud<pcl::PointXYZ> & cloud2);
|
||||
|
||||
/**
|
||||
* @brief Extracts 3D point correspondences from 2D pixel matches using depth images.
|
||||
*
|
||||
* This function projects matched 2D keypoints (pixel correspondences) from two RGB-D images into 3D space
|
||||
* using the provided camera intrinsic parameters. Only valid and finite 3D points are retained. If a `maxDepth`
|
||||
* threshold is provided, points farther than this threshold are excluded.
|
||||
*
|
||||
* The function returns two synchronized point clouds, `cloud1` and `cloud2`, where each point pair at the
|
||||
* same index corresponds to a match between the two views.
|
||||
*
|
||||
* @param correspondences List of 2D point correspondences between image 1 and image 2.
|
||||
* @param depthImage1 Depth image corresponding to the first set of points (CV_32FC1 or CV_16UC1).
|
||||
* @param depthImage2 Depth image corresponding to the second set of points (same format as depthImage1).
|
||||
* @param cx Principal point x-coordinate (camera intrinsic).
|
||||
* @param cy Principal point y-coordinate (camera intrinsic).
|
||||
* @param fx Focal length in x-direction (camera intrinsic).
|
||||
* @param fy Focal length in y-direction (camera intrinsic).
|
||||
* @param maxDepth Maximum allowed depth for a correspondence to be considered valid. If <= 0, all depths are accepted.
|
||||
* @param cloud1 Output point cloud with 3D points corresponding to the first image.
|
||||
* @param cloud2 Output point cloud with 3D points corresponding to the second image.
|
||||
*
|
||||
* @note
|
||||
* - Both output point clouds are resized to contain only the valid 3D matches after filtering.
|
||||
* - Invalid, non-finite, or out-of-range depth values are automatically filtered out.
|
||||
*
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT extractXYZCorrespondences(const std::list<std::pair<cv::Point2f, cv::Point2f> > & correspondences,
|
||||
const cv::Mat & depthImage1,
|
||||
const cv::Mat & depthImage2,
|
||||
@@ -132,152 +85,28 @@ void RTABMAP_CORE_EXPORT extractXYZCorrespondences(const std::list<std::pair<cv:
|
||||
pcl::PointCloud<pcl::PointXYZ> & cloud1,
|
||||
pcl::PointCloud<pcl::PointXYZ> & cloud2);
|
||||
|
||||
/**
|
||||
* @brief Extracts 3D correspondences from 2D feature matches using `pcl::PointXYZ` organized point clouds.
|
||||
*
|
||||
* This function projects 2D keypoint matches into 3D using the corresponding organized
|
||||
* point clouds (`cloud1` and `cloud2`). Points that are not finite are discarded.
|
||||
*
|
||||
* @param correspondences List of 2D point correspondences between image 1 and image 2.
|
||||
* @param cloud1 Organized `pcl::PointXYZ` point cloud corresponding to the first image.
|
||||
* @param cloud2 Organized `pcl::PointXYZ` point cloud corresponding to the second image.
|
||||
* @param inliers1 Output 3D points from `cloud1` corresponding to valid 2D matches.
|
||||
* @param inliers2 Output 3D points from `cloud2` corresponding to valid 2D matches.
|
||||
*
|
||||
* @note
|
||||
* - Only organized point clouds are supported (i.e., width × height layout must match
|
||||
* image size from which 2D keypoints were taken).
|
||||
*
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT extractXYZCorrespondences(const std::list<std::pair<cv::Point2f, cv::Point2f> > & correspondences,
|
||||
const pcl::PointCloud<pcl::PointXYZ> & cloud1,
|
||||
const pcl::PointCloud<pcl::PointXYZ> & cloud2,
|
||||
pcl::PointCloud<pcl::PointXYZ> & inliers1,
|
||||
pcl::PointCloud<pcl::PointXYZ> & inliers2);
|
||||
/**
|
||||
* @brief Extracts 3D correspondences from 2D feature matches using `pcl::PointXYZRGB` organized point clouds.
|
||||
*
|
||||
* This overload behaves identically to the `pcl::PointXYZ` version, but supports input point clouds
|
||||
* that contain RGB color data. The color is not used—only the XYZ fields are extracted.
|
||||
*
|
||||
* @param correspondences List of matched 2D keypoints between two images.
|
||||
* @param cloud1 Organized `pcl::PointXYZRGB` point cloud for the first image.
|
||||
* @param cloud2 Organized `pcl::PointXYZRGB` point cloud for the second image.
|
||||
* @param inliers1 Output 3D points from `cloud1` corresponding to valid 2D matches.
|
||||
* @param inliers2 Output 3D points from `cloud2` corresponding to valid 2D matches.
|
||||
*/
|
||||
pcl::PointCloud<pcl::PointXYZ> & inliers2,
|
||||
char depthAxis);
|
||||
void RTABMAP_CORE_EXPORT extractXYZCorrespondences(const std::list<std::pair<cv::Point2f, cv::Point2f> > & correspondences,
|
||||
const pcl::PointCloud<pcl::PointXYZRGB> & cloud1,
|
||||
const pcl::PointCloud<pcl::PointXYZRGB> & cloud2,
|
||||
pcl::PointCloud<pcl::PointXYZ> & inliers1,
|
||||
pcl::PointCloud<pcl::PointXYZ> & inliers2);
|
||||
pcl::PointCloud<pcl::PointXYZ> & inliers2,
|
||||
char depthAxis);
|
||||
|
||||
/**
|
||||
* @brief Counts the number of unique 3D point correspondences between two sets of word-indexed features.
|
||||
*
|
||||
* This function iterates over the unique keys (word IDs) in `wordsA` and checks if the same key exists
|
||||
* in `wordsB`. A pair is considered "unique" if both `wordsA` and `wordsB` contain exactly one 3D point
|
||||
* (i.e., one `pcl::PointXYZ`) associated with the same key.
|
||||
*
|
||||
* @param wordsA A multimap of word IDs to 3D points (e.g., from frame A).
|
||||
* @param wordsB A multimap of word IDs to 3D points (e.g., from frame B).
|
||||
* @return The number of unique pairs where both `wordsA` and `wordsB` contain exactly one point for a given word ID.
|
||||
*/
|
||||
int RTABMAP_CORE_EXPORT countUniquePairs(const std::multimap<int, pcl::PointXYZ> & wordsA,
|
||||
const std::multimap<int, pcl::PointXYZ> & wordsB);
|
||||
|
||||
/**
|
||||
* @brief Filters pairs of 3D points by maximum depth along a specified axis and optionally removes duplicates.
|
||||
*
|
||||
* This function takes two point clouds (`inliers1` and `inliers2`) containing corresponding 3D points,
|
||||
* and filters out pairs where either point exceeds a specified maximum depth value along the given axis.
|
||||
* It can also optionally remove duplicate points in the first point cloud.
|
||||
*
|
||||
* @param[in,out] inliers1 The first point cloud of 3D points to be filtered. Points failing the filter will be removed.
|
||||
* @param[in,out] inliers2 The second point cloud of 3D points corresponding to `inliers1`. Points failing the filter will be removed.
|
||||
* Must be the same size as `inliers1`.
|
||||
* @param[in] maxDepth The maximum allowed depth value along the specified axis. Points with coordinate values greater or equal to
|
||||
* this value on that axis will be removed. If `maxDepth` is less or equal to zero, no filtering is performed.
|
||||
* @param[in] depthAxis The axis ('x', 'y', or 'z') along which to measure depth for filtering.
|
||||
* @param[in] removeDuplicates If `true`, duplicate points in `inliers1` (exact coordinate matches) will be removed.
|
||||
* Duplicates are detected only in `inliers1`.
|
||||
*
|
||||
* @warning The function modifies `inliers1` and `inliers2` in place, replacing them with filtered versions.
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT filterMaxDepth(pcl::PointCloud<pcl::PointXYZ> & inliers1,
|
||||
pcl::PointCloud<pcl::PointXYZ> & inliers2,
|
||||
float maxDepth,
|
||||
char depthAxis,
|
||||
bool removeDuplicates);
|
||||
|
||||
/**
|
||||
* @brief Finds 2D point correspondences between two sets of keypoints based on matching word IDs.
|
||||
*
|
||||
* This function compares two multimap structures containing word IDs associated with `cv::KeyPoint`s.
|
||||
* It extracts correspondences where the same word ID appears **exactly once** in each set.
|
||||
*
|
||||
* @param[in] wordsA A multimap from word ID to keypoints in set A.
|
||||
* @param[in] wordsB A multimap from word ID to keypoints in set B.
|
||||
* @param[out] pairs A list of matching 2D point correspondences (Point2f) between wordsA and wordsB.
|
||||
*
|
||||
* @note Only unique word ID matches (count == 1 in both sets) are considered valid correspondences.
|
||||
*
|
||||
* @par Example
|
||||
* If `wordsA = [1 2 3 4 6 6]` and `wordsB = [1 1 2 4 5 6 6]`, the output `pairs` will contain correspondences
|
||||
* for IDs `2` and `4`, because only those have exactly one match in both sets.
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT findCorrespondences(
|
||||
const std::multimap<int, cv::KeyPoint> & wordsA,
|
||||
const std::multimap<int, cv::KeyPoint> & wordsB,
|
||||
std::list<std::pair<cv::Point2f, cv::Point2f> > & pairs);
|
||||
|
||||
/**
|
||||
* @brief Finds 3D point correspondences between two sets of points based on matching word IDs.
|
||||
*
|
||||
* This function compares two multimaps of 3D points (typically from different views or frames).
|
||||
* It returns point pairs where the same word ID appears **once** in both maps, the points are finite and valid,
|
||||
* and optionally filtered by a maximum X-depth.
|
||||
*
|
||||
* @param[in] words1 A multimap of word IDs to 3D points in the first set.
|
||||
* @param[in] words2 A multimap of word IDs to 3D points in the second set.
|
||||
* @param[out] inliers1 Output vector of 3D points from `words1` with valid correspondences.
|
||||
* @param[out] inliers2 Output vector of corresponding 3D points from `words2`.
|
||||
* @param[in] maxDepth Optional filter: only points with X-values in (0, maxDepth] are kept. Use <= 0 to disable.
|
||||
* @param[out] uniqueCorrespondences (Optional) Vector of word IDs corresponding to each pair.
|
||||
*
|
||||
* @note Only pairs with exactly one occurrence in each map and non-zero, finite coordinates are kept.
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT findCorrespondences(
|
||||
const std::multimap<int, cv::Point3f> & words1,
|
||||
const std::multimap<int, cv::Point3f> & words2,
|
||||
std::vector<cv::Point3f> & inliers1,
|
||||
std::vector<cv::Point3f> & inliers2,
|
||||
float maxDepth,
|
||||
std::vector<int> * uniqueCorrespondences = 0);
|
||||
|
||||
/**
|
||||
* @brief Finds 3D point correspondences between two sets of uniquely indexed 3D points.
|
||||
*
|
||||
* This overload works with `std::map`, where each word ID appears at most once. It finds matching IDs
|
||||
* and returns valid point pairs based on similar criteria to the multimap version.
|
||||
*
|
||||
* @param[in] words1 A map of word IDs to 3D points in the first set.
|
||||
* @param[in] words2 A map of word IDs to 3D points in the second set.
|
||||
* @param[out] inliers1 Output vector of 3D points from `words1` with valid correspondences.
|
||||
* @param[out] inliers2 Output vector of corresponding 3D points from `words2`.
|
||||
* @param[in] maxDepth Optional filter: only points with X-values in (0, maxDepth] are kept. Use <= 0 to disable.
|
||||
* @param[out] correspondences (Optional) Vector of word IDs corresponding to valid matched pairs.
|
||||
*
|
||||
* @note Finite, non-zero points are required. The function ignores word IDs not found in both sets.
|
||||
*/
|
||||
void RTABMAP_CORE_EXPORT findCorrespondences(
|
||||
const std::map<int, cv::Point3f> & words1,
|
||||
const std::map<int, cv::Point3f> & words2,
|
||||
std::vector<cv::Point3f> & inliers1,
|
||||
std::vector<cv::Point3f> & inliers2,
|
||||
float maxDepth,
|
||||
std::vector<int> * correspondences = 0);
|
||||
|
||||
} // namespace util3d
|
||||
} // namespace rtabmap
|
||||
|
||||
|
||||
@@ -30,12 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <rtabmap/core/rtabmap_core_export.h>
|
||||
|
||||
#include <opencv2/core/version.hpp>
|
||||
#if CV_MAJOR_VERSION < 5
|
||||
#include <opencv2/calib3d/calib3d.hpp>
|
||||
#else
|
||||
#include <opencv2/calib.hpp>
|
||||
#endif
|
||||
#include <rtabmap/core/Transform.h>
|
||||
#include <rtabmap/core/CameraModel.h>
|
||||
#include <rtabmap/core/StereoCameraModel.h>
|
||||
@@ -48,41 +43,7 @@ namespace rtabmap
|
||||
namespace util3d
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Projects 2D keypoints to 3D space using the provided depth image and camera models.
|
||||
*
|
||||
* This function takes a vector of 2D keypoints and projects them into 3D space by using
|
||||
* depth values from a depth image and the associated camera models. It supports multi-camera setups
|
||||
* by assuming the depth image is horizontally stacked with sub-images corresponding to each camera.
|
||||
*
|
||||
* If a depth value at a keypoint location is invalid or outside the specified depth range
|
||||
* (`minDepth`, `maxDepth`), the output 3D point will be set to NaN.
|
||||
*
|
||||
* @param keypoints A vector of 2D keypoints (in image coordinates).
|
||||
* @param depth The depth image (must be either `CV_32FC1` or `CV_16UC1`).
|
||||
* For multiple cameras, the depth images should be horizontally concatenated.
|
||||
* @param cameraModels A vector of camera models, one per camera. Each model must provide intrinsic
|
||||
* parameters and optionally a local transform to apply to the resulting 3D point.
|
||||
* @param minDepth Minimum valid depth value. If negative, no minimum is enforced.
|
||||
* @param maxDepth Maximum valid depth value. If zero or negative, no maximum is enforced.
|
||||
*
|
||||
* @return A vector of 3D points (`cv::Point3f`) corresponding to the input keypoints.
|
||||
* If the depth is invalid or outside the valid range, the point will contain NaNs.
|
||||
*
|
||||
* @throws Assertion failure if the depth image is empty or not of the expected type,
|
||||
* or if the camera model vector is empty, or if camera index computation fails.
|
||||
*/
|
||||
std::vector<cv::Point3f> RTABMAP_CORE_EXPORT generateKeypoints3DDepth(
|
||||
const std::vector<cv::KeyPoint> & keypoints,
|
||||
const cv::Mat & depth,
|
||||
const std::vector<CameraModel> & cameraModels,
|
||||
float minDepth = 0,
|
||||
float maxDepth = 0);
|
||||
/**
|
||||
* @brief Projects 2D keypoints to 3D space using the provided depth image and camera model.
|
||||
*
|
||||
* @see util3d::generateKeypoints3DDepth()
|
||||
*/
|
||||
|
||||
std::vector<cv::Point3f> RTABMAP_CORE_EXPORT generateKeypoints3DDepth(
|
||||
const std::vector<cv::KeyPoint> & keypoints,
|
||||
const cv::Mat & depth,
|
||||
@@ -90,29 +51,13 @@ std::vector<cv::Point3f> RTABMAP_CORE_EXPORT generateKeypoints3DDepth(
|
||||
float minDepth = 0,
|
||||
float maxDepth = 0);
|
||||
|
||||
/**
|
||||
* @brief Projects 2D keypoints into 3D space using a disparity image and a stereo camera model.
|
||||
*
|
||||
* This function computes 3D coordinates for each input 2D keypoint by using the disparity image
|
||||
* and the stereo camera model. Invalid or out-of-range depth values result in 3D points with `NaN` components.
|
||||
*
|
||||
* The function applies the local transform of the left camera (from the stereo model) to each valid 3D point,
|
||||
* if the transform is not null or identity.
|
||||
*
|
||||
* @param keypoints A vector of 2D keypoints (image coordinates) to be projected into 3D.
|
||||
* @param disparity The disparity image (must be of type `CV_16SC1` or `CV_32F`).
|
||||
* Disparity values should correspond to the keypoints' locations.
|
||||
* @param stereoCameraModel A valid stereo camera model that provides projection parameters
|
||||
* and an optional local transform.
|
||||
* @param minDepth Minimum depth threshold. If negative, no minimum constraint is applied.
|
||||
* @param maxDepth Maximum depth threshold. If zero or negative, no maximum constraint is applied.
|
||||
*
|
||||
* @return A vector of 3D points (`cv::Point3f`) corresponding to the input keypoints.
|
||||
* Points with invalid or out-of-range depth are returned as `(NaN, NaN, NaN)`.
|
||||
*
|
||||
* @throws Assertion failure if the disparity image is empty or of incorrect type,
|
||||
* or if the stereo camera model is not valid for projection.
|
||||
*/
|
||||
std::vector<cv::Point3f> RTABMAP_CORE_EXPORT generateKeypoints3DDepth(
|
||||
const std::vector<cv::KeyPoint> & keypoints,
|
||||
const cv::Mat & depth,
|
||||
const std::vector<CameraModel> & cameraModels,
|
||||
float minDepth = 0,
|
||||
float maxDepth = 0);
|
||||
|
||||
std::vector<cv::Point3f> RTABMAP_CORE_EXPORT generateKeypoints3DDisparity(
|
||||
const std::vector<cv::KeyPoint> & keypoints,
|
||||
const cv::Mat & disparity,
|
||||
@@ -120,36 +65,6 @@ std::vector<cv::Point3f> RTABMAP_CORE_EXPORT generateKeypoints3DDisparity(
|
||||
float minDepth = 0,
|
||||
float maxDepth = 0);
|
||||
|
||||
/**
|
||||
* @brief Computes 3D keypoints from corresponding 2D points in a stereo image pair.
|
||||
*
|
||||
* This function triangulates 3D points from pairs of corresponding 2D points
|
||||
* (`leftCorners`, `rightCorners`) using a given stereo camera model. It optionally applies
|
||||
* a validity mask and filters 3D points by depth range.
|
||||
*
|
||||
* For each point pair, the disparity is computed as the x-coordinate difference between
|
||||
* left and right corners. Only positive disparities are considered valid. If a mask is
|
||||
* provided, only entries with a non-zero value are processed.
|
||||
*
|
||||
* The resulting 3D points are optionally transformed using the stereo camera model's local transform,
|
||||
* if one is defined and non-identity.
|
||||
*
|
||||
* Invalid or out-of-range points are set to `(NaN, NaN, NaN)`.
|
||||
*
|
||||
* @param leftCorners A vector of 2D points from the left stereo image.
|
||||
* @param rightCorners A vector of corresponding 2D points from the right stereo image.
|
||||
* @param model The stereo camera model containing intrinsic parameters and optional local transform.
|
||||
* @param mask (Optional) A binary mask indicating which matches are valid (non-zero = valid).
|
||||
* If empty, all matches are considered valid.
|
||||
* @param minDepth Minimum allowed depth value. If negative, no minimum is applied.
|
||||
* @param maxDepth Maximum allowed depth value. If zero or negative, no maximum is applied.
|
||||
*
|
||||
* @return A vector of 3D points (`cv::Point3f`) corresponding to valid stereo matches.
|
||||
* Invalid points or those outside the depth range are returned as `(NaN, NaN, NaN)`.
|
||||
*
|
||||
* @throws Assertion failure if the input vectors are inconsistent in size,
|
||||
* or if the stereo camera model is invalid (e.g., non-positive focal length or baseline).
|
||||
*/
|
||||
std::vector<cv::Point3f> RTABMAP_CORE_EXPORT generateKeypoints3DStereo(
|
||||
const std::vector<cv::Point2f> & leftCorners,
|
||||
const std::vector<cv::Point2f> & rightCorners,
|
||||
@@ -170,23 +85,6 @@ std::map<int, cv::Point3f> RTABMAP_CORE_EXPORT generateWords3DMono(
|
||||
double * variance = 0,
|
||||
std::vector<int> * matchesOut = 0);
|
||||
|
||||
/**
|
||||
* @brief Aggregates word IDs and corresponding keypoints into a multimap.
|
||||
*
|
||||
* This function pairs each word ID from the input list with the corresponding keypoint
|
||||
* from the input vector and stores them in a `std::multimap<int, cv::KeyPoint>`.
|
||||
*
|
||||
* It is assumed that the `wordIds` list and the `keypoints` vector are of the same length
|
||||
* and ordered such that each word ID corresponds to the keypoint at the same index.
|
||||
*
|
||||
* @param wordIds A list of integer word IDs (e.g., visual word identifiers).
|
||||
* @param keypoints A vector of keypoints associated with the word IDs.
|
||||
*
|
||||
* @return A multimap where each key is a word ID and the value is the corresponding `cv::KeyPoint`.
|
||||
* Multiple keypoints can be associated with the same word ID.
|
||||
*
|
||||
* @throws Assertion failure if `wordIds.size() != keypoints.size()`.
|
||||
*/
|
||||
std::multimap<int, cv::KeyPoint> RTABMAP_CORE_EXPORT aggregate(
|
||||
const std::list<int> & wordIds,
|
||||
const std::vector<cv::KeyPoint> & keypoints);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user