Merge branch 'develop' into fix/planeFactor
commit
b5789f6b80
|
|
@ -34,7 +34,7 @@ include(GtsamTesting)
|
||||||
include(GtsamPrinting)
|
include(GtsamPrinting)
|
||||||
|
|
||||||
# guard against in-source builds
|
# guard against in-source builds
|
||||||
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
|
if(${GTSAM_SOURCE_DIR} STREQUAL ${GTSAM_BINARY_DIR})
|
||||||
message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ")
|
message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -62,6 +62,11 @@ add_subdirectory(CppUnitLite)
|
||||||
|
|
||||||
# This is the new wrapper
|
# This is the new wrapper
|
||||||
if(GTSAM_BUILD_PYTHON)
|
if(GTSAM_BUILD_PYTHON)
|
||||||
|
# Need to set this for the wrap package so we don't use the default value.
|
||||||
|
set(WRAP_PYTHON_VERSION ${GTSAM_PYTHON_VERSION}
|
||||||
|
CACHE STRING "The Python version to use for wrapping")
|
||||||
|
|
||||||
|
add_subdirectory(wrap)
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/wrap/cmake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/wrap/cmake")
|
||||||
add_subdirectory(python)
|
add_subdirectory(python)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
31
INSTALL.md
31
INSTALL.md
|
|
@ -70,7 +70,34 @@ execute commands as follows for an out-of-source build:
|
||||||
This will build the library and unit tests, run all of the unit tests,
|
This will build the library and unit tests, run all of the unit tests,
|
||||||
and then install the library itself.
|
and then install the library itself.
|
||||||
|
|
||||||
## CMake Configuration Options and Details
|
# Windows Installation
|
||||||
|
|
||||||
|
This section details how to build a GTSAM `.sln` file using Visual Studio.
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Visual Studio with C++ CMake tools for Windows
|
||||||
|
- All the other pre-requisites listed above.
|
||||||
|
|
||||||
|
### Steps
|
||||||
|
|
||||||
|
1. Open Visual Studio.
|
||||||
|
2. Select `Open a local folder` and select the GTSAM source directory.
|
||||||
|
3. Go to `Project -> CMake Settings`.
|
||||||
|
- (Optional) Set `Configuration name`.
|
||||||
|
- (Optional) Set `Configuration type`.
|
||||||
|
- Set the `Toolset` to `msvc_x64_x64`. If you know what toolset you require, then skip this step.
|
||||||
|
- Update the `Build root` to `${projectDir}\build\${name}`.
|
||||||
|
- You can optionally create a new configuration for a `Release` build.
|
||||||
|
- Set the necessary CMake variables for your use case.
|
||||||
|
- Click on `Show advanced settings`.
|
||||||
|
- For `CMake generator`, select a version which matches `Visual Studio <Version> <Year> Win64`, e.g. `Visual Studio 16 2019 Win64`.
|
||||||
|
- Save the settings (Ctrl + S).
|
||||||
|
4. Click on `Project -> Generate Cache`. This will generate the CMake build files (as seen in the Output window).
|
||||||
|
5. The last step will generate a `GTSAM.sln` file in the `build` directory. At this point, GTSAM can be used as a regular Visual Studio project.
|
||||||
|
|
||||||
|
|
||||||
|
# CMake Configuration Options and Details
|
||||||
|
|
||||||
GTSAM has a number of options that can be configured, which is best done with
|
GTSAM has a number of options that can be configured, which is best done with
|
||||||
one of the following:
|
one of the following:
|
||||||
|
|
@ -78,7 +105,7 @@ one of the following:
|
||||||
- ccmake the curses GUI for cmake
|
- ccmake the curses GUI for cmake
|
||||||
- cmake-gui a real GUI for cmake
|
- cmake-gui a real GUI for cmake
|
||||||
|
|
||||||
### Important Options:
|
## Important Options:
|
||||||
|
|
||||||
#### CMAKE_BUILD_TYPE
|
#### CMAKE_BUILD_TYPE
|
||||||
We support several build configurations for GTSAM (case insensitive)
|
We support several build configurations for GTSAM (case insensitive)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
get_filename_component(OUR_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
get_filename_component(OUR_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||||
if(EXISTS "${OUR_CMAKE_DIR}/CMakeCache.txt")
|
if(EXISTS "${OUR_CMAKE_DIR}/CMakeCache.txt")
|
||||||
# In build tree
|
# In build tree
|
||||||
set(@PACKAGE_NAME@_INCLUDE_DIR @CMAKE_SOURCE_DIR@ CACHE PATH "@PACKAGE_NAME@ include directory")
|
set(@PACKAGE_NAME@_INCLUDE_DIR @GTSAM_SOURCE_DIR@ CACHE PATH "@PACKAGE_NAME@ include directory")
|
||||||
else()
|
else()
|
||||||
# Find installed library
|
# Find installed library
|
||||||
set(@PACKAGE_NAME@_INCLUDE_DIR "${OUR_CMAKE_DIR}/@CONF_REL_INCLUDE_DIR@" CACHE PATH "@PACKAGE_NAME@ include directory")
|
set(@PACKAGE_NAME@_INCLUDE_DIR "${OUR_CMAKE_DIR}/@CONF_REL_INCLUDE_DIR@" CACHE PATH "@PACKAGE_NAME@ include directory")
|
||||||
|
|
|
||||||
|
|
@ -204,9 +204,9 @@ endif()
|
||||||
|
|
||||||
# Make common binary output directory when on Windows
|
# Make common binary output directory when on Windows
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(RUNTIME_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin")
|
set(RUNTIME_OUTPUT_PATH "${GTSAM_BINARY_DIR}/bin")
|
||||||
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}/bin")
|
set(EXECUTABLE_OUTPUT_PATH "${GTSAM_BINARY_DIR}/bin")
|
||||||
set(LIBRARY_OUTPUT_PATH "${CMAKE_BINARY_DIR}/lib")
|
set(LIBRARY_OUTPUT_PATH "${GTSAM_BINARY_DIR}/lib")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Set up build type list for cmake-gui
|
# Set up build type list for cmake-gui
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,19 @@
|
||||||
|
# Check / set dependent variables for MATLAB wrapper
|
||||||
|
if(GTSAM_INSTALL_MATLAB_TOOLBOX)
|
||||||
|
find_package(Matlab COMPONENTS MEX_COMPILER REQUIRED)
|
||||||
|
if(NOT Matlab_MEX_COMPILER)
|
||||||
|
message(FATAL_ERROR "Cannot find MEX compiler binary. Please check your Matlab installation and ensure MEX in installed as well.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(GTSAM_BUILD_TYPE_POSTFIXES)
|
||||||
|
set(CURRENT_POSTFIX ${CMAKE_${CMAKE_BUILD_TYPE_UPPER}_POSTFIX})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT BUILD_SHARED_LIBS)
|
||||||
|
message(FATAL_ERROR "GTSAM_INSTALL_MATLAB_TOOLBOX and BUILD_SHARED_LIBS=OFF. The MATLAB wrapper cannot be compiled with a static GTSAM library because mex modules are themselves shared libraries. If you want a self-contained mex module, enable GTSAM_MEX_BUILD_STATIC_MODULE instead of BUILD_SHARED_LIBS=OFF.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Set up cache options
|
# Set up cache options
|
||||||
option(GTSAM_MEX_BUILD_STATIC_MODULE "Build MATLAB wrapper statically (increases build time)" OFF)
|
option(GTSAM_MEX_BUILD_STATIC_MODULE "Build MATLAB wrapper statically (increases build time)" OFF)
|
||||||
set(GTSAM_BUILD_MEX_BINARY_FLAGS "" CACHE STRING "Extra flags for running Matlab MEX compilation")
|
set(GTSAM_BUILD_MEX_BINARY_FLAGS "" CACHE STRING "Extra flags for running Matlab MEX compilation")
|
||||||
|
|
@ -12,30 +28,27 @@ if(GTSAM_MEX_BUILD_STATIC_MODULE AND WIN32)
|
||||||
message(FATAL_ERROR "GTSAM_MEX_BUILD_STATIC_MODULE should not be set on Windows - the linker already automatically compiles in any dependent static libraries. To create a standalone toolbox pacakge, simply ensure that CMake finds the static versions of all dependent libraries (Boost, etc).")
|
message(FATAL_ERROR "GTSAM_MEX_BUILD_STATIC_MODULE should not be set on Windows - the linker already automatically compiles in any dependent static libraries. To create a standalone toolbox pacakge, simply ensure that CMake finds the static versions of all dependent libraries (Boost, etc).")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Try to automatically configure mex path
|
set(MEX_COMMAND ${Matlab_MEX_COMPILER} CACHE PATH "Path to MATLAB MEX compiler")
|
||||||
if(APPLE)
|
set(MATLAB_ROOT ${Matlab_ROOT_DIR} CACHE PATH "Path to MATLAB installation root (e.g. /usr/local/MATLAB/R2012a)")
|
||||||
file(GLOB matlab_bin_directories "/Applications/MATLAB*/bin")
|
|
||||||
set(mex_program_name "mex")
|
# Try to automatically configure mex path from provided custom `bin` path.
|
||||||
elseif(WIN32)
|
if(GTSAM_CUSTOM_MATLAB_PATH)
|
||||||
file(GLOB matlab_bin_directories "C:/Program Files*/MATLAB/*/bin")
|
set(matlab_bin_directory ${GTSAM_CUSTOM_MATLAB_PATH})
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
set(mex_program_name "mex.bat")
|
set(mex_program_name "mex.bat")
|
||||||
else()
|
else()
|
||||||
file(GLOB matlab_bin_directories "/usr/local/MATLAB/*/bin")
|
|
||||||
set(mex_program_name "mex")
|
set(mex_program_name "mex")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(GTSAM_CUSTOM_MATLAB_PATH)
|
|
||||||
set(matlab_bin_directories ${GTSAM_CUSTOM_MATLAB_PATH})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Run find_program explicitly putting $PATH after our predefined program
|
# Run find_program explicitly putting $PATH after our predefined program
|
||||||
# directories using 'ENV PATH' and 'NO_SYSTEM_ENVIRONMENT_PATH' - this prevents
|
# directories using 'ENV PATH' and 'NO_SYSTEM_ENVIRONMENT_PATH' - this prevents
|
||||||
# finding the LaTeX mex program (totally unrelated to MATLAB Mex) when LaTeX is
|
# finding the LaTeX mex program (totally unrelated to MATLAB Mex) when LaTeX is
|
||||||
# on the system path.
|
# on the system path.
|
||||||
list(REVERSE matlab_bin_directories) # Reverse list so the highest version (sorted alphabetically) is preferred
|
|
||||||
find_program(MEX_COMMAND ${mex_program_name}
|
find_program(MEX_COMMAND ${mex_program_name}
|
||||||
PATHS ${matlab_bin_directories} ENV PATH
|
PATHS ${matlab_bin_directory} ENV PATH
|
||||||
NO_DEFAULT_PATH)
|
NO_DEFAULT_PATH)
|
||||||
|
|
||||||
mark_as_advanced(FORCE MEX_COMMAND)
|
mark_as_advanced(FORCE MEX_COMMAND)
|
||||||
# Now that we have mex, trace back to find the Matlab installation root
|
# Now that we have mex, trace back to find the Matlab installation root
|
||||||
get_filename_component(MEX_COMMAND "${MEX_COMMAND}" REALPATH)
|
get_filename_component(MEX_COMMAND "${MEX_COMMAND}" REALPATH)
|
||||||
|
|
@ -45,7 +58,7 @@ if(mex_path MATCHES ".*/win64$")
|
||||||
else()
|
else()
|
||||||
get_filename_component(MATLAB_ROOT "${mex_path}/.." ABSOLUTE)
|
get_filename_component(MATLAB_ROOT "${mex_path}/.." ABSOLUTE)
|
||||||
endif()
|
endif()
|
||||||
set(MATLAB_ROOT "${MATLAB_ROOT}" CACHE PATH "Path to MATLAB installation root (e.g. /usr/local/MATLAB/R2012a)")
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# User-friendly wrapping function. Builds a mex module from the provided
|
# User-friendly wrapping function. Builds a mex module from the provided
|
||||||
|
|
@ -227,12 +240,16 @@ function(wrap_library_internal interfaceHeader linkLibraries extraIncludeDirs ex
|
||||||
|
|
||||||
set(_ignore gtsam::Point2
|
set(_ignore gtsam::Point2
|
||||||
gtsam::Point3)
|
gtsam::Point3)
|
||||||
|
|
||||||
|
# set the matlab wrapping script variable
|
||||||
|
set(MATLAB_WRAP_SCRIPT "${GTSAM_SOURCE_DIR}/wrap/scripts/matlab_wrap.py")
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${generated_cpp_file}
|
OUTPUT ${generated_cpp_file}
|
||||||
DEPENDS ${interfaceHeader} ${module_library_target} ${otherLibraryTargets} ${otherSourcesAndObjects}
|
DEPENDS ${interfaceHeader} ${module_library_target} ${otherLibraryTargets} ${otherSourcesAndObjects}
|
||||||
COMMAND
|
COMMAND
|
||||||
${PYTHON_EXECUTABLE}
|
${PYTHON_EXECUTABLE}
|
||||||
${CMAKE_SOURCE_DIR}/wrap/matlab_wrapper.py
|
${MATLAB_WRAP_SCRIPT}
|
||||||
--src ${interfaceHeader}
|
--src ${interfaceHeader}
|
||||||
--module_name ${moduleName}
|
--module_name ${moduleName}
|
||||||
--out ${generated_files_path}
|
--out ${generated_files_path}
|
||||||
|
|
@ -418,4 +435,3 @@ function(install_matlab_scripts source_directory patterns)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ else()
|
||||||
set(GTSAM_EIGEN_INCLUDE_FOR_INSTALL "include/gtsam/3rdparty/Eigen/")
|
set(GTSAM_EIGEN_INCLUDE_FOR_INSTALL "include/gtsam/3rdparty/Eigen/")
|
||||||
|
|
||||||
# The actual include directory (for BUILD cmake target interface):
|
# The actual include directory (for BUILD cmake target interface):
|
||||||
set(GTSAM_EIGEN_INCLUDE_FOR_BUILD "${CMAKE_SOURCE_DIR}/gtsam/3rdparty/Eigen/")
|
set(GTSAM_EIGEN_INCLUDE_FOR_BUILD "${GTSAM_SOURCE_DIR}/gtsam/3rdparty/Eigen/")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Detect Eigen version:
|
# Detect Eigen version:
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ option(GTSAM_WITH_EIGEN_MKL "Eigen will use Intel MKL if available"
|
||||||
option(GTSAM_WITH_EIGEN_MKL_OPENMP "Eigen, when using Intel MKL, will also use OpenMP for multithreading if available" OFF)
|
option(GTSAM_WITH_EIGEN_MKL_OPENMP "Eigen, when using Intel MKL, will also use OpenMP for multithreading if available" OFF)
|
||||||
option(GTSAM_THROW_CHEIRALITY_EXCEPTION "Throw exception when a triangulated point is behind a camera" ON)
|
option(GTSAM_THROW_CHEIRALITY_EXCEPTION "Throw exception when a triangulated point is behind a camera" ON)
|
||||||
option(GTSAM_BUILD_PYTHON "Enable/Disable building & installation of Python module with pybind11" OFF)
|
option(GTSAM_BUILD_PYTHON "Enable/Disable building & installation of Python module with pybind11" OFF)
|
||||||
|
option(GTSAM_INSTALL_MATLAB_TOOLBOX "Enable/Disable installation of matlab toolbox" OFF)
|
||||||
option(GTSAM_ALLOW_DEPRECATED_SINCE_V41 "Allow use of methods/functions deprecated in GTSAM 4.1" ON)
|
option(GTSAM_ALLOW_DEPRECATED_SINCE_V41 "Allow use of methods/functions deprecated in GTSAM 4.1" ON)
|
||||||
option(GTSAM_SUPPORT_NESTED_DISSECTION "Support Metis-based nested dissection" ON)
|
option(GTSAM_SUPPORT_NESTED_DISSECTION "Support Metis-based nested dissection" ON)
|
||||||
option(GTSAM_TANGENT_PREINTEGRATION "Use new ImuFactor with integration on tangent space" ON)
|
option(GTSAM_TANGENT_PREINTEGRATION "Use new ImuFactor with integration on tangent space" ON)
|
||||||
|
|
@ -40,16 +41,5 @@ elseif(GTSAM_ROT3_EXPMAP)
|
||||||
set(GTSAM_POSE3_EXPMAP 1 CACHE BOOL "" FORCE)
|
set(GTSAM_POSE3_EXPMAP 1 CACHE BOOL "" FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Options relating to MATLAB wrapper
|
# Set the default Python version. This is later updated in HandlePython.cmake.
|
||||||
# TODO: Check for matlab mex binary before handling building of binaries
|
|
||||||
option(GTSAM_INSTALL_MATLAB_TOOLBOX "Enable/Disable installation of matlab toolbox" OFF)
|
|
||||||
set(GTSAM_PYTHON_VERSION "Default" CACHE STRING "The version of Python to build the wrappers against.")
|
set(GTSAM_PYTHON_VERSION "Default" CACHE STRING "The version of Python to build the wrappers against.")
|
||||||
|
|
||||||
# Check / set dependent variables for MATLAB wrapper
|
|
||||||
if(GTSAM_INSTALL_MATLAB_TOOLBOX AND GTSAM_BUILD_TYPE_POSTFIXES)
|
|
||||||
set(CURRENT_POSTFIX ${CMAKE_${CMAKE_BUILD_TYPE_UPPER}_POSTFIX})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(GTSAM_INSTALL_MATLAB_TOOLBOX AND NOT BUILD_SHARED_LIBS)
|
|
||||||
message(FATAL_ERROR "GTSAM_INSTALL_MATLAB_TOOLBOX and BUILD_SHARED_LIBS=OFF. The MATLAB wrapper cannot be compiled with a static GTSAM library because mex modules are themselves shared libraries. If you want a self-contained mex module, enable GTSAM_MEX_BUILD_STATIC_MODULE instead of BUILD_SHARED_LIBS=OFF.")
|
|
||||||
endif()
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,48 @@
|
||||||
# Set Python version if either Python or MATLAB wrapper is requested.
|
# Set Python version if either Python or MATLAB wrapper is requested.
|
||||||
if(GTSAM_BUILD_PYTHON OR GTSAM_INSTALL_MATLAB_TOOLBOX)
|
if(GTSAM_BUILD_PYTHON OR GTSAM_INSTALL_MATLAB_TOOLBOX)
|
||||||
if(${GTSAM_PYTHON_VERSION} STREQUAL "Default")
|
if(${GTSAM_PYTHON_VERSION} STREQUAL "Default")
|
||||||
|
|
||||||
|
if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
|
||||||
|
# Use older version of cmake's find_python
|
||||||
|
find_package(PythonInterp)
|
||||||
|
|
||||||
|
if(NOT ${PYTHONINTERP_FOUND})
|
||||||
|
message(
|
||||||
|
FATAL_ERROR
|
||||||
|
"Cannot find Python interpreter. Please install Python >= 3.6.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(PythonLibs ${PYTHON_VERSION_STRING})
|
||||||
|
|
||||||
|
set(Python_VERSION_MAJOR ${PYTHON_VERSION_MAJOR})
|
||||||
|
set(Python_VERSION_MINOR ${PYTHON_VERSION_MINOR})
|
||||||
|
set(Python_EXECUTABLE ${PYTHON_EXECUTABLE})
|
||||||
|
|
||||||
|
else()
|
||||||
# Get info about the Python3 interpreter
|
# Get info about the Python3 interpreter
|
||||||
# https://cmake.org/cmake/help/latest/module/FindPython3.html#module:FindPython3
|
# https://cmake.org/cmake/help/latest/module/FindPython3.html#module:FindPython3
|
||||||
find_package(Python3 COMPONENTS Interpreter Development)
|
find_package(Python3 COMPONENTS Interpreter Development)
|
||||||
|
|
||||||
if(NOT ${Python3_FOUND})
|
if(NOT ${Python3_FOUND})
|
||||||
message(FATAL_ERROR "Cannot find Python3 interpreter. Please install Python >= 3.6.")
|
message(
|
||||||
|
FATAL_ERROR
|
||||||
|
"Cannot find Python3 interpreter. Please install Python >= 3.6.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(GTSAM_PYTHON_VERSION "${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}"
|
set(Python_VERSION_MAJOR ${Python3_VERSION_MAJOR})
|
||||||
CACHE
|
set(Python_VERSION_MINOR ${Python3_VERSION_MINOR})
|
||||||
STRING
|
|
||||||
"The version of Python to build the wrappers against."
|
endif()
|
||||||
|
|
||||||
|
set(GTSAM_PYTHON_VERSION
|
||||||
|
"${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}"
|
||||||
|
CACHE STRING "The version of Python to build the wrappers against."
|
||||||
FORCE)
|
FORCE)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Check for build of Unstable modules
|
||||||
if(GTSAM_BUILD_PYTHON)
|
if(GTSAM_BUILD_PYTHON)
|
||||||
if(GTSAM_UNSTABLE_BUILD_PYTHON)
|
if(GTSAM_UNSTABLE_BUILD_PYTHON)
|
||||||
if (NOT GTSAM_BUILD_UNSTABLE)
|
if (NOT GTSAM_BUILD_UNSTABLE)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -125,7 +125,7 @@ int main(int argc, char* argv[]) {
|
||||||
output_filename = var_map["output_filename"].as<string>();
|
output_filename = var_map["output_filename"].as<string>();
|
||||||
use_isam = var_map["use_isam"].as<bool>();
|
use_isam = var_map["use_isam"].as<bool>();
|
||||||
|
|
||||||
ISAM2* isam2;
|
ISAM2* isam2 = 0;
|
||||||
if (use_isam) {
|
if (use_isam) {
|
||||||
printf("Using ISAM2\n");
|
printf("Using ISAM2\n");
|
||||||
ISAM2Params parameters;
|
ISAM2Params parameters;
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ int main(const int argc, const char* argv[]) {
|
||||||
auto priorModel = noiseModel::Diagonal::Variances(
|
auto priorModel = noiseModel::Diagonal::Variances(
|
||||||
(Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
|
(Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
|
||||||
Key firstKey = 0;
|
Key firstKey = 0;
|
||||||
for (const Values::ConstKeyValuePair& key_value : *initial) {
|
for (const auto key_value : *initial) {
|
||||||
std::cout << "Adding prior to g2o file " << std::endl;
|
std::cout << "Adding prior to g2o file " << std::endl;
|
||||||
firstKey = key_value.key;
|
firstKey = key_value.key;
|
||||||
graph->addPrior(firstKey, Pose3(), priorModel);
|
graph->addPrior(firstKey, Pose3(), priorModel);
|
||||||
|
|
@ -74,7 +74,7 @@ int main(const int argc, const char* argv[]) {
|
||||||
|
|
||||||
// Calculate and print marginal covariances for all variables
|
// Calculate and print marginal covariances for all variables
|
||||||
Marginals marginals(*graph, result);
|
Marginals marginals(*graph, result);
|
||||||
for (const auto& key_value : result) {
|
for (const auto key_value : result) {
|
||||||
auto p = dynamic_cast<const GenericValue<Pose3>*>(&key_value.value);
|
auto p = dynamic_cast<const GenericValue<Pose3>*>(&key_value.value);
|
||||||
if (!p) continue;
|
if (!p) continue;
|
||||||
std::cout << marginals.marginalCovariance(key_value.key) << endl;
|
std::cout << marginals.marginalCovariance(key_value.key) << endl;
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ int main(const int argc, const char *argv[]) {
|
||||||
std::cout << "Rewriting input to file: " << inputFileRewritten << std::endl;
|
std::cout << "Rewriting input to file: " << inputFileRewritten << std::endl;
|
||||||
// Additional: rewrite input with simplified keys 0,1,...
|
// Additional: rewrite input with simplified keys 0,1,...
|
||||||
Values simpleInitial;
|
Values simpleInitial;
|
||||||
for(const Values::ConstKeyValuePair& key_value: *initial) {
|
for(const auto key_value: *initial) {
|
||||||
Key key;
|
Key key;
|
||||||
if(add)
|
if(add)
|
||||||
key = key_value.key + firstKey;
|
key = key_value.key + firstKey;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ int main(const int argc, const char* argv[]) {
|
||||||
auto priorModel = noiseModel::Diagonal::Variances(
|
auto priorModel = noiseModel::Diagonal::Variances(
|
||||||
(Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
|
(Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
|
||||||
Key firstKey = 0;
|
Key firstKey = 0;
|
||||||
for (const Values::ConstKeyValuePair& key_value : *initial) {
|
for (const auto key_value : *initial) {
|
||||||
std::cout << "Adding prior to g2o file " << std::endl;
|
std::cout << "Adding prior to g2o file " << std::endl;
|
||||||
firstKey = key_value.key;
|
firstKey = key_value.key;
|
||||||
graph->addPrior(firstKey, Pose3(), priorModel);
|
graph->addPrior(firstKey, Pose3(), priorModel);
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ int main(const int argc, const char* argv[]) {
|
||||||
auto priorModel = noiseModel::Diagonal::Variances(
|
auto priorModel = noiseModel::Diagonal::Variances(
|
||||||
(Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
|
(Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
|
||||||
Key firstKey = 0;
|
Key firstKey = 0;
|
||||||
for (const Values::ConstKeyValuePair& key_value : *initial) {
|
for (const auto key_value : *initial) {
|
||||||
std::cout << "Adding prior to g2o file " << std::endl;
|
std::cout << "Adding prior to g2o file " << std::endl;
|
||||||
firstKey = key_value.key;
|
firstKey = key_value.key;
|
||||||
graph->addPrior(firstKey, Pose3(), priorModel);
|
graph->addPrior(firstKey, Pose3(), priorModel);
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ int main(const int argc, const char* argv[]) {
|
||||||
auto priorModel = noiseModel::Diagonal::Variances(
|
auto priorModel = noiseModel::Diagonal::Variances(
|
||||||
(Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
|
(Vector(6) << 1e-6, 1e-6, 1e-6, 1e-4, 1e-4, 1e-4).finished());
|
||||||
Key firstKey = 0;
|
Key firstKey = 0;
|
||||||
for (const Values::ConstKeyValuePair& key_value : *initial) {
|
for (const auto key_value : *initial) {
|
||||||
std::cout << "Adding prior to g2o file " << std::endl;
|
std::cout << "Adding prior to g2o file " << std::endl;
|
||||||
firstKey = key_value.key;
|
firstKey = key_value.key;
|
||||||
graph->addPrior(firstKey, Pose3(), priorModel);
|
graph->addPrior(firstKey, Pose3(), priorModel);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@
|
||||||
* Read 3D dataset sphere25000.txt and output to shonan.g2o (default)
|
* Read 3D dataset sphere25000.txt and output to shonan.g2o (default)
|
||||||
* ./ShonanAveragingCLI -i spere2500.txt
|
* ./ShonanAveragingCLI -i spere2500.txt
|
||||||
*
|
*
|
||||||
|
* If you prefer using a robust Huber loss, you can add the option "-h true",
|
||||||
|
* for instance
|
||||||
|
* ./ShonanAveragingCLI -i spere2500.txt -h true
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtsam/base/timing.h>
|
#include <gtsam/base/timing.h>
|
||||||
|
|
@ -43,7 +46,8 @@ int main(int argc, char* argv[]) {
|
||||||
string datasetName;
|
string datasetName;
|
||||||
string inputFile;
|
string inputFile;
|
||||||
string outputFile;
|
string outputFile;
|
||||||
int d, seed;
|
int d, seed, pMin;
|
||||||
|
bool useHuberLoss;
|
||||||
po::options_description desc(
|
po::options_description desc(
|
||||||
"Shonan Rotation Averaging CLI reads a *pose* graph, extracts the "
|
"Shonan Rotation Averaging CLI reads a *pose* graph, extracts the "
|
||||||
"rotation constraints, and runs the Shonan algorithm.");
|
"rotation constraints, and runs the Shonan algorithm.");
|
||||||
|
|
@ -58,6 +62,10 @@ int main(int argc, char* argv[]) {
|
||||||
"Write solution to the specified file")(
|
"Write solution to the specified file")(
|
||||||
"dimension,d", po::value<int>(&d)->default_value(3),
|
"dimension,d", po::value<int>(&d)->default_value(3),
|
||||||
"Optimize over 2D or 3D rotations")(
|
"Optimize over 2D or 3D rotations")(
|
||||||
|
"useHuberLoss,h", po::value<bool>(&useHuberLoss)->default_value(false),
|
||||||
|
"set True to use Huber loss")("pMin,p",
|
||||||
|
po::value<int>(&pMin)->default_value(3),
|
||||||
|
"set to use desired rank pMin")(
|
||||||
"seed,s", po::value<int>(&seed)->default_value(42),
|
"seed,s", po::value<int>(&seed)->default_value(42),
|
||||||
"Random seed for initial estimate");
|
"Random seed for initial estimate");
|
||||||
po::variables_map vm;
|
po::variables_map vm;
|
||||||
|
|
@ -85,11 +93,14 @@ int main(int argc, char* argv[]) {
|
||||||
NonlinearFactorGraph::shared_ptr inputGraph;
|
NonlinearFactorGraph::shared_ptr inputGraph;
|
||||||
Values::shared_ptr posesInFile;
|
Values::shared_ptr posesInFile;
|
||||||
Values poses;
|
Values poses;
|
||||||
|
auto lmParams = LevenbergMarquardtParams::CeresDefaults();
|
||||||
if (d == 2) {
|
if (d == 2) {
|
||||||
cout << "Running Shonan averaging for SO(2) on " << inputFile << endl;
|
cout << "Running Shonan averaging for SO(2) on " << inputFile << endl;
|
||||||
ShonanAveraging2 shonan(inputFile);
|
ShonanAveraging2::Parameters parameters(lmParams);
|
||||||
|
parameters.setUseHuber(useHuberLoss);
|
||||||
|
ShonanAveraging2 shonan(inputFile, parameters);
|
||||||
auto initial = shonan.initializeRandomly(rng);
|
auto initial = shonan.initializeRandomly(rng);
|
||||||
auto result = shonan.run(initial);
|
auto result = shonan.run(initial, pMin);
|
||||||
|
|
||||||
// Parse file again to set up translation problem, adding a prior
|
// Parse file again to set up translation problem, adding a prior
|
||||||
boost::tie(inputGraph, posesInFile) = load2D(inputFile);
|
boost::tie(inputGraph, posesInFile) = load2D(inputFile);
|
||||||
|
|
@ -101,9 +112,11 @@ int main(int argc, char* argv[]) {
|
||||||
poses = initialize::computePoses<Pose2>(result.first, &poseGraph);
|
poses = initialize::computePoses<Pose2>(result.first, &poseGraph);
|
||||||
} else if (d == 3) {
|
} else if (d == 3) {
|
||||||
cout << "Running Shonan averaging for SO(3) on " << inputFile << endl;
|
cout << "Running Shonan averaging for SO(3) on " << inputFile << endl;
|
||||||
ShonanAveraging3 shonan(inputFile);
|
ShonanAveraging3::Parameters parameters(lmParams);
|
||||||
|
parameters.setUseHuber(useHuberLoss);
|
||||||
|
ShonanAveraging3 shonan(inputFile, parameters);
|
||||||
auto initial = shonan.initializeRandomly(rng);
|
auto initial = shonan.initializeRandomly(rng);
|
||||||
auto result = shonan.run(initial);
|
auto result = shonan.run(initial, pMin);
|
||||||
|
|
||||||
// Parse file again to set up translation problem, adding a prior
|
// Parse file again to set up translation problem, adding a prior
|
||||||
boost::tie(inputGraph, posesInFile) = load3D(inputFile);
|
boost::tie(inputGraph, posesInFile) = load3D(inputFile);
|
||||||
|
|
@ -118,7 +131,7 @@ int main(int argc, char* argv[]) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
cout << "Writing result to " << outputFile << endl;
|
cout << "Writing result to " << outputFile << endl;
|
||||||
writeG2o(NonlinearFactorGraph(), poses, outputFile);
|
writeG2o(*inputGraph, poses, outputFile);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -559,7 +559,7 @@ void runPerturb()
|
||||||
|
|
||||||
// Perturb values
|
// Perturb values
|
||||||
VectorValues noise;
|
VectorValues noise;
|
||||||
for(const Values::KeyValuePair& key_val: initial)
|
for(const Values::KeyValuePair key_val: initial)
|
||||||
{
|
{
|
||||||
Vector noisev(key_val.value.dim());
|
Vector noisev(key_val.value.dim());
|
||||||
for(Vector::Index i = 0; i < noisev.size(); ++i)
|
for(Vector::Index i = 0; i < noisev.size(); ++i)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(METIS)
|
project(METIS)
|
||||||
|
|
||||||
# Add flags for currect directory and below
|
# Add flags for currect directory and below
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ endif()
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set_target_properties(metis-gtsam PROPERTIES
|
set_target_properties(metis-gtsam PROPERTIES
|
||||||
PREFIX ""
|
PREFIX ""
|
||||||
|
COMPILE_FLAGS /w
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/../../../bin")
|
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/../../../bin")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,12 +134,12 @@ endif()
|
||||||
# of any previously installed GTSAM headers.
|
# of any previously installed GTSAM headers.
|
||||||
target_include_directories(gtsam BEFORE PUBLIC
|
target_include_directories(gtsam BEFORE PUBLIC
|
||||||
# main gtsam includes:
|
# main gtsam includes:
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
|
$<BUILD_INTERFACE:${GTSAM_SOURCE_DIR}>
|
||||||
$<INSTALL_INTERFACE:include/>
|
$<INSTALL_INTERFACE:include/>
|
||||||
# config.h
|
# config.h
|
||||||
$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>
|
$<BUILD_INTERFACE:${GTSAM_BINARY_DIR}>
|
||||||
# unit tests:
|
# unit tests:
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/CppUnitLite>
|
$<BUILD_INTERFACE:${GTSAM_SOURCE_DIR}/CppUnitLite>
|
||||||
)
|
)
|
||||||
# 3rdparty libraries: use the "system" flag so they are included via "-isystem"
|
# 3rdparty libraries: use the "system" flag so they are included via "-isystem"
|
||||||
# and warnings (and warnings-considered-errors) in those headers are not
|
# and warnings (and warnings-considered-errors) in those headers are not
|
||||||
|
|
@ -156,9 +156,9 @@ target_include_directories(gtsam SYSTEM BEFORE PUBLIC
|
||||||
)
|
)
|
||||||
if(GTSAM_SUPPORT_NESTED_DISSECTION)
|
if(GTSAM_SUPPORT_NESTED_DISSECTION)
|
||||||
target_include_directories(gtsam BEFORE PUBLIC
|
target_include_directories(gtsam BEFORE PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/gtsam/3rdparty/metis/include>
|
$<BUILD_INTERFACE:${GTSAM_SOURCE_DIR}/gtsam/3rdparty/metis/include>
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/gtsam/3rdparty/metis/libmetis>
|
$<BUILD_INTERFACE:${GTSAM_SOURCE_DIR}/gtsam/3rdparty/metis/libmetis>
|
||||||
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/gtsam/3rdparty/metis/GKlib>
|
$<BUILD_INTERFACE:${GTSAM_SOURCE_DIR}/gtsam/3rdparty/metis/GKlib>
|
||||||
$<INSTALL_INTERFACE:include/gtsam/3rdparty/metis/>
|
$<INSTALL_INTERFACE:include/gtsam/3rdparty/metis/>
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -199,7 +199,7 @@ if(WIN32)
|
||||||
else()
|
else()
|
||||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
|
if("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
|
||||||
# Suppress all warnings from 3rd party sources.
|
# Suppress all warnings from 3rd party sources.
|
||||||
set_source_files_properties(${3rdparty_srcs} PROPERTIES COMPILE_FLAGS "-w")
|
set_source_files_properties(${3rdparty_srcs} PROPERTIES COMPILE_FLAGS "-w -Wno-everything")
|
||||||
else()
|
else()
|
||||||
set_source_files_properties(${3rdparty_srcs} PROPERTIES COMPILE_FLAGS "-Wno-error")
|
set_source_files_properties(${3rdparty_srcs} PROPERTIES COMPILE_FLAGS "-Wno-error")
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ const Eigen::IOFormat& matlabFormat() {
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
//3 argument call
|
//3 argument call
|
||||||
void print(const Matrix& A, const string &s, ostream& stream) {
|
void print(const Matrix& A, const string &s, ostream& stream) {
|
||||||
cout << s << A.format(matlabFormat()) << endl;
|
stream << s << A.format(matlabFormat()) << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ bool equal_with_abs_tol(const Eigen::DenseBase<MATRIX>& A, const Eigen::DenseBas
|
||||||
|
|
||||||
for(size_t i=0; i<m1; i++)
|
for(size_t i=0; i<m1; i++)
|
||||||
for(size_t j=0; j<n1; j++) {
|
for(size_t j=0; j<n1; j++) {
|
||||||
if(!fpEqual(A(i,j), B(i,j), tol)) {
|
if(!fpEqual(A(i,j), B(i,j), tol, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/shared_ptr.hpp>
|
|
||||||
#include <boost/concept_check.hpp>
|
#include <boost/concept_check.hpp>
|
||||||
#include <stdio.h>
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#define GTSAM_PRINT(x)((x).print(#x))
|
#define GTSAM_PRINT(x)((x).print(#x))
|
||||||
|
|
@ -72,10 +72,10 @@ namespace gtsam {
|
||||||
}; // \ Testable
|
}; // \ Testable
|
||||||
|
|
||||||
inline void print(float v, const std::string& s = "") {
|
inline void print(float v, const std::string& s = "") {
|
||||||
printf("%s%f\n",s.c_str(),v);
|
std::cout << (s == "" ? s : s + " ") << v << std::endl;
|
||||||
}
|
}
|
||||||
inline void print(double v, const std::string& s = "") {
|
inline void print(double v, const std::string& s = "") {
|
||||||
printf("%s%lf\n",s.c_str(),v);
|
std::cout << (s == "" ? s : s + " ") << v << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Call equal on the object */
|
/** Call equal on the object */
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace gtsam {
|
||||||
* 1. https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
* 1. https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
||||||
* 2. https://floating-point-gui.de/errors/comparison/
|
* 2. https://floating-point-gui.de/errors/comparison/
|
||||||
* ************************************************************************* */
|
* ************************************************************************* */
|
||||||
bool fpEqual(double a, double b, double tol) {
|
bool fpEqual(double a, double b, double tol, bool check_relative_also) {
|
||||||
using std::abs;
|
using std::abs;
|
||||||
using std::isnan;
|
using std::isnan;
|
||||||
using std::isinf;
|
using std::isinf;
|
||||||
|
|
@ -48,7 +48,7 @@ bool fpEqual(double a, double b, double tol) {
|
||||||
double larger = (abs(b) > abs(a)) ? abs(b) : abs(a);
|
double larger = (abs(b) > abs(a)) ? abs(b) : abs(a);
|
||||||
|
|
||||||
// handle NaNs
|
// handle NaNs
|
||||||
if(std::isnan(a) || isnan(b)) {
|
if(isnan(a) || isnan(b)) {
|
||||||
return isnan(a) && isnan(b);
|
return isnan(a) && isnan(b);
|
||||||
}
|
}
|
||||||
// handle inf
|
// handle inf
|
||||||
|
|
@ -60,13 +60,15 @@ bool fpEqual(double a, double b, double tol) {
|
||||||
else if(a == 0 || b == 0 || (abs(a) + abs(b)) < DOUBLE_MIN_NORMAL) {
|
else if(a == 0 || b == 0 || (abs(a) + abs(b)) < DOUBLE_MIN_NORMAL) {
|
||||||
return abs(a-b) <= tol * DOUBLE_MIN_NORMAL;
|
return abs(a-b) <= tol * DOUBLE_MIN_NORMAL;
|
||||||
}
|
}
|
||||||
// Check if the numbers are really close
|
// Check if the numbers are really close.
|
||||||
// Needed when comparing numbers near zero or tol is in vicinity
|
// Needed when comparing numbers near zero or tol is in vicinity.
|
||||||
else if (abs(a - b) <= tol) {
|
else if (abs(a - b) <= tol) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Use relative error
|
// Check for relative error
|
||||||
else if(abs(a-b) <= tol * min(larger, std::numeric_limits<double>::max())) {
|
else if (abs(a - b) <=
|
||||||
|
tol * min(larger, std::numeric_limits<double>::max()) &&
|
||||||
|
check_relative_also) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,9 +85,15 @@ static_assert(
|
||||||
* respectively for the comparison to be true.
|
* respectively for the comparison to be true.
|
||||||
* If one is NaN/Inf and the other is not, returns false.
|
* If one is NaN/Inf and the other is not, returns false.
|
||||||
*
|
*
|
||||||
|
* @param check_relative_also is a flag which toggles additional checking for
|
||||||
|
* relative error. This means that if either the absolute error or the relative
|
||||||
|
* error is within the tolerance, the result will be true.
|
||||||
|
* By default, the flag is true.
|
||||||
|
*
|
||||||
* Return true if two numbers are close wrt tol.
|
* Return true if two numbers are close wrt tol.
|
||||||
*/
|
*/
|
||||||
GTSAM_EXPORT bool fpEqual(double a, double b, double tol);
|
GTSAM_EXPORT bool fpEqual(double a, double b, double tol,
|
||||||
|
bool check_relative_also = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* print without optional string, must specify cout yourself
|
* print without optional string, must specify cout yourself
|
||||||
|
|
|
||||||
|
|
@ -1163,6 +1163,19 @@ TEST(Matrix , IsVectorSpace) {
|
||||||
BOOST_CONCEPT_ASSERT((IsVectorSpace<Vector5>));
|
BOOST_CONCEPT_ASSERT((IsVectorSpace<Vector5>));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Matrix, AbsoluteError) {
|
||||||
|
double a = 2000, b = 1997, tol = 1e-1;
|
||||||
|
bool isEqual;
|
||||||
|
|
||||||
|
// Test only absolute error
|
||||||
|
isEqual = fpEqual(a, b, tol, false);
|
||||||
|
EXPECT(!isEqual);
|
||||||
|
|
||||||
|
// Test relative error as well
|
||||||
|
isEqual = fpEqual(a, b, tol);
|
||||||
|
EXPECT(isEqual);
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
int main() {
|
int main() {
|
||||||
TestResult tr;
|
TestResult tr;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file Cal3.cpp
|
||||||
|
* @brief Common code for all calibration models.
|
||||||
|
* @author Frank Dellaert
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtsam/geometry/Cal3.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace gtsam {
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
Cal3::Cal3(double fov, int w, int h)
|
||||||
|
: s_(0), u0_((double)w / 2.0), v0_((double)h / 2.0) {
|
||||||
|
double a = fov * M_PI / 360.0; // fov/2 in radians
|
||||||
|
fx_ = double(w) / (2.0 * tan(a));
|
||||||
|
fy_ = fx_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
Cal3::Cal3(const std::string& path) {
|
||||||
|
const auto buffer = path + std::string("/calibration_info.txt");
|
||||||
|
std::ifstream infile(buffer, std::ios::in);
|
||||||
|
|
||||||
|
if (infile && !infile.eof()) {
|
||||||
|
infile >> fx_ >> fy_ >> s_ >> u0_ >> v0_;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Cal3: Unable to load the calibration");
|
||||||
|
}
|
||||||
|
|
||||||
|
infile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Cal3& cal) {
|
||||||
|
os << "fx: " << cal.fx() << ", fy: " << cal.fy() << ", s: " << cal.skew()
|
||||||
|
<< ", px: " << cal.px() << ", py: " << cal.py();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
void Cal3::print(const std::string& s) const { gtsam::print((Matrix)K(), s); }
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
bool Cal3::equals(const Cal3& K, double tol) const {
|
||||||
|
return (std::fabs(fx_ - K.fx_) < tol && std::fabs(fy_ - K.fy_) < tol &&
|
||||||
|
std::fabs(s_ - K.s_) < tol && std::fabs(u0_ - K.u0_) < tol &&
|
||||||
|
std::fabs(v0_ - K.v0_) < tol);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matrix3 Cal3::inverse() const {
|
||||||
|
const double fxy = fx_ * fy_, sv0 = s_ * v0_, fyu0 = fy_ * u0_;
|
||||||
|
Matrix3 K_inverse;
|
||||||
|
K_inverse << 1.0 / fx_, -s_ / fxy, (sv0 - fyu0) / fxy, 0.0, 1.0 / fy_,
|
||||||
|
-v0_ / fy_, 0.0, 0.0, 1.0;
|
||||||
|
return K_inverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
} // \ namespace gtsam
|
||||||
|
|
@ -0,0 +1,206 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file Cal3.h
|
||||||
|
* @brief Common code for all Calibration models.
|
||||||
|
* @author Varun Agrawal
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup geometry
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/geometry/Point2.h>
|
||||||
|
|
||||||
|
namespace gtsam {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function which makes use of the Implicit Function Theorem to compute the
|
||||||
|
* Jacobians of `calibrate` using `uncalibrate`.
|
||||||
|
* This is useful when there are iterative operations in the `calibrate`
|
||||||
|
* function which make computing jacobians difficult.
|
||||||
|
*
|
||||||
|
* Given f(pi, pn) = uncalibrate(pn) - pi, and g(pi) = calibrate, we can
|
||||||
|
* easily compute the Jacobians:
|
||||||
|
* df/pi = -I (pn and pi are independent args)
|
||||||
|
* Dp = -inv(H_uncal_pn) * df/pi = -inv(H_uncal_pn) * (-I) = inv(H_uncal_pn)
|
||||||
|
* Dcal = -inv(H_uncal_pn) * df/K = -inv(H_uncal_pn) * H_uncal_K
|
||||||
|
*
|
||||||
|
* @tparam Cal Calibration model.
|
||||||
|
* @tparam Dim The number of parameters in the calibration model.
|
||||||
|
* @param p Calibrated point.
|
||||||
|
* @param Dcal optional 2*p Jacobian wrpt `p` Cal3DS2 parameters.
|
||||||
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates.
|
||||||
|
*/
|
||||||
|
template <typename Cal, size_t Dim>
|
||||||
|
void calibrateJacobians(const Cal& calibration, const Point2& pn,
|
||||||
|
OptionalJacobian<2, Dim> Dcal = boost::none,
|
||||||
|
OptionalJacobian<2, 2> Dp = boost::none) {
|
||||||
|
if (Dcal || Dp) {
|
||||||
|
Eigen::Matrix<double, 2, Dim> H_uncal_K;
|
||||||
|
Matrix22 H_uncal_pn, H_uncal_pn_inv;
|
||||||
|
|
||||||
|
// Compute uncalibrate Jacobians
|
||||||
|
calibration.uncalibrate(pn, Dcal ? &H_uncal_K : nullptr, H_uncal_pn);
|
||||||
|
|
||||||
|
H_uncal_pn_inv = H_uncal_pn.inverse();
|
||||||
|
|
||||||
|
if (Dp) *Dp = H_uncal_pn_inv;
|
||||||
|
if (Dcal) *Dcal = -H_uncal_pn_inv * H_uncal_K;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Common base class for all calibration models.
|
||||||
|
* @addtogroup geometry
|
||||||
|
* \nosubgrouping
|
||||||
|
*/
|
||||||
|
class GTSAM_EXPORT Cal3 {
|
||||||
|
protected:
|
||||||
|
double fx_ = 1.0f, fy_ = 1.0f; ///< focal length
|
||||||
|
double s_ = 0.0f; ///< skew
|
||||||
|
double u0_ = 0.0f, v0_ = 0.0f; ///< principal point
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum { dimension = 5 };
|
||||||
|
///< shared pointer to calibration object
|
||||||
|
using shared_ptr = boost::shared_ptr<Cal3>;
|
||||||
|
|
||||||
|
/// @name Standard Constructors
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Create a default calibration that leaves coordinates unchanged
|
||||||
|
Cal3() = default;
|
||||||
|
|
||||||
|
/// constructor from doubles
|
||||||
|
Cal3(double fx, double fy, double s, double u0, double v0)
|
||||||
|
: fx_(fx), fy_(fy), s_(s), u0_(u0), v0_(v0) {}
|
||||||
|
|
||||||
|
/// constructor from vector
|
||||||
|
Cal3(const Vector5& d)
|
||||||
|
: fx_(d(0)), fy_(d(1)), s_(d(2)), u0_(d(3)), v0_(d(4)) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Easy constructor, takes fov in degrees, asssumes zero skew, unit aspect
|
||||||
|
* @param fov field of view in degrees
|
||||||
|
* @param w image width
|
||||||
|
* @param h image height
|
||||||
|
*/
|
||||||
|
Cal3(double fov, int w, int h);
|
||||||
|
|
||||||
|
/// Virtual destructor
|
||||||
|
virtual ~Cal3() {}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
/// @name Advanced Constructors
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load calibration parameters from `calibration_info.txt` file located in
|
||||||
|
* `path` directory.
|
||||||
|
*
|
||||||
|
* The contents of calibration file should be the 5 parameters in order:
|
||||||
|
* `fx, fy, s, u0, v0`
|
||||||
|
*
|
||||||
|
* @param path path to directory containing `calibration_info.txt`.
|
||||||
|
*/
|
||||||
|
Cal3(const std::string& path);
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
/// @name Testable
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Output stream operator
|
||||||
|
GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
const Cal3& cal);
|
||||||
|
|
||||||
|
/// print with optional string
|
||||||
|
virtual void print(const std::string& s = "") const;
|
||||||
|
|
||||||
|
/// Check if equal up to specified tolerance
|
||||||
|
bool equals(const Cal3& K, double tol = 10e-9) const;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
/// @name Standard Interface
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// focal length x
|
||||||
|
inline double fx() const { return fx_; }
|
||||||
|
|
||||||
|
/// focal length y
|
||||||
|
inline double fy() const { return fy_; }
|
||||||
|
|
||||||
|
/// aspect ratio
|
||||||
|
inline double aspectRatio() const { return fx_ / fy_; }
|
||||||
|
|
||||||
|
/// skew
|
||||||
|
inline double skew() const { return s_; }
|
||||||
|
|
||||||
|
/// image center in x
|
||||||
|
inline double px() const { return u0_; }
|
||||||
|
|
||||||
|
/// image center in y
|
||||||
|
inline double py() const { return v0_; }
|
||||||
|
|
||||||
|
/// return the principal point
|
||||||
|
Point2 principalPoint() const { return Point2(u0_, v0_); }
|
||||||
|
|
||||||
|
/// vectorized form (column-wise)
|
||||||
|
Vector5 vector() const {
|
||||||
|
Vector5 v;
|
||||||
|
v << fx_, fy_, s_, u0_, v0_;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return calibration matrix K
|
||||||
|
virtual Matrix3 K() const {
|
||||||
|
Matrix3 K;
|
||||||
|
K << fx_, s_, u0_, 0.0, fy_, v0_, 0.0, 0.0, 1.0;
|
||||||
|
return K;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V41
|
||||||
|
/** @deprecated The following function has been deprecated, use K above */
|
||||||
|
Matrix3 matrix() const { return K(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Return inverted calibration matrix inv(K)
|
||||||
|
Matrix3 inverse() const;
|
||||||
|
|
||||||
|
/// return DOF, dimensionality of tangent space
|
||||||
|
inline virtual size_t dim() const { return Dim(); }
|
||||||
|
|
||||||
|
/// return DOF, dimensionality of tangent space
|
||||||
|
inline static size_t Dim() { return dimension; }
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
/// @name Advanced Interface
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Serialization function
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
||||||
|
ar& BOOST_SERIALIZATION_NVP(fx_);
|
||||||
|
ar& BOOST_SERIALIZATION_NVP(fy_);
|
||||||
|
ar& BOOST_SERIALIZATION_NVP(s_);
|
||||||
|
ar& BOOST_SERIALIZATION_NVP(u0_);
|
||||||
|
ar& BOOST_SERIALIZATION_NVP(v0_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // \ namespace gtsam
|
||||||
|
|
@ -15,28 +15,19 @@
|
||||||
* @author ydjian
|
* @author ydjian
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtsam/base/Vector.h>
|
|
||||||
#include <gtsam/base/Matrix.h>
|
#include <gtsam/base/Matrix.h>
|
||||||
|
#include <gtsam/base/Vector.h>
|
||||||
|
#include <gtsam/geometry/Cal3Bundler.h>
|
||||||
#include <gtsam/geometry/Point2.h>
|
#include <gtsam/geometry/Point2.h>
|
||||||
#include <gtsam/geometry/Point3.h>
|
#include <gtsam/geometry/Point3.h>
|
||||||
#include <gtsam/geometry/Cal3Bundler.h>
|
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
Cal3Bundler::Cal3Bundler() :
|
|
||||||
f_(1), k1_(0), k2_(0), u0_(0), v0_(0), tol_(1e-5) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
Cal3Bundler::Cal3Bundler(double f, double k1, double k2, double u0, double v0,
|
|
||||||
double tol)
|
|
||||||
: f_(f), k1_(k1), k2_(k2), u0_(u0), v0_(v0), tol_(tol) {}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Matrix3 Cal3Bundler::K() const {
|
Matrix3 Cal3Bundler::K() const {
|
||||||
|
// This function is needed to ensure skew = 0;
|
||||||
Matrix3 K;
|
Matrix3 K;
|
||||||
K << f_, 0, u0_, 0, f_, v0_, 0, 0, 1;
|
K << fx_, 0, u0_, 0, fy_, v0_, 0, 0, 1.0;
|
||||||
return K;
|
return K;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,35 +39,42 @@ Vector4 Cal3Bundler::k() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Vector3 Cal3Bundler::vector() const {
|
Vector3 Cal3Bundler::vector() const { return Vector3(fx_, k1_, k2_); }
|
||||||
return Vector3(f_, k1_, k2_);
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Cal3Bundler& cal) {
|
||||||
|
os << "f: " << cal.fx() << ", k1: " << cal.k1() << ", k2: " << cal.k2()
|
||||||
|
<< ", px: " << cal.px() << ", py: " << cal.py();
|
||||||
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
void Cal3Bundler::print(const std::string& s) const {
|
void Cal3Bundler::print(const std::string& s) const {
|
||||||
gtsam::print((Vector)(Vector(5) << f_, k1_, k2_, u0_, v0_).finished(), s + ".K");
|
gtsam::print((Vector)(Vector(5) << fx_, k1_, k2_, u0_, v0_).finished(),
|
||||||
|
s + ".K");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
bool Cal3Bundler::equals(const Cal3Bundler& K, double tol) const {
|
bool Cal3Bundler::equals(const Cal3Bundler& K, double tol) const {
|
||||||
if (std::abs(f_ - K.f_) > tol || std::abs(k1_ - K.k1_) > tol
|
const Cal3* base = dynamic_cast<const Cal3*>(&K);
|
||||||
|| std::abs(k2_ - K.k2_) > tol || std::abs(u0_ - K.u0_) > tol
|
return (Cal3::equals(*base, tol) && std::fabs(k1_ - K.k1_) < tol &&
|
||||||
|| std::abs(v0_ - K.v0_) > tol)
|
std::fabs(k2_ - K.k2_) < tol && std::fabs(u0_ - K.u0_) < tol &&
|
||||||
return false;
|
std::fabs(v0_ - K.v0_) < tol);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 Cal3Bundler::uncalibrate(const Point2& p, //
|
Point2 Cal3Bundler::uncalibrate(const Point2& p, OptionalJacobian<2, 3> Dcal,
|
||||||
OptionalJacobian<2, 3> Dcal, OptionalJacobian<2, 2> Dp) const {
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
// r = x^2 + y^2;
|
// r = x² + y²;
|
||||||
// g = (1 + k(1)*r + k(2)*r^2);
|
// g = (1 + k(1)*r + k(2)*r²);
|
||||||
// pi(:,i) = g * pn(:,i)
|
// pi(:,i) = g * pn(:,i)
|
||||||
const double x = p.x(), y = p.y();
|
const double x = p.x(), y = p.y();
|
||||||
const double r = x * x + y * y;
|
const double r = x * x + y * y;
|
||||||
const double g = 1. + (k1_ + k2_ * r) * r;
|
const double g = 1. + (k1_ + k2_ * r) * r;
|
||||||
const double u = g * x, v = g * y;
|
const double u = g * x, v = g * y;
|
||||||
|
|
||||||
|
const double f_ = fx_;
|
||||||
|
|
||||||
// Derivatives make use of intermediate variables above
|
// Derivatives make use of intermediate variables above
|
||||||
if (Dcal) {
|
if (Dcal) {
|
||||||
double rx = r * x, ry = r * y;
|
double rx = r * x, ry = r * y;
|
||||||
|
|
@ -94,23 +92,22 @@ Point2 Cal3Bundler::uncalibrate(const Point2& p, //
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 Cal3Bundler::calibrate(const Point2& pi,
|
Point2 Cal3Bundler::calibrate(const Point2& pi, OptionalJacobian<2, 3> Dcal,
|
||||||
OptionalJacobian<2, 3> Dcal,
|
|
||||||
OptionalJacobian<2, 2> Dp) const {
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
// Copied from Cal3DS2 :-(
|
// Copied from Cal3DS2
|
||||||
// but specialized with k1, k2 non-zero only and fx=fy and s=0
|
// but specialized with k1, k2 non-zero only and fx=fy and s=0
|
||||||
double x = (pi.x() - u0_)/f_, y = (pi.y() - v0_)/f_;
|
double x = (pi.x() - u0_) / fx_, y = (pi.y() - v0_) / fx_;
|
||||||
const Point2 invKPi(x, y);
|
const Point2 invKPi(x, y);
|
||||||
|
|
||||||
// initialize by ignoring the distortion at all, might be problematic for pixels around boundary
|
// initialize by ignoring the distortion at all, might be problematic for
|
||||||
|
// pixels around boundary
|
||||||
Point2 pn(x, y);
|
Point2 pn(x, y);
|
||||||
|
|
||||||
// iterate until the uncalibrate is close to the actual pixel coordinate
|
// iterate until the uncalibrate is close to the actual pixel coordinate
|
||||||
const int maxIterations = 10;
|
const int maxIterations = 10;
|
||||||
int iteration;
|
int iteration;
|
||||||
for (iteration = 0; iteration < maxIterations; ++iteration) {
|
for (iteration = 0; iteration < maxIterations; ++iteration) {
|
||||||
if (distance2(uncalibrate(pn), pi) <= tol_)
|
if (distance2(uncalibrate(pn), pi) <= tol_) break;
|
||||||
break;
|
|
||||||
const double px = pn.x(), py = pn.y(), xx = px * px, yy = py * py;
|
const double px = pn.x(), py = pn.y(), xx = px * px, yy = py * py;
|
||||||
const double rr = xx + yy;
|
const double rr = xx + yy;
|
||||||
const double g = (1 + k1_ * rr + k2_ * rr * rr);
|
const double g = (1 + k1_ * rr + k2_ * rr * rr);
|
||||||
|
|
@ -119,26 +116,10 @@ Point2 Cal3Bundler::calibrate(const Point2& pi,
|
||||||
|
|
||||||
if (iteration >= maxIterations)
|
if (iteration >= maxIterations)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Cal3Bundler::calibrate fails to converge. need a better initialization");
|
"Cal3Bundler::calibrate fails to converge. need a better "
|
||||||
|
"initialization");
|
||||||
|
|
||||||
// We make use of the Implicit Function Theorem to compute the Jacobians from uncalibrate
|
calibrateJacobians<Cal3Bundler, dimension>(*this, pn, Dcal, Dp);
|
||||||
// Given f(pi, pn) = uncalibrate(pn) - pi, and g(pi) = calibrate, we can easily compute the Jacobians
|
|
||||||
// df/pi = -I (pn and pi are independent args)
|
|
||||||
// Dcal = -inv(H_uncal_pn) * df/pi = -inv(H_uncal_pn) * (-I) = inv(H_uncal_pn)
|
|
||||||
// Dp = -inv(H_uncal_pn) * df/K = -inv(H_uncal_pn) * H_uncal_K
|
|
||||||
Matrix23 H_uncal_K;
|
|
||||||
Matrix22 H_uncal_pn, H_uncal_pn_inv;
|
|
||||||
|
|
||||||
if (Dcal || Dp) {
|
|
||||||
// Compute uncalibrate Jacobians
|
|
||||||
uncalibrate(pn, Dcal ? &H_uncal_K : nullptr, H_uncal_pn);
|
|
||||||
|
|
||||||
H_uncal_pn_inv = H_uncal_pn.inverse();
|
|
||||||
|
|
||||||
if (Dp) *Dp = H_uncal_pn_inv;
|
|
||||||
if (Dcal) *Dcal = -H_uncal_pn_inv * H_uncal_K;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
|
|
@ -167,14 +148,4 @@ Matrix25 Cal3Bundler::D2d_intrinsic_calibration(const Point2& p) const {
|
||||||
return H;
|
return H;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
} // \ namespace gtsam
|
||||||
Cal3Bundler Cal3Bundler::retract(const Vector& d) const {
|
|
||||||
return Cal3Bundler(f_ + d(0), k1_ + d(1), k2_ + d(2), u0_, v0_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
Vector3 Cal3Bundler::localCoordinates(const Cal3Bundler& T2) const {
|
|
||||||
return T2.vector() - vector();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,12 @@
|
||||||
* @brief Calibration used by Bundler
|
* @brief Calibration used by Bundler
|
||||||
* @date Sep 25, 2010
|
* @date Sep 25, 2010
|
||||||
* @author Yong Dian Jian
|
* @author Yong Dian Jian
|
||||||
|
* @author Varun Agrawal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/geometry/Cal3.h>
|
||||||
#include <gtsam/geometry/Point2.h>
|
#include <gtsam/geometry/Point2.h>
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
@ -27,23 +29,23 @@ namespace gtsam {
|
||||||
* @addtogroup geometry
|
* @addtogroup geometry
|
||||||
* \nosubgrouping
|
* \nosubgrouping
|
||||||
*/
|
*/
|
||||||
class GTSAM_EXPORT Cal3Bundler {
|
class GTSAM_EXPORT Cal3Bundler : public Cal3 {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double f_; ///< focal length
|
double k1_ = 0.0f, k2_ = 0.0f; ///< radial distortion
|
||||||
double k1_, k2_; ///< radial distortion
|
double tol_ = 1e-5; ///< tolerance value when calibrating
|
||||||
double u0_, v0_; ///< image center, not a parameter to be optimized but a constant
|
|
||||||
double tol_; ///< tolerance value when calibrating
|
// NOTE: We use the base class fx to represent the common focal length.
|
||||||
|
// Also, image center parameters (u0, v0) are not optimized
|
||||||
|
// but are treated as constants.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum { dimension = 3 };
|
enum { dimension = 3 };
|
||||||
|
|
||||||
/// @name Standard Constructors
|
/// @name Standard Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// Default constructor
|
/// Default constructor
|
||||||
Cal3Bundler();
|
Cal3Bundler() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
|
@ -55,7 +57,8 @@ public:
|
||||||
* @param tol optional calibration tolerance value
|
* @param tol optional calibration tolerance value
|
||||||
*/
|
*/
|
||||||
Cal3Bundler(double f, double k1, double k2, double u0 = 0, double v0 = 0,
|
Cal3Bundler(double f, double k1, double k2, double u0 = 0, double v0 = 0,
|
||||||
double tol = 1e-5);
|
double tol = 1e-5)
|
||||||
|
: Cal3(f, f, 0, u0, v0), k1_(k1), k2_(k2), tol_(tol) {}
|
||||||
|
|
||||||
virtual ~Cal3Bundler() {}
|
virtual ~Cal3Bundler() {}
|
||||||
|
|
||||||
|
|
@ -63,8 +66,12 @@ public:
|
||||||
/// @name Testable
|
/// @name Testable
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
/// Output stream operator
|
||||||
|
GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
const Cal3Bundler& cal);
|
||||||
|
|
||||||
/// print with optional string
|
/// print with optional string
|
||||||
void print(const std::string& s = "") const;
|
void print(const std::string& s = "") const override;
|
||||||
|
|
||||||
/// assert equality up to a tolerance
|
/// assert equality up to a tolerance
|
||||||
bool equals(const Cal3Bundler& K, double tol = 10e-9) const;
|
bool equals(const Cal3Bundler& K, double tol = 10e-9) const;
|
||||||
|
|
@ -73,59 +80,36 @@ public:
|
||||||
/// @name Standard Interface
|
/// @name Standard Interface
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
Matrix3 K() const; ///< Standard 3*3 calibration matrix
|
/// distorsion parameter k1
|
||||||
|
inline double k1() const { return k1_; }
|
||||||
|
|
||||||
|
/// distorsion parameter k2
|
||||||
|
inline double k2() const { return k2_; }
|
||||||
|
|
||||||
|
/// image center in x
|
||||||
|
inline double px() const { return u0_; }
|
||||||
|
|
||||||
|
/// image center in y
|
||||||
|
inline double py() const { return v0_; }
|
||||||
|
|
||||||
|
Matrix3 K() const override; ///< Standard 3*3 calibration matrix
|
||||||
Vector4 k() const; ///< Radial distortion parameters (4 of them, 2 0)
|
Vector4 k() const; ///< Radial distortion parameters (4 of them, 2 0)
|
||||||
|
|
||||||
Vector3 vector() const;
|
Vector3 vector() const;
|
||||||
|
|
||||||
/// focal length x
|
|
||||||
inline double fx() const {
|
|
||||||
return f_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// focal length y
|
|
||||||
inline double fy() const {
|
|
||||||
return f_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// distorsion parameter k1
|
|
||||||
inline double k1() const {
|
|
||||||
return k1_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// distorsion parameter k2
|
|
||||||
inline double k2() const {
|
|
||||||
return k2_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// image center in x
|
|
||||||
inline double px() const {
|
|
||||||
return u0_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// image center in y
|
|
||||||
inline double py() const {
|
|
||||||
return v0_;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V41
|
#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V41
|
||||||
/// get parameter u0
|
/// get parameter u0
|
||||||
inline double u0() const {
|
inline double u0() const { return u0_; }
|
||||||
return u0_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get parameter v0
|
/// get parameter v0
|
||||||
inline double v0() const {
|
inline double v0() const { return v0_; }
|
||||||
return v0_;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief: convert intrinsic coordinates xy to image coordinates uv
|
* @brief: convert intrinsic coordinates xy to image coordinates uv
|
||||||
* Version of uncalibrate with derivatives
|
* Version of uncalibrate with derivatives
|
||||||
* @param p point in intrinsic coordinates
|
* @param p point in intrinsic coordinates
|
||||||
* @param Dcal optional 2*3 Jacobian wrpt CalBundler parameters
|
* @param Dcal optional 2*3 Jacobian wrpt Cal3Bundler parameters
|
||||||
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
||||||
* @return point in image coordinates
|
* @return point in image coordinates
|
||||||
*/
|
*/
|
||||||
|
|
@ -140,8 +124,7 @@ public:
|
||||||
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
||||||
* @return point in intrinsic coordinates
|
* @return point in intrinsic coordinates
|
||||||
*/
|
*/
|
||||||
Point2 calibrate(const Point2& pi,
|
Point2 calibrate(const Point2& pi, OptionalJacobian<2, 3> Dcal = boost::none,
|
||||||
OptionalJacobian<2, 3> Dcal = boost::none,
|
|
||||||
OptionalJacobian<2, 2> Dp = boost::none) const;
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
/// @deprecated might be removed in next release, use uncalibrate
|
/// @deprecated might be removed in next release, use uncalibrate
|
||||||
|
|
@ -157,24 +140,23 @@ public:
|
||||||
/// @name Manifold
|
/// @name Manifold
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
/// return DOF, dimensionality of tangent space
|
||||||
|
virtual size_t dim() const override { return Dim(); }
|
||||||
|
|
||||||
|
/// return DOF, dimensionality of tangent space
|
||||||
|
inline static size_t Dim() { return dimension; }
|
||||||
|
|
||||||
/// Update calibration with tangent space delta
|
/// Update calibration with tangent space delta
|
||||||
Cal3Bundler retract(const Vector& d) const;
|
inline Cal3Bundler retract(const Vector& d) const {
|
||||||
|
return Cal3Bundler(fx_ + d(0), k1_ + d(1), k2_ + d(2), u0_, v0_);
|
||||||
/// Calculate local coordinates to another calibration
|
|
||||||
Vector3 localCoordinates(const Cal3Bundler& T2) const;
|
|
||||||
|
|
||||||
/// dimensionality
|
|
||||||
virtual size_t dim() const {
|
|
||||||
return 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// dimensionality
|
/// Calculate local coordinates to another calibration
|
||||||
static size_t Dim() {
|
Vector3 localCoordinates(const Cal3Bundler& T2) const {
|
||||||
return 3;
|
return T2.vector() - vector();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Advanced Interface
|
/// @name Advanced Interface
|
||||||
/// @{
|
/// @{
|
||||||
|
|
@ -183,16 +165,14 @@ private:
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, const unsigned int /*version*/) {
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
||||||
ar & BOOST_SERIALIZATION_NVP(f_);
|
ar& boost::serialization::make_nvp(
|
||||||
|
"Cal3Bundler", boost::serialization::base_object<Cal3>(*this));
|
||||||
ar& BOOST_SERIALIZATION_NVP(k1_);
|
ar& BOOST_SERIALIZATION_NVP(k1_);
|
||||||
ar& BOOST_SERIALIZATION_NVP(k2_);
|
ar& BOOST_SERIALIZATION_NVP(k2_);
|
||||||
ar & BOOST_SERIALIZATION_NVP(u0_);
|
|
||||||
ar & BOOST_SERIALIZATION_NVP(v0_);
|
|
||||||
ar& BOOST_SERIALIZATION_NVP(tol_);
|
ar& BOOST_SERIALIZATION_NVP(tol_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
|
||||||
|
|
@ -13,28 +13,30 @@
|
||||||
* @file Cal3DS2.cpp
|
* @file Cal3DS2.cpp
|
||||||
* @date Feb 28, 2010
|
* @date Feb 28, 2010
|
||||||
* @author ydjian
|
* @author ydjian
|
||||||
|
* @author Varun Agrawal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtsam/base/Vector.h>
|
|
||||||
#include <gtsam/base/Matrix.h>
|
#include <gtsam/base/Matrix.h>
|
||||||
|
#include <gtsam/base/Vector.h>
|
||||||
|
#include <gtsam/geometry/Cal3DS2.h>
|
||||||
#include <gtsam/geometry/Point2.h>
|
#include <gtsam/geometry/Point2.h>
|
||||||
#include <gtsam/geometry/Point3.h>
|
#include <gtsam/geometry/Point3.h>
|
||||||
#include <gtsam/geometry/Cal3DS2.h>
|
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
void Cal3DS2::print(const std::string& s_) const {
|
std::ostream& operator<<(std::ostream& os, const Cal3DS2& cal) {
|
||||||
Base::print(s_);
|
os << (Cal3DS2_Base&)cal;
|
||||||
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
void Cal3DS2::print(const std::string& s_) const { Base::print(s_); }
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
bool Cal3DS2::equals(const Cal3DS2& K, double tol) const {
|
bool Cal3DS2::equals(const Cal3DS2& K, double tol) const {
|
||||||
if (std::abs(fx_ - K.fx_) > tol || std::abs(fy_ - K.fy_) > tol || std::abs(s_ - K.s_) > tol ||
|
const Cal3DS2_Base* base = dynamic_cast<const Cal3DS2_Base*>(&K);
|
||||||
std::abs(u0_ - K.u0_) > tol || std::abs(v0_ - K.v0_) > tol || std::abs(k1_ - K.k1_) > tol ||
|
return Cal3DS2_Base::equals(*base, tol);
|
||||||
std::abs(k2_ - K.k2_) > tol || std::abs(p1_ - K.p1_) > tol || std::abs(p2_ - K.p2_) > tol)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
@ -46,8 +48,5 @@ Cal3DS2 Cal3DS2::retract(const Vector& d) const {
|
||||||
Vector Cal3DS2::localCoordinates(const Cal3DS2& T2) const {
|
Vector Cal3DS2::localCoordinates(const Cal3DS2& T2) const {
|
||||||
return T2.vector() - vector();
|
return T2.vector() - vector();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,11 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file Cal3DS2.h
|
* @file Cal3DS2.h
|
||||||
* @brief Calibration of a camera with radial distortion, calculations in base class Cal3DS2_Base
|
* @brief Calibration of a camera with radial distortion, calculations in base
|
||||||
|
* class Cal3DS2_Base
|
||||||
* @date Feb 28, 2010
|
* @date Feb 28, 2010
|
||||||
* @author ydjian
|
* @author ydjian
|
||||||
|
* @autho Varun Agrawal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -30,22 +32,20 @@ namespace gtsam {
|
||||||
* \nosubgrouping
|
* \nosubgrouping
|
||||||
*/
|
*/
|
||||||
class GTSAM_EXPORT Cal3DS2 : public Cal3DS2_Base {
|
class GTSAM_EXPORT Cal3DS2 : public Cal3DS2_Base {
|
||||||
|
using Base = Cal3DS2_Base;
|
||||||
typedef Cal3DS2_Base Base;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum { dimension = 9 };
|
enum { dimension = 9 };
|
||||||
|
|
||||||
/// @name Standard Constructors
|
/// @name Standard Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// Default Constructor with only unit focal length
|
/// Default Constructor with only unit focal length
|
||||||
Cal3DS2() : Base() {}
|
Cal3DS2() = default;
|
||||||
|
|
||||||
Cal3DS2(double fx, double fy, double s, double u0, double v0,
|
Cal3DS2(double fx, double fy, double s, double u0, double v0, double k1,
|
||||||
double k1, double k2, double p1 = 0.0, double p2 = 0.0) :
|
double k2, double p1 = 0.0, double p2 = 0.0, double tol = 1e-5)
|
||||||
Base(fx, fy, s, u0, v0, k1, k2, p1, p2) {}
|
: Base(fx, fy, s, u0, v0, k1, k2, p1, p2, tol) {}
|
||||||
|
|
||||||
virtual ~Cal3DS2() {}
|
virtual ~Cal3DS2() {}
|
||||||
|
|
||||||
|
|
@ -53,12 +53,16 @@ public:
|
||||||
/// @name Advanced Constructors
|
/// @name Advanced Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
Cal3DS2(const Vector &v) : Base(v) {}
|
Cal3DS2(const Vector9& v) : Base(v) {}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Testable
|
/// @name Testable
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
/// Output stream operator
|
||||||
|
GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
const Cal3DS2& cal);
|
||||||
|
|
||||||
/// print with optional string
|
/// print with optional string
|
||||||
void print(const std::string& s = "") const override;
|
void print(const std::string& s = "") const override;
|
||||||
|
|
||||||
|
|
@ -76,10 +80,10 @@ public:
|
||||||
Vector localCoordinates(const Cal3DS2& T2) const;
|
Vector localCoordinates(const Cal3DS2& T2) const;
|
||||||
|
|
||||||
/// Return dimensions of calibration manifold object
|
/// Return dimensions of calibration manifold object
|
||||||
virtual size_t dim() const { return dimension ; }
|
virtual size_t dim() const override { return Dim(); }
|
||||||
|
|
||||||
/// Return dimensions of calibration manifold object
|
/// Return dimensions of calibration manifold object
|
||||||
static size_t Dim() { return dimension; }
|
inline static size_t Dim() { return dimension; }
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Clone
|
/// @name Clone
|
||||||
|
|
@ -92,23 +96,19 @@ public:
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// @name Advanced Interface
|
/// @name Advanced Interface
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/** Serialization function */
|
/** Serialization function */
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive & ar, const unsigned int /*version*/)
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
||||||
{
|
ar& boost::serialization::make_nvp(
|
||||||
ar & boost::serialization::make_nvp("Cal3DS2",
|
"Cal3DS2", boost::serialization::base_object<Cal3DS2_Base>(*this));
|
||||||
boost::serialization::base_object<Cal3DS2_Base>(*this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
@ -116,6 +116,4 @@ struct traits<Cal3DS2> : public internal::Manifold<Cal3DS2> {};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct traits<const Cal3DS2> : public internal::Manifold<Cal3DS2> {};
|
struct traits<const Cal3DS2> : public internal::Manifold<Cal3DS2> {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,27 +13,17 @@
|
||||||
* @file Cal3DS2_Base.cpp
|
* @file Cal3DS2_Base.cpp
|
||||||
* @date Feb 28, 2010
|
* @date Feb 28, 2010
|
||||||
* @author ydjian
|
* @author ydjian
|
||||||
|
* @author Varun Agrawal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtsam/base/Vector.h>
|
|
||||||
#include <gtsam/base/Matrix.h>
|
#include <gtsam/base/Matrix.h>
|
||||||
|
#include <gtsam/base/Vector.h>
|
||||||
|
#include <gtsam/geometry/Cal3DS2_Base.h>
|
||||||
#include <gtsam/geometry/Point2.h>
|
#include <gtsam/geometry/Point2.h>
|
||||||
#include <gtsam/geometry/Point3.h>
|
#include <gtsam/geometry/Point3.h>
|
||||||
#include <gtsam/geometry/Cal3DS2_Base.h>
|
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
Cal3DS2_Base::Cal3DS2_Base(const Vector &v):
|
|
||||||
fx_(v[0]), fy_(v[1]), s_(v[2]), u0_(v[3]), v0_(v[4]), k1_(v[5]), k2_(v[6]), p1_(v[7]), p2_(v[8]){}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
Matrix3 Cal3DS2_Base::K() const {
|
|
||||||
Matrix3 K;
|
|
||||||
K << fx_, s_, u0_, 0.0, fy_, v0_, 0.0, 0.0, 1.0;
|
|
||||||
return K;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Vector9 Cal3DS2_Base::vector() const {
|
Vector9 Cal3DS2_Base::vector() const {
|
||||||
Vector9 v;
|
Vector9 v;
|
||||||
|
|
@ -41,6 +31,14 @@ Vector9 Cal3DS2_Base::vector() const {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Cal3DS2_Base& cal) {
|
||||||
|
os << (Cal3&)cal;
|
||||||
|
os << ", k1: " << cal.k1() << ", k2: " << cal.k2() << ", p1: " << cal.p1()
|
||||||
|
<< ", p2: " << cal.p2();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
void Cal3DS2_Base::print(const std::string& s_) const {
|
void Cal3DS2_Base::print(const std::string& s_) const {
|
||||||
gtsam::print((Matrix)K(), s_ + ".K");
|
gtsam::print((Matrix)K(), s_ + ".K");
|
||||||
|
|
@ -49,17 +47,16 @@ void Cal3DS2_Base::print(const std::string& s_) const {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
bool Cal3DS2_Base::equals(const Cal3DS2_Base& K, double tol) const {
|
bool Cal3DS2_Base::equals(const Cal3DS2_Base& K, double tol) const {
|
||||||
if (std::abs(fx_ - K.fx_) > tol || std::abs(fy_ - K.fy_) > tol || std::abs(s_ - K.s_) > tol ||
|
const Cal3* base = dynamic_cast<const Cal3*>(&K);
|
||||||
std::abs(u0_ - K.u0_) > tol || std::abs(v0_ - K.v0_) > tol || std::abs(k1_ - K.k1_) > tol ||
|
return Cal3::equals(*base, tol) && std::fabs(k1_ - K.k1_) < tol &&
|
||||||
std::abs(k2_ - K.k2_) > tol || std::abs(p1_ - K.p1_) > tol || std::abs(p2_ - K.p2_) > tol)
|
std::fabs(k2_ - K.k2_) < tol && std::fabs(p1_ - K.p1_) < tol &&
|
||||||
return false;
|
std::fabs(p2_ - K.p2_) < tol;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
static Matrix29 D2dcalibration(double x, double y, double xx,
|
static Matrix29 D2dcalibration(double x, double y, double xx, double yy,
|
||||||
double yy, double xy, double rr, double r4, double pnx, double pny,
|
double xy, double rr, double r4, double pnx,
|
||||||
const Matrix2& DK) {
|
double pny, const Matrix2& DK) {
|
||||||
Matrix25 DR1;
|
Matrix25 DR1;
|
||||||
DR1 << pnx, 0.0, pny, 1.0, 0.0, 0.0, pny, 0.0, 0.0, 1.0;
|
DR1 << pnx, 0.0, pny, 1.0, 0.0, 0.0, pny, 0.0, 0.0, 1.0;
|
||||||
Matrix24 DR2;
|
Matrix24 DR2;
|
||||||
|
|
@ -71,8 +68,8 @@ static Matrix29 D2dcalibration(double x, double y, double xx,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
static Matrix2 D2dintrinsic(double x, double y, double rr,
|
static Matrix2 D2dintrinsic(double x, double y, double rr, double g, double k1,
|
||||||
double g, double k1, double k2, double p1, double p2,
|
double k2, double p1, double p2,
|
||||||
const Matrix2& DK) {
|
const Matrix2& DK) {
|
||||||
const double drdx = 2. * x;
|
const double drdx = 2. * x;
|
||||||
const double drdy = 2. * y;
|
const double drdy = 2. * y;
|
||||||
|
|
@ -94,12 +91,11 @@ static Matrix2 D2dintrinsic(double x, double y, double rr,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 Cal3DS2_Base::uncalibrate(const Point2& p,
|
Point2 Cal3DS2_Base::uncalibrate(const Point2& p, OptionalJacobian<2, 9> Dcal,
|
||||||
OptionalJacobian<2,9> H1, OptionalJacobian<2,2> H2) const {
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
|
// r² = x² + y²;
|
||||||
// rr = x^2 + y^2;
|
// g = (1 + k(1)*r² + k(2)*r⁴);
|
||||||
// g = (1 + k(1)*rr + k(2)*rr^2);
|
// dp = [2*k(3)*x*y + k(4)*(r² + 2*x²); 2*k(4)*x*y + k(3)*(r² + 2*y²)];
|
||||||
// dp = [2*k(3)*x*y + k(4)*(rr + 2*x^2); 2*k(4)*x*y + k(3)*(rr + 2*y^2)];
|
|
||||||
// pi(:,i) = g * pn(:,i) + dp;
|
// pi(:,i) = g * pn(:,i) + dp;
|
||||||
const double x = p.x(), y = p.y(), xy = x * y, xx = x * x, yy = y * y;
|
const double x = p.x(), y = p.y(), xy = x * y, xx = x * x, yy = y * y;
|
||||||
const double rr = xx + yy;
|
const double rr = xx + yy;
|
||||||
|
|
@ -115,37 +111,44 @@ Point2 Cal3DS2_Base::uncalibrate(const Point2& p,
|
||||||
const double pny = g * y + dy;
|
const double pny = g * y + dy;
|
||||||
|
|
||||||
Matrix2 DK;
|
Matrix2 DK;
|
||||||
if (H1 || H2) DK << fx_, s_, 0.0, fy_;
|
if (Dcal || Dp) {
|
||||||
|
DK << fx_, s_, 0.0, fy_;
|
||||||
|
}
|
||||||
|
|
||||||
// Derivative for calibration
|
// Derivative for calibration
|
||||||
if (H1)
|
if (Dcal) {
|
||||||
*H1 = D2dcalibration(x, y, xx, yy, xy, rr, r4, pnx, pny, DK);
|
*Dcal = D2dcalibration(x, y, xx, yy, xy, rr, r4, pnx, pny, DK);
|
||||||
|
}
|
||||||
|
|
||||||
// Derivative for points
|
// Derivative for points
|
||||||
if (H2)
|
if (Dp) {
|
||||||
*H2 = D2dintrinsic(x, y, rr, g, k1_, k2_, p1_, p2_, DK);
|
*Dp = D2dintrinsic(x, y, rr, g, k1_, k2_, p1_, p2_, DK);
|
||||||
|
}
|
||||||
|
|
||||||
// Regular uncalibrate after distortion
|
// Regular uncalibrate after distortion
|
||||||
return Point2(fx_ * pnx + s_ * pny + u0_, fy_ * pny + v0_);
|
return Point2(fx_ * pnx + s_ * pny + u0_, fy_ * pny + v0_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 Cal3DS2_Base::calibrate(const Point2& pi, const double tol) const {
|
Point2 Cal3DS2_Base::calibrate(const Point2& pi, OptionalJacobian<2, 9> Dcal,
|
||||||
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
// Use the following fixed point iteration to invert the radial distortion.
|
// Use the following fixed point iteration to invert the radial distortion.
|
||||||
// pn_{t+1} = (inv(K)*pi - dp(pn_{t})) / g(pn_{t})
|
// pn_{t+1} = (inv(K)*pi - dp(pn_{t})) / g(pn_{t})
|
||||||
|
|
||||||
const Point2 invKPi((1 / fx_) * (pi.x() - u0_ - (s_ / fy_) * (pi.y() - v0_)),
|
const Point2 invKPi((1 / fx_) * (pi.x() - u0_ - (s_ / fy_) * (pi.y() - v0_)),
|
||||||
(1 / fy_) * (pi.y() - v0_));
|
(1 / fy_) * (pi.y() - v0_));
|
||||||
|
|
||||||
// initialize by ignoring the distortion at all, might be problematic for pixels around boundary
|
// initialize by ignoring the distortion at all, might be problematic for
|
||||||
|
// pixels around boundary
|
||||||
Point2 pn = invKPi;
|
Point2 pn = invKPi;
|
||||||
|
|
||||||
// iterate until the uncalibrate is close to the actual pixel coordinate
|
// iterate until the uncalibrate is close to the actual pixel coordinate
|
||||||
const int maxIterations = 10;
|
const int maxIterations = 10;
|
||||||
int iteration;
|
int iteration;
|
||||||
for (iteration = 0; iteration < maxIterations; ++iteration) {
|
for (iteration = 0; iteration < maxIterations; ++iteration) {
|
||||||
if (distance2(uncalibrate(pn), pi) <= tol) break;
|
if (distance2(uncalibrate(pn), pi) <= tol_) break;
|
||||||
const double x = pn.x(), y = pn.y(), xy = x * y, xx = x * x, yy = y * y;
|
const double px = pn.x(), py = pn.y(), xy = px * py, xx = px * px,
|
||||||
|
yy = py * py;
|
||||||
const double rr = xx + yy;
|
const double rr = xx + yy;
|
||||||
const double g = (1 + k1_ * rr + k2_ * rr * rr);
|
const double g = (1 + k1_ * rr + k2_ * rr * rr);
|
||||||
const double dx = 2 * p1_ * xy + p2_ * (rr + 2 * xx);
|
const double dx = 2 * p1_ * xy + p2_ * (rr + 2 * xx);
|
||||||
|
|
@ -154,7 +157,10 @@ Point2 Cal3DS2_Base::calibrate(const Point2& pi, const double tol) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iteration >= maxIterations)
|
if (iteration >= maxIterations)
|
||||||
throw std::runtime_error("Cal3DS2::calibrate fails to converge. need a better initialization");
|
throw std::runtime_error(
|
||||||
|
"Cal3DS2::calibrate fails to converge. need a better initialization");
|
||||||
|
|
||||||
|
calibrateJacobians<Cal3DS2_Base, dimension>(*this, pn, Dcal, Dp);
|
||||||
|
|
||||||
return pn;
|
return pn;
|
||||||
}
|
}
|
||||||
|
|
@ -184,8 +190,5 @@ Matrix29 Cal3DS2_Base::D2d_calibration(const Point2& p) const {
|
||||||
DK << fx_, s_, 0.0, fy_;
|
DK << fx_, s_, 0.0, fy_;
|
||||||
return D2dcalibration(x, y, xx, yy, xy, rr, r4, pnx, pny, DK);
|
return D2dcalibration(x, y, xx, yy, xy, rr, r4, pnx, pny, DK);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,12 @@
|
||||||
* @brief Calibration of a camera with radial distortion
|
* @brief Calibration of a camera with radial distortion
|
||||||
* @date Feb 28, 2010
|
* @date Feb 28, 2010
|
||||||
* @author ydjian
|
* @author ydjian
|
||||||
|
* @author Varun Agrawal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/geometry/Cal3.h>
|
||||||
#include <gtsam/geometry/Point2.h>
|
#include <gtsam/geometry/Point2.h>
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
@ -31,30 +33,34 @@ namespace gtsam {
|
||||||
* http://docs.opencv.org/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html
|
* http://docs.opencv.org/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html
|
||||||
* but using only k1,k2,p1, and p2 coefficients.
|
* but using only k1,k2,p1, and p2 coefficients.
|
||||||
* K = [ fx s u0 ; 0 fy v0 ; 0 0 1 ]
|
* K = [ fx s u0 ; 0 fy v0 ; 0 0 1 ]
|
||||||
* rr = Pn.x^2 + Pn.y^2
|
* r² = P.x² + P.y²
|
||||||
* \hat{Pn} = (1 + k1*rr + k2*rr^2 ) Pn + [ 2*p1 Pn.x Pn.y + p2 (rr + 2 Pn.x^2) ;
|
* P̂ = (1 + k1*r² + k2*r⁴) P + [ (2*p1 P.x P.y) + p2 (r² + 2 Pn.x²)
|
||||||
* p1 (rr + 2 Pn.y^2) + 2*p2 Pn.x Pn.y ]
|
* p1 (r² + 2 Pn.y²) + (2*p2 Pn.x Pn.y) ]
|
||||||
* pi = K*Pn
|
* pi = K*P̂
|
||||||
*/
|
*/
|
||||||
class GTSAM_EXPORT Cal3DS2_Base {
|
class GTSAM_EXPORT Cal3DS2_Base : public Cal3 {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
double k1_ = 0.0f, k2_ = 0.0f; ///< radial 2nd-order and 4th-order
|
||||||
double fx_, fy_, s_, u0_, v0_ ; // focal length, skew and principal point
|
double p1_ = 0.0f, p2_ = 0.0f; ///< tangential distortion
|
||||||
double k1_, k2_ ; // radial 2nd-order and 4th-order
|
double tol_ = 1e-5; ///< tolerance value when calibrating
|
||||||
double p1_, p2_ ; // tangential distortion
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum { dimension = 9 };
|
||||||
|
|
||||||
/// @name Standard Constructors
|
/// @name Standard Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// Default Constructor with only unit focal length
|
/// Default Constructor with only unit focal length
|
||||||
Cal3DS2_Base() : fx_(1), fy_(1), s_(0), u0_(0), v0_(0), k1_(0), k2_(0), p1_(0), p2_(0) {}
|
Cal3DS2_Base() = default;
|
||||||
|
|
||||||
Cal3DS2_Base(double fx, double fy, double s, double u0, double v0,
|
Cal3DS2_Base(double fx, double fy, double s, double u0, double v0, double k1,
|
||||||
double k1, double k2, double p1 = 0.0, double p2 = 0.0) :
|
double k2, double p1 = 0.0, double p2 = 0.0, double tol = 1e-5)
|
||||||
fx_(fx), fy_(fy), s_(s), u0_(u0), v0_(v0), k1_(k1), k2_(k2), p1_(p1), p2_(p2) {}
|
: Cal3(fx, fy, s, u0, v0),
|
||||||
|
k1_(k1),
|
||||||
|
k2_(k2),
|
||||||
|
p1_(p1),
|
||||||
|
p2_(p2),
|
||||||
|
tol_(tol) {}
|
||||||
|
|
||||||
virtual ~Cal3DS2_Base() {}
|
virtual ~Cal3DS2_Base() {}
|
||||||
|
|
||||||
|
|
@ -62,37 +68,31 @@ public:
|
||||||
/// @name Advanced Constructors
|
/// @name Advanced Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
Cal3DS2_Base(const Vector &v) ;
|
Cal3DS2_Base(const Vector9& v)
|
||||||
|
: Cal3(v(0), v(1), v(2), v(3), v(4)),
|
||||||
|
k1_(v(5)),
|
||||||
|
k2_(v(6)),
|
||||||
|
p1_(v(7)),
|
||||||
|
p2_(v(8)) {}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Testable
|
/// @name Testable
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
/// Output stream operator
|
||||||
|
GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
const Cal3DS2_Base& cal);
|
||||||
|
|
||||||
/// print with optional string
|
/// print with optional string
|
||||||
virtual void print(const std::string& s = "") const;
|
void print(const std::string& s = "") const override;
|
||||||
|
|
||||||
/// assert equality up to a tolerance
|
/// assert equality up to a tolerance
|
||||||
bool equals(const Cal3DS2_Base& K, double tol = 10e-9) const;
|
bool equals(const Cal3DS2_Base& K, double tol = 1e-8) const;
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Standard Interface
|
/// @name Standard Interface
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// focal length x
|
|
||||||
inline double fx() const { return fx_;}
|
|
||||||
|
|
||||||
/// focal length x
|
|
||||||
inline double fy() const { return fy_;}
|
|
||||||
|
|
||||||
/// skew
|
|
||||||
inline double skew() const { return s_;}
|
|
||||||
|
|
||||||
/// image center in x
|
|
||||||
inline double px() const { return u0_;}
|
|
||||||
|
|
||||||
/// image center in y
|
|
||||||
inline double py() const { return v0_;}
|
|
||||||
|
|
||||||
/// First distortion coefficient
|
/// First distortion coefficient
|
||||||
inline double k1() const { return k1_; }
|
inline double k1() const { return k1_; }
|
||||||
|
|
||||||
|
|
@ -105,9 +105,6 @@ public:
|
||||||
/// Second tangential distortion coefficient
|
/// Second tangential distortion coefficient
|
||||||
inline double p2() const { return p2_; }
|
inline double p2() const { return p2_; }
|
||||||
|
|
||||||
/// return calibration matrix -- not really applicable
|
|
||||||
Matrix3 K() const;
|
|
||||||
|
|
||||||
/// return distortion parameter vector
|
/// return distortion parameter vector
|
||||||
Vector4 k() const { return Vector4(k1_, k2_, p1_, p2_); }
|
Vector4 k() const { return Vector4(k1_, k2_, p1_, p2_); }
|
||||||
|
|
||||||
|
|
@ -121,12 +118,12 @@ public:
|
||||||
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
||||||
* @return point in (distorted) image coordinates
|
* @return point in (distorted) image coordinates
|
||||||
*/
|
*/
|
||||||
Point2 uncalibrate(const Point2& p,
|
Point2 uncalibrate(const Point2& p, OptionalJacobian<2, 9> Dcal = boost::none,
|
||||||
OptionalJacobian<2,9> Dcal = boost::none,
|
|
||||||
OptionalJacobian<2, 2> Dp = boost::none) const;
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
/// Convert (distorted) image coordinates uv to intrinsic coordinates xy
|
/// Convert (distorted) image coordinates uv to intrinsic coordinates xy
|
||||||
Point2 calibrate(const Point2& p, const double tol=1e-5) const;
|
Point2 calibrate(const Point2& p, OptionalJacobian<2, 9> Dcal = boost::none,
|
||||||
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
/// Derivative of uncalibrate wrpt intrinsic coordinates
|
/// Derivative of uncalibrate wrpt intrinsic coordinates
|
||||||
Matrix2 D2d_intrinsic(const Point2& p) const;
|
Matrix2 D2d_intrinsic(const Point2& p) const;
|
||||||
|
|
@ -134,6 +131,12 @@ public:
|
||||||
/// Derivative of uncalibrate wrpt the calibration parameters
|
/// Derivative of uncalibrate wrpt the calibration parameters
|
||||||
Matrix29 D2d_calibration(const Point2& p) const;
|
Matrix29 D2d_calibration(const Point2& p) const;
|
||||||
|
|
||||||
|
/// return DOF, dimensionality of tangent space
|
||||||
|
virtual size_t dim() const override { return Dim(); }
|
||||||
|
|
||||||
|
/// return DOF, dimensionality of tangent space
|
||||||
|
inline static size_t Dim() { return dimension; }
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Clone
|
/// @name Clone
|
||||||
/// @{
|
/// @{
|
||||||
|
|
@ -146,29 +149,22 @@ public:
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// @name Advanced Interface
|
/// @name Advanced Interface
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/** Serialization function */
|
/** Serialization function */
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive & ar, const unsigned int /*version*/)
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
||||||
{
|
ar& boost::serialization::make_nvp(
|
||||||
ar & BOOST_SERIALIZATION_NVP(fx_);
|
"Cal3DS2_Base", boost::serialization::base_object<Cal3>(*this));
|
||||||
ar & BOOST_SERIALIZATION_NVP(fy_);
|
|
||||||
ar & BOOST_SERIALIZATION_NVP(s_);
|
|
||||||
ar & BOOST_SERIALIZATION_NVP(u0_);
|
|
||||||
ar & BOOST_SERIALIZATION_NVP(v0_);
|
|
||||||
ar& BOOST_SERIALIZATION_NVP(k1_);
|
ar& BOOST_SERIALIZATION_NVP(k1_);
|
||||||
ar& BOOST_SERIALIZATION_NVP(k2_);
|
ar& BOOST_SERIALIZATION_NVP(k2_);
|
||||||
ar& BOOST_SERIALIZATION_NVP(p1_);
|
ar& BOOST_SERIALIZATION_NVP(p1_);
|
||||||
ar& BOOST_SERIALIZATION_NVP(p2_);
|
ar& BOOST_SERIALIZATION_NVP(p2_);
|
||||||
|
ar& BOOST_SERIALIZATION_NVP(tol_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
* @file Cal3Fisheye.cpp
|
* @file Cal3Fisheye.cpp
|
||||||
* @date Apr 8, 2020
|
* @date Apr 8, 2020
|
||||||
* @author ghaggin
|
* @author ghaggin
|
||||||
|
* @author Varun Agrawal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtsam/base/Matrix.h>
|
#include <gtsam/base/Matrix.h>
|
||||||
|
|
@ -23,18 +24,6 @@
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
Cal3Fisheye::Cal3Fisheye(const Vector9& v)
|
|
||||||
: fx_(v[0]),
|
|
||||||
fy_(v[1]),
|
|
||||||
s_(v[2]),
|
|
||||||
u0_(v[3]),
|
|
||||||
v0_(v[4]),
|
|
||||||
k1_(v[5]),
|
|
||||||
k2_(v[6]),
|
|
||||||
k3_(v[7]),
|
|
||||||
k4_(v[8]) {}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Vector9 Cal3Fisheye::vector() const {
|
Vector9 Cal3Fisheye::vector() const {
|
||||||
Vector9 v;
|
Vector9 v;
|
||||||
|
|
@ -42,13 +31,6 @@ Vector9 Cal3Fisheye::vector() const {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
Matrix3 Cal3Fisheye::K() const {
|
|
||||||
Matrix3 K;
|
|
||||||
K << fx_, s_, u0_, 0.0, fy_, v0_, 0.0, 0.0, 1.0;
|
|
||||||
return K;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
double Cal3Fisheye::Scaling(double r) {
|
double Cal3Fisheye::Scaling(double r) {
|
||||||
static constexpr double threshold = 1e-8;
|
static constexpr double threshold = 1e-8;
|
||||||
|
|
@ -122,14 +104,15 @@ Point2 Cal3Fisheye::uncalibrate(const Point2& p, OptionalJacobian<2, 9> H1,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 Cal3Fisheye::calibrate(const Point2& uv, const double tol) const {
|
Point2 Cal3Fisheye::calibrate(const Point2& uv, OptionalJacobian<2, 9> Dcal,
|
||||||
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
// initial gues just inverts the pinhole model
|
// initial gues just inverts the pinhole model
|
||||||
const double u = uv.x(), v = uv.y();
|
const double u = uv.x(), v = uv.y();
|
||||||
const double yd = (v - v0_) / fy_;
|
const double yd = (v - v0_) / fy_;
|
||||||
const double xd = (u - s_ * yd - u0_) / fx_;
|
const double xd = (u - s_ * yd - u0_) / fx_;
|
||||||
Point2 pi(xd, yd);
|
Point2 pi(xd, yd);
|
||||||
|
|
||||||
// Perform newtons method, break when solution converges past tol,
|
// Perform newtons method, break when solution converges past tol_,
|
||||||
// throw exception if max iterations are reached
|
// throw exception if max iterations are reached
|
||||||
const int maxIterations = 10;
|
const int maxIterations = 10;
|
||||||
int iteration;
|
int iteration;
|
||||||
|
|
@ -140,7 +123,7 @@ Point2 Cal3Fisheye::calibrate(const Point2& uv, const double tol) const {
|
||||||
const Point2 uv_hat = uncalibrate(pi, boost::none, jac);
|
const Point2 uv_hat = uncalibrate(pi, boost::none, jac);
|
||||||
|
|
||||||
// Test convergence
|
// Test convergence
|
||||||
if ((uv_hat - uv).norm() < tol) break;
|
if ((uv_hat - uv).norm() < tol_) break;
|
||||||
|
|
||||||
// Newton's method update step
|
// Newton's method update step
|
||||||
pi = pi - jac.inverse() * (uv_hat - uv);
|
pi = pi - jac.inverse() * (uv_hat - uv);
|
||||||
|
|
@ -151,9 +134,19 @@ Point2 Cal3Fisheye::calibrate(const Point2& uv, const double tol) const {
|
||||||
"Cal3Fisheye::calibrate fails to converge. need a better "
|
"Cal3Fisheye::calibrate fails to converge. need a better "
|
||||||
"initialization");
|
"initialization");
|
||||||
|
|
||||||
|
calibrateJacobians<Cal3Fisheye, dimension>(*this, pi, Dcal, Dp);
|
||||||
|
|
||||||
return pi;
|
return pi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Cal3Fisheye& cal) {
|
||||||
|
os << (Cal3&)cal;
|
||||||
|
os << ", k1: " << cal.k1() << ", k2: " << cal.k2() << ", k3: " << cal.k3()
|
||||||
|
<< ", k4: " << cal.k4();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
void Cal3Fisheye::print(const std::string& s_) const {
|
void Cal3Fisheye::print(const std::string& s_) const {
|
||||||
gtsam::print((Matrix)K(), s_ + ".K");
|
gtsam::print((Matrix)K(), s_ + ".K");
|
||||||
|
|
@ -162,24 +155,12 @@ void Cal3Fisheye::print(const std::string& s_) const {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
bool Cal3Fisheye::equals(const Cal3Fisheye& K, double tol) const {
|
bool Cal3Fisheye::equals(const Cal3Fisheye& K, double tol) const {
|
||||||
if (std::abs(fx_ - K.fx_) > tol || std::abs(fy_ - K.fy_) > tol ||
|
const Cal3* base = dynamic_cast<const Cal3*>(&K);
|
||||||
std::abs(s_ - K.s_) > tol || std::abs(u0_ - K.u0_) > tol ||
|
return Cal3::equals(*base, tol) && std::fabs(k1_ - K.k1_) < tol &&
|
||||||
std::abs(v0_ - K.v0_) > tol || std::abs(k1_ - K.k1_) > tol ||
|
std::fabs(k2_ - K.k2_) < tol && std::fabs(k3_ - K.k3_) < tol &&
|
||||||
std::abs(k2_ - K.k2_) > tol || std::abs(k3_ - K.k3_) > tol ||
|
std::fabs(k4_ - K.k4_) < tol;
|
||||||
std::abs(k4_ - K.k4_) > tol)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Cal3Fisheye Cal3Fisheye::retract(const Vector& d) const {
|
|
||||||
return Cal3Fisheye(vector() + d);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
} // \ namespace gtsam
|
||||||
Vector Cal3Fisheye::localCoordinates(const Cal3Fisheye& T2) const {
|
|
||||||
return T2.vector() - vector();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace gtsam
|
|
||||||
/* ************************************************************************* */
|
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,12 @@
|
||||||
* @brief Calibration of a fisheye camera
|
* @brief Calibration of a fisheye camera
|
||||||
* @date Apr 8, 2020
|
* @date Apr 8, 2020
|
||||||
* @author ghaggin
|
* @author ghaggin
|
||||||
|
* @author Varun Agrawal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/geometry/Cal3.h>
|
||||||
#include <gtsam/geometry/Point2.h>
|
#include <gtsam/geometry/Point2.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -36,43 +38,40 @@ namespace gtsam {
|
||||||
* Intrinsic coordinates:
|
* Intrinsic coordinates:
|
||||||
* [x_i;y_i] = [x/z; y/z]
|
* [x_i;y_i] = [x/z; y/z]
|
||||||
* Distorted coordinates:
|
* Distorted coordinates:
|
||||||
* r^2 = (x_i)^2 + (y_i)^2
|
* r² = (x_i)² + (y_i)²
|
||||||
* th = atan(r)
|
* th = atan(r)
|
||||||
* th_d = th(1 + k1*th^2 + k2*th^4 + k3*th^6 + k4*th^8)
|
* th_d = th(1 + k1*th² + k2*th⁴ + k3*th⁶ + k4*th⁸)
|
||||||
* [x_d; y_d] = (th_d / r)*[x_i; y_i]
|
* [x_d; y_d] = (th_d / r)*[x_i; y_i]
|
||||||
* Pixel coordinates:
|
* Pixel coordinates:
|
||||||
* K = [fx s u0; 0 fy v0 ;0 0 1]
|
* K = [fx s u0; 0 fy v0 ;0 0 1]
|
||||||
* [u; v; 1] = K*[x_d; y_d; 1]
|
* [u; v; 1] = K*[x_d; y_d; 1]
|
||||||
*/
|
*/
|
||||||
class GTSAM_EXPORT Cal3Fisheye {
|
class GTSAM_EXPORT Cal3Fisheye : public Cal3 {
|
||||||
private:
|
private:
|
||||||
double fx_, fy_, s_, u0_, v0_; // focal length, skew and principal point
|
double k1_ = 0.0f, k2_ = 0.0f; ///< fisheye distortion coefficients
|
||||||
double k1_, k2_, k3_, k4_; // fisheye distortion coefficients
|
double k3_ = 0.0f, k4_ = 0.0f; ///< fisheye distortion coefficients
|
||||||
|
double tol_ = 1e-5; ///< tolerance value when calibrating
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum { dimension = 9 };
|
enum { dimension = 9 };
|
||||||
typedef boost::shared_ptr<Cal3Fisheye>
|
///< shared pointer to fisheye calibration object
|
||||||
shared_ptr; ///< shared pointer to fisheye calibration object
|
using shared_ptr = boost::shared_ptr<Cal3Fisheye>;
|
||||||
|
|
||||||
/// @name Standard Constructors
|
/// @name Standard Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// Default Constructor with only unit focal length
|
/// Default Constructor with only unit focal length
|
||||||
Cal3Fisheye()
|
Cal3Fisheye() = default;
|
||||||
: fx_(1), fy_(1), s_(0), u0_(0), v0_(0), k1_(0), k2_(0), k3_(0), k4_(0) {}
|
|
||||||
|
|
||||||
Cal3Fisheye(const double fx, const double fy, const double s, const double u0,
|
Cal3Fisheye(const double fx, const double fy, const double s, const double u0,
|
||||||
const double v0, const double k1, const double k2,
|
const double v0, const double k1, const double k2,
|
||||||
const double k3, const double k4)
|
const double k3, const double k4, double tol = 1e-5)
|
||||||
: fx_(fx),
|
: Cal3(fx, fy, s, u0, v0),
|
||||||
fy_(fy),
|
|
||||||
s_(s),
|
|
||||||
u0_(u0),
|
|
||||||
v0_(v0),
|
|
||||||
k1_(k1),
|
k1_(k1),
|
||||||
k2_(k2),
|
k2_(k2),
|
||||||
k3_(k3),
|
k3_(k3),
|
||||||
k4_(k4) {}
|
k4_(k4),
|
||||||
|
tol_(tol) {}
|
||||||
|
|
||||||
virtual ~Cal3Fisheye() {}
|
virtual ~Cal3Fisheye() {}
|
||||||
|
|
||||||
|
|
@ -80,27 +79,17 @@ class GTSAM_EXPORT Cal3Fisheye {
|
||||||
/// @name Advanced Constructors
|
/// @name Advanced Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
explicit Cal3Fisheye(const Vector9& v);
|
explicit Cal3Fisheye(const Vector9& v)
|
||||||
|
: Cal3(v(0), v(1), v(2), v(3), v(4)),
|
||||||
|
k1_(v(5)),
|
||||||
|
k2_(v(6)),
|
||||||
|
k3_(v(7)),
|
||||||
|
k4_(v(8)) {}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Standard Interface
|
/// @name Standard Interface
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// focal length x
|
|
||||||
inline double fx() const { return fx_; }
|
|
||||||
|
|
||||||
/// focal length x
|
|
||||||
inline double fy() const { return fy_; }
|
|
||||||
|
|
||||||
/// skew
|
|
||||||
inline double skew() const { return s_; }
|
|
||||||
|
|
||||||
/// image center in x
|
|
||||||
inline double px() const { return u0_; }
|
|
||||||
|
|
||||||
/// image center in y
|
|
||||||
inline double py() const { return v0_; }
|
|
||||||
|
|
||||||
/// First distortion coefficient
|
/// First distortion coefficient
|
||||||
inline double k1() const { return k1_; }
|
inline double k1() const { return k1_; }
|
||||||
|
|
||||||
|
|
@ -113,9 +102,6 @@ class GTSAM_EXPORT Cal3Fisheye {
|
||||||
/// Second tangential distortion coefficient
|
/// Second tangential distortion coefficient
|
||||||
inline double k4() const { return k4_; }
|
inline double k4() const { return k4_; }
|
||||||
|
|
||||||
/// return calibration matrix
|
|
||||||
Matrix3 K() const;
|
|
||||||
|
|
||||||
/// return distortion parameter vector
|
/// return distortion parameter vector
|
||||||
Vector4 k() const { return Vector4(k1_, k2_, k3_, k4_); }
|
Vector4 k() const { return Vector4(k1_, k2_, k3_, k4_); }
|
||||||
|
|
||||||
|
|
@ -129,24 +115,34 @@ class GTSAM_EXPORT Cal3Fisheye {
|
||||||
* @brief convert intrinsic coordinates [x_i; y_i] to (distorted) image
|
* @brief convert intrinsic coordinates [x_i; y_i] to (distorted) image
|
||||||
* coordinates [u; v]
|
* coordinates [u; v]
|
||||||
* @param p point in intrinsic coordinates
|
* @param p point in intrinsic coordinates
|
||||||
* @param Dcal optional 2*9 Jacobian wrpt intrinsic parameters (fx, fy, ...,
|
* @param Dcal optional 2*9 Jacobian wrpt intrinsic parameters
|
||||||
* k4)
|
|
||||||
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates (xi, yi)
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates (xi, yi)
|
||||||
* @return point in (distorted) image coordinates
|
* @return point in (distorted) image coordinates
|
||||||
*/
|
*/
|
||||||
Point2 uncalibrate(const Point2& p, OptionalJacobian<2, 9> Dcal = boost::none,
|
Point2 uncalibrate(const Point2& p, OptionalJacobian<2, 9> Dcal = boost::none,
|
||||||
OptionalJacobian<2, 2> Dp = boost::none) const;
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
/// Convert (distorted) image coordinates [u;v] to intrinsic coordinates [x_i,
|
/**
|
||||||
/// y_i]
|
* Convert (distorted) image coordinates [u;v] to intrinsic coordinates [x_i,
|
||||||
Point2 calibrate(const Point2& p, const double tol = 1e-5) const;
|
* y_i]
|
||||||
|
* @param p point in image coordinates
|
||||||
|
* @param Dcal optional 2*9 Jacobian wrpt intrinsic parameters
|
||||||
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates (xi, yi)
|
||||||
|
* @return point in intrinsic coordinates
|
||||||
|
*/
|
||||||
|
Point2 calibrate(const Point2& p, OptionalJacobian<2, 9> Dcal = boost::none,
|
||||||
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Testable
|
/// @name Testable
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
/// Output stream operator
|
||||||
|
GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
const Cal3Fisheye& cal);
|
||||||
|
|
||||||
/// print with optional string
|
/// print with optional string
|
||||||
virtual void print(const std::string& s = "") const;
|
virtual void print(const std::string& s = "") const override;
|
||||||
|
|
||||||
/// assert equality up to a tolerance
|
/// assert equality up to a tolerance
|
||||||
bool equals(const Cal3Fisheye& K, double tol = 10e-9) const;
|
bool equals(const Cal3Fisheye& K, double tol = 10e-9) const;
|
||||||
|
|
@ -155,17 +151,21 @@ class GTSAM_EXPORT Cal3Fisheye {
|
||||||
/// @name Manifold
|
/// @name Manifold
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
/// Return dimensions of calibration manifold object
|
||||||
|
virtual size_t dim() const override { return Dim(); }
|
||||||
|
|
||||||
|
/// Return dimensions of calibration manifold object
|
||||||
|
inline static size_t Dim() { return dimension; }
|
||||||
|
|
||||||
/// Given delta vector, update calibration
|
/// Given delta vector, update calibration
|
||||||
Cal3Fisheye retract(const Vector& d) const;
|
inline Cal3Fisheye retract(const Vector& d) const {
|
||||||
|
return Cal3Fisheye(vector() + d);
|
||||||
|
}
|
||||||
|
|
||||||
/// Given a different calibration, calculate update to obtain it
|
/// Given a different calibration, calculate update to obtain it
|
||||||
Vector localCoordinates(const Cal3Fisheye& T2) const;
|
Vector localCoordinates(const Cal3Fisheye& T2) const {
|
||||||
|
return T2.vector() - vector();
|
||||||
/// Return dimensions of calibration manifold object
|
}
|
||||||
virtual size_t dim() const { return 9; }
|
|
||||||
|
|
||||||
/// Return dimensions of calibration manifold object
|
|
||||||
static size_t Dim() { return 9; }
|
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Clone
|
/// @name Clone
|
||||||
|
|
@ -186,11 +186,8 @@ class GTSAM_EXPORT Cal3Fisheye {
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, const unsigned int /*version*/) {
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
||||||
ar& BOOST_SERIALIZATION_NVP(fx_);
|
ar& boost::serialization::make_nvp(
|
||||||
ar& BOOST_SERIALIZATION_NVP(fy_);
|
"Cal3Fisheye", boost::serialization::base_object<Cal3>(*this));
|
||||||
ar& BOOST_SERIALIZATION_NVP(s_);
|
|
||||||
ar& BOOST_SERIALIZATION_NVP(u0_);
|
|
||||||
ar& BOOST_SERIALIZATION_NVP(v0_);
|
|
||||||
ar& BOOST_SERIALIZATION_NVP(k1_);
|
ar& BOOST_SERIALIZATION_NVP(k1_);
|
||||||
ar& BOOST_SERIALIZATION_NVP(k2_);
|
ar& BOOST_SERIALIZATION_NVP(k2_);
|
||||||
ar& BOOST_SERIALIZATION_NVP(k3_);
|
ar& BOOST_SERIALIZATION_NVP(k3_);
|
||||||
|
|
|
||||||
|
|
@ -13,21 +13,18 @@
|
||||||
* @file Cal3Unified.cpp
|
* @file Cal3Unified.cpp
|
||||||
* @date Mar 8, 2014
|
* @date Mar 8, 2014
|
||||||
* @author Jing Dong
|
* @author Jing Dong
|
||||||
|
* @author Varun Agrawal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtsam/base/Vector.h>
|
|
||||||
#include <gtsam/base/Matrix.h>
|
#include <gtsam/base/Matrix.h>
|
||||||
#include <gtsam/geometry/Point2.h>
|
#include <gtsam/base/Vector.h>
|
||||||
#include <gtsam/geometry/Cal3Unified.h>
|
#include <gtsam/geometry/Cal3Unified.h>
|
||||||
|
#include <gtsam/geometry/Point2.h>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
Cal3Unified::Cal3Unified(const Vector &v):
|
|
||||||
Base(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]), xi_(v[9]) {}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Vector10 Cal3Unified::vector() const {
|
Vector10 Cal3Unified::vector() const {
|
||||||
Vector10 v;
|
Vector10 v;
|
||||||
|
|
@ -35,6 +32,13 @@ Vector10 Cal3Unified::vector() const {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Cal3Unified& cal) {
|
||||||
|
os << (Cal3DS2_Base&)cal;
|
||||||
|
os << ", xi: " << cal.xi();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
void Cal3Unified::print(const std::string& s) const {
|
void Cal3Unified::print(const std::string& s) const {
|
||||||
Base::print(s);
|
Base::print(s);
|
||||||
|
|
@ -43,20 +47,14 @@ void Cal3Unified::print(const std::string& s) const {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
bool Cal3Unified::equals(const Cal3Unified& K, double tol) const {
|
bool Cal3Unified::equals(const Cal3Unified& K, double tol) const {
|
||||||
if (std::abs(fx_ - K.fx_) > tol || std::abs(fy_ - K.fy_) > tol || std::abs(s_ - K.s_) > tol ||
|
const Cal3DS2_Base* base = dynamic_cast<const Cal3DS2_Base*>(&K);
|
||||||
std::abs(u0_ - K.u0_) > tol || std::abs(v0_ - K.v0_) > tol || std::abs(k1_ - K.k1_) > tol ||
|
return Cal3DS2_Base::equals(*base, tol) && std::fabs(xi_ - K.xi_) < tol;
|
||||||
std::abs(k2_ - K.k2_) > tol || std::abs(p1_ - K.p1_) > tol || std::abs(p2_ - K.p2_) > tol ||
|
|
||||||
std::abs(xi_ - K.xi_) > tol)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
// todo: make a fixed sized jacobian version of this
|
// todo: make a fixed sized jacobian version of this
|
||||||
Point2 Cal3Unified::uncalibrate(const Point2& p,
|
Point2 Cal3Unified::uncalibrate(const Point2& p, OptionalJacobian<2, 10> Dcal,
|
||||||
OptionalJacobian<2,10> H1,
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
OptionalJacobian<2,2> H2) const {
|
|
||||||
|
|
||||||
// this part of code is modified from Cal3DS2,
|
// this part of code is modified from Cal3DS2,
|
||||||
// since the second part of this model (after project to normalized plane)
|
// since the second part of this model (after project to normalized plane)
|
||||||
// is same as Cal3DS2
|
// is same as Cal3DS2
|
||||||
|
|
@ -78,16 +76,16 @@ Point2 Cal3Unified::uncalibrate(const Point2& p,
|
||||||
Point2 puncalib = Base::uncalibrate(m, H1base, H2base);
|
Point2 puncalib = Base::uncalibrate(m, H1base, H2base);
|
||||||
|
|
||||||
// Inlined derivative for calibration
|
// Inlined derivative for calibration
|
||||||
if (H1) {
|
if (Dcal) {
|
||||||
// part1
|
// part1
|
||||||
Vector2 DU;
|
Vector2 DU;
|
||||||
DU << -xs * sqrt_nx * xi_sqrt_nx2, //
|
DU << -xs * sqrt_nx * xi_sqrt_nx2, //
|
||||||
-ys * sqrt_nx * xi_sqrt_nx2;
|
-ys * sqrt_nx * xi_sqrt_nx2;
|
||||||
*H1 << H1base, H2base * DU;
|
*Dcal << H1base, H2base * DU;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inlined derivative for points
|
// Inlined derivative for points
|
||||||
if (H2) {
|
if (Dp) {
|
||||||
// part1
|
// part1
|
||||||
const double denom = 1.0 * xi_sqrt_nx2 / sqrt_nx;
|
const double denom = 1.0 * xi_sqrt_nx2 / sqrt_nx;
|
||||||
const double mid = -(xi * xs * ys) * denom;
|
const double mid = -(xi * xs * ys) * denom;
|
||||||
|
|
@ -95,24 +93,27 @@ Point2 Cal3Unified::uncalibrate(const Point2& p,
|
||||||
DU << (sqrt_nx + xi * (ys * ys + 1)) * denom, mid, //
|
DU << (sqrt_nx + xi * (ys * ys + 1)) * denom, mid, //
|
||||||
mid, (sqrt_nx + xi * (xs * xs + 1)) * denom;
|
mid, (sqrt_nx + xi * (xs * xs + 1)) * denom;
|
||||||
|
|
||||||
*H2 << H2base * DU;
|
*Dp << H2base * DU;
|
||||||
}
|
}
|
||||||
|
|
||||||
return puncalib;
|
return puncalib;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 Cal3Unified::calibrate(const Point2& pi, const double tol) const {
|
Point2 Cal3Unified::calibrate(const Point2& pi, OptionalJacobian<2, 10> Dcal,
|
||||||
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
// calibrate point to Nplane use base class::calibrate()
|
// calibrate point to Nplane use base class::calibrate()
|
||||||
Point2 pnplane = Base::calibrate(pi, tol);
|
Point2 pnplane = Base::calibrate(pi);
|
||||||
|
|
||||||
// call nplane to space
|
// call nplane to space
|
||||||
return this->nPlaneToSpace(pnplane);
|
Point2 pn = this->nPlaneToSpace(pnplane);
|
||||||
|
|
||||||
|
calibrateJacobians<Cal3Unified, dimension>(*this, pn, Dcal, Dp);
|
||||||
|
|
||||||
|
return pn;
|
||||||
}
|
}
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 Cal3Unified::nPlaneToSpace(const Point2& p) const {
|
Point2 Cal3Unified::nPlaneToSpace(const Point2& p) const {
|
||||||
|
|
||||||
const double x = p.x(), y = p.y();
|
const double x = p.x(), y = p.y();
|
||||||
const double xy2 = x * x + y * y;
|
const double xy2 = x * x + y * y;
|
||||||
const double sq_xy = (xi_ + sqrt(1 + (1 - xi_ * xi_) * xy2)) / (xy2 + 1);
|
const double sq_xy = (xi_ + sqrt(1 + (1 - xi_ * xi_) * xy2)) / (xy2 + 1);
|
||||||
|
|
@ -122,7 +123,6 @@ Point2 Cal3Unified::nPlaneToSpace(const Point2& p) const {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 Cal3Unified::spaceToNPlane(const Point2& p) const {
|
Point2 Cal3Unified::spaceToNPlane(const Point2& p) const {
|
||||||
|
|
||||||
const double x = p.x(), y = p.y();
|
const double x = p.x(), y = p.y();
|
||||||
const double sq_xy = 1 + xi_ * sqrt(x * x + y * y + 1);
|
const double sq_xy = 1 + xi_ * sqrt(x * x + y * y + 1);
|
||||||
|
|
||||||
|
|
@ -135,11 +135,10 @@ Cal3Unified Cal3Unified::retract(const Vector& d) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Vector10 Cal3Unified::localCoordinates(const Cal3Unified& T2) const {
|
Vector Cal3Unified::localCoordinates(const Cal3Unified& T2) const {
|
||||||
return T2.vector() - vector();
|
return T2.vector() - vector();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
} // \ namespace gtsam
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
* @brief Unified Calibration Model, see Mei07icra for details
|
* @brief Unified Calibration Model, see Mei07icra for details
|
||||||
* @date Mar 8, 2014
|
* @date Mar 8, 2014
|
||||||
* @author Jing Dong
|
* @author Jing Dong
|
||||||
|
* @author Varun Agrawal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -27,40 +28,39 @@
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calibration of a omni-directional camera with mirror + lens radial distortion
|
* @brief Calibration of a omni-directional camera with mirror + lens radial
|
||||||
|
* distortion
|
||||||
* @addtogroup geometry
|
* @addtogroup geometry
|
||||||
* \nosubgrouping
|
* \nosubgrouping
|
||||||
*
|
*
|
||||||
* Similar to Cal3DS2, does distortion but has additional mirror parameter xi
|
* Similar to Cal3DS2, does distortion but has additional mirror parameter xi
|
||||||
* K = [ fx s u0 ; 0 fy v0 ; 0 0 1 ]
|
* K = [ fx s u0 ; 0 fy v0 ; 0 0 1 ]
|
||||||
* Pn = [ P.x / (1 + xi * \sqrt{P.x^2 + P.y^2 + 1}), P.y / (1 + xi * \sqrt{P.x^2 + P.y^2 + 1})]
|
* Pn = [ P.x / (1 + xi * \sqrt{P.x² + P.y² + 1}), P.y / (1 + xi * \sqrt{P.x² +
|
||||||
* rr = Pn.x^2 + Pn.y^2
|
* P.y² + 1})]
|
||||||
* \hat{pn} = (1 + k1*rr + k2*rr^2 ) pn + [ 2*k3 pn.x pn.y + k4 (rr + 2 Pn.x^2) ;
|
* r² = Pn.x² + Pn.y²
|
||||||
* k3 (rr + 2 Pn.y^2) + 2*k4 pn.x pn.y ]
|
* \hat{pn} = (1 + k1*r² + k2*r⁴ ) pn + [ 2*k3 pn.x pn.y + k4 (r² + 2 Pn.x²) ;
|
||||||
|
* k3 (rr + 2 Pn.y²) + 2*k4 pn.x pn.y ]
|
||||||
* pi = K*pn
|
* pi = K*pn
|
||||||
*/
|
*/
|
||||||
class GTSAM_EXPORT Cal3Unified : public Cal3DS2_Base {
|
class GTSAM_EXPORT Cal3Unified : public Cal3DS2_Base {
|
||||||
|
using This = Cal3Unified;
|
||||||
typedef Cal3Unified This;
|
using Base = Cal3DS2_Base;
|
||||||
typedef Cal3DS2_Base Base;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
double xi_ = 0.0f; ///< mirror parameter
|
||||||
double xi_; // mirror parameter
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum { dimension = 10 };
|
enum { dimension = 10 };
|
||||||
|
|
||||||
/// @name Standard Constructors
|
/// @name Standard Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// Default Constructor with only unit focal length
|
/// Default Constructor with only unit focal length
|
||||||
Cal3Unified() : Base(), xi_(0) {}
|
Cal3Unified() = default;
|
||||||
|
|
||||||
Cal3Unified(double fx, double fy, double s, double u0, double v0,
|
Cal3Unified(double fx, double fy, double s, double u0, double v0, double k1,
|
||||||
double k1, double k2, double p1 = 0.0, double p2 = 0.0, double xi = 0.0) :
|
double k2, double p1 = 0.0, double p2 = 0.0, double xi = 0.0)
|
||||||
Base(fx, fy, s, u0, v0, k1, k2, p1, p2), xi_(xi) {}
|
: Base(fx, fy, s, u0, v0, k1, k2, p1, p2), xi_(xi) {}
|
||||||
|
|
||||||
virtual ~Cal3Unified() {}
|
virtual ~Cal3Unified() {}
|
||||||
|
|
||||||
|
|
@ -68,12 +68,17 @@ public:
|
||||||
/// @name Advanced Constructors
|
/// @name Advanced Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
Cal3Unified(const Vector &v) ;
|
Cal3Unified(const Vector10& v)
|
||||||
|
: Base(v(0), v(1), v(2), v(3), v(4), v(5), v(6), v(7), v(8)), xi_(v(9)) {}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Testable
|
/// @name Testable
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
/// Output stream operator
|
||||||
|
GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
const Cal3Unified& cal);
|
||||||
|
|
||||||
/// print with optional string
|
/// print with optional string
|
||||||
void print(const std::string& s = "") const override;
|
void print(const std::string& s = "") const override;
|
||||||
|
|
||||||
|
|
@ -87,6 +92,9 @@ public:
|
||||||
/// mirror parameter
|
/// mirror parameter
|
||||||
inline double xi() const { return xi_; }
|
inline double xi() const { return xi_; }
|
||||||
|
|
||||||
|
/// Return all parameters as a vector
|
||||||
|
Vector10 vector() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert intrinsic coordinates xy to image coordinates uv
|
* convert intrinsic coordinates xy to image coordinates uv
|
||||||
* @param p point in intrinsic coordinates
|
* @param p point in intrinsic coordinates
|
||||||
|
|
@ -99,7 +107,8 @@ public:
|
||||||
OptionalJacobian<2, 2> Dp = boost::none) const;
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
/// Conver a pixel coordinate to ideal coordinate
|
/// Conver a pixel coordinate to ideal coordinate
|
||||||
Point2 calibrate(const Point2& p, const double tol=1e-5) const;
|
Point2 calibrate(const Point2& p, OptionalJacobian<2, 10> Dcal = boost::none,
|
||||||
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
/// Convert a 3D point to normalized unit plane
|
/// Convert a 3D point to normalized unit plane
|
||||||
Point2 spaceToNPlane(const Point2& p) const;
|
Point2 spaceToNPlane(const Point2& p) const;
|
||||||
|
|
@ -115,31 +124,25 @@ public:
|
||||||
Cal3Unified retract(const Vector& d) const;
|
Cal3Unified retract(const Vector& d) const;
|
||||||
|
|
||||||
/// Given a different calibration, calculate update to obtain it
|
/// Given a different calibration, calculate update to obtain it
|
||||||
Vector10 localCoordinates(const Cal3Unified& T2) const ;
|
Vector localCoordinates(const Cal3Unified& T2) const;
|
||||||
|
|
||||||
/// Return dimensions of calibration manifold object
|
/// Return dimensions of calibration manifold object
|
||||||
virtual size_t dim() const { return dimension ; }
|
virtual size_t dim() const override { return Dim(); }
|
||||||
|
|
||||||
/// Return dimensions of calibration manifold object
|
/// Return dimensions of calibration manifold object
|
||||||
static size_t Dim() { return dimension; }
|
inline static size_t Dim() { return dimension; }
|
||||||
|
|
||||||
/// Return all parameters as a vector
|
|
||||||
Vector10 vector() const ;
|
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/** Serialization function */
|
/** Serialization function */
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive & ar, const unsigned int /*version*/)
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
||||||
{
|
ar& boost::serialization::make_nvp(
|
||||||
ar & boost::serialization::make_nvp("Cal3Unified",
|
"Cal3Unified", boost::serialization::base_object<Cal3DS2_Base>(*this));
|
||||||
boost::serialization::base_object<Cal3DS2_Base>(*this));
|
|
||||||
ar& BOOST_SERIALIZATION_NVP(xi_);
|
ar& BOOST_SERIALIZATION_NVP(xi_);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
@ -147,6 +150,4 @@ struct traits<Cal3Unified> : public internal::Manifold<Cal3Unified> {};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct traits<const Cal3Unified> : public internal::Manifold<Cal3Unified> {};
|
struct traits<const Cal3Unified> : public internal::Manifold<Cal3Unified> {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,69 +22,30 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Cal3_S2::Cal3_S2(double fov, int w, int h) :
|
std::ostream& operator<<(std::ostream& os, const Cal3_S2& cal) {
|
||||||
s_(0), u0_((double) w / 2.0), v0_((double) h / 2.0) {
|
// Use the base class version since it is identical.
|
||||||
double a = fov * M_PI / 360.0; // fov/2 in radians
|
os << (Cal3&)cal;
|
||||||
fx_ = (double) w / (2.0 * tan(a)); // old formula: fx_ = (double) w * tan(a);
|
|
||||||
fy_ = fx_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
Cal3_S2::Cal3_S2(const std::string &path) :
|
|
||||||
fx_(320), fy_(320), s_(0), u0_(320), v0_(140) {
|
|
||||||
|
|
||||||
char buffer[200];
|
|
||||||
buffer[0] = 0;
|
|
||||||
sprintf(buffer, "%s/calibration_info.txt", path.c_str());
|
|
||||||
std::ifstream infile(buffer, std::ios::in);
|
|
||||||
|
|
||||||
if (infile)
|
|
||||||
infile >> fx_ >> fy_ >> s_ >> u0_ >> v0_;
|
|
||||||
else {
|
|
||||||
throw std::runtime_error("Cal3_S2: Unable to load the calibration");
|
|
||||||
}
|
|
||||||
|
|
||||||
infile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
|
||||||
ostream& operator<<(ostream& os, const Cal3_S2& cal) {
|
|
||||||
os << "{fx: " << cal.fx() << ", fy: " << cal.fy() << ", s:" << cal.skew() << ", px:" << cal.px()
|
|
||||||
<< ", py:" << cal.py() << "}";
|
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
void Cal3_S2::print(const std::string& s) const {
|
void Cal3_S2::print(const std::string& s) const {
|
||||||
gtsam::print((Matrix)matrix(), s);
|
gtsam::print((Matrix)K(), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
bool Cal3_S2::equals(const Cal3_S2& K, double tol) const {
|
bool Cal3_S2::equals(const Cal3_S2& K, double tol) const {
|
||||||
if (std::abs(fx_ - K.fx_) > tol)
|
return Cal3::equals(K, tol);
|
||||||
return false;
|
|
||||||
if (std::abs(fy_ - K.fy_) > tol)
|
|
||||||
return false;
|
|
||||||
if (std::abs(s_ - K.s_) > tol)
|
|
||||||
return false;
|
|
||||||
if (std::abs(u0_ - K.u0_) > tol)
|
|
||||||
return false;
|
|
||||||
if (std::abs(v0_ - K.v0_) > tol)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 Cal3_S2::uncalibrate(const Point2& p, OptionalJacobian<2, 5> Dcal,
|
Point2 Cal3_S2::uncalibrate(const Point2& p, OptionalJacobian<2, 5> Dcal,
|
||||||
OptionalJacobian<2, 2> Dp) const {
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
const double x = p.x(), y = p.y();
|
const double x = p.x(), y = p.y();
|
||||||
if (Dcal)
|
if (Dcal) *Dcal << x, 0.0, y, 1.0, 0.0, 0.0, y, 0.0, 0.0, 1.0;
|
||||||
*Dcal << x, 0.0, y, 1.0, 0.0, 0.0, y, 0.0, 0.0, 1.0;
|
if (Dp) *Dp << fx_, s_, 0.0, fy_;
|
||||||
if (Dp)
|
|
||||||
*Dp << fx_, s_, 0.0, fy_;
|
|
||||||
return Point2(fx_ * x + s_ * y + u0_, fy_ * y + v0_);
|
return Point2(fx_ * x + s_ * y + u0_, fy_ * y + v0_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,22 +55,21 @@ Point2 Cal3_S2::calibrate(const Point2& p, OptionalJacobian<2,5> Dcal,
|
||||||
const double u = p.x(), v = p.y();
|
const double u = p.x(), v = p.y();
|
||||||
double delta_u = u - u0_, delta_v = v - v0_;
|
double delta_u = u - u0_, delta_v = v - v0_;
|
||||||
double inv_fx = 1 / fx_, inv_fy = 1 / fy_;
|
double inv_fx = 1 / fx_, inv_fy = 1 / fy_;
|
||||||
double inv_fy_delta_v = inv_fy * delta_v, inv_fx_s_inv_fy = inv_fx * s_ * inv_fy;
|
double inv_fy_delta_v = inv_fy * delta_v;
|
||||||
Point2 point(inv_fx * (delta_u - s_ * inv_fy_delta_v),
|
double inv_fx_s_inv_fy = inv_fx * s_ * inv_fy;
|
||||||
inv_fy_delta_v);
|
|
||||||
if(Dcal)
|
Point2 point(inv_fx * (delta_u - s_ * inv_fy_delta_v), inv_fy_delta_v);
|
||||||
*Dcal << - inv_fx * point.x(), inv_fx * s_ * inv_fy * inv_fy_delta_v, -inv_fx * point.y(),
|
if (Dcal) {
|
||||||
-inv_fx, inv_fx_s_inv_fy,
|
*Dcal << -inv_fx * point.x(), inv_fx * s_ * inv_fy * inv_fy_delta_v,
|
||||||
0, -inv_fy * point.y(), 0, 0, -inv_fy;
|
-inv_fx * point.y(), -inv_fx, inv_fx_s_inv_fy, 0, -inv_fy * point.y(),
|
||||||
if(Dp)
|
0, 0, -inv_fy;
|
||||||
*Dp << inv_fx, -inv_fx_s_inv_fy, 0, inv_fy;
|
}
|
||||||
|
if (Dp) *Dp << inv_fx, -inv_fx_s_inv_fy, 0, inv_fy;
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Vector3 Cal3_S2::calibrate(const Vector3& p) const {
|
Vector3 Cal3_S2::calibrate(const Vector3& p) const { return inverse() * p; }
|
||||||
return matrix_inverse() * p;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/geometry/Cal3.h>
|
||||||
#include <gtsam/geometry/Point2.h>
|
#include <gtsam/geometry/Point2.h>
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
@ -30,31 +31,25 @@ namespace gtsam {
|
||||||
* @addtogroup geometry
|
* @addtogroup geometry
|
||||||
* \nosubgrouping
|
* \nosubgrouping
|
||||||
*/
|
*/
|
||||||
class GTSAM_EXPORT Cal3_S2 {
|
class GTSAM_EXPORT Cal3_S2 : public Cal3 {
|
||||||
private:
|
|
||||||
double fx_, fy_, s_, u0_, v0_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum { dimension = 5 };
|
enum { dimension = 5 };
|
||||||
typedef boost::shared_ptr<Cal3_S2> shared_ptr; ///< shared pointer to calibration object
|
|
||||||
|
///< shared pointer to calibration object
|
||||||
|
using shared_ptr = boost::shared_ptr<Cal3_S2>;
|
||||||
|
|
||||||
/// @name Standard Constructors
|
/// @name Standard Constructors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// Create a default calibration that leaves coordinates unchanged
|
/// Create a default calibration that leaves coordinates unchanged
|
||||||
Cal3_S2() :
|
Cal3_S2() = default;
|
||||||
fx_(1), fy_(1), s_(0), u0_(0), v0_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// constructor from doubles
|
/// constructor from doubles
|
||||||
Cal3_S2(double fx, double fy, double s, double u0, double v0) :
|
Cal3_S2(double fx, double fy, double s, double u0, double v0)
|
||||||
fx_(fx), fy_(fy), s_(s), u0_(u0), v0_(v0) {
|
: Cal3(fx, fy, s, u0, v0) {}
|
||||||
}
|
|
||||||
|
|
||||||
/// constructor from vector
|
/// constructor from vector
|
||||||
Cal3_S2(const Vector &d) :
|
Cal3_S2(const Vector5& d) : Cal3(d) {}
|
||||||
fx_(d(0)), fy_(d(1)), s_(d(2)), u0_(d(3)), v0_(d(4)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Easy constructor, takes fov in degrees, asssumes zero skew, unit aspect
|
* Easy constructor, takes fov in degrees, asssumes zero skew, unit aspect
|
||||||
|
|
@ -62,99 +57,12 @@ public:
|
||||||
* @param w image width
|
* @param w image width
|
||||||
* @param h image height
|
* @param h image height
|
||||||
*/
|
*/
|
||||||
Cal3_S2(double fov, int w, int h);
|
Cal3_S2(double fov, int w, int h) : Cal3(fov, w, h) {}
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Advanced Constructors
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// load calibration from location (default name is calibration_info.txt)
|
|
||||||
Cal3_S2(const std::string &path);
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Testable
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// Output stream operator
|
|
||||||
GTSAM_EXPORT friend std::ostream &operator<<(std::ostream &os, const Cal3_S2& cal);
|
|
||||||
|
|
||||||
/// print with optional string
|
|
||||||
void print(const std::string& s = "Cal3_S2") const;
|
|
||||||
|
|
||||||
/// Check if equal up to specified tolerance
|
|
||||||
bool equals(const Cal3_S2& K, double tol = 10e-9) const;
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
/// @name Standard Interface
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// focal length x
|
|
||||||
inline double fx() const {
|
|
||||||
return fx_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// focal length y
|
|
||||||
inline double fy() const {
|
|
||||||
return fy_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// aspect ratio
|
|
||||||
inline double aspectRatio() const {
|
|
||||||
return fx_/fy_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// skew
|
|
||||||
inline double skew() const {
|
|
||||||
return s_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// image center in x
|
|
||||||
inline double px() const {
|
|
||||||
return u0_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// image center in y
|
|
||||||
inline double py() const {
|
|
||||||
return v0_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return the principal point
|
|
||||||
Point2 principalPoint() const {
|
|
||||||
return Point2(u0_, v0_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// vectorized form (column-wise)
|
|
||||||
Vector5 vector() const {
|
|
||||||
Vector5 v;
|
|
||||||
v << fx_, fy_, s_, u0_, v0_;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return calibration matrix K
|
|
||||||
Matrix3 K() const {
|
|
||||||
Matrix3 K;
|
|
||||||
K << fx_, s_, u0_, 0.0, fy_, v0_, 0.0, 0.0, 1.0;
|
|
||||||
return K;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated The following function has been deprecated, use K above */
|
|
||||||
Matrix3 matrix() const {
|
|
||||||
return K();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return inverted calibration matrix inv(K)
|
|
||||||
Matrix3 matrix_inverse() const {
|
|
||||||
const double fxy = fx_ * fy_, sv0 = s_ * v0_, fyu0 = fy_ * u0_;
|
|
||||||
Matrix3 K_inverse;
|
|
||||||
K_inverse << 1.0 / fx_, -s_ / fxy, (sv0 - fyu0) / fxy, 0.0,
|
|
||||||
1.0 / fy_, -v0_ / fy_, 0.0, 0.0, 1.0;
|
|
||||||
return K_inverse;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert intrinsic coordinates xy to image coordinates uv, fixed derivaitves
|
* Convert intrinsic coordinates xy to image coordinates uv, fixed derivaitves
|
||||||
* @param p point in intrinsic coordinates
|
* @param p point in intrinsic coordinates
|
||||||
* @param Dcal optional 2*5 Jacobian wrpt Cal3_S2 parameters
|
* @param Dcal optional 2*5 Jacobian wrpt Cal3 parameters
|
||||||
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
||||||
* @return point in image coordinates
|
* @return point in image coordinates
|
||||||
*/
|
*/
|
||||||
|
|
@ -162,9 +70,9 @@ public:
|
||||||
OptionalJacobian<2, 2> Dp = boost::none) const;
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert image coordinates uv to intrinsic coordinates xy
|
* Convert image coordinates uv to intrinsic coordinates xy
|
||||||
* @param p point in image coordinates
|
* @param p point in image coordinates
|
||||||
* @param Dcal optional 2*5 Jacobian wrpt Cal3_S2 parameters
|
* @param Dcal optional 2*5 Jacobian wrpt Cal3 parameters
|
||||||
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
||||||
* @return point in intrinsic coordinates
|
* @return point in intrinsic coordinates
|
||||||
*/
|
*/
|
||||||
|
|
@ -172,31 +80,42 @@ public:
|
||||||
OptionalJacobian<2, 2> Dp = boost::none) const;
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert homogeneous image coordinates to intrinsic coordinates
|
* Convert homogeneous image coordinates to intrinsic coordinates
|
||||||
* @param p point in image coordinates
|
* @param p point in image coordinates
|
||||||
* @return point in intrinsic coordinates
|
* @return point in intrinsic coordinates
|
||||||
*/
|
*/
|
||||||
Vector3 calibrate(const Vector3& p) const;
|
Vector3 calibrate(const Vector3& p) const;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
/// @name Testable
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Output stream operator
|
||||||
|
GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
const Cal3_S2& cal);
|
||||||
|
|
||||||
|
/// print with optional string
|
||||||
|
void print(const std::string& s = "Cal3_S2") const override;
|
||||||
|
|
||||||
|
/// Check if equal up to specified tolerance
|
||||||
|
bool equals(const Cal3_S2& K, double tol = 10e-9) const;
|
||||||
|
|
||||||
/// "Between", subtracts calibrations. between(p,q) == compose(inverse(p),q)
|
/// "Between", subtracts calibrations. between(p,q) == compose(inverse(p),q)
|
||||||
inline Cal3_S2 between(const Cal3_S2& q,
|
inline Cal3_S2 between(const Cal3_S2& q,
|
||||||
OptionalJacobian<5, 5> H1 = boost::none,
|
OptionalJacobian<5, 5> H1 = boost::none,
|
||||||
OptionalJacobian<5, 5> H2 = boost::none) const {
|
OptionalJacobian<5, 5> H2 = boost::none) const {
|
||||||
if (H1) *H1 = -I_5x5;
|
if (H1) *H1 = -I_5x5;
|
||||||
if (H2) *H2 = I_5x5;
|
if (H2) *H2 = I_5x5;
|
||||||
return Cal3_S2(q.fx_-fx_, q.fy_-fy_, q.s_-s_, q.u0_-u0_, q.v0_-v0_);
|
return Cal3_S2(q.fx_ - fx_, q.fy_ - fy_, q.s_ - s_, q.u0_ - u0_,
|
||||||
|
q.v0_ - v0_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Manifold
|
/// @name Manifold
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// return DOF, dimensionality of tangent space
|
/// return DOF, dimensionality of tangent space
|
||||||
inline size_t dim() const { return dimension; }
|
inline static size_t Dim() { return dimension; }
|
||||||
|
|
||||||
/// return DOF, dimensionality of tangent space
|
|
||||||
static size_t Dim() { return dimension; }
|
|
||||||
|
|
||||||
/// Given 5-dim tangent vector, create new calibration
|
/// Given 5-dim tangent vector, create new calibration
|
||||||
inline Cal3_S2 retract(const Vector& d) const {
|
inline Cal3_S2 retract(const Vector& d) const {
|
||||||
|
|
@ -213,20 +132,15 @@ public:
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// Serialization function
|
/// Serialization function
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, const unsigned int /*version*/) {
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
||||||
ar & BOOST_SERIALIZATION_NVP(fx_);
|
ar& boost::serialization::make_nvp(
|
||||||
ar & BOOST_SERIALIZATION_NVP(fy_);
|
"Cal3_S2", boost::serialization::base_object<Cal3>(*this));
|
||||||
ar & BOOST_SERIALIZATION_NVP(s_);
|
|
||||||
ar & BOOST_SERIALIZATION_NVP(u0_);
|
|
||||||
ar & BOOST_SERIALIZATION_NVP(v0_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
|
||||||
|
|
@ -20,18 +20,54 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
using namespace std;
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
std::ostream& operator<<(std::ostream& os, const Cal3_S2Stereo& cal) {
|
||||||
|
os << (Cal3_S2&)cal;
|
||||||
|
os << ", b: " << cal.baseline();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
void Cal3_S2Stereo::print(const std::string& s) const {
|
void Cal3_S2Stereo::print(const std::string& s) const {
|
||||||
K_.print(s+"K: ");
|
std::cout << s << (s != "" ? " " : "");
|
||||||
std::cout << s << "Baseline: " << b_ << std::endl;
|
std::cout << "K: " << (Matrix)K() << std::endl;
|
||||||
|
std::cout << "Baseline: " << b_ << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
bool Cal3_S2Stereo::equals(const Cal3_S2Stereo& other, double tol) const {
|
bool Cal3_S2Stereo::equals(const Cal3_S2Stereo& other, double tol) const {
|
||||||
if (std::abs(b_ - other.b_) > tol) return false;
|
const Cal3_S2* base = dynamic_cast<const Cal3_S2*>(&other);
|
||||||
return K_.equals(other.K_,tol);
|
return (Cal3_S2::equals(*base, tol) &&
|
||||||
|
std::fabs(b_ - other.baseline()) < tol);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
Point2 Cal3_S2Stereo::uncalibrate(const Point2& p, OptionalJacobian<2, 6> Dcal,
|
||||||
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
|
const double x = p.x(), y = p.y();
|
||||||
|
if (Dcal) *Dcal << x, 0.0, y, 1.0, 0.0, 0.0, 0.0, y, 0.0, 0.0, 1.0, 0.0;
|
||||||
|
if (Dp) *Dp << fx_, s_, 0.0, fy_;
|
||||||
|
return Point2(fx_ * x + s_ * y + u0_, fy_ * y + v0_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
Point2 Cal3_S2Stereo::calibrate(const Point2& p, OptionalJacobian<2, 6> Dcal,
|
||||||
|
OptionalJacobian<2, 2> Dp) const {
|
||||||
|
const double u = p.x(), v = p.y();
|
||||||
|
double delta_u = u - u0_, delta_v = v - v0_;
|
||||||
|
double inv_fx = 1 / fx_, inv_fy = 1 / fy_;
|
||||||
|
double inv_fy_delta_v = inv_fy * delta_v;
|
||||||
|
double inv_fx_s_inv_fy = inv_fx * s_ * inv_fy;
|
||||||
|
|
||||||
|
Point2 point(inv_fx * (delta_u - s_ * inv_fy_delta_v), inv_fy_delta_v);
|
||||||
|
if (Dcal) {
|
||||||
|
*Dcal << -inv_fx * point.x(), inv_fx * s_ * inv_fy * inv_fy_delta_v,
|
||||||
|
-inv_fx * point.y(), -inv_fx, inv_fx_s_inv_fy, 0, 0,
|
||||||
|
-inv_fy * point.y(), 0, 0, -inv_fy, 0;
|
||||||
|
}
|
||||||
|
if (Dp) *Dp << inv_fx, -inv_fx_s_inv_fy, 0, inv_fy;
|
||||||
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -27,43 +27,71 @@ namespace gtsam {
|
||||||
* @addtogroup geometry
|
* @addtogroup geometry
|
||||||
* \nosubgrouping
|
* \nosubgrouping
|
||||||
*/
|
*/
|
||||||
class GTSAM_EXPORT Cal3_S2Stereo {
|
class GTSAM_EXPORT Cal3_S2Stereo : public Cal3_S2 {
|
||||||
private:
|
private:
|
||||||
|
double b_ = 1.0f; ///< Stereo baseline.
|
||||||
Cal3_S2 K_;
|
|
||||||
double b_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum { dimension = 6 };
|
enum { dimension = 6 };
|
||||||
typedef boost::shared_ptr<Cal3_S2Stereo> shared_ptr; ///< shared pointer to stereo calibration object
|
|
||||||
|
///< shared pointer to stereo calibration object
|
||||||
|
using shared_ptr = boost::shared_ptr<Cal3_S2Stereo>;
|
||||||
|
|
||||||
/// @name Standard Constructors
|
/// @name Standard Constructors
|
||||||
/// @
|
/// @
|
||||||
|
|
||||||
/// default calibration leaves coordinates unchanged
|
/// default calibration leaves coordinates unchanged
|
||||||
Cal3_S2Stereo() :
|
Cal3_S2Stereo() = default;
|
||||||
K_(1, 1, 0, 0, 0), b_(1.0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// constructor from doubles
|
/// constructor from doubles
|
||||||
Cal3_S2Stereo(double fx, double fy, double s, double u0, double v0, double b) :
|
Cal3_S2Stereo(double fx, double fy, double s, double u0, double v0, double b)
|
||||||
K_(fx, fy, s, u0, v0), b_(b) {
|
: Cal3_S2(fx, fy, s, u0, v0), b_(b) {}
|
||||||
}
|
|
||||||
|
|
||||||
/// constructor from vector
|
/// constructor from vector
|
||||||
Cal3_S2Stereo(const Vector &d): K_(d(0), d(1), d(2), d(3), d(4)), b_(d(5)){}
|
Cal3_S2Stereo(const Vector6& d)
|
||||||
|
: Cal3_S2(d(0), d(1), d(2), d(3), d(4)), b_(d(5)) {}
|
||||||
|
|
||||||
/// easy constructor; field-of-view in degrees, assumes zero skew
|
/// easy constructor; field-of-view in degrees, assumes zero skew
|
||||||
Cal3_S2Stereo(double fov, int w, int h, double b) :
|
Cal3_S2Stereo(double fov, int w, int h, double b)
|
||||||
K_(fov, w, h), b_(b) {
|
: Cal3_S2(fov, w, h), b_(b) {}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Convert intrinsic coordinates xy to image coordinates uv, fixed derivaitves
|
||||||
|
* @param p point in intrinsic coordinates
|
||||||
|
* @param Dcal optional 2*6 Jacobian wrpt Cal3_S2Stereo parameters
|
||||||
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
||||||
|
* @return point in image coordinates
|
||||||
|
*/
|
||||||
|
Point2 uncalibrate(const Point2& p, OptionalJacobian<2, 6> Dcal = boost::none,
|
||||||
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert image coordinates uv to intrinsic coordinates xy
|
||||||
|
* @param p point in image coordinates
|
||||||
|
* @param Dcal optional 2*6 Jacobian wrpt Cal3_S2Stereo parameters
|
||||||
|
* @param Dp optional 2*2 Jacobian wrpt intrinsic coordinates
|
||||||
|
* @return point in intrinsic coordinates
|
||||||
|
*/
|
||||||
|
Point2 calibrate(const Point2& p, OptionalJacobian<2, 6> Dcal = boost::none,
|
||||||
|
OptionalJacobian<2, 2> Dp = boost::none) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert homogeneous image coordinates to intrinsic coordinates
|
||||||
|
* @param p point in image coordinates
|
||||||
|
* @return point in intrinsic coordinates
|
||||||
|
*/
|
||||||
|
Vector3 calibrate(const Vector3& p) const { return Cal3_S2::calibrate(p); }
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Testable
|
/// @name Testable
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
void print(const std::string& s = "") const;
|
/// Output stream operator
|
||||||
|
GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os,
|
||||||
|
const Cal3_S2Stereo& cal);
|
||||||
|
|
||||||
|
/// print with optional string
|
||||||
|
void print(const std::string& s = "") const override;
|
||||||
|
|
||||||
/// Check if equal up to specified tolerance
|
/// Check if equal up to specified tolerance
|
||||||
bool equals(const Cal3_S2Stereo& other, double tol = 10e-9) const;
|
bool equals(const Cal3_S2Stereo& other, double tol = 10e-9) const;
|
||||||
|
|
@ -73,28 +101,10 @@ namespace gtsam {
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// return calibration, same for left and right
|
/// return calibration, same for left and right
|
||||||
const Cal3_S2& calibration() const { return K_;}
|
const Cal3_S2& calibration() const { return *this; }
|
||||||
|
|
||||||
/// return calibration matrix K, same for left and right
|
/// return calibration matrix K, same for left and right
|
||||||
Matrix matrix() const { return K_.matrix();}
|
Matrix3 K() const override { return Cal3_S2::K(); }
|
||||||
|
|
||||||
/// focal length x
|
|
||||||
inline double fx() const { return K_.fx();}
|
|
||||||
|
|
||||||
/// focal length x
|
|
||||||
inline double fy() const { return K_.fy();}
|
|
||||||
|
|
||||||
/// skew
|
|
||||||
inline double skew() const { return K_.skew();}
|
|
||||||
|
|
||||||
/// image center in x
|
|
||||||
inline double px() const { return K_.px();}
|
|
||||||
|
|
||||||
/// image center in y
|
|
||||||
inline double py() const { return K_.py();}
|
|
||||||
|
|
||||||
/// return the principal point
|
|
||||||
Point2 principalPoint() const { return K_.principalPoint();}
|
|
||||||
|
|
||||||
/// return baseline
|
/// return baseline
|
||||||
inline double baseline() const { return b_; }
|
inline double baseline() const { return b_; }
|
||||||
|
|
@ -102,7 +112,7 @@ namespace gtsam {
|
||||||
/// vectorized form (column-wise)
|
/// vectorized form (column-wise)
|
||||||
Vector6 vector() const {
|
Vector6 vector() const {
|
||||||
Vector6 v;
|
Vector6 v;
|
||||||
v << K_.vector(), b_;
|
v << Cal3_S2::vector(), b_;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,14 +121,15 @@ namespace gtsam {
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
/// return DOF, dimensionality of tangent space
|
/// return DOF, dimensionality of tangent space
|
||||||
inline size_t dim() const { return dimension; }
|
inline size_t dim() const override { return Dim(); }
|
||||||
|
|
||||||
/// return DOF, dimensionality of tangent space
|
/// return DOF, dimensionality of tangent space
|
||||||
static size_t Dim() { return dimension; }
|
inline static size_t Dim() { return dimension; }
|
||||||
|
|
||||||
/// Given 6-dim tangent vector, create new calibration
|
/// Given 6-dim tangent vector, create new calibration
|
||||||
inline Cal3_S2Stereo retract(const Vector& d) const {
|
inline Cal3_S2Stereo retract(const Vector& d) const {
|
||||||
return Cal3_S2Stereo(K_.fx() + d(0), K_.fy() + d(1), K_.skew() + d(2), K_.px() + d(3), K_.py() + d(4), b_ + d(5));
|
return Cal3_S2Stereo(fx() + d(0), fy() + d(1), skew() + d(2), px() + d(3),
|
||||||
|
py() + d(4), b_ + d(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unretraction for the calibration
|
/// Unretraction for the calibration
|
||||||
|
|
@ -126,7 +137,6 @@ namespace gtsam {
|
||||||
return T2.vector() - vector();
|
return T2.vector() - vector();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Advanced Interface
|
/// @name Advanced Interface
|
||||||
/// @{
|
/// @{
|
||||||
|
|
@ -135,19 +145,17 @@ namespace gtsam {
|
||||||
/** Serialization function */
|
/** Serialization function */
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive & ar, const unsigned int /*version*/)
|
void serialize(Archive& ar, const unsigned int /*version*/) {
|
||||||
{
|
ar& boost::serialization::make_nvp(
|
||||||
ar & BOOST_SERIALIZATION_NVP(K_);
|
"Cal3_S2", boost::serialization::base_object<Cal3_S2>(*this));
|
||||||
ar& BOOST_SERIALIZATION_NVP(b_);
|
ar& BOOST_SERIALIZATION_NVP(b_);
|
||||||
}
|
}
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define GTSAM traits
|
// Define GTSAM traits
|
||||||
template <>
|
template <>
|
||||||
struct traits<Cal3_S2Stereo> : public internal::Manifold<Cal3_S2Stereo> {
|
struct traits<Cal3_S2Stereo> : public internal::Manifold<Cal3_S2Stereo> {};
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct traits<const Cal3_S2Stereo> : public internal::Manifold<Cal3_S2Stereo> {
|
struct traits<const Cal3_S2Stereo> : public internal::Manifold<Cal3_S2Stereo> {
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,27 @@ class Line3 {
|
||||||
*/
|
*/
|
||||||
Point3 point(double distance = 0) const;
|
Point3 point(double distance = 0) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the rotation of the line.
|
||||||
|
*/
|
||||||
|
inline Rot3 R() const {
|
||||||
|
return R_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the x-coordinate of the intersection of the line with the xy plane.
|
||||||
|
*/
|
||||||
|
inline double a() const {
|
||||||
|
return a_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the y-coordinate of the intersection of the line with the xy plane.
|
||||||
|
*/
|
||||||
|
inline double b() const {
|
||||||
|
return b_;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform a line from world to camera frame
|
* Transform a line from world to camera frame
|
||||||
* @param wTc - Pose3 of camera in world frame
|
* @param wTc - Pose3 of camera in world frame
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ GTSAM_EXPORT double dot(const Point3& p, const Point3& q,
|
||||||
|
|
||||||
/// mean
|
/// mean
|
||||||
template <class CONTAINER>
|
template <class CONTAINER>
|
||||||
GTSAM_EXPORT Point3 mean(const CONTAINER& points) {
|
Point3 mean(const CONTAINER& points) {
|
||||||
if (points.size() == 0) throw std::invalid_argument("Point3::mean input container is empty");
|
if (points.size() == 0) throw std::invalid_argument("Point3::mean input container is empty");
|
||||||
Point3 sum(0, 0, 0);
|
Point3 sum(0, 0, 0);
|
||||||
sum = std::accumulate(points.begin(), points.end(), sum);
|
sum = std::accumulate(points.begin(), points.end(), sum);
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,13 @@ Matrix3 Pose2::matrix() const {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
void Pose2::print(const string& s) const {
|
void Pose2::print(const string& s) const {
|
||||||
cout << s << "(" << t_.x() << ", " << t_.y() << ", " << r_.theta() << ")" << endl;
|
cout << s << this << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
std::ostream &operator<<(std::ostream &os, const Pose2& pose) {
|
||||||
|
os << "(" << pose.x() << ", " << pose.y() << ", " << pose.theta() << ")";
|
||||||
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -287,6 +287,10 @@ public:
|
||||||
*/
|
*/
|
||||||
static std::pair<size_t, size_t> rotationInterval() { return std::make_pair(2, 2); }
|
static std::pair<size_t, size_t> rotationInterval() { return std::make_pair(2, 2); }
|
||||||
|
|
||||||
|
/// Output stream operator
|
||||||
|
GTSAM_EXPORT
|
||||||
|
friend std::ostream &operator<<(std::ostream &os, const Pose2& p);
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,8 @@ Vector6 Pose3::adjointTranspose(const Vector6& xi, const Vector6& y,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
void Pose3::print(const string& s) const {
|
void Pose3::print(const std::string& s) const {
|
||||||
cout << (s.empty() ? s : s + " ") << *this << endl;
|
std::cout << (s.empty() ? s : s + " ") << *this << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,25 @@ public:
|
||||||
return Pose3(R_ * T.R_, t_ + R_ * T.t_);
|
return Pose3(R_ * T.R_, t_ + R_ * T.t_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interpolate between two poses via individual rotation and translation
|
||||||
|
* interpolation.
|
||||||
|
*
|
||||||
|
* The default "interpolate" method defined in Lie.h minimizes the geodesic
|
||||||
|
* distance on the manifold, leading to a screw motion interpolation in
|
||||||
|
* Cartesian space, which might not be what is expected.
|
||||||
|
* In contrast, this method executes a straight line interpolation for the
|
||||||
|
* translation, while still using interpolate (aka "slerp") for the rotational
|
||||||
|
* component. This might be more intuitive in many applications.
|
||||||
|
*
|
||||||
|
* @param T End point of interpolation.
|
||||||
|
* @param t A value in [0, 1].
|
||||||
|
*/
|
||||||
|
Pose3 interpolateRt(const Pose3& T, double t) const {
|
||||||
|
return Pose3(interpolate<Rot3>(R_, T.R_, t),
|
||||||
|
interpolate<Point3>(t_, T.t_, t));
|
||||||
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
/// @name Lie Group
|
/// @name Lie Group
|
||||||
/// @{
|
/// @{
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
// Implementation for N>=5 just uses dynamic version
|
// Implementation for N>=5 just uses dynamic version
|
||||||
|
|
@ -108,7 +106,7 @@ typename SO<N>::VectorN2 SO<N>::vec(
|
||||||
|
|
||||||
template <int N>
|
template <int N>
|
||||||
void SO<N>::print(const std::string& s) const {
|
void SO<N>::print(const std::string& s) const {
|
||||||
cout << s << matrix_ << endl;
|
std::cout << s << matrix_ << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace gtsam
|
} // namespace gtsam
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
|
#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V41
|
||||||
SimpleCamera simpleCamera(const Matrix34& P) {
|
SimpleCamera simpleCamera(const Matrix34& P) {
|
||||||
|
|
||||||
// P = [A|a] = s K cRw [I|-T], with s the unknown scale
|
// P = [A|a] = s K cRw [I|-T], with s the unknown scale
|
||||||
|
|
@ -45,5 +46,6 @@ namespace gtsam {
|
||||||
return SimpleCamera(Pose3(wRc, T),
|
return SimpleCamera(Pose3(wRc, T),
|
||||||
Cal3_S2(K(0, 0), K(1, 1), K(0, 1), K(0, 2), K(1, 2)));
|
Cal3_S2(K(0, 0), K(1, 1), K(0, 1), K(0, 2), K(1, 2)));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,22 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtsam/geometry/BearingRange.h>
|
#include <gtsam/geometry/BearingRange.h>
|
||||||
#include <gtsam/geometry/PinholeCamera.h>
|
#include <gtsam/geometry/Cal3Bundler.h>
|
||||||
|
#include <gtsam/geometry/Cal3DS2.h>
|
||||||
|
#include <gtsam/geometry/Cal3Unified.h>
|
||||||
#include <gtsam/geometry/Cal3_S2.h>
|
#include <gtsam/geometry/Cal3_S2.h>
|
||||||
|
#include <gtsam/geometry/PinholeCamera.h>
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
/// A simple camera class with a Cal3_S2 calibration
|
/// Convenient aliases for Pinhole camera classes with different calibrations.
|
||||||
typedef gtsam::PinholeCamera<gtsam::Cal3_S2> PinholeCameraCal3_S2;
|
/// Also needed as forward declarations in the wrapper.
|
||||||
|
using PinholeCameraCal3_S2 = gtsam::PinholeCamera<gtsam::Cal3_S2>;
|
||||||
|
using PinholeCameraCal3Bundler = gtsam::PinholeCamera<gtsam::Cal3Bundler>;
|
||||||
|
using PinholeCameraCal3DS2 = gtsam::PinholeCamera<gtsam::Cal3DS2>;
|
||||||
|
using PinholeCameraCal3Unified = gtsam::PinholeCamera<gtsam::Cal3Unified>;
|
||||||
|
|
||||||
|
#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V41
|
||||||
/**
|
/**
|
||||||
* @deprecated: SimpleCamera for backwards compatability with GTSAM 3.x
|
* @deprecated: SimpleCamera for backwards compatability with GTSAM 3.x
|
||||||
* Use PinholeCameraCal3_S2 instead
|
* Use PinholeCameraCal3_S2 instead
|
||||||
|
|
@ -140,4 +148,6 @@ struct traits<const SimpleCamera> : public internal::Manifold<SimpleCamera> {};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct Range<SimpleCamera, T> : HasRange<SimpleCamera, T, double> {};
|
struct Range<SimpleCamera, T> : HasRange<SimpleCamera, T, double> {};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace gtsam
|
} // namespace gtsam
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,12 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file testCal3Bundler.cpp
|
* @file testCal3Bundler.cpp
|
||||||
* @brief Unit tests for transform derivatives
|
* @brief Unit tests for Bundler calibration model.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <CppUnitLite/TestHarness.h>
|
#include <CppUnitLite/TestHarness.h>
|
||||||
#include <gtsam/base/Testable.h>
|
#include <gtsam/base/Testable.h>
|
||||||
|
#include <gtsam/base/TestableAssertions.h>
|
||||||
#include <gtsam/base/numericalDerivative.h>
|
#include <gtsam/base/numericalDerivative.h>
|
||||||
#include <gtsam/geometry/Cal3Bundler.h>
|
#include <gtsam/geometry/Cal3Bundler.h>
|
||||||
|
|
||||||
|
|
@ -28,8 +29,7 @@ static Cal3Bundler K(500, 1e-3, 1e-3, 1000, 2000);
|
||||||
static Point2 p(2, 3);
|
static Point2 p(2, 3);
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Bundler, vector)
|
TEST(Cal3Bundler, vector) {
|
||||||
{
|
|
||||||
Cal3Bundler K;
|
Cal3Bundler K;
|
||||||
Vector expected(3);
|
Vector expected(3);
|
||||||
expected << 1, 0, 0;
|
expected << 1, 0, 0;
|
||||||
|
|
@ -37,8 +37,7 @@ TEST( Cal3Bundler, vector)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Bundler, uncalibrate)
|
TEST(Cal3Bundler, uncalibrate) {
|
||||||
{
|
|
||||||
Vector v = K.vector();
|
Vector v = K.vector();
|
||||||
double r = p.x() * p.x() + p.y() * p.y();
|
double r = p.x() * p.x() + p.y() * p.y();
|
||||||
double g = v[0] * (1 + v[1] * r + v[2] * r * r);
|
double g = v[0] * (1 + v[1] * r + v[2] * r * r);
|
||||||
|
|
@ -47,8 +46,7 @@ TEST( Cal3Bundler, uncalibrate)
|
||||||
CHECK(assert_equal(expected, actual));
|
CHECK(assert_equal(expected, actual));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST( Cal3Bundler, calibrate )
|
TEST(Cal3Bundler, calibrate) {
|
||||||
{
|
|
||||||
Point2 pn(0.5, 0.5);
|
Point2 pn(0.5, 0.5);
|
||||||
Point2 pi = K.uncalibrate(pn);
|
Point2 pi = K.uncalibrate(pn);
|
||||||
Point2 pn_hat = K.calibrate(pi);
|
Point2 pn_hat = K.calibrate(pi);
|
||||||
|
|
@ -56,13 +54,16 @@ TEST( Cal3Bundler, calibrate )
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 uncalibrate_(const Cal3Bundler& k, const Point2& pt) { return k.uncalibrate(pt); }
|
Point2 uncalibrate_(const Cal3Bundler& k, const Point2& pt) {
|
||||||
|
return k.uncalibrate(pt);
|
||||||
|
}
|
||||||
|
|
||||||
Point2 calibrate_(const Cal3Bundler& k, const Point2& pt) { return k.calibrate(pt); }
|
Point2 calibrate_(const Cal3Bundler& k, const Point2& pt) {
|
||||||
|
return k.calibrate(pt);
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Bundler, Duncalibrate)
|
TEST(Cal3Bundler, Duncalibrate) {
|
||||||
{
|
|
||||||
Matrix Dcal, Dp;
|
Matrix Dcal, Dp;
|
||||||
Point2 actual = K.uncalibrate(p, Dcal, Dp);
|
Point2 actual = K.uncalibrate(p, Dcal, Dp);
|
||||||
Point2 expected(2182, 3773);
|
Point2 expected(2182, 3773);
|
||||||
|
|
@ -74,8 +75,7 @@ TEST( Cal3Bundler, Duncalibrate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Bundler, Dcalibrate)
|
TEST(Cal3Bundler, Dcalibrate) {
|
||||||
{
|
|
||||||
Matrix Dcal, Dp;
|
Matrix Dcal, Dp;
|
||||||
Point2 pn(0.5, 0.5);
|
Point2 pn(0.5, 0.5);
|
||||||
Point2 pi = K.uncalibrate(pn);
|
Point2 pi = K.uncalibrate(pn);
|
||||||
|
|
@ -88,16 +88,17 @@ TEST( Cal3Bundler, Dcalibrate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Bundler, assert_equal)
|
TEST(Cal3Bundler, assert_equal) { CHECK(assert_equal(K, K, 1e-7)); }
|
||||||
{
|
|
||||||
CHECK(assert_equal(K,K,1e-7));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Bundler, retract)
|
TEST(Cal3Bundler, retract) {
|
||||||
{
|
|
||||||
Cal3Bundler expected(510, 2e-3, 2e-3, 1000, 2000);
|
Cal3Bundler expected(510, 2e-3, 2e-3, 1000, 2000);
|
||||||
Vector d(3);
|
EXPECT_LONGS_EQUAL(3, expected.dim());
|
||||||
|
|
||||||
|
EXPECT_LONGS_EQUAL(Cal3Bundler::Dim(), 3);
|
||||||
|
EXPECT_LONGS_EQUAL(expected.dim(), 3);
|
||||||
|
|
||||||
|
Vector3 d;
|
||||||
d << 10, 1e-3, 1e-3;
|
d << 10, 1e-3, 1e-3;
|
||||||
Cal3Bundler actual = K.retract(d);
|
Cal3Bundler actual = K.retract(d);
|
||||||
CHECK(assert_equal(expected, actual, 1e-7));
|
CHECK(assert_equal(expected, actual, 1e-7));
|
||||||
|
|
@ -105,5 +106,18 @@ TEST( Cal3Bundler, retract)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
|
TEST(Cal3_S2, Print) {
|
||||||
|
Cal3Bundler cal(1, 2, 3, 4, 5);
|
||||||
|
std::stringstream os;
|
||||||
|
os << "f: " << cal.fx() << ", k1: " << cal.k1() << ", k2: " << cal.k2()
|
||||||
|
<< ", px: " << cal.px() << ", py: " << cal.py();
|
||||||
|
|
||||||
|
EXPECT(assert_stdout_equal(os.str(), cal));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
int main() {
|
||||||
|
TestResult tr;
|
||||||
|
return TestRegistry::runAllTests(tr);
|
||||||
|
}
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,13 @@
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file testCal3DFisheye.cpp
|
* @file testCal3Fisheye.cpp
|
||||||
* @brief Unit tests for fisheye calibration class
|
* @brief Unit tests for fisheye calibration class
|
||||||
* @author ghaggin
|
* @author ghaggin
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <gtsam/base/Testable.h>
|
#include <gtsam/base/Testable.h>
|
||||||
|
#include <gtsam/base/TestableAssertions.h>
|
||||||
#include <gtsam/base/numericalDerivative.h>
|
#include <gtsam/base/numericalDerivative.h>
|
||||||
#include <gtsam/geometry/Cal3Fisheye.h>
|
#include <gtsam/geometry/Cal3Fisheye.h>
|
||||||
#include <gtsam/geometry/Point3.h>
|
#include <gtsam/geometry/Point3.h>
|
||||||
|
|
@ -41,7 +42,11 @@ TEST(Cal3Fisheye, retract) {
|
||||||
Cal3Fisheye expected(K.fx() + 1, K.fy() + 2, K.skew() + 3, K.px() + 4,
|
Cal3Fisheye expected(K.fx() + 1, K.fy() + 2, K.skew() + 3, K.px() + 4,
|
||||||
K.py() + 5, K.k1() + 6, K.k2() + 7, K.k3() + 8,
|
K.py() + 5, K.k1() + 6, K.k2() + 7, K.k3() + 8,
|
||||||
K.k4() + 9);
|
K.k4() + 9);
|
||||||
Vector d(9);
|
|
||||||
|
EXPECT_LONGS_EQUAL(Cal3Fisheye::Dim(), 9);
|
||||||
|
EXPECT_LONGS_EQUAL(expected.dim(), 9);
|
||||||
|
|
||||||
|
Vector9 d;
|
||||||
d << 1, 2, 3, 4, 5, 6, 7, 8, 9;
|
d << 1, 2, 3, 4, 5, 6, 7, 8, 9;
|
||||||
Cal3Fisheye actual = K.retract(d);
|
Cal3Fisheye actual = K.retract(d);
|
||||||
CHECK(assert_equal(expected, actual, 1e-7));
|
CHECK(assert_equal(expected, actual, 1e-7));
|
||||||
|
|
@ -181,6 +186,33 @@ TEST(Cal3Fisheye, calibrate3) {
|
||||||
CHECK(assert_equal(xi_hat, xi));
|
CHECK(assert_equal(xi_hat, xi));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point2 calibrate_(const Cal3Fisheye& k, const Point2& pt) {
|
||||||
|
return k.calibrate(pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3Fisheye, Dcalibrate) {
|
||||||
|
Point2 p(0.5, 0.5);
|
||||||
|
Point2 pi = K.uncalibrate(p);
|
||||||
|
Matrix Dcal, Dp;
|
||||||
|
K.calibrate(pi, Dcal, Dp);
|
||||||
|
Matrix numerical1 = numericalDerivative21(calibrate_, K, pi);
|
||||||
|
CHECK(assert_equal(numerical1, Dcal, 1e-5));
|
||||||
|
Matrix numerical2 = numericalDerivative22(calibrate_, K, pi);
|
||||||
|
CHECK(assert_equal(numerical2, Dp, 1e-5));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3Fisheye, Print) {
|
||||||
|
Cal3Fisheye cal(1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||||
|
std::stringstream os;
|
||||||
|
os << "fx: " << cal.fx() << ", fy: " << cal.fy() << ", s: " << cal.skew()
|
||||||
|
<< ", px: " << cal.px() << ", py: " << cal.py() << ", k1: " << cal.k1()
|
||||||
|
<< ", k2: " << cal.k2() << ", k3: " << cal.k3() << ", k4: " << cal.k4();
|
||||||
|
|
||||||
|
EXPECT(assert_stdout_equal(os.str(), cal));
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
int main() {
|
int main() {
|
||||||
TestResult tr;
|
TestResult tr;
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,12 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file testCal3DS2.cpp
|
* @file testCal3DS2.cpp
|
||||||
* @brief Unit tests for transform derivatives
|
* @brief Unit tests for Cal3DS2 calibration model.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <CppUnitLite/TestHarness.h>
|
#include <CppUnitLite/TestHarness.h>
|
||||||
#include <gtsam/base/Testable.h>
|
#include <gtsam/base/Testable.h>
|
||||||
|
#include <gtsam/base/TestableAssertions.h>
|
||||||
#include <gtsam/base/numericalDerivative.h>
|
#include <gtsam/base/numericalDerivative.h>
|
||||||
#include <gtsam/geometry/Cal3DS2.h>
|
#include <gtsam/geometry/Cal3DS2.h>
|
||||||
|
|
||||||
|
|
@ -25,12 +25,12 @@ using namespace gtsam;
|
||||||
GTSAM_CONCEPT_TESTABLE_INST(Cal3DS2)
|
GTSAM_CONCEPT_TESTABLE_INST(Cal3DS2)
|
||||||
GTSAM_CONCEPT_MANIFOLD_INST(Cal3DS2)
|
GTSAM_CONCEPT_MANIFOLD_INST(Cal3DS2)
|
||||||
|
|
||||||
static Cal3DS2 K(500, 100, 0.1, 320, 240, 1e-3, 2.0*1e-3, 3.0*1e-3, 4.0*1e-3);
|
static Cal3DS2 K(500, 100, 0.1, 320, 240, 1e-3, 2.0 * 1e-3, 3.0 * 1e-3,
|
||||||
|
4.0 * 1e-3);
|
||||||
static Point2 p(2, 3);
|
static Point2 p(2, 3);
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3DS2, uncalibrate)
|
TEST(Cal3DS2, Uncalibrate) {
|
||||||
{
|
|
||||||
Vector k = K.k();
|
Vector k = K.k();
|
||||||
double r = p.x() * p.x() + p.y() * p.y();
|
double r = p.x() * p.x() + p.y() * p.y();
|
||||||
double g = 1 + k[0] * r + k[1] * r * r;
|
double g = 1 + k[0] * r + k[1] * r * r;
|
||||||
|
|
@ -43,19 +43,19 @@ TEST( Cal3DS2, uncalibrate)
|
||||||
CHECK(assert_equal(q, p_i));
|
CHECK(assert_equal(q, p_i));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST( Cal3DS2, calibrate )
|
TEST(Cal3DS2, Calibrate) {
|
||||||
{
|
|
||||||
Point2 pn(0.5, 0.5);
|
Point2 pn(0.5, 0.5);
|
||||||
Point2 pi = K.uncalibrate(pn);
|
Point2 pi = K.uncalibrate(pn);
|
||||||
Point2 pn_hat = K.calibrate(pi);
|
Point2 pn_hat = K.calibrate(pi);
|
||||||
CHECK(traits<Point2>::Equals(pn, pn_hat, 1e-5));
|
CHECK(traits<Point2>::Equals(pn, pn_hat, 1e-5));
|
||||||
}
|
}
|
||||||
|
|
||||||
Point2 uncalibrate_(const Cal3DS2& k, const Point2& pt) { return k.uncalibrate(pt); }
|
Point2 uncalibrate_(const Cal3DS2& k, const Point2& pt) {
|
||||||
|
return k.uncalibrate(pt);
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3DS2, Duncalibrate1)
|
TEST(Cal3DS2, Duncalibrate1) {
|
||||||
{
|
|
||||||
Matrix computed;
|
Matrix computed;
|
||||||
K.uncalibrate(p, computed, boost::none);
|
K.uncalibrate(p, computed, boost::none);
|
||||||
Matrix numerical = numericalDerivative21(uncalibrate_, K, p, 1e-7);
|
Matrix numerical = numericalDerivative21(uncalibrate_, K, p, 1e-7);
|
||||||
|
|
@ -65,27 +65,43 @@ TEST( Cal3DS2, Duncalibrate1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3DS2, Duncalibrate2)
|
TEST(Cal3DS2, Duncalibrate2) {
|
||||||
{
|
Matrix computed;
|
||||||
Matrix computed; K.uncalibrate(p, boost::none, computed);
|
K.uncalibrate(p, boost::none, computed);
|
||||||
Matrix numerical = numericalDerivative22(uncalibrate_, K, p, 1e-7);
|
Matrix numerical = numericalDerivative22(uncalibrate_, K, p, 1e-7);
|
||||||
CHECK(assert_equal(numerical, computed, 1e-5));
|
CHECK(assert_equal(numerical, computed, 1e-5));
|
||||||
Matrix separate = K.D2d_intrinsic(p);
|
Matrix separate = K.D2d_intrinsic(p);
|
||||||
CHECK(assert_equal(numerical, separate, 1e-5));
|
CHECK(assert_equal(numerical, separate, 1e-5));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
Point2 calibrate_(const Cal3DS2& k, const Point2& pt) {
|
||||||
TEST( Cal3DS2, assert_equal)
|
return k.calibrate(pt);
|
||||||
{
|
|
||||||
CHECK(assert_equal(K,K,1e-5));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3DS2, retract)
|
TEST(Cal3DS2, Dcalibrate) {
|
||||||
{
|
Point2 pn(0.5, 0.5);
|
||||||
|
Point2 pi = K.uncalibrate(pn);
|
||||||
|
Matrix Dcal, Dp;
|
||||||
|
K.calibrate(pi, Dcal, Dp);
|
||||||
|
Matrix numerical1 = numericalDerivative21(calibrate_, K, pi, 1e-7);
|
||||||
|
CHECK(assert_equal(numerical1, Dcal, 1e-5));
|
||||||
|
Matrix numerical2 = numericalDerivative22(calibrate_, K, pi, 1e-7);
|
||||||
|
CHECK(assert_equal(numerical2, Dp, 1e-5));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3DS2, Equal) { CHECK(assert_equal(K, K, 1e-5)); }
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3DS2, Retract) {
|
||||||
Cal3DS2 expected(500 + 1, 100 + 2, 0.1 + 3, 320 + 4, 240 + 5, 1e-3 + 6,
|
Cal3DS2 expected(500 + 1, 100 + 2, 0.1 + 3, 320 + 4, 240 + 5, 1e-3 + 6,
|
||||||
2.0 * 1e-3 + 7, 3.0 * 1e-3 + 8, 4.0 * 1e-3 + 9);
|
2.0 * 1e-3 + 7, 3.0 * 1e-3 + 8, 4.0 * 1e-3 + 9);
|
||||||
Vector d(9);
|
|
||||||
|
EXPECT_LONGS_EQUAL(Cal3DS2::Dim(), 9);
|
||||||
|
EXPECT_LONGS_EQUAL(expected.dim(), 9);
|
||||||
|
|
||||||
|
Vector9 d;
|
||||||
d << 1, 2, 3, 4, 5, 6, 7, 8, 9;
|
d << 1, 2, 3, 4, 5, 6, 7, 8, 9;
|
||||||
Cal3DS2 actual = K.retract(d);
|
Cal3DS2 actual = K.retract(d);
|
||||||
CHECK(assert_equal(expected, actual, 1e-7));
|
CHECK(assert_equal(expected, actual, 1e-7));
|
||||||
|
|
@ -93,5 +109,19 @@ TEST( Cal3DS2, retract)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
|
TEST(Cal3DS2, Print) {
|
||||||
|
Cal3DS2 cal(1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||||
|
std::stringstream os;
|
||||||
|
os << "fx: " << cal.fx() << ", fy: " << cal.fy() << ", s: " << cal.skew()
|
||||||
|
<< ", px: " << cal.px() << ", py: " << cal.py() << ", k1: " << cal.k1()
|
||||||
|
<< ", k2: " << cal.k2() << ", p1: " << cal.p1() << ", p2: " << cal.p2();
|
||||||
|
|
||||||
|
EXPECT(assert_stdout_equal(os.str(), cal));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
int main() {
|
||||||
|
TestResult tr;
|
||||||
|
return TestRegistry::runAllTests(tr);
|
||||||
|
}
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,18 @@
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file testCal3Unify.cpp
|
* @file testCal3Unified.cpp
|
||||||
* @brief Unit tests for transform derivatives
|
* @brief Unit tests for Cal3Unified calibration model.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <CppUnitLite/TestHarness.h>
|
#include <CppUnitLite/TestHarness.h>
|
||||||
#include <gtsam/base/Testable.h>
|
#include <gtsam/base/Testable.h>
|
||||||
|
#include <gtsam/base/TestableAssertions.h>
|
||||||
#include <gtsam/base/numericalDerivative.h>
|
#include <gtsam/base/numericalDerivative.h>
|
||||||
#include <gtsam/geometry/Cal3Unified.h>
|
#include <gtsam/geometry/Cal3Unified.h>
|
||||||
|
|
||||||
#include <gtsam/nonlinear/Values.h>
|
|
||||||
#include <gtsam/inference/Key.h>
|
#include <gtsam/inference/Key.h>
|
||||||
|
#include <gtsam/nonlinear/Values.h>
|
||||||
|
|
||||||
using namespace gtsam;
|
using namespace gtsam;
|
||||||
|
|
||||||
|
|
@ -35,38 +36,37 @@ V = [0.1, 1e-3, 2.0*1e-3, 3.0*1e-3, 4.0*1e-3, 0, 0, 100, 105, 320, 240];
|
||||||
matlab toolbox available at http://homepages.laas.fr/~cmei/index.php/Toolbox
|
matlab toolbox available at http://homepages.laas.fr/~cmei/index.php/Toolbox
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static Cal3Unified K(100, 105, 0.0, 320, 240, 1e-3, 2.0*1e-3, 3.0*1e-3, 4.0*1e-3, 0.1);
|
static Cal3Unified K(100, 105, 0.0, 320, 240, 1e-3, 2.0 * 1e-3, 3.0 * 1e-3,
|
||||||
|
4.0 * 1e-3, 0.1);
|
||||||
static Point2 p(0.5, 0.7);
|
static Point2 p(0.5, 0.7);
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Unified, uncalibrate)
|
TEST(Cal3Unified, Uncalibrate) {
|
||||||
{
|
|
||||||
Point2 p_i(364.7791831734982, 305.6677211952602);
|
Point2 p_i(364.7791831734982, 305.6677211952602);
|
||||||
Point2 q = K.uncalibrate(p);
|
Point2 q = K.uncalibrate(p);
|
||||||
CHECK(assert_equal(q, p_i));
|
CHECK(assert_equal(q, p_i));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Unified, spaceNplane)
|
TEST(Cal3Unified, SpaceNplane) {
|
||||||
{
|
|
||||||
Point2 q = K.spaceToNPlane(p);
|
Point2 q = K.spaceToNPlane(p);
|
||||||
CHECK(assert_equal(Point2(0.441731600049497, 0.618424240069295), q));
|
CHECK(assert_equal(Point2(0.441731600049497, 0.618424240069295), q));
|
||||||
CHECK(assert_equal(p, K.nPlaneToSpace(q)));
|
CHECK(assert_equal(p, K.nPlaneToSpace(q)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Unified, calibrate)
|
TEST(Cal3Unified, Calibrate) {
|
||||||
{
|
|
||||||
Point2 pi = K.uncalibrate(p);
|
Point2 pi = K.uncalibrate(p);
|
||||||
Point2 pn_hat = K.calibrate(pi);
|
Point2 pn_hat = K.calibrate(pi);
|
||||||
CHECK(traits<Point2>::Equals(p, pn_hat, 1e-8));
|
CHECK(traits<Point2>::Equals(p, pn_hat, 1e-8));
|
||||||
}
|
}
|
||||||
|
|
||||||
Point2 uncalibrate_(const Cal3Unified& k, const Point2& pt) { return k.uncalibrate(pt); }
|
Point2 uncalibrate_(const Cal3Unified& k, const Point2& pt) {
|
||||||
|
return k.uncalibrate(pt);
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Unified, Duncalibrate1)
|
TEST(Cal3Unified, Duncalibrate1) {
|
||||||
{
|
|
||||||
Matrix computed;
|
Matrix computed;
|
||||||
K.uncalibrate(p, computed, boost::none);
|
K.uncalibrate(p, computed, boost::none);
|
||||||
Matrix numerical = numericalDerivative21(uncalibrate_, K, p, 1e-7);
|
Matrix numerical = numericalDerivative21(uncalibrate_, K, p, 1e-7);
|
||||||
|
|
@ -74,26 +74,41 @@ TEST( Cal3Unified, Duncalibrate1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Unified, Duncalibrate2)
|
TEST(Cal3Unified, Duncalibrate2) {
|
||||||
{
|
|
||||||
Matrix computed;
|
Matrix computed;
|
||||||
K.uncalibrate(p, boost::none, computed);
|
K.uncalibrate(p, boost::none, computed);
|
||||||
Matrix numerical = numericalDerivative22(uncalibrate_, K, p, 1e-7);
|
Matrix numerical = numericalDerivative22(uncalibrate_, K, p, 1e-7);
|
||||||
CHECK(assert_equal(numerical, computed, 1e-6));
|
CHECK(assert_equal(numerical, computed, 1e-6));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
Point2 calibrate_(const Cal3Unified& k, const Point2& pt) {
|
||||||
TEST( Cal3Unified, assert_equal)
|
return k.calibrate(pt);
|
||||||
{
|
|
||||||
CHECK(assert_equal(K,K,1e-9));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Unified, retract)
|
TEST(Cal3Unified, Dcalibrate) {
|
||||||
{
|
Point2 pi = K.uncalibrate(p);
|
||||||
Cal3Unified expected(100 + 2, 105 + 3, 0.0 + 4, 320 + 5, 240 + 6,
|
Matrix Dcal, Dp;
|
||||||
1e-3 + 7, 2.0*1e-3 + 8, 3.0*1e-3 + 9, 4.0*1e-3 + 10, 0.1 + 1);
|
K.calibrate(pi, Dcal, Dp);
|
||||||
Vector d(10);
|
Matrix numerical1 = numericalDerivative21(calibrate_, K, pi);
|
||||||
|
CHECK(assert_equal(numerical1, Dcal, 1e-5));
|
||||||
|
Matrix numerical2 = numericalDerivative22(calibrate_, K, pi);
|
||||||
|
CHECK(assert_equal(numerical2, Dp, 1e-5));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3Unified, Equal) { CHECK(assert_equal(K, K, 1e-9)); }
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3Unified, Retract) {
|
||||||
|
Cal3Unified expected(100 + 2, 105 + 3, 0.0 + 4, 320 + 5, 240 + 6, 1e-3 + 7,
|
||||||
|
2.0 * 1e-3 + 8, 3.0 * 1e-3 + 9, 4.0 * 1e-3 + 10,
|
||||||
|
0.1 + 1);
|
||||||
|
|
||||||
|
EXPECT_LONGS_EQUAL(Cal3Unified::Dim(), 10);
|
||||||
|
EXPECT_LONGS_EQUAL(expected.dim(), 10);
|
||||||
|
|
||||||
|
Vector10 d;
|
||||||
d << 2, 3, 4, 5, 6, 7, 8, 9, 10, 1;
|
d << 2, 3, 4, 5, 6, 7, 8, 9, 10, 1;
|
||||||
Cal3Unified actual = K.retract(d);
|
Cal3Unified actual = K.retract(d);
|
||||||
CHECK(assert_equal(expected, actual, 1e-9));
|
CHECK(assert_equal(expected, actual, 1e-9));
|
||||||
|
|
@ -101,8 +116,7 @@ TEST( Cal3Unified, retract)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3Unified, DerivedValue)
|
TEST(Cal3Unified, DerivedValue) {
|
||||||
{
|
|
||||||
Values values;
|
Values values;
|
||||||
Cal3Unified cal(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
Cal3Unified cal(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||||
Key key = 1;
|
Key key = 1;
|
||||||
|
|
@ -114,5 +128,20 @@ TEST( Cal3Unified, DerivedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
|
TEST(Cal3Unified, Print) {
|
||||||
|
Cal3Unified cal(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||||
|
std::stringstream os;
|
||||||
|
os << "fx: " << cal.fx() << ", fy: " << cal.fy() << ", s: " << cal.skew()
|
||||||
|
<< ", px: " << cal.px() << ", py: " << cal.py() << ", k1: " << cal.k1()
|
||||||
|
<< ", k2: " << cal.k2() << ", p1: " << cal.p1() << ", p2: " << cal.p2()
|
||||||
|
<< ", xi: " << cal.xi();
|
||||||
|
|
||||||
|
EXPECT(assert_stdout_equal(os.str(), cal));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
int main() {
|
||||||
|
TestResult tr;
|
||||||
|
return TestRegistry::runAllTests(tr);
|
||||||
|
}
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file testCal3_S2.cpp
|
* @file testCal3_S2.cpp
|
||||||
* @brief Unit tests for transform derivatives
|
* @brief Unit tests for basic Cal3_S2 calibration model.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <CppUnitLite/TestHarness.h>
|
#include <CppUnitLite/TestHarness.h>
|
||||||
|
|
@ -31,8 +31,7 @@ static Point2 p_uv(1320.3, 1740);
|
||||||
static Point2 p_xy(2, 3);
|
static Point2 p_xy(2, 3);
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3_S2, easy_constructor)
|
TEST(Cal3_S2, Constructor) {
|
||||||
{
|
|
||||||
Cal3_S2 expected(554.256, 554.256, 0, 640 / 2, 480 / 2);
|
Cal3_S2 expected(554.256, 554.256, 0, 640 / 2, 480 / 2);
|
||||||
|
|
||||||
double fov = 60; // degrees
|
double fov = 60; // degrees
|
||||||
|
|
@ -43,8 +42,7 @@ TEST( Cal3_S2, easy_constructor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3_S2, calibrate)
|
TEST(Cal3_S2, Calibrate) {
|
||||||
{
|
|
||||||
Point2 intrinsic(2, 3);
|
Point2 intrinsic(2, 3);
|
||||||
Point2 expectedimage(1320.3, 1740);
|
Point2 expectedimage(1320.3, 1740);
|
||||||
Point2 imagecoordinates = K.uncalibrate(intrinsic);
|
Point2 imagecoordinates = K.uncalibrate(intrinsic);
|
||||||
|
|
@ -53,33 +51,38 @@ TEST( Cal3_S2, calibrate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3_S2, calibrate_homogeneous) {
|
TEST(Cal3_S2, CalibrateHomogeneous) {
|
||||||
Vector3 intrinsic(2, 3, 1);
|
Vector3 intrinsic(2, 3, 1);
|
||||||
Vector3 image(1320.3, 1740, 1);
|
Vector3 image(1320.3, 1740, 1);
|
||||||
CHECK(assert_equal((Vector)intrinsic, (Vector)K.calibrate(image)));
|
CHECK(assert_equal((Vector)intrinsic, (Vector)K.calibrate(image)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
Point2 uncalibrate_(const Cal3_S2& k, const Point2& pt) { return k.uncalibrate(pt); }
|
Point2 uncalibrate_(const Cal3_S2& k, const Point2& pt) {
|
||||||
TEST( Cal3_S2, Duncalibrate1)
|
return k.uncalibrate(pt);
|
||||||
{
|
}
|
||||||
Matrix25 computed; K.uncalibrate(p, computed, boost::none);
|
|
||||||
|
TEST(Cal3_S2, Duncalibrate1) {
|
||||||
|
Matrix25 computed;
|
||||||
|
K.uncalibrate(p, computed, boost::none);
|
||||||
Matrix numerical = numericalDerivative21(uncalibrate_, K, p);
|
Matrix numerical = numericalDerivative21(uncalibrate_, K, p);
|
||||||
CHECK(assert_equal(numerical, computed, 1e-8));
|
CHECK(assert_equal(numerical, computed, 1e-8));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3_S2, Duncalibrate2)
|
TEST(Cal3_S2, Duncalibrate2) {
|
||||||
{
|
Matrix computed;
|
||||||
Matrix computed; K.uncalibrate(p, boost::none, computed);
|
K.uncalibrate(p, boost::none, computed);
|
||||||
Matrix numerical = numericalDerivative22(uncalibrate_, K, p);
|
Matrix numerical = numericalDerivative22(uncalibrate_, K, p);
|
||||||
CHECK(assert_equal(numerical, computed, 1e-9));
|
CHECK(assert_equal(numerical, computed, 1e-9));
|
||||||
}
|
}
|
||||||
|
|
||||||
Point2 calibrate_(const Cal3_S2& k, const Point2& pt) {return k.calibrate(pt); }
|
Point2 calibrate_(const Cal3_S2& k, const Point2& pt) {
|
||||||
|
return k.calibrate(pt);
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST(Cal3_S2, Dcalibrate1)
|
TEST(Cal3_S2, Dcalibrate1) {
|
||||||
{
|
|
||||||
Matrix computed;
|
Matrix computed;
|
||||||
Point2 expected = K.calibrate(p_uv, computed, boost::none);
|
Point2 expected = K.calibrate(p_uv, computed, boost::none);
|
||||||
Matrix numerical = numericalDerivative21(calibrate_, K, p_uv);
|
Matrix numerical = numericalDerivative21(calibrate_, K, p_uv);
|
||||||
|
|
@ -88,8 +91,7 @@ TEST(Cal3_S2, Dcalibrate1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST(Cal3_S2, Dcalibrate2)
|
TEST(Cal3_S2, Dcalibrate2) {
|
||||||
{
|
|
||||||
Matrix computed;
|
Matrix computed;
|
||||||
Point2 expected = K.calibrate(p_uv, boost::none, computed);
|
Point2 expected = K.calibrate(p_uv, boost::none, computed);
|
||||||
Matrix numerical = numericalDerivative22(calibrate_, K, p_uv);
|
Matrix numerical = numericalDerivative22(calibrate_, K, p_uv);
|
||||||
|
|
@ -98,8 +100,7 @@ TEST(Cal3_S2, Dcalibrate2)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3_S2, assert_equal)
|
TEST(Cal3_S2, Equal) {
|
||||||
{
|
|
||||||
CHECK(assert_equal(K, K, 1e-9));
|
CHECK(assert_equal(K, K, 1e-9));
|
||||||
|
|
||||||
Cal3_S2 K1(500, 500, 0.1, 640 / 2, 480 / 2);
|
Cal3_S2 K1(500, 500, 0.1, 640 / 2, 480 / 2);
|
||||||
|
|
@ -107,10 +108,13 @@ TEST( Cal3_S2, assert_equal)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST( Cal3_S2, retract)
|
TEST(Cal3_S2, Retract) {
|
||||||
{
|
|
||||||
Cal3_S2 expected(500 + 1, 500 + 2, 0.1 + 3, 640 / 2 + 4, 480 / 2 + 5);
|
Cal3_S2 expected(500 + 1, 500 + 2, 0.1 + 3, 640 / 2 + 4, 480 / 2 + 5);
|
||||||
Vector d(5);
|
|
||||||
|
EXPECT_LONGS_EQUAL(Cal3_S2::Dim(), 5);
|
||||||
|
EXPECT_LONGS_EQUAL(expected.dim(), 5);
|
||||||
|
|
||||||
|
Vector5 d;
|
||||||
d << 1, 2, 3, 4, 5;
|
d << 1, 2, 3, 4, 5;
|
||||||
Cal3_S2 actual = K.retract(d);
|
Cal3_S2 actual = K.retract(d);
|
||||||
CHECK(assert_equal(expected, actual, 1e-7));
|
CHECK(assert_equal(expected, actual, 1e-7));
|
||||||
|
|
@ -125,15 +129,14 @@ TEST(Cal3_S2, between) {
|
||||||
EXPECT(assert_equal(Cal3_S2(0, 1, 2, 3, 4), k1.between(k2, H1, H2)));
|
EXPECT(assert_equal(Cal3_S2(0, 1, 2, 3, 4), k1.between(k2, H1, H2)));
|
||||||
EXPECT(assert_equal(-I_5x5, H1));
|
EXPECT(assert_equal(-I_5x5, H1));
|
||||||
EXPECT(assert_equal(I_5x5, H2));
|
EXPECT(assert_equal(I_5x5, H2));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST(Cal3_S2, Print) {
|
TEST(Cal3_S2, Print) {
|
||||||
Cal3_S2 cal(5, 5, 5, 5, 5);
|
Cal3_S2 cal(5, 5, 5, 5, 5);
|
||||||
std::stringstream os;
|
std::stringstream os;
|
||||||
os << "{fx: " << cal.fx() << ", fy: " << cal.fy() << ", s:" << cal.skew() << ", px:" << cal.px()
|
os << "fx: " << cal.fx() << ", fy: " << cal.fy() << ", s: " << cal.skew()
|
||||||
<< ", py:" << cal.py() << "}";
|
<< ", px: " << cal.px() << ", py: " << cal.py();
|
||||||
|
|
||||||
EXPECT(assert_stdout_equal(os.str(), cal));
|
EXPECT(assert_stdout_equal(os.str(), cal));
|
||||||
}
|
}
|
||||||
|
|
@ -144,4 +147,3 @@ int main() {
|
||||||
return TestRegistry::runAllTests(tr);
|
return TestRegistry::runAllTests(tr);
|
||||||
}
|
}
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file testCal3_S2Stereo.cpp
|
||||||
|
* @brief Unit tests for stereo-rig calibration model.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <CppUnitLite/TestHarness.h>
|
||||||
|
#include <gtsam/base/Testable.h>
|
||||||
|
#include <gtsam/base/TestableAssertions.h>
|
||||||
|
#include <gtsam/base/numericalDerivative.h>
|
||||||
|
#include <gtsam/geometry/Cal3_S2Stereo.h>
|
||||||
|
|
||||||
|
using namespace gtsam;
|
||||||
|
|
||||||
|
GTSAM_CONCEPT_TESTABLE_INST(Cal3_S2Stereo)
|
||||||
|
GTSAM_CONCEPT_MANIFOLD_INST(Cal3_S2Stereo)
|
||||||
|
|
||||||
|
static Cal3_S2Stereo K(500, 500, 0.1, 640 / 2, 480 / 2, 1);
|
||||||
|
static Point2 p(1, -2);
|
||||||
|
static Point2 p_uv(1320.3, 1740);
|
||||||
|
static Point2 p_xy(2, 3);
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3_S2Stereo, Constructor) {
|
||||||
|
Cal3_S2Stereo expected(554.256, 554.256, 0, 640 / 2, 480 / 2, 3);
|
||||||
|
|
||||||
|
double fov = 60; // degrees
|
||||||
|
size_t w = 640, h = 480;
|
||||||
|
Cal3_S2Stereo actual(fov, w, h, 3);
|
||||||
|
|
||||||
|
CHECK(assert_equal(expected, actual, 1e-3));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3_S2Stereo, Calibrate) {
|
||||||
|
Point2 intrinsic(2, 3);
|
||||||
|
Point2 expectedimage(1320.3, 1740);
|
||||||
|
Point2 imagecoordinates = K.uncalibrate(intrinsic);
|
||||||
|
CHECK(assert_equal(expectedimage, imagecoordinates));
|
||||||
|
CHECK(assert_equal(intrinsic, K.calibrate(imagecoordinates)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3_S2Stereo, CalibrateHomogeneous) {
|
||||||
|
Vector3 intrinsic(2, 3, 1);
|
||||||
|
Vector3 image(1320.3, 1740, 1);
|
||||||
|
CHECK(assert_equal(intrinsic, K.calibrate(image)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
Point2 uncalibrate_(const Cal3_S2Stereo& k, const Point2& pt) {
|
||||||
|
return k.uncalibrate(pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Cal3_S2Stereo, Duncalibrate) {
|
||||||
|
Matrix26 Dcal;
|
||||||
|
Matrix22 Dp;
|
||||||
|
K.uncalibrate(p, Dcal, Dp);
|
||||||
|
|
||||||
|
Matrix numerical1 = numericalDerivative21(uncalibrate_, K, p);
|
||||||
|
CHECK(assert_equal(numerical1, Dcal, 1e-8));
|
||||||
|
Matrix numerical2 = numericalDerivative22(uncalibrate_, K, p);
|
||||||
|
CHECK(assert_equal(numerical2, Dp, 1e-9));
|
||||||
|
}
|
||||||
|
|
||||||
|
Point2 calibrate_(const Cal3_S2Stereo& K, const Point2& pt) {
|
||||||
|
return K.calibrate(pt);
|
||||||
|
}
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3_S2Stereo, Dcalibrate) {
|
||||||
|
Matrix26 Dcal;
|
||||||
|
Matrix22 Dp;
|
||||||
|
Point2 expected = K.calibrate(p_uv, Dcal, Dp);
|
||||||
|
CHECK(assert_equal(expected, p_xy, 1e-8));
|
||||||
|
|
||||||
|
Matrix numerical1 = numericalDerivative21(calibrate_, K, p_uv);
|
||||||
|
CHECK(assert_equal(numerical1, Dcal, 1e-8));
|
||||||
|
Matrix numerical2 = numericalDerivative22(calibrate_, K, p_uv);
|
||||||
|
CHECK(assert_equal(numerical2, Dp, 1e-8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3_S2Stereo, Equal) {
|
||||||
|
CHECK(assert_equal(K, K, 1e-9));
|
||||||
|
|
||||||
|
Cal3_S2Stereo K1(500, 500, 0.1, 640 / 2, 480 / 2, 1);
|
||||||
|
CHECK(assert_equal(K, K1, 1e-9));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3_S2Stereo, Retract) {
|
||||||
|
Cal3_S2Stereo expected(500 + 1, 500 + 2, 0.1 + 3, 640 / 2 + 4, 480 / 2 + 5,
|
||||||
|
7);
|
||||||
|
EXPECT_LONGS_EQUAL(Cal3_S2Stereo::Dim(), 6);
|
||||||
|
EXPECT_LONGS_EQUAL(expected.dim(), 6);
|
||||||
|
|
||||||
|
Vector6 d;
|
||||||
|
d << 1, 2, 3, 4, 5, 6;
|
||||||
|
Cal3_S2Stereo actual = K.retract(d);
|
||||||
|
CHECK(assert_equal(expected, actual, 1e-7));
|
||||||
|
CHECK(assert_equal(d, K.localCoordinates(actual), 1e-7));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Cal3_S2Stereo, Print) {
|
||||||
|
Cal3_S2Stereo cal(5, 5, 5, 5, 5, 2);
|
||||||
|
std::stringstream os;
|
||||||
|
os << "fx: " << cal.fx() << ", fy: " << cal.fy() << ", s: " << cal.skew()
|
||||||
|
<< ", px: " << cal.px() << ", py: " << cal.py()
|
||||||
|
<< ", b: " << cal.baseline();
|
||||||
|
EXPECT(assert_stdout_equal(os.str(), cal));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
int main() {
|
||||||
|
TestResult tr;
|
||||||
|
return TestRegistry::runAllTests(tr);
|
||||||
|
}
|
||||||
|
/* ************************************************************************* */
|
||||||
|
|
@ -14,6 +14,16 @@ GTSAM_CONCEPT_MANIFOLD_INST(Line3)
|
||||||
|
|
||||||
static const Line3 l(Rot3(), 1, 1);
|
static const Line3 l(Rot3(), 1, 1);
|
||||||
|
|
||||||
|
// Testing getters
|
||||||
|
TEST(Line3, getMethods) {
|
||||||
|
const double a = 5, b = 10;
|
||||||
|
const Rot3 R = Rot3::Expmap(Vector3(0.1, 0.2, 0.3));
|
||||||
|
const Line3 line(R, a, b);
|
||||||
|
EXPECT_DOUBLES_EQUAL(a, line.a(), 1e-8);
|
||||||
|
EXPECT_DOUBLES_EQUAL(b, line.b(), 1e-8);
|
||||||
|
EXPECT(assert_equal(R, line.R(), 1e-8));
|
||||||
|
}
|
||||||
|
|
||||||
// Testing equals function of Line3
|
// Testing equals function of Line3
|
||||||
TEST(Line3, equals) {
|
TEST(Line3, equals) {
|
||||||
Line3 l_same = l;
|
Line3 l_same = l;
|
||||||
|
|
|
||||||
|
|
@ -1016,6 +1016,33 @@ TEST(Pose3, TransformCovariance6) {
|
||||||
TEST(Pose3, interpolate) {
|
TEST(Pose3, interpolate) {
|
||||||
EXPECT(assert_equal(T2, interpolate(T2,T3, 0.0)));
|
EXPECT(assert_equal(T2, interpolate(T2,T3, 0.0)));
|
||||||
EXPECT(assert_equal(T3, interpolate(T2,T3, 1.0)));
|
EXPECT(assert_equal(T3, interpolate(T2,T3, 1.0)));
|
||||||
|
|
||||||
|
// Trivial example: start at origin and move to (1, 0, 0) while rotating pi/2
|
||||||
|
// about z-axis.
|
||||||
|
Pose3 start;
|
||||||
|
Pose3 end(Rot3::Rz(M_PI_2), Point3(1, 0, 0));
|
||||||
|
// This interpolation is easy to calculate by hand.
|
||||||
|
double t = 0.5;
|
||||||
|
Pose3 expected0(Rot3::Rz(M_PI_4), Point3(0.5, 0, 0));
|
||||||
|
EXPECT(assert_equal(expected0, start.interpolateRt(end, t)));
|
||||||
|
|
||||||
|
// Example from Peter Corke
|
||||||
|
// https://robotacademy.net.au/lesson/interpolating-pose-in-3d/
|
||||||
|
t = 0.0759; // corresponds to the 10th element when calling `ctraj` in
|
||||||
|
// the video
|
||||||
|
Pose3 O;
|
||||||
|
Pose3 F(Rot3::Roll(0.6).compose(Rot3::Pitch(0.8)).compose(Rot3::Yaw(1.4)),
|
||||||
|
Point3(1, 2, 3));
|
||||||
|
|
||||||
|
// The expected answer matches the result presented in the video.
|
||||||
|
Pose3 expected1(interpolate(O.rotation(), F.rotation(), t),
|
||||||
|
interpolate(O.translation(), F.translation(), t));
|
||||||
|
EXPECT(assert_equal(expected1, O.interpolateRt(F, t)));
|
||||||
|
|
||||||
|
// Non-trivial interpolation, translation value taken from output.
|
||||||
|
Pose3 expected2(interpolate(T2.rotation(), T3.rotation(), t),
|
||||||
|
interpolate(T2.translation(), T3.translation(), t));
|
||||||
|
EXPECT(assert_equal(expected2, T2.interpolateRt(T3, t)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -807,15 +807,15 @@ TEST(Rot3, RQ_derivative) {
|
||||||
test_xyz.push_back(VecAndErr{{0, 0, 0}, error});
|
test_xyz.push_back(VecAndErr{{0, 0, 0}, error});
|
||||||
test_xyz.push_back(VecAndErr{{0, 0.5, -0.5}, error});
|
test_xyz.push_back(VecAndErr{{0, 0.5, -0.5}, error});
|
||||||
test_xyz.push_back(VecAndErr{{0.3, 0, 0.2}, error});
|
test_xyz.push_back(VecAndErr{{0.3, 0, 0.2}, error});
|
||||||
test_xyz.push_back(VecAndErr{{-0.6, 1.3, 0}, error});
|
test_xyz.push_back(VecAndErr{{-0.6, 1.3, 0}, 1e-8});
|
||||||
test_xyz.push_back(VecAndErr{{1.0, 0.7, 0.8}, error});
|
test_xyz.push_back(VecAndErr{{1.0, 0.7, 0.8}, error});
|
||||||
test_xyz.push_back(VecAndErr{{3.0, 0.7, -0.6}, error});
|
test_xyz.push_back(VecAndErr{{3.0, 0.7, -0.6}, error});
|
||||||
test_xyz.push_back(VecAndErr{{M_PI / 2, 0, 0}, error});
|
test_xyz.push_back(VecAndErr{{M_PI / 2, 0, 0}, error});
|
||||||
test_xyz.push_back(VecAndErr{{0, 0, M_PI / 2}, error});
|
test_xyz.push_back(VecAndErr{{0, 0, M_PI / 2}, error});
|
||||||
|
|
||||||
// Test close to singularity
|
// Test close to singularity
|
||||||
test_xyz.push_back(VecAndErr{{0, M_PI / 2 - 1e-1, 0}, 1e-8});
|
test_xyz.push_back(VecAndErr{{0, M_PI / 2 - 1e-1, 0}, 1e-7});
|
||||||
test_xyz.push_back(VecAndErr{{0, 3 * M_PI / 2 + 1e-1, 0}, 1e-8});
|
test_xyz.push_back(VecAndErr{{0, 3 * M_PI / 2 + 1e-1, 0}, 1e-7});
|
||||||
test_xyz.push_back(VecAndErr{{0, M_PI / 2 - 1.1e-2, 0}, 1e-4});
|
test_xyz.push_back(VecAndErr{{0, M_PI / 2 - 1.1e-2, 0}, 1e-4});
|
||||||
test_xyz.push_back(VecAndErr{{0, 3 * M_PI / 2 + 1.1e-2, 0}, 1e-4});
|
test_xyz.push_back(VecAndErr{{0, 3 * M_PI / 2 + 1.1e-2, 0}, 1e-4});
|
||||||
|
|
||||||
|
|
@ -825,7 +825,7 @@ TEST(Rot3, RQ_derivative) {
|
||||||
const auto R = Rot3::RzRyRx(xyz).matrix();
|
const auto R = Rot3::RzRyRx(xyz).matrix();
|
||||||
const auto num = numericalDerivative11(RQ_proxy, R);
|
const auto num = numericalDerivative11(RQ_proxy, R);
|
||||||
Matrix39 calc;
|
Matrix39 calc;
|
||||||
RQ(R, calc).second;
|
auto dummy = RQ(R, calc).second;
|
||||||
|
|
||||||
const auto err = vec_err.second;
|
const auto err = vec_err.second;
|
||||||
CHECK(assert_equal(num, calc, err));
|
CHECK(assert_equal(num, calc, err));
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace gtsam;
|
using namespace gtsam;
|
||||||
|
|
||||||
|
#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V41
|
||||||
|
|
||||||
static const Cal3_S2 K(625, 625, 0, 0, 0);
|
static const Cal3_S2 K(625, 625, 0, 0, 0);
|
||||||
|
|
||||||
static const Pose3 pose1(Rot3(Vector3(1, -1, -1).asDiagonal()),
|
static const Pose3 pose1(Rot3(Vector3(1, -1, -1).asDiagonal()),
|
||||||
|
|
@ -149,6 +151,8 @@ TEST( SimpleCamera, simpleCamera)
|
||||||
CHECK(assert_equal(expected, actual,1e-1));
|
CHECK(assert_equal(expected, actual,1e-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
|
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtsam/geometry/PinholeCamera.h>
|
#include <gtsam/geometry/Cal3_S2.h>
|
||||||
|
#include <gtsam/geometry/Cal3Bundler.h>
|
||||||
#include <gtsam/geometry/CameraSet.h>
|
#include <gtsam/geometry/CameraSet.h>
|
||||||
|
#include <gtsam/geometry/PinholeCamera.h>
|
||||||
#include <gtsam/geometry/Pose2.h>
|
#include <gtsam/geometry/Pose2.h>
|
||||||
#include <gtsam/slam/TriangulationFactor.h>
|
#include <gtsam/slam/TriangulationFactor.h>
|
||||||
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
|
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
|
||||||
|
|
@ -494,5 +496,9 @@ TriangulationResult triangulateSafe(const CameraSet<CAMERA>& cameras,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vector of Cameras - used by the Python/MATLAB wrapper
|
||||||
|
using CameraSetCal3Bundler = CameraSet<PinholeCamera<Cal3Bundler>>;
|
||||||
|
using CameraSetCal3_S2 = CameraSet<PinholeCamera<Cal3_S2>>;
|
||||||
|
|
||||||
} // \namespace gtsam
|
} // \namespace gtsam
|
||||||
|
|
||||||
|
|
|
||||||
134
gtsam/gtsam.i
134
gtsam/gtsam.i
|
|
@ -329,7 +329,7 @@ virtual class Value {
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <gtsam/base/GenericValue.h>
|
#include <gtsam/base/GenericValue.h>
|
||||||
template<T = {Vector, Matrix, gtsam::Point2, gtsam::Point3, gtsam::Rot2, gtsam::Rot3, gtsam::Pose2, gtsam::Pose3, gtsam::StereoPoint2, gtsam::Cal3_S2, gtsam::Cal3DS2, gtsam::Cal3Bundler, gtsam::EssentialMatrix, gtsam::CalibratedCamera, gtsam::SimpleCamera, gtsam::imuBias::ConstantBias}>
|
template<T = {Vector, Matrix, gtsam::Point2, gtsam::Point3, gtsam::Rot2, gtsam::Rot3, gtsam::Pose2, gtsam::Pose3, gtsam::StereoPoint2, gtsam::Cal3_S2, gtsam::Cal3DS2, gtsam::Cal3Bundler, gtsam::EssentialMatrix, gtsam::CalibratedCamera, gtsam::imuBias::ConstantBias}>
|
||||||
virtual class GenericValue : gtsam::Value {
|
virtual class GenericValue : gtsam::Value {
|
||||||
void serializable() const;
|
void serializable() const;
|
||||||
};
|
};
|
||||||
|
|
@ -852,8 +852,7 @@ class Cal3_S2 {
|
||||||
gtsam::Point2 principalPoint() const;
|
gtsam::Point2 principalPoint() const;
|
||||||
Vector vector() const;
|
Vector vector() const;
|
||||||
Matrix K() const;
|
Matrix K() const;
|
||||||
Matrix matrix() const;
|
Matrix inverse() const;
|
||||||
Matrix matrix_inverse() const;
|
|
||||||
|
|
||||||
// enabling serialization functionality
|
// enabling serialization functionality
|
||||||
void serialize() const;
|
void serialize() const;
|
||||||
|
|
@ -881,7 +880,7 @@ virtual class Cal3DS2_Base {
|
||||||
|
|
||||||
// Action on Point2
|
// Action on Point2
|
||||||
gtsam::Point2 uncalibrate(const gtsam::Point2& p) const;
|
gtsam::Point2 uncalibrate(const gtsam::Point2& p) const;
|
||||||
gtsam::Point2 calibrate(const gtsam::Point2& p, double tol) const;
|
gtsam::Point2 calibrate(const gtsam::Point2& p) const;
|
||||||
|
|
||||||
// enabling serialization functionality
|
// enabling serialization functionality
|
||||||
void serialize() const;
|
void serialize() const;
|
||||||
|
|
@ -1059,56 +1058,24 @@ class PinholeCamera {
|
||||||
void serialize() const;
|
void serialize() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Forward declaration of PinholeCameraCalX is defined here.
|
||||||
#include <gtsam/geometry/SimpleCamera.h>
|
#include <gtsam/geometry/SimpleCamera.h>
|
||||||
virtual class SimpleCamera {
|
|
||||||
// Standard Constructors and Named Constructors
|
|
||||||
SimpleCamera();
|
|
||||||
SimpleCamera(const gtsam::Pose3& pose);
|
|
||||||
SimpleCamera(const gtsam::Pose3& pose, const gtsam::Cal3_S2& K);
|
|
||||||
static gtsam::SimpleCamera Level(const gtsam::Cal3_S2& K, const gtsam::Pose2& pose, double height);
|
|
||||||
static gtsam::SimpleCamera Level(const gtsam::Pose2& pose, double height);
|
|
||||||
static gtsam::SimpleCamera Lookat(const gtsam::Point3& eye, const gtsam::Point3& target,
|
|
||||||
const gtsam::Point3& upVector, const gtsam::Cal3_S2& K);
|
|
||||||
static gtsam::SimpleCamera Lookat(const gtsam::Point3& eye, const gtsam::Point3& target,
|
|
||||||
const gtsam::Point3& upVector);
|
|
||||||
|
|
||||||
// Testable
|
|
||||||
void print(string s) const;
|
|
||||||
bool equals(const gtsam::SimpleCamera& camera, double tol) const;
|
|
||||||
|
|
||||||
// Standard Interface
|
|
||||||
gtsam::Pose3 pose() const;
|
|
||||||
gtsam::Cal3_S2 calibration() const;
|
|
||||||
|
|
||||||
// Manifold
|
|
||||||
gtsam::SimpleCamera retract(Vector d) const;
|
|
||||||
Vector localCoordinates(const gtsam::SimpleCamera& T2) const;
|
|
||||||
size_t dim() const;
|
|
||||||
static size_t Dim();
|
|
||||||
|
|
||||||
// Transformations and measurement functions
|
|
||||||
static gtsam::Point2 Project(const gtsam::Point3& cameraPoint);
|
|
||||||
pair<gtsam::Point2,bool> projectSafe(const gtsam::Point3& pw) const;
|
|
||||||
gtsam::Point2 project(const gtsam::Point3& point);
|
|
||||||
gtsam::Point3 backproject(const gtsam::Point2& p, double depth) const;
|
|
||||||
double range(const gtsam::Point3& point);
|
|
||||||
double range(const gtsam::Pose3& pose);
|
|
||||||
|
|
||||||
// enabling serialization functionality
|
|
||||||
void serialize() const;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
gtsam::SimpleCamera simpleCamera(const Matrix& P);
|
|
||||||
|
|
||||||
// Some typedefs for common camera types
|
// Some typedefs for common camera types
|
||||||
// PinholeCameraCal3_S2 is the same as SimpleCamera above
|
// PinholeCameraCal3_S2 is the same as SimpleCamera above
|
||||||
typedef gtsam::PinholeCamera<gtsam::Cal3_S2> PinholeCameraCal3_S2;
|
typedef gtsam::PinholeCamera<gtsam::Cal3_S2> PinholeCameraCal3_S2;
|
||||||
//TODO (Issue 237) due to lack of jacobians of Cal3DS2_Base::calibrate, PinholeCamera does not apply to Cal3DS2/Unified
|
typedef gtsam::PinholeCamera<gtsam::Cal3DS2> PinholeCameraCal3DS2;
|
||||||
//typedef gtsam::PinholeCamera<gtsam::Cal3DS2> PinholeCameraCal3DS2;
|
typedef gtsam::PinholeCamera<gtsam::Cal3Unified> PinholeCameraCal3Unified;
|
||||||
//typedef gtsam::PinholeCamera<gtsam::Cal3Unified> PinholeCameraCal3Unified;
|
|
||||||
typedef gtsam::PinholeCamera<gtsam::Cal3Bundler> PinholeCameraCal3Bundler;
|
typedef gtsam::PinholeCamera<gtsam::Cal3Bundler> PinholeCameraCal3Bundler;
|
||||||
|
|
||||||
|
template<T>
|
||||||
|
class CameraSet {
|
||||||
|
CameraSet();
|
||||||
|
|
||||||
|
// structure specific methods
|
||||||
|
T at(size_t i) const;
|
||||||
|
void push_back(const T& cam);
|
||||||
|
};
|
||||||
|
|
||||||
#include <gtsam/geometry/StereoCamera.h>
|
#include <gtsam/geometry/StereoCamera.h>
|
||||||
class StereoCamera {
|
class StereoCamera {
|
||||||
// Standard Constructors and Named Constructors
|
// Standard Constructors and Named Constructors
|
||||||
|
|
@ -1140,7 +1107,7 @@ class StereoCamera {
|
||||||
|
|
||||||
#include <gtsam/geometry/triangulation.h>
|
#include <gtsam/geometry/triangulation.h>
|
||||||
|
|
||||||
// Templates appear not yet supported for free functions
|
// Templates appear not yet supported for free functions - issue raised at borglab/wrap#14 to add support
|
||||||
gtsam::Point3 triangulatePoint3(const gtsam::Pose3Vector& poses,
|
gtsam::Point3 triangulatePoint3(const gtsam::Pose3Vector& poses,
|
||||||
gtsam::Cal3_S2* sharedCal, const gtsam::Point2Vector& measurements,
|
gtsam::Cal3_S2* sharedCal, const gtsam::Point2Vector& measurements,
|
||||||
double rank_tol, bool optimize);
|
double rank_tol, bool optimize);
|
||||||
|
|
@ -1150,6 +1117,12 @@ gtsam::Point3 triangulatePoint3(const gtsam::Pose3Vector& poses,
|
||||||
gtsam::Point3 triangulatePoint3(const gtsam::Pose3Vector& poses,
|
gtsam::Point3 triangulatePoint3(const gtsam::Pose3Vector& poses,
|
||||||
gtsam::Cal3Bundler* sharedCal, const gtsam::Point2Vector& measurements,
|
gtsam::Cal3Bundler* sharedCal, const gtsam::Point2Vector& measurements,
|
||||||
double rank_tol, bool optimize);
|
double rank_tol, bool optimize);
|
||||||
|
gtsam::Point3 triangulatePoint3(const gtsam::CameraSetCal3_S2& cameras,
|
||||||
|
const gtsam::Point2Vector& measurements, double rank_tol,
|
||||||
|
bool optimize);
|
||||||
|
gtsam::Point3 triangulatePoint3(const gtsam::CameraSetCal3Bundler& cameras,
|
||||||
|
const gtsam::Point2Vector& measurements, double rank_tol,
|
||||||
|
bool optimize);
|
||||||
|
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
// Symbolic
|
// Symbolic
|
||||||
|
|
@ -1963,6 +1936,21 @@ class KalmanFilter {
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include <gtsam/inference/Symbol.h>
|
#include <gtsam/inference/Symbol.h>
|
||||||
|
|
||||||
|
class Symbol {
|
||||||
|
Symbol();
|
||||||
|
Symbol(char c, uint64_t j);
|
||||||
|
Symbol(size_t key);
|
||||||
|
|
||||||
|
size_t key() const;
|
||||||
|
void print(const string& s) const;
|
||||||
|
bool equals(const gtsam::Symbol& expected, double tol) const;
|
||||||
|
|
||||||
|
char chr() const;
|
||||||
|
uint64_t index() const;
|
||||||
|
string string() const;
|
||||||
|
};
|
||||||
|
|
||||||
size_t symbol(char chr, size_t index);
|
size_t symbol(char chr, size_t index);
|
||||||
char symbolChr(size_t key);
|
char symbolChr(size_t key);
|
||||||
size_t symbolIndex(size_t key);
|
size_t symbolIndex(size_t key);
|
||||||
|
|
@ -2069,7 +2057,7 @@ class NonlinearFactorGraph {
|
||||||
gtsam::KeySet keys() const;
|
gtsam::KeySet keys() const;
|
||||||
gtsam::KeyVector keyVector() const;
|
gtsam::KeyVector keyVector() const;
|
||||||
|
|
||||||
template<T = {Vector, gtsam::Point2, gtsam::StereoPoint2, gtsam::Point3, gtsam::Rot2, gtsam::SO3, gtsam::SO4, gtsam::Rot3, gtsam::Pose2, gtsam::Pose3, gtsam::Cal3_S2,gtsam::CalibratedCamera, gtsam::SimpleCamera, gtsam::PinholeCameraCal3_S2, gtsam::PinholeCamera<gtsam::Cal3Bundler>, gtsam::imuBias::ConstantBias}>
|
template<T = {Vector, gtsam::Point2, gtsam::StereoPoint2, gtsam::Point3, gtsam::Rot2, gtsam::SO3, gtsam::SO4, gtsam::Rot3, gtsam::Pose2, gtsam::Pose3, gtsam::Cal3_S2,gtsam::CalibratedCamera, gtsam::PinholeCameraCal3_S2, gtsam::PinholeCamera<gtsam::Cal3Bundler>, gtsam::imuBias::ConstantBias}>
|
||||||
void addPrior(size_t key, const T& prior, const gtsam::noiseModel::Base* noiseModel);
|
void addPrior(size_t key, const T& prior, const gtsam::noiseModel::Base* noiseModel);
|
||||||
|
|
||||||
// NonlinearFactorGraph
|
// NonlinearFactorGraph
|
||||||
|
|
@ -2083,6 +2071,7 @@ class NonlinearFactorGraph {
|
||||||
|
|
||||||
// enabling serialization functionality
|
// enabling serialization functionality
|
||||||
void serialize() const;
|
void serialize() const;
|
||||||
|
void saveGraph(const string& s) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <gtsam/nonlinear/NonlinearFactor.h>
|
#include <gtsam/nonlinear/NonlinearFactor.h>
|
||||||
|
|
@ -2493,7 +2482,7 @@ class ISAM2 {
|
||||||
template <VALUE = {gtsam::Point2, gtsam::Rot2, gtsam::Pose2, gtsam::Point3,
|
template <VALUE = {gtsam::Point2, gtsam::Rot2, gtsam::Pose2, gtsam::Point3,
|
||||||
gtsam::Rot3, gtsam::Pose3, gtsam::Cal3_S2, gtsam::Cal3DS2,
|
gtsam::Rot3, gtsam::Pose3, gtsam::Cal3_S2, gtsam::Cal3DS2,
|
||||||
gtsam::Cal3Bundler, gtsam::EssentialMatrix,
|
gtsam::Cal3Bundler, gtsam::EssentialMatrix,
|
||||||
gtsam::SimpleCamera, gtsam::PinholeCameraCal3_S2, gtsam::PinholeCamera<gtsam::Cal3Bundler>,
|
gtsam::PinholeCameraCal3_S2, gtsam::PinholeCamera<gtsam::Cal3Bundler>,
|
||||||
Vector, Matrix}>
|
Vector, Matrix}>
|
||||||
VALUE calculateEstimate(size_t key) const;
|
VALUE calculateEstimate(size_t key) const;
|
||||||
gtsam::Values calculateBestEstimate() const;
|
gtsam::Values calculateBestEstimate() const;
|
||||||
|
|
@ -2527,12 +2516,11 @@ class NonlinearISAM {
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
// Nonlinear factor types
|
// Nonlinear factor types
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
#include <gtsam/geometry/SimpleCamera.h>
|
|
||||||
#include <gtsam/geometry/CalibratedCamera.h>
|
#include <gtsam/geometry/CalibratedCamera.h>
|
||||||
#include <gtsam/geometry/StereoPoint2.h>
|
#include <gtsam/geometry/StereoPoint2.h>
|
||||||
|
|
||||||
#include <gtsam/nonlinear/PriorFactor.h>
|
#include <gtsam/nonlinear/PriorFactor.h>
|
||||||
template<T = {Vector, gtsam::Point2, gtsam::StereoPoint2, gtsam::Point3, gtsam::Rot2, gtsam::SO3, gtsam::SO4, gtsam::SOn, gtsam::Rot3, gtsam::Pose2, gtsam::Pose3, gtsam::Unit3, gtsam::Cal3_S2,gtsam::CalibratedCamera, gtsam::SimpleCamera, gtsam::PinholeCameraCal3_S2, gtsam::imuBias::ConstantBias, gtsam::PinholeCamera<gtsam::Cal3Bundler>}>
|
template<T = {Vector, gtsam::Point2, gtsam::StereoPoint2, gtsam::Point3, gtsam::Rot2, gtsam::SO3, gtsam::SO4, gtsam::SOn, gtsam::Rot3, gtsam::Pose2, gtsam::Pose3, gtsam::Unit3, gtsam::Cal3_S2, gtsam::Cal3DS2, gtsam::Cal3Bundler, gtsam::CalibratedCamera, gtsam::PinholeCameraCal3_S2, gtsam::imuBias::ConstantBias, gtsam::PinholeCamera<gtsam::Cal3Bundler>}>
|
||||||
virtual class PriorFactor : gtsam::NoiseModelFactor {
|
virtual class PriorFactor : gtsam::NoiseModelFactor {
|
||||||
PriorFactor(size_t key, const T& prior, const gtsam::noiseModel::Base* noiseModel);
|
PriorFactor(size_t key, const T& prior, const gtsam::noiseModel::Base* noiseModel);
|
||||||
T prior() const;
|
T prior() const;
|
||||||
|
|
@ -2556,7 +2544,7 @@ virtual class BetweenFactor : gtsam::NoiseModelFactor {
|
||||||
template <T = {gtsam::Point2, gtsam::StereoPoint2, gtsam::Point3, gtsam::Rot2,
|
template <T = {gtsam::Point2, gtsam::StereoPoint2, gtsam::Point3, gtsam::Rot2,
|
||||||
gtsam::SO3, gtsam::SO4, gtsam::SOn, gtsam::Rot3, gtsam::Pose2,
|
gtsam::SO3, gtsam::SO4, gtsam::SOn, gtsam::Rot3, gtsam::Pose2,
|
||||||
gtsam::Pose3, gtsam::Cal3_S2, gtsam::CalibratedCamera,
|
gtsam::Pose3, gtsam::Cal3_S2, gtsam::CalibratedCamera,
|
||||||
gtsam::SimpleCamera, gtsam::PinholeCameraCal3_S2,
|
gtsam::PinholeCameraCal3_S2,
|
||||||
gtsam::imuBias::ConstantBias}>
|
gtsam::imuBias::ConstantBias}>
|
||||||
virtual class NonlinearEquality : gtsam::NoiseModelFactor {
|
virtual class NonlinearEquality : gtsam::NoiseModelFactor {
|
||||||
// Constructor - forces exact evaluation
|
// Constructor - forces exact evaluation
|
||||||
|
|
@ -2611,6 +2599,7 @@ virtual class BearingFactor : gtsam::NoiseModelFactor {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef gtsam::BearingFactor<gtsam::Pose2, gtsam::Point2, gtsam::Rot2> BearingFactor2D;
|
typedef gtsam::BearingFactor<gtsam::Pose2, gtsam::Point2, gtsam::Rot2> BearingFactor2D;
|
||||||
|
typedef gtsam::BearingFactor<gtsam::Pose3, gtsam::Point3, gtsam::Unit3> BearingFactor3D;
|
||||||
typedef gtsam::BearingFactor<gtsam::Pose2, gtsam::Pose2, gtsam::Rot2> BearingFactorPose2;
|
typedef gtsam::BearingFactor<gtsam::Pose2, gtsam::Pose2, gtsam::Rot2> BearingFactorPose2;
|
||||||
|
|
||||||
#include <gtsam/geometry/BearingRange.h>
|
#include <gtsam/geometry/BearingRange.h>
|
||||||
|
|
@ -2634,6 +2623,8 @@ virtual class BearingRangeFactor : gtsam::NoiseModelFactor {
|
||||||
const BEARING& measuredBearing, const RANGE& measuredRange,
|
const BEARING& measuredBearing, const RANGE& measuredRange,
|
||||||
const gtsam::noiseModel::Base* noiseModel);
|
const gtsam::noiseModel::Base* noiseModel);
|
||||||
|
|
||||||
|
BearingRange<POSE, POINT, BEARING, RANGE> measured() const;
|
||||||
|
|
||||||
// enabling serialization functionality
|
// enabling serialization functionality
|
||||||
void serialize() const;
|
void serialize() const;
|
||||||
};
|
};
|
||||||
|
|
@ -2675,11 +2666,10 @@ virtual class GeneralSFMFactor : gtsam::NoiseModelFactor {
|
||||||
gtsam::Point2 measured() const;
|
gtsam::Point2 measured() const;
|
||||||
};
|
};
|
||||||
typedef gtsam::GeneralSFMFactor<gtsam::PinholeCameraCal3_S2, gtsam::Point3> GeneralSFMFactorCal3_S2;
|
typedef gtsam::GeneralSFMFactor<gtsam::PinholeCameraCal3_S2, gtsam::Point3> GeneralSFMFactorCal3_S2;
|
||||||
//TODO (Issue 237) due to lack of jacobians of Cal3DS2_Base::calibrate, GeneralSFMFactor does not apply to Cal3DS2
|
typedef gtsam::GeneralSFMFactor<gtsam::PinholeCameraCal3DS2, gtsam::Point3> GeneralSFMFactorCal3DS2;
|
||||||
//typedef gtsam::GeneralSFMFactor<gtsam::PinholeCameraCal3DS2, gtsam::Point3> GeneralSFMFactorCal3DS2;
|
|
||||||
typedef gtsam::GeneralSFMFactor<gtsam::PinholeCamera<gtsam::Cal3Bundler>, gtsam::Point3> GeneralSFMFactorCal3Bundler;
|
typedef gtsam::GeneralSFMFactor<gtsam::PinholeCamera<gtsam::Cal3Bundler>, gtsam::Point3> GeneralSFMFactorCal3Bundler;
|
||||||
|
|
||||||
template<CALIBRATION = {gtsam::Cal3_S2}>
|
template<CALIBRATION = {gtsam::Cal3_S2, gtsam::Cal3DS2, gtsam::Cal3Bundler}>
|
||||||
virtual class GeneralSFMFactor2 : gtsam::NoiseModelFactor {
|
virtual class GeneralSFMFactor2 : gtsam::NoiseModelFactor {
|
||||||
GeneralSFMFactor2(const gtsam::Point2& measured, const gtsam::noiseModel::Base* model, size_t poseKey, size_t landmarkKey, size_t calibKey);
|
GeneralSFMFactor2(const gtsam::Point2& measured, const gtsam::noiseModel::Base* model, size_t poseKey, size_t landmarkKey, size_t calibKey);
|
||||||
gtsam::Point2 measured() const;
|
gtsam::Point2 measured() const;
|
||||||
|
|
@ -2780,6 +2770,12 @@ class SfmTrack {
|
||||||
pair<size_t, gtsam::Point2> measurement(size_t idx) const;
|
pair<size_t, gtsam::Point2> measurement(size_t idx) const;
|
||||||
pair<size_t, size_t> siftIndex(size_t idx) const;
|
pair<size_t, size_t> siftIndex(size_t idx) const;
|
||||||
void add_measurement(size_t idx, const gtsam::Point2& m);
|
void add_measurement(size_t idx, const gtsam::Point2& m);
|
||||||
|
|
||||||
|
// enabling serialization functionality
|
||||||
|
void serialize() const;
|
||||||
|
|
||||||
|
// enabling function to compare objects
|
||||||
|
bool equals(const gtsam::SfmTrack& expected, double tol) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SfmData {
|
class SfmData {
|
||||||
|
|
@ -2790,6 +2786,12 @@ class SfmData {
|
||||||
gtsam::SfmTrack track(size_t idx) const;
|
gtsam::SfmTrack track(size_t idx) const;
|
||||||
void add_track(const gtsam::SfmTrack& t) ;
|
void add_track(const gtsam::SfmTrack& t) ;
|
||||||
void add_camera(const gtsam::SfmCamera& cam);
|
void add_camera(const gtsam::SfmCamera& cam);
|
||||||
|
|
||||||
|
// enabling serialization functionality
|
||||||
|
void serialize() const;
|
||||||
|
|
||||||
|
// enabling function to compare objects
|
||||||
|
bool equals(const gtsam::SfmData& expected, double tol) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
gtsam::SfmData readBal(string filename);
|
gtsam::SfmData readBal(string filename);
|
||||||
|
|
@ -2933,9 +2935,14 @@ class ShonanAveragingParameters2 {
|
||||||
void setAnchorWeight(double value);
|
void setAnchorWeight(double value);
|
||||||
double getAnchorWeight() const;
|
double getAnchorWeight() const;
|
||||||
void setKarcherWeight(double value);
|
void setKarcherWeight(double value);
|
||||||
double getKarcherWeight();
|
double getKarcherWeight() const;
|
||||||
void setGaugesWeight(double value);
|
void setGaugesWeight(double value);
|
||||||
double getGaugesWeight();
|
double getGaugesWeight() const;
|
||||||
|
void setUseHuber(bool value);
|
||||||
|
bool getUseHuber() const;
|
||||||
|
void setCertifyOptimality(bool value);
|
||||||
|
bool getCertifyOptimality() const;
|
||||||
|
void print() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShonanAveragingParameters3 {
|
class ShonanAveragingParameters3 {
|
||||||
|
|
@ -2949,9 +2956,14 @@ class ShonanAveragingParameters3 {
|
||||||
void setAnchorWeight(double value);
|
void setAnchorWeight(double value);
|
||||||
double getAnchorWeight() const;
|
double getAnchorWeight() const;
|
||||||
void setKarcherWeight(double value);
|
void setKarcherWeight(double value);
|
||||||
double getKarcherWeight();
|
double getKarcherWeight() const;
|
||||||
void setGaugesWeight(double value);
|
void setGaugesWeight(double value);
|
||||||
double getGaugesWeight();
|
double getGaugesWeight() const;
|
||||||
|
void setUseHuber(bool value);
|
||||||
|
bool getUseHuber() const;
|
||||||
|
void setCertifyOptimality(bool value);
|
||||||
|
bool getCertifyOptimality() const;
|
||||||
|
void print() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShonanAveraging2 {
|
class ShonanAveraging2 {
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,9 @@ public:
|
||||||
/** Create a string from the key */
|
/** Create a string from the key */
|
||||||
operator std::string() const;
|
operator std::string() const;
|
||||||
|
|
||||||
|
/// Return string representation of the key
|
||||||
|
std::string string() const { return std::string(*this); };
|
||||||
|
|
||||||
/** Comparison for use in maps */
|
/** Comparison for use in maps */
|
||||||
bool operator<(const Symbol& comp) const {
|
bool operator<(const Symbol& comp) const {
|
||||||
return c_ < comp.c_ || (comp.c_ == c_ && j_ < comp.j_);
|
return c_ < comp.c_ || (comp.c_ == c_ && j_ < comp.j_);
|
||||||
|
|
@ -167,10 +170,11 @@ inline Key Z(std::uint64_t j) { return Symbol('z', j); }
|
||||||
/** Generates symbol shorthands with alternative names different than the
|
/** Generates symbol shorthands with alternative names different than the
|
||||||
* one-letter predefined ones. */
|
* one-letter predefined ones. */
|
||||||
class SymbolGenerator {
|
class SymbolGenerator {
|
||||||
const char c_;
|
const unsigned char c_;
|
||||||
public:
|
public:
|
||||||
SymbolGenerator(const char c) : c_(c) {}
|
constexpr SymbolGenerator(const unsigned char c) : c_(c) {}
|
||||||
Symbol operator()(const std::uint64_t j) const { return Symbol(c_, j); }
|
Symbol operator()(const std::uint64_t j) const { return Symbol(c_, j); }
|
||||||
|
constexpr unsigned char chr() const { return c_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// traits
|
/// traits
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,12 @@ TEST(Key, SymbolGenerator) {
|
||||||
EXPECT(assert_equal(a1, ddz1));
|
EXPECT(assert_equal(a1, ddz1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(Key, SymbolGeneratorConstexpr) {
|
||||||
|
constexpr auto Z = gtsam::SymbolGenerator('x');
|
||||||
|
EXPECT(assert_equal(Z.chr(), 'x'));
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template<int KeySize>
|
template<int KeySize>
|
||||||
Key KeyTestValue();
|
Key KeyTestValue();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,176 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010-2019, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file AcceleratedPowerMethod.h
|
||||||
|
* @date Sept 2020
|
||||||
|
* @author Jing Wu
|
||||||
|
* @brief accelerated power method for fast eigenvalue and eigenvector
|
||||||
|
* computation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/linear/PowerMethod.h>
|
||||||
|
|
||||||
|
namespace gtsam {
|
||||||
|
|
||||||
|
using Sparse = Eigen::SparseMatrix<double>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compute maximum Eigenpair with accelerated power method
|
||||||
|
*
|
||||||
|
* References :
|
||||||
|
* 1) G. Golub and C. V. Loan, Matrix Computations, 3rd ed. Baltimore, Johns
|
||||||
|
* Hopkins University Press, 1996, pp.405-411
|
||||||
|
* 2) Rosen, D. and Carlone, L., 2017, September. Computational
|
||||||
|
* enhancements for certifiably correct SLAM. In Proceedings of the
|
||||||
|
* International Conference on Intelligent Robots and Systems.
|
||||||
|
* 3) Yulun Tian and Kasra Khosoussi and David M. Rosen and Jonathan P. How,
|
||||||
|
* 2020, Aug, Distributed Certifiably Correct Pose-Graph Optimization, Arxiv
|
||||||
|
* 4) C. de Sa, B. He, I. Mitliagkas, C. Ré, and P. Xu, “Accelerated
|
||||||
|
* stochastic power iteration,” in Proc. Mach. Learn. Res., no. 84, 2018, pp.
|
||||||
|
* 58–67
|
||||||
|
*
|
||||||
|
* It performs the following iteration: \f$ x_{k+1} = A * x_k - \beta *
|
||||||
|
* x_{k-1} \f$ where A is the aim matrix we want to get eigenpair of, x is the
|
||||||
|
* Ritz vector
|
||||||
|
*
|
||||||
|
* Template argument Operator just needs multiplication operator
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <class Operator>
|
||||||
|
class AcceleratedPowerMethod : public PowerMethod<Operator> {
|
||||||
|
|
||||||
|
double beta_ = 0; // a Polyak momentum term
|
||||||
|
|
||||||
|
Vector previousVector_; // store previous vector
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor from aim matrix A (given as Matrix or Sparse), optional intial
|
||||||
|
* vector as ritzVector
|
||||||
|
*/
|
||||||
|
explicit AcceleratedPowerMethod(
|
||||||
|
const Operator &A, const boost::optional<Vector> initial = boost::none,
|
||||||
|
double initialBeta = 0.0)
|
||||||
|
: PowerMethod<Operator>(A, initial) {
|
||||||
|
// initialize Ritz eigen vector and previous vector
|
||||||
|
this->ritzVector_ = initial ? initial.get() : Vector::Random(this->dim_);
|
||||||
|
this->ritzVector_.normalize();
|
||||||
|
previousVector_ = Vector::Zero(this->dim_);
|
||||||
|
|
||||||
|
// initialize beta_
|
||||||
|
beta_ = initialBeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run accelerated power iteration to get ritzVector with beta and previous
|
||||||
|
* two ritzVector x0 and x00, and return y = (A * x0 - \beta * x00) / || A * x0
|
||||||
|
* - \beta * x00 ||
|
||||||
|
*/
|
||||||
|
Vector acceleratedPowerIteration (const Vector &x1, const Vector &x0,
|
||||||
|
const double beta) const {
|
||||||
|
Vector y = this->A_ * x1 - beta * x0;
|
||||||
|
y.normalize();
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run accelerated power iteration to get ritzVector with beta and previous
|
||||||
|
* two ritzVector x0 and x00, and return y = (A * x0 - \beta * x00) / || A * x0
|
||||||
|
* - \beta * x00 ||
|
||||||
|
*/
|
||||||
|
Vector acceleratedPowerIteration () const {
|
||||||
|
Vector y = acceleratedPowerIteration(this->ritzVector_, previousVector_, beta_);
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tuning the momentum beta using the Best Heavy Ball algorithm in Ref(3), T
|
||||||
|
* is the iteration time to find beta with largest Rayleigh quotient
|
||||||
|
*/
|
||||||
|
double estimateBeta(const size_t T = 10) const {
|
||||||
|
// set initial estimation of maxBeta
|
||||||
|
Vector initVector = this->ritzVector_;
|
||||||
|
const double up = initVector.dot( this->A_ * initVector );
|
||||||
|
const double down = initVector.dot(initVector);
|
||||||
|
const double mu = up / down;
|
||||||
|
double maxBeta = mu * mu / 4;
|
||||||
|
size_t maxIndex;
|
||||||
|
std::vector<double> betas;
|
||||||
|
|
||||||
|
Matrix R = Matrix::Zero(this->dim_, 10);
|
||||||
|
// run T times of iteration to find the beta that has the largest Rayleigh quotient
|
||||||
|
for (size_t t = 0; t < T; t++) {
|
||||||
|
// after each t iteration, reset the betas with the current maxBeta
|
||||||
|
betas = {2 / 3 * maxBeta, 0.99 * maxBeta, maxBeta, 1.01 * maxBeta,
|
||||||
|
1.5 * maxBeta};
|
||||||
|
// iterate through every beta value
|
||||||
|
for (size_t k = 0; k < betas.size(); ++k) {
|
||||||
|
// initialize x0 and x00 in each iteration of each beta
|
||||||
|
Vector x0 = initVector;
|
||||||
|
Vector x00 = Vector::Zero(this->dim_);
|
||||||
|
// run 10 steps of accelerated power iteration with this beta
|
||||||
|
for (size_t j = 1; j < 10; j++) {
|
||||||
|
if (j < 2) {
|
||||||
|
R.col(0) = acceleratedPowerIteration(x0, x00, betas[k]);
|
||||||
|
R.col(1) = acceleratedPowerIteration(R.col(0), x0, betas[k]);
|
||||||
|
} else {
|
||||||
|
R.col(j) = acceleratedPowerIteration(R.col(j - 1), R.col(j - 2),
|
||||||
|
betas[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// compute the Rayleigh quotient for the randomly sampled vector after
|
||||||
|
// 10 steps of power accelerated iteration
|
||||||
|
const Vector x = R.col(9);
|
||||||
|
const double up = x.dot(this->A_ * x);
|
||||||
|
const double down = x.dot(x);
|
||||||
|
const double mu = up / down;
|
||||||
|
// store the momentum with largest Rayleigh quotient and its according index of beta_
|
||||||
|
if (mu * mu / 4 > maxBeta) {
|
||||||
|
// save the max beta index
|
||||||
|
maxIndex = k;
|
||||||
|
maxBeta = mu * mu / 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set beta_ to momentum with largest Rayleigh quotient
|
||||||
|
return betas[maxIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the accelerated iteration, after performing the
|
||||||
|
* accelerated iteration, calculate the ritz error, repeat this
|
||||||
|
* operation until the ritz error converge. If converged return true, else
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
bool compute(size_t maxIterations, double tol) {
|
||||||
|
// Starting
|
||||||
|
bool isConverged = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < maxIterations && !isConverged; i++) {
|
||||||
|
++(this->nrIterations_);
|
||||||
|
Vector tmp = this->ritzVector_;
|
||||||
|
// update the ritzVector after accelerated power iteration
|
||||||
|
this->ritzVector_ = acceleratedPowerIteration();
|
||||||
|
// update the previousVector with ritzVector
|
||||||
|
previousVector_ = tmp;
|
||||||
|
// update the ritzValue
|
||||||
|
this->ritzValue_ = this->ritzVector_.dot(this->A_ * this->ritzVector_);
|
||||||
|
isConverged = this->converged(tol);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isConverged;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gtsam
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010-2019, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file PowerMethod.h
|
||||||
|
* @date Sept 2020
|
||||||
|
* @author Jing Wu
|
||||||
|
* @brief Power method for fast eigenvalue and eigenvector
|
||||||
|
* computation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/base/Matrix.h>
|
||||||
|
#include <gtsam/base/Vector.h>
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/Sparse>
|
||||||
|
#include <random>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace gtsam {
|
||||||
|
|
||||||
|
using Sparse = Eigen::SparseMatrix<double>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compute maximum Eigenpair with power method
|
||||||
|
*
|
||||||
|
* References :
|
||||||
|
* 1) G. Golub and C. V. Loan, Matrix Computations, 3rd ed. Baltimore, Johns
|
||||||
|
* Hopkins University Press, 1996, pp.405-411
|
||||||
|
* 2) Rosen, D. and Carlone, L., 2017, September. Computational
|
||||||
|
* enhancements for certifiably correct SLAM. In Proceedings of the
|
||||||
|
* International Conference on Intelligent Robots and Systems.
|
||||||
|
* 3) Yulun Tian and Kasra Khosoussi and David M. Rosen and Jonathan P. How,
|
||||||
|
* 2020, Aug, Distributed Certifiably Correct Pose-Graph Optimization, Arxiv
|
||||||
|
* 4) C. de Sa, B. He, I. Mitliagkas, C. Ré, and P. Xu, “Accelerated
|
||||||
|
* stochastic power iteration,” in Proc. Mach. Learn. Res., no. 84, 2018, pp.
|
||||||
|
* 58–67
|
||||||
|
*
|
||||||
|
* It performs the following iteration: \f$ x_{k+1} = A * x_k \f$
|
||||||
|
* where A is the aim matrix we want to get eigenpair of, x is the
|
||||||
|
* Ritz vector
|
||||||
|
*
|
||||||
|
* Template argument Operator just needs multiplication operator
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <class Operator>
|
||||||
|
class PowerMethod {
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Const reference to an externally-held matrix whose minimum-eigenvalue we
|
||||||
|
* want to compute
|
||||||
|
*/
|
||||||
|
const Operator &A_;
|
||||||
|
|
||||||
|
const int dim_; // dimension of Matrix A
|
||||||
|
|
||||||
|
size_t nrIterations_; // number of iterations
|
||||||
|
|
||||||
|
double ritzValue_; // Ritz eigenvalue
|
||||||
|
Vector ritzVector_; // Ritz eigenvector
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// @name Standard Constructors
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// Construct from the aim matrix and intial ritz vector
|
||||||
|
explicit PowerMethod(const Operator &A,
|
||||||
|
const boost::optional<Vector> initial = boost::none)
|
||||||
|
: A_(A), dim_(A.rows()), nrIterations_(0) {
|
||||||
|
Vector x0;
|
||||||
|
x0 = initial ? initial.get() : Vector::Random(dim_);
|
||||||
|
x0.normalize();
|
||||||
|
|
||||||
|
// initialize Ritz eigen value
|
||||||
|
ritzValue_ = 0.0;
|
||||||
|
|
||||||
|
// initialize Ritz eigen vector
|
||||||
|
ritzVector_ = powerIteration(x0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run power iteration to get ritzVector with previous ritzVector x, and
|
||||||
|
* return A * x / || A * x ||
|
||||||
|
*/
|
||||||
|
Vector powerIteration(const Vector &x) const {
|
||||||
|
Vector y = A_ * x;
|
||||||
|
y.normalize();
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run power iteration to get ritzVector with previous ritzVector x, and
|
||||||
|
* return A * x / || A * x ||
|
||||||
|
*/
|
||||||
|
Vector powerIteration() const { return powerIteration(ritzVector_); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After Perform power iteration on a single Ritz value, check if the Ritz
|
||||||
|
* residual for the current Ritz pair is less than the required convergence
|
||||||
|
* tol, return true if yes, else false
|
||||||
|
*/
|
||||||
|
bool converged(double tol) const {
|
||||||
|
const Vector x = ritzVector_;
|
||||||
|
// store the Ritz eigen value
|
||||||
|
const double ritzValue = x.dot(A_ * x);
|
||||||
|
const double error = (A_ * x - ritzValue * x).norm();
|
||||||
|
return error < tol;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the number of iterations
|
||||||
|
size_t nrIterations() const { return nrIterations_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the power/accelerated iteration, after performing the
|
||||||
|
* power/accelerated iteration, calculate the ritz error, repeat this
|
||||||
|
* operation until the ritz error converge. If converged return true, else
|
||||||
|
* false.
|
||||||
|
*/
|
||||||
|
bool compute(size_t maxIterations, double tol) {
|
||||||
|
// Starting
|
||||||
|
bool isConverged = false;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < maxIterations && !isConverged; i++) {
|
||||||
|
++nrIterations_;
|
||||||
|
// update the ritzVector after power iteration
|
||||||
|
ritzVector_ = powerIteration();
|
||||||
|
// update the ritzValue
|
||||||
|
ritzValue_ = ritzVector_.dot(A_ * ritzVector_);
|
||||||
|
isConverged = converged(tol);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isConverged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the eigenvalue
|
||||||
|
double eigenvalue() const { return ritzValue_; }
|
||||||
|
|
||||||
|
/// Return the eigenvector
|
||||||
|
Vector eigenvector() const { return ritzVector_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gtsam
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file SparseEigen.h
|
||||||
|
*
|
||||||
|
* @brief Utilities for creating Eigen sparse matrices (gtsam::SparseEigen)
|
||||||
|
*
|
||||||
|
* @date Aug 2019
|
||||||
|
* @author Mandy Xie
|
||||||
|
* @author Fan Jiang
|
||||||
|
* @author Gerry Chen
|
||||||
|
* @author Frank Dellaert
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/linear/GaussianFactorGraph.h>
|
||||||
|
#include <gtsam/linear/VectorValues.h>
|
||||||
|
|
||||||
|
#include <Eigen/Sparse>
|
||||||
|
|
||||||
|
namespace gtsam {
|
||||||
|
|
||||||
|
typedef Eigen::SparseMatrix<double> SparseEigen;
|
||||||
|
|
||||||
|
/// Constructs an Eigen-format SparseMatrix of a GaussianFactorGraph
|
||||||
|
SparseEigen sparseJacobianEigen(
|
||||||
|
const GaussianFactorGraph &gfg, const Ordering &ordering) {
|
||||||
|
// TODO(gerry): eliminate copy/pasta by making GaussianFactorGraph version
|
||||||
|
// more general, or by creating an Eigen::Triplet compatible wrapper for
|
||||||
|
// boost::tuple return type
|
||||||
|
|
||||||
|
// First find dimensions of each variable
|
||||||
|
std::map<Key, size_t> dims;
|
||||||
|
for (const boost::shared_ptr<GaussianFactor> &factor : gfg) {
|
||||||
|
if (!static_cast<bool>(factor)) continue;
|
||||||
|
|
||||||
|
for (auto it = factor->begin(); it != factor->end(); ++it) {
|
||||||
|
dims[*it] = factor->getDim(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute first scalar column of each variable
|
||||||
|
size_t currentColIndex = 0;
|
||||||
|
std::map<Key, size_t> columnIndices;
|
||||||
|
for (const auto key : ordering) {
|
||||||
|
columnIndices[key] = currentColIndex;
|
||||||
|
currentColIndex += dims[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all factors, adding sparse scalar entries
|
||||||
|
std::vector<Eigen::Triplet<double>> entries;
|
||||||
|
entries.reserve(60 * gfg.size());
|
||||||
|
|
||||||
|
size_t row = 0;
|
||||||
|
for (const boost::shared_ptr<GaussianFactor> &factor : gfg) {
|
||||||
|
if (!static_cast<bool>(factor)) continue;
|
||||||
|
|
||||||
|
// Convert to JacobianFactor if necessary
|
||||||
|
JacobianFactor::shared_ptr jacobianFactor(
|
||||||
|
boost::dynamic_pointer_cast<JacobianFactor>(factor));
|
||||||
|
if (!jacobianFactor) {
|
||||||
|
HessianFactor::shared_ptr hessian(
|
||||||
|
boost::dynamic_pointer_cast<HessianFactor>(factor));
|
||||||
|
if (hessian)
|
||||||
|
jacobianFactor.reset(new JacobianFactor(*hessian));
|
||||||
|
else
|
||||||
|
throw std::invalid_argument(
|
||||||
|
"GaussianFactorGraph contains a factor that is neither a "
|
||||||
|
"JacobianFactor nor a HessianFactor.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whiten the factor and add entries for it
|
||||||
|
// iterate over all variables in the factor
|
||||||
|
const JacobianFactor whitened(jacobianFactor->whiten());
|
||||||
|
for (JacobianFactor::const_iterator key = whitened.begin();
|
||||||
|
key < whitened.end(); ++key) {
|
||||||
|
JacobianFactor::constABlock whitenedA = whitened.getA(key);
|
||||||
|
// find first column index for this key
|
||||||
|
size_t column_start = columnIndices[*key];
|
||||||
|
for (size_t i = 0; i < (size_t)whitenedA.rows(); i++)
|
||||||
|
for (size_t j = 0; j < (size_t)whitenedA.cols(); j++) {
|
||||||
|
double s = whitenedA(i, j);
|
||||||
|
if (std::abs(s) > 1e-12)
|
||||||
|
entries.emplace_back(row + i, column_start + j, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JacobianFactor::constBVector whitenedb(whitened.getb());
|
||||||
|
size_t bcolumn = currentColIndex;
|
||||||
|
for (size_t i = 0; i < (size_t)whitenedb.size(); i++) {
|
||||||
|
double s = whitenedb(i);
|
||||||
|
if (std::abs(s) > 1e-12) entries.emplace_back(row + i, bcolumn, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment row index
|
||||||
|
row += jacobianFactor->rows();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and make a sparse matrix with it.
|
||||||
|
SparseEigen Ab(row, currentColIndex + 1);
|
||||||
|
Ab.setFromTriplets(entries.begin(), entries.end());
|
||||||
|
return Ab;
|
||||||
|
}
|
||||||
|
|
||||||
|
SparseEigen sparseJacobianEigen(const GaussianFactorGraph &gfg) {
|
||||||
|
return sparseJacobianEigen(gfg, Ordering(gfg.keys()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace gtsam
|
||||||
|
|
@ -383,7 +383,7 @@ Subgraph SubgraphBuilder::operator()(const GaussianFactorGraph &gfg) const {
|
||||||
const vector<size_t> tree = buildTree(gfg, forward_ordering, weights);
|
const vector<size_t> tree = buildTree(gfg, forward_ordering, weights);
|
||||||
if (tree.size() != n - 1) {
|
if (tree.size() != n - 1) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"SubgraphBuilder::operator() failure: tree.size() != n-1");
|
"SubgraphBuilder::operator() failure: tree.size() != n-1, might be caused by disconnected graph");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Downweight the tree edges to zero.
|
// Downweight the tree edges to zero.
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ namespace gtsam {
|
||||||
bool VectorValues::equals(const VectorValues& x, double tol) const {
|
bool VectorValues::equals(const VectorValues& x, double tol) const {
|
||||||
if(this->size() != x.size())
|
if(this->size() != x.size())
|
||||||
return false;
|
return false;
|
||||||
for(const auto& values: boost::combine(*this, x)) {
|
for(const auto values: boost::combine(*this, x)) {
|
||||||
if(values.get<0>().first != values.get<1>().first ||
|
if(values.get<0>().first != values.get<1>().first ||
|
||||||
!equal_with_abs_tol(values.get<0>().second, values.get<1>().second, tol))
|
!equal_with_abs_tol(values.get<0>().second, values.get<1>().second, tol))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -233,7 +233,7 @@ namespace gtsam {
|
||||||
double result = 0.0;
|
double result = 0.0;
|
||||||
typedef boost::tuple<value_type, value_type> ValuePair;
|
typedef boost::tuple<value_type, value_type> ValuePair;
|
||||||
using boost::adaptors::map_values;
|
using boost::adaptors::map_values;
|
||||||
for(const ValuePair& values: boost::combine(*this, v)) {
|
for(const ValuePair values: boost::combine(*this, v)) {
|
||||||
assert_throw(values.get<0>().first == values.get<1>().first,
|
assert_throw(values.get<0>().first == values.get<1>().first,
|
||||||
invalid_argument("VectorValues::dot called with a VectorValues of different structure"));
|
invalid_argument("VectorValues::dot called with a VectorValues of different structure"));
|
||||||
assert_throw(values.get<0>().second.size() == values.get<1>().second.size(),
|
assert_throw(values.get<0>().second.size() == values.get<1>().second.size(),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010-2019, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* powerMethodExample.h
|
||||||
|
*
|
||||||
|
* @file powerMethodExample.h
|
||||||
|
* @date Nov 2020
|
||||||
|
* @author Jing Wu
|
||||||
|
* @brief Create sparse and dense factor graph for
|
||||||
|
* PowerMethod/AcceleratedPowerMethod
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtsam/inference/Symbol.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
namespace gtsam {
|
||||||
|
namespace linear {
|
||||||
|
namespace test {
|
||||||
|
namespace example {
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
inline GaussianFactorGraph createSparseGraph() {
|
||||||
|
using symbol_shorthand::X;
|
||||||
|
// Let's make a scalar synchronization graph with 4 nodes
|
||||||
|
GaussianFactorGraph fg;
|
||||||
|
auto model = noiseModel::Unit::Create(1);
|
||||||
|
for (size_t j = 0; j < 3; j++) {
|
||||||
|
fg.add(X(j), -I_1x1, X(j + 1), I_1x1, Vector1::Zero(), model);
|
||||||
|
}
|
||||||
|
fg.add(X(3), -I_1x1, X(0), I_1x1, Vector1::Zero(), model); // extra row
|
||||||
|
|
||||||
|
return fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
inline GaussianFactorGraph createDenseGraph() {
|
||||||
|
using symbol_shorthand::X;
|
||||||
|
// Let's make a scalar synchronization graph with 10 nodes
|
||||||
|
GaussianFactorGraph fg;
|
||||||
|
auto model = noiseModel::Unit::Create(1);
|
||||||
|
// Iterate over nodes
|
||||||
|
for (size_t j = 0; j < 10; j++) {
|
||||||
|
// Each node has an edge with all the others
|
||||||
|
for (size_t i = 1; i < 10; i++)
|
||||||
|
fg.add(X(j), -I_1x1, X((j + i) % 10), I_1x1, Vector1::Zero(), model);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
} // namespace example
|
||||||
|
} // namespace test
|
||||||
|
} // namespace linear
|
||||||
|
} // namespace gtsam
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010-2019, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* testPowerMethod.cpp
|
||||||
|
*
|
||||||
|
* @file testAcceleratedPowerMethod.cpp
|
||||||
|
* @date Sept 2020
|
||||||
|
* @author Jing Wu
|
||||||
|
* @brief Check eigenvalue and eigenvector computed by accelerated power method
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <CppUnitLite/TestHarness.h>
|
||||||
|
#include <gtsam/base/Matrix.h>
|
||||||
|
#include <gtsam/base/VectorSpace.h>
|
||||||
|
#include <gtsam/inference/Symbol.h>
|
||||||
|
#include <gtsam/linear/AcceleratedPowerMethod.h>
|
||||||
|
#include <gtsam/linear/GaussianFactorGraph.h>
|
||||||
|
#include <gtsam/linear/tests/powerMethodExample.h>
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
#include <Eigen/Eigenvalues>
|
||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace gtsam;
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(AcceleratedPowerMethod, acceleratedPowerIteration) {
|
||||||
|
// test power iteration, beta is set to 0
|
||||||
|
Sparse A(6, 6);
|
||||||
|
A.coeffRef(0, 0) = 6;
|
||||||
|
A.coeffRef(1, 1) = 5;
|
||||||
|
A.coeffRef(2, 2) = 4;
|
||||||
|
A.coeffRef(3, 3) = 3;
|
||||||
|
A.coeffRef(4, 4) = 2;
|
||||||
|
A.coeffRef(5, 5) = 1;
|
||||||
|
Vector initial = (Vector(6) << 0.24434602, 0.22829942, 0.70094486, 0.15463092, 0.55871359,
|
||||||
|
0.2465342).finished();
|
||||||
|
const double ev1 = 6.0;
|
||||||
|
|
||||||
|
// test accelerated power iteration
|
||||||
|
AcceleratedPowerMethod<Sparse> apf(A, initial);
|
||||||
|
apf.compute(100, 1e-5);
|
||||||
|
EXPECT_LONGS_EQUAL(6, apf.eigenvector().rows());
|
||||||
|
|
||||||
|
Vector6 actual1 = apf.eigenvector();
|
||||||
|
const double ritzValue = actual1.dot(A * actual1);
|
||||||
|
const double ritzResidual = (A * actual1 - ritzValue * actual1).norm();
|
||||||
|
EXPECT_DOUBLES_EQUAL(0, ritzResidual, 1e-5);
|
||||||
|
|
||||||
|
EXPECT_DOUBLES_EQUAL(ev1, apf.eigenvalue(), 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(AcceleratedPowerMethod, useFactorGraphSparse) {
|
||||||
|
// Let's make a scalar synchronization graph with 4 nodes
|
||||||
|
GaussianFactorGraph fg = gtsam::linear::test::example::createSparseGraph();
|
||||||
|
|
||||||
|
// Get eigenvalues and eigenvectors with Eigen
|
||||||
|
auto L = fg.hessian();
|
||||||
|
Eigen::EigenSolver<Matrix> solver(L.first);
|
||||||
|
|
||||||
|
// find the index of the max eigenvalue
|
||||||
|
size_t maxIdx = 0;
|
||||||
|
for (auto i = 0; i < solver.eigenvalues().rows(); ++i) {
|
||||||
|
if (solver.eigenvalues()(i).real() >= solver.eigenvalues()(maxIdx).real())
|
||||||
|
maxIdx = i;
|
||||||
|
}
|
||||||
|
// Store the max eigenvalue and its according eigenvector
|
||||||
|
const auto ev1 = solver.eigenvalues()(maxIdx).real();
|
||||||
|
|
||||||
|
Vector disturb = Vector4::Random();
|
||||||
|
disturb.normalize();
|
||||||
|
Vector initial = L.first.row(0);
|
||||||
|
double magnitude = initial.norm();
|
||||||
|
initial += 0.03 * magnitude * disturb;
|
||||||
|
AcceleratedPowerMethod<Matrix> apf(L.first, initial);
|
||||||
|
apf.compute(100, 1e-5);
|
||||||
|
// Check if the eigenvalue is the maximum eigen value
|
||||||
|
EXPECT_DOUBLES_EQUAL(ev1, apf.eigenvalue(), 1e-8);
|
||||||
|
|
||||||
|
// Check if the according ritz residual converged to the threshold
|
||||||
|
Vector actual1 = apf.eigenvector();
|
||||||
|
const double ritzValue = actual1.dot(L.first * actual1);
|
||||||
|
const double ritzResidual = (L.first * actual1 - ritzValue * actual1).norm();
|
||||||
|
EXPECT_DOUBLES_EQUAL(0, ritzResidual, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(AcceleratedPowerMethod, useFactorGraphDense) {
|
||||||
|
// Let's make a scalar synchronization graph with 10 nodes
|
||||||
|
GaussianFactorGraph fg = gtsam::linear::test::example::createDenseGraph();
|
||||||
|
|
||||||
|
// Get eigenvalues and eigenvectors with Eigen
|
||||||
|
auto L = fg.hessian();
|
||||||
|
Eigen::EigenSolver<Matrix> solver(L.first);
|
||||||
|
|
||||||
|
// find the index of the max eigenvalue
|
||||||
|
size_t maxIdx = 0;
|
||||||
|
for (auto i = 0; i < solver.eigenvalues().rows(); ++i) {
|
||||||
|
if (solver.eigenvalues()(i).real() >= solver.eigenvalues()(maxIdx).real())
|
||||||
|
maxIdx = i;
|
||||||
|
}
|
||||||
|
// Store the max eigenvalue and its according eigenvector
|
||||||
|
const auto ev1 = solver.eigenvalues()(maxIdx).real();
|
||||||
|
|
||||||
|
Vector disturb = Vector10::Random();
|
||||||
|
disturb.normalize();
|
||||||
|
Vector initial = L.first.row(0);
|
||||||
|
double magnitude = initial.norm();
|
||||||
|
initial += 0.03 * magnitude * disturb;
|
||||||
|
AcceleratedPowerMethod<Matrix> apf(L.first, initial);
|
||||||
|
apf.compute(100, 1e-5);
|
||||||
|
// Check if the eigenvalue is the maximum eigen value
|
||||||
|
EXPECT_DOUBLES_EQUAL(ev1, apf.eigenvalue(), 1e-8);
|
||||||
|
|
||||||
|
// Check if the according ritz residual converged to the threshold
|
||||||
|
Vector actual1 = apf.eigenvector();
|
||||||
|
const double ritzValue = actual1.dot(L.first * actual1);
|
||||||
|
const double ritzResidual = (L.first * actual1 - ritzValue * actual1).norm();
|
||||||
|
EXPECT_DOUBLES_EQUAL(0, ritzResidual, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
int main() {
|
||||||
|
TestResult tr;
|
||||||
|
return TestRegistry::runAllTests(tr);
|
||||||
|
}
|
||||||
|
/* ************************************************************************* */
|
||||||
|
|
@ -36,9 +36,18 @@ using namespace boost::assign;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace gtsam;
|
using namespace gtsam;
|
||||||
|
|
||||||
// static SharedDiagonal
|
typedef boost::tuple<size_t, size_t, double> BoostTriplet;
|
||||||
// sigma0_1 = noiseModel::Isotropic::Sigma(2,0.1), sigma_02 = noiseModel::Isotropic::Sigma(2,0.2),
|
bool triplet_equal(BoostTriplet a, BoostTriplet b) {
|
||||||
// constraintModel = noiseModel::Constrained::All(2);
|
if (a.get<0>() == b.get<0>() && a.get<1>() == b.get<1>() &&
|
||||||
|
a.get<2>() == b.get<2>()) return true;
|
||||||
|
|
||||||
|
cout << "not equal:" << endl;
|
||||||
|
cout << "\texpected: "
|
||||||
|
"(" << a.get<0>() << ", " << a.get<1>() << ") = " << a.get<2>() << endl;
|
||||||
|
cout << "\tactual: "
|
||||||
|
"(" << b.get<0>() << ", " << b.get<1>() << ") = " << b.get<2>() << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
TEST(GaussianFactorGraph, initialization) {
|
TEST(GaussianFactorGraph, initialization) {
|
||||||
|
|
@ -74,8 +83,8 @@ TEST(GaussianFactorGraph, sparseJacobian) {
|
||||||
// 9 10 0 11 12 13
|
// 9 10 0 11 12 13
|
||||||
// 0 0 0 14 15 16
|
// 0 0 0 14 15 16
|
||||||
|
|
||||||
// Expected - NOTE that we transpose this!
|
// Expected
|
||||||
Matrix expectedT = (Matrix(16, 3) <<
|
Matrix expected = (Matrix(16, 3) <<
|
||||||
1., 1., 2.,
|
1., 1., 2.,
|
||||||
1., 2., 4.,
|
1., 2., 4.,
|
||||||
1., 3., 6.,
|
1., 3., 6.,
|
||||||
|
|
@ -93,17 +102,32 @@ TEST(GaussianFactorGraph, sparseJacobian) {
|
||||||
3., 6.,26.,
|
3., 6.,26.,
|
||||||
4., 6.,32.).finished();
|
4., 6.,32.).finished();
|
||||||
|
|
||||||
Matrix expected = expectedT.transpose();
|
// expected: in matlab format - NOTE the transpose!)
|
||||||
|
Matrix expectedMatlab = expected.transpose();
|
||||||
|
|
||||||
GaussianFactorGraph gfg;
|
GaussianFactorGraph gfg;
|
||||||
SharedDiagonal model = noiseModel::Isotropic::Sigma(2, 0.5);
|
SharedDiagonal model = noiseModel::Isotropic::Sigma(2, 0.5);
|
||||||
gfg.add(0, (Matrix(2, 3) << 1., 2., 3., 5., 6., 7.).finished(), Vector2(4., 8.), model);
|
const Key x123 = 0, x45 = 1;
|
||||||
gfg.add(0, (Matrix(2, 3) << 9., 10., 0., 0., 0., 0.).finished(), 1,
|
gfg.add(x123, (Matrix(2, 3) << 1, 2, 3, 5, 6, 7).finished(),
|
||||||
(Matrix(2, 2) << 11., 12., 14., 15.).finished(), Vector2(13., 16.), model);
|
Vector2(4, 8), model);
|
||||||
|
gfg.add(x123, (Matrix(2, 3) << 9, 10, 0, 0, 0, 0).finished(),
|
||||||
|
x45, (Matrix(2, 2) << 11, 12, 14, 15.).finished(),
|
||||||
|
Vector2(13, 16), model);
|
||||||
|
|
||||||
Matrix actual = gfg.sparseJacobian_();
|
Matrix actual = gfg.sparseJacobian_();
|
||||||
|
|
||||||
EXPECT(assert_equal(expected, actual));
|
EXPECT(assert_equal(expectedMatlab, actual));
|
||||||
|
|
||||||
|
// BoostTriplets
|
||||||
|
auto boostActual = gfg.sparseJacobian();
|
||||||
|
// check the triplets size...
|
||||||
|
EXPECT_LONGS_EQUAL(16, boostActual.size());
|
||||||
|
// check content
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
EXPECT(triplet_equal(
|
||||||
|
BoostTriplet(expected(i, 0) - 1, expected(i, 1) - 1, expected(i, 2)),
|
||||||
|
boostActual.at(i)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010-2019, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* testPowerMethod.cpp
|
||||||
|
*
|
||||||
|
* @file testPowerMethod.cpp
|
||||||
|
* @date Sept 2020
|
||||||
|
* @author Jing Wu
|
||||||
|
* @brief Check eigenvalue and eigenvector computed by power method
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <CppUnitLite/TestHarness.h>
|
||||||
|
#include <gtsam/base/Matrix.h>
|
||||||
|
#include <gtsam/base/VectorSpace.h>
|
||||||
|
#include <gtsam/inference/Symbol.h>
|
||||||
|
#include <gtsam/linear/GaussianFactorGraph.h>
|
||||||
|
#include <gtsam/linear/PowerMethod.h>
|
||||||
|
#include <gtsam/linear/tests/powerMethodExample.h>
|
||||||
|
|
||||||
|
#include <Eigen/Core>
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
#include <Eigen/Eigenvalues>
|
||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace gtsam;
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(PowerMethod, powerIteration) {
|
||||||
|
// test power iteration, beta is set to 0
|
||||||
|
Sparse A(6, 6);
|
||||||
|
A.coeffRef(0, 0) = 6;
|
||||||
|
A.coeffRef(1, 1) = 5;
|
||||||
|
A.coeffRef(2, 2) = 4;
|
||||||
|
A.coeffRef(3, 3) = 3;
|
||||||
|
A.coeffRef(4, 4) = 2;
|
||||||
|
A.coeffRef(5, 5) = 1;
|
||||||
|
Vector initial = (Vector(6) << 0.24434602, 0.22829942, 0.70094486, 0.15463092, 0.55871359,
|
||||||
|
0.2465342).finished();
|
||||||
|
PowerMethod<Sparse> pf(A, initial);
|
||||||
|
pf.compute(100, 1e-5);
|
||||||
|
EXPECT_LONGS_EQUAL(6, pf.eigenvector().rows());
|
||||||
|
|
||||||
|
Vector6 actual1 = pf.eigenvector();
|
||||||
|
const double ritzValue = actual1.dot(A * actual1);
|
||||||
|
const double ritzResidual = (A * actual1 - ritzValue * actual1).norm();
|
||||||
|
EXPECT_DOUBLES_EQUAL(0, ritzResidual, 1e-5);
|
||||||
|
|
||||||
|
const double ev1 = 6.0;
|
||||||
|
EXPECT_DOUBLES_EQUAL(ev1, pf.eigenvalue(), 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(PowerMethod, useFactorGraphSparse) {
|
||||||
|
// Let's make a scalar synchronization graph with 4 nodes
|
||||||
|
GaussianFactorGraph fg = gtsam::linear::test::example::createSparseGraph();
|
||||||
|
|
||||||
|
// Get eigenvalues and eigenvectors with Eigen
|
||||||
|
auto L = fg.hessian();
|
||||||
|
Eigen::EigenSolver<Matrix> solver(L.first);
|
||||||
|
|
||||||
|
// find the index of the max eigenvalue
|
||||||
|
size_t maxIdx = 0;
|
||||||
|
for (auto i = 0; i < solver.eigenvalues().rows(); ++i) {
|
||||||
|
if (solver.eigenvalues()(i).real() >= solver.eigenvalues()(maxIdx).real())
|
||||||
|
maxIdx = i;
|
||||||
|
}
|
||||||
|
// Store the max eigenvalue and its according eigenvector
|
||||||
|
const auto ev1 = solver.eigenvalues()(maxIdx).real();
|
||||||
|
|
||||||
|
Vector initial = Vector4::Random();
|
||||||
|
PowerMethod<Matrix> pf(L.first, initial);
|
||||||
|
pf.compute(100, 1e-5);
|
||||||
|
EXPECT_DOUBLES_EQUAL(ev1, pf.eigenvalue(), 1e-8);
|
||||||
|
auto actual2 = pf.eigenvector();
|
||||||
|
const double ritzValue = actual2.dot(L.first * actual2);
|
||||||
|
const double ritzResidual = (L.first * actual2 - ritzValue * actual2).norm();
|
||||||
|
EXPECT_DOUBLES_EQUAL(0, ritzResidual, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(PowerMethod, useFactorGraphDense) {
|
||||||
|
// Let's make a scalar synchronization graph with 10 nodes
|
||||||
|
GaussianFactorGraph fg = gtsam::linear::test::example::createDenseGraph();
|
||||||
|
|
||||||
|
// Get eigenvalues and eigenvectors with Eigen
|
||||||
|
auto L = fg.hessian();
|
||||||
|
Eigen::EigenSolver<Matrix> solver(L.first);
|
||||||
|
|
||||||
|
// find the index of the max eigenvalue
|
||||||
|
size_t maxIdx = 0;
|
||||||
|
for (auto i = 0; i < solver.eigenvalues().rows(); ++i) {
|
||||||
|
if (solver.eigenvalues()(i).real() >= solver.eigenvalues()(maxIdx).real())
|
||||||
|
maxIdx = i;
|
||||||
|
}
|
||||||
|
// Store the max eigenvalue and its according eigenvector
|
||||||
|
const auto ev1 = solver.eigenvalues()(maxIdx).real();
|
||||||
|
|
||||||
|
Vector initial = Vector10::Random();
|
||||||
|
PowerMethod<Matrix> pf(L.first, initial);
|
||||||
|
pf.compute(100, 1e-5);
|
||||||
|
EXPECT_DOUBLES_EQUAL(ev1, pf.eigenvalue(), 1e-8);
|
||||||
|
auto actual2 = pf.eigenvector();
|
||||||
|
const double ritzValue = actual2.dot(L.first * actual2);
|
||||||
|
const double ritzResidual = (L.first * actual2 - ritzValue * actual2).norm();
|
||||||
|
EXPECT_DOUBLES_EQUAL(0, ritzResidual, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
int main() {
|
||||||
|
TestResult tr;
|
||||||
|
return TestRegistry::runAllTests(tr);
|
||||||
|
}
|
||||||
|
/* ************************************************************************* */
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file testSparseMatrix.cpp
|
||||||
|
* @author Mandy Xie
|
||||||
|
* @author Fan Jiang
|
||||||
|
* @author Gerry Chen
|
||||||
|
* @author Frank Dellaert
|
||||||
|
* @date Jan, 2021
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gtsam/linear/GaussianFactorGraph.h>
|
||||||
|
#include <gtsam/linear/SparseEigen.h>
|
||||||
|
|
||||||
|
#include <boost/assign/list_of.hpp>
|
||||||
|
using boost::assign::list_of;
|
||||||
|
|
||||||
|
#include <gtsam/base/TestableAssertions.h>
|
||||||
|
#include <CppUnitLite/TestHarness.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace gtsam;
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
TEST(SparseEigen, sparseJacobianEigen) {
|
||||||
|
GaussianFactorGraph gfg;
|
||||||
|
SharedDiagonal model = noiseModel::Isotropic::Sigma(2, 0.5);
|
||||||
|
const Key x123 = 0, x45 = 1;
|
||||||
|
gfg.add(x123, (Matrix(2, 3) << 1, 2, 3, 5, 6, 7).finished(),
|
||||||
|
Vector2(4, 8), model);
|
||||||
|
gfg.add(x123, (Matrix(2, 3) << 9, 10, 0, 0, 0, 0).finished(),
|
||||||
|
x45, (Matrix(2, 2) << 11, 12, 14, 15.).finished(),
|
||||||
|
Vector2(13, 16), model);
|
||||||
|
|
||||||
|
// Sparse Matrix
|
||||||
|
auto sparseResult = sparseJacobianEigen(gfg);
|
||||||
|
EXPECT_LONGS_EQUAL(16, sparseResult.nonZeros());
|
||||||
|
EXPECT(assert_equal(4, sparseResult.rows()));
|
||||||
|
EXPECT(assert_equal(6, sparseResult.cols()));
|
||||||
|
EXPECT(assert_equal(gfg.augmentedJacobian(), Matrix(sparseResult)));
|
||||||
|
|
||||||
|
// Call sparseJacobian with optional ordering...
|
||||||
|
auto ordering = Ordering(list_of(x45)(x123));
|
||||||
|
|
||||||
|
// Eigen Sparse with optional ordering
|
||||||
|
EXPECT(assert_equal(gfg.augmentedJacobian(ordering),
|
||||||
|
Matrix(sparseJacobianEigen(gfg, ordering))));
|
||||||
|
|
||||||
|
// Check matrix dimensions when zero rows / cols
|
||||||
|
gfg.add(x123, Matrix23::Zero(), Vector2::Zero(), model); // zero row
|
||||||
|
gfg.add(2, Matrix21::Zero(), Vector2::Zero(), model); // zero col
|
||||||
|
sparseResult = sparseJacobianEigen(gfg);
|
||||||
|
EXPECT_LONGS_EQUAL(16, sparseResult.nonZeros());
|
||||||
|
EXPECT(assert_equal(8, sparseResult.rows()));
|
||||||
|
EXPECT(assert_equal(7, sparseResult.cols()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
int main() {
|
||||||
|
TestResult tr;
|
||||||
|
return TestRegistry::runAllTests(tr);
|
||||||
|
}
|
||||||
|
/* ************************************************************************* */
|
||||||
|
|
@ -29,8 +29,8 @@ namespace imuBias {
|
||||||
|
|
||||||
class GTSAM_EXPORT ConstantBias {
|
class GTSAM_EXPORT ConstantBias {
|
||||||
private:
|
private:
|
||||||
Vector3 biasAcc_;
|
Vector3 biasAcc_; ///< The units for stddev are σ = m/s² or m √Hz/s²
|
||||||
Vector3 biasGyro_;
|
Vector3 biasGyro_; ///< The units for stddev are σ = rad/s or rad √Hz/s
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// dimension of the variable - used to autodetect sizes
|
/// dimension of the variable - used to autodetect sizes
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ namespace gtsam {
|
||||||
/// Parameters for pre-integration:
|
/// Parameters for pre-integration:
|
||||||
/// Usage: Create just a single Params and pass a shared pointer to the constructor
|
/// Usage: Create just a single Params and pass a shared pointer to the constructor
|
||||||
struct GTSAM_EXPORT PreintegratedRotationParams {
|
struct GTSAM_EXPORT PreintegratedRotationParams {
|
||||||
Matrix3 gyroscopeCovariance; ///< continuous-time "Covariance" of gyroscope measurements
|
/// Continuous-time "Covariance" of gyroscope measurements
|
||||||
|
/// The units for stddev are σ = rad/s/√Hz
|
||||||
|
Matrix3 gyroscopeCovariance;
|
||||||
boost::optional<Vector3> omegaCoriolis; ///< Coriolis constant
|
boost::optional<Vector3> omegaCoriolis; ///< Coriolis constant
|
||||||
boost::optional<Pose3> body_P_sensor; ///< The pose of the sensor in the body frame
|
boost::optional<Pose3> body_P_sensor; ///< The pose of the sensor in the body frame
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ namespace gtsam {
|
||||||
/// Parameters for pre-integration:
|
/// Parameters for pre-integration:
|
||||||
/// Usage: Create just a single Params and pass a shared pointer to the constructor
|
/// Usage: Create just a single Params and pass a shared pointer to the constructor
|
||||||
struct GTSAM_EXPORT PreintegrationParams: PreintegratedRotationParams {
|
struct GTSAM_EXPORT PreintegrationParams: PreintegratedRotationParams {
|
||||||
Matrix3 accelerometerCovariance; ///< continuous-time "Covariance" of accelerometer
|
/// Continuous-time "Covariance" of accelerometer
|
||||||
|
/// The units for stddev are σ = m/s²/√Hz
|
||||||
|
Matrix3 accelerometerCovariance;
|
||||||
Matrix3 integrationCovariance; ///< continuous-time "Covariance" describing integration uncertainty
|
Matrix3 integrationCovariance; ///< continuous-time "Covariance" describing integration uncertainty
|
||||||
bool use2ndOrderCoriolis; ///< Whether to use second order Coriolis integration
|
bool use2ndOrderCoriolis; ///< Whether to use second order Coriolis integration
|
||||||
Vector3 n_gravity; ///< Gravity vector in nav frame
|
Vector3 n_gravity; ///< Gravity vector in nav frame
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,8 @@ class GTSAM_EXPORT FunctorizedFactor : public NoiseModelFactor1<T> {
|
||||||
NonlinearFactor::shared_ptr(new FunctorizedFactor<R, T>(*this)));
|
NonlinearFactor::shared_ptr(new FunctorizedFactor<R, T>(*this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector evaluateError(const T ¶ms,
|
Vector evaluateError(const T ¶ms, boost::optional<Matrix &> H =
|
||||||
boost::optional<Matrix &> H = boost::none) const override {
|
boost::none) const override {
|
||||||
R x = func_(params, H);
|
R x = func_(params, H);
|
||||||
Vector error = traits<R>::Local(measured_, x);
|
Vector error = traits<R>::Local(measured_, x);
|
||||||
return error;
|
return error;
|
||||||
|
|
@ -96,7 +96,8 @@ class GTSAM_EXPORT FunctorizedFactor : public NoiseModelFactor1<T> {
|
||||||
|
|
||||||
/// @name Testable
|
/// @name Testable
|
||||||
/// @{
|
/// @{
|
||||||
void print(const std::string &s = "",
|
void print(
|
||||||
|
const std::string &s = "",
|
||||||
const KeyFormatter &keyFormatter = DefaultKeyFormatter) const override {
|
const KeyFormatter &keyFormatter = DefaultKeyFormatter) const override {
|
||||||
Base::print(s, keyFormatter);
|
Base::print(s, keyFormatter);
|
||||||
std::cout << s << (s != "" ? " " : "") << "FunctorizedFactor("
|
std::cout << s << (s != "" ? " " : "") << "FunctorizedFactor("
|
||||||
|
|
@ -144,4 +145,111 @@ FunctorizedFactor<R, T> MakeFunctorizedFactor(Key key, const R &z,
|
||||||
return FunctorizedFactor<R, T>(key, z, model, func);
|
return FunctorizedFactor<R, T>(key, z, model, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factor which evaluates provided binary functor and uses the result to compute
|
||||||
|
* error with respect to the provided measurement.
|
||||||
|
*
|
||||||
|
* Template parameters are
|
||||||
|
* @param R: The return type of the functor after evaluation.
|
||||||
|
* @param T1: The first argument type for the functor.
|
||||||
|
* @param T2: The second argument type for the functor.
|
||||||
|
*/
|
||||||
|
template <typename R, typename T1, typename T2>
|
||||||
|
class GTSAM_EXPORT FunctorizedFactor2 : public NoiseModelFactor2<T1, T2> {
|
||||||
|
private:
|
||||||
|
using Base = NoiseModelFactor2<T1, T2>;
|
||||||
|
|
||||||
|
R measured_; ///< value that is compared with functor return value
|
||||||
|
SharedNoiseModel noiseModel_; ///< noise model
|
||||||
|
using FunctionType = std::function<R(T1, T2, boost::optional<Matrix &>,
|
||||||
|
boost::optional<Matrix &>)>;
|
||||||
|
FunctionType func_; ///< functor instance
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** default constructor - only use for serialization */
|
||||||
|
FunctorizedFactor2() {}
|
||||||
|
|
||||||
|
/** Construct with given x and the parameters of the basis
|
||||||
|
*
|
||||||
|
* @param key: Factor key
|
||||||
|
* @param z: Measurement object of same type as that returned by functor
|
||||||
|
* @param model: Noise model
|
||||||
|
* @param func: The instance of the functor object
|
||||||
|
*/
|
||||||
|
FunctorizedFactor2(Key key1, Key key2, const R &z,
|
||||||
|
const SharedNoiseModel &model, const FunctionType func)
|
||||||
|
: Base(model, key1, key2),
|
||||||
|
measured_(z),
|
||||||
|
noiseModel_(model),
|
||||||
|
func_(func) {}
|
||||||
|
|
||||||
|
virtual ~FunctorizedFactor2() {}
|
||||||
|
|
||||||
|
/// @return a deep copy of this factor
|
||||||
|
NonlinearFactor::shared_ptr clone() const override {
|
||||||
|
return boost::static_pointer_cast<NonlinearFactor>(
|
||||||
|
NonlinearFactor::shared_ptr(new FunctorizedFactor2<R, T1, T2>(*this)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector evaluateError(
|
||||||
|
const T1 ¶ms1, const T2 ¶ms2,
|
||||||
|
boost::optional<Matrix &> H1 = boost::none,
|
||||||
|
boost::optional<Matrix &> H2 = boost::none) const override {
|
||||||
|
R x = func_(params1, params2, H1, H2);
|
||||||
|
Vector error = traits<R>::Local(measured_, x);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @name Testable
|
||||||
|
/// @{
|
||||||
|
void print(
|
||||||
|
const std::string &s = "",
|
||||||
|
const KeyFormatter &keyFormatter = DefaultKeyFormatter) const override {
|
||||||
|
Base::print(s, keyFormatter);
|
||||||
|
std::cout << s << (s != "" ? " " : "") << "FunctorizedFactor2("
|
||||||
|
<< keyFormatter(this->key1()) << ", "
|
||||||
|
<< keyFormatter(this->key2()) << ")" << std::endl;
|
||||||
|
traits<R>::Print(measured_, " measurement: ");
|
||||||
|
std::cout << " noise model sigmas: " << noiseModel_->sigmas().transpose()
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool equals(const NonlinearFactor &other, double tol = 1e-9) const override {
|
||||||
|
const FunctorizedFactor2<R, T1, T2> *e =
|
||||||
|
dynamic_cast<const FunctorizedFactor2<R, T1, T2> *>(&other);
|
||||||
|
return e && Base::equals(other, tol) &&
|
||||||
|
traits<R>::Equals(this->measured_, e->measured_, tol);
|
||||||
|
}
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Serialization function */
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
template <class ARCHIVE>
|
||||||
|
void serialize(ARCHIVE &ar, const unsigned int /*version*/) {
|
||||||
|
ar &boost::serialization::make_nvp(
|
||||||
|
"NoiseModelFactor2", boost::serialization::base_object<Base>(*this));
|
||||||
|
ar &BOOST_SERIALIZATION_NVP(measured_);
|
||||||
|
ar &BOOST_SERIALIZATION_NVP(func_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// traits
|
||||||
|
template <typename R, typename T1, typename T2>
|
||||||
|
struct traits<FunctorizedFactor2<R, T1, T2>>
|
||||||
|
: public Testable<FunctorizedFactor2<R, T1, T2>> {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to create a functorized factor.
|
||||||
|
*
|
||||||
|
* Uses function template deduction to identify return type and functor type, so
|
||||||
|
* template list only needs the functor argument type.
|
||||||
|
*/
|
||||||
|
template <typename T1, typename T2, typename R, typename FUNC>
|
||||||
|
FunctorizedFactor2<R, T1, T2> MakeFunctorizedFactor2(
|
||||||
|
Key key1, Key key2, const R &z, const SharedNoiseModel &model,
|
||||||
|
const FUNC func) {
|
||||||
|
return FunctorizedFactor2<R, T1, T2>(key1, key2, z, model, func);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace gtsam
|
} // namespace gtsam
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ class GaussNewtonOptimizer;
|
||||||
* NonlinearOptimizationParams.
|
* NonlinearOptimizationParams.
|
||||||
*/
|
*/
|
||||||
class GTSAM_EXPORT GaussNewtonParams : public NonlinearOptimizerParams {
|
class GTSAM_EXPORT GaussNewtonParams : public NonlinearOptimizerParams {
|
||||||
|
public:
|
||||||
|
using OptimizerType = GaussNewtonOptimizer;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,338 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file GncOptimizer.h
|
||||||
|
* @brief The GncOptimizer class
|
||||||
|
* @author Jingnan Shi
|
||||||
|
* @author Luca Carlone
|
||||||
|
* @author Frank Dellaert
|
||||||
|
*
|
||||||
|
* Implementation of the paper: Yang, Antonante, Tzoumas, Carlone, "Graduated Non-Convexity for Robust Spatial Perception:
|
||||||
|
* From Non-Minimal Solvers to Global Outlier Rejection", ICRA/RAL, 2020. (arxiv version: https://arxiv.org/pdf/1909.08605.pdf)
|
||||||
|
*
|
||||||
|
* See also:
|
||||||
|
* Antonante, Tzoumas, Yang, Carlone, "Outlier-Robust Estimation: Hardness, Minimally-Tuned Algorithms, and Applications",
|
||||||
|
* arxiv: https://arxiv.org/pdf/2007.15109.pdf, 2020.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/nonlinear/GncParams.h>
|
||||||
|
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
|
||||||
|
|
||||||
|
namespace gtsam {
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
template<class GncParameters>
|
||||||
|
class GncOptimizer {
|
||||||
|
public:
|
||||||
|
/// For each parameter, specify the corresponding optimizer: e.g., GaussNewtonParams -> GaussNewtonOptimizer.
|
||||||
|
typedef typename GncParameters::OptimizerType BaseOptimizer;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NonlinearFactorGraph nfg_; ///< Original factor graph to be solved by GNC.
|
||||||
|
Values state_; ///< Initial values to be used at each iteration by GNC.
|
||||||
|
GncParameters params_; ///< GNC parameters.
|
||||||
|
Vector weights_; ///< Weights associated to each factor in GNC (this could be a local variable in optimize, but it is useful to make it accessible from outside).
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Constructor.
|
||||||
|
GncOptimizer(const NonlinearFactorGraph& graph, const Values& initialValues,
|
||||||
|
const GncParameters& params = GncParameters())
|
||||||
|
: state_(initialValues),
|
||||||
|
params_(params) {
|
||||||
|
|
||||||
|
// make sure all noiseModels are Gaussian or convert to Gaussian
|
||||||
|
nfg_.resize(graph.size());
|
||||||
|
for (size_t i = 0; i < graph.size(); i++) {
|
||||||
|
if (graph[i]) {
|
||||||
|
NoiseModelFactor::shared_ptr factor = boost::dynamic_pointer_cast<
|
||||||
|
NoiseModelFactor>(graph[i]);
|
||||||
|
auto robust = boost::dynamic_pointer_cast<
|
||||||
|
noiseModel::Robust>(factor->noiseModel());
|
||||||
|
// if the factor has a robust loss, we remove the robust loss
|
||||||
|
nfg_[i] = robust ? factor-> cloneWithNewNoiseModel(robust->noise()) : factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access a copy of the internal factor graph.
|
||||||
|
const NonlinearFactorGraph& getFactors() const { return nfg_; }
|
||||||
|
|
||||||
|
/// Access a copy of the internal values.
|
||||||
|
const Values& getState() const { return state_; }
|
||||||
|
|
||||||
|
/// Access a copy of the parameters.
|
||||||
|
const GncParameters& getParams() const { return params_;}
|
||||||
|
|
||||||
|
/// Access a copy of the GNC weights.
|
||||||
|
const Vector& getWeights() const { return weights_;}
|
||||||
|
|
||||||
|
/// Compute optimal solution using graduated non-convexity.
|
||||||
|
Values optimize() {
|
||||||
|
// start by assuming all measurements are inliers
|
||||||
|
weights_ = Vector::Ones(nfg_.size());
|
||||||
|
BaseOptimizer baseOptimizer(nfg_, state_);
|
||||||
|
Values result = baseOptimizer.optimize();
|
||||||
|
double mu = initializeMu();
|
||||||
|
double prev_cost = nfg_.error(result);
|
||||||
|
double cost = 0.0; // this will be updated in the main loop
|
||||||
|
|
||||||
|
// handle the degenerate case that corresponds to small
|
||||||
|
// maximum residual errors at initialization
|
||||||
|
// For GM: if residual error is small, mu -> 0
|
||||||
|
// For TLS: if residual error is small, mu -> -1
|
||||||
|
if (mu <= 0) {
|
||||||
|
if (params_.verbosity >= GncParameters::Verbosity::SUMMARY) {
|
||||||
|
std::cout << "GNC Optimizer stopped because maximum residual at "
|
||||||
|
"initialization is small."
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if (params_.verbosity >= GncParameters::Verbosity::VALUES) {
|
||||||
|
result.print("result\n");
|
||||||
|
std::cout << "mu: " << mu << std::endl;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t iter;
|
||||||
|
for (iter = 0; iter < params_.maxIterations; iter++) {
|
||||||
|
|
||||||
|
// display info
|
||||||
|
if (params_.verbosity >= GncParameters::Verbosity::VALUES) {
|
||||||
|
std::cout << "iter: " << iter << std::endl;
|
||||||
|
result.print("result\n");
|
||||||
|
std::cout << "mu: " << mu << std::endl;
|
||||||
|
std::cout << "weights: " << weights_ << std::endl;
|
||||||
|
}
|
||||||
|
// weights update
|
||||||
|
weights_ = calculateWeights(result, mu);
|
||||||
|
|
||||||
|
// variable/values update
|
||||||
|
NonlinearFactorGraph graph_iter = this->makeWeightedGraph(weights_);
|
||||||
|
BaseOptimizer baseOptimizer_iter(graph_iter, state_);
|
||||||
|
result = baseOptimizer_iter.optimize();
|
||||||
|
|
||||||
|
// stopping condition
|
||||||
|
cost = graph_iter.error(result);
|
||||||
|
if (checkConvergence(mu, weights_, cost, prev_cost)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update mu
|
||||||
|
mu = updateMu(mu);
|
||||||
|
|
||||||
|
// get ready for next iteration
|
||||||
|
prev_cost = cost;
|
||||||
|
|
||||||
|
// display info
|
||||||
|
if (params_.verbosity >= GncParameters::Verbosity::VALUES) {
|
||||||
|
std::cout << "previous cost: " << prev_cost << std::endl;
|
||||||
|
std::cout << "current cost: " << cost << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// display info
|
||||||
|
if (params_.verbosity >= GncParameters::Verbosity::SUMMARY) {
|
||||||
|
std::cout << "final iterations: " << iter << std::endl;
|
||||||
|
std::cout << "final mu: " << mu << std::endl;
|
||||||
|
std::cout << "final weights: " << weights_ << std::endl;
|
||||||
|
std::cout << "previous cost: " << prev_cost << std::endl;
|
||||||
|
std::cout << "current cost: " << cost << std::endl;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the gnc parameter mu such that loss is approximately convex (remark 5 in GNC paper).
|
||||||
|
double initializeMu() const {
|
||||||
|
// compute largest error across all factors
|
||||||
|
double rmax_sq = 0.0;
|
||||||
|
for (size_t i = 0; i < nfg_.size(); i++) {
|
||||||
|
if (nfg_[i]) {
|
||||||
|
rmax_sq = std::max(rmax_sq, nfg_[i]->error(state_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set initial mu
|
||||||
|
switch (params_.lossType) {
|
||||||
|
case GncLossType::GM:
|
||||||
|
// surrogate cost is convex for large mu
|
||||||
|
return 2 * rmax_sq / params_.barcSq; // initial mu
|
||||||
|
case GncLossType::TLS:
|
||||||
|
/* initialize mu to the value specified in Remark 5 in GNC paper.
|
||||||
|
surrogate cost is convex for mu close to zero
|
||||||
|
degenerate case: 2 * rmax_sq - params_.barcSq < 0 (handled in the main loop)
|
||||||
|
according to remark mu = params_.barcSq / (2 * rmax_sq - params_.barcSq) = params_.barcSq/ excessResidual
|
||||||
|
however, if the denominator is 0 or negative, we return mu = -1 which leads to termination of the main GNC loop
|
||||||
|
*/
|
||||||
|
return
|
||||||
|
(2 * rmax_sq - params_.barcSq) > 0 ?
|
||||||
|
params_.barcSq / (2 * rmax_sq - params_.barcSq) : -1;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"GncOptimizer::initializeMu: called with unknown loss type.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the gnc parameter mu to gradually increase nonconvexity.
|
||||||
|
double updateMu(const double mu) const {
|
||||||
|
switch (params_.lossType) {
|
||||||
|
case GncLossType::GM:
|
||||||
|
// reduce mu, but saturate at 1 (original cost is recovered for mu -> 1)
|
||||||
|
return std::max(1.0, mu / params_.muStep);
|
||||||
|
case GncLossType::TLS:
|
||||||
|
// increases mu at each iteration (original cost is recovered for mu -> inf)
|
||||||
|
return mu * params_.muStep;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"GncOptimizer::updateMu: called with unknown loss type.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if we have reached the value of mu for which the surrogate loss matches the original loss.
|
||||||
|
bool checkMuConvergence(const double mu) const {
|
||||||
|
bool muConverged = false;
|
||||||
|
switch (params_.lossType) {
|
||||||
|
case GncLossType::GM:
|
||||||
|
muConverged = std::fabs(mu - 1.0) < 1e-9; // mu=1 recovers the original GM function
|
||||||
|
break;
|
||||||
|
case GncLossType::TLS:
|
||||||
|
muConverged = false; // for TLS there is no stopping condition on mu (it must tend to infinity)
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"GncOptimizer::checkMuConvergence: called with unknown loss type.");
|
||||||
|
}
|
||||||
|
if (muConverged && params_.verbosity >= GncParameters::Verbosity::SUMMARY)
|
||||||
|
std::cout << "muConverged = true " << std::endl;
|
||||||
|
return muConverged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check convergence of relative cost differences.
|
||||||
|
bool checkCostConvergence(const double cost, const double prev_cost) const {
|
||||||
|
bool costConverged = std::fabs(cost - prev_cost) / std::max(prev_cost, 1e-7)
|
||||||
|
< params_.relativeCostTol;
|
||||||
|
if (costConverged && params_.verbosity >= GncParameters::Verbosity::SUMMARY)
|
||||||
|
std::cout << "checkCostConvergence = true " << std::endl;
|
||||||
|
return costConverged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check convergence of weights to binary values.
|
||||||
|
bool checkWeightsConvergence(const Vector& weights) const {
|
||||||
|
bool weightsConverged = false;
|
||||||
|
switch (params_.lossType) {
|
||||||
|
case GncLossType::GM:
|
||||||
|
weightsConverged = false; // for GM, there is no clear binary convergence for the weights
|
||||||
|
break;
|
||||||
|
case GncLossType::TLS:
|
||||||
|
weightsConverged = true;
|
||||||
|
for (size_t i = 0; i < weights.size(); i++) {
|
||||||
|
if (std::fabs(weights[i] - std::round(weights[i]))
|
||||||
|
> params_.weightsTol) {
|
||||||
|
weightsConverged = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"GncOptimizer::checkWeightsConvergence: called with unknown loss type.");
|
||||||
|
}
|
||||||
|
if (weightsConverged
|
||||||
|
&& params_.verbosity >= GncParameters::Verbosity::SUMMARY)
|
||||||
|
std::cout << "weightsConverged = true " << std::endl;
|
||||||
|
return weightsConverged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check for convergence between consecutive GNC iterations.
|
||||||
|
bool checkConvergence(const double mu, const Vector& weights,
|
||||||
|
const double cost, const double prev_cost) const {
|
||||||
|
return checkCostConvergence(cost, prev_cost)
|
||||||
|
|| checkWeightsConvergence(weights) || checkMuConvergence(mu);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a graph where each factor is weighted by the gnc weights.
|
||||||
|
NonlinearFactorGraph makeWeightedGraph(const Vector& weights) const {
|
||||||
|
// make sure all noiseModels are Gaussian or convert to Gaussian
|
||||||
|
NonlinearFactorGraph newGraph;
|
||||||
|
newGraph.resize(nfg_.size());
|
||||||
|
for (size_t i = 0; i < nfg_.size(); i++) {
|
||||||
|
if (nfg_[i]) {
|
||||||
|
auto factor = boost::dynamic_pointer_cast<
|
||||||
|
NoiseModelFactor>(nfg_[i]);
|
||||||
|
auto noiseModel =
|
||||||
|
boost::dynamic_pointer_cast<noiseModel::Gaussian>(
|
||||||
|
factor->noiseModel());
|
||||||
|
if (noiseModel) {
|
||||||
|
Matrix newInfo = weights[i] * noiseModel->information();
|
||||||
|
auto newNoiseModel = noiseModel::Gaussian::Information(newInfo);
|
||||||
|
newGraph[i] = factor->cloneWithNewNoiseModel(newNoiseModel);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"GncOptimizer::makeWeightedGraph: unexpected non-Gaussian noise model.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate gnc weights.
|
||||||
|
Vector calculateWeights(const Values& currentEstimate, const double mu) {
|
||||||
|
Vector weights = Vector::Ones(nfg_.size());
|
||||||
|
|
||||||
|
// do not update the weights that the user has decided are known inliers
|
||||||
|
std::vector<size_t> allWeights;
|
||||||
|
for (size_t k = 0; k < nfg_.size(); k++) {
|
||||||
|
allWeights.push_back(k);
|
||||||
|
}
|
||||||
|
std::vector<size_t> unknownWeights;
|
||||||
|
std::set_difference(allWeights.begin(), allWeights.end(),
|
||||||
|
params_.knownInliers.begin(),
|
||||||
|
params_.knownInliers.end(),
|
||||||
|
std::inserter(unknownWeights, unknownWeights.begin()));
|
||||||
|
|
||||||
|
// update weights of known inlier/outlier measurements
|
||||||
|
switch (params_.lossType) {
|
||||||
|
case GncLossType::GM: { // use eq (12) in GNC paper
|
||||||
|
for (size_t k : unknownWeights) {
|
||||||
|
if (nfg_[k]) {
|
||||||
|
double u2_k = nfg_[k]->error(currentEstimate); // squared (and whitened) residual
|
||||||
|
weights[k] = std::pow(
|
||||||
|
(mu * params_.barcSq) / (u2_k + mu * params_.barcSq), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return weights;
|
||||||
|
}
|
||||||
|
case GncLossType::TLS: { // use eq (14) in GNC paper
|
||||||
|
double upperbound = (mu + 1) / mu * params_.barcSq;
|
||||||
|
double lowerbound = mu / (mu + 1) * params_.barcSq;
|
||||||
|
for (size_t k : unknownWeights) {
|
||||||
|
if (nfg_[k]) {
|
||||||
|
double u2_k = nfg_[k]->error(currentEstimate); // squared (and whitened) residual
|
||||||
|
if (u2_k >= upperbound) {
|
||||||
|
weights[k] = 0;
|
||||||
|
} else if (u2_k <= lowerbound) {
|
||||||
|
weights[k] = 1;
|
||||||
|
} else {
|
||||||
|
weights[k] = std::sqrt(params_.barcSq * mu * (mu + 1) / u2_k)
|
||||||
|
- mu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return weights;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"GncOptimizer::calculateWeights: called with unknown loss type.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
||||||
|
* Atlanta, Georgia 30332-0415
|
||||||
|
* All Rights Reserved
|
||||||
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
||||||
|
|
||||||
|
* See LICENSE for the license information
|
||||||
|
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file GncOptimizer.h
|
||||||
|
* @brief The GncOptimizer class
|
||||||
|
* @author Jingnan Shi
|
||||||
|
* @author Luca Carlone
|
||||||
|
* @author Frank Dellaert
|
||||||
|
*
|
||||||
|
* Implementation of the paper: Yang, Antonante, Tzoumas, Carlone, "Graduated Non-Convexity for Robust Spatial Perception:
|
||||||
|
* From Non-Minimal Solvers to Global Outlier Rejection", ICRA/RAL, 2020. (arxiv version: https://arxiv.org/pdf/1909.08605.pdf)
|
||||||
|
*
|
||||||
|
* See also:
|
||||||
|
* Antonante, Tzoumas, Yang, Carlone, "Outlier-Robust Estimation: Hardness, Minimally-Tuned Algorithms, and Applications",
|
||||||
|
* arxiv: https://arxiv.org/pdf/2007.15109.pdf, 2020.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
|
||||||
|
#include <gtsam/nonlinear/GaussNewtonOptimizer.h>
|
||||||
|
|
||||||
|
namespace gtsam {
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
/// Choice of robust loss function for GNC.
|
||||||
|
enum GncLossType {
|
||||||
|
GM /*Geman McClure*/,
|
||||||
|
TLS /*Truncated least squares*/
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class BaseOptimizerParameters>
|
||||||
|
class GncParams {
|
||||||
|
public:
|
||||||
|
/// For each parameter, specify the corresponding optimizer: e.g., GaussNewtonParams -> GaussNewtonOptimizer.
|
||||||
|
typedef typename BaseOptimizerParameters::OptimizerType OptimizerType;
|
||||||
|
|
||||||
|
/// Verbosity levels
|
||||||
|
enum Verbosity {
|
||||||
|
SILENT = 0,
|
||||||
|
SUMMARY,
|
||||||
|
VALUES
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
GncParams(const BaseOptimizerParameters& baseOptimizerParams)
|
||||||
|
: baseOptimizerParams(baseOptimizerParams) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default constructor.
|
||||||
|
GncParams()
|
||||||
|
: baseOptimizerParams() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GNC parameters.
|
||||||
|
BaseOptimizerParameters baseOptimizerParams; ///< Optimization parameters used to solve the weighted least squares problem at each GNC iteration
|
||||||
|
/// any other specific GNC parameters:
|
||||||
|
GncLossType lossType = TLS; ///< Default loss
|
||||||
|
size_t maxIterations = 100; ///< Maximum number of iterations
|
||||||
|
double barcSq = 1.0; ///< A factor is considered an inlier if factor.error() < barcSq. Note that factor.error() whitens by the covariance
|
||||||
|
double muStep = 1.4; ///< Multiplicative factor to reduce/increase the mu in gnc
|
||||||
|
double relativeCostTol = 1e-5; ///< If relative cost change is below this threshold, stop iterating
|
||||||
|
double weightsTol = 1e-4; ///< If the weights are within weightsTol from being binary, stop iterating (only for TLS)
|
||||||
|
Verbosity verbosity = SILENT; ///< Verbosity level
|
||||||
|
std::vector<size_t> knownInliers = std::vector<size_t>(); ///< Slots in the factor graph corresponding to measurements that we know are inliers
|
||||||
|
|
||||||
|
/// Set the robust loss function to be used in GNC (chosen among the ones in GncLossType).
|
||||||
|
void setLossType(const GncLossType type) {
|
||||||
|
lossType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the maximum number of iterations in GNC (changing the max nr of iters might lead to less accurate solutions and is not recommended).
|
||||||
|
void setMaxIterations(const size_t maxIter) {
|
||||||
|
std::cout
|
||||||
|
<< "setMaxIterations: changing the max nr of iters might lead to less accurate solutions and is not recommended! "
|
||||||
|
<< std::endl;
|
||||||
|
maxIterations = maxIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the maximum weighted residual error for an inlier. For a factor in the form f(x) = 0.5 * || r(x) ||^2_Omega,
|
||||||
|
* the inlier threshold is the largest value of f(x) for the corresponding measurement to be considered an inlier.
|
||||||
|
* In other words, an inlier at x is such that 0.5 * || r(x) ||^2_Omega <= barcSq.
|
||||||
|
* Assuming a isotropic measurement covariance sigma^2 * Identity, the cost becomes: 0.5 * 1/sigma^2 || r(x) ||^2 <= barcSq.
|
||||||
|
* Hence || r(x) ||^2 <= 2 * barcSq * sigma^2.
|
||||||
|
* */
|
||||||
|
void setInlierCostThreshold(const double inth) {
|
||||||
|
barcSq = inth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the graduated non-convexity step: at each GNC iteration, mu is updated as mu <- mu * muStep.
|
||||||
|
void setMuStep(const double step) {
|
||||||
|
muStep = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the maximum relative difference in mu values to stop iterating.
|
||||||
|
void setRelativeCostTol(double value) {
|
||||||
|
relativeCostTol = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the maximum difference between the weights and their rounding in {0,1} to stop iterating.
|
||||||
|
void setWeightsTol(double value) {
|
||||||
|
weightsTol = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the verbosity level.
|
||||||
|
void setVerbosityGNC(const Verbosity value) {
|
||||||
|
verbosity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** (Optional) Provide a vector of measurements that must be considered inliers. The enties in the vector
|
||||||
|
* corresponds to the slots in the factor graph. For instance, if you have a nonlinear factor graph nfg,
|
||||||
|
* and you provide knownIn = {0, 2, 15}, GNC will not apply outlier rejection to nfg[0], nfg[2], and nfg[15].
|
||||||
|
* This functionality is commonly used in SLAM when one may assume the odometry is outlier free, and
|
||||||
|
* only apply GNC to prune outliers from the loop closures.
|
||||||
|
* */
|
||||||
|
void setKnownInliers(const std::vector<size_t>& knownIn) {
|
||||||
|
for (size_t i = 0; i < knownIn.size(); i++)
|
||||||
|
knownInliers.push_back(knownIn[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Equals.
|
||||||
|
bool equals(const GncParams& other, double tol = 1e-9) const {
|
||||||
|
return baseOptimizerParams.equals(other.baseOptimizerParams)
|
||||||
|
&& lossType == other.lossType && maxIterations == other.maxIterations
|
||||||
|
&& std::fabs(barcSq - other.barcSq) <= tol
|
||||||
|
&& std::fabs(muStep - other.muStep) <= tol
|
||||||
|
&& verbosity == other.verbosity && knownInliers == other.knownInliers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print.
|
||||||
|
void print(const std::string& str) const {
|
||||||
|
std::cout << str << "\n";
|
||||||
|
switch (lossType) {
|
||||||
|
case GM:
|
||||||
|
std::cout << "lossType: Geman McClure" << "\n";
|
||||||
|
break;
|
||||||
|
case TLS:
|
||||||
|
std::cout << "lossType: Truncated Least-squares" << "\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("GncParams::print: unknown loss type.");
|
||||||
|
}
|
||||||
|
std::cout << "maxIterations: " << maxIterations << "\n";
|
||||||
|
std::cout << "barcSq: " << barcSq << "\n";
|
||||||
|
std::cout << "muStep: " << muStep << "\n";
|
||||||
|
std::cout << "relativeCostTol: " << relativeCostTol << "\n";
|
||||||
|
std::cout << "weightsTol: " << weightsTol << "\n";
|
||||||
|
std::cout << "verbosity: " << verbosity << "\n";
|
||||||
|
for (size_t i = 0; i < knownInliers.size(); i++)
|
||||||
|
std::cout << "knownInliers: " << knownInliers[i] << "\n";
|
||||||
|
baseOptimizerParams.print(str);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
namespace gtsam {
|
namespace gtsam {
|
||||||
|
|
||||||
|
class LevenbergMarquardtOptimizer;
|
||||||
|
|
||||||
/** Parameters for Levenberg-Marquardt optimization. Note that this parameters
|
/** Parameters for Levenberg-Marquardt optimization. Note that this parameters
|
||||||
* class inherits from NonlinearOptimizerParams, which specifies the parameters
|
* class inherits from NonlinearOptimizerParams, which specifies the parameters
|
||||||
* common to all nonlinear optimization algorithms. This class also contains
|
* common to all nonlinear optimization algorithms. This class also contains
|
||||||
|
|
@ -40,6 +42,7 @@ public:
|
||||||
|
|
||||||
static VerbosityLM verbosityLMTranslator(const std::string &s);
|
static VerbosityLM verbosityLMTranslator(const std::string &s);
|
||||||
static std::string verbosityLMTranslator(VerbosityLM value);
|
static std::string verbosityLMTranslator(VerbosityLM value);
|
||||||
|
using OptimizerType = LevenbergMarquardtOptimizer;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,10 @@ boost::tuple<V, int> nonlinearConjugateGradient(const S &system,
|
||||||
currentValues = system.advance(prevValues, alpha, direction);
|
currentValues = system.advance(prevValues, alpha, direction);
|
||||||
currentError = system.error(currentValues);
|
currentError = system.error(currentValues);
|
||||||
|
|
||||||
|
// User hook:
|
||||||
|
if (params.iterationHook)
|
||||||
|
params.iterationHook(iteration, prevError, currentError);
|
||||||
|
|
||||||
// Maybe show output
|
// Maybe show output
|
||||||
if (params.verbosity >= NonlinearOptimizerParams::ERROR)
|
if (params.verbosity >= NonlinearOptimizerParams::ERROR)
|
||||||
std::cout << "iteration: " << iteration << ", currentError: " << currentError << std::endl;
|
std::cout << "iteration: " << iteration << ", currentError: " << currentError << std::endl;
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,14 @@ bool NoiseModelFactor::equals(const NonlinearFactor& f, double tol) const {
|
||||||
&& noiseModel_->equals(*e->noiseModel_, tol)));
|
&& noiseModel_->equals(*e->noiseModel_, tol)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
NoiseModelFactor::shared_ptr NoiseModelFactor::cloneWithNewNoiseModel(
|
||||||
|
const SharedNoiseModel newNoise) const {
|
||||||
|
NoiseModelFactor::shared_ptr new_factor = boost::dynamic_pointer_cast<NoiseModelFactor>(clone());
|
||||||
|
new_factor->noiseModel_ = newNoise;
|
||||||
|
return new_factor;
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
static void check(const SharedNoiseModel& noiseModel, size_t m) {
|
static void check(const SharedNoiseModel& noiseModel, size_t m) {
|
||||||
if (noiseModel && m != noiseModel->dim())
|
if (noiseModel && m != noiseModel->dim())
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,12 @@ public:
|
||||||
*/
|
*/
|
||||||
boost::shared_ptr<GaussianFactor> linearize(const Values& x) const override;
|
boost::shared_ptr<GaussianFactor> linearize(const Values& x) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a shared_ptr clone of the
|
||||||
|
* factor with a new noise model
|
||||||
|
*/
|
||||||
|
shared_ptr cloneWithNewNoiseModel(const SharedNoiseModel newNoise) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Serialization function */
|
/** Serialization function */
|
||||||
friend class boost::serialization::access;
|
friend class boost::serialization::access;
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
@ -256,6 +257,16 @@ void NonlinearFactorGraph::saveGraph(std::ostream &stm, const Values& values,
|
||||||
stm << "}\n";
|
stm << "}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
void NonlinearFactorGraph::saveGraph(
|
||||||
|
const std::string& file, const Values& values,
|
||||||
|
const GraphvizFormatting& graphvizFormatting,
|
||||||
|
const KeyFormatter& keyFormatter) const {
|
||||||
|
std::ofstream of(file);
|
||||||
|
saveGraph(of, values, graphvizFormatting, keyFormatter);
|
||||||
|
of.close();
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
double NonlinearFactorGraph::error(const Values& values) const {
|
double NonlinearFactorGraph::error(const Values& values) const {
|
||||||
gttic(NonlinearFactorGraph_error);
|
gttic(NonlinearFactorGraph_error);
|
||||||
|
|
@ -365,7 +376,7 @@ static Scatter scatterFromValues(const Values& values) {
|
||||||
scatter.reserve(values.size());
|
scatter.reserve(values.size());
|
||||||
|
|
||||||
// use "natural" ordering with keys taken from the initial values
|
// use "natural" ordering with keys taken from the initial values
|
||||||
for (const auto& key_value : values) {
|
for (const auto key_value : values) {
|
||||||
scatter.add(key_value.key, key_value.value.dim());
|
scatter.add(key_value.key, key_value.value.dim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file NonlinearFactorGraph.h
|
* @file NonlinearFactorGraph.h
|
||||||
* @brief Factor Graph Constsiting of non-linear factors
|
* @brief Factor Graph consisting of non-linear factors
|
||||||
* @author Frank Dellaert
|
* @author Frank Dellaert
|
||||||
* @author Carlos Nieto
|
* @author Carlos Nieto
|
||||||
* @author Christian Potthast
|
* @author Christian Potthast
|
||||||
|
|
@ -111,11 +111,21 @@ namespace gtsam {
|
||||||
/** Test equality */
|
/** Test equality */
|
||||||
bool equals(const NonlinearFactorGraph& other, double tol = 1e-9) const;
|
bool equals(const NonlinearFactorGraph& other, double tol = 1e-9) const;
|
||||||
|
|
||||||
/** Write the graph in GraphViz format for visualization */
|
/// Write the graph in GraphViz format for visualization
|
||||||
void saveGraph(std::ostream& stm, const Values& values = Values(),
|
void saveGraph(std::ostream& stm, const Values& values = Values(),
|
||||||
const GraphvizFormatting& graphvizFormatting = GraphvizFormatting(),
|
const GraphvizFormatting& graphvizFormatting = GraphvizFormatting(),
|
||||||
const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
|
const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the graph in GraphViz format to file for visualization.
|
||||||
|
*
|
||||||
|
* This is a wrapper friendly version since wrapped languages don't have
|
||||||
|
* access to C++ streams.
|
||||||
|
*/
|
||||||
|
void saveGraph(const std::string& file, const Values& values = Values(),
|
||||||
|
const GraphvizFormatting& graphvizFormatting = GraphvizFormatting(),
|
||||||
|
const KeyFormatter& keyFormatter = DefaultKeyFormatter) const;
|
||||||
|
|
||||||
/** unnormalized error, \f$ 0.5 \sum_i (h_i(X_i)-z)^2/\sigma^2 \f$ in the most common case */
|
/** unnormalized error, \f$ 0.5 \sum_i (h_i(X_i)-z)^2/\sigma^2 \f$ in the most common case */
|
||||||
double error(const Values& values) const;
|
double error(const Values& values) const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,10 @@ void NonlinearOptimizer::defaultOptimize() {
|
||||||
// Update newError for either printouts or conditional-end checks:
|
// Update newError for either printouts or conditional-end checks:
|
||||||
newError = error();
|
newError = error();
|
||||||
|
|
||||||
|
// User hook:
|
||||||
|
if (params.iterationHook)
|
||||||
|
params.iterationHook(iterations(), currentError, newError);
|
||||||
|
|
||||||
// Maybe show output
|
// Maybe show output
|
||||||
if (params.verbosity >= NonlinearOptimizerParams::VALUES)
|
if (params.verbosity >= NonlinearOptimizerParams::VALUES)
|
||||||
values().print("newValues");
|
values().print("newValues");
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** A shared pointer to this class */
|
/** A shared pointer to this class */
|
||||||
typedef boost::shared_ptr<const NonlinearOptimizer> shared_ptr;
|
using shared_ptr = boost::shared_ptr<const NonlinearOptimizer>;
|
||||||
|
|
||||||
/// @name Standard interface
|
/// @name Standard interface
|
||||||
/// @{
|
/// @{
|
||||||
|
|
|
||||||
|
|
@ -38,21 +38,12 @@ public:
|
||||||
SILENT, TERMINATION, ERROR, VALUES, DELTA, LINEAR
|
SILENT, TERMINATION, ERROR, VALUES, DELTA, LINEAR
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t maxIterations; ///< The maximum iterations to stop iterating (default 100)
|
size_t maxIterations = 100; ///< The maximum iterations to stop iterating (default 100)
|
||||||
double relativeErrorTol; ///< The maximum relative error decrease to stop iterating (default 1e-5)
|
double relativeErrorTol = 1e-5; ///< The maximum relative error decrease to stop iterating (default 1e-5)
|
||||||
double absoluteErrorTol; ///< The maximum absolute error decrease to stop iterating (default 1e-5)
|
double absoluteErrorTol = 1e-5; ///< The maximum absolute error decrease to stop iterating (default 1e-5)
|
||||||
double errorTol; ///< The maximum total error to stop iterating (default 0.0)
|
double errorTol = 0.0; ///< The maximum total error to stop iterating (default 0.0)
|
||||||
Verbosity verbosity; ///< The printing verbosity during optimization (default SILENT)
|
Verbosity verbosity = SILENT; ///< The printing verbosity during optimization (default SILENT)
|
||||||
Ordering::OrderingType orderingType; ///< The method of ordering use during variable elimination (default COLAMD)
|
Ordering::OrderingType orderingType = Ordering::COLAMD; ///< The method of ordering use during variable elimination (default COLAMD)
|
||||||
|
|
||||||
NonlinearOptimizerParams() :
|
|
||||||
maxIterations(100), relativeErrorTol(1e-5), absoluteErrorTol(1e-5), errorTol(
|
|
||||||
0.0), verbosity(SILENT), orderingType(Ordering::COLAMD),
|
|
||||||
linearSolverType(MULTIFRONTAL_CHOLESKY) {}
|
|
||||||
|
|
||||||
virtual ~NonlinearOptimizerParams() {
|
|
||||||
}
|
|
||||||
virtual void print(const std::string& str = "") const;
|
|
||||||
|
|
||||||
size_t getMaxIterations() const { return maxIterations; }
|
size_t getMaxIterations() const { return maxIterations; }
|
||||||
double getRelativeErrorTol() const { return relativeErrorTol; }
|
double getRelativeErrorTol() const { return relativeErrorTol; }
|
||||||
|
|
@ -71,6 +62,37 @@ public:
|
||||||
static Verbosity verbosityTranslator(const std::string &s) ;
|
static Verbosity verbosityTranslator(const std::string &s) ;
|
||||||
static std::string verbosityTranslator(Verbosity value) ;
|
static std::string verbosityTranslator(Verbosity value) ;
|
||||||
|
|
||||||
|
/** Type for an optional user-provided hook to be called after each
|
||||||
|
* internal optimizer iteration. See iterationHook below. */
|
||||||
|
using IterationHook = std::function<
|
||||||
|
void(size_t /*iteration*/, double/*errorBefore*/, double/*errorAfter*/)>;
|
||||||
|
|
||||||
|
/** Optional user-provided iteration hook to be called after each
|
||||||
|
* optimization iteration (Default: none).
|
||||||
|
* Note that `IterationHook` is defined as a std::function<> with this
|
||||||
|
* signature:
|
||||||
|
* \code
|
||||||
|
* void(size_t iteration, double errorBefore, double errorAfter)
|
||||||
|
* \endcode
|
||||||
|
* which allows binding by means of a reference to a regular function:
|
||||||
|
* \code
|
||||||
|
* void foo(size_t iteration, double errorBefore, double errorAfter);
|
||||||
|
* // ...
|
||||||
|
* lmOpts.iterationHook = &foo;
|
||||||
|
* \endcode
|
||||||
|
* or to a C++11 lambda (preferred if you need to capture additional
|
||||||
|
* context variables, such that the optimizer object itself, the factor graph,
|
||||||
|
* etc.):
|
||||||
|
* \code
|
||||||
|
* lmOpts.iterationHook = [&](size_t iter, double oldError, double newError)
|
||||||
|
* {
|
||||||
|
* // ...
|
||||||
|
* };
|
||||||
|
* \endcode
|
||||||
|
* or to the result of a properly-formed `std::bind` call.
|
||||||
|
*/
|
||||||
|
IterationHook iterationHook;
|
||||||
|
|
||||||
/** See NonlinearOptimizerParams::linearSolverType */
|
/** See NonlinearOptimizerParams::linearSolverType */
|
||||||
enum LinearSolverType {
|
enum LinearSolverType {
|
||||||
MULTIFRONTAL_CHOLESKY,
|
MULTIFRONTAL_CHOLESKY,
|
||||||
|
|
@ -81,10 +103,27 @@ public:
|
||||||
CHOLMOD, /* Experimental Flag */
|
CHOLMOD, /* Experimental Flag */
|
||||||
};
|
};
|
||||||
|
|
||||||
LinearSolverType linearSolverType; ///< The type of linear solver to use in the nonlinear optimizer
|
LinearSolverType linearSolverType = MULTIFRONTAL_CHOLESKY; ///< The type of linear solver to use in the nonlinear optimizer
|
||||||
boost::optional<Ordering> ordering; ///< The optional variable elimination ordering, or empty to use COLAMD (default: empty)
|
boost::optional<Ordering> ordering; ///< The optional variable elimination ordering, or empty to use COLAMD (default: empty)
|
||||||
IterativeOptimizationParameters::shared_ptr iterativeParams; ///< The container for iterativeOptimization parameters. used in CG Solvers.
|
IterativeOptimizationParameters::shared_ptr iterativeParams; ///< The container for iterativeOptimization parameters. used in CG Solvers.
|
||||||
|
|
||||||
|
NonlinearOptimizerParams() = default;
|
||||||
|
virtual ~NonlinearOptimizerParams() {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void print(const std::string& str = "") const;
|
||||||
|
|
||||||
|
bool equals(const NonlinearOptimizerParams& other, double tol = 1e-9) const {
|
||||||
|
return maxIterations == other.getMaxIterations()
|
||||||
|
&& std::abs(relativeErrorTol - other.getRelativeErrorTol()) <= tol
|
||||||
|
&& std::abs(absoluteErrorTol - other.getAbsoluteErrorTol()) <= tol
|
||||||
|
&& std::abs(errorTol - other.getErrorTol()) <= tol
|
||||||
|
&& verbosityTranslator(verbosity) == other.getVerbosity();
|
||||||
|
// && orderingType.equals(other.getOrderingType()_;
|
||||||
|
// && linearSolverType == other.getLinearSolverType();
|
||||||
|
// TODO: check ordering, iterativeParams, and iterationsHook
|
||||||
|
}
|
||||||
|
|
||||||
inline bool isMultifrontal() const {
|
inline bool isMultifrontal() const {
|
||||||
return (linearSolverType == MULTIFRONTAL_CHOLESKY)
|
return (linearSolverType == MULTIFRONTAL_CHOLESKY)
|
||||||
|| (linearSolverType == MULTIFRONTAL_QR);
|
|| (linearSolverType == MULTIFRONTAL_QR);
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ namespace gtsam {
|
||||||
/** Constructor from a Filtered view copies out all values */
|
/** Constructor from a Filtered view copies out all values */
|
||||||
template<class ValueType>
|
template<class ValueType>
|
||||||
Values::Values(const Values::Filtered<ValueType>& view) {
|
Values::Values(const Values::Filtered<ValueType>& view) {
|
||||||
for(const typename Filtered<ValueType>::KeyValuePair& key_value: view) {
|
for(const auto key_value: view) {
|
||||||
Key key = key_value.key;
|
Key key = key_value.key;
|
||||||
insert(key, static_cast<const ValueType&>(key_value.value));
|
insert(key, static_cast<const ValueType&>(key_value.value));
|
||||||
}
|
}
|
||||||
|
|
@ -226,7 +226,7 @@ namespace gtsam {
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template<class ValueType>
|
template<class ValueType>
|
||||||
Values::Values(const Values::ConstFiltered<ValueType>& view) {
|
Values::Values(const Values::ConstFiltered<ValueType>& view) {
|
||||||
for(const typename ConstFiltered<ValueType>::KeyValuePair& key_value: view) {
|
for(const auto key_value: view) {
|
||||||
Key key = key_value.key;
|
Key key = key_value.key;
|
||||||
insert(key, static_cast<const ValueType&>(key_value.value));
|
insert(key, static_cast<const ValueType&>(key_value.value));
|
||||||
}
|
}
|
||||||
|
|
@ -339,13 +339,12 @@ namespace gtsam {
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
template <typename ValueType>
|
template <typename ValueType>
|
||||||
ValueType Values::at(Key j) const {
|
const ValueType Values::at(Key j) const {
|
||||||
// Find the item
|
// Find the item
|
||||||
KeyValueMap::const_iterator item = values_.find(j);
|
KeyValueMap::const_iterator item = values_.find(j);
|
||||||
|
|
||||||
// Throw exception if it does not exist
|
// Throw exception if it does not exist
|
||||||
if(item == values_.end())
|
if (item == values_.end()) throw ValuesKeyDoesNotExist("at", j);
|
||||||
throw ValuesKeyDoesNotExist("at", j);
|
|
||||||
|
|
||||||
// Check the type and throw exception if incorrect
|
// Check the type and throw exception if incorrect
|
||||||
// h() split in two lines to avoid internal compiler error (MSVC2017)
|
// h() split in two lines to avoid internal compiler error (MSVC2017)
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ namespace gtsam {
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
size_t Values::dim() const {
|
size_t Values::dim() const {
|
||||||
size_t result = 0;
|
size_t result = 0;
|
||||||
for(const ConstKeyValuePair& key_value: *this) {
|
for(const auto key_value: *this) {
|
||||||
result += key_value.value.dim();
|
result += key_value.value.dim();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -215,7 +215,7 @@ namespace gtsam {
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
VectorValues Values::zeroVectors() const {
|
VectorValues Values::zeroVectors() const {
|
||||||
VectorValues result;
|
VectorValues result;
|
||||||
for(const ConstKeyValuePair& key_value: *this)
|
for(const auto key_value: *this)
|
||||||
result.insert(key_value.key, Vector::Zero(key_value.value.dim()));
|
result.insert(key_value.key, Vector::Zero(key_value.value.dim()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ namespace gtsam {
|
||||||
* @return The stored value
|
* @return The stored value
|
||||||
*/
|
*/
|
||||||
template <typename ValueType>
|
template <typename ValueType>
|
||||||
ValueType at(Key j) const;
|
const ValueType at(Key j) const;
|
||||||
|
|
||||||
/// version for double
|
/// version for double
|
||||||
double atDouble(size_t key) const { return at<double>(key);}
|
double atDouble(size_t key) const { return at<double>(key);}
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ struct LevenbergMarquardtState : public NonlinearOptimizerState {
|
||||||
noiseModelCache.resize(0);
|
noiseModelCache.resize(0);
|
||||||
// for each of the variables, add a prior
|
// for each of the variables, add a prior
|
||||||
damped.reserve(damped.size() + values.size());
|
damped.reserve(damped.size() + values.size());
|
||||||
for (const auto& key_value : values) {
|
for (const auto key_value : values) {
|
||||||
const Key key = key_value.key;
|
const Key key = key_value.key;
|
||||||
const size_t dim = key_value.value.dim();
|
const size_t dim = key_value.value.dim();
|
||||||
const CachedModel* item = getCachedModel(dim);
|
const CachedModel* item = getCachedModel(dim);
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,15 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace gtsam;
|
using namespace gtsam;
|
||||||
|
|
||||||
|
// Key for FunctorizedFactor
|
||||||
Key key = Symbol('X', 0);
|
Key key = Symbol('X', 0);
|
||||||
|
|
||||||
|
// Keys for FunctorizedFactor2
|
||||||
|
Key keyA = Symbol('A', 0);
|
||||||
|
Key keyx = Symbol('x', 0);
|
||||||
|
|
||||||
auto model = noiseModel::Isotropic::Sigma(9, 1);
|
auto model = noiseModel::Isotropic::Sigma(9, 1);
|
||||||
|
auto model2 = noiseModel::Isotropic::Sigma(3, 1);
|
||||||
|
|
||||||
/// Functor that takes a matrix and multiplies every element by m
|
/// Functor that takes a matrix and multiplies every element by m
|
||||||
class MultiplyFunctor {
|
class MultiplyFunctor {
|
||||||
|
|
@ -44,6 +51,21 @@ class MultiplyFunctor {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Functor that performs Ax where A is a matrix and x is a vector.
|
||||||
|
class ProjectionFunctor {
|
||||||
|
public:
|
||||||
|
Vector operator()(const Matrix &A, const Vector &x,
|
||||||
|
OptionalJacobian<-1, -1> H1 = boost::none,
|
||||||
|
OptionalJacobian<-1, -1> H2 = boost::none) const {
|
||||||
|
if (H1) {
|
||||||
|
H1->resize(x.size(), A.size());
|
||||||
|
*H1 << I_3x3, I_3x3, I_3x3;
|
||||||
|
}
|
||||||
|
if (H2) *H2 = A;
|
||||||
|
return A * x;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
// Test identity operation for FunctorizedFactor.
|
// Test identity operation for FunctorizedFactor.
|
||||||
TEST(FunctorizedFactor, Identity) {
|
TEST(FunctorizedFactor, Identity) {
|
||||||
|
|
@ -88,7 +110,7 @@ TEST(FunctorizedFactor, Equality) {
|
||||||
EXPECT(factor1.equals(factor2));
|
EXPECT(factor1.equals(factor2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *************************************************************************** */
|
/* ************************************************************************* */
|
||||||
// Test Jacobians of FunctorizedFactor.
|
// Test Jacobians of FunctorizedFactor.
|
||||||
TEST(FunctorizedFactor, Jacobians) {
|
TEST(FunctorizedFactor, Jacobians) {
|
||||||
Matrix X = Matrix::Identity(3, 3);
|
Matrix X = Matrix::Identity(3, 3);
|
||||||
|
|
@ -168,6 +190,83 @@ TEST(FunctorizedFactor, Lambda) {
|
||||||
EXPECT(assert_equal(Vector::Zero(9), error, 1e-9));
|
EXPECT(assert_equal(Vector::Zero(9), error, 1e-9));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
// Test identity operation for FunctorizedFactor2.
|
||||||
|
TEST(FunctorizedFactor, Identity2) {
|
||||||
|
// x = Ax since A is I_3x3
|
||||||
|
Matrix A = Matrix::Identity(3, 3);
|
||||||
|
Vector x = Vector::Ones(3);
|
||||||
|
|
||||||
|
auto functor = ProjectionFunctor();
|
||||||
|
auto factor =
|
||||||
|
MakeFunctorizedFactor2<Matrix, Vector>(keyA, keyx, x, model2, functor);
|
||||||
|
|
||||||
|
Vector error = factor.evaluateError(A, x);
|
||||||
|
|
||||||
|
EXPECT(assert_equal(Vector::Zero(3), error, 1e-9));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
// Test Jacobians of FunctorizedFactor2.
|
||||||
|
TEST(FunctorizedFactor, Jacobians2) {
|
||||||
|
Matrix A = Matrix::Identity(3, 3);
|
||||||
|
Vector x = Vector::Ones(3);
|
||||||
|
Matrix actualH1, actualH2;
|
||||||
|
|
||||||
|
auto factor = MakeFunctorizedFactor2<Matrix, Vector>(keyA, keyx, x, model2,
|
||||||
|
ProjectionFunctor());
|
||||||
|
|
||||||
|
Values values;
|
||||||
|
values.insert<Matrix>(keyA, A);
|
||||||
|
values.insert<Vector>(keyx, x);
|
||||||
|
|
||||||
|
// Check Jacobians
|
||||||
|
EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, 1e-7, 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
// Test FunctorizedFactor2 using a std::function type.
|
||||||
|
TEST(FunctorizedFactor, Functional2) {
|
||||||
|
Matrix A = Matrix::Identity(3, 3);
|
||||||
|
Vector3 x(1, 2, 3);
|
||||||
|
Vector measurement = A * x;
|
||||||
|
|
||||||
|
std::function<Matrix(Matrix, Matrix, boost::optional<Matrix &>,
|
||||||
|
boost::optional<Matrix &>)>
|
||||||
|
functional = ProjectionFunctor();
|
||||||
|
auto factor = MakeFunctorizedFactor2<Matrix, Vector>(keyA, keyx, measurement,
|
||||||
|
model2, functional);
|
||||||
|
|
||||||
|
Vector error = factor.evaluateError(A, x);
|
||||||
|
|
||||||
|
EXPECT(assert_equal(Vector::Zero(3), error, 1e-9));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
// Test FunctorizedFactor2 with a lambda function.
|
||||||
|
TEST(FunctorizedFactor, Lambda2) {
|
||||||
|
Matrix A = Matrix::Identity(3, 3);
|
||||||
|
Vector3 x = Vector3(1, 2, 3);
|
||||||
|
Matrix measurement = A * x;
|
||||||
|
|
||||||
|
auto lambda = [](const Matrix &A, const Vector &x,
|
||||||
|
OptionalJacobian<-1, -1> H1 = boost::none,
|
||||||
|
OptionalJacobian<-1, -1> H2 = boost::none) {
|
||||||
|
if (H1) {
|
||||||
|
H1->resize(x.size(), A.size());
|
||||||
|
*H1 << I_3x3, I_3x3, I_3x3;
|
||||||
|
}
|
||||||
|
if (H2) *H2 = A;
|
||||||
|
return A * x;
|
||||||
|
};
|
||||||
|
// FunctorizedFactor<Matrix> factor(key, measurement, model, lambda);
|
||||||
|
auto factor = MakeFunctorizedFactor2<Matrix, Vector>(keyA, keyx, measurement, model2, lambda);
|
||||||
|
|
||||||
|
Vector error = factor.evaluateError(A, x);
|
||||||
|
|
||||||
|
EXPECT(assert_equal(Vector::Zero(3), error, 1e-9));
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************************************* */
|
/* ************************************************************************* */
|
||||||
int main() {
|
int main() {
|
||||||
TestResult tr;
|
TestResult tr;
|
||||||
|
|
|
||||||
|
|
@ -175,10 +175,11 @@ TEST(Values, basic_functions)
|
||||||
{
|
{
|
||||||
Values values;
|
Values values;
|
||||||
const Values& values_c = values;
|
const Values& values_c = values;
|
||||||
values.insert(2, Vector3());
|
Matrix23 M1 = Matrix23::Zero(), M2 = Matrix23::Zero();
|
||||||
values.insert(4, Vector3());
|
values.insert(2, Vector3(0, 0, 0));
|
||||||
values.insert(6, Matrix23());
|
values.insert(4, Vector3(0, 0, 0));
|
||||||
values.insert(8, Matrix23());
|
values.insert(6, M1);
|
||||||
|
values.insert(8, M2);
|
||||||
|
|
||||||
// find
|
// find
|
||||||
EXPECT_LONGS_EQUAL(4, values.find(4)->key);
|
EXPECT_LONGS_EQUAL(4, values.find(4)->key);
|
||||||
|
|
@ -335,7 +336,7 @@ TEST(Values, filter) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
Values::Filtered<Value> filtered = values.filter(boost::bind(std::greater_equal<Key>(), _1, 2));
|
Values::Filtered<Value> filtered = values.filter(boost::bind(std::greater_equal<Key>(), _1, 2));
|
||||||
EXPECT_LONGS_EQUAL(2, (long)filtered.size());
|
EXPECT_LONGS_EQUAL(2, (long)filtered.size());
|
||||||
for(const Values::Filtered<>::KeyValuePair& key_value: filtered) {
|
for(const auto key_value: filtered) {
|
||||||
if(i == 0) {
|
if(i == 0) {
|
||||||
LONGS_EQUAL(2, (long)key_value.key);
|
LONGS_EQUAL(2, (long)key_value.key);
|
||||||
try {key_value.value.cast<Pose2>();} catch (const std::bad_cast& e) { FAIL("can't cast Value to Pose2");}
|
try {key_value.value.cast<Pose2>();} catch (const std::bad_cast& e) { FAIL("can't cast Value to Pose2");}
|
||||||
|
|
@ -370,7 +371,7 @@ TEST(Values, filter) {
|
||||||
i = 0;
|
i = 0;
|
||||||
Values::ConstFiltered<Pose3> pose_filtered = values.filter<Pose3>();
|
Values::ConstFiltered<Pose3> pose_filtered = values.filter<Pose3>();
|
||||||
EXPECT_LONGS_EQUAL(2, (long)pose_filtered.size());
|
EXPECT_LONGS_EQUAL(2, (long)pose_filtered.size());
|
||||||
for(const Values::ConstFiltered<Pose3>::KeyValuePair& key_value: pose_filtered) {
|
for(const auto key_value: pose_filtered) {
|
||||||
if(i == 0) {
|
if(i == 0) {
|
||||||
EXPECT_LONGS_EQUAL(1, (long)key_value.key);
|
EXPECT_LONGS_EQUAL(1, (long)key_value.key);
|
||||||
EXPECT(assert_equal(pose1, key_value.value));
|
EXPECT(assert_equal(pose1, key_value.value));
|
||||||
|
|
@ -408,7 +409,7 @@ TEST(Values, Symbol_filter) {
|
||||||
values.insert(Symbol('y', 3), pose3);
|
values.insert(Symbol('y', 3), pose3);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(const Values::Filtered<Value>::KeyValuePair& key_value: values.filter(Symbol::ChrTest('y'))) {
|
for(const auto key_value: values.filter(Symbol::ChrTest('y'))) {
|
||||||
if(i == 0) {
|
if(i == 0) {
|
||||||
LONGS_EQUAL(Symbol('y', 1), (long)key_value.key);
|
LONGS_EQUAL(Symbol('y', 1), (long)key_value.key);
|
||||||
EXPECT(assert_equal(pose1, key_value.value.cast<Pose3>()));
|
EXPECT(assert_equal(pose1, key_value.value.cast<Pose3>()));
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,8 @@ private:
|
||||||
public:
|
public:
|
||||||
BinaryMeasurement(Key key1, Key key2, const T &measured,
|
BinaryMeasurement(Key key1, Key key2, const T &measured,
|
||||||
const SharedNoiseModel &model = nullptr)
|
const SharedNoiseModel &model = nullptr)
|
||||||
: Factor(std::vector<Key>({key1, key2})), measured_(measured),
|
: Factor(std::vector<Key>({key1, key2})),
|
||||||
|
measured_(measured),
|
||||||
noiseModel_(model) {}
|
noiseModel_(model) {}
|
||||||
|
|
||||||
/// @name Standard Interface
|
/// @name Standard Interface
|
||||||
|
|
|
||||||
|
|
@ -121,8 +121,8 @@ MFAS::MFAS(const TranslationEdges& relativeTranslations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Key> MFAS::computeOrdering() const {
|
KeyVector MFAS::computeOrdering() const {
|
||||||
vector<Key> ordering; // Nodes in MFAS order (result).
|
KeyVector ordering; // Nodes in MFAS order (result).
|
||||||
|
|
||||||
// A graph is an unordered map from keys to nodes. Each node contains a list
|
// A graph is an unordered map from keys to nodes. Each node contains a list
|
||||||
// of its adjacent nodes. Create the graph from the edgeWeights.
|
// of its adjacent nodes. Create the graph from the edgeWeights.
|
||||||
|
|
@ -140,7 +140,7 @@ vector<Key> MFAS::computeOrdering() const {
|
||||||
|
|
||||||
map<MFAS::KeyPair, double> MFAS::computeOutlierWeights() const {
|
map<MFAS::KeyPair, double> MFAS::computeOutlierWeights() const {
|
||||||
// Find the ordering.
|
// Find the ordering.
|
||||||
vector<Key> ordering = computeOrdering();
|
KeyVector ordering = computeOrdering();
|
||||||
|
|
||||||
// Create a map from the node key to its position in the ordering. This makes
|
// Create a map from the node key to its position in the ordering. This makes
|
||||||
// it easier to lookup positions of different nodes.
|
// it easier to lookup positions of different nodes.
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue