diff --git a/modules/sfm/CMakeLists.txt b/modules/sfm/CMakeLists.txt new file mode 100644 index 00000000000..e4e23fb55f1 --- /dev/null +++ b/modules/sfm/CMakeLists.txt @@ -0,0 +1,128 @@ +set(the_description "SFM algorithms") + + +### LIBMV LIGHT EXTERNAL DEPENDENCIES ### + +find_package(Ceres QUIET) + +if(NOT DEFINED SFM_DEPS_OK) + + if(NOT DEFINED GLOG_LIBRARIES) + set(GLOG_LIBRARIES "glog") + endif() + + set(_fname "${CMAKE_CURRENT_BINARY_DIR}/test_sfm_deps.cpp") + file(WRITE "${_fname}" "#include \n#include \nint main() { (void)(0); return 0; }\n") + try_compile(SFM_DEPS_OK "${CMAKE_CURRENT_BINARY_DIR}" "${_fname}" + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${GLOG_INCLUDE_DIRS};${GFLAGS_INCLUDE_DIRS}" + LINK_LIBRARIES ${GLOG_LIBRARIES} ${GFLAGS_LIBRARIES} + OUTPUT_VARIABLE OUTPUT + ) + file(REMOVE "${_fname}") + message(STATUS "Checking SFM deps... ${SFM_DEPS_OK}") +endif() + +if(NOT HAVE_EIGEN OR NOT SFM_DEPS_OK) + set(DISABLE_MSG "Module opencv_sfm disabled because the following dependencies are not found:") + if(NOT HAVE_EIGEN) + set(DISABLE_MSG "${DISABLE_MSG} Eigen") + endif() + if(NOT SFM_DEPS_OK) + set(DISABLE_MSG "${DISABLE_MSG} Glog/Gflags") + endif() + message(STATUS ${DISABLE_MSG}) + ocv_module_disable(sfm) +endif() + + +### LIBMV LIGHT DEFINITIONS ### + +set(LIBMV_LIGHT_INCLUDES + src/libmv_light + "${OpenCV_SOURCE_DIR}/include/opencv" + "${GLOG_INCLUDE_DIRS}" + "${GFLAGS_INCLUDE_DIRS}" +) + +set(LIBMV_LIGHT_LIBS + correspondence + multiview + numeric + ${GLOG_LIBRARIES} + ${GFLAGS_LIBRARIES} +) + +if(Ceres_FOUND) + add_definitions("-DCERES_FOUND=1") + list(APPEND LIBMV_LIGHT_LIBS simple_pipeline) +else() + add_definitions("-DCERES_FOUND=0") + message(STATUS "CERES support is disabled. Ceres Solver for reconstruction API is required.") +endif() + +### DEFINE OPENCV SFM MODULE DEPENDENCIES ### + +### CREATE OPENCV SFM MODULE ### + +ocv_add_module(sfm + opencv_core + opencv_calib3d + opencv_features2d + opencv_xfeatures2d + WRAP python +) + + +ocv_warnings_disable(CMAKE_CXX_FLAGS + -Wundef + -Wshadow + -Wsign-compare + -Wmissing-declarations + -Wunused-but-set-variable + -Wunused-parameter + -Wunused-function +) + +if(UNIX) + if(CMAKE_COMPILER_IS_GNUCXX OR CV_ICC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + endif() +endif() + +ocv_include_directories( ${LIBMV_LIGHT_INCLUDES} ) +ocv_module_include_directories() + +# source files +FILE(GLOB OPENCV_SFM_SRC src/*.cpp) + +# define the header files (make the headers appear in IDEs.) +FILE(GLOB OPENCV_SFM_HDRS include/opencv2/sfm/*.hpp) + +ocv_set_module_sources(HEADERS ${OPENCV_SFM_HDRS} + SOURCES ${OPENCV_SFM_SRC}) + +ocv_create_module() + +# build libmv_light +if(NOT CMAKE_VERSION VERSION_LESS 2.8.11) # See ocv_target_include_directories() implementation + if(TARGET ${the_module}) + get_target_property(__include_dirs ${the_module} INCLUDE_DIRECTORIES) + include_directories(${__include_dirs}) + endif() +endif() +include_directories(${OCV_TARGET_INCLUDE_DIRS_${the_module}}) +add_subdirectory(src/libmv_light) + +ocv_target_link_libraries(${the_module} ${LIBMV_LIGHT_LIBS}) + + +### CREATE OPENCV SFM TESTS ### + +ocv_add_accuracy_tests() + + +### CREATE OPENCV SFM SAMPLES ### + +if(Ceres_FOUND) + ocv_add_samples(opencv_viz) +endif () diff --git a/modules/sfm/README.md b/modules/sfm/README.md new file mode 100644 index 00000000000..57f34ffd29d --- /dev/null +++ b/modules/sfm/README.md @@ -0,0 +1,118 @@ +Structure From Motion module +============================ + +This module contains algorithms to perform 3d reconstruction from 2d images. The core of the module is a light version of [Libmv](https://developer.blender.org/project/profile/59), which is a Library for Multiview Reconstruction (or LMV) divided into different modules (correspondence/numeric/multiview/simple_pipeline) that allow to resolve part of the SfM process. + + +Dependencies +------------ + +Before compiling, take a look at the following details in order to give a proper use of the Struncture from Motion module. **Advice:** The module is only available for Linux/GNU systems. + +In addition, it depends on some open source libraries: + +- [Eigen](http://eigen.tuxfamily.org/index.php?title=Main_Page) 3.2.2 or later. **Required** +- [Google Log](http://code.google.com/p/google-glog) 0.3.1 or later. **Required** +- [Google Flags](http://code.google.com/p/gflags). **Required** +- [Ceres Solver](http://ceres-solver.org). Needed by the reconstruction API in order to solve part of the Bundle Adjustment plus the points Intersect. If Ceres Solver is not installed on your system, the reconstruction funcionality will be disabled. **Recommended** + +Installation +------------ +**Required Dependencies** + +In case you are on [Ubuntu](http://www.ubuntu.com/) you can simply install the required dependencies by typing the following command. + + sudo apt-get install libeigen3-dev libgflags-dev libgoogle-glog-dev + +**Ceres Solver** + +Start by installing all the dependencies. + + # CMake + sudo apt-get install cmake + # google-glog + gflags + sudo apt-get install libgoogle-glog-dev + # BLAS & LAPACK + sudo apt-get install libatlas-base-dev + # Eigen3 + sudo apt-get install libeigen3-dev + # SuiteSparse and CXSparse (optional) + # - If you want to build Ceres as a *static* library (the default) + # you can use the SuiteSparse package in the main Ubuntu package + # repository: + sudo apt-get install libsuitesparse-dev + # - However, if you want to build Ceres as a *shared* library, you must + # add the following PPA: + sudo add-apt-repository ppa:bzindovic/suitesparse-bugfix-1319687 + sudo apt-get update + sudo apt-get install libsuitesparse-dev + +We are now ready to build, test, and install Ceres. + + git clone https://ceres-solver.googlesource.com/ceres-solver + cd ceres-solver + mkdir build && cd build + cmake .. + make -j4 + make test + sudo make install + +Usage +----- + +**trajectory_reconstruction.cpp** + +This program shows the camera trajectory reconstruction capabilities in the OpenCV Structure From Motion (SFM) module. It loads a file with the tracked 2d points over all the frames which are embedded into a vector of 2d points array, where each inner array represents a different frame. Every frame is composed by a list of 2d points which e.g. the first point in frame 1 is the same point in frame 2. If there is no point in a frame the assigned value will be (-1,-1). + +To run this example you can type the following command in the opencv binaries directory specifying the file path in your system and the camera intrinsics (in this case the tracks file was obtained using Blender Motion module). + + ./example_sfm_trajectory_reconstruction tracks_file.txt 1914 640 360 + +Finally, the script reconstructs the given set of tracked points and show the result using the OpenCV 3D visualizer (viz). On the image below, it's shown a screenshot with the result you should obtain running the "desktop_tracks.txt" found inside the samples directory. + +

+ +

+ +**scene_reconstruction.cpp** + +This program shows the multiview scene reconstruction capabilities in the OpenCV Structure From Motion (SFM) module. It calls the recontruction API using the overloaded signature for real images. In this case the script loads a file which provides a list with all the image paths that we want to reconstruct. Internally, this script extract and compute the sparse 2d features using DAISY descriptors which are matched using FlannBasedMatcher to finally build the tracks structure. + +To run this example you can type the following command in the opencv binaries directory specifying the file path and the camera intrinsics. + + ./example_sfm_scene_reconstruction image_paths_file.txt 350 240 360 + +This sample shows the estimated camera trajectory plus the sparse 3D reconstruction using the the OpenCV 3D visualizer (viz). + +On the next pictures, it's shown a screenshot where you can see the used images as input from the "Temple of the Dioskouroi" [1] and the obtained result after running the reconstruction API. + +

+ +

+

+ +

+ +On the next pictures, it's shown a screenshot where you can see the used images as input from la Sagrada Familia (BCN) [2] which you can find in the samples directory and the obtained result after running the reconstruction API. + +

+ +

+

+ +

+ + +[1] [http://vision.middlebury.edu/mview/data](http://vision.middlebury.edu/mview/data) + +[2] Penate Sanchez, A. and Moreno-Noguer, F. and Andrade Cetto, J. and Fleuret, F. (2014). LETHA: *Learning from High Quality Inputs for 3D Pose Estimation in Low Quality Images*. Proceedings of the International Conference on 3D vision (3DV). [[URL]](http://www.iri.upc.edu/research/webprojects/pau/datasets/sagfam) + + +Future Work +----------- + +* Update signatures documentation. +* Add prototype for dense reconstruction once is working (DAISY paper implementation). +* Decide which functions are kept since most of them are the same in calib3d. +* Finish to implement computeOrientation(). +* Find a good features matchig algorithm for reconstruction() in case we provide pure images for autocalibration (look into OpenMVG). diff --git a/modules/sfm/doc/pics/desktop_trajectory.png b/modules/sfm/doc/pics/desktop_trajectory.png new file mode 100644 index 00000000000..d242edef84b Binary files /dev/null and b/modules/sfm/doc/pics/desktop_trajectory.png differ diff --git a/modules/sfm/doc/pics/sagrada_familia_input.jpg b/modules/sfm/doc/pics/sagrada_familia_input.jpg new file mode 100644 index 00000000000..8b6baf74691 Binary files /dev/null and b/modules/sfm/doc/pics/sagrada_familia_input.jpg differ diff --git a/modules/sfm/doc/pics/sagrada_familia_reconstruction.jpg b/modules/sfm/doc/pics/sagrada_familia_reconstruction.jpg new file mode 100644 index 00000000000..7e29b98098b Binary files /dev/null and b/modules/sfm/doc/pics/sagrada_familia_reconstruction.jpg differ diff --git a/modules/sfm/doc/pics/temple_input.jpg b/modules/sfm/doc/pics/temple_input.jpg new file mode 100644 index 00000000000..7f1fa07e9a1 Binary files /dev/null and b/modules/sfm/doc/pics/temple_input.jpg differ diff --git a/modules/sfm/doc/pics/temple_reconstruction.jpg b/modules/sfm/doc/pics/temple_reconstruction.jpg new file mode 100644 index 00000000000..a273f75b831 Binary files /dev/null and b/modules/sfm/doc/pics/temple_reconstruction.jpg differ diff --git a/modules/sfm/include/opencv2/sfm.hpp b/modules/sfm/include/opencv2/sfm.hpp new file mode 100644 index 00000000000..f6e24cd43e8 --- /dev/null +++ b/modules/sfm/include/opencv2/sfm.hpp @@ -0,0 +1,102 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __OPENCV_SFM_HPP__ +#define __OPENCV_SFM_HPP__ + +#include +#include +#include +#include +#include +#if CERES_FOUND +#include +#include +#endif + +/** @defgroup sfm Structure From Motion + +The opencv_sfm module contains algorithms to perform 3d reconstruction +from 2d images.\n +The core of the module is based on a light version of +[Libmv](https://developer.blender.org/project/profile/59) originally +developed by Sameer Agarwal and Keir Mierle. + +__Whats is libmv?__ \n +libmv, also known as the Library for Multiview Reconstruction (or LMV), +is the computer vision backend for Blender's motion tracking abilities. +Unlike other vision libraries with general ambitions, libmv is focused +on algorithms for match moving, specifically targeting [Blender](https://developer.blender.org) as the +primary customer. Dense reconstruction, reconstruction from unorganized +photo collections, image recognition, and other tasks are not a focus +of libmv. + +__Development__ \n +libmv is officially under the Blender umbrella, and so is developed +on developer.blender.org. The [source repository](https://developer.blender.org/diffusion/LMV) can get checked out +independently from Blender. + +This module has been originally developed as a project for Google Summer of Code 2012-2015. + +@note + - Notice that it is compiled only when Eigen, GLog and GFlags are correctly installed.\n + Check installation instructions in the following tutorial: @ref tutorial_sfm_installation + + @{ + @defgroup conditioning Conditioning + @defgroup fundamental Fundamental + @defgroup numeric Numeric + @defgroup projection Projection + @defgroup robust Robust Estimation + @defgroup triangulation Triangulation + + @defgroup reconstruction Reconstruction + @note + - Notice that it is compiled only when Ceres Solver is correctly installed.\n + Check installation instructions in the following tutorial: @ref tutorial_sfm_installation + + + @defgroup simple_pipeline Simple Pipeline + @note + - Notice that it is compiled only when Ceres Solver is correctly installed.\n + Check installation instructions in the following tutorial: @ref tutorial_sfm_installation + + @} + +*/ + +#endif + +/* End of file. */ diff --git a/modules/sfm/include/opencv2/sfm/conditioning.hpp b/modules/sfm/include/opencv2/sfm/conditioning.hpp new file mode 100644 index 00000000000..45505d3c76f --- /dev/null +++ b/modules/sfm/include/opencv2/sfm/conditioning.hpp @@ -0,0 +1,123 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __OPENCV_CONDITIONING_HPP__ +#define __OPENCV_CONDITIONING_HPP__ + +#include + +namespace cv +{ +namespace sfm +{ + +//! @addtogroup conditioning +//! @{ + +/** Point conditioning (non isotropic). + @param points Input vector of N-dimensional points. + @param T Output 3x3 transformation matrix. + + Computes the transformation matrix such that the two principal moments of the set of points are equal to unity, + forming an approximately symmetric circular cloud of points of radius 1 about the origin.\n + Reference: @cite HartleyZ00 4.4.4 pag.109 +*/ +CV_EXPORTS_W +void +preconditionerFromPoints( InputArray points, + OutputArray T ); + +/** @brief Point conditioning (isotropic). + @param points Input vector of N-dimensional points. + @param T Output 3x3 transformation matrix. + + Computes the transformation matrix such that each coordinate direction will be scaled equally, + bringing the centroid to the origin with an average centroid \f$(1,1,1)^T\f$.\n + Reference: @cite HartleyZ00 4.4.4 pag.107. +*/ +CV_EXPORTS_W +void +isotropicPreconditionerFromPoints( InputArray points, + OutputArray T ); + +/** @brief Apply Transformation to points. + @param points Input vector of N-dimensional points. + @param T Input 3x3 transformation matrix such that \f$x = T*X\f$, where \f$X\f$ are the points to transform and \f$x\f$ the transformed points. + @param transformed_points Output vector of N-dimensional transformed points. +*/ +CV_EXPORTS_W +void +applyTransformationToPoints( InputArray points, + InputArray T, + OutputArray transformed_points ); + +/** @brief This function normalizes points (non isotropic). + @param points Input vector of N-dimensional points. + @param normalized_points Output vector of the same N-dimensional points but with mean 0 and average norm \f$\sqrt{2}\f$. + @param T Output 3x3 transform matrix such that \f$x = T*X\f$, where \f$X\f$ are the points to normalize and \f$x\f$ the normalized points. + + Internally calls @ref preconditionerFromPoints in order to get the scaling matrix before applying @ref applyTransformationToPoints. + This operation is an essential step before applying the DLT algorithm in order to consider the result as optimal.\n + Reference: @cite HartleyZ00 4.4.4 pag.109 +*/ +CV_EXPORTS_W +void +normalizePoints( InputArray points, + OutputArray normalized_points, + OutputArray T ); + +/** @brief This function normalizes points. (isotropic). + @param points Input vector of N-dimensional points. + @param normalized_points Output vector of the same N-dimensional points but with mean 0 and average norm \f$\sqrt{2}\f$. + @param T Output 3x3 transform matrix such that \f$x = T*X\f$, where \f$X\f$ are the points to normalize and \f$x\f$ the normalized points. + + Internally calls @ref preconditionerFromPoints in order to get the scaling matrix before applying @ref applyTransformationToPoints. + This operation is an essential step before applying the DLT algorithm in order to consider the result as optimal.\n + Reference: @cite HartleyZ00 4.4.4 pag.107. +*/ +CV_EXPORTS_W +void +normalizeIsotropicPoints( InputArray points, + OutputArray normalized_points, + OutputArray T ); + +//! @} sfm + +} /* namespace sfm */ +} /* namespace cv */ + +#endif + +/* End of file. */ \ No newline at end of file diff --git a/modules/sfm/include/opencv2/sfm/fundamental.hpp b/modules/sfm/include/opencv2/sfm/fundamental.hpp new file mode 100644 index 00000000000..34b3d534f4d --- /dev/null +++ b/modules/sfm/include/opencv2/sfm/fundamental.hpp @@ -0,0 +1,225 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __OPENCV_SFM_FUNDAMENTAL_HPP__ +#define __OPENCV_SFM_FUNDAMENTAL_HPP__ + +#include + +#include + +namespace cv +{ +namespace sfm +{ + +//! @addtogroup fundamental +//! @{ + +/** @brief Get projection matrices from Fundamental matrix + @param F Input 3x3 fundamental matrix. + @param P1 Output 3x4 one possible projection matrix. + @param P2 Output 3x4 another possible projection matrix. + */ +CV_EXPORTS_W +void +projectionsFromFundamental( InputArray F, + OutputArray P1, + OutputArray P2 ); + +/** @brief Get Fundamental matrix from Projection matrices. + @param P1 Input 3x4 first projection matrix. + @param P2 Input 3x4 second projection matrix. + @param F Output 3x3 fundamental matrix. + */ +CV_EXPORTS_W +void +fundamentalFromProjections( InputArray P1, + InputArray P2, + OutputArray F ); + +/** @brief Estimate the fundamental matrix between two dataset of 2D point (image coords space). + @param x1 Input 2xN Array of 2D points in view 1. + @param x2 Input 2xN Array of 2D points in view 2. + @param F Output 3x3 fundamental matrix. + + Uses the normalized 8-point fundamental matrix solver. + Reference: @cite HartleyZ00 11.2 pag.281 (x1 = x, x2 = x') + */ +CV_EXPORTS_W +void +normalizedEightPointSolver( InputArray x1, + InputArray x2, + OutputArray F ); + +/** @brief Computes the relative camera motion between two cameras. + @param R1 Input 3x3 first camera rotation matrix. + @param t1 Input 3x1 first camera translation vector. + @param R2 Input 3x3 second camera rotation matrix. + @param t2 Input 3x1 second camera translation vector. + @param R Output 3x3 relative rotation matrix. + @param t Output 3x1 relative translation vector. + + Given the motion parameters of two cameras, computes the motion parameters + of the second one assuming the first one to be at the origin. + If T1 and T2 are the camera motions, the computed relative motion is \f$T = T_2 T_1^{-1}\f$ + */ +CV_EXPORTS_W +void +relativeCameraMotion( InputArray R1, + InputArray t1, + InputArray R2, + InputArray t2, + OutputArray R, + OutputArray t ); + +/** Get Motion (R's and t's ) from Essential matrix. + @param E Input 3x3 essential matrix. + @param Rs Output vector of 3x3 rotation matrices. + @param ts Output vector of 3x1 translation vectors. + + Reference: @cite HartleyZ00 9.6 pag 259 (Result 9.19) + */ +CV_EXPORTS_W +void +motionFromEssential( InputArray E, + OutputArrayOfArrays Rs, + OutputArrayOfArrays ts ); + +/** Choose one of the four possible motion solutions from an essential matrix. + @param Rs Input vector of 3x3 rotation matrices. + @param ts Input vector of 3x1 translation vectors. + @param K1 Input 3x3 first camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. + @param x1 Input 2x1 vector with first 2d point. + @param K2 Input 3x3 second camera matrix. The parameters are similar to K1. + @param x2 Input 2x1 vector with second 2d point. + + Decides the right solution by checking that the triangulation of a match + x1--x2 lies in front of the cameras. Return index of the right solution or -1 if no solution. + + Reference: See @cite HartleyZ00 9.6 pag 259 (9.6.3 Geometrical interpretation of the 4 solutions). + */ +CV_EXPORTS_W +int motionFromEssentialChooseSolution( InputArrayOfArrays Rs, + InputArrayOfArrays ts, + InputArray K1, + InputArray x1, + InputArray K2, + InputArray x2 ); + +/** @brief Get Essential matrix from Fundamental and Camera matrices. + @param E Input 3x3 essential matrix. + @param K1 Input 3x3 first camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. + @param K2 Input 3x3 second camera matrix. The parameters are similar to K1. + @param F Output 3x3 fundamental matrix. + + Reference: @cite HartleyZ00 9.6 pag 257 (formula 9.12) or http://ai.stanford.edu/~birch/projective/node20.html + */ +CV_EXPORTS_W +void +fundamentalFromEssential( InputArray E, + InputArray K1, + InputArray K2, + OutputArray F ); + +/** @brief Get Essential matrix from Fundamental and Camera matrices. + @param F Input 3x3 fundamental matrix. + @param K1 Input 3x3 first camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. + @param K2 Input 3x3 second camera matrix. The parameters are similar to K1. + @param E Output 3x3 essential matrix. + + Reference: @cite HartleyZ00 9.6 pag 257 (formula 9.12) + */ +CV_EXPORTS_W +void +essentialFromFundamental( InputArray F, + InputArray K1, + InputArray K2, + OutputArray E ); + +/** @brief Get Essential matrix from Motion (R's and t's ). + @param R1 Input 3x3 first camera rotation matrix. + @param t1 Input 3x1 first camera translation vector. + @param R2 Input 3x3 second camera rotation matrix. + @param t2 Input 3x1 second camera translation vector. + @param E Output 3x3 essential matrix. + + Reference: @cite HartleyZ00 9.6 pag 257 (formula 9.12) + */ +CV_EXPORTS_W +void +essentialFromRt( InputArray R1, + InputArray t1, + InputArray R2, + InputArray t2, + OutputArray E ); + +/** @brief Normalizes the Fundamental matrix. + @param F Input 3x3 fundamental matrix. + @param F_normalized Output 3x3 normalized fundamental matrix. + + By default divides the fundamental matrix by its L2 norm. + */ +CV_EXPORTS_W +void +normalizeFundamental( InputArray F, + OutputArray F_normalized ); + +/** @brief Computes Absolute or Exterior Orientation (Pose Estimation) between 2 sets of 3D point. + @param x1 Input first 3xN or 2xN array of points. + @param x2 Input second 3xN or 2xN array of points. + @param R Output 3x3 computed rotation matrix. + @param t Output 3x1 computed translation vector. + @param s Output computed scale factor. + + Find the best transformation such that xp=projection*(s*R*x+t) (same as Pose Estimation, ePNP). + The routines below are only for the orthographic case for now. + */ +CV_EXPORTS_W +void +computeOrientation( InputArrayOfArrays x1, + InputArrayOfArrays x2, + OutputArray R, + OutputArray t, + double s ); + +//! @} sfm + +} /* namespace sfm */ +} /* namespace cv */ + +#endif + +/* End of file. */ diff --git a/modules/sfm/include/opencv2/sfm/numeric.hpp b/modules/sfm/include/opencv2/sfm/numeric.hpp new file mode 100644 index 00000000000..f191b2b9f24 --- /dev/null +++ b/modules/sfm/include/opencv2/sfm/numeric.hpp @@ -0,0 +1,94 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __OPENCV_SFM_NUMERIC_HPP__ +#define __OPENCV_SFM_NUMERIC_HPP__ + +#include + +#include + +namespace cv +{ +namespace sfm +{ + +//! @addtogroup numeric +//! @{ + +/** @brief Computes the mean and variance of a given matrix along its rows. + @param A Input NxN matrix. + @param mean Output Nx1 matrix with computed mean. + @param variance Output Nx1 matrix with computed variance. + + It computes in the same way as woud do @ref reduce but with \a Variance function. +*/ +CV_EXPORTS_W +void +meanAndVarianceAlongRows( InputArray A, + OutputArray mean, + OutputArray variance ); + +/** @brief Returns the 3x3 skew symmetric matrix of a vector. + @param x Input 3x1 vector. + + Reference: @cite HartleyZ00, p581, equation (A4.5). +*/ +CV_EXPORTS_W +Mat +skew( InputArray x ); + +///** @brief Returns the skew anti-symmetric matrix of a vector. +// @param x Input 3x3 matrix. +//*/ +//CV_EXPORTS +//Matx33d +//skewMat( const Vec3d &x ); +// +///** @brief Returns the skew anti-symmetric matrix of a vector with only the first two (independent) lines. +// @param x Input 3x3 matrix. +//*/ +//CV_EXPORTS +//Matx33d +//skewMatMinimal( const Vec3d &x ); + +//! @} numeric + +} /* namespace sfm */ +} /* namespace cv */ + +#endif + +/* End of file. */ diff --git a/modules/sfm/include/opencv2/sfm/projection.hpp b/modules/sfm/include/opencv2/sfm/projection.hpp new file mode 100644 index 00000000000..d796878382f --- /dev/null +++ b/modules/sfm/include/opencv2/sfm/projection.hpp @@ -0,0 +1,106 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __OPENCV_PROJECTION_HPP__ +#define __OPENCV_PROJECTION_HPP__ + +#include + +namespace cv +{ +namespace sfm +{ + +//! @addtogroup projection +//! @{ + +/** @brief Converts point coordinates from homogeneous to euclidean pixel coordinates. E.g., ((x,y,z)->(x/z, y/z)) + @param src Input vector of N-dimensional points. + @param dst Output vector of N-1-dimensional points. +*/ +CV_EXPORTS_W +void +homogeneousToEuclidean(InputArray src, OutputArray dst); + +/** @brief Converts points from Euclidean to homogeneous space. E.g., ((x,y)->(x,y,1)) + @param src Input vector of N-dimensional points. + @param dst Output vector of N+1-dimensional points. +*/ +CV_EXPORTS_W +void +euclideanToHomogeneous(InputArray src, OutputArray dst); + +/** @brief Get projection matrix P from K, R and t. + @param K Input 3x3 camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. + @param R Input 3x3 rotation matrix. + @param t Input 3x1 translation vector. + @param P Output 3x4 projection matrix. + + This function estimate the projection matrix by solving the following equation: \f$P = K * [R|t]\f$ + + */ +CV_EXPORTS_W +void +projectionFromKRt(InputArray K, InputArray R, InputArray t, OutputArray P); + +/** @brief Get K, R and t from projection matrix P, decompose using the RQ decomposition. + @param P Input 3x4 projection matrix. + @param K Output 3x3 camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. + @param R Output 3x3 rotation matrix. + @param t Output 3x1 translation vector. + + Reference: @cite HartleyZ00 A4.1.1 pag.579 + */ +CV_EXPORTS_W +void +KRtFromProjection( InputArray P, OutputArray K, OutputArray R, OutputArray t ); + +/** @brief Returns the depth of a point transformed by a rigid transform. + @param R Input 3x3 rotation matrix. + @param t Input 3x1 translation vector. + @param X Input 3x1 or 4x1 vector with the 3d point. + */ +CV_EXPORTS_W +double +depth( InputArray R, InputArray t, InputArray X); + +//! @} sfm + +} /* namespace sfm */ +} /* namespace cv */ + +#endif + +/* End of file. */ diff --git a/modules/sfm/include/opencv2/sfm/reconstruct.hpp b/modules/sfm/include/opencv2/sfm/reconstruct.hpp new file mode 100644 index 00000000000..2c1267deb72 --- /dev/null +++ b/modules/sfm/include/opencv2/sfm/reconstruct.hpp @@ -0,0 +1,143 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// + // + // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. + // + // By downloading, copying, installing or using the software you agree to this license. + // If you do not agree to this license, do not download, install, + // copy or use the software. + // + // + // License Agreement + // For Open Source Computer Vision Library + // + // Copyright (C) 2015, OpenCV Foundation, all rights reserved. + // Third party copyrights are property of their respective owners. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistribution's of source code must retain the above copyright notice, + // this list of conditions and the following disclaimer. + // + // * Redistribution's in binary form must reproduce the above copyright notice, + // this list of conditions and the following disclaimer in the documentation + // and/or other materials provided with the distribution. + // + // * The name of the copyright holders may not be used to endorse or promote products + // derived from this software without specific prior written permission. + // + // This software is provided by the copyright holders and contributors "as is" and + // any express or implied warranties, including, but not limited to, the implied + // warranties of merchantability and fitness for a particular purpose are disclaimed. + // In no event shall the Intel Corporation or contributors be liable for any direct, + // indirect, incidental, special, exemplary, or consequential damages + // (including, but not limited to, procurement of substitute goods or services; + // loss of use, data, or profits; or business interruption) however caused + // and on any theory of liability, whether in contract, strict liability, + // or tort (including negligence or otherwise) arising in any way out of + // the use of this software, even if advised of the possibility of such damage. + // + //M*/ + +#ifndef __OPENCV_SFM_RECONSTRUCT_HPP__ +#define __OPENCV_SFM_RECONSTRUCT_HPP__ + +#include +#include + +#include + +namespace cv +{ +namespace sfm +{ + +//! @addtogroup reconstruction +//! @{ + +#if defined(CV_DOXYGEN) || defined(CERES_FOUND) + +/** @brief Reconstruct 3d points from 2d correspondences while performing autocalibration. + @param points2d Input vector of vectors of 2d points (the inner vector is per image). + @param Ps Output vector with the 3x4 projections matrices of each image. + @param points3d Output array with estimated 3d points. + @param K Input/Output camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. Input parameters used as initial guess. + @param is_projective if true, the cameras are supposed to be projective. + + This method calls below signature and extracts projection matrices from estimated K, R and t. + + @note + - Tracks must be as precise as possible. It does not handle outliers and is very sensible to them. +*/ +CV_EXPORTS +void +reconstruct(InputArrayOfArrays points2d, OutputArray Ps, OutputArray points3d, InputOutputArray K, + bool is_projective = false); + +/** @brief Reconstruct 3d points from 2d correspondences while performing autocalibration. + @param points2d Input vector of vectors of 2d points (the inner vector is per image). + @param Rs Output vector of 3x3 rotations of the camera. + @param Ts Output vector of 3x1 translations of the camera. + @param points3d Output array with estimated 3d points. + @param K Input/Output camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. Input parameters used as initial guess. + @param is_projective if true, the cameras are supposed to be projective. + + Internally calls libmv simple pipeline routine with some default parameters by instatiating SFMLibmvEuclideanReconstruction class. + + @note + - Tracks must be as precise as possible. It does not handle outliers and is very sensible to them. + - To see a working example for camera motion reconstruction, check the following tutorial: @ref tutorial_sfm_trajectory_estimation. +*/ +CV_EXPORTS +void +reconstruct(InputArrayOfArrays points2d, OutputArray Rs, OutputArray Ts, InputOutputArray K, + OutputArray points3d, bool is_projective = false); + +/** @brief Reconstruct 3d points from 2d images while performing autocalibration. + @param images a vector of string with the images paths. + @param Ps Output vector with the 3x4 projections matrices of each image. + @param points3d Output array with estimated 3d points. + @param K Input/Output camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. Input parameters used as initial guess. + @param is_projective if true, the cameras are supposed to be projective. + + This method calls below signature and extracts projection matrices from estimated K, R and t. + + @note + - The images must be ordered as they were an image sequence. Additionally, each frame should be as close as posible to the previous and posterior. + - For now DAISY features are used in order to compute the 2d points tracks and it only works for 3-4 images. +*/ +CV_EXPORTS +void +reconstruct(const std::vector images, OutputArray Ps, OutputArray points3d, + InputOutputArray K, bool is_projective = false); + +/** @brief Reconstruct 3d points from 2d images while performing autocalibration. + @param images a vector of string with the images paths. + @param Rs Output vector of 3x3 rotations of the camera. + @param Ts Output vector of 3x1 translations of the camera. + @param points3d Output array with estimated 3d points. + @param K Input/Output camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. Input parameters used as initial guess. + @param is_projective if true, the cameras are supposed to be projective. + + Internally calls libmv simple pipeline routine with some default parameters by instatiating SFMLibmvEuclideanReconstruction class. + + @note + - The images must be ordered as they were an image sequence. Additionally, each frame should be as close as posible to the previous and posterior. + - For now DAISY features are used in order to compute the 2d points tracks and it only works for 3-4 images. + - To see a working example for scene reconstruction, check the following tutorial: @ref tutorial_sfm_scene_reconstruction. +*/ +CV_EXPORTS +void +reconstruct(const std::vector images, OutputArray Rs, OutputArray Ts, + InputOutputArray K, OutputArray points3d, bool is_projective = false); + +#endif /* CV_DOXYGEN || CERES_FOUND */ + +//! @} sfm + +} /* namespace cv */ +} /* namespace sfm */ + +#endif + +/* End of file. */ diff --git a/modules/sfm/include/opencv2/sfm/robust.hpp b/modules/sfm/include/opencv2/sfm/robust.hpp new file mode 100644 index 00000000000..368b32a9023 --- /dev/null +++ b/modules/sfm/include/opencv2/sfm/robust.hpp @@ -0,0 +1,106 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __OPENCV_SFM_ROBUST_HPP__ +#define __OPENCV_SFM_ROBUST_HPP__ + +#ifdef __cplusplus + +#include + +namespace cv +{ +namespace sfm +{ + +//! @addtogroup robust +//! @{ + +/** @brief Estimate robustly the fundamental matrix between two dataset of 2D point (image coords space). + @param x1 Input 2xN Array of 2D points in view 1. + @param x2 Input 2xN Array of 2D points in view 2. + @param max_error maximum error (in pixels). + @param F Output 3x3 fundamental matrix such that \f$x_2^T F x_1=0\f$. + @param inliers Output 1xN vector that contains the indexes of the detected inliers. + @param outliers_probability outliers probability (in ]0,1[). + The number of iterations is controlled using the following equation: + \f$k = \frac{log(1-p)}{log(1.0 - w^n )}\f$ where \f$k\f$, \f$w\f$ and \f$n\f$ are the number of + iterations, the inliers ratio and minimun number of selected independent samples. + The more this value is high, the less the function selects ramdom samples. + +The fundamental solver relies on the 8 point solution. Returns the best error (in pixels), associated to the solution F. + */ +CV_EXPORTS_W +double +fundamentalFromCorrespondences8PointRobust( InputArray x1, + InputArray x2, + double max_error, + OutputArray F, + OutputArray inliers, + double outliers_probability = 1e-2 ); + +/** @brief Estimate robustly the fundamental matrix between two dataset of 2D point (image coords space). + @param x1 Input 2xN Array of 2D points in view 1. + @param x2 Input 2xN Array of 2D points in view 2. + @param max_error maximum error (in pixels). + @param F Output 3x3 fundamental matrix such that \f$x_2^T F x_1=0\f$. + @param inliers Output 1xN vector that contains the indexes of the detected inliers. + @param outliers_probability outliers probability (in ]0,1[). + The number of iterations is controlled using the following equation: + \f$k = \frac{log(1-p)}{log(1.0 - w^n )}\f$ where \f$k\f$, \f$w\f$ and \f$n\f$ are the number of + iterations, the inliers ratio and minimun number of selected independent samples. + The more this value is high, the less the function selects ramdom samples. + +The fundamental solver relies on the 7 point solution. Returns the best error (in pixels), associated to the solution F. + */ +CV_EXPORTS_W +double +fundamentalFromCorrespondences7PointRobust( InputArray x1, + InputArray x2, + double max_error, + OutputArray F, + OutputArray inliers, + double outliers_probability = 1e-2 ); + +//! @} sfm + +} /* namespace cv */ +} /* namespace sfm */ + +#endif /* __cplusplus */ + +#endif + +/* End of file. */ \ No newline at end of file diff --git a/modules/sfm/include/opencv2/sfm/simple_pipeline.hpp b/modules/sfm/include/opencv2/sfm/simple_pipeline.hpp new file mode 100644 index 00000000000..79e96e5874d --- /dev/null +++ b/modules/sfm/include/opencv2/sfm/simple_pipeline.hpp @@ -0,0 +1,290 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __OPENCV_SFM_SIMPLE_PIPELINE_HPP__ +#define __OPENCV_SFM_SIMPLE_PIPELINE_HPP__ + +#include + +namespace cv +{ +namespace sfm +{ + +//! @addtogroup simple_pipeline +//! @{ + +/** @brief Different camera models that libmv supports. + */ +enum { + SFM_DISTORTION_MODEL_POLYNOMIAL = 0, // LIBMV_DISTORTION_MODEL_POLYNOMIAL + SFM_DISTORTION_MODEL_DIVISION = 1, // LIBMV_DISTORTION_MODEL_DIVISION +}; + +/** @brief Data structure describing the camera model and its parameters. + @param _distortion_model Type of camera model. + @param _focal_length focal length of the camera (in pixels). + @param _principal_point_x principal point of the camera in the x direction (in pixels). + @param _principal_point_y principal point of the camera in the y direction (in pixels). + @param _polynomial_k1 radial distortion parameter. + @param _polynomial_k2 radial distortion parameter. + @param _polynomial_k3 radial distortion parameter. + @param _polynomial_p1 radial distortion parameter. + @param _polynomial_p2 radial distortion parameter. + + Is assumed that modern cameras have their principal point in the image center.\n + In case that the camera model was SFM_DISTORTION_MODEL_DIVISION, it's only needed to provide + _polynomial_k1 and _polynomial_k2 which will be assigned as division distortion parameters. + */ +class CV_EXPORTS_W_SIMPLE libmv_CameraIntrinsicsOptions +{ +public: + CV_WRAP + libmv_CameraIntrinsicsOptions(const int _distortion_model=0, + const double _focal_length=0, + const double _principal_point_x=0, + const double _principal_point_y=0, + const double _polynomial_k1=0, + const double _polynomial_k2=0, + const double _polynomial_k3=0, + const double _polynomial_p1=0, + const double _polynomial_p2=0) + : distortion_model(_distortion_model), + image_width(2*_principal_point_x), + image_height(2*_principal_point_y), + focal_length(_focal_length), + principal_point_x(_principal_point_x), + principal_point_y(_principal_point_y), + polynomial_k1(_polynomial_k1), + polynomial_k2(_polynomial_k2), + polynomial_k3(_polynomial_k3), + division_k1(_polynomial_p1), + division_k2(_polynomial_p2) + { + if ( _distortion_model == SFM_DISTORTION_MODEL_DIVISION ) + { + division_k1 = _polynomial_k1; + division_k2 = _polynomial_k2; + } + } + + // Common settings of all distortion models. + CV_PROP_RW int distortion_model; + CV_PROP_RW int image_width, image_height; + CV_PROP_RW double focal_length; + CV_PROP_RW double principal_point_x, principal_point_y; + + // Radial distortion model. + CV_PROP_RW double polynomial_k1, polynomial_k2, polynomial_k3; + CV_PROP_RW double polynomial_p1, polynomial_p2; + + // Division distortion model. + CV_PROP_RW double division_k1, division_k2; +}; + + +/** @brief All internal camera parameters that libmv is able to refine. + */ +enum { SFM_REFINE_FOCAL_LENGTH = (1 << 0), // libmv::BUNDLE_FOCAL_LENGTH + SFM_REFINE_PRINCIPAL_POINT = (1 << 1), // libmv::BUNDLE_PRINCIPAL_POINT + SFM_REFINE_RADIAL_DISTORTION_K1 = (1 << 2), // libmv::BUNDLE_RADIAL_K1 + SFM_REFINE_RADIAL_DISTORTION_K2 = (1 << 4), // libmv::BUNDLE_RADIAL_K2 +}; + + +/** @brief Data structure describing the reconstruction options. + @param _keyframe1 first keyframe used in order to initialize the reconstruction. + @param _keyframe2 second keyframe used in order to initialize the reconstruction. + @param _refine_intrinsics camera parameter or combination of parameters to refine. + @param _select_keyframes allows to select automatically the initial keyframes. If 1 then autoselection is enabled. If 0 then is disabled. + @param _verbosity_level verbosity logs level for Glog. If -1 then logs are disabled, otherwise the log level will be the input integer. + */ +class CV_EXPORTS_W_SIMPLE libmv_ReconstructionOptions +{ +public: + CV_WRAP + libmv_ReconstructionOptions(const int _keyframe1=1, + const int _keyframe2=2, + const int _refine_intrinsics=1, + const int _select_keyframes=1, + const int _verbosity_level=-1) + : keyframe1(_keyframe1), keyframe2(_keyframe2), + refine_intrinsics(_refine_intrinsics), + select_keyframes(_select_keyframes), + verbosity_level(_verbosity_level) {} + + CV_PROP_RW int keyframe1, keyframe2; + CV_PROP_RW int refine_intrinsics; + CV_PROP_RW int select_keyframes; + CV_PROP_RW int verbosity_level; +}; + + +/** @brief base class BaseSFM declares a common API that would be used in a typical scene reconstruction scenario + */ +class CV_EXPORTS_W BaseSFM +{ +public: + virtual ~BaseSFM() {}; + + CV_WRAP + virtual void run(InputArrayOfArrays points2d) = 0; + + CV_WRAP + virtual void run(InputArrayOfArrays points2d, InputOutputArray K, OutputArray Rs, + OutputArray Ts, OutputArray points3d) = 0; + + virtual void run(const std::vector &images) = 0; + virtual void run(const std::vector &images, InputOutputArray K, OutputArray Rs, + OutputArray Ts, OutputArray points3d) = 0; + + CV_WRAP virtual double getError() const = 0; + CV_WRAP virtual void getPoints(OutputArray points3d) = 0; + CV_WRAP virtual cv::Mat getIntrinsics() const = 0; + CV_WRAP virtual void getCameras(OutputArray Rs, OutputArray Ts) = 0; + + CV_WRAP + virtual void + setReconstructionOptions(const libmv_ReconstructionOptions &libmv_reconstruction_options) = 0; + + CV_WRAP + virtual void + setCameraIntrinsicOptions(const libmv_CameraIntrinsicsOptions &libmv_camera_intrinsics_options) = 0; +}; + +/** @brief SFMLibmvEuclideanReconstruction class provides an interface with the Libmv Structure From Motion pipeline. + */ +class CV_EXPORTS_W SFMLibmvEuclideanReconstruction : public BaseSFM +{ +public: + /** @brief Calls the pipeline in order to perform Eclidean reconstruction. + @param points2d Input vector of vectors of 2d points (the inner vector is per image). + + @note + - Tracks must be as precise as possible. It does not handle outliers and is very sensible to them. + */ + CV_WRAP + virtual void run(InputArrayOfArrays points2d) = 0; + + /** @brief Calls the pipeline in order to perform Eclidean reconstruction. + @param points2d Input vector of vectors of 2d points (the inner vector is per image). + @param K Input/Output camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. Input parameters used as initial guess. + @param Rs Output vector of 3x3 rotations of the camera. + @param Ts Output vector of 3x1 translations of the camera. + @param points3d Output array with estimated 3d points. + + @note + - Tracks must be as precise as possible. It does not handle outliers and is very sensible to them. + */ + CV_WRAP + virtual void run(InputArrayOfArrays points2d, InputOutputArray K, OutputArray Rs, + OutputArray Ts, OutputArray points3d) = 0; + + /** @brief Calls the pipeline in order to perform Eclidean reconstruction. + @param images a vector of string with the images paths. + + @note + - The images must be ordered as they were an image sequence. Additionally, each frame should be as close as posible to the previous and posterior. + - For now DAISY features are used in order to compute the 2d points tracks and it only works for 3-4 images. + */ + virtual void run(const std::vector &images) = 0; + + /** @brief Calls the pipeline in order to perform Eclidean reconstruction. + @param images a vector of string with the images paths. + @param K Input/Output camera matrix \f$K = \vecthreethree{f_x}{0}{c_x}{0}{f_y}{c_y}{0}{0}{1}\f$. Input parameters used as initial guess. + @param Rs Output vector of 3x3 rotations of the camera. + @param Ts Output vector of 3x1 translations of the camera. + @param points3d Output array with estimated 3d points. + + @note + - The images must be ordered as they were an image sequence. Additionally, each frame should be as close as posible to the previous and posterior. + - For now DAISY features are used in order to compute the 2d points tracks and it only works for 3-4 images. + */ + virtual void run(const std::vector &images, InputOutputArray K, OutputArray Rs, + OutputArray Ts, OutputArray points3d) = 0; + + /** @brief Returns the computed reprojection error. + */ + CV_WRAP + virtual double getError() const = 0; + + /** @brief Returns the estimated 3d points. + @param points3d Output array with estimated 3d points. + */ + CV_WRAP + virtual void getPoints(OutputArray points3d) = 0; + + /** @brief Returns the refined camera calibration matrix. + */ + CV_WRAP + virtual cv::Mat getIntrinsics() const = 0; + + /** @brief Returns the estimated camera extrinsic parameters. + @param Rs Output vector of 3x3 rotations of the camera. + @param Ts Output vector of 3x1 translations of the camera. + */ + CV_WRAP + virtual void getCameras(OutputArray Rs, OutputArray Ts) = 0; + + /** @brief Setter method for reconstruction options. + @param libmv_reconstruction_options struct with reconstruction options such as initial keyframes, + automatic keyframe selection, parameters to refine and the verbosity level. + */ + CV_WRAP + virtual void + setReconstructionOptions(const libmv_ReconstructionOptions &libmv_reconstruction_options) = 0; + + /** @brief Setter method for camera intrinsic options. + @param libmv_camera_intrinsics_options struct with camera intrinsic options such as camera model and + the internal camera parameters. + */ + CV_WRAP + virtual void + setCameraIntrinsicOptions(const libmv_CameraIntrinsicsOptions &libmv_camera_intrinsics_options) = 0; + + /** @brief Creates an instance of the SFMLibmvEuclideanReconstruction class. Initializes Libmv. */ + static Ptr + create(const libmv_CameraIntrinsicsOptions &camera_instrinsic_options=libmv_CameraIntrinsicsOptions(), + const libmv_ReconstructionOptions &reconstruction_options=libmv_ReconstructionOptions()); + }; + +//! @} sfm + +} /* namespace cv */ +} /* namespace sfm */ + +#endif + +/* End of file. */ diff --git a/modules/sfm/include/opencv2/sfm/triangulation.hpp b/modules/sfm/include/opencv2/sfm/triangulation.hpp new file mode 100644 index 00000000000..99bf05b524c --- /dev/null +++ b/modules/sfm/include/opencv2/sfm/triangulation.hpp @@ -0,0 +1,69 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __OPENCV_SFM_TRIANGULATION_HPP__ +#define __OPENCV_SFM_TRIANGULATION_HPP__ + +#include + +namespace cv +{ +namespace sfm +{ + +//! @addtogroup triangulation +//! @{ + +/** @brief Reconstructs bunch of points by triangulation. + @param points2d Input vector of vectors of 2d points (the inner vector is per image). Has to be 2 X N. + @param projection_matrices Input vector with 3x4 projections matrices of each image. + @param points3d Output array with computed 3d points. Is 3 x N. + + Triangulates the 3d position of 2d correspondences between several images. + Reference: Internally it uses DLT method @cite HartleyZ00 12.2 pag.312 +*/ +CV_EXPORTS_W +void +triangulatePoints(InputArrayOfArrays points2d, InputArrayOfArrays projection_matrices, + OutputArray points3d); + +//! @} sfm + +} /* namespace sfm */ +} /* namespace cv */ + +#endif + +/* End of file. */ diff --git a/modules/sfm/samples/data/backyard.blend b/modules/sfm/samples/data/backyard.blend new file mode 100644 index 00000000000..62d317e45b0 Binary files /dev/null and b/modules/sfm/samples/data/backyard.blend differ diff --git a/modules/sfm/samples/data/backyard_tracks.txt b/modules/sfm/samples/data/backyard_tracks.txt new file mode 100644 index 00000000000..6a108c6ab96 --- /dev/null +++ b/modules/sfm/samples/data/backyard_tracks.txt @@ -0,0 +1,63 @@ +642.00 415.00 643.03 417.76 644.27 419.38 646.46 418.84 646.97 421.63 646.11 422.53 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +683.00 134.00 683.39 135.78 684.46 136.37 685.65 135.53 683.91 138.36 680.94 138.20 677.61 139.01 675.47 139.49 674.54 137.65 673.67 136.83 671.58 137.38 670.32 138.06 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +370.00 173.00 370.44 174.72 371.19 175.38 372.25 175.17 370.86 178.49 368.39 179.64 365.41 181.18 363.57 181.86 362.55 180.14 361.51 179.14 359.57 179.75 358.40 180.38 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +550.00 137.00 550.17 138.71 551.03 139.34 551.93 138.87 550.34 141.86 547.60 142.26 544.40 143.37 542.42 143.88 541.47 142.14 540.42 141.14 538.41 141.82 537.19 142.47 536.69 144.31 536.54 146.24 536.71 146.61 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +315.00 423.00 315.49 425.51 316.26 426.61 318.21 426.09 318.20 429.37 317.00 431.42 315.32 433.13 313.47 434.74 312.58 433.40 312.17 432.01 310.59 432.52 309.39 433.36 308.95 434.93 308.86 437.33 308.77 437.69 309.92 439.31 311.20 442.22 311.59 444.07 313.20 445.47 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +447.00 425.00 447.67 427.64 448.62 428.92 450.68 428.26 450.89 431.40 449.94 433.07 448.49 434.17 446.76 435.69 445.93 434.32 445.48 432.97 444.07 433.41 442.98 434.20 442.58 435.90 442.68 438.31 442.63 438.81 443.95 440.75 445.69 443.45 446.68 444.43 448.85 444.32 450.85 444.57 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +770.00 422.00 771.40 424.94 772.93 426.73 775.46 426.02 776.31 428.68 775.32 429.02 773.45 428.70 771.51 429.69 770.39 428.29 769.88 427.05 768.20 427.11 767.00 427.60 767.16 429.64 767.82 432.38 767.91 433.04 770.07 435.52 772.57 438.82 773.56 440.83 776.14 443.53 778.26 445.12 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +161.00 421.00 161.34 423.40 162.20 424.21 164.30 423.79 164.09 427.29 162.82 429.88 160.79 432.33 158.75 434.05 157.96 432.72 157.56 431.39 155.93 431.93 154.56 432.87 154.02 434.35 153.82 436.57 153.69 436.81 154.94 438.08 156.16 440.98 156.35 442.78 158.37 443.32 140.51 442.90 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +624.00 25.00 624.10 26.45 624.89 26.56 625.11 26.18 622.65 29.17 618.99 29.10 615.16 30.29 613.20 30.52 612.09 28.44 610.88 27.61 608.61 28.39 607.30 28.99 606.75 31.20 606.41 32.75 606.46 33.20 608.00 36.00 608.11 38.07 608.11 39.91 609.62 41.88 609.53 44.08 609.00 47.00 610.61 48.98 609.12 49.93 606.48 51.16 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +30.00 416.00 30.29 418.21 31.18 418.91 33.65 418.44 33.03 422.10 31.08 425.29 28.53 428.24 25.85 430.21 25.06 428.82 24.57 427.55 22.51 428.18 20.80 429.13 19.99 430.58 19.55 432.68 19.42 432.76 20.77 433.77 22.03 436.56 22.09 438.31 23.65 439.86 24.88 441.43 23.72 443.07 24.36 442.71 24.64 442.42 21.78 442.39 21.57 443.17 21.87 443.82 19.44 443.76 19.68 442.03 22.61 439.14 20.63 438.22 20.65 436.36 19.45 433.59 15.99 430.31 11.61 426.11 8.54 422.84 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +777.00 212.00 777.68 214.04 779.00 214.82 780.82 214.12 779.68 216.56 777.21 216.04 773.88 216.38 771.82 217.18 770.79 215.43 769.89 214.41 767.59 214.86 766.32 215.66 766.02 217.46 766.10 219.53 766.24 220.03 768.28 222.57 769.48 225.07 770.28 226.99 772.73 229.12 773.75 231.09 773.32 234.04 775.29 236.84 775.10 238.42 772.71 239.65 773.54 242.81 774.09 246.58 772.62 249.66 774.23 250.75 774.93 247.79 771.34 245.32 770.59 242.49 767.62 239.99 762.37 235.52 757.49 232.61 754.22 231.28 752.53 230.80 755.63 232.20 756.17 232.74 755.53 237.41 762.52 240.66 772.69 238.60 785.24 241.10 765.24 240.06 777.53 241.97 784.93 245.87 790.32 244.58 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +390.00 311.00 390.58 313.20 391.55 314.15 393.23 313.72 392.86 317.02 391.33 318.39 389.23 319.83 387.44 321.04 386.58 319.49 386.10 318.33 384.49 318.89 383.39 319.64 383.07 321.32 383.17 323.47 383.15 323.83 384.61 325.72 385.87 328.50 386.22 330.31 387.83 332.32 389.00 334.30 388.38 336.54 389.47 337.86 389.54 338.66 387.44 339.10 387.77 341.07 388.25 343.55 386.72 344.89 387.45 344.84 388.94 342.28 386.73 340.66 386.36 338.34 384.70 335.84 381.09 331.82 377.30 328.00 374.63 325.59 373.16 324.03 375.72 324.52 376.85 324.99 375.87 329.25 380.71 331.87 389.11 330.58 398.59 332.50 407.75 335.34 415.26 337.58 423.16 342.11 427.69 342.89 431.40 344.15 433.69 346.54 430.79 345.63 429.65 344.49 426.54 340.07 419.56 336.13 416.19 338.57 414.11 339.37 409.17 341.59 403.33 340.00 398.77 337.54 393.54 336.44 387.45 336.70 379.78 337.71 375.45 338.41 375.14 339.35 374.26 341.13 375.55 345.10 380.43 349.56 386.77 352.83 392.30 356.10 397.48 358.19 403.47 357.77 406.85 356.91 411.93 355.67 417.29 354.84 422.53 354.94 429.97 355.07 434.45 353.77 437.03 357.21 440.63 362.90 442.82 366.46 446.44 370.32 449.30 370.26 449.24 372.63 451.52 375.67 454.06 378.88 455.95 381.05 455.94 383.84 453.18 385.20 449.14 385.04 445.16 382.79 441.06 384.90 436.89 383.40 432.14 382.24 427.74 379.61 420.97 374.47 415.81 372.38 412.55 370.64 410.68 372.15 411.36 376.29 413.92 376.80 418.37 377.23 422.68 376.31 +120.00 302.00 120.37 303.97 121.30 304.58 123.04 304.44 122.25 308.05 120.28 310.43 117.59 312.74 115.41 313.98 114.48 312.43 113.79 311.26 111.74 311.94 110.37 312.74 109.85 314.19 109.73 316.12 109.68 316.13 111.19 317.64 112.35 320.34 112.47 322.02 114.22 323.66 115.28 325.35 114.44 326.91 115.61 327.00 115.53 327.07 113.01 327.14 113.22 328.26 113.55 329.46 111.67 329.48 112.46 328.14 113.90 325.60 111.15 324.58 110.58 322.56 108.52 320.04 104.29 316.41 99.86 312.21 96.70 309.29 94.95 306.87 97.63 306.55 98.49 306.56 96.75 310.48 102.12 311.50 111.42 310.28 121.68 311.51 131.47 312.69 139.33 313.95 147.16 318.71 151.28 319.94 154.48 321.87 155.81 325.77 151.53 327.21 149.55 325.99 145.14 322.91 136.52 320.52 131.58 323.16 128.25 324.27 121.54 325.99 114.24 324.04 108.40 320.98 101.74 319.14 93.86 319.00 84.25 319.94 78.68 320.24 77.54 321.37 75.85 321.69 76.68 324.05 81.57 327.73 88.18 331.26 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +95.00 19.00 95.25 20.57 96.08 20.82 96.40 21.68 94.33 25.32 90.57 27.24 86.69 29.18 84.38 29.23 82.81 27.12 81.19 26.14 78.76 26.75 77.20 27.26 76.81 29.14 76.75 30.68 76.86 30.73 78.98 33.43 79.60 35.93 80.00 37.89 82.03 39.99 82.41 42.07 82.23 43.27 84.49 43.94 83.07 43.90 80.43 44.23 81.50 45.96 81.20 47.06 79.64 46.75 80.78 45.23 78.28 43.02 73.17 41.81 70.25 39.37 64.93 36.10 57.39 31.49 51.24 26.55 46.19 23.29 44.14 21.40 46.23 21.87 43.75 22.73 42.89 26.91 49.70 27.86 57.06 28.34 67.71 31.63 77.03 33.42 84.32 36.26 90.12 41.91 90.80 41.72 91.80 45.09 87.31 48.16 78.45 47.20 74.31 44.47 62.33 38.39 49.88 35.12 42.74 35.67 36.13 34.71 25.85 33.23 15.53 26.96 6.44 21.74 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +515.00 311.00 515.69 313.27 516.75 314.39 518.51 313.88 518.29 316.94 516.86 317.81 514.75 318.75 513.01 319.87 512.20 318.34 511.67 317.30 510.07 317.86 509.01 318.50 508.79 320.27 508.97 322.48 509.04 322.90 510.62 325.03 511.95 327.83 512.30 329.81 514.14 331.82 515.36 333.95 514.94 336.41 516.14 338.31 516.22 339.51 514.25 340.13 514.75 342.44 515.38 345.52 514.03 347.49 514.75 348.01 516.35 345.37 514.22 343.41 513.83 340.98 512.28 338.54 508.68 334.35 504.85 330.67 502.25 328.62 500.79 327.44 503.55 328.23 504.86 328.95 504.15 333.33 509.28 336.53 517.97 335.32 527.94 337.51 537.56 341.10 545.55 343.82 554.15 348.20 559.24 348.62 563.34 349.53 566.21 351.14 563.47 349.06 562.56 347.95 559.59 342.92 552.65 338.23 549.58 340.32 547.87 340.92 543.21 343.33 537.47 341.93 533.18 339.61 528.06 338.89 522.17 339.28 514.57 340.20 510.47 341.08 510.43 341.91 509.83 344.28 511.54 348.90 516.95 353.66 523.80 356.81 529.93 359.45 535.68 360.77 542.15 358.86 545.96 356.77 533.33 354.55 538.72 353.35 544.40 352.87 552.42 352.39 557.08 350.72 560.11 353.56 564.36 359.33 567.17 362.27 571.35 365.69 574.68 364.78 574.78 367.37 577.65 371.16 580.74 374.71 582.98 377.05 583.49 379.54 581.04 380.64 577.36 379.83 573.28 378.19 569.55 381.16 565.57 380.13 560.95 378.87 556.75 376.31 550.38 370.54 544.72 369.29 541.70 367.70 539.91 370.11 541.57 373.64 544.70 373.60 548.86 374.95 553.69 374.21 +173.00 193.00 173.42 194.84 174.41 195.44 175.64 195.60 174.45 199.20 172.09 201.14 169.02 203.19 167.01 203.89 165.87 202.33 165.10 201.36 163.05 202.08 161.69 202.63 161.20 204.34 161.26 206.14 161.29 206.16 162.98 208.26 164.10 210.83 164.24 212.49 166.08 214.26 166.85 216.07 166.12 217.74 167.73 218.21 167.29 218.19 164.91 218.42 165.50 220.04 165.81 221.31 164.01 221.48 165.17 220.36 165.43 217.89 162.00 216.88 160.85 214.65 157.92 212.08 152.94 208.26 148.28 203.84 144.82 200.99 143.22 198.88 145.62 198.79 145.53 199.23 144.10 203.37 150.01 204.33 158.26 203.62 168.02 205.73 177.27 207.50 184.73 209.38 191.79 214.28 194.72 214.65 197.18 217.01 196.81 220.38 191.28 220.67 189.26 219.02 182.94 214.94 173.66 212.30 168.81 214.22 164.97 214.71 157.93 215.85 150.67 212.62 144.52 209.29 137.82 206.85 129.62 206.39 119.83 206.50 114.48 206.32 112.92 207.23 111.63 207.33 113.20 210.09 117.59 214.15 123.55 217.85 127.17 222.10 130.67 225.07 133.92 227.54 135.17 228.08 138.91 228.17 142.34 228.63 149.99 226.04 155.48 227.39 158.64 226.74 158.96 231.98 161.64 236.86 158.46 245.48 164.52 245.24 164.74 246.54 164.30 248.57 166.17 249.79 168.26 251.33 168.52 253.48 166.76 255.69 161.60 256.82 155.55 255.87 150.26 252.72 145.02 251.94 139.55 248.12 133.74 245.76 126.88 241.77 122.61 232.95 115.59 230.25 111.42 227.36 109.63 227.06 105.27 233.07 110.60 230.86 115.13 230.85 118.28 228.32 +675.00 282.00 675.69 284.21 676.97 285.34 678.73 284.56 678.20 287.40 676.46 287.51 674.03 287.97 672.20 288.92 671.19 287.31 670.63 286.26 668.91 286.73 667.65 287.41 667.49 289.19 667.75 291.41 667.85 291.99 669.65 294.32 671.04 296.96 671.67 298.87 673.67 300.99 674.98 303.17 674.48 306.06 675.99 308.57 676.01 310.09 673.80 311.04 674.44 313.82 675.16 317.48 673.79 320.12 674.78 321.27 676.16 318.54 673.42 316.29 672.73 313.61 670.78 311.16 666.44 306.74 661.96 303.45 659.12 301.73 657.49 301.05 660.46 302.36 661.72 303.18 660.71 307.59 666.75 311.33 676.21 309.96 687.30 312.51 698.24 316.63 707.33 319.78 717.17 323.79 722.67 323.67 727.54 324.20 730.51 324.49 727.20 320.93 726.38 319.74 722.61 313.72 714.85 308.40 711.63 310.23 709.91 310.39 704.91 312.65 698.82 311.10 694.16 309.20 688.74 308.68 682.18 309.11 674.01 309.94 669.66 310.79 669.70 311.43 669.35 314.52 671.64 319.69 677.89 324.59 685.52 327.29 692.10 329.13 698.26 329.15 704.64 325.23 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +433.00 64.00 433.04 65.52 433.72 65.92 434.04 65.83 431.94 69.06 428.71 69.68 425.33 71.22 423.29 71.50 422.07 69.49 420.90 68.56 418.69 69.21 417.28 69.88 416.79 71.81 416.53 73.56 416.57 73.95 418.03 76.45 418.47 78.92 418.48 80.78 419.88 82.77 419.92 84.85 419.40 87.06 420.98 88.55 419.68 89.25 417.06 90.10 417.49 92.50 416.70 94.82 414.83 96.08 415.40 95.31 413.35 92.35 408.70 90.57 406.18 87.66 401.68 84.62 395.42 80.12 389.93 76.21 385.47 73.80 383.30 72.49 384.38 73.00 382.24 73.57 380.23 78.21 385.21 78.95 391.26 77.32 399.53 79.56 407.33 81.26 413.59 83.23 418.55 86.71 419.73 85.25 421.06 86.75 418.16 87.36 411.23 84.86 408.51 82.32 400.32 75.27 391.06 71.67 385.52 72.25 380.94 71.40 373.34 71.61 366.16 67.05 359.56 63.44 352.60 60.60 343.53 59.89 334.44 59.67 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +259.00 313.00 259.52 315.16 260.47 315.94 262.22 315.68 261.74 319.13 260.10 321.09 257.83 322.94 255.99 324.24 255.19 322.64 254.66 321.52 252.93 322.18 251.76 322.88 251.38 324.48 251.40 326.54 251.40 326.69 252.93 328.48 254.12 331.23 254.39 332.94 256.03 334.79 257.10 336.64 256.43 338.56 257.57 339.34 257.61 339.72 255.47 340.00 255.80 341.52 256.26 343.37 254.66 344.13 255.41 343.50 256.95 340.90 254.80 339.61 254.39 337.49 252.79 334.95 249.22 331.15 245.41 327.08 242.76 324.48 241.21 322.51 243.85 322.60 245.01 322.88 243.79 326.97 248.75 328.85 257.43 327.57 267.05 329.20 276.32 331.29 283.85 332.98 291.61 337.71 296.05 338.72 299.57 340.32 301.57 343.52 298.35 343.78 297.03 342.63 293.73 338.90 286.49 335.70 282.64 338.28 280.23 339.28 274.85 341.37 268.74 339.70 263.91 336.93 258.44 335.59 251.90 335.68 243.87 336.73 239.28 337.24 238.75 338.28 237.63 339.42 238.82 342.67 243.55 346.76 249.88 350.19 255.38 353.91 260.44 356.72 266.42 357.86 269.78 357.99 274.80 357.49 280.13 357.39 285.41 358.12 292.83 358.84 297.27 358.18 299.72 362.20 303.01 367.68 305.05 371.68 308.38 375.75 311.14 376.46 310.79 378.88 312.83 381.24 314.98 383.80 316.63 385.91 316.32 388.98 313.20 390.56 308.81 390.77 304.59 388.34 300.09 389.50 295.60 387.43 290.53 386.35 285.93 383.53 278.99 378.45 273.55 376.16 270.09 374.19 267.92 375.02 268.29 378.99 270.69 379.84 274.98 380.09 279.33 378.79 +221.00 76.00 221.06 77.69 221.92 77.98 222.42 78.43 220.50 81.93 217.16 83.46 213.76 85.32 211.76 85.54 210.48 83.54 209.21 82.55 206.85 83.33 205.53 83.92 204.94 85.81 204.83 87.35 204.96 87.54 206.57 90.01 207.13 92.45 207.31 94.39 208.94 96.25 209.32 98.26 208.92 99.93 210.65 100.69 209.51 101.07 207.01 101.51 207.47 103.31 207.23 104.91 205.43 105.14 206.33 103.98 204.55 101.49 200.10 100.11 197.65 97.62 193.33 94.75 186.94 90.25 181.51 86.01 177.14 83.13 175.06 81.37 176.74 81.68 174.87 82.39 173.24 86.60 178.89 87.38 185.70 86.85 194.82 89.37 203.04 91.00 209.74 93.20 215.25 98.00 216.43 97.61 217.70 100.26 214.65 102.47 207.31 101.52 204.04 99.33 195.09 93.55 184.39 90.47 178.46 91.66 173.20 91.16 164.58 90.92 156.54 86.20 149.41 82.13 141.72 78.85 131.57 77.58 121.57 76.86 115.79 76.10 113.12 76.48 110.79 76.59 112.61 79.77 116.16 83.76 120.19 87.58 121.64 91.91 123.41 94.39 122.57 96.60 121.61 96.19 123.08 96.29 123.67 96.54 126.06 97.97 129.62 98.89 129.27 99.18 128.52 104.70 130.02 109.31 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +772.00 30.00 772.11 31.49 773.27 31.50 773.90 30.58 771.00 33.45 767.01 32.74 762.75 33.72 760.48 34.08 759.58 32.01 758.31 31.20 755.74 31.99 754.34 32.72 753.65 34.82 753.29 36.60 753.61 37.14 755.29 39.64 755.52 41.69 755.56 43.68 757.35 45.50 757.41 47.75 756.82 50.80 758.88 53.31 757.32 54.60 754.50 56.19 755.22 59.51 754.49 62.84 752.53 65.69 753.83 65.35 752.04 61.49 747.04 58.95 744.65 55.67 739.83 52.43 733.24 47.94 727.85 45.03 723.40 43.39 721.60 42.66 723.35 43.26 721.20 43.50 719.23 48.47 725.83 48.70 734.22 44.85 745.15 46.01 755.57 47.08 764.21 47.87 771.22 48.67 774.29 44.89 777.25 44.35 774.67 40.87 768.57 36.32 767.73 32.86 760.24 23.32 751.62 19.42 747.23 18.94 743.66 17.55 736.19 17.90 730.32 13.54 724.84 10.84 718.65 9.18 709.18 9.03 700.10 9.72 695.51 8.79 693.76 8.54 692.95 11.12 695.31 14.37 699.19 16.34 703.30 15.67 704.62 15.06 705.56 9.65 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +64.00 140.00 64.48 141.67 65.44 142.05 66.73 142.49 65.16 146.19 62.24 148.39 58.71 150.58 56.64 151.16 55.39 149.43 54.17 148.38 51.70 149.08 50.31 149.59 49.78 151.16 49.75 152.77 49.82 152.81 51.90 154.75 52.87 157.35 53.19 159.02 55.31 160.75 56.16 162.61 55.67 163.77 57.64 163.95 56.90 163.86 54.47 163.92 55.55 165.14 55.79 165.98 54.23 165.61 55.84 164.19 55.34 161.98 51.28 161.12 49.75 158.92 46.06 156.12 40.15 152.27 35.01 147.72 30.93 144.70 29.35 142.39 32.42 142.50 31.79 142.99 30.39 146.94 37.52 147.60 46.35 147.55 57.32 149.84 67.18 151.17 75.29 153.15 82.51 158.57 84.86 159.11 87.21 162.02 85.61 165.84 78.31 166.48 75.74 164.63 67.65 160.68 56.77 158.41 50.86 160.29 45.96 160.39 37.59 160.78 29.04 156.74 21.60 152.91 13.68 149.86 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 25.00 231.00 15.92 230.53 5.37 230.83 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 38.00 104.00 27.57 102.66 16.48 101.72 10.27 100.90 7.91 101.69 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 777.00 421.00 770.60 421.28 762.12 421.59 757.39 422.63 758.15 423.23 758.30 426.64 761.08 433.03 768.96 439.21 778.99 443.01 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 170.00 430.00 163.34 430.21 154.69 431.76 149.25 432.53 148.78 433.67 147.40 434.51 147.46 437.30 152.21 441.19 159.56 443.97 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 750.00 22.00 740.51 22.00 731.23 23.11 726.72 22.32 725.39 22.15 724.92 24.90 727.48 28.38 731.81 30.53 736.55 29.78 738.45 29.08 740.43 23.64 740.02 16.66 739.56 9.59 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 40.00 410.00 31.95 410.03 21.51 411.82 14.93 412.35 13.88 413.67 11.96 413.93 12.02 415.88 17.18 419.24 24.52 422.51 31.15 426.70 37.04 430.64 45.00 433.54 49.57 435.38 55.85 435.42 62.79 436.19 69.05 437.68 78.25 438.92 84.10 438.78 86.88 443.46 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 743.00 246.00 735.82 246.33 727.12 247.32 722.92 248.02 722.87 248.45 722.32 251.81 725.24 256.86 731.70 261.26 739.13 263.20 745.38 264.30 751.06 263.12 756.53 258.15 759.59 253.63 765.12 249.62 770.53 246.15 776.56 243.53 784.97 240.98 789.16 237.10 792.05 239.14 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 327.00 147.00 318.69 146.58 309.96 146.87 304.85 146.64 302.89 147.37 301.22 148.39 302.48 151.93 305.83 155.73 310.10 158.62 312.16 161.78 314.30 162.78 315.25 162.75 315.15 160.96 316.86 159.62 318.47 158.69 320.93 158.39 324.69 158.26 324.82 156.98 324.38 161.53 326.05 166.11 325.26 169.44 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 521.00 374.00 515.21 374.41 507.87 375.31 503.79 376.30 503.81 377.12 503.37 379.61 504.95 384.23 510.42 389.16 517.41 392.50 524.01 395.43 530.01 397.22 537.26 395.52 541.43 393.91 547.27 392.26 553.58 390.83 559.75 390.42 568.18 390.15 573.58 388.46 577.30 391.04 581.77 397.24 585.07 400.49 589.40 404.24 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 182.00 193.00 173.52 192.45 164.23 192.72 159.09 192.50 157.43 193.40 155.77 193.91 157.18 196.88 161.18 200.72 166.39 204.34 169.43 208.31 172.86 210.87 175.23 212.80 175.98 212.75 178.92 212.30 181.68 212.40 185.10 213.26 190.45 214.01 191.90 213.87 192.41 218.78 194.59 223.71 194.50 227.67 196.64 231.60 196.64 232.49 195.34 234.88 197.51 236.31 199.35 238.33 199.27 240.12 197.20 242.34 192.21 243.59 185.76 242.26 180.39 239.13 175.68 238.45 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 413.00 32.00 403.77 31.13 394.56 30.94 389.31 30.07 386.66 30.15 384.90 31.32 385.86 34.74 388.30 38.03 390.75 39.97 390.89 42.18 390.70 41.22 388.55 39.35 386.26 36.04 385.91 33.16 385.16 31.04 385.32 29.60 386.83 28.19 384.18 26.17 382.39 30.47 382.10 34.56 379.44 37.28 379.68 39.71 376.50 38.45 375.22 40.72 377.09 41.66 377.73 42.81 376.44 43.82 372.66 43.71 366.46 43.20 359.35 38.96 354.31 34.96 350.55 33.22 344.19 27.82 337.55 23.67 330.63 16.53 321.83 8.90 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 580.00 51.00 571.27 50.61 562.52 51.03 557.75 50.33 555.78 50.37 554.66 52.44 556.24 55.95 559.36 58.86 562.79 59.68 563.92 60.56 564.73 57.76 563.72 53.55 562.31 48.54 562.87 44.04 562.88 40.37 563.97 37.18 566.31 34.22 564.54 30.47 563.10 33.81 563.37 37.24 561.29 38.89 562.09 40.31 559.70 37.69 559.07 40.34 561.80 41.65 563.43 42.88 562.95 43.40 560.10 42.86 554.63 42.11 548.73 37.85 544.88 34.64 542.21 34.30 536.87 29.44 531.35 25.55 525.49 19.03 517.81 12.00 512.73 8.17 510.06 5.64 508.63 6.47 508.39 7.48 509.41 5.68 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 360.00 281.00 353.35 281.06 345.24 281.91 341.07 282.48 340.25 283.36 339.07 284.88 340.37 288.61 345.01 292.96 350.65 296.02 355.32 299.36 359.49 301.05 364.24 301.01 366.59 300.08 370.93 298.90 375.21 298.15 379.42 298.19 386.10 298.43 389.47 297.16 391.11 300.84 394.37 306.34 395.66 309.76 398.42 313.39 400.48 313.44 400.03 315.96 402.26 318.57 404.45 321.39 405.77 323.37 405.32 326.02 401.60 327.14 397.08 326.68 392.76 324.27 388.74 325.76 384.52 323.65 379.38 322.38 374.45 319.15 367.24 313.77 361.81 311.54 358.79 309.65 356.69 310.86 357.28 314.62 359.35 314.93 363.71 315.30 367.57 313.68 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 656.00 149.00 648.15 149.00 639.60 149.78 635.33 149.59 634.25 150.10 633.55 152.79 635.80 157.15 640.37 160.74 645.91 161.94 649.35 162.98 652.56 160.80 654.57 156.37 655.55 151.65 658.50 147.46 661.05 143.81 664.44 140.64 669.47 138.05 670.67 134.18 671.18 136.69 673.45 140.95 673.46 142.34 676.17 144.26 676.13 141.44 675.93 144.38 679.61 146.76 682.38 149.19 683.65 150.10 682.44 150.44 678.20 150.35 673.26 147.53 669.63 145.32 667.11 147.82 662.85 144.71 657.90 142.18 653.06 137.95 645.72 131.76 640.93 129.38 638.43 127.21 637.42 129.49 638.22 131.68 640.24 129.74 645.63 129.18 637.19 121.84 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 538.00 253.00 531.21 253.25 523.16 254.20 518.75 254.70 518.13 255.47 517.22 257.78 518.99 262.21 523.68 266.47 529.67 268.99 534.20 271.40 538.45 271.65 542.59 269.19 544.70 266.70 548.63 264.17 552.61 262.08 557.11 260.71 563.46 259.71 566.26 257.28 567.70 260.28 570.87 265.37 571.97 268.04 575.30 271.11 576.69 269.66 576.39 272.33 579.24 275.70 582.04 278.97 583.55 280.66 583.02 282.58 579.49 283.08 575.09 281.87 571.32 279.77 567.72 282.61 563.53 280.81 558.54 278.90 553.90 275.88 546.76 270.48 541.72 268.52 538.68 266.77 537.41 268.71 538.25 272.28 540.50 271.74 545.32 271.94 549.05 270.52 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 679.00 349.00 672.77 349.44 664.86 350.25 660.66 351.25 660.97 351.93 660.70 354.97 663.00 360.41 669.32 365.59 677.30 368.62 684.45 370.78 691.12 371.51 698.59 368.00 703.04 365.02 709.38 362.32 716.13 359.96 722.90 358.86 732.39 357.81 738.20 355.25 742.30 357.29 747.82 363.48 726.41 366.50 731.29 370.34 735.41 368.42 736.30 370.61 740.08 375.40 744.17 379.89 747.32 382.27 748.46 384.45 745.90 385.40 741.80 384.47 737.81 382.58 734.34 386.50 730.18 386.07 725.27 384.53 720.63 382.47 713.17 376.80 707.43 375.36 704.40 373.75 703.06 376.75 704.64 381.46 708.23 381.42 714.01 381.93 719.53 382.39 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 311.00 396.00 304.88 396.19 297.07 397.43 292.40 398.17 292.07 399.22 290.98 400.69 291.70 404.22 296.35 408.71 302.82 412.14 308.89 415.71 314.32 418.58 321.34 419.04 325.43 419.01 330.86 418.18 336.79 417.82 342.45 418.32 350.35 418.86 355.55 417.92 358.57 421.47 362.07 427.37 364.61 431.43 368.20 435.55 371.69 436.19 371.72 438.48 373.40 441.46 376.28 443.70 380.83 444.01 390.21 441.17 387.57 442.83 383.64 442.99 379.49 440.90 375.14 442.48 370.54 441.14 365.67 440.16 351.71 444.04 342.13 441.38 336.62 439.15 333.23 437.36 330.90 438.33 331.14 442.70 334.05 443.34 338.44 443.43 342.63 443.16 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 190.00 73.00 180.36 72.03 170.65 71.38 165.05 70.76 162.48 71.22 160.37 71.53 161.66 74.62 164.77 78.49 168.44 82.04 169.61 86.12 170.66 87.83 169.89 89.49 168.64 88.58 169.58 88.02 170.19 88.04 171.91 88.86 175.04 89.63 173.86 89.55 172.95 94.70 173.91 99.36 172.29 103.44 173.51 107.19 171.26 107.73 169.52 109.83 171.47 110.61 172.33 111.85 171.36 113.75 167.72 114.64 160.96 114.86 153.23 112.02 147.07 107.97 142.28 105.40 135.24 100.41 127.71 97.02 119.90 90.40 109.86 83.29 105.36 67.48 100.62 86.46 98.10 86.94 98.01 89.37 95.93 77.21 102.26 65.69 101.37 73.15 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 449.00 159.00 441.11 158.74 432.48 159.21 427.88 159.00 426.26 159.76 424.80 161.44 426.19 165.23 429.77 169.12 434.30 171.42 436.84 174.03 439.22 173.99 440.60 172.33 440.60 169.57 442.73 167.18 444.60 165.31 447.37 164.17 451.30 163.09 451.95 161.04 451.79 164.73 453.41 169.40 452.82 172.21 454.88 175.06 454.20 174.14 452.98 176.71 455.59 178.74 457.30 181.07 457.56 182.51 455.76 183.75 451.18 184.23 445.51 182.03 440.95 179.20 437.44 179.95 432.47 176.59 426.73 174.21 421.28 169.50 413.55 163.37 408.11 160.48 404.64 157.93 402.99 159.33 403.22 162.14 404.54 161.14 408.54 160.91 411.20 157.91 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 130.00 314.00 122.39 313.90 112.96 314.82 107.57 315.06 106.44 316.21 104.73 316.61 105.60 319.16 110.35 322.97 116.66 326.43 121.69 330.59 126.56 333.89 131.91 336.29 134.60 337.12 139.40 337.10 144.51 337.60 149.42 338.73 156.64 339.85 160.58 339.68 162.10 344.23 164.85 349.32 166.41 353.73 169.21 357.91 171.34 359.33 170.33 361.75 171.80 363.19 173.51 365.16 174.61 367.24 173.54 370.35 169.40 371.99 163.78 372.30 159.12 369.66 153.44 369.57 148.21 366.78 141.79 365.57 136.43 362.31 128.36 357.11 122.10 354.65 117.57 352.26 114.76 352.27 114.89 356.00 116.87 356.80 121.33 356.91 125.56 355.08 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 614.00 30.00 611.92 29.16 613.21 29.49 610.60 29.97 608.51 34.87 614.19 35.08 620.95 31.89 630.37 33.49 639.18 34.73 646.27 35.95 651.69 37.63 653.59 34.40 655.49 34.64 652.13 32.22 645.67 28.19 643.99 24.78 636.01 15.66 627.46 11.77 622.64 11.41 618.55 10.12 611.12 10.20 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 32.00 296.00 30.12 293.50 33.05 292.92 33.80 292.95 31.83 296.75 37.67 297.45 47.56 296.20 58.33 297.30 68.67 298.22 76.87 299.26 84.84 304.04 88.93 305.21 92.01 307.28 92.98 311.56 87.80 313.46 85.62 312.15 80.59 309.27 70.99 307.24 65.49 309.99 61.69 311.02 54.13 312.66 46.05 310.55 39.54 307.31 32.05 305.18 23.42 305.05 12.68 305.93 6.28 306.07 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 780.00 430.00 778.26 429.61 782.01 431.58 784.83 433.13 784.80 437.35 791.24 443.38 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 215.00 27.00 212.77 25.44 213.73 25.89 210.93 26.68 209.31 31.12 214.62 31.78 220.60 30.97 229.29 33.77 237.00 35.31 243.15 37.64 247.74 42.14 248.18 41.23 248.84 43.71 244.34 45.34 236.30 43.46 232.64 40.54 222.30 33.82 211.57 30.44 205.16 30.91 199.27 29.88 190.45 29.04 181.86 23.37 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 523.00 193.00 521.10 192.06 523.32 192.82 522.73 193.30 521.15 197.89 526.61 199.97 534.14 198.23 543.52 200.24 552.52 202.86 559.97 205.08 567.20 208.62 570.38 207.58 573.32 208.62 573.41 208.75 568.63 206.46 567.25 204.74 561.91 198.44 553.67 194.47 549.97 196.02 547.04 195.90 540.80 197.40 534.72 194.47 529.75 192.12 523.81 190.72 516.46 190.81 508.12 191.61 503.83 191.77 502.55 192.30 501.33 194.36 503.14 198.59 507.40 202.59 512.53 204.81 516.12 206.97 519.34 206.53 521.83 204.14 522.53 200.85 525.53 198.12 528.31 195.78 532.34 194.65 537.36 193.49 539.38 191.04 539.08 193.99 541.45 198.58 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 68.00 419.00 66.12 416.48 68.91 415.52 70.68 415.19 68.95 419.12 73.44 420.29 83.72 418.24 94.46 418.55 104.74 419.36 112.73 419.87 121.24 424.24 126.64 426.03 130.58 427.46 133.36 431.80 130.69 434.22 129.02 433.28 126.41 430.73 119.01 428.41 114.09 431.59 111.25 433.26 105.15 435.75 98.01 434.91 92.47 431.91 86.00 430.58 78.66 430.83 69.08 432.73 62.87 433.43 62.12 434.79 60.30 435.21 60.39 437.57 65.32 441.14 72.10 443.95 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 542.00 351.00 540.60 349.84 543.34 350.92 544.92 351.73 544.27 356.01 549.36 359.66 558.34 358.52 568.49 360.92 578.49 364.74 586.71 367.74 595.69 372.22 601.09 372.96 605.52 373.68 608.81 375.26 606.47 372.84 605.65 371.71 602.86 366.69 596.04 361.44 593.01 363.56 591.46 364.28 586.87 366.64 581.22 365.39 576.79 363.23 571.81 362.50 565.99 362.91 558.47 363.79 554.38 364.74 554.49 365.55 554.04 368.20 555.74 373.03 561.42 378.05 568.70 381.38 575.37 383.97 581.48 385.32 588.61 383.19 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 230.00 350.00 228.23 348.00 230.56 347.99 231.61 348.23 230.14 352.34 234.52 354.45 243.11 353.15 252.37 354.70 261.46 356.84 268.46 358.72 275.84 363.82 280.19 365.29 283.68 366.63 285.56 370.41 282.37 370.94 280.55 370.33 277.15 367.11 269.46 364.47 265.15 367.47 262.53 368.54 256.79 371.00 250.39 369.49 245.24 366.74 239.34 365.56 232.49 365.75 223.96 367.13 218.74 367.87 217.92 369.32 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 400.00 198.00 399.34 196.87 399.70 197.10 400.41 197.85 397.39 202.07 402.88 203.90 409.54 202.25 419.52 204.62 427.80 206.90 434.75 208.98 441.41 213.06 445.47 212.88 447.03 214.13 449.38 215.91 441.69 213.87 439.90 212.23 434.93 206.71 426.00 203.03 421.97 204.81 418.42 204.87 415.80 206.91 408.59 203.93 401.56 200.90 394.52 199.09 386.96 198.94 380.32 199.84 375.55 199.93 375.43 200.86 371.04 201.90 373.42 205.87 377.30 209.90 383.42 212.92 387.44 215.92 386.89 216.15 392.36 215.87 393.63 214.08 395.57 212.16 399.22 210.95 398.63 209.96 408.55 210.18 404.91 207.89 408.78 212.24 411.64 217.20 400.82 219.20 409.34 223.16 403.16 222.06 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 227.00 207.00 225.27 205.18 227.32 205.46 227.12 205.77 225.40 209.95 230.89 211.35 238.62 210.25 247.84 212.31 256.81 214.21 263.90 216.07 270.61 220.73 273.55 220.92 276.00 222.92 275.88 225.66 270.33 225.55 268.37 223.88 262.48 219.31 253.64 216.51 248.96 218.53 245.30 218.89 238.35 220.28 231.64 217.30 225.51 214.14 210.95 207.72 202.66 207.31 193.82 207.83 188.77 207.84 187.43 208.80 185.34 209.34 186.91 212.62 190.93 216.40 195.98 219.62 199.54 223.61 202.86 226.05 205.70 227.66 206.42 227.20 209.76 226.83 212.84 226.82 216.73 227.66 222.18 228.55 223.98 228.00 224.44 232.64 226.51 237.56 226.48 241.61 229.51 245.56 229.37 246.57 218.73 240.76 230.55 250.48 233.07 252.65 233.37 254.65 220.49 248.51 226.62 257.90 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 776.00 36.00 774.38 35.47 776.64 35.98 774.53 36.24 772.72 41.24 780.17 41.31 789.60 37.07 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 90.00 186.00 88.43 183.74 91.25 183.67 91.06 184.13 89.61 188.22 96.02 188.97 104.61 188.46 114.92 190.50 124.70 191.87 132.41 193.72 139.63 198.69 142.58 199.32 145.14 201.89 144.52 205.46 138.35 206.04 136.10 204.54 129.29 200.56 119.43 198.21 113.87 200.30 109.88 200.65 102.42 201.62 94.55 198.40 87.97 194.98 80.92 192.33 71.78 191.88 61.85 191.95 56.40 191.74 61.70 195.63 60.35 195.70 62.20 197.95 60.13 199.14 66.11 203.12 70.04 207.75 74.11 211.07 77.52 214.21 78.66 215.13 82.54 215.61 86.42 216.53 90.64 218.18 97.21 219.74 99.58 220.19 100.91 225.37 103.52 230.19 103.72 234.62 106.38 238.80 106.84 240.50 106.01 242.82 108.13 243.54 109.56 245.10 110.24 247.17 107.89 249.67 102.78 251.02 96.15 250.14 97.18 249.11 85.33 245.43 79.05 241.73 78.75 242.56 72.61 237.99 62.73 232.30 55.80 229.23 51.56 226.14 49.45 226.59 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 377.00 330.00 375.50 328.44 378.04 328.92 379.19 329.43 378.19 333.64 382.97 336.33 391.38 335.05 400.89 336.97 410.06 339.83 417.54 342.06 425.45 346.62 430.01 347.44 433.76 348.65 436.15 351.06 433.24 350.16 432.14 349.03 429.06 344.64 422.10 340.62 418.72 343.09 416.66 343.89 411.72 346.18 405.91 344.60 401.35 342.14 396.13 341.08 390.02 341.30 382.39 342.37 378.03 343.03 377.67 343.94 376.78 345.78 378.14 349.72 382.96 354.34 389.35 357.59 394.83 360.80 400.12 362.90 406.13 362.54 409.57 361.66 414.64 360.41 420.04 359.57 425.30 359.64 432.73 359.73 437.30 358.49 439.92 361.83 443.57 367.61 445.78 371.17 449.37 375.02 452.29 374.94 452.28 377.39 454.51 380.49 457.05 383.73 459.01 385.88 459.07 388.71 456.27 390.06 452.24 389.97 448.30 387.67 444.14 389.85 440.05 388.39 435.24 387.25 430.91 384.65 424.12 379.51 418.91 377.43 415.65 375.66 413.79 377.19 414.45 381.36 417.03 381.90 421.45 382.31 425.78 381.43 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 487.00 39.00 484.60 37.72 485.54 38.32 482.94 38.74 480.82 43.53 485.71 44.06 491.85 41.78 500.23 43.80 507.98 45.31 514.27 46.84 519.12 49.64 520.34 47.33 521.46 48.29 517.76 47.33 511.15 44.18 508.70 41.06 500.27 32.80 491.23 28.92 485.95 28.99 481.40 27.84 473.80 27.84 466.98 22.79 460.56 19.10 453.90 16.45 444.72 15.72 435.66 15.50 430.36 14.45 427.80 14.53 426.12 15.85 427.07 19.19 429.44 22.39 431.81 23.89 431.77 25.69 431.22 23.91 428.63 21.46 426.34 17.62 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 761.00 250.00 759.55 249.62 762.94 251.08 763.93 251.87 763.02 256.42 770.07 259.96 780.56 258.01 792.94 260.47 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 91.00 66.00 89.02 63.98 91.03 64.39 89.22 65.12 87.96 69.27 94.26 70.10 101.69 70.04 111.73 72.85 120.59 74.48 127.78 76.92 133.62 82.24 134.78 82.13 136.20 85.06 132.69 88.02 124.74 87.44 121.09 85.07 110.88 79.55 99.50 76.54 92.79 77.53 87.01 76.90 77.60 76.31 68.40 70.94 60.22 66.50 51.68 62.69 40.23 60.80 28.75 59.26 22.28 58.27 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 345.00 30.00 342.61 28.66 343.32 29.05 340.51 29.67 338.47 34.30 343.38 34.81 348.91 33.33 357.19 35.73 364.56 37.26 370.42 39.19 374.76 42.87 375.46 41.15 375.94 42.91 371.85 43.34 364.42 40.87 361.12 37.87 351.65 30.33 341.75 26.66 335.89 26.98 330.51 25.80 322.36 25.29 314.59 19.84 307.56 15.78 300.19 12.57 290.40 11.35 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 663.00 359.00 661.29 358.34 664.18 359.94 665.82 360.90 665.19 365.14 670.26 361.71 681.13 381.73 675.87 368.42 686.96 373.20 697.06 384.59 707.05 388.50 713.24 388.63 718.15 388.52 722.20 389.63 718.70 380.66 717.90 379.42 714.94 373.90 724.29 378.65 721.21 380.87 702.77 370.11 698.16 372.60 692.06 371.48 704.03 381.14 682.00 369.11 675.78 369.62 667.84 370.45 665.95 388.30 663.82 372.42 665.92 392.27 667.97 398.47 674.06 401.31 682.02 404.51 688.20 397.57 694.20 393.53 702.05 390.45 705.99 383.63 713.20 385.11 720.21 383.14 727.06 382.19 735.96 377.19 742.89 379.21 747.19 381.25 753.92 392.41 757.97 395.38 762.19 393.48 767.91 397.30 768.87 399.37 772.88 404.38 777.39 409.50 781.24 412.77 782.47 414.45 780.24 415.80 775.89 414.26 771.58 412.42 767.96 417.29 763.38 416.52 758.35 415.70 753.54 413.57 745.36 407.85 754.36 405.68 750.85 404.39 734.80 408.43 751.34 411.84 755.17 412.09 746.27 413.76 752.37 413.63 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 689.00 125.00 687.42 124.42 689.50 125.28 688.53 125.68 687.01 130.51 693.27 132.04 701.76 129.16 712.51 131.06 722.51 133.26 731.27 134.99 738.75 137.01 742.46 134.61 745.71 134.67 745.20 132.74 739.41 128.70 738.89 126.42 732.75 118.44 724.37 114.40 720.56 114.84 717.60 114.20 710.90 115.38 705.04 112.27 699.89 109.91 694.24 108.80 685.92 108.81 677.20 109.72 672.79 109.56 671.76 109.64 671.13 112.46 673.68 116.48 678.25 119.71 683.55 120.22 686.41 120.65 689.36 117.37 690.58 111.95 690.87 106.43 693.45 101.32 695.57 97.12 698.41 93.22 703.11 89.70 703.52 85.27 703.43 87.60 705.23 91.05 704.67 92.11 707.16 93.16 706.65 89.98 706.42 92.88 710.58 94.93 713.55 96.89 714.43 97.34 712.61 97.14 708.50 96.75 703.23 93.29 700.19 91.26 697.96 92.84 693.72 89.31 688.80 86.25 683.96 81.34 676.53 75.27 671.83 72.53 669.73 70.26 668.94 72.59 669.69 73.85 671.80 71.27 677.20 69.89 680.90 65.97 +565.39 158.71 565.66 160.57 566.63 161.19 567.62 160.60 566.31 163.62 563.69 163.93 560.57 164.91 558.56 165.60 557.57 163.83 556.62 162.91 554.81 163.39 553.64 164.05 553.21 165.86 553.14 167.87 553.26 168.25 554.78 170.64 555.59 173.11 555.69 175.01 557.42 176.97 558.09 179.08 557.39 181.54 558.85 183.63 558.30 184.55 555.77 185.48 556.39 188.06 556.19 191.06 554.49 193.08 555.35 193.12 554.93 190.08 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +599.62 147.64 599.96 149.41 600.75 150.09 601.86 149.43 600.34 152.44 597.67 152.56 594.58 153.53 592.41 154.14 591.52 152.33 590.63 151.41 588.57 152.02 587.38 152.68 586.79 154.59 586.80 156.49 586.91 156.89 588.49 159.35 589.14 161.73 589.41 163.56 590.94 165.58 591.53 167.72 590.99 170.34 592.62 172.44 591.87 173.52 589.44 174.44 589.85 177.29 589.69 180.26 588.05 182.36 588.83 182.46 588.44 179.35 584.53 177.28 582.84 174.46 579.50 171.58 574.07 167.31 569.27 163.92 565.59 161.99 563.67 161.12 565.74 161.74 564.86 162.29 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 \ No newline at end of file diff --git a/modules/sfm/samples/data/desktop.blend b/modules/sfm/samples/data/desktop.blend new file mode 100644 index 00000000000..080db23d0be Binary files /dev/null and b/modules/sfm/samples/data/desktop.blend differ diff --git a/modules/sfm/samples/data/desktop_tracks.txt b/modules/sfm/samples/data/desktop_tracks.txt new file mode 100644 index 00000000000..bfe63d6bcf5 --- /dev/null +++ b/modules/sfm/samples/data/desktop_tracks.txt @@ -0,0 +1,26 @@ +792.80 84.80 791.74 83.65 791.76 81.20 791.58 78.70 791.66 76.45 792.60 75.41 792.67 76.34 792.28 76.21 792.85 75.18 793.84 74.14 794.37 73.66 793.82 73.72 792.67 74.06 791.89 75.28 791.90 75.26 791.72 75.16 791.39 76.00 791.40 76.00 792.00 75.65 792.01 76.81 791.35 78.53 790.81 79.46 789.68 80.70 788.35 82.22 786.65 82.82 784.74 83.20 782.65 83.97 779.66 84.00 777.29 84.90 775.37 85.20 772.74 85.36 770.56 85.46 767.64 84.59 765.73 83.22 762.85 81.18 760.83 79.14 758.37 77.64 755.18 75.99 753.39 74.80 751.13 74.48 748.64 74.13 746.19 73.99 743.58 73.92 741.24 73.86 739.17 73.93 736.62 74.01 734.15 73.91 731.68 73.75 729.41 73.24 727.43 72.70 725.75 71.78 725.75 70.78 722.63 71.42 717.63 70.42 713.18 69.59 711.23 68.55 707.41 68.61 703.39 68.61 699.65 67.56 695.58 67.55 692.12 67.09 688.08 66.71 684.08 66.71 680.07 66.37 676.52 65.79 672.69 66.29 668.21 66.33 664.22 66.31 660.33 65.37 656.15 65.75 652.66 65.72 649.84 64.69 649.50 64.25 649.23 65.25 651.19 66.30 655.40 66.39 659.40 66.62 663.35 66.71 665.88 66.38 662.52 66.66 656.36 66.59 651.36 66.59 647.37 67.26 641.81 68.78 635.48 70.05 630.48 71.05 627.14 72.72 621.56 74.61 615.09 76.00 608.37 77.91 601.38 79.30 596.06 80.78 593.44 82.20 593.23 82.73 593.56 82.59 591.74 83.78 587.84 84.70 585.45 84.33 583.69 83.74 583.28 83.84 581.28 83.84 577.86 83.13 574.88 81.26 565.90 79.35 570.05 80.90 566.16 80.22 560.78 79.80 554.95 80.38 550.84 80.27 548.76 79.72 546.55 79.41 537.78 77.48 537.78 77.48 538.21 76.97 539.68 75.94 542.22 75.57 545.64 75.43 550.09 75.30 555.55 74.42 560.31 74.16 559.05 73.45 557.15 73.79 561.42 72.67 563.91 72.42 562.15 72.83 560.42 73.00 561.52 73.37 561.82 75.35 561.31 76.78 566.84 77.91 571.35 80.37 572.93 81.93 575.42 83.92 575.79 85.60 572.35 88.08 568.65 89.70 565.18 91.01 562.55 93.10 559.12 94.94 557.12 96.94 555.67 98.80 554.78 100.84 552.37 102.40 549.28 104.93 547.78 107.17 548.31 108.93 549.59 110.98 550.10 112.54 548.74 113.67 545.56 114.76 543.23 116.11 542.09 117.37 542.53 118.39 541.50 119.20 538.55 120.53 534.67 121.86 529.83 122.33 523.78 123.09 518.80 123.59 515.39 124.18 510.42 124.17 502.82 124.23 495.56 124.92 489.34 125.30 482.92 124.88 476.48 124.72 470.76 124.67 465.68 124.28 461.64 124.58 458.15 124.98 455.36 124.87 453.88 124.88 453.01 125.05 451.92 125.56 450.35 126.31 448.85 126.78 447.50 127.25 446.28 127.31 443.81 127.40 440.16 127.83 437.50 127.98 436.19 128.14 435.08 128.15 433.21 127.61 430.30 127.25 428.07 127.16 427.00 126.69 427.24 126.27 428.18 125.58 429.58 124.75 430.96 123.26 432.16 122.10 432.67 120.06 431.92 118.87 430.89 118.39 429.35 116.91 428.42 116.10 426.77 115.93 424.82 115.35 423.21 115.80 421.57 115.96 420.52 116.40 418.65 119.78 416.55 120.79 415.46 120.34 413.95 118.34 411.77 114.80 409.30 111.22 404.60 108.64 400.66 107.10 397.81 105.45 395.68 104.85 392.22 104.72 388.23 105.45 383.13 105.95 377.59 107.44 372.21 110.68 368.79 112.73 363.79 114.73 357.51 119.05 354.60 123.05 350.74 127.54 346.82 133.08 342.66 137.98 339.53 139.36 335.96 136.54 333.63 131.52 331.71 128.11 330.13 126.63 328.48 124.17 326.66 122.17 326.12 120.58 325.03 117.42 323.50 113.41 322.72 109.69 321.66 105.77 320.38 103.88 318.94 102.60 316.88 101.12 315.14 100.40 311.48 101.14 309.32 101.41 307.04 101.59 305.53 103.99 302.75 107.58 300.32 109.29 297.65 108.78 292.82 106.84 288.64 106.67 285.78 107.12 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 566.40 427.20 567.28 425.92 567.43 426.42 567.21 426.06 567.63 424.60 569.03 423.50 569.59 422.67 569.33 422.74 568.53 423.10 567.99 424.18 568.10 424.05 567.89 423.82 567.68 424.59 567.73 424.56 568.14 424.08 568.17 425.41 567.75 427.35 567.40 428.71 566.63 430.26 565.30 431.79 564.14 432.83 562.54 433.51 560.38 434.60 557.78 434.95 555.50 435.99 553.81 436.62 551.41 437.09 549.35 437.24 546.67 436.80 545.07 435.70 542.98 434.38 541.54 432.70 539.65 431.67 537.13 430.51 536.16 430.08 534.63 430.14 532.81 430.14 531.30 430.29 529.59 430.65 528.24 430.90 526.91 431.44 525.09 431.74 523.03 432.04 521.17 432.08 519.80 431.78 518.79 431.77 518.62 431.27 520.38 430.75 518.42 431.82 515.16 431.66 512.61 431.67 512.71 431.08 510.96 431.96 508.60 432.54 506.74 432.47 504.50 432.90 502.74 433.14 500.20 433.31 497.61 434.30 495.35 434.53 493.22 434.42 491.25 435.11 488.41 436.05 485.84 436.51 483.49 436.27 481.30 437.14 480.07 437.57 479.54 437.27 481.16 437.62 482.76 439.18 486.62 440.38 492.29 440.84 498.72 441.60 505.05 442.93 509.90 443.06 508.62 443.89 504.59 444.62 502.09 445.42 500.81 446.78 498.15 448.77 495.29 450.74 493.78 452.46 492.81 454.56 490.27 456.87 486.61 458.96 482.35 461.53 477.70 463.52 474.38 465.08 473.57 466.55 474.83 467.38 476.25 467.54 475.68 468.60 472.71 469.60 470.65 469.19 469.68 468.82 469.94 469.22 469.11 469.21 466.46 468.74 464.45 467.51 463.71 467.94 463.06 468.33 461.58 468.62 458.90 469.57 456.22 471.09 455.03 472.42 455.93 472.68 456.55 473.18 458.02 473.16 458.77 473.37 460.41 473.02 462.49 472.37 465.40 471.72 468.83 470.84 473.17 470.43 478.52 468.96 483.37 468.17 482.73 467.11 482.06 466.93 486.79 465.09 489.71 464.06 487.52 463.71 485.70 462.65 487.73 461.71 488.95 462.59 489.83 462.87 496.70 462.81 502.83 463.89 505.38 464.16 508.84 465.11 509.78 465.57 506.92 466.69 503.67 467.26 501.33 467.68 500.08 468.67 498.29 469.71 498.04 470.81 498.39 471.98 498.76 473.03 498.05 473.98 496.08 475.88 496.03 477.31 497.48 478.73 499.89 480.02 501.41 481.61 501.60 482.76 500.49 483.96 500.11 485.83 501.55 487.41 504.56 489.00 506.49 490.45 506.48 492.19 504.96 494.08 502.53 495.05 498.94 496.49 496.67 497.56 495.21 499.20 492.35 499.57 487.11 500.65 482.14 502.09 477.90 503.20 473.30 503.70 468.94 503.92 465.21 504.56 462.36 504.96 460.22 505.80 458.45 506.58 457.30 507.25 457.47 508.12 458.08 508.52 458.11 509.62 457.52 510.68 457.47 511.73 457.37 512.66 457.39 513.00 456.19 513.42 454.30 514.41 453.25 514.76 453.37 514.99 453.44 515.03 452.48 514.96 450.98 514.90 449.82 514.77 449.90 514.35 451.46 513.84 453.29 512.93 455.31 512.52 457.25 510.65 458.88 509.77 460.14 507.88 460.70 506.48 460.87 506.04 460.79 504.46 461.41 503.76 461.75 503.38 462.34 502.46 462.84 502.62 463.17 502.82 463.69 503.23 464.44 506.03 464.42 506.55 464.37 506.10 464.00 503.94 463.21 500.31 462.01 496.69 458.34 494.33 455.68 492.82 454.27 490.89 452.96 490.22 450.59 490.21 448.06 490.57 444.83 490.70 440.06 492.58 436.01 495.52 434.69 497.22 431.95 499.11 427.03 503.16 425.96 506.92 423.39 511.27 420.99 516.69 418.57 521.48 417.37 522.43 415.67 519.03 414.18 513.77 412.83 510.69 411.84 509.15 410.62 506.63 409.55 504.96 408.81 503.37 408.28 500.02 407.45 496.04 407.23 492.46 407.06 488.65 406.33 486.80 405.41 485.73 404.30 484.28 403.35 483.75 400.55 484.30 399.79 484.42 398.65 484.34 397.53 486.66 395.36 490.35 394.04 491.79 392.40 490.78 388.99 488.84 385.85 488.32 384.60 488.42 +933.72 457.11 932.27 455.82 932.00 453.48 931.88 450.86 931.93 448.61 932.81 447.67 933.10 448.41 932.60 448.33 933.23 447.39 934.37 446.30 935.01 445.73 934.90 445.80 934.25 445.95 933.95 447.13 934.13 447.15 934.11 447.22 934.12 448.23 934.37 448.57 934.91 448.66 935.36 450.19 935.41 452.21 935.55 453.61 935.13 455.25 934.46 457.13 933.88 458.28 932.61 459.13 931.17 460.49 929.13 461.28 927.46 462.58 926.36 463.70 924.68 464.44 923.06 465.08 920.90 465.08 919.91 464.30 918.46 462.76 917.51 461.38 916.17 460.33 914.26 459.31 913.89 458.63 912.84 458.54 911.76 458.80 910.49 459.09 909.67 459.36 908.73 459.87 907.90 460.72 907.11 461.35 905.70 461.85 904.74 462.51 903.77 462.86 903.64 463.08 904.03 462.74 906.27 462.35 905.41 463.37 902.91 462.84 900.96 462.25 901.78 461.48 900.90 461.64 899.26 461.73 898.42 461.33 896.72 461.52 896.00 461.56 894.08 461.60 892.27 462.47 890.94 462.65 889.63 462.73 888.00 463.84 886.15 464.26 884.50 464.88 882.97 464.67 881.15 465.42 880.79 465.71 880.76 465.13 883.14 465.19 885.31 466.28 889.65 467.94 896.24 468.45 903.27 469.05 910.38 469.80 915.98 469.81 915.04 470.37 911.75 470.34 909.75 469.88 909.09 470.36 906.91 470.73 904.23 470.74 903.20 470.78 902.65 472.14 900.58 473.12 897.11 473.91 893.54 475.38 889.49 476.53 886.42 477.48 885.76 479.22 887.59 480.04 889.49 480.82 888.99 482.29 886.33 483.73 884.38 484.04 883.75 484.36 884.43 485.04 883.67 485.25 881.37 485.48 879.68 484.26 879.56 484.17 879.49 484.12 878.66 483.58 876.65 482.57 874.61 482.01 874.32 481.42 875.63 479.64 876.69 478.44 878.50 477.12 879.37 476.32 880.94 475.68 883.14 474.87 885.64 474.65 888.63 474.52 892.77 474.26 897.70 473.70 902.07 472.92 900.86 471.20 899.43 470.21 903.60 467.78 905.66 466.29 902.89 466.07 899.75 464.78 900.84 463.27 900.85 462.78 900.75 461.67 906.49 459.66 911.17 459.44 912.72 459.07 915.02 459.16 915.04 458.99 911.14 459.52 907.04 459.46 903.62 459.07 901.33 459.17 898.77 458.82 897.58 458.39 897.21 458.28 896.86 458.80 895.56 458.56 893.21 459.56 892.71 460.88 894.13 462.14 896.36 463.92 898.17 465.64 898.34 466.10 897.31 466.73 897.45 467.97 899.44 468.45 903.06 469.22 905.48 469.52 905.85 470.25 904.93 471.08 903.00 471.61 900.05 472.36 898.30 472.89 897.24 474.39 895.00 474.88 890.08 475.37 885.49 476.93 881.66 478.06 877.61 478.79 873.63 479.17 870.19 479.43 867.59 479.63 865.61 480.33 864.23 481.46 863.49 481.76 864.14 482.53 865.25 483.56 865.50 485.06 865.40 486.48 865.49 487.54 865.54 488.32 865.64 489.20 864.70 489.54 863.23 489.92 861.66 489.95 861.77 490.16 861.87 490.42 861.16 490.42 859.34 490.41 858.41 490.42 858.40 490.31 859.54 490.14 861.15 489.96 863.24 489.78 864.74 488.70 866.10 488.36 867.12 486.70 867.34 485.44 867.25 484.31 866.46 482.63 866.72 481.10 866.59 479.51 866.58 477.16 866.69 476.04 866.68 474.94 866.90 474.61 867.02 475.65 866.22 474.99 865.72 473.96 865.12 470.89 863.47 466.52 861.74 462.18 857.70 459.46 854.41 457.55 852.33 455.23 850.70 454.45 847.74 453.96 844.83 453.35 840.89 452.73 835.99 453.86 830.92 456.44 828.93 456.86 825.87 457.32 820.40 460.18 818.77 463.21 815.69 466.36 812.79 470.88 810.00 474.69 808.17 474.62 805.64 469.92 803.69 464.29 802.06 461.39 800.76 459.82 799.02 456.98 797.72 455.36 796.79 453.97 796.08 450.40 794.63 445.95 794.28 441.84 793.60 437.49 792.46 435.29 791.51 433.14 790.00 430.87 788.81 429.46 785.68 429.00 784.31 427.84 782.78 426.54 781.23 428.22 778.68 430.98 776.65 430.91 774.37 428.13 770.22 424.75 766.21 422.40 764.00 420.80 +566.36 604.29 565.03 602.47 564.46 599.61 564.21 596.31 564.21 593.77 565.09 592.06 565.22 592.64 564.37 592.21 564.85 590.79 566.12 589.38 566.49 588.63 566.25 588.65 565.44 589.05 564.84 590.26 564.82 590.09 564.38 589.85 564.09 590.69 563.99 590.64 563.98 590.16 563.88 591.56 563.27 593.59 562.70 595.00 561.73 596.54 560.22 598.21 558.87 599.25 557.04 599.94 554.63 601.10 551.72 601.68 549.12 602.83 547.11 603.40 544.43 604.01 542.06 604.35 539.11 603.84 537.35 602.93 535.32 601.59 533.54 600.03 531.32 598.75 528.78 597.85 527.85 597.28 526.24 597.34 524.01 597.35 522.10 597.62 520.29 598.22 518.73 598.48 517.00 598.94 515.01 599.28 512.72 599.60 510.35 599.63 508.38 599.34 506.99 599.20 506.47 598.75 507.85 598.38 505.79 599.27 502.24 599.04 499.82 599.10 499.87 598.60 498.04 599.56 495.53 600.22 493.69 600.04 491.28 600.68 489.48 600.93 486.51 601.03 483.80 602.06 481.40 602.26 478.92 602.26 476.48 603.09 473.44 603.88 470.78 604.44 468.27 604.23 465.83 605.21 464.18 605.69 463.52 605.29 464.98 605.63 466.39 607.13 469.61 608.09 475.04 608.48 481.23 609.45 487.53 610.75 492.33 610.90 490.79 611.90 486.75 612.59 484.39 613.33 483.45 614.83 481.15 616.82 478.69 618.74 477.55 620.45 476.88 622.46 474.54 624.87 471.00 627.04 466.92 629.58 462.44 631.68 459.45 633.65 458.21 635.11 459.32 635.98 460.31 636.12 459.47 637.21 456.16 638.30 453.44 637.64 452.33 637.42 452.41 637.75 451.09 637.81 448.23 637.42 445.94 636.28 445.20 636.46 444.45 637.21 443.31 637.88 441.16 638.74 439.01 640.69 438.32 641.87 439.68 642.68 440.92 643.22 442.54 643.36 443.83 643.44 445.20 643.17 447.18 642.14 450.00 641.29 452.95 640.56 457.22 639.51 462.01 638.24 466.97 637.08 466.61 635.95 466.45 635.52 471.32 633.48 474.66 631.97 472.51 631.16 470.78 629.70 473.19 628.43 475.05 629.00 476.60 628.67 484.04 628.04 490.86 628.72 493.98 628.89 497.56 629.40 499.12 629.41 496.49 630.25 493.45 630.37 491.67 630.40 490.75 631.50 489.49 631.94 489.95 632.81 490.72 633.77 491.26 634.37 490.91 635.01 489.30 636.71 489.23 638.22 490.74 639.20 492.76 640.62 494.24 641.99 494.40 643.11 493.12 644.75 493.00 646.26 494.63 648.27 497.50 650.09 499.65 651.63 499.75 653.64 498.23 655.58 495.73 656.49 492.44 658.34 489.99 659.64 488.07 661.01 484.76 661.81 479.37 663.08 474.14 665.00 469.39 665.93 464.44 666.31 459.86 667.26 455.90 667.90 452.77 668.33 450.31 669.40 448.29 670.42 446.65 671.25 446.56 671.91 446.67 672.68 446.35 673.99 445.40 675.13 445.10 676.26 444.67 677.15 444.30 677.88 443.09 678.41 441.48 679.24 439.99 679.56 439.95 680.09 439.75 680.34 438.70 680.27 436.88 680.23 435.41 680.09 435.30 679.46 436.52 678.96 437.68 678.38 439.39 677.44 440.63 675.97 441.92 674.55 442.79 672.63 443.36 671.50 443.55 670.50 443.29 669.29 444.04 667.99 444.47 667.53 445.43 667.05 446.31 667.12 446.96 667.18 447.86 667.36 448.78 670.01 449.14 670.58 449.25 669.99 449.14 668.06 448.66 664.46 447.65 660.55 444.00 658.02 441.25 656.26 439.90 654.29 438.46 653.59 436.13 653.38 433.75 653.78 430.62 653.86 426.00 655.76 422.03 658.68 421.13 660.29 418.53 662.29 413.98 666.43 413.16 670.27 410.71 674.66 408.34 680.19 406.25 684.42 405.31 685.33 403.82 682.52 402.38 677.20 400.97 673.64 399.68 671.87 398.64 669.48 397.31 667.46 396.24 665.82 395.57 662.85 394.85 658.72 394.63 655.18 394.63 651.31 393.92 649.31 393.32 647.85 392.61 646.78 392.10 645.89 389.45 646.83 389.01 646.88 388.32 646.72 387.21 649.01 385.23 652.45 384.32 653.77 383.43 652.69 380.35 650.67 377.79 649.81 377.05 649.82 +949.21 607.23 947.63 606.11 947.13 603.99 946.57 601.06 946.47 598.92 947.30 597.69 947.39 598.53 946.67 598.57 947.13 597.59 947.95 596.56 948.53 595.92 948.36 596.08 947.69 596.28 947.43 597.59 947.49 597.62 947.35 597.84 947.26 598.90 947.32 599.45 947.56 599.54 947.70 601.25 947.73 603.28 947.74 604.83 947.51 606.66 946.57 608.71 945.64 609.92 944.62 610.91 943.01 612.46 940.79 613.29 938.86 614.93 937.53 616.05 935.66 617.12 933.99 617.96 931.59 618.00 930.30 617.63 928.89 616.06 927.87 615.00 926.26 614.06 924.31 613.19 923.79 612.75 923.02 612.77 921.32 613.15 920.26 613.54 919.09 614.05 918.10 615.02 916.93 615.86 915.85 616.76 914.36 617.63 912.74 618.47 911.71 619.11 911.12 619.59 911.46 619.59 913.40 619.56 912.19 620.61 909.74 620.39 907.86 620.03 908.71 619.61 907.81 620.03 906.23 620.55 905.40 620.12 903.77 620.64 902.98 621.01 900.92 621.57 898.97 622.64 897.41 623.11 895.80 623.62 894.13 624.89 892.09 625.84 890.08 626.58 888.26 626.79 886.58 627.92 885.83 628.49 885.69 628.22 887.93 628.61 890.04 630.28 894.13 631.99 900.30 633.10 907.22 634.21 914.25 635.38 919.38 635.88 918.78 636.92 915.35 637.12 913.69 636.97 913.03 637.61 911.51 638.18 909.50 638.38 908.62 638.71 908.25 640.05 906.42 641.54 903.50 642.48 900.11 644.39 896.42 645.84 893.37 647.12 892.48 649.07 894.04 650.49 895.23 651.46 894.55 653.30 891.83 655.02 889.35 655.58 888.07 656.25 888.45 657.20 887.75 657.54 885.14 658.00 883.18 657.33 882.95 657.50 882.98 657.91 882.31 657.44 880.96 657.18 879.66 657.12 879.77 656.52 881.80 655.22 883.61 654.48 885.68 653.12 887.22 652.67 888.53 652.12 890.46 651.20 892.90 650.79 895.59 650.77 899.42 650.60 903.77 650.06 907.99 649.30 907.31 647.18 906.14 646.01 910.33 643.37 912.55 641.64 909.74 640.66 906.83 638.99 907.93 636.81 908.53 635.70 908.94 633.96 915.39 631.61 920.73 630.76 922.71 629.79 925.23 629.44 925.42 628.68 921.60 628.98 917.70 628.54 914.65 627.50 912.71 627.26 910.64 626.51 910.07 626.02 910.03 625.52 909.97 625.72 908.98 625.21 906.85 626.21 906.36 627.22 907.58 628.65 909.54 630.66 910.89 632.60 911.28 633.24 910.38 634.53 910.58 635.80 912.81 637.11 916.62 638.22 919.14 639.14 919.83 640.07 918.95 641.68 917.07 642.48 914.35 643.77 912.48 644.94 911.36 646.89 908.77 648.24 903.84 649.32 899.13 651.30 894.98 653.22 890.51 654.51 886.30 655.31 882.96 656.47 879.80 657.34 877.73 658.46 876.38 659.94 875.26 661.28 875.49 662.38 876.29 663.90 876.23 666.10 875.42 668.21 875.44 669.50 875.32 670.83 875.08 672.28 874.14 672.89 872.34 673.54 870.86 674.01 870.76 674.76 870.58 675.30 869.72 675.71 867.78 676.06 866.11 676.55 865.72 676.79 866.64 676.93 867.81 677.14 869.16 677.26 870.08 676.76 870.95 676.54 871.61 675.14 871.72 673.70 871.49 672.97 870.67 671.56 870.91 670.06 871.08 668.70 871.72 666.61 872.23 665.40 872.35 664.37 872.81 664.00 873.53 665.11 873.30 664.61 873.07 663.79 872.31 661.37 871.10 657.03 869.72 652.59 865.54 649.45 861.92 647.77 859.99 645.40 858.40 644.29 855.28 643.98 852.37 643.72 848.69 643.03 843.77 644.33 838.99 646.80 837.51 647.28 834.72 648.08 829.76 651.21 828.29 654.37 825.62 657.79 823.10 662.71 820.63 666.05 819.04 666.20 816.84 662.49 814.80 656.91 812.82 653.03 811.24 651.54 809.83 649.03 807.97 647.12 806.82 646.09 805.77 643.02 804.64 638.40 803.99 634.58 803.56 629.99 802.96 627.19 801.86 625.17 800.96 622.81 799.94 621.27 797.21 620.70 796.59 619.45 795.24 617.87 794.15 619.55 791.81 622.06 790.24 621.90 788.70 619.20 785.27 615.35 781.74 612.48 780.40 610.45 +953.34 258.56 952.24 257.43 952.25 255.42 952.18 252.76 952.20 250.76 953.31 249.38 953.79 250.36 953.26 250.42 953.77 249.43 955.14 248.39 955.62 247.93 955.49 247.97 954.51 248.41 954.14 249.36 954.30 249.35 954.37 249.46 954.39 250.31 954.79 250.64 955.49 250.55 955.96 252.02 955.89 253.77 955.90 254.93 955.45 256.39 954.66 257.97 953.77 258.91 952.48 259.40 951.00 260.43 948.96 260.96 947.27 262.08 946.09 263.00 944.17 263.48 942.59 263.91 940.58 263.49 939.48 262.52 937.69 260.48 936.47 259.02 934.95 257.56 932.73 256.19 931.88 255.31 930.70 254.89 929.21 254.88 927.88 254.90 926.39 254.87 925.26 255.00 924.35 255.46 923.08 255.59 921.70 255.86 920.46 256.21 919.45 256.14 918.97 255.74 918.83 255.25 920.61 254.51 918.88 254.91 915.73 254.07 913.25 253.00 913.21 251.69 911.40 251.52 908.89 251.30 907.20 250.32 904.68 250.19 903.47 249.81 900.93 249.41 898.51 249.77 896.64 249.41 894.65 248.92 892.61 249.46 890.08 249.49 887.86 249.60 885.36 249.12 883.09 249.18 881.55 248.99 880.65 247.93 882.41 247.31 883.64 247.92 887.32 248.99 893.29 249.00 899.69 248.15 905.69 248.12 910.40 247.45 908.93 247.49 904.37 247.06 901.28 246.39 899.26 246.39 895.59 246.46 891.15 246.42 888.49 246.25 886.83 246.96 883.44 247.87 878.55 248.32 873.61 249.71 868.67 250.25 864.85 251.18 863.43 252.24 864.80 252.84 866.44 253.07 865.66 254.22 862.74 255.34 861.05 255.48 860.28 255.42 860.56 255.50 859.53 255.51 857.11 255.20 855.12 253.71 854.21 253.23 853.24 252.59 851.04 251.44 847.55 250.01 843.75 249.08 841.76 247.94 841.57 245.99 841.29 244.28 841.76 242.69 841.73 241.65 842.87 240.75 844.69 239.87 847.24 239.37 850.40 239.17 854.62 239.12 859.67 238.43 864.41 237.95 865.25 237.95 863.35 237.32 867.29 235.36 869.33 234.62 866.57 234.77 863.67 234.31 864.21 233.32 863.75 233.76 862.81 233.73 867.68 232.70 871.76 233.19 872.87 233.45 874.88 234.45 874.77 235.01 870.41 236.50 866.19 237.22 862.38 237.27 859.37 238.12 856.16 238.46 854.20 238.78 852.77 239.29 851.91 240.21 849.84 240.39 846.69 241.92 845.73 243.37 846.71 244.80 848.70 246.38 850.08 247.83 849.52 248.37 847.71 248.78 846.99 249.35 847.58 249.49 849.91 249.77 850.90 249.46 849.97 249.59 847.67 250.28 844.57 250.29 840.30 250.30 837.30 250.30 835.39 250.79 831.95 250.75 826.07 250.58 820.56 251.19 815.89 251.74 810.95 251.51 805.90 251.03 801.69 250.69 798.02 250.28 795.26 250.25 793.13 250.70 791.60 250.31 791.14 250.28 791.63 250.34 791.63 251.34 790.70 252.28 790.31 252.60 789.90 252.86 789.47 253.20 788.05 252.86 785.37 252.86 783.40 252.41 782.81 252.14 782.42 252.02 781.05 251.52 778.83 251.04 777.21 250.68 776.65 250.00 777.60 249.37 779.15 248.84 780.59 248.08 782.11 246.68 783.48 245.80 784.07 243.87 783.68 242.25 782.95 241.12 781.70 239.13 781.21 237.28 779.80 235.90 778.44 233.75 777.44 232.75 776.44 231.75 775.37 231.27 774.16 232.80 772.16 232.36 771.16 231.36 769.58 228.64 767.50 224.24 765.15 219.81 760.45 216.90 756.64 215.03 754.44 212.61 752.11 211.50 748.51 211.02 744.62 210.75 740.18 210.25 734.11 211.46 728.47 213.97 725.59 214.53 718.04 214.41 711.37 217.64 708.82 220.66 704.73 224.21 700.86 228.95 696.94 232.50 694.37 232.56 691.10 228.54 688.78 222.88 686.90 219.29 685.22 217.73 683.33 214.73 681.89 212.95 680.97 211.39 679.96 207.92 678.40 203.42 677.52 199.37 676.61 194.92 675.40 192.57 673.84 190.59 671.55 188.46 669.98 186.96 666.11 186.87 664.03 186.02 661.62 185.03 659.66 187.05 656.66 190.06 653.92 190.55 650.67 188.51 646.01 185.67 641.15 183.79 638.07 183.01 +781.62 244.15 780.31 243.06 780.31 240.52 779.97 237.89 780.04 235.66 781.13 234.24 781.15 235.16 780.77 235.11 781.24 234.05 782.61 232.90 783.07 232.44 782.59 232.44 781.59 232.68 780.98 234.00 781.08 233.92 780.95 233.72 780.83 234.45 780.94 234.89 781.34 234.51 781.41 235.94 780.95 237.69 780.49 239.16 779.86 240.28 778.47 241.91 777.36 242.51 775.89 243.02 773.52 244.03 770.88 244.52 768.71 245.50 767.10 246.10 764.60 246.43 762.60 246.75 759.91 246.27 758.28 245.11 755.86 243.05 754.21 241.42 752.21 239.91 749.42 238.69 747.94 238.17 746.30 237.67 744.31 237.66 742.34 237.56 740.03 237.70 738.48 238.04 737.06 238.45 734.99 238.40 732.81 238.61 730.74 238.68 728.83 238.65 727.34 238.22 726.70 237.75 727.71 237.30 725.20 237.78 721.22 237.30 717.45 236.45 716.92 235.46 713.88 235.81 710.58 235.82 707.76 235.40 704.66 235.50 702.21 235.18 698.81 235.12 695.34 235.67 692.65 235.33 689.73 234.88 686.98 235.70 683.47 236.09 680.12 236.05 677.21 235.42 673.64 236.18 671.14 236.51 669.76 235.39 670.30 235.32 671.03 236.33 673.94 237.49 678.88 237.77 684.16 238.04 689.35 238.60 693.31 238.33 690.81 238.86 685.53 238.95 682.04 238.98 678.98 239.77 674.72 241.15 669.75 242.10 666.45 243.04 664.11 244.55 660.14 246.22 654.68 247.75 649.09 249.81 643.23 251.12 638.80 252.64 636.89 254.15 637.62 254.68 638.40 255.13 637.20 255.83 633.69 256.83 631.37 256.67 629.93 256.21 630.14 256.43 628.53 256.42 625.32 256.17 622.92 254.63 621.39 254.27 619.86 254.27 617.20 253.89 612.98 253.53 608.47 254.09 605.93 253.93 604.97 253.43 604.36 253.15 604.31 252.24 604.29 251.81 605.30 251.33 607.08 250.33 609.65 249.88 612.72 249.27 617.19 249.28 622.61 248.29 627.10 247.57 626.03 246.61 624.60 246.30 629.07 244.40 631.69 243.94 629.29 243.94 627.16 243.79 628.65 243.32 629.05 244.33 628.87 244.92 634.89 245.17 639.48 246.72 641.30 247.55 643.87 249.03 644.30 249.67 640.80 251.65 637.02 252.53 633.82 253.20 631.50 254.67 628.65 255.70 627.24 257.17 626.35 258.22 625.65 260.13 624.06 260.90 620.93 262.84 620.11 264.77 621.16 266.28 622.88 268.04 623.93 269.54 622.86 270.80 620.40 271.45 619.30 273.03 619.29 274.11 620.78 275.06 621.41 275.88 619.92 276.89 616.83 278.39 613.05 278.82 608.02 279.74 604.52 280.31 601.48 281.32 597.48 281.32 590.79 281.68 584.79 282.68 579.27 283.17 573.73 282.98 567.85 282.84 562.92 283.22 558.91 283.21 555.50 283.22 552.57 283.70 550.30 283.85 549.72 283.91 549.31 284.34 548.72 285.18 547.42 286.28 546.69 286.71 545.74 287.23 545.00 287.43 542.84 287.54 540.26 287.70 537.74 287.90 537.02 287.88 536.29 287.84 534.79 287.38 532.24 287.01 530.58 286.86 529.85 286.28 530.53 285.82 531.86 285.20 533.37 284.20 534.76 282.72 535.97 281.59 536.68 279.53 536.07 278.18 535.55 277.62 534.55 275.62 534.02 274.57 532.95 274.03 531.80 272.75 530.89 272.54 529.91 272.68 529.65 272.82 528.65 275.82 526.98 276.01 525.99 275.15 524.84 273.10 522.81 269.18 520.80 265.25 516.56 262.26 512.74 261.05 510.55 258.89 508.55 257.89 505.41 257.10 501.48 258.17 497.22 257.62 491.56 259.54 486.57 262.63 484.34 263.67 479.82 265.47 473.82 269.47 471.63 272.82 467.63 277.82 464.45 282.21 460.45 287.21 458.45 287.21 455.45 284.21 453.45 279.21 451.45 276.21 450.45 273.21 448.45 272.21 447.45 269.21 446.45 268.21 445.45 264.21 443.45 260.21 443.45 256.21 442.45 253.21 441.45 250.21 439.70 248.89 438.40 246.95 436.50 246.32 432.53 246.54 435.98 238.05 433.48 238.62 428.48 247.62 425.47 251.61 423.47 252.61 421.34 250.61 416.34 249.61 411.42 248.67 420.02 249.38 +756.00 403.00 754.85 401.73 754.70 399.14 754.47 396.23 754.57 393.92 755.53 392.74 755.96 393.42 755.44 393.30 755.97 392.00 757.30 390.99 757.90 390.52 757.69 390.46 756.89 390.84 756.47 391.90 756.61 391.84 756.57 391.74 756.44 392.70 756.75 392.81 757.12 392.62 757.40 394.01 757.15 396.00 757.13 397.44 756.59 398.86 755.59 400.49 754.47 401.59 753.05 402.20 751.38 403.44 748.93 403.97 747.00 405.17 745.62 405.88 743.47 406.62 741.73 407.02 739.30 406.62 738.04 405.66 736.21 404.11 735.10 402.63 733.39 401.33 731.18 400.32 730.33 399.75 729.17 399.66 727.44 399.71 726.12 399.88 724.81 400.30 723.65 400.60 722.50 401.06 721.09 401.53 719.50 401.99 718.00 402.16 716.74 402.19 716.14 402.21 716.12 401.85 718.10 401.25 716.44 402.26 713.39 401.82 711.00 401.39 711.33 400.52 709.79 401.17 707.70 401.70 705.99 401.02 703.98 401.58 702.37 401.72 700.03 401.80 697.96 402.61 695.85 402.65 693.88 402.62 692.13 403.51 689.60 404.09 687.45 404.62 685.41 404.38 683.20 404.99 682.17 405.36 681.60 405.01 683.56 405.01 685.15 406.40 689.07 407.82 695.10 408.19 701.69 408.86 708.02 409.51 713.03 409.51 711.76 410.50 707.79 410.69 705.32 410.69 704.04 411.64 701.07 413.02 698.08 413.99 696.72 414.96 695.62 416.19 693.01 418.20 689.01 419.50 684.99 421.51 680.26 422.87 677.06 424.28 676.10 425.70 677.52 426.61 679.07 426.68 678.35 427.98 675.86 429.11 673.53 429.16 672.80 429.08 673.23 429.26 672.24 429.28 669.84 429.30 667.82 427.98 667.24 428.02 666.83 428.57 665.15 428.09 662.75 427.92 659.72 428.39 658.71 428.66 659.62 428.09 660.02 427.61 661.25 427.00 661.97 426.81 663.34 426.36 665.33 425.32 668.29 424.86 671.35 424.56 675.69 424.10 680.77 423.17 685.36 422.56 684.48 420.93 683.37 420.47 687.72 418.25 690.10 417.31 687.70 416.84 685.42 416.02 686.90 415.02 687.16 415.35 687.35 415.19 693.67 414.10 699.03 414.65 700.98 415.00 703.82 415.58 704.06 415.89 700.58 416.91 697.00 417.32 694.03 417.32 692.12 418.04 689.91 418.33 688.95 419.00 688.79 419.61 688.79 420.61 687.41 420.87 685.18 422.44 684.92 423.90 686.21 425.29 688.39 426.92 690.07 428.48 690.02 429.35 688.78 430.40 688.27 431.68 689.96 432.75 693.02 433.99 694.82 434.57 694.80 435.99 693.00 437.31 690.72 438.03 687.25 438.93 684.83 440.34 683.52 440.99 680.82 441.61 675.35 442.51 670.38 443.84 666.32 444.87 661.90 445.34 657.19 445.46 653.81 445.99 650.40 446.38 648.43 446.87 646.69 447.83 645.71 448.24 645.92 448.78 646.53 449.71 646.58 450.72 646.01 452.17 646.01 452.92 646.00 453.70 645.92 454.39 644.66 454.58 642.85 455.02 641.45 455.30 641.48 455.41 641.39 455.49 640.44 455.46 638.68 455.44 637.59 455.32 637.58 454.87 638.84 454.45 640.32 454.08 642.36 453.49 643.99 452.32 645.65 451.62 646.71 449.70 646.88 448.39 646.89 447.61 646.28 446.04 646.59 444.76 646.61 443.90 646.54 442.37 646.53 441.93 646.66 441.50 646.74 441.54 646.97 443.82 646.27 443.87 646.00 443.04 645.38 440.70 644.07 436.93 642.48 432.85 638.41 430.11 635.31 428.34 633.64 426.31 631.99 425.37 629.48 425.34 626.38 425.24 622.44 425.10 617.84 426.59 613.22 429.50 611.39 430.48 607.93 432.07 602.68 435.62 601.15 439.12 598.32 443.01 595.74 448.00 592.83 452.01 591.31 452.76 589.00 449.08 587.32 443.61 585.80 440.11 584.42 438.62 582.96 436.12 581.96 434.12 580.93 432.83 580.35 429.71 579.13 425.35 578.84 421.43 578.27 417.47 577.27 415.47 576.35 413.60 574.93 411.85 573.93 410.85 570.74 411.15 569.55 410.78 568.06 410.18 566.78 412.16 564.24 415.65 562.45 416.36 560.31 414.87 556.31 412.15 552.80 411.07 550.72 410.46 +985.73 366.51 984.55 365.44 984.44 363.35 984.13 360.86 984.21 358.79 985.17 357.69 985.45 358.59 985.06 358.69 985.61 357.66 986.81 356.75 987.41 356.25 987.27 356.31 986.46 356.47 986.08 357.75 986.26 357.80 986.26 357.86 986.26 358.86 986.71 359.18 987.28 359.25 987.77 360.74 987.77 362.66 987.89 363.86 987.59 365.45 986.82 367.27 986.10 368.16 985.08 369.07 983.47 370.18 981.35 370.80 979.73 372.17 978.69 373.20 976.86 373.85 975.37 374.39 973.30 374.36 972.29 373.52 970.74 371.70 969.72 370.13 968.22 369.16 966.18 367.79 965.65 367.00 964.58 366.80 963.17 366.79 962.12 366.93 960.95 367.04 959.97 367.51 959.09 368.02 958.10 368.49 956.73 369.10 955.63 369.52 954.67 369.66 954.27 369.71 954.58 369.29 956.61 368.82 955.31 369.34 952.35 368.82 950.30 367.85 950.75 366.79 949.35 366.72 947.34 366.69 946.06 365.68 944.09 365.71 942.86 365.44 940.83 365.43 938.79 365.96 937.16 365.79 935.32 365.70 933.58 366.51 931.36 366.88 929.42 367.16 927.43 366.70 925.43 367.10 924.47 367.11 923.91 366.07 926.03 365.74 927.87 366.79 931.89 368.02 938.20 368.36 944.86 368.66 951.75 368.97 956.76 368.48 955.69 368.80 951.64 368.43 949.07 367.56 947.71 367.77 944.71 367.77 941.21 367.66 939.19 367.43 938.18 368.21 935.41 369.17 931.35 369.54 927.07 370.89 922.42 371.65 919.05 372.56 917.99 374.11 919.47 374.70 920.93 375.26 920.45 376.75 917.69 378.16 915.73 378.51 915.20 378.59 915.49 379.10 914.66 379.13 912.22 379.14 910.22 378.14 909.61 377.73 909.21 377.27 907.67 376.13 905.17 374.71 902.39 373.78 901.13 372.53 901.50 370.61 902.14 368.95 902.98 367.15 903.52 366.15 904.79 365.23 906.81 364.26 909.36 363.82 912.42 363.68 916.57 363.55 921.59 363.08 926.11 362.62 924.57 360.77 922.86 359.84 926.87 357.81 928.64 356.48 925.65 356.48 922.65 355.48 923.31 354.08 923.10 353.96 922.43 353.11 927.60 351.47 932.12 351.39 933.21 351.24 935.31 351.66 935.11 351.70 930.77 352.75 926.39 353.17 922.71 352.77 919.82 353.17 916.79 353.03 915.10 352.86 914.23 352.86 913.57 353.46 911.57 353.46 908.76 354.64 908.03 355.90 909.09 357.34 911.16 359.02 912.56 360.59 912.44 361.01 910.86 361.63 910.67 362.47 911.93 362.69 914.87 363.12 916.46 363.04 916.17 363.28 914.49 364.10 911.84 364.11 908.18 364.35 905.61 364.77 904.22 365.48 901.26 365.73 895.51 365.91 890.40 366.86 885.94 367.74 881.43 367.76 876.66 367.83 872.66 367.83 869.44 367.74 866.83 367.98 864.91 368.51 863.76 368.71 863.85 369.09 864.37 369.52 864.42 370.86 863.80 371.94 863.73 372.53 863.31 373.31 863.29 373.84 861.64 373.84 859.44 373.88 857.62 373.68 857.30 373.64 857.19 373.66 855.72 373.53 853.82 373.09 852.23 373.00 851.67 372.62 852.81 372.05 854.20 371.69 855.65 371.56 857.22 370.35 858.40 369.61 858.91 367.81 858.69 366.04 857.77 365.06 856.78 363.06 856.74 361.13 855.81 359.58 854.95 357.31 854.35 355.81 853.48 354.59 852.86 354.19 852.27 355.21 850.76 354.40 849.76 353.40 848.38 350.64 846.52 346.13 844.14 341.66 839.47 338.56 835.78 336.50 833.48 333.91 831.26 332.75 827.66 332.29 823.90 331.88 819.39 331.05 813.53 332.18 808.37 334.56 805.65 334.74 801.34 335.45 795.31 338.34 793.25 340.95 789.27 344.51 785.79 349.00 782.29 352.16 779.65 352.23 776.71 348.22 774.72 342.39 772.62 338.74 770.84 337.12 769.25 334.14 767.77 332.12 766.80 330.59 765.74 327.36 764.24 322.77 763.47 318.66 762.55 314.07 761.42 311.41 759.77 309.43 757.82 307.34 756.39 305.39 752.63 305.05 750.63 304.05 748.78 302.49 746.82 304.37 743.92 306.60 741.55 306.84 738.68 304.38 734.11 300.98 729.37 298.55 726.82 296.58 +667.20 232.00 665.89 230.59 665.78 228.06 665.40 225.12 665.46 222.69 666.16 221.52 666.34 222.27 665.68 222.13 666.19 220.87 667.22 219.90 667.54 219.36 666.94 219.40 665.74 219.86 664.90 221.03 664.78 220.96 664.37 220.78 663.88 221.65 663.88 221.65 663.99 221.19 663.83 222.65 662.86 224.53 662.05 225.58 660.75 226.99 659.13 228.55 657.16 229.33 655.09 229.67 652.42 230.56 649.27 230.62 646.29 231.61 644.08 232.01 641.07 232.32 638.30 232.44 635.20 231.55 632.79 230.48 629.77 228.59 627.47 226.81 624.81 225.21 621.37 223.94 619.42 222.96 616.90 222.94 614.18 222.83 611.53 222.85 608.56 223.05 606.01 223.34 603.55 223.75 600.88 223.71 597.81 223.68 594.81 223.68 592.10 223.25 589.65 222.84 587.80 222.41 587.83 221.89 584.36 222.65 579.23 222.12 574.64 221.75 572.64 221.13 568.89 221.65 564.51 221.97 560.87 221.35 556.58 221.84 553.08 221.76 548.69 221.64 544.31 222.10 540.35 221.92 536.33 221.41 532.54 222.27 528.04 222.80 523.66 222.91 519.53 222.35 515.24 222.95 511.71 223.40 509.29 222.73 508.93 222.76 508.61 224.08 510.74 225.28 514.86 225.70 519.20 226.47 523.20 227.47 526.18 227.51 522.79 228.01 516.78 228.56 511.88 229.02 508.52 230.48 503.43 232.37 497.83 234.39 493.77 236.38 490.49 238.32 485.84 240.49 479.42 242.83 473.12 245.48 466.28 247.25 460.93 248.89 458.30 250.78 458.38 251.36 458.74 251.37 456.56 252.70 452.59 253.60 449.71 253.11 447.74 252.67 447.25 252.87 445.23 252.83 441.57 252.00 438.57 250.48 436.34 250.60 433.84 251.09 430.49 251.00 425.56 251.54 420.50 253.06 417.06 253.91 415.49 254.29 414.24 254.55 413.79 254.57 413.64 254.50 414.23 254.07 416.18 252.89 418.73 252.31 422.25 251.78 427.07 251.22 432.52 250.11 437.46 249.60 436.38 248.75 435.29 249.11 439.98 247.89 443.18 247.41 441.65 247.38 440.25 246.98 442.24 246.98 443.14 248.83 444.10 250.23 450.52 251.16 456.16 253.48 458.67 254.98 462.04 256.95 463.12 258.29 460.08 260.25 456.76 261.74 453.90 262.86 451.89 264.89 449.39 266.67 448.36 268.74 447.82 270.74 447.63 272.77 445.95 274.29 442.93 276.75 442.16 278.81 443.11 280.79 444.60 282.53 445.26 284.46 444.04 285.66 441.01 287.16 439.23 288.83 438.69 290.80 439.59 292.53 439.19 293.84 436.75 295.61 433.27 297.49 428.65 298.44 423.16 299.39 418.52 300.30 414.96 301.19 409.95 301.31 402.51 301.76 395.42 302.77 389.17 303.20 382.72 302.75 376.24 303.00 370.61 303.11 365.73 303.05 361.76 303.24 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 526.82 248.92 523.90 248.48 522.24 248.15 521.64 248.24 519.65 248.24 516.28 247.58 513.40 245.82 511.34 245.95 509.22 246.12 505.88 245.88 501.32 246.01 496.25 247.22 493.11 247.53 491.80 247.59 490.60 247.54 490.42 247.03 490.14 246.90 490.94 246.24 492.60 245.34 495.23 244.83 498.42 244.28 503.06 243.88 508.52 242.87 513.45 242.24 512.26 241.30 510.92 241.43 515.52 240.12 518.49 239.57 516.65 239.73 514.92 239.43 516.58 239.36 517.45 240.92 517.89 242.01 524.17 242.83 529.48 244.85 531.67 246.03 534.69 248.00 535.51 249.08 532.19 251.26 528.77 252.31 525.73 253.34 523.54 255.19 520.95 256.68 519.50 258.68 518.92 260.29 518.49 262.24 516.71 263.64 513.68 266.06 512.89 267.95 513.63 269.78 515.21 271.47 515.92 273.26 514.79 274.61 512.09 275.80 510.29 277.26 509.78 278.91 510.85 280.43 510.71 281.41 508.52 282.87 505.12 284.55 500.67 285.22 495.17 286.30 490.76 287.31 487.48 288.07 482.93 288.04 475.36 288.69 468.37 289.73 462.50 290.09 456.31 289.93 449.84 289.91 444.47 290.00 439.68 289.89 435.91 290.15 432.56 290.82 429.87 290.85 428.59 291.15 428.12 291.44 427.09 292.17 425.42 292.97 423.82 293.48 422.88 294.21 421.86 294.40 419.52 294.69 416.50 295.20 413.73 295.43 412.70 295.45 411.74 295.43 409.87 295.14 407.34 294.93 405.20 294.70 404.27 294.11 404.53 293.70 405.70 292.90 406.78 291.98 408.98 291.61 410.22 290.22 410.72 288.21 410.21 286.89 409.52 286.14 408.34 284.68 407.84 283.64 406.74 283.35 405.50 282.77 404.45 283.18 403.65 283.32 403.15 283.86 402.18 287.01 400.65 287.93 399.71 287.32 398.33 285.39 396.65 281.74 394.47 277.92 389.99 275.34 386.42 273.68 383.99 271.95 381.96 271.07 378.62 271.10 375.11 271.61 370.57 272.13 365.03 273.79 359.96 276.84 357.23 278.70 353.11 280.75 346.90 285.02 344.78 289.00 340.97 293.42 337.48 299.23 333.93 303.64 331.52 304.87 328.62 302.01 326.56 296.92 324.62 293.46 323.19 291.82 321.58 289.33 320.08 287.39 319.16 285.45 318.24 282.42 316.76 278.39 316.26 274.54 315.38 270.78 314.35 268.80 312.86 267.46 311.21 266.19 309.78 265.23 306.29 265.99 304.68 266.35 302.80 266.34 301.45 268.59 298.89 272.09 296.96 273.53 294.64 272.84 290.62 270.90 286.55 270.50 284.53 270.85 +1020.54 126.01 1019.42 125.09 1019.52 123.11 1019.38 120.55 1019.46 118.52 1020.43 117.39 1020.76 118.28 1020.35 118.48 1020.85 117.47 1022.11 116.59 1022.63 116.01 1022.32 116.04 1021.36 116.32 1020.53 117.27 1020.67 117.25 1020.69 117.42 1020.66 118.30 1020.98 118.70 1021.72 118.29 1022.14 119.68 1022.03 121.23 1021.98 122.22 1021.17 123.23 1020.18 124.75 1019.25 125.33 1017.76 125.86 1016.13 126.63 1013.70 126.78 1011.95 127.84 1010.85 128.41 1008.65 128.58 1006.89 128.77 1004.86 128.30 1003.50 126.96 1001.19 124.52 999.74 122.67 998.12 121.30 995.36 119.41 994.33 118.34 992.52 117.37 990.70 116.88 988.93 116.57 986.92 116.09 985.31 115.97 983.87 116.08 982.26 115.97 980.48 115.89 978.72 115.54 977.41 115.38 976.40 114.83 975.45 113.67 976.57 112.38 974.19 112.42 970.07 110.93 966.65 109.54 965.68 107.52 962.64 106.67 959.64 106.23 956.74 104.37 953.69 103.83 951.25 102.84 947.83 102.19 944.62 101.82 941.90 100.92 939.27 100.28 937.38 100.28 933.87 99.82 930.80 99.47 927.78 98.47 924.17 97.78 921.41 96.78 919.61 95.21 919.73 93.94 920.32 94.37 922.98 94.62 928.06 94.28 933.04 93.46 938.00 92.75 941.40 91.21 938.85 91.14 933.12 89.86 928.58 88.67 925.31 88.34 919.83 87.89 914.13 87.61 909.71 87.22 906.58 87.34 902.03 88.22 895.77 88.07 889.74 89.06 883.43 89.44 878.14 89.69 876.18 90.70 876.53 90.79 877.54 90.80 875.86 91.66 872.80 92.56 870.48 92.44 869.40 92.40 869.13 92.31 867.33 91.66 864.56 91.23 861.60 88.91 859.45 87.70 857.47 86.73 854.54 85.31 850.41 84.21 844.41 82.21 840.60 79.82 839.10 77.65 837.08 75.60 836.46 73.55 835.84 72.50 836.15 71.32 837.66 70.32 839.71 69.50 842.36 69.42 846.56 69.35 852.44 69.30 856.61 68.62 854.70 67.53 852.32 67.15 855.58 65.38 858.08 65.63 855.65 66.22 852.73 66.36 852.95 66.30 851.98 67.26 850.54 67.64 854.82 67.57 858.34 68.76 858.89 69.77 861.26 72.10 860.59 72.87 856.13 74.93 851.35 76.54 847.29 77.27 844.25 79.21 839.70 79.51 837.17 80.67 835.10 81.55 833.67 83.25 830.42 83.47 826.72 85.39 825.26 87.11 825.38 88.80 827.13 90.48 827.47 91.81 826.22 92.42 823.22 92.42 821.14 92.86 820.45 92.71 820.86 92.55 820.65 92.13 817.86 91.81 814.19 92.17 809.65 91.60 803.47 91.17 798.32 90.42 795.29 90.45 791.22 90.31 783.22 89.31 775.50 89.02 769.90 88.95 763.32 87.84 756.66 86.79 751.06 86.13 745.64 85.08 741.77 84.72 738.02 84.30 735.50 83.65 733.98 83.21 732.91 82.70 731.65 82.81 730.24 83.54 728.80 83.41 727.23 83.27 726.51 83.28 723.15 82.45 719.73 82.32 716.31 81.49 715.13 81.46 713.43 80.59 710.93 79.52 708.34 79.17 705.66 78.42 704.20 77.48 703.85 76.59 704.45 75.73 705.61 74.87 706.76 73.57 706.55 71.63 706.55 69.63 705.56 68.18 704.31 67.24 702.21 65.38 700.10 63.13 697.20 61.36 695.11 60.13 692.82 59.35 690.24 58.31 688.59 58.38 684.82 58.32 681.80 58.27 679.61 56.98 677.43 54.42 674.53 50.19 671.24 45.83 666.05 42.87 661.72 40.93 658.11 38.58 655.00 37.39 650.71 37.04 645.85 36.94 639.91 36.46 633.51 37.99 627.16 40.43 623.13 41.61 617.52 42.83 609.86 46.08 606.15 49.42 601.17 53.26 596.27 58.46 591.32 62.18 587.57 62.62 583.31 58.77 580.43 53.20 578.00 49.51 575.95 47.89 573.42 44.87 571.82 42.88 570.68 41.02 569.23 37.63 567.38 33.10 566.17 29.13 564.76 24.84 563.21 22.45 561.24 20.55 558.53 18.48 556.59 17.15 552.46 17.23 549.46 16.73 546.59 16.30 544.16 18.22 540.66 21.58 537.74 22.54 534.28 21.07 528.94 18.55 523.39 17.11 520.39 17.11 +1141.19 172.07 1140.06 171.44 1140.19 169.53 1140.06 167.35 1140.43 165.32 1141.32 164.34 1141.78 165.35 1141.29 165.56 1141.89 164.59 1143.30 163.89 1143.81 163.35 1143.64 163.37 1142.65 163.51 1142.17 164.41 1142.57 164.40 1142.69 164.61 1142.71 165.51 1143.22 165.98 1144.21 165.95 1144.73 167.39 1144.78 168.85 1144.90 169.57 1144.70 170.95 1144.16 172.46 1143.33 173.17 1142.32 173.68 1141.00 174.65 1138.98 175.11 1137.45 176.07 1136.59 176.77 1134.89 177.11 1133.56 177.55 1131.74 177.07 1130.97 176.09 1129.27 173.99 1128.26 171.90 1126.82 170.38 1124.73 168.66 1124.18 167.26 1122.90 166.54 1121.44 166.05 1120.28 165.72 1118.86 165.21 1117.91 165.20 1117.06 165.21 1116.09 165.21 1114.76 165.28 1113.77 165.26 1112.90 164.96 1112.40 164.50 1112.62 163.46 1114.68 162.39 1113.11 162.01 1109.86 160.53 1107.10 158.76 1107.26 156.82 1105.03 155.86 1102.70 154.86 1100.75 152.94 1098.38 151.95 1096.82 150.85 1094.57 150.11 1092.12 149.69 1090.20 148.71 1088.25 147.70 1086.04 147.73 1083.42 147.06 1081.32 146.46 1078.82 145.37 1076.42 144.74 1074.72 143.49 1073.56 141.56 1075.12 140.12 1076.04 139.95 1079.57 140.01 1085.75 139.31 1091.90 138.32 1097.90 136.79 1102.57 135.46 1100.57 134.95 1095.88 133.41 1091.88 131.41 1089.69 130.53 1085.14 129.14 1080.12 127.69 1076.41 126.31 1074.47 126.17 1070.37 126.08 1065.21 125.35 1059.24 125.83 1054.30 125.57 1050.30 125.83 1048.64 126.34 1049.76 126.55 1051.42 126.50 1050.59 127.54 1047.59 128.54 1045.59 128.54 1044.75 128.41 1044.82 128.32 1043.96 127.70 1041.82 127.39 1039.54 125.38 1038.55 124.19 1037.42 122.65 1034.58 120.23 1030.59 117.40 1025.82 114.94 1023.54 111.84 1022.28 108.45 1021.53 105.40 1020.58 102.41 1021.32 100.54 1020.70 99.24 1022.81 97.73 1025.17 97.17 1027.57 97.36 1031.57 97.36 1037.30 96.82 1041.38 96.75 1039.50 95.48 1035.87 94.90 1039.55 92.98 1040.76 92.51 1038.34 93.26 1035.32 93.25 1034.53 92.46 1033.49 93.21 1031.34 93.05 1033.75 91.68 1037.51 92.28 1037.72 92.56 1038.72 93.56 1037.84 94.74 1033.59 96.90 1027.77 97.93 1024.77 97.93 1020.54 99.07 1016.42 99.22 1012.65 99.35 1010.69 99.69 1007.77 100.61 1005.77 100.61 1001.96 102.10 1000.58 103.18 1000.69 104.63 1002.64 105.97 1003.54 107.19 1002.40 107.12 1000.35 106.66 998.35 106.66 998.14 106.66 999.63 105.66 999.96 104.05 998.31 102.90 994.88 102.19 991.23 101.04 985.91 99.94 982.06 98.76 979.25 98.11 975.37 96.90 968.49 95.71 961.93 95.24 956.84 94.89 950.96 93.52 945.22 92.11 940.03 90.81 935.50 89.37 932.09 88.40 928.92 87.43 926.63 86.25 925.66 85.25 925.18 84.52 924.63 84.56 923.45 84.76 922.44 84.30 921.35 83.97 920.61 83.60 918.24 82.78 914.98 81.90 911.99 80.81 910.63 79.97 909.68 79.15 907.64 78.12 904.82 77.13 902.48 76.11 901.28 75.02 901.16 73.75 901.72 72.76 902.90 71.74 903.53 69.73 904.61 68.66 904.62 66.16 903.14 63.97 901.49 62.38 897.41 59.51 895.72 56.98 893.22 54.71 890.30 51.74 887.75 49.77 885.15 48.12 882.93 47.14 880.08 47.82 876.74 46.81 874.80 45.30 872.26 41.88 869.39 37.33 866.00 32.56 860.35 29.13 855.75 26.72 852.10 24.03 848.83 22.50 844.43 21.77 839.17 20.87 833.21 19.85 826.41 20.81 819.96 22.80 815.19 22.87 809.29 23.34 801.47 25.77 797.44 28.25 791.91 31.40 786.84 35.72 781.46 38.65 777.44 38.24 773.08 33.87 769.91 27.74 767.43 23.94 765.28 22.05 762.77 19.06 760.67 16.79 759.30 15.17 757.78 11.66 755.78 7.66 754.41 3.65 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +1192.10 481.88 1190.62 481.06 1190.22 479.51 1189.85 477.15 1189.91 475.35 1190.69 474.41 1191.03 475.52 1190.50 475.75 1190.95 475.15 1192.09 474.36 1192.75 473.88 1192.65 474.03 1192.04 474.15 1191.81 475.38 1191.99 475.62 1192.03 475.93 1192.09 477.06 1192.59 477.74 1193.26 478.26 1193.93 479.93 1194.28 481.83 1194.71 483.29 1194.69 484.92 1194.39 486.87 1193.87 488.02 1193.00 488.95 1191.88 490.50 1190.01 491.43 1188.75 493.00 1187.88 494.37 1186.49 495.38 1185.25 496.36 1183.38 496.68 1182.81 496.22 1181.63 494.53 1180.99 493.34 1179.79 492.44 1178.27 491.33 1178.17 490.65 1177.51 490.28 1176.62 490.46 1175.91 490.94 1175.30 491.08 1174.69 491.98 1174.29 493.02 1173.76 493.78 1172.86 494.78 1172.11 495.70 1171.78 496.37 1171.85 496.83 1172.68 496.85 1175.33 496.86 1174.83 497.54 1172.64 496.91 1171.40 496.03 1172.62 495.07 1172.10 494.89 1170.86 494.70 1170.43 493.62 1169.52 493.43 1168.97 493.38 1167.54 493.51 1166.25 494.18 1165.45 494.28 1164.35 494.60 1163.28 495.75 1161.82 495.94 1160.71 496.51 1159.32 496.53 1158.22 496.97 1158.04 496.83 1158.44 496.10 1161.31 496.16 1163.91 497.27 1168.71 498.60 1175.83 499.57 1183.63 500.31 1191.59 500.77 1197.63 500.83 1197.38 501.18 1194.28 500.42 1192.55 499.20 1192.18 498.78 1190.24 497.78 1187.85 496.42 1186.86 495.37 1186.59 495.85 1184.69 496.10 1181.64 495.88 1178.15 496.94 1174.36 497.41 1171.64 498.28 1171.18 500.07 1173.19 501.44 1175.15 502.98 1174.98 505.04 1172.52 506.82 1170.65 507.95 1170.25 508.85 1171.10 509.89 1170.67 510.35 1168.52 510.79 1167.10 510.03 1167.25 509.82 1167.67 509.19 1167.19 507.49 1165.76 505.35 1164.29 503.32 1164.38 500.80 1166.31 497.75 1167.84 495.20 1169.70 492.70 1170.87 490.94 1172.44 490.00 1174.46 488.93 1176.95 488.65 1179.71 488.93 1183.60 489.23 1188.28 489.30 1192.49 489.28 1190.68 487.28 1188.70 485.67 1192.30 483.08 1193.92 481.57 1190.03 480.92 1186.25 479.36 1186.25 477.22 1185.34 475.83 1184.14 473.46 1189.07 470.57 1193.10 469.30 1193.81 468.00 1195.27 467.91 1194.40 467.29 1189.48 467.46 1184.44 467.04 1180.30 466.06 1177.17 465.62 1173.77 464.38 1171.94 463.02 1170.85 462.03 1169.85 461.89 1167.98 461.00 1165.18 461.63 1164.37 462.49 1165.37 464.05 1167.64 466.14 1169.45 467.90 1169.64 468.30 1168.96 468.64 1169.45 469.31 1171.89 469.17 1175.86 469.12 1178.79 468.60 1179.50 468.36 1178.84 468.77 1177.07 468.56 1174.48 468.49 1172.94 468.49 1172.36 469.71 1170.39 470.21 1165.60 470.42 1161.21 471.42 1157.68 472.73 1153.71 473.34 1149.78 473.67 1146.62 473.96 1143.96 474.00 1142.17 474.49 1141.14 474.96 1140.70 475.35 1141.50 475.84 1142.50 476.84 1143.26 478.71 1143.29 480.23 1143.71 481.06 1143.83 482.14 1144.21 483.21 1143.21 483.58 1141.56 483.50 1140.04 483.39 1140.06 483.44 1140.10 483.97 1139.31 484.06 1137.38 484.04 1136.22 484.25 1135.94 484.06 1136.98 484.27 1138.29 484.55 1140.05 484.83 1141.41 484.29 1142.59 483.95 1143.09 482.48 1142.80 480.69 1142.25 479.14 1141.25 477.14 1141.05 474.75 1140.40 472.36 1139.99 468.81 1139.40 466.35 1139.06 464.37 1138.66 462.58 1138.06 462.36 1136.91 460.73 1135.92 459.04 1134.55 455.68 1132.57 450.85 1130.20 445.80 1125.33 442.29 1121.38 440.22 1118.98 437.23 1116.60 435.82 1113.14 434.87 1109.14 433.90 1104.43 432.25 1098.53 433.01 1093.06 434.42 1090.50 433.88 1086.49 433.74 1080.34 435.06 1078.18 437.01 1074.21 439.83 1070.69 442.99 1067.26 445.28 1064.92 444.52 1061.76 439.71 1059.25 433.76 1057.01 430.06 1054.94 428.56 1052.97 425.61 1051.34 423.66 1050.13 422.04 1048.81 418.46 1047.33 413.54 1046.28 408.92 1045.39 404.05 1044.12 401.14 1042.60 398.29 1040.66 395.18 1039.33 392.49 1035.64 391.13 1033.70 388.94 1031.49 386.22 1029.49 387.07 1026.12 388.37 1023.36 386.92 1020.31 382.93 1015.24 377.89 1010.33 373.84 1007.15 370.08 +1138.70 311.08 1137.66 310.36 1137.58 308.42 1137.45 306.24 1137.65 304.36 1138.70 303.33 1139.03 304.36 1138.70 304.52 1139.49 303.68 1140.78 302.90 1141.51 302.28 1141.40 302.44 1140.73 302.49 1140.31 303.45 1140.73 303.56 1140.84 303.77 1141.02 304.82 1141.66 305.33 1142.53 305.53 1143.17 307.00 1143.53 308.72 1143.97 309.84 1143.94 311.38 1143.49 313.07 1143.07 314.12 1142.20 314.77 1141.11 316.16 1139.36 316.74 1138.14 318.02 1137.53 319.04 1135.99 319.69 1134.97 320.34 1133.32 320.41 1132.71 319.58 1131.43 317.67 1130.88 316.16 1129.73 315.01 1128.11 313.61 1127.99 312.63 1127.22 312.13 1126.28 312.04 1125.47 311.99 1124.79 311.92 1124.28 312.26 1123.79 312.87 1123.29 313.26 1122.61 313.73 1122.08 314.22 1121.81 314.39 1121.99 314.39 1122.88 313.88 1125.58 313.29 1124.83 313.35 1122.54 312.32 1120.97 311.13 1122.01 309.64 1121.14 309.07 1119.72 308.50 1118.97 307.07 1117.68 306.38 1117.16 305.97 1115.63 305.74 1114.31 305.81 1113.34 305.40 1112.33 305.09 1111.21 305.74 1109.55 305.51 1108.38 305.34 1107.06 304.80 1105.72 304.83 1105.26 304.19 1105.37 302.82 1108.01 302.14 1110.50 302.55 1115.06 303.23 1122.22 303.26 1129.63 303.03 1137.11 302.49 1142.98 301.77 1142.43 301.69 1138.90 300.53 1136.64 298.97 1135.66 298.22 1132.99 297.12 1129.69 295.52 1128.08 294.26 1127.26 294.43 1124.80 294.41 1121.20 293.90 1117.18 294.43 1113.10 294.52 1110.08 295.08 1109.61 296.08 1111.66 296.76 1114.03 297.39 1114.03 298.86 1111.98 300.24 1110.50 300.79 1110.45 301.03 1111.31 301.48 1110.86 301.45 1109.00 301.39 1107.74 300.14 1107.63 299.33 1107.64 298.28 1106.42 296.16 1104.10 293.53 1101.52 291.21 1100.47 288.43 1101.34 285.16 1101.74 282.20 1102.57 279.63 1103.34 277.81 1104.74 276.75 1106.72 275.54 1109.25 275.15 1112.35 275.31 1116.37 275.46 1121.37 275.46 1125.98 275.29 1124.10 273.63 1121.93 272.47 1125.39 270.17 1127.03 269.14 1123.55 269.21 1119.85 268.41 1119.78 266.86 1118.58 266.54 1116.93 265.36 1121.24 262.98 1124.86 262.36 1125.25 261.71 1126.69 262.09 1125.80 262.16 1120.95 263.24 1116.00 263.39 1111.73 263.02 1108.39 263.02 1104.80 262.45 1102.54 261.58 1100.99 261.20 1099.84 261.28 1097.67 260.78 1094.63 261.56 1093.73 262.47 1094.76 263.86 1097.14 265.37 1099.00 266.57 1099.00 266.58 1098.00 266.58 1097.94 266.59 1099.47 265.78 1102.96 264.83 1105.01 263.78 1105.01 262.78 1103.80 262.45 1101.70 261.51 1098.35 260.91 1096.50 260.28 1095.50 260.28 1093.55 259.78 1088.53 259.39 1083.93 259.53 1080.49 259.92 1076.53 259.50 1072.44 258.90 1069.10 258.47 1066.17 257.52 1064.27 257.43 1063.08 257.12 1062.53 256.37 1063.11 255.89 1064.11 255.89 1064.94 257.04 1065.00 257.98 1065.00 257.98 1065.19 258.10 1065.96 258.44 1064.75 258.27 1062.81 257.39 1061.39 256.78 1061.24 256.24 1061.24 256.24 1060.67 255.45 1059.09 254.78 1057.77 254.32 1057.60 253.62 1058.91 252.94 1060.65 252.81 1062.66 252.18 1064.66 250.66 1066.18 250.18 1067.11 248.31 1066.37 246.35 1066.28 244.64 1065.18 242.30 1064.74 239.67 1063.67 237.28 1062.38 233.79 1061.32 231.30 1060.28 229.51 1059.28 227.85 1057.97 227.90 1055.98 225.91 1055.18 224.39 1053.90 220.98 1051.85 216.38 1049.00 211.50 1044.31 207.78 1040.31 205.78 1038.33 203.12 1036.11 201.50 1032.11 200.50 1028.39 199.23 1023.50 197.89 1017.69 198.56 1012.10 200.17 1008.47 199.45 1004.45 199.02 997.65 201.38 994.97 202.94 990.32 205.29 986.59 208.92 982.57 211.66 979.55 211.08 975.92 206.29 973.27 200.52 971.48 196.69 969.93 194.98 968.36 191.79 966.71 189.15 965.92 187.95 964.37 184.30 963.45 179.19 962.51 175.62 961.02 170.06 959.44 167.40 957.85 164.71 956.08 162.36 954.28 160.25 949.74 158.65 947.88 157.46 945.12 155.06 942.12 156.07 939.20 158.58 935.43 157.79 931.67 154.94 926.10 151.19 920.59 148.42 916.82 145.61 +318.19 303.95 317.37 301.96 317.77 298.87 317.77 295.92 318.27 293.40 319.37 291.92 319.86 292.40 319.88 291.91 320.78 290.45 322.36 289.15 323.05 288.63 322.99 288.61 322.36 289.04 321.86 290.19 322.31 290.11 322.19 289.72 322.13 290.27 322.53 290.14 323.05 289.27 323.31 290.33 322.88 292.17 322.40 293.19 321.80 294.66 320.68 295.99 319.48 296.91 318.01 297.29 316.04 298.22 313.51 298.38 311.44 299.10 309.91 299.28 307.70 299.61 305.72 299.59 303.26 298.58 301.92 297.53 299.82 296.46 298.37 294.84 296.55 293.50 294.10 292.57 293.14 292.09 292.00 292.20 290.28 292.08 288.86 292.19 287.32 292.76 286.08 293.14 284.95 293.40 283.46 293.53 282.10 293.66 280.55 293.24 279.66 292.86 279.19 292.85 279.35 292.61 281.35 292.21 279.84 293.61 276.84 293.61 274.44 294.06 274.92 294.12 273.22 295.49 271.29 296.40 269.68 296.41 267.67 297.10 266.48 297.64 264.39 297.74 262.26 298.74 260.33 298.90 258.79 298.96 257.21 300.03 254.82 300.93 252.85 301.39 251.01 301.17 249.31 302.44 248.32 303.44 248.24 303.36 250.40 304.17 252.51 305.90 256.97 306.87 263.62 307.55 270.32 308.86 276.80 310.88 281.84 311.33 281.04 312.72 277.44 313.75 274.99 315.18 274.24 317.21 271.67 320.13 268.69 322.93 267.32 325.53 266.90 328.30 264.57 331.27 261.00 334.21 257.00 337.20 252.40 339.26 249.40 341.75 248.93 343.29 250.88 344.05 252.91 344.09 252.85 345.51 250.38 345.98 248.97 345.09 248.59 344.65 249.57 345.08 249.02 345.11 246.98 344.40 245.41 343.35 244.91 343.63 244.42 344.64 242.98 345.23 240.05 346.75 237.08 349.29 235.53 351.27 236.07 352.73 236.57 354.37 237.73 355.23 238.79 356.10 240.60 356.00 243.00 355.35 246.13 354.91 250.05 354.41 255.00 353.52 260.56 352.39 265.80 351.90 265.08 351.59 264.09 352.07 269.09 350.86 272.14 350.29 270.39 349.58 268.70 348.76 270.61 348.91 271.89 350.51 272.91 351.60 279.72 352.77 285.59 355.02 288.28 356.07 291.77 357.58 293.17 358.41 290.30 360.08 287.48 361.13 285.36 362.13 283.98 364.04 282.35 365.86 282.04 367.98 282.56 369.94 283.14 371.54 282.70 372.90 281.16 375.39 281.29 376.88 283.42 378.29 286.26 379.45 288.26 380.89 288.80 382.27 287.77 383.81 287.77 385.81 289.41 387.95 292.41 389.95 294.78 391.86 294.79 394.16 293.79 396.16 291.75 397.16 288.77 398.95 286.90 400.41 286.26 401.64 284.17 402.17 279.47 403.37 275.00 404.85 271.69 405.50 268.17 405.59 264.28 406.26 261.34 407.19 259.08 407.57 257.61 408.13 256.63 409.11 256.16 409.41 256.97 409.94 258.18 410.47 258.87 411.40 258.97 412.13 259.50 413.12 259.84 413.93 260.48 414.30 259.77 414.71 258.36 415.86 257.73 416.48 258.28 416.95 258.93 416.94 258.85 416.78 257.86 416.75 257.31 416.67 257.94 416.22 260.32 416.04 262.73 415.14 265.73 414.14 268.63 414.61 270.73 415.10 272.67 413.21 273.91 412.30 274.75 411.90 275.35 410.92 276.46 410.36 277.03 410.47 277.78 410.70 278.50 411.60 279.39 412.29 280.35 412.88 281.18 416.44 281.42 418.06 282.02 417.69 282.19 416.05 281.82 413.04 281.22 410.02 278.07 407.64 275.71 406.25 274.81 404.80 274.21 404.30 272.29 404.34 270.26 405.33 267.26 405.90 263.26 407.88 259.40 410.95 258.47 413.42 256.10 415.93 251.47 420.47 250.80 424.51 248.57 429.35 246.50 435.21 244.36 440.08 243.31 441.66 242.00 440.51 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +888.03 187.52 886.91 186.47 886.94 184.09 886.70 181.69 886.81 179.51 887.91 178.23 888.10 179.26 887.63 179.20 888.30 178.16 889.48 177.18 889.98 176.64 889.57 176.72 888.72 176.97 888.21 178.11 888.21 178.11 888.14 178.14 887.97 179.07 888.40 179.04 888.96 178.99 889.23 180.32 888.89 182.08 888.51 183.09 888.06 184.39 887.08 185.91 885.85 186.56 884.36 187.08 882.52 188.10 880.09 188.53 878.19 189.40 876.84 189.96 874.44 190.41 872.60 190.62 870.10 190.21 868.79 189.05 866.69 187.08 865.14 185.08 863.15 183.65 860.62 182.12 859.46 181.14 857.71 180.68 855.78 180.56 854.29 180.23 852.31 180.25 850.78 180.31 849.43 180.52 847.54 180.70 845.64 180.77 844.02 180.81 842.46 180.50 841.33 180.02 840.64 179.35 841.65 178.42 839.37 178.87 835.37 177.94 831.94 176.93 831.19 175.51 828.64 175.44 825.54 175.23 823.09 174.19 819.84 174.11 817.38 173.55 814.16 173.15 811.16 173.37 808.25 173.00 805.69 172.39 802.87 172.86 799.34 172.88 796.47 172.92 793.60 171.99 790.32 172.14 787.89 172.11 786.29 170.77 786.75 170.29 787.59 170.91 790.59 171.91 795.74 171.96 801.24 171.86 806.40 171.81 810.24 170.99 807.79 171.11 802.75 170.82 798.68 170.40 795.73 170.81 791.31 171.31 785.91 171.59 782.25 171.79 779.51 172.88 775.11 173.94 769.58 174.98 764.19 176.49 758.19 177.26 753.72 178.31 751.71 179.49 752.31 179.99 753.32 179.98 751.99 181.08 749.97 181.43 746.71 182.01 745.58 181.71 745.59 181.83 744.11 181.58 741.13 181.16 738.93 179.54 737.08 178.89 735.45 178.46 732.46 177.57 729.41 176.30 723.53 176.11 720.60 175.29 719.32 173.89 718.19 172.78 717.85 171.31 717.62 170.66 720.86 170.39 720.16 168.82 722.55 168.35 725.52 168.28 730.10 167.89 736.70 167.33 741.57 167.04 741.13 165.85 737.90 164.45 740.71 163.63 744.62 163.02 741.81 163.49 737.98 163.33 739.01 162.93 738.48 163.94 738.50 164.55 742.91 164.28 746.68 164.26 748.48 166.57 750.50 168.11 750.57 169.09 746.57 171.09 742.57 172.09 740.59 172.62 735.68 174.13 732.68 175.13 730.34 176.02 729.21 177.11 728.11 178.67 725.77 179.49 722.38 181.51 721.58 183.05 722.69 184.69 723.91 186.47 724.63 187.08 724.07 187.85 721.34 189.23 719.71 190.15 719.62 190.69 720.90 190.90 721.88 190.97 720.13 191.56 716.22 192.36 712.01 192.90 706.75 193.11 702.78 193.11 699.71 192.33 696.89 192.28 689.86 192.32 683.21 193.08 677.99 193.35 672.25 193.01 666.39 192.54 661.31 192.36 656.91 191.80 653.51 191.79 650.53 191.90 648.14 191.89 647.19 191.92 646.83 191.95 646.39 192.46 644.99 193.48 643.93 193.74 642.97 194.13 642.27 194.09 639.92 194.08 636.86 194.10 634.26 193.94 633.27 193.90 632.44 193.72 630.62 193.09 628.18 192.62 626.08 192.19 625.05 191.69 625.43 191.18 626.67 190.26 628.09 189.56 629.18 188.11 630.42 187.02 630.91 185.02 630.11 183.44 629.13 182.44 627.70 180.78 626.70 179.31 625.09 178.42 623.31 176.83 621.75 176.30 620.32 175.89 619.28 175.95 617.53 178.29 615.54 178.35 614.73 177.21 612.67 175.14 610.31 171.23 607.90 166.98 602.89 164.18 598.97 162.34 596.42 160.30 594.07 159.25 590.34 158.98 586.19 159.12 581.19 159.12 575.22 160.47 569.64 163.47 566.58 164.41 561.63 165.90 555.14 169.56 552.15 173.08 548.15 177.08 544.08 182.55 539.78 186.43 536.85 186.96 533.69 183.37 531.25 177.99 529.18 174.41 527.50 172.75 525.55 170.23 524.16 168.07 524.02 166.01 522.15 163.33 520.49 158.64 519.49 154.76 518.48 150.73 517.26 148.45 515.39 146.65 513.34 144.81 511.65 143.58 507.62 143.77 505.54 143.65 503.17 143.15 501.25 145.29 498.35 148.71 495.80 149.71 492.76 148.26 488.15 145.95 483.18 144.87 480.32 144.61 +433.75 206.25 432.83 204.51 433.17 201.69 433.23 198.79 433.52 196.21 434.66 195.00 434.84 195.56 434.84 195.22 435.52 193.77 437.05 192.74 437.80 192.11 437.36 192.22 436.36 192.69 436.00 193.99 436.29 193.86 436.02 193.57 435.87 194.32 436.20 194.21 436.65 193.51 436.92 194.82 436.22 196.63 435.26 197.65 434.59 199.07 433.39 200.50 431.94 201.40 430.19 201.85 428.05 202.60 425.43 203.00 423.02 203.59 421.32 203.91 418.83 204.28 416.66 204.28 413.98 203.31 412.09 202.21 409.57 200.72 407.91 199.22 405.53 197.73 403.06 196.82 401.45 196.21 399.88 196.26 397.98 196.14 395.89 196.26 393.60 196.85 392.36 197.31 390.87 197.78 388.80 197.80 386.84 197.93 384.85 197.66 383.30 197.42 382.49 197.47 381.77 197.10 383.10 196.94 380.78 197.96 377.19 197.95 373.85 198.22 373.37 198.24 370.91 199.39 368.11 200.11 365.67 200.09 363.18 201.06 360.85 201.28 358.20 201.63 355.08 202.35 352.55 202.54 350.10 202.54 347.81 203.53 344.73 204.50 341.98 204.76 339.44 204.75 336.97 205.84 335.23 206.70 334.17 206.61 335.32 207.33 336.65 208.91 340.47 210.37 346.33 210.99 352.27 212.39 357.66 213.99 362.18 214.62 360.33 215.73 355.83 216.78 352.57 217.97 350.73 219.96 347.17 222.54 343.29 225.31 340.82 227.84 339.45 230.56 336.05 233.45 331.43 235.99 326.43 239.00 321.36 241.23 317.47 243.38 316.20 245.37 317.93 246.15 319.36 246.33 318.46 247.58 315.73 248.17 314.05 247.55 313.44 247.16 313.69 247.56 312.69 247.56 310.33 246.91 308.47 245.55 307.58 246.05 306.25 246.67 303.90 247.24 300.36 248.61 296.25 250.75 293.96 252.30 293.50 253.48 293.43 254.90 294.02 255.63 294.57 256.12 295.79 256.01 298.16 255.19 301.45 255.15 305.32 254.61 310.18 254.06 315.85 252.90 320.65 252.45 320.07 252.02 318.89 252.62 323.49 251.48 326.48 251.08 325.10 250.74 323.53 250.26 325.31 250.42 326.42 252.21 326.87 253.46 333.65 254.86 339.19 257.07 341.62 258.58 345.03 260.20 346.27 261.38 343.26 263.39 340.17 264.50 337.72 265.76 335.98 267.86 334.09 269.76 333.38 271.91 333.51 273.82 333.99 275.85 332.69 277.38 330.71 279.89 330.40 281.76 332.06 283.29 334.32 284.77 336.24 286.48 336.25 287.91 334.72 289.47 333.81 291.30 334.42 293.46 336.83 295.49 338.19 297.14 337.27 299.46 335.64 301.48 332.99 302.54 328.93 304.08 326.50 305.57 324.77 306.61 322.02 307.03 316.40 308.11 311.35 309.65 307.39 310.23 302.83 310.26 298.36 310.77 294.76 311.34 291.71 311.65 289.51 312.35 287.76 313.10 286.83 313.52 287.14 314.07 287.66 314.59 287.92 315.39 287.55 316.18 287.54 317.06 287.51 317.81 287.68 318.25 286.53 318.77 284.60 319.78 283.43 320.39 283.83 320.88 283.82 320.90 283.24 320.84 281.70 320.84 280.90 320.72 281.36 320.62 282.96 320.11 285.23 319.48 287.66 318.53 290.33 316.96 292.47 315.69 294.20 313.83 294.82 313.01 295.28 312.56 295.33 311.48 295.86 311.01 295.64 311.23 295.66 311.23 295.89 312.17 296.46 312.90 296.56 313.71 296.81 317.18 296.04 318.50 296.50 318.26 296.09 316.69 295.42 313.68 294.34 310.48 290.91 308.22 288.23 306.83 287.11 305.60 285.81 304.80 283.47 304.90 281.06 305.93 277.42 306.56 273.44 308.54 269.18 311.78 267.52 314.03 264.49 316.47 259.27 320.79 257.52 325.04 254.84 329.85 251.98 335.42 248.98 340.42 247.29 342.12 244.93 339.54 242.93 334.54 241.93 331.54 241.21 330.40 239.21 327.40 239.21 326.40 239.10 324.45 238.20 321.53 237.38 318.43 237.42 314.60 236.43 311.59 235.43 309.59 234.43 308.59 234.62 308.46 233.62 307.46 230.97 308.72 229.97 309.72 228.23 309.93 227.26 312.56 224.54 316.45 222.68 318.41 221.39 318.41 217.51 317.22 213.58 317.24 211.92 318.29 +654.98 492.15 653.67 490.42 653.49 487.74 653.30 484.84 653.36 482.32 653.99 480.89 654.16 481.47 653.74 481.19 654.16 479.85 655.52 478.64 655.98 478.00 655.78 478.11 654.94 478.40 654.37 479.51 654.48 479.37 654.22 479.26 653.98 480.19 653.98 480.19 654.23 479.86 654.32 481.20 653.93 483.30 653.65 484.76 652.87 486.14 651.57 487.93 650.41 489.00 648.85 489.71 646.74 490.76 644.11 491.43 641.88 492.48 640.12 493.22 637.82 493.94 635.63 494.24 632.94 493.80 631.35 492.76 629.35 491.45 627.79 489.87 625.87 488.61 623.41 487.64 622.55 487.12 621.02 487.16 619.19 487.23 617.67 487.47 615.90 487.85 614.44 488.21 613.19 488.83 611.45 489.27 609.37 489.59 607.32 489.74 605.78 489.64 604.80 489.63 604.66 489.24 606.17 488.69 604.39 489.76 600.88 489.48 598.40 489.42 598.52 488.75 596.84 489.55 594.39 490.02 592.78 489.84 590.47 490.37 588.82 490.59 586.11 490.69 583.42 491.72 581.17 491.92 578.97 491.87 576.78 492.71 574.01 493.68 571.55 494.17 569.26 493.98 566.91 494.71 565.41 495.35 564.82 494.79 566.64 495.15 568.04 496.57 571.60 497.98 577.38 498.48 583.67 499.15 589.95 500.42 594.71 500.53 593.40 501.33 589.29 501.97 586.87 502.49 585.58 503.71 582.94 505.54 580.25 507.18 578.65 508.35 577.85 510.22 575.39 512.34 571.54 514.34 567.37 516.65 562.74 518.49 559.50 520.13 558.36 521.61 559.74 522.56 560.77 522.71 560.16 523.85 557.11 525.04 554.57 524.78 553.62 524.67 553.73 524.98 552.68 525.09 549.76 524.85 547.67 523.45 547.12 523.66 546.63 524.29 544.97 524.55 542.58 525.13 540.18 526.34 539.13 527.25 540.22 527.37 540.97 527.54 542.52 527.40 543.36 527.34 544.83 526.92 546.94 525.77 549.58 525.29 552.90 524.72 557.21 523.88 562.26 522.83 567.01 521.74 566.29 520.53 565.46 520.01 570.29 518.03 573.31 516.72 570.92 516.28 568.95 514.96 570.94 514.02 571.83 514.36 573.01 514.35 580.00 513.71 585.84 514.35 588.50 514.47 591.59 515.01 592.60 515.09 589.50 516.43 586.36 516.59 583.97 516.59 582.50 517.50 580.60 518.19 580.42 518.88 580.64 519.58 580.92 520.77 580.31 521.19 578.21 523.11 578.08 524.40 579.45 525.61 581.50 527.34 582.97 528.69 582.99 529.79 581.96 531.00 581.56 532.55 583.01 534.35 585.99 536.00 587.89 536.93 587.81 538.67 586.49 540.56 583.95 541.34 580.37 542.93 577.99 544.08 576.18 545.51 573.34 546.15 567.72 547.33 562.61 548.82 558.13 549.93 553.48 550.33 548.85 550.88 544.92 551.72 541.90 552.04 539.52 552.67 537.62 553.95 536.16 554.47 536.19 555.20 536.49 556.10 536.32 557.20 535.68 558.62 535.53 559.46 535.30 560.51 535.05 561.08 533.67 561.42 531.77 562.29 530.47 562.61 530.45 562.87 530.23 563.08 529.23 563.05 527.32 562.96 526.20 562.88 526.05 562.54 527.20 561.99 528.75 561.32 530.51 560.78 531.94 559.19 533.45 558.42 534.24 556.50 534.67 555.06 534.74 554.40 534.31 552.90 535.00 551.88 535.21 551.32 535.63 550.16 536.06 550.10 536.38 550.05 536.84 550.23 537.59 552.70 537.46 553.22 537.30 552.49 536.69 550.33 535.82 546.64 534.39 542.80 530.68 540.08 527.60 538.52 526.31 536.51 524.72 535.62 522.28 535.51 519.60 535.77 516.00 535.81 511.41 537.42 507.00 540.48 505.56 541.74 502.76 543.48 497.82 547.55 496.48 551.33 493.92 555.47 491.45 560.89 488.96 565.17 487.56 565.90 485.74 562.76 484.38 557.10 482.69 553.91 481.52 552.31 480.25 549.77 478.92 547.84 478.09 546.27 477.49 543.21 476.41 539.00 476.09 535.24 475.92 531.34 475.16 529.36 474.20 527.85 473.25 526.31 472.24 525.53 469.40 525.95 468.68 525.87 467.41 525.56 466.43 527.77 464.18 531.23 462.76 532.30 461.14 531.07 457.82 528.72 454.73 527.68 453.43 527.47 +656.00 592.80 654.52 590.94 654.07 588.06 653.73 585.26 653.72 582.72 654.42 581.11 654.54 581.73 653.90 581.38 654.28 579.81 655.51 578.49 655.92 577.88 655.69 577.80 654.88 578.06 654.26 579.29 654.31 579.01 653.98 578.89 653.66 579.77 653.64 579.75 653.63 579.52 653.58 581.02 653.16 582.98 652.63 584.59 651.66 586.34 650.39 587.99 649.07 589.33 647.23 590.18 645.10 591.23 642.40 591.92 640.00 593.02 638.00 594.02 635.59 594.61 633.23 594.97 630.27 594.58 628.66 593.87 626.68 592.49 624.99 591.03 622.83 590.09 620.40 589.08 619.42 588.40 617.96 588.55 615.88 588.68 613.98 589.12 612.27 589.58 610.78 590.01 609.16 590.65 607.13 591.05 604.94 591.64 602.66 592.01 600.67 591.95 599.74 591.87 599.16 591.33 600.53 591.03 598.56 592.05 595.32 591.87 592.82 591.87 592.89 591.20 591.20 592.02 588.77 592.64 586.91 592.63 584.60 593.12 582.68 593.44 580.01 593.50 577.24 594.59 574.80 594.95 572.32 594.96 569.88 595.88 567.11 596.87 564.47 597.42 561.89 597.23 559.44 598.15 557.96 598.97 557.12 598.46 558.56 598.97 560.20 599.96 563.27 601.52 568.95 601.76 575.15 602.59 581.37 603.95 586.02 604.29 584.53 605.40 580.51 605.95 578.15 606.34 577.05 607.72 574.59 609.62 572.03 611.18 570.96 612.61 569.90 614.55 567.55 616.82 564.18 618.70 560.12 621.08 555.63 623.04 552.27 624.84 551.20 626.46 551.99 627.36 553.22 627.60 552.07 628.81 548.99 629.90 546.15 629.84 544.83 629.80 544.81 630.31 543.58 630.32 540.51 630.24 538.29 628.83 537.75 629.07 536.98 629.90 535.76 630.32 533.58 630.88 531.45 632.49 530.88 633.24 532.08 633.53 533.45 634.07 535.13 633.84 536.10 633.87 537.63 633.29 539.52 632.33 542.24 631.63 545.10 630.82 549.26 630.01 554.02 628.64 558.69 627.81 558.28 626.19 558.00 625.65 562.74 623.43 565.74 621.94 563.42 621.09 561.53 619.66 563.84 618.32 565.15 618.54 566.75 617.92 574.11 616.83 580.39 617.27 583.26 617.28 586.71 617.55 587.70 617.44 584.89 618.51 581.93 618.34 579.80 618.12 578.40 619.08 577.09 619.39 577.15 619.89 577.86 620.36 578.18 621.42 577.76 621.51 575.81 623.30 575.71 624.60 576.80 625.86 578.75 627.33 580.30 628.82 580.35 630.00 579.10 631.26 578.75 632.98 580.43 634.85 583.32 636.50 585.36 637.79 585.43 639.51 584.02 641.48 581.49 642.78 578.05 644.20 575.57 645.67 573.57 647.21 570.34 648.02 564.80 649.30 559.54 650.86 554.67 652.34 549.62 652.93 544.91 653.74 540.90 654.70 537.70 654.92 535.17 655.86 533.03 657.07 531.43 658.03 531.35 658.86 531.40 659.76 530.95 660.94 529.76 662.48 529.44 663.69 529.20 664.65 528.75 665.27 527.20 666.07 525.40 666.90 524.08 667.15 523.86 667.50 523.38 667.84 522.37 667.73 520.37 667.74 518.79 667.78 518.43 667.54 519.65 666.89 520.64 666.37 522.28 665.55 523.35 664.09 524.56 663.07 525.15 661.40 525.48 659.90 525.46 659.25 525.11 657.63 525.68 656.47 526.03 655.87 526.75 654.62 527.37 654.73 527.93 654.67 528.61 654.55 529.40 657.28 529.44 657.81 529.44 656.97 529.15 654.48 528.42 650.55 527.19 646.62 523.33 643.95 520.33 642.56 518.74 640.70 517.40 639.25 514.73 639.23 512.11 639.52 509.03 639.28 504.32 640.65 500.00 644.01 498.59 645.99 496.05 647.17 491.30 651.09 490.15 655.16 487.56 659.38 485.13 664.71 482.83 669.14 481.82 669.68 480.04 666.21 478.48 661.05 476.82 657.78 475.54 656.16 474.26 653.62 472.96 651.46 471.72 650.33 471.14 646.62 470.10 642.72 469.75 638.91 469.65 635.15 469.01 632.96 468.36 631.54 467.35 630.17 466.74 629.36 464.11 629.56 463.47 629.63 462.59 629.46 461.57 630.99 459.45 634.44 458.39 635.92 457.39 633.92 454.09 631.65 451.38 630.89 450.40 630.28 +807.45 580.79 806.12 579.45 805.49 576.74 805.05 573.93 804.95 571.51 805.81 570.17 805.93 571.05 805.20 570.87 805.65 569.58 806.56 568.52 807.06 567.86 806.73 567.93 806.26 568.14 805.76 569.19 805.79 569.17 805.52 569.02 805.15 570.08 805.19 570.45 805.43 570.26 805.56 571.84 805.25 574.05 805.04 575.65 804.34 577.58 803.42 579.23 802.13 580.70 800.72 581.49 798.86 582.73 796.26 583.57 794.29 584.94 792.66 585.89 790.44 586.84 788.40 587.46 785.69 587.36 784.23 586.79 782.33 585.35 781.16 583.82 779.31 582.80 777.10 581.85 776.15 581.53 775.06 581.57 773.12 581.99 771.56 582.31 770.33 582.77 768.78 583.49 767.46 584.15 765.96 584.82 764.18 585.48 762.06 586.06 760.48 586.17 759.69 586.65 759.62 586.36 761.07 586.28 759.63 587.27 756.36 586.98 754.19 586.88 754.59 586.20 753.11 586.92 750.93 587.47 749.43 586.95 747.47 587.67 746.11 587.81 743.52 588.22 741.01 589.44 739.03 589.82 736.93 589.85 734.76 591.04 732.13 591.96 729.73 592.67 727.47 592.55 725.35 593.53 724.21 594.14 723.62 593.70 725.36 594.15 726.79 595.69 730.60 597.06 736.15 597.96 742.57 598.82 749.10 600.13 754.06 600.35 752.94 601.19 748.87 601.70 746.65 601.86 745.92 602.88 743.64 604.05 741.11 604.98 739.86 605.79 739.27 607.48 737.08 609.18 733.37 610.70 729.58 612.76 725.28 614.38 722.03 615.96 720.91 617.92 722.04 618.83 723.12 619.44 722.12 620.94 719.19 622.34 716.43 622.57 715.31 622.64 715.36 623.27 714.22 623.58 711.22 623.58 709.03 622.69 708.63 622.93 708.34 623.40 707.02 623.32 705.15 623.38 703.25 624.24 702.85 624.54 704.33 623.85 705.83 623.46 707.47 622.80 708.60 622.48 709.90 622.13 711.86 621.05 714.38 620.60 717.36 620.11 721.25 619.50 725.89 618.57 730.53 617.76 729.58 615.83 728.74 614.79 733.32 612.33 735.90 611.12 733.28 609.99 730.91 608.61 732.88 606.84 733.56 606.46 734.54 605.45 741.52 603.63 747.05 603.71 749.56 603.22 752.56 603.35 753.13 602.89 749.78 603.52 746.41 603.28 743.85 602.80 742.02 603.10 740.02 603.07 739.83 602.93 739.85 602.95 740.03 603.56 739.51 603.52 737.43 604.89 736.96 606.24 737.82 607.71 739.83 609.38 741.45 611.04 741.51 611.94 740.07 613.25 739.94 614.70 741.94 616.14 745.06 617.75 747.23 618.68 747.43 620.23 746.24 621.84 743.79 622.76 740.27 624.12 737.86 625.50 736.33 627.32 733.21 628.15 727.86 629.27 722.57 631.20 717.89 632.64 713.09 633.53 708.58 634.08 704.48 635.11 701.34 635.69 698.77 636.71 696.85 638.15 695.40 639.05 695.38 640.06 695.46 641.10 695.32 642.74 694.28 644.68 694.06 645.72 693.52 647.10 693.18 647.84 691.81 648.62 689.93 649.44 688.57 649.48 688.44 649.80 687.84 650.48 686.53 650.50 684.41 650.67 683.01 650.87 682.52 650.75 683.58 650.58 684.36 650.18 686.08 649.68 686.73 648.91 687.93 648.27 688.55 646.43 688.58 645.01 688.47 644.30 687.62 642.72 688.14 641.32 688.26 640.51 688.59 638.62 688.84 638.23 689.23 637.82 689.66 637.72 690.48 639.55 690.10 639.59 689.74 638.53 688.98 635.95 688.18 631.62 686.49 627.37 682.40 624.52 679.28 622.82 677.50 620.79 675.48 620.07 672.94 619.71 669.92 619.58 666.41 619.18 661.61 620.16 656.98 623.41 655.37 624.58 652.68 625.26 647.50 629.13 646.37 632.64 643.56 636.31 640.82 641.65 638.36 645.62 637.15 646.16 635.15 642.16 633.12 636.56 631.11 633.44 629.71 631.95 628.52 629.15 626.64 627.56 625.66 625.99 624.65 622.47 623.83 618.16 623.14 614.26 622.86 610.05 621.86 608.05 621.22 606.15 619.90 604.32 619.45 602.75 616.49 602.85 615.52 602.42 614.69 601.48 613.24 603.41 610.98 606.57 609.84 607.00 608.54 604.35 604.93 601.41 601.91 599.85 600.61 598.54 +914.61 501.71 913.24 500.38 912.64 498.11 912.44 495.42 912.41 493.23 913.41 491.94 913.52 492.96 913.04 492.91 913.40 491.65 914.50 490.74 914.94 490.13 914.98 490.26 914.15 490.48 913.70 491.62 913.86 491.63 913.66 491.69 913.71 492.78 914.11 493.23 914.57 493.28 914.79 494.76 914.79 496.76 914.78 498.22 914.65 499.97 913.77 501.82 912.72 502.90 911.68 503.87 910.35 505.17 908.09 506.05 906.25 507.36 905.10 508.51 903.26 509.28 901.62 510.04 899.27 509.92 898.23 509.26 896.64 507.67 895.74 506.44 894.38 505.31 892.44 504.43 892.06 503.80 890.95 503.74 889.61 504.07 888.50 504.41 887.32 504.67 886.33 505.26 885.33 506.18 884.28 506.75 882.80 507.34 881.75 508.06 880.49 508.50 880.26 508.72 880.45 508.51 882.88 508.23 881.53 509.13 878.93 508.80 877.15 508.33 878.02 507.44 876.83 507.86 874.98 508.22 873.87 507.60 872.48 508.03 871.17 508.21 869.17 508.50 867.38 509.29 865.77 509.48 864.24 509.62 862.74 510.92 860.65 511.50 858.79 512.15 857.12 512.09 855.20 512.90 854.51 513.34 854.57 512.76 856.49 512.98 858.66 514.19 862.75 515.84 868.98 516.57 876.03 517.44 882.99 518.15 888.46 518.23 887.56 518.99 883.84 519.05 881.82 518.79 881.24 519.52 879.19 520.13 876.62 520.30 875.60 520.40 875.06 521.87 873.06 522.87 869.80 524.13 865.76 525.73 861.86 526.84 858.97 528.24 858.28 529.78 859.66 530.90 861.33 531.52 860.76 533.25 858.01 534.60 856.04 535.15 854.94 535.48 855.54 535.94 854.67 536.23 852.22 536.40 850.29 535.55 849.93 535.56 849.94 535.62 849.12 535.18 847.42 534.40 845.53 534.24 845.18 533.60 846.51 532.41 847.71 531.33 849.39 530.10 850.40 529.35 852.11 528.92 854.11 527.92 856.57 527.50 859.50 527.32 863.55 527.10 868.37 526.57 872.76 525.95 871.76 524.11 870.32 522.73 874.61 520.39 876.97 519.04 873.88 518.47 871.11 517.20 872.28 515.42 872.52 514.97 872.41 513.72 878.63 511.86 883.68 511.56 885.60 511.06 888.03 511.14 888.02 510.86 884.17 511.41 880.22 511.28 877.04 510.66 875.03 510.67 872.40 510.37 871.69 510.00 871.39 509.93 871.14 510.39 869.87 510.20 867.61 511.34 867.17 512.58 868.17 513.85 870.46 515.71 872.33 517.39 872.37 518.03 871.40 518.89 871.61 520.19 873.61 521.15 877.17 522.12 879.41 522.46 879.71 523.36 878.90 524.59 876.81 525.16 873.77 526.20 871.76 527.12 870.57 528.65 867.98 529.32 863.09 530.14 858.31 531.57 854.46 533.17 849.95 533.75 845.59 534.28 842.37 534.98 839.37 535.43 837.38 536.05 835.80 537.40 834.81 537.94 835.46 538.82 836.46 539.82 836.40 541.67 835.91 543.31 836.09 544.32 836.09 545.32 836.07 546.32 834.77 546.66 833.03 547.05 831.71 547.20 831.73 547.48 831.67 547.97 830.72 548.11 829.06 548.12 827.72 548.28 827.53 548.19 828.54 548.14 829.93 547.85 831.63 547.72 832.97 546.83 834.55 546.53 834.83 544.91 835.09 543.48 834.89 542.54 834.22 540.93 834.60 539.42 834.59 538.06 834.52 535.88 834.86 534.78 834.91 533.82 835.19 533.43 835.57 534.87 834.77 534.30 834.50 533.36 833.19 530.71 831.58 526.48 830.03 522.10 826.03 519.10 822.93 517.25 821.11 514.96 819.22 513.85 816.34 513.50 813.19 513.20 809.36 512.59 803.76 513.82 799.53 516.26 798.14 516.74 794.45 517.65 789.09 520.66 787.68 523.76 784.48 527.25 781.75 532.04 779.47 535.50 777.91 535.59 774.74 531.52 772.59 525.98 771.88 522.43 770.31 520.95 768.61 518.15 767.44 516.44 766.39 515.14 765.17 511.73 763.87 507.26 763.02 503.35 762.81 498.86 762.04 496.51 761.03 494.50 759.46 492.29 758.71 490.78 755.58 490.50 754.34 489.30 753.14 488.20 751.38 489.74 749.22 492.55 747.30 492.54 744.70 489.88 740.75 486.70 737.23 484.27 735.50 482.52 +771.03 553.78 769.60 552.14 769.15 549.42 768.82 546.47 768.77 543.96 769.51 542.69 769.63 543.33 769.06 543.33 769.46 541.64 770.60 540.73 771.10 540.16 770.83 540.36 770.07 540.30 769.62 541.37 769.64 541.34 769.37 541.46 769.13 542.40 769.15 542.57 769.38 542.47 769.47 543.83 769.22 546.06 768.96 547.80 768.27 549.61 767.23 551.32 766.02 552.48 764.47 553.35 762.54 554.37 759.98 555.51 757.84 556.61 756.21 557.39 753.91 558.39 751.86 558.84 749.24 558.56 747.78 557.83 745.93 556.48 744.54 555.02 742.60 553.98 740.37 552.97 739.54 552.56 738.17 552.69 736.37 552.87 734.87 553.32 733.38 553.66 732.00 554.63 730.60 555.03 729.01 555.48 727.11 556.17 725.21 556.62 723.62 556.72 722.76 556.98 722.47 556.62 724.09 556.65 722.45 557.38 719.26 557.00 717.00 556.96 717.26 556.50 715.68 557.01 713.53 557.53 711.91 557.41 709.85 557.78 708.21 558.28 705.65 558.48 703.18 559.37 701.11 559.70 698.99 559.65 696.80 560.65 694.23 561.49 691.76 562.35 689.40 562.21 687.21 563.18 685.90 563.73 685.34 563.32 687.09 563.71 688.60 565.09 692.17 566.39 697.87 567.28 704.27 567.77 710.80 569.22 715.56 569.30 714.24 570.40 710.29 570.76 708.06 570.90 707.02 572.20 704.58 573.31 702.01 574.39 700.72 575.37 699.79 577.20 697.46 579.08 694.01 580.72 690.03 582.78 685.60 584.40 682.27 586.12 681.24 587.77 682.29 588.44 683.44 589.04 682.54 590.51 679.44 591.93 676.86 591.96 675.63 592.25 675.80 592.65 674.70 593.16 671.79 592.88 669.69 591.86 669.02 592.15 668.53 592.62 667.38 592.71 665.25 592.70 663.25 593.70 662.63 593.75 663.93 593.58 665.10 593.29 666.60 592.66 667.74 592.53 669.11 591.94 671.08 590.96 673.65 590.37 676.58 589.71 680.63 589.19 685.42 588.12 690.03 587.47 689.18 585.57 688.46 584.54 693.04 582.14 695.80 581.06 693.24 579.92 690.85 578.82 692.69 577.27 693.70 576.92 694.58 576.39 701.44 574.70 707.19 574.97 709.66 574.61 712.58 575.12 713.28 574.64 710.10 575.31 706.55 575.36 703.98 574.95 702.47 575.41 700.56 575.43 700.19 575.53 700.40 576.06 700.58 576.88 699.60 576.99 697.60 578.42 697.26 579.75 698.46 581.26 700.48 582.78 701.91 584.37 701.91 585.52 700.77 586.55 700.51 588.35 702.13 589.58 705.30 591.11 707.35 592.23 707.41 593.68 706.00 595.36 703.52 596.28 700.14 597.46 697.82 599.04 696.02 600.61 692.99 601.58 687.57 602.44 682.35 604.03 677.78 605.54 672.86 606.16 668.22 606.73 664.29 607.64 661.05 608.32 658.57 609.36 656.58 610.47 655.18 611.10 655.14 612.27 655.34 612.94 655.10 614.65 654.21 616.25 654.02 617.19 653.58 618.45 653.21 619.29 651.86 619.67 650.04 620.48 648.48 620.78 648.32 621.06 647.97 621.47 646.84 621.29 644.81 621.34 643.37 621.38 642.98 621.36 643.95 620.92 645.14 620.55 646.63 619.92 647.84 618.71 648.80 618.22 649.50 616.35 649.64 614.85 649.58 613.95 648.85 612.51 649.26 611.34 649.41 610.22 649.75 609.14 650.29 608.27 650.55 607.79 650.83 607.98 651.46 609.86 651.19 610.11 650.86 609.63 650.42 606.86 649.27 602.52 647.81 598.29 643.81 595.47 640.55 593.80 638.81 592.11 637.15 591.04 634.41 590.34 631.54 590.51 628.00 590.30 623.02 591.55 618.53 594.54 617.08 596.09 614.19 596.74 609.07 600.52 607.80 604.33 605.06 608.20 602.41 613.25 599.88 617.75 598.50 618.37 596.58 614.31 594.80 608.83 592.94 605.66 591.49 604.12 590.24 601.34 588.71 599.90 587.64 598.20 586.76 594.81 585.80 590.43 585.31 586.54 584.99 582.43 584.12 580.78 583.35 578.67 582.21 576.75 581.36 575.63 578.52 575.87 577.65 575.33 576.61 574.23 575.37 576.53 573.05 579.89 571.82 580.35 570.31 578.02 566.84 575.38 563.75 573.70 562.36 573.02 +-1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 396.90 459.98 397.89 458.31 398.39 458.76 398.23 458.28 398.93 456.43 400.53 455.18 401.30 454.54 401.21 454.40 400.67 454.88 400.58 455.98 400.85 455.73 400.79 455.42 400.76 456.02 401.07 455.92 401.73 455.14 402.00 456.14 401.76 458.08 401.68 459.24 400.99 460.66 400.08 462.20 398.96 463.26 397.89 463.76 395.98 464.66 393.76 465.19 391.94 465.95 390.57 466.43 388.68 466.80 387.00 466.80 384.74 465.94 383.58 465.34 382.04 464.16 380.84 462.55 379.28 461.17 377.25 460.24 376.80 459.72 375.89 459.87 374.43 459.75 373.41 459.85 372.59 460.30 371.68 460.67 371.05 460.90 369.83 461.27 368.63 461.34 367.63 461.13 366.88 460.62 366.90 460.63 367.55 460.36 370.18 460.01 369.12 460.95 366.91 460.85 365.26 461.19 366.54 461.09 365.57 462.17 364.12 462.75 363.40 463.02 361.98 463.53 361.38 464.03 359.85 464.29 358.34 465.24 356.98 465.29 355.88 465.41 354.91 466.35 353.22 467.20 351.66 467.61 350.42 467.44 349.16 468.60 348.97 469.12 349.57 469.14 352.29 469.65 355.19 471.37 359.69 472.35 366.78 472.45 374.26 473.79 381.71 475.16 387.56 475.55 387.19 476.71 384.14 477.67 382.72 478.71 382.56 480.62 381.03 483.09 379.18 485.45 378.71 487.45 378.70 489.57 377.28 492.38 374.69 494.90 371.49 497.48 367.72 499.57 365.41 501.55 365.37 503.13 367.69 503.66 369.90 503.65 370.01 504.66 368.08 505.44 366.42 504.79 366.41 504.27 367.30 504.70 366.90 504.70 364.92 504.18 363.91 502.76 363.83 503.28 363.85 504.20 363.15 504.85 361.21 506.16 359.21 508.15 358.93 509.70 360.43 510.75 362.02 512.09 364.02 512.56 365.24 513.07 367.03 513.04 369.66 512.16 372.96 511.80 376.49 511.24 381.05 510.32 386.41 509.05 391.47 508.20 390.79 507.39 390.28 507.46 394.93 505.84 397.93 504.88 395.52 504.08 393.67 503.12 395.67 502.65 397.07 503.42 398.06 503.96 405.16 503.97 411.18 505.31 413.95 505.59 417.41 506.49 418.59 506.66 415.71 508.00 412.87 508.36 410.76 508.86 409.37 510.23 408.37 511.23 408.39 512.57 409.23 513.69 410.14 515.10 409.83 515.68 408.35 517.67 408.94 519.13 410.91 520.11 413.82 521.37 416.11 522.52 416.88 523.58 416.59 524.99 416.94 526.54 419.52 528.35 423.31 530.35 426.32 531.85 427.32 533.81 426.83 535.78 425.41 536.63 423.23 538.38 422.19 539.60 421.69 541.10 420.22 541.71 416.09 543.12 412.25 544.94 409.42 545.59 405.96 546.16 402.86 546.74 400.27 547.73 398.64 548.19 397.49 548.88 396.62 550.03 396.64 550.55 397.82 551.28 399.16 552.07 400.04 553.19 400.27 554.23 401.15 555.26 401.69 556.18 402.36 556.84 402.18 557.12 401.38 558.14 400.82 558.44 401.74 558.78 402.53 559.16 402.45 558.93 401.49 558.74 401.29 558.61 402.16 558.29 404.28 557.71 406.69 556.76 409.66 556.24 412.10 554.80 414.45 553.83 416.27 551.88 417.61 550.81 418.63 550.37 419.27 549.16 420.60 548.61 421.58 548.25 423.06 547.72 424.08 548.27 425.15 548.78 426.66 549.10 427.67 552.24 428.45 553.18 428.93 552.71 429.35 551.12 429.34 547.55 428.91 544.23 425.74 541.83 423.75 540.33 422.78 538.90 422.05 538.14 419.97 538.29 417.98 538.76 414.88 539.13 410.33 541.10 406.67 544.26 405.16 546.18 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 +304.33 468.99 303.55 466.90 303.70 463.65 303.92 460.62 304.31 457.90 305.47 456.37 306.04 456.78 305.85 456.10 306.81 454.58 308.36 452.98 309.25 452.32 309.13 452.30 308.58 452.65 308.29 453.76 308.70 453.53 308.70 453.04 308.65 453.64 308.92 453.27 309.39 452.43 309.53 453.44 309.26 455.34 308.89 456.68 308.22 457.94 307.23 459.42 306.16 460.28 304.66 460.70 302.95 461.46 300.50 461.80 298.51 462.56 297.06 462.81 294.95 463.14 293.12 463.00 290.66 462.15 289.45 461.18 287.71 460.26 286.59 458.61 284.95 457.29 282.82 456.31 282.27 455.81 281.25 455.83 279.71 455.49 278.45 455.61 277.46 456.21 276.42 456.53 275.45 456.82 274.31 456.92 273.05 456.90 271.71 456.55 270.95 456.00 270.86 455.78 271.42 455.41 273.82 454.94 272.75 455.98 270.24 456.03 268.75 456.50 269.74 456.44 268.84 457.75 267.36 458.51 266.42 458.53 265.05 459.30 264.35 459.73 262.71 459.79 260.87 460.76 259.58 460.85 258.25 460.83 257.31 461.68 255.57 462.46 253.84 462.85 252.59 462.72 251.32 463.85 250.89 464.66 251.42 464.38 254.11 465.23 256.70 466.63 261.58 467.54 268.48 467.67 275.96 469.08 283.00 470.67 288.95 471.13 288.52 472.20 285.44 473.36 283.91 474.56 283.65 476.52 282.01 479.23 280.20 481.84 279.81 484.36 279.91 486.87 278.44 489.71 275.73 492.42 272.40 495.23 268.64 497.56 266.23 499.73 266.18 501.44 268.31 501.95 270.68 501.88 270.62 502.92 268.63 503.57 267.10 502.58 266.84 502.22 267.77 502.29 267.59 502.29 265.42 501.73 264.13 500.50 264.13 500.92 264.18 501.86 263.35 502.53 261.60 504.15 259.54 506.68 258.94 508.61 260.40 510.09 261.82 511.64 263.68 512.51 265.21 513.20 267.11 513.15 269.54 512.34 272.80 511.80 276.52 511.11 281.25 510.32 286.70 508.83 291.72 508.16 291.21 507.43 290.68 507.88 295.74 506.35 298.81 505.43 296.86 504.54 295.14 503.48 297.35 503.20 298.87 504.35 300.07 504.91 307.57 505.49 313.74 507.12 316.75 507.90 320.31 508.80 321.73 509.31 319.24 510.76 316.30 511.27 314.50 511.90 313.52 513.58 312.29 514.78 312.66 516.61 313.53 518.04 314.61 519.24 314.43 520.45 313.13 522.41 313.77 523.80 315.77 524.80 318.75 525.92 321.00 527.14 321.90 528.39 321.46 529.82 321.87 531.62 324.19 533.69 327.99 535.58 330.93 537.16 331.85 539.36 331.39 541.45 330.04 542.52 327.70 544.23 326.49 545.81 326.04 546.98 324.40 547.58 320.28 549.05 316.37 550.67 313.21 551.54 309.81 551.73 306.58 552.41 304.04 553.33 302.17 553.86 301.09 554.58 300.22 555.56 300.13 556.02 301.20 556.83 302.63 557.31 303.36 558.17 303.56 559.22 304.19 560.03 304.79 560.92 305.49 561.39 305.18 561.99 304.25 563.07 303.89 563.55 304.76 563.98 305.59 564.06 305.58 563.87 304.58 563.87 304.33 563.68 305.09 563.38 307.36 562.89 309.67 562.06 312.48 561.08 315.11 559.52 317.42 558.33 319.34 556.45 320.63 555.58 321.62 555.13 322.22 554.03 323.64 553.50 324.64 553.50 326.07 553.48 327.30 554.29 328.40 554.78 329.68 555.29 331.10 558.60 331.92 559.81 332.63 559.46 333.16 557.80 333.15 554.65 332.73 551.49 329.85 549.26 327.77 547.79 327.07 546.25 326.56 545.56 324.85 545.76 323.19 546.51 320.53 546.96 316.56 548.81 313.27 551.94 312.75 554.00 310.73 556.57 306.59 560.91 306.32 565.00 304.32 569.52 302.60 575.19 300.81 580.17 300.36 581.46 299.26 578.85 298.48 573.94 297.55 570.79 296.77 569.31 296.25 567.14 295.32 565.21 294.97 563.68 294.97 560.68 294.36 557.22 294.40 553.71 294.50 550.38 294.14 548.72 293.72 547.78 293.09 546.89 292.53 546.75 290.08 547.81 289.64 548.44 288.88 548.96 287.92 551.59 286.10 555.52 285.26 557.55 284.10 557.36 281.06 556.17 278.48 556.20 277.35 557.13 +353.36 58.97 353.12 57.14 353.64 54.61 354.21 51.62 354.80 49.19 356.12 47.63 357.15 48.19 357.49 47.85 358.55 46.69 360.18 45.60 361.27 44.97 361.24 45.06 360.66 45.68 360.22 47.10 360.79 47.02 361.19 46.60 361.73 47.18 362.40 46.88 363.38 46.23 364.53 47.11 364.05 49.23 363.58 50.75 363.62 51.70 363.14 52.88 362.54 53.74 361.74 54.16 360.28 55.32 358.28 55.86 357.25 56.29 356.71 56.34 354.82 57.01 353.58 57.00 351.96 56.00 351.14 54.95 349.12 54.02 348.05 52.65 346.91 51.31 345.06 50.23 344.21 50.05 343.52 50.09 342.03 50.22 341.31 50.24 340.29 50.61 339.50 51.25 339.18 51.19 338.28 51.93 337.69 52.23 336.80 52.60 337.31 51.68 337.61 51.94 338.09 51.73 340.70 51.79 339.79 52.96 337.34 53.13 335.17 54.03 335.83 53.90 334.40 55.36 333.23 55.94 331.82 56.41 330.61 57.05 329.76 57.84 328.47 58.35 327.10 59.24 325.90 59.97 325.41 59.38 324.43 61.01 323.11 61.84 321.18 62.72 320.85 62.47 319.38 63.73 318.89 64.67 318.89 65.67 321.08 66.59 324.81 67.51 329.52 69.29 337.00 69.36 343.00 72.36 349.01 74.36 355.66 74.31 355.09 75.68 351.10 76.67 348.31 78.67 348.53 79.76 344.81 83.36 341.79 85.88 340.79 87.88 338.85 91.75 337.03 93.70 333.03 96.70 329.27 98.56 324.58 101.24 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 -1.00 \ No newline at end of file diff --git a/modules/sfm/samples/data/images/dataset_files.txt b/modules/sfm/samples/data/images/dataset_files.txt new file mode 100644 index 00000000000..1caa208d8e6 --- /dev/null +++ b/modules/sfm/samples/data/images/dataset_files.txt @@ -0,0 +1,4 @@ +resized_IMG_2889.jpg +resized_IMG_2890.jpg +resized_IMG_2891.jpg +resized_IMG_2892.jpg \ No newline at end of file diff --git a/modules/sfm/samples/data/images/resized_IMG_2889.jpg b/modules/sfm/samples/data/images/resized_IMG_2889.jpg new file mode 100644 index 00000000000..673971def93 Binary files /dev/null and b/modules/sfm/samples/data/images/resized_IMG_2889.jpg differ diff --git a/modules/sfm/samples/data/images/resized_IMG_2890.jpg b/modules/sfm/samples/data/images/resized_IMG_2890.jpg new file mode 100644 index 00000000000..227f29dac7e Binary files /dev/null and b/modules/sfm/samples/data/images/resized_IMG_2890.jpg differ diff --git a/modules/sfm/samples/data/images/resized_IMG_2891.jpg b/modules/sfm/samples/data/images/resized_IMG_2891.jpg new file mode 100644 index 00000000000..330af2f5e5d Binary files /dev/null and b/modules/sfm/samples/data/images/resized_IMG_2891.jpg differ diff --git a/modules/sfm/samples/data/images/resized_IMG_2892.jpg b/modules/sfm/samples/data/images/resized_IMG_2892.jpg new file mode 100644 index 00000000000..45e5787ef49 Binary files /dev/null and b/modules/sfm/samples/data/images/resized_IMG_2892.jpg differ diff --git a/modules/sfm/samples/data/recon2v_checkerboards.txt b/modules/sfm/samples/data/recon2v_checkerboards.txt new file mode 100644 index 00000000000..2184fc21f2a --- /dev/null +++ b/modules/sfm/samples/data/recon2v_checkerboards.txt @@ -0,0 +1,309 @@ + 1.0200000e+02 + 1.3565461e+02 -2.1840103e+01 1.9819526e+02 -1.0170771e+01 + 1.3632370e+02 1.2187567e+01 1.9869165e+02 1.9780392e+01 + 1.3659126e+02 4.6222740e+01 1.9946641e+02 4.9773407e+01 + 1.3618322e+02 8.0487054e+01 1.9890466e+02 7.9833292e+01 + 1.3618923e+02 1.1488794e+02 1.9924137e+02 1.1009794e+02 + 1.3557352e+02 1.4888312e+02 1.9854904e+02 1.4019595e+02 + 1.0460796e+02 -2.2508578e+01 1.7365956e+02 -1.0556587e+01 + 1.0465313e+02 1.1736607e+01 1.7395584e+02 1.9691431e+01 + 1.0478526e+02 4.6460713e+01 1.7458636e+02 5.0252516e+01 + 1.0457158e+02 8.0953990e+01 1.7417263e+02 8.0783049e+01 + 1.0459174e+02 1.1535848e+02 1.7444097e+02 1.1115772e+02 + 1.0437634e+02 1.5019535e+02 1.7410457e+02 1.4160445e+02 + 7.2535504e+01 -2.3201340e+01 1.4849881e+02 -1.1138970e+01 + 7.2558710e+01 1.1594239e+01 1.4863036e+02 1.9607124e+01 + 7.2628399e+01 4.6604103e+01 1.4918117e+02 5.0626676e+01 + 7.2525945e+01 8.1233985e+01 1.4858668e+02 8.1245004e+01 + 7.2287421e+01 1.1627549e+02 1.4859441e+02 1.1210216e+02 + 7.1969831e+01 1.5120192e+02 1.4853599e+02 1.4321231e+02 + 3.9773632e+01 -2.3602728e+01 1.2246578e+02 -1.1551178e+01 + 4.0144464e+01 1.1556425e+01 1.2268584e+02 1.9586701e+01 + 4.0447929e+01 4.6670400e+01 1.2334768e+02 5.0749467e+01 + 3.9626964e+01 8.2028161e+01 1.2265015e+02 8.2021137e+01 + 3.9514609e+01 1.1716629e+02 1.2252761e+02 1.1321018e+02 + 3.9423381e+01 1.5227031e+02 1.2248098e+02 1.4436653e+02 + 6.8546879e+00 -2.4418765e+01 9.6014444e+01 -1.1984500e+01 + 7.2574490e+00 1.1396253e+01 9.6609162e+01 1.9608204e+01 + 7.3284311e+00 4.6698371e+01 9.6687887e+01 5.0849626e+01 + 6.5433913e+00 8.2169734e+01 9.6408732e+01 8.2744839e+01 + 6.4800159e+00 1.1766813e+02 9.5872017e+01 1.1424657e+02 + 6.4791015e+00 1.5343124e+02 9.6082919e+01 1.4602665e+02 + -2.6558934e+01 -2.4640903e+01 6.8995111e+01 -1.2555627e+01 + -2.6499960e+01 1.0829206e+01 6.9518077e+01 1.9526829e+01 + -2.6586633e+01 4.6747549e+01 6.9532848e+01 5.1400625e+01 + -2.7122409e+01 8.2779745e+01 6.9228731e+01 8.3212654e+01 + -2.7385173e+01 1.1841828e+02 6.9078117e+01 1.1529126e+02 + -2.7461512e+01 1.5443854e+02 6.9167034e+01 1.4739623e+02 + -6.0759788e+01 -2.5584288e+01 4.1583311e+01 -1.2965988e+01 + -6.1347509e+01 1.0630597e+01 4.1541133e+01 1.9556284e+01 + -6.1562412e+01 4.6764284e+01 4.1226148e+01 5.1769646e+01 + -6.1554706e+01 8.3050630e+01 4.1329061e+01 8.4151474e+01 + -6.1746894e+01 1.1932705e+02 4.1119768e+01 1.1640925e+02 + -6.1871649e+01 1.5566108e+02 4.1440242e+01 1.4913669e+02 + -9.6614546e+01 -2.5864918e+01 1.2549031e+01 -1.3519330e+01 + -9.6654949e+01 1.0570803e+01 1.2551417e+01 1.9505076e+01 + -9.7135252e+01 4.6926792e+01 1.2380775e+01 5.1955423e+01 + -9.7376300e+01 8.3781084e+01 1.2426714e+01 8.5042035e+01 + -9.7294524e+01 1.2029188e+02 1.2560996e+01 1.1785493e+02 + -9.7539254e+01 1.5707802e+02 1.2462437e+01 1.5042962e+02 + -9.8505492e+01 -1.6361335e+02 -1.3630076e+02 -1.5228957e+02 + -9.8546116e+01 -1.4949510e+02 -1.3618931e+02 -1.3877090e+02 + -9.8728409e+01 -1.3541719e+02 -1.3653833e+02 -1.2526451e+02 + -9.8794674e+01 -1.2135899e+02 -1.3654775e+02 -1.1162840e+02 + -9.9016295e+01 -1.0711728e+02 -1.3661742e+02 -9.8041445e+01 + -9.9178368e+01 -9.3059874e+01 -1.3679150e+02 -8.4335285e+01 + -1.1251654e+02 -1.6450504e+02 -1.4910087e+02 -1.5326354e+02 + -1.1245267e+02 -1.5047783e+02 -1.4882490e+02 -1.3973736e+02 + -1.1251829e+02 -1.3630654e+02 -1.4923631e+02 -1.2608675e+02 + -1.1264220e+02 -1.2222459e+02 -1.4937956e+02 -1.1255550e+02 + -1.1270267e+02 -1.0790924e+02 -1.4948916e+02 -9.8785792e+01 + -1.1283560e+02 -9.3795606e+01 -1.4971112e+02 -8.4963177e+01 + -1.2631101e+02 -1.6543112e+02 -1.6174075e+02 -1.5431046e+02 + -1.2623416e+02 -1.5139519e+02 -1.6170397e+02 -1.4096352e+02 + -1.2640168e+02 -1.3728845e+02 -1.6184356e+02 -1.2698410e+02 + -1.2652703e+02 -1.2306434e+02 -1.6210896e+02 -1.1336062e+02 + -1.2665368e+02 -1.0868052e+02 -1.6230640e+02 -9.9509215e+01 + -1.2672292e+02 -9.4349594e+01 -1.6252543e+02 -8.5845064e+01 + -1.4013932e+02 -1.6653733e+02 -1.7471300e+02 -1.5540299e+02 + -1.4026713e+02 -1.5236959e+02 -1.7471518e+02 -1.4189690e+02 + -1.4028912e+02 -1.3838655e+02 -1.7485014e+02 -1.2822890e+02 + -1.4060449e+02 -1.2374255e+02 -1.7509545e+02 -1.1422046e+02 + -1.4058667e+02 -1.0930583e+02 -1.7522061e+02 -1.0032104e+02 + -1.4070093e+02 -9.4997511e+01 -1.7561372e+02 -8.6661780e+01 + -1.5398443e+02 -1.6740996e+02 -1.8768269e+02 -1.5659464e+02 + -1.5406324e+02 -1.5332849e+02 -1.8776973e+02 -1.4302072e+02 + -1.5442798e+02 -1.3897887e+02 -1.8815877e+02 -1.2909606e+02 + -1.5446011e+02 -1.2462435e+02 -1.8818723e+02 -1.1512541e+02 + -1.5457604e+02 -1.1006693e+02 -1.8844448e+02 -1.0115724e+02 + -1.5472241e+02 -9.5985074e+01 -1.8855533e+02 -8.7470802e+01 + -1.6827308e+02 -1.6839068e+02 -2.0131646e+02 -1.5770454e+02 + -1.6830325e+02 -1.5419519e+02 -2.0125869e+02 -1.4408292e+02 + -1.6849131e+02 -1.3975338e+02 -2.0144343e+02 -1.3007771e+02 + -1.6856150e+02 -1.2521708e+02 -2.0148004e+02 -1.1610880e+02 + -1.6862721e+02 -1.1091631e+02 -2.0163825e+02 -1.0205420e+02 + -1.6877598e+02 -9.6558432e+01 -2.0170395e+02 -8.8164557e+01 + -1.8252897e+02 -1.6936038e+02 -2.1470075e+02 -1.5885911e+02 + -1.8248901e+02 -1.5508723e+02 -2.1456502e+02 -1.4512083e+02 + -1.8259150e+02 -1.4061469e+02 -2.1471396e+02 -1.3108421e+02 + -1.8263321e+02 -1.2623806e+02 -2.1476969e+02 -1.1711428e+02 + -1.8306648e+02 -1.1171650e+02 -2.1512165e+02 -1.0292363e+02 + -1.8320429e+02 -9.7225508e+01 -2.1541221e+02 -8.8875520e+01 + -1.9705983e+02 -1.7033925e+02 -2.2851382e+02 -1.6013800e+02 + -1.9724930e+02 -1.5580169e+02 -2.2852055e+02 -1.4617309e+02 + -1.9720787e+02 -1.4143932e+02 -2.2848484e+02 -1.3215955e+02 + -1.9742033e+02 -1.2687768e+02 -2.2856255e+02 -1.1809495e+02 + -1.9745757e+02 -1.1245588e+02 -2.2864574e+02 -1.0384620e+02 + -1.9766865e+02 -9.7816323e+01 -2.2928057e+02 -8.9824935e+01 + -2.1161336e+02 -1.7120575e+02 -2.4245331e+02 -1.6121824e+02 + -2.1162545e+02 -1.5682027e+02 -2.4242890e+02 -1.4728707e+02 + -2.1180027e+02 -1.4215400e+02 -2.4248723e+02 -1.3317135e+02 + -2.1181616e+02 -1.2770807e+02 -2.4251854e+02 -1.1902442e+02 + -2.1230706e+02 -1.1306155e+02 -2.4260610e+02 -1.0476462e+02 + -2.1230218e+02 -9.8630005e+01 -2.4305346e+02 -9.0630093e+01 + 1.0200000e+02 + 1.0200000e+02 + 1.3565461e+02 -2.1840103e+01 + 1.3632370e+02 1.2187567e+01 + 1.3659126e+02 4.6222740e+01 + 1.3618322e+02 8.0487054e+01 + 1.3618923e+02 1.1488794e+02 + 1.3557352e+02 1.4888312e+02 + 1.0460796e+02 -2.2508578e+01 + 1.0465313e+02 1.1736607e+01 + 1.0478526e+02 4.6460713e+01 + 1.0457158e+02 8.0953990e+01 + 1.0459174e+02 1.1535848e+02 + 1.0437634e+02 1.5019535e+02 + 7.2535504e+01 -2.3201340e+01 + 7.2558710e+01 1.1594239e+01 + 7.2628399e+01 4.6604103e+01 + 7.2525945e+01 8.1233985e+01 + 7.2287421e+01 1.1627549e+02 + 7.1969831e+01 1.5120192e+02 + 3.9773632e+01 -2.3602728e+01 + 4.0144464e+01 1.1556425e+01 + 4.0447929e+01 4.6670400e+01 + 3.9626964e+01 8.2028161e+01 + 3.9514609e+01 1.1716629e+02 + 3.9423381e+01 1.5227031e+02 + 6.8546879e+00 -2.4418765e+01 + 7.2574490e+00 1.1396253e+01 + 7.3284311e+00 4.6698371e+01 + 6.5433913e+00 8.2169734e+01 + 6.4800159e+00 1.1766813e+02 + 6.4791015e+00 1.5343124e+02 + -2.6558934e+01 -2.4640903e+01 + -2.6499960e+01 1.0829206e+01 + -2.6586633e+01 4.6747549e+01 + -2.7122409e+01 8.2779745e+01 + -2.7385173e+01 1.1841828e+02 + -2.7461512e+01 1.5443854e+02 + -6.0759788e+01 -2.5584288e+01 + -6.1347509e+01 1.0630597e+01 + -6.1562412e+01 4.6764284e+01 + -6.1554706e+01 8.3050630e+01 + -6.1746894e+01 1.1932705e+02 + -6.1871649e+01 1.5566108e+02 + -9.6614546e+01 -2.5864918e+01 + -9.6654949e+01 1.0570803e+01 + -9.7135252e+01 4.6926792e+01 + -9.7376300e+01 8.3781084e+01 + -9.7294524e+01 1.2029188e+02 + -9.7539254e+01 1.5707802e+02 + -9.8505492e+01 -1.6361335e+02 + -9.8546116e+01 -1.4949510e+02 + -9.8728409e+01 -1.3541719e+02 + -9.8794674e+01 -1.2135899e+02 + -9.9016295e+01 -1.0711728e+02 + -9.9178368e+01 -9.3059874e+01 + -1.1251654e+02 -1.6450504e+02 + -1.1245267e+02 -1.5047783e+02 + -1.1251829e+02 -1.3630654e+02 + -1.1264220e+02 -1.2222459e+02 + -1.1270267e+02 -1.0790924e+02 + -1.1283560e+02 -9.3795606e+01 + -1.2631101e+02 -1.6543112e+02 + -1.2623416e+02 -1.5139519e+02 + -1.2640168e+02 -1.3728845e+02 + -1.2652703e+02 -1.2306434e+02 + -1.2665368e+02 -1.0868052e+02 + -1.2672292e+02 -9.4349594e+01 + -1.4013932e+02 -1.6653733e+02 + -1.4026713e+02 -1.5236959e+02 + -1.4028912e+02 -1.3838655e+02 + -1.4060449e+02 -1.2374255e+02 + -1.4058667e+02 -1.0930583e+02 + -1.4070093e+02 -9.4997511e+01 + -1.5398443e+02 -1.6740996e+02 + -1.5406324e+02 -1.5332849e+02 + -1.5442798e+02 -1.3897887e+02 + -1.5446011e+02 -1.2462435e+02 + -1.5457604e+02 -1.1006693e+02 + -1.5472241e+02 -9.5985074e+01 + -1.6827308e+02 -1.6839068e+02 + -1.6830325e+02 -1.5419519e+02 + -1.6849131e+02 -1.3975338e+02 + -1.6856150e+02 -1.2521708e+02 + -1.6862721e+02 -1.1091631e+02 + -1.6877598e+02 -9.6558432e+01 + -1.8252897e+02 -1.6936038e+02 + -1.8248901e+02 -1.5508723e+02 + -1.8259150e+02 -1.4061469e+02 + -1.8263321e+02 -1.2623806e+02 + -1.8306648e+02 -1.1171650e+02 + -1.8320429e+02 -9.7225508e+01 + -1.9705983e+02 -1.7033925e+02 + -1.9724930e+02 -1.5580169e+02 + -1.9720787e+02 -1.4143932e+02 + -1.9742033e+02 -1.2687768e+02 + -1.9745757e+02 -1.1245588e+02 + -1.9766865e+02 -9.7816323e+01 + -2.1161336e+02 -1.7120575e+02 + -2.1162545e+02 -1.5682027e+02 + -2.1180027e+02 -1.4215400e+02 + -2.1181616e+02 -1.2770807e+02 + -2.1230706e+02 -1.1306155e+02 + -2.1230218e+02 -9.8630005e+01 + 1.9819526e+02 -1.0170771e+01 + 1.9869165e+02 1.9780392e+01 + 1.9946641e+02 4.9773407e+01 + 1.9890466e+02 7.9833292e+01 + 1.9924137e+02 1.1009794e+02 + 1.9854904e+02 1.4019595e+02 + 1.7365956e+02 -1.0556587e+01 + 1.7395584e+02 1.9691431e+01 + 1.7458636e+02 5.0252516e+01 + 1.7417263e+02 8.0783049e+01 + 1.7444097e+02 1.1115772e+02 + 1.7410457e+02 1.4160445e+02 + 1.4849881e+02 -1.1138970e+01 + 1.4863036e+02 1.9607124e+01 + 1.4918117e+02 5.0626676e+01 + 1.4858668e+02 8.1245004e+01 + 1.4859441e+02 1.1210216e+02 + 1.4853599e+02 1.4321231e+02 + 1.2246578e+02 -1.1551178e+01 + 1.2268584e+02 1.9586701e+01 + 1.2334768e+02 5.0749467e+01 + 1.2265015e+02 8.2021137e+01 + 1.2252761e+02 1.1321018e+02 + 1.2248098e+02 1.4436653e+02 + 9.6014444e+01 -1.1984500e+01 + 9.6609162e+01 1.9608204e+01 + 9.6687887e+01 5.0849626e+01 + 9.6408732e+01 8.2744839e+01 + 9.5872017e+01 1.1424657e+02 + 9.6082919e+01 1.4602665e+02 + 6.8995111e+01 -1.2555627e+01 + 6.9518077e+01 1.9526829e+01 + 6.9532848e+01 5.1400625e+01 + 6.9228731e+01 8.3212654e+01 + 6.9078117e+01 1.1529126e+02 + 6.9167034e+01 1.4739623e+02 + 4.1583311e+01 -1.2965988e+01 + 4.1541133e+01 1.9556284e+01 + 4.1226148e+01 5.1769646e+01 + 4.1329061e+01 8.4151474e+01 + 4.1119768e+01 1.1640925e+02 + 4.1440242e+01 1.4913669e+02 + 1.2549031e+01 -1.3519330e+01 + 1.2551417e+01 1.9505076e+01 + 1.2380775e+01 5.1955423e+01 + 1.2426714e+01 8.5042035e+01 + 1.2560996e+01 1.1785493e+02 + 1.2462437e+01 1.5042962e+02 + -1.3630076e+02 -1.5228957e+02 + -1.3618931e+02 -1.3877090e+02 + -1.3653833e+02 -1.2526451e+02 + -1.3654775e+02 -1.1162840e+02 + -1.3661742e+02 -9.8041445e+01 + -1.3679150e+02 -8.4335285e+01 + -1.4910087e+02 -1.5326354e+02 + -1.4882490e+02 -1.3973736e+02 + -1.4923631e+02 -1.2608675e+02 + -1.4937956e+02 -1.1255550e+02 + -1.4948916e+02 -9.8785792e+01 + -1.4971112e+02 -8.4963177e+01 + -1.6174075e+02 -1.5431046e+02 + -1.6170397e+02 -1.4096352e+02 + -1.6184356e+02 -1.2698410e+02 + -1.6210896e+02 -1.1336062e+02 + -1.6230640e+02 -9.9509215e+01 + -1.6252543e+02 -8.5845064e+01 + -1.7471300e+02 -1.5540299e+02 + -1.7471518e+02 -1.4189690e+02 + -1.7485014e+02 -1.2822890e+02 + -1.7509545e+02 -1.1422046e+02 + -1.7522061e+02 -1.0032104e+02 + -1.7561372e+02 -8.6661780e+01 + -1.8768269e+02 -1.5659464e+02 + -1.8776973e+02 -1.4302072e+02 + -1.8815877e+02 -1.2909606e+02 + -1.8818723e+02 -1.1512541e+02 + -1.8844448e+02 -1.0115724e+02 + -1.8855533e+02 -8.7470802e+01 + -2.0131646e+02 -1.5770454e+02 + -2.0125869e+02 -1.4408292e+02 + -2.0144343e+02 -1.3007771e+02 + -2.0148004e+02 -1.1610880e+02 + -2.0163825e+02 -1.0205420e+02 + -2.0170395e+02 -8.8164557e+01 + -2.1470075e+02 -1.5885911e+02 + -2.1456502e+02 -1.4512083e+02 + -2.1471396e+02 -1.3108421e+02 + -2.1476969e+02 -1.1711428e+02 + -2.1512165e+02 -1.0292363e+02 + -2.1541221e+02 -8.8875520e+01 + -2.2851382e+02 -1.6013800e+02 + -2.2852055e+02 -1.4617309e+02 + -2.2848484e+02 -1.3215955e+02 + -2.2856255e+02 -1.1809495e+02 + -2.2864574e+02 -1.0384620e+02 + -2.2928057e+02 -8.9824935e+01 + -2.4245331e+02 -1.6121824e+02 + -2.4242890e+02 -1.4728707e+02 + -2.4248723e+02 -1.3317135e+02 + -2.4251854e+02 -1.1902442e+02 + -2.4260610e+02 -1.0476462e+02 + -2.4305346e+02 -9.0630093e+01 \ No newline at end of file diff --git a/modules/sfm/samples/recon2v.cpp b/modules/sfm/samples/recon2v.cpp new file mode 100644 index 00000000000..85d8dc02bec --- /dev/null +++ b/modules/sfm/samples/recon2v.cpp @@ -0,0 +1,126 @@ +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace cv::sfm; + +static void help() { + cout + << "\n------------------------------------------------------------------\n" + << " This program shows the two view reconstruction capabilities in the \n" + << " OpenCV Structure From Motion (SFM) module.\n" + << " It uses the following data from the VGG datasets at ...\n" + << " Usage:\n" + << " reconv2_pts.txt \n " + << " where the first line has the number of points and each subsequent \n" + << " line has entries for matched points as: \n" + << " x1 y1 x2 y2 \n" + << "------------------------------------------------------------------\n\n" + << endl; +} + +int main(int argc, char** argv) +{ + // Do projective reconstruction + bool is_projective = true; + + // Read 2D points from text file + + Mat_ x1, x2; + int npts; + + if (argc < 2) { + help(); + exit(0); + } else { + ifstream myfile(argv[1]); + if (!myfile.is_open()) { + cout << "Unable to read file: " << argv[1] << endl; + exit(0); + + } else { + string line; + + // Read number of points + getline(myfile, line); + npts = (int) atof(line.c_str()); + + x1 = Mat_(2, npts); + x2 = Mat_(2, npts); + + // Read the point coordinates + for (int i = 0; i < npts; ++i) { + getline(myfile, line); + stringstream s(line); + string cord; + + s >> cord; + x1(0, i) = atof(cord.c_str()); + s >> cord; + x1(1, i) = atof(cord.c_str()); + + s >> cord; + x2(0, i) = atof(cord.c_str()); + s >> cord; + x2(1, i) = atof(cord.c_str()); + + } + + myfile.close(); + + } + } + + // Call the reconstruction function + + std::vector < Mat_ > points2d; + points2d.push_back(x1); + points2d.push_back(x2); + Matx33d K_estimated; + Mat_ points3d_estimated; + std::vector < cv::Mat > Ps_estimated; + + reconstruct(points2d, Ps_estimated, points3d_estimated, K_estimated, is_projective); + + + // Print output + + cout << endl; + cout << "Projection Matrix of View 1: " << endl; + cout << "============================ " << endl; + cout << Ps_estimated[0] << endl << endl; + cout << "Projection Matrix of View 2: " << endl; + cout << "============================ " << endl; + cout << Ps_estimated[1] << endl << endl; + + + // Display 3D points using VIZ module + + // Create the pointcloud + std::vector point_cloud; + for (int i = 0; i < npts; ++i) { + cv::Vec3f point3d((float) points3d_estimated(0, i), + (float) points3d_estimated(1, i), + (float) points3d_estimated(2, i)); + point_cloud.push_back(point3d); + } + + // Create a 3D window + viz::Viz3d myWindow("Coordinate Frame"); + + /// Add coordinate axes + myWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem()); + + viz::WCloud cloud_widget(point_cloud, viz::Color::green()); + + myWindow.showWidget("cloud", cloud_widget); + myWindow.spin(); + + return 0; +} \ No newline at end of file diff --git a/modules/sfm/samples/scene_reconstruction.cpp b/modules/sfm/samples/scene_reconstruction.cpp new file mode 100644 index 00000000000..3f956674972 --- /dev/null +++ b/modules/sfm/samples/scene_reconstruction.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace cv::sfm; + +static void help() { + cout + << "\n------------------------------------------------------------------------------------\n" + << " This program shows the multiview reconstruction capabilities in the \n" + << " OpenCV Structure From Motion (SFM) module.\n" + << " It reconstruct a scene from a set of 2D images \n" + << " Usage:\n" + << " example_sfm_scene_reconstruction \n" + << " where: path_to_file is the file absolute path into your system which contains\n" + << " the list of images to use for reconstruction. \n" + << " f is the focal lenght in pixels. \n" + << " cx is the image principal point x coordinates in pixels. \n" + << " cy is the image principal point y coordinates in pixels. \n" + << "------------------------------------------------------------------------------------\n\n" + << endl; +} + + +int getdir(const string _filename, vector &files) +{ + ifstream myfile(_filename.c_str()); + if (!myfile.is_open()) { + cout << "Unable to read file: " << _filename << endl; + exit(0); + } else {; + size_t found = _filename.find_last_of("/\\"); + string line_str, path_to_file = _filename.substr(0, found); + while ( getline(myfile, line_str) ) + files.push_back(path_to_file+string("/")+line_str); + } + return 1; +} + + +int main(int argc, char* argv[]) +{ + // Read input parameters + + if ( argc != 5 ) + { + help(); + exit(0); + } + + // Parse the image paths + + vector images_paths; + getdir( argv[1], images_paths ); + + + // Build instrinsics + + float f = atof(argv[2]), + cx = atof(argv[3]), cy = atof(argv[4]); + + Matx33d K = Matx33d( f, 0, cx, + 0, f, cy, + 0, 0, 1); + + + /// Reconstruct the scene using the 2d images + + bool is_projective = true; + vector Rs_est, ts_est, points3d_estimated; + reconstruct(images_paths, Rs_est, ts_est, K, points3d_estimated, is_projective); + + + // Print output + + cout << "\n----------------------------\n" << endl; + cout << "Reconstruction: " << endl; + cout << "============================" << endl; + cout << "Estimated 3D points: " << points3d_estimated.size() << endl; + cout << "Estimated cameras: " << Rs_est.size() << endl; + cout << "Refined intrinsics: " << endl << K << endl << endl; + cout << "3D Visualization: " << endl; + cout << "============================" << endl; + + + /// Create 3D windows + + viz::Viz3d window("Coordinate Frame"); + window.setWindowSize(Size(500,500)); + window.setWindowPosition(Point(150,150)); + window.setBackgroundColor(); // black by default + + // Create the pointcloud + cout << "Recovering points ... "; + + // recover estimated points3d + vector point_cloud_est; + for (int i = 0; i < points3d_estimated.size(); ++i) + point_cloud_est.push_back(Vec3f(points3d_estimated[i])); + + cout << "[DONE]" << endl; + + + /// Recovering cameras + cout << "Recovering cameras ... "; + + vector path; + for (size_t i = 0; i < Rs_est.size(); ++i) + path.push_back(Affine3d(Rs_est[i],ts_est[i])); + + cout << "[DONE]" << endl; + + + /// Add the pointcloud + if ( point_cloud_est.size() > 0 ) + { + cout << "Rendering points ... "; + + viz::WCloud cloud_widget(point_cloud_est, viz::Color::green()); + window.showWidget("point_cloud", cloud_widget); + + cout << "[DONE]" << endl; + } + else + { + cout << "Cannot render points: Empty pointcloud" << endl; + } + + + /// Add cameras + if ( path.size() > 0 ) + { + cout << "Rendering Cameras ... "; + + window.showWidget("cameras_frames_and_lines", viz::WTrajectory(path, viz::WTrajectory::BOTH, 0.1, viz::Color::green())); + window.showWidget("cameras_frustums", viz::WTrajectoryFrustums(path, K, 0.1, viz::Color::yellow())); + + window.setViewerPose(path[0]); + + cout << "[DONE]" << endl; + } + else + { + cout << "Cannot render the cameras: Empty path" << endl; + } + + /// Wait for key 'q' to close the window + cout << endl << "Press 'q' to close each windows ... " << endl; + + window.spin(); + + return 0; +} \ No newline at end of file diff --git a/modules/sfm/samples/trajectory_reconstruccion.cpp b/modules/sfm/samples/trajectory_reconstruccion.cpp new file mode 100644 index 00000000000..5c3dc709224 --- /dev/null +++ b/modules/sfm/samples/trajectory_reconstruccion.cpp @@ -0,0 +1,246 @@ +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace cv::sfm; + +static void help() { + cout + << "\n------------------------------------------------------------------\n" + << " This program shows the camera trajectory reconstruction capabilities\n" + << " in the OpenCV Structure From Motion (SFM) module.\n" + << " \n" + << " Usage:\n" + << " example_sfm_trajectory_reconstruction \n" + << " where: is the tracks file absolute path into your system. \n" + << " \n" + << " The file must have the following format: \n" + << " row1 : x1 y1 x2 y2 ... x36 y36 for track 1\n" + << " row2 : x1 y1 x2 y2 ... x36 y36 for track 2\n" + << " etc\n" + << " \n" + << " i.e. a row gives the 2D measured position of a point as it is tracked\n" + << " through frames 1 to 36. If there is no match found in a view then x\n" + << " and y are -1.\n" + << " \n" + << " Each row corresponds to a different point.\n" + << " \n" + << " f is the focal lenght in pixels. \n" + << " cx is the image principal point x coordinates in pixels. \n" + << " cy is the image principal point y coordinates in pixels. \n" + << "------------------------------------------------------------------\n\n" + << endl; +} + + +/* Build the following structure data + * + * frame1 frame2 frameN + * track1 | (x11,y11) | -> | (x12,y12) | -> | (x1N,y1N) | + * track2 | (x21,y11) | -> | (x22,y22) | -> | (x2N,y2N) | + * trackN | (xN1,yN1) | -> | (xN2,yN2) | -> | (xNN,yNN) | + * + * + * In case a marker (x,y) does not appear in a frame its + * values will be (-1,-1). + */ + +void +parser_2D_tracks(const string &_filename, std::vector &points2d ) +{ + ifstream myfile(_filename.c_str()); + + if (!myfile.is_open()) + { + cout << "Unable to read file: " << _filename << endl; + exit(0); + + } else { + + double x, y; + string line_str; + int n_frames = 0, n_tracks = 0; + + // extract data from text file + + vector > tracks; + for ( ; getline(myfile,line_str); ++n_tracks) + { + istringstream line(line_str); + + vector track; + for ( n_frames = 0; line >> x >> y; ++n_frames) + { + if ( x > 0 && y > 0) + track.push_back(Vec2d(x,y)); + else + track.push_back(Vec2d(-1)); + } + tracks.push_back(track); + } + + // embed data in reconstruction api format + + for (int i = 0; i < n_frames; ++i) + { + Mat_ frame(2, n_tracks); + + for (int j = 0; j < n_tracks; ++j) + { + frame(0,j) = tracks[j][i][0]; + frame(1,j) = tracks[j][i][1]; + } + points2d.push_back(Mat(frame)); + } + + myfile.close(); + } + +} + + +/* Keyboard callback to control 3D visualization + */ + +bool camera_pov = false; + +void keyboard_callback(const viz::KeyboardEvent &event, void* cookie) +{ + if ( event.action == 0 &&!event.symbol.compare("s") ) + camera_pov = !camera_pov; +} + + +/* Sample main code + */ + +int main(int argc, char** argv) +{ + // Read input parameters + + if ( argc != 5 ) + { + help(); + exit(0); + } + + // Read 2D points from text file + std::vector points2d; + parser_2D_tracks( argv[1], points2d ); + + // Set the camera calibration matrix + const double f = atof(argv[2]), + cx = atof(argv[3]), cy = atof(argv[4]); + + Matx33d K = Matx33d( f, 0, cx, + 0, f, cy, + 0, 0, 1); + + /// Reconstruct the scene using the 2d correspondences + + bool is_projective = true; + vector Rs_est, ts_est, points3d_estimated; + reconstruct(points2d, Rs_est, ts_est, K, points3d_estimated, is_projective); + + // Print output + + cout << "\n----------------------------\n" << endl; + cout << "Reconstruction: " << endl; + cout << "============================" << endl; + cout << "Estimated 3D points: " << points3d_estimated.size() << endl; + cout << "Estimated cameras: " << Rs_est.size() << endl; + cout << "Refined intrinsics: " << endl << K << endl << endl; + + cout << "3D Visualization: " << endl; + cout << "============================" << endl; + + + /// Create 3D windows + viz::Viz3d window_est("Estimation Coordinate Frame"); + window_est.setBackgroundColor(); // black by default + window_est.registerKeyboardCallback(&keyboard_callback); + + // Create the pointcloud + cout << "Recovering points ... "; + + // recover estimated points3d + vector point_cloud_est; + for (int i = 0; i < points3d_estimated.size(); ++i) + point_cloud_est.push_back(Vec3f(points3d_estimated[i])); + + cout << "[DONE]" << endl; + + + /// Recovering cameras + cout << "Recovering cameras ... "; + + vector path_est; + for (size_t i = 0; i < Rs_est.size(); ++i) + path_est.push_back(Affine3d(Rs_est[i],ts_est[i])); + + cout << "[DONE]" << endl; + + /// Add cameras + cout << "Rendering Trajectory ... "; + + /// Wait for key 'q' to close the window + cout << endl << "Press: " << endl; + cout << " 's' to switch the camera pov" << endl; + cout << " 'q' to close the windows " << endl; + + + if ( path_est.size() > 0 ) + { + // animated trajectory + int idx = 0, forw = -1, n = static_cast(path_est.size()); + + while(!window_est.wasStopped()) + { + /// Render points as 3D cubes + for (size_t i = 0; i < point_cloud_est.size(); ++i) + { + Vec3d point = point_cloud_est[i]; + Affine3d point_pose(Mat::eye(3,3,CV_64F), point); + + char buffer[50]; + sprintf (buffer, "%d", static_cast(i)); + + viz::WCube cube_widget(Point3f(0.1,0.1,0.0), Point3f(0.0,0.0,-0.1), true, viz::Color::blue()); + cube_widget.setRenderingProperty(viz::LINE_WIDTH, 2.0); + window_est.showWidget("Cube"+string(buffer), cube_widget, point_pose); + } + + Affine3d cam_pose = path_est[idx]; + + viz::WCameraPosition cpw(0.25); // Coordinate axes + viz::WCameraPosition cpw_frustum(K, 0.3, viz::Color::yellow()); // Camera frustum + + if ( camera_pov ) + window_est.setViewerPose(cam_pose); + else + { + // render complete trajectory + window_est.showWidget("cameras_frames_and_lines_est", viz::WTrajectory(path_est, viz::WTrajectory::PATH, 1.0, viz::Color::green())); + + window_est.showWidget("CPW", cpw, cam_pose); + window_est.showWidget("CPW_FRUSTUM", cpw_frustum, cam_pose); + } + + // update trajectory index (spring effect) + forw *= (idx==n || idx==0) ? -1: 1; idx += forw; + + // frame rate 1s + window_est.spinOnce(1, true); + window_est.removeAllWidgets(); + } + + } + + return 0; +} diff --git a/modules/sfm/src/conditioning.cpp b/modules/sfm/src/conditioning.cpp new file mode 100644 index 00000000000..7c70a8fcb84 --- /dev/null +++ b/modules/sfm/src/conditioning.cpp @@ -0,0 +1,187 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "precomp.hpp" + +// Eigen +#include + +// OpenCV +#include +#include + +// libmv headers +#include "libmv/multiview/conditioning.h" + +namespace cv +{ +namespace sfm +{ + +template +void +preconditionerFromPoints( const Mat_ &_points, + Mat_ _Tr ) +{ + libmv::Mat points; + libmv::Mat3 Tr; + + cv2eigen( _points, points ); + + libmv::PreconditionerFromPoints( points, &Tr ); + + eigen2cv( Tr, _Tr ); +} + + +void +preconditionerFromPoints( InputArray _points, + OutputArray _T ) +{ + const Mat points = _points.getMat(); + const int depth = points.depth(); + CV_Assert((points.dims == 2 || points.dims == 3) && (depth == CV_32F || depth == CV_64F)); + + _T.create(3, 3, depth); + + Mat T = _T.getMat(); + + if ( depth == CV_32F ) + { + preconditionerFromPoints(points, T); + } + else + { + preconditionerFromPoints(points, T); + } +} + +template +void +isotropicPreconditionerFromPoints( const Mat_ &_points, + Mat_ _T ) +{ + libmv::Mat points; + libmv::Mat3 Tr; + + cv2eigen( _points, points ); + + libmv::IsotropicPreconditionerFromPoints( points, &Tr ); + + eigen2cv( Tr, _T ); +} + +void +isotropicPreconditionerFromPoints( InputArray _points, + OutputArray _T ) +{ + const Mat points = _points.getMat(); + const int depth = points.depth(); + CV_Assert((points.dims == 2 || points.dims == 3) && (depth == CV_32F || depth == CV_64F)); + + _T.create(3, 3, depth); + + Mat T = _T.getMat(); + + if ( depth == CV_32F ) + { + isotropicPreconditionerFromPoints(points, T); + } + else + { + isotropicPreconditionerFromPoints(points, T); + } +} + +template +void +applyTransformationToPoints( const Mat_ &_points, + const Mat_ &_T, + Mat_ _transformed_points ) +{ + libmv::Mat points, transformed_points; + libmv::Mat3 Tr; + + cv2eigen( _points, points ); + cv2eigen( _T, Tr ); + + libmv::ApplyTransformationToPoints( points, Tr, &transformed_points ); + + eigen2cv( transformed_points, _transformed_points ); +} + +void +applyTransformationToPoints( InputArray _points, + InputArray _T, + OutputArray _transformed_points ) +{ + const Mat points = _points.getMat(), T = _T.getMat(); + const int depth = points.depth(); + CV_Assert((points.dims == 2 || points.dims == 3) && T.size() == Size(3,3) && (depth == CV_32F || depth == CV_64F)); + + _transformed_points.create(points.size(), depth); + + Mat transformed_points = _transformed_points.getMat(); + + if ( depth == CV_32F ) + { + applyTransformationToPoints(points, T, transformed_points); + } + else + { + applyTransformationToPoints(points, T, transformed_points); + } +} + +void +normalizePoints( InputArray points, + OutputArray normalized_points, + OutputArray T ) +{ + preconditionerFromPoints(points, T); + applyTransformationToPoints(points, T, normalized_points); +} + +void +normalizeIsotropicPoints( InputArray points, + OutputArray normalized_points, + OutputArray T ) +{ + isotropicPreconditionerFromPoints(points, T); + applyTransformationToPoints(points, T, normalized_points); +} + +} /* namespace sfm */ +} /* namespace cv */ diff --git a/modules/sfm/src/fundamental.cpp b/modules/sfm/src/fundamental.cpp new file mode 100644 index 00000000000..ca0523ee088 --- /dev/null +++ b/modules/sfm/src/fundamental.cpp @@ -0,0 +1,595 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "precomp.hpp" + +// Eigen +#include + +// OpenCV +#include +#include +#include +#include +#include +#include + +// libmv headers +#include "libmv/multiview/fundamental.h" + +#include +using namespace std; + +namespace cv +{ +namespace sfm +{ + template + void + projectionsFromFundamental( const Mat_ &F, + Mat_ P1, + Mat_ P2 ) + { + P1 << 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0; + + Vec e2; + cv::SVD::solveZ(F.t(), e2); + + Mat_ P2cols = skew(e2) * F; + for(char j=0;j<3;++j) { + for(char i=0;i<3;++i) + P2(j,i) = P2cols(j,i); + P2(j,3) = e2(j); + } + + } + + void + projectionsFromFundamental( InputArray _F, + OutputArray _P1, + OutputArray _P2 ) + { + const Mat F = _F.getMat(); + const int depth = F.depth(); + CV_Assert(F.cols == 3 && F.rows == 3 && (depth == CV_32F || depth == CV_64F)); + + _P1.create(3, 4, depth); + _P2.create(3, 4, depth); + + Mat P1 = _P1.getMat(), P2 = _P2.getMat(); + + // type + if( depth == CV_32F ) + { + projectionsFromFundamental(F, P1, P2); + } + else + { + projectionsFromFundamental(F, P1, P2); + } + + } + + template + void + fundamentalFromProjections( const Mat_ &P1, + const Mat_ &P2, + Mat_ F ) + { + Mat_ X[3]; + vconcat( P1.row(1), P1.row(2), X[0] ); + vconcat( P1.row(2), P1.row(0), X[1] ); + vconcat( P1.row(0), P1.row(1), X[2] ); + + Mat_ Y[3]; + vconcat( P2.row(1), P2.row(2), Y[0] ); + vconcat( P2.row(2), P2.row(0), Y[1] ); + vconcat( P2.row(0), P2.row(1), Y[2] ); + + Mat_ XY; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + { + vconcat(X[j], Y[i], XY); + F(i, j) = determinant(XY); + } + } + + void + fundamentalFromProjections( InputArray _P1, + InputArray _P2, + OutputArray _F ) + { + const Mat P1 = _P1.getMat(), P2 = _P2.getMat(); + const int depth = P1.depth(); + CV_Assert((P1.cols == 4 && P1.rows == 3) && P1.rows == P2.rows && P1.cols == P2.cols); + CV_Assert((depth == CV_32F || depth == CV_64F) && depth == P2.depth()); + + _F.create(3, 3, depth); + + Mat F = _F.getMat(); + + // type + if( depth == CV_32F ) + { + fundamentalFromProjections(P1, P2, F); + } + else + { + fundamentalFromProjections(P1, P2, F); + } + + } + + template + void + normalizedEightPointSolver( const Mat_ &_x1, + const Mat_ &_x2, + Mat_ _F ) + { + libmv::Mat x1, x2; + libmv::Mat3 F; + + cv2eigen(_x1, x1); + cv2eigen(_x2, x2); + + libmv::NormalizedEightPointSolver(x1, x2, &F); + + eigen2cv(F, _F); + } + + void + normalizedEightPointSolver( InputArray _x1, InputArray _x2, OutputArray _F ) + { + const Mat x1 = _x1.getMat(), x2 = _x2.getMat(); + const int depth = x1.depth(); + CV_Assert(x1.dims == 2 && x1.dims == x2.dims && (depth == CV_32F || depth == CV_64F)); + + _F.create(3, 3, depth); + + Mat F = _F.getMat(); + + // type + if( depth == CV_32F ) + { + normalizedEightPointSolver(x1, x2, F); + } + else + { + normalizedEightPointSolver(x1, x2, F); + } + + } + + template + void + relativeCameraMotion( const Mat_ &R1, + const Mat_ &t1, + const Mat_ &R2, + const Mat_ &t2, + Mat_ R, + Mat_ t ) + { + R = R2 * R1.t(); + t = t2 - R * t1; + } + + void + relativeCameraMotion( InputArray _R1, InputArray _t1, InputArray _R2, + InputArray _t2, OutputArray _R, OutputArray _t ) + { + const Mat R1 = _R1.getMat(), t1 = _t1.getMat(), R2 = _R2.getMat(), t2 = _t2.getMat(); + const int depth = R1.depth(); + CV_Assert((R1.cols == 3 && R1.rows == 3) && (R1.size() == R2.size())); + CV_Assert((t1.cols == 1 && t1.rows == 3) && (t1.size() == t2.size())); + CV_Assert((depth == CV_32F || depth == CV_64F) && depth == R2.depth() && depth == t1.depth() && depth == t2.depth()); + + _R.create(3, 3, depth); + _t.create(3, 1, depth); + + Mat R = _R.getMat(), t = _t.getMat(); + + // type + if( depth == CV_32F ) + { + relativeCameraMotion(R1, t1, R2, t2, R, t); + } + else + { + relativeCameraMotion(R1, t1, R2, t2, R, t); + } + + } + + template + void + motionFromEssential( const Mat_ &_E, + std::vector &_Rs, + std::vector &_ts ) + { + libmv::Mat3 E; + std::vector < libmv::Mat3 > Rs; + std::vector < libmv::Vec3 > ts; + + cv2eigen(_E, E); + + libmv::MotionFromEssential(E, &Rs, &ts); + + _Rs.clear(); + _ts.clear(); + + int n = Rs.size(); + CV_Assert(ts.size() == n); + + for ( int i = 0; i < n; ++i ) + { + Mat_ R_temp, t_temp; + + eigen2cv(Rs[i], R_temp); + _Rs.push_back(R_temp); + + eigen2cv(ts[i], t_temp); + _ts.push_back(t_temp); + } + + } + + void + motionFromEssential( InputArray _E, OutputArrayOfArrays _Rs, + OutputArrayOfArrays _ts ) + { + const Mat E = _E.getMat(); + const int depth = E.depth(), cn = 4; + CV_Assert(E.cols == 3 && E.rows == 3 && (depth == CV_32F || depth == CV_64F)); + + _Rs.create(cn, 1, depth); + _ts.create(cn, 1, depth); + for (int i = 0; i < cn; ++i) + { + _Rs.create(Size(3,3), depth, i); + _ts.create(Size(3,1), depth, i); + } + + std::vector Rs, ts; + _Rs.getMatVector(Rs); + _ts.getMatVector(ts); + + // type + if( depth == CV_32F ) + { + motionFromEssential(E, Rs, ts); + } + else + { + motionFromEssential(E, Rs, ts); + } + + for (int i = 0; i < cn; ++i) + { + Rs[i].copyTo(_Rs.getMatRef(i)); + ts[i].copyTo(_ts.getMatRef(i)); + } + + } + + template + int motionFromEssentialChooseSolution( const std::vector &Rs, + const std::vector &ts, + const Mat_ &K1, + const Mat_ &x1, + const Mat_ &K2, + const Mat_ &x2 ) + { + Mat_ P1, P2, R1 = Mat_::eye(3,3); + + T val = static_cast(0.0); + Vec t1(val, val, val); + + projectionFromKRt(K1, R1, t1, P1); + + std::vector > points2d; + points2d.push_back(x1); + points2d.push_back(x2); + + for ( int i = 0; i < 4; ++i ) + { + const Mat_ R2 = Rs[i]; + const Vec t2 = ts[i]; + projectionFromKRt(K2, R2, t2, P2); + + std::vector > Ps; + Ps.push_back(P1); + Ps.push_back(P2); + + Vec X; + triangulatePoints(points2d, Ps, X); + + T d1 = depth(R1, t1, X); + T d2 = depth(R2, t2, X); + + // Test if point is front to the two cameras. + if ( d1 > 0 && d2 > 0 ) + { + return i; + } + } + + return -1; + } + + int motionFromEssentialChooseSolution( InputArrayOfArrays _Rs, + InputArrayOfArrays _ts, + InputArray _K1, + InputArray _x1, + InputArray _K2, + InputArray _x2 ) + { + std::vector Rs, ts; + _Rs.getMatVector(Rs); + _ts.getMatVector(ts); + const Mat K1 = _K1.getMat(), x1 = _x1.getMat(), K2 = _K2.getMat(), x2 = _x2.getMat(); + const int depth = K1.depth(); + CV_Assert( Rs.size() == 4 && ts.size() == 4 ); + CV_Assert((K1.cols == 3 && K1.rows == 3) && (K1.size() == K2.size())); + CV_Assert((x1.cols == 1 && x1.rows == 2) && (x1.size() == x2.size())); + CV_Assert((depth == CV_32F || depth == CV_64F) && depth == K2.depth() && depth == x1.depth() && depth == x2.depth()); + + int solution = 0; + + // type + if( depth == CV_32F ) + { + solution = motionFromEssentialChooseSolution(Rs, ts, K1, x1, K2, x2); + } + else + { + solution = motionFromEssentialChooseSolution(Rs, ts, K1, x1, K2, x2); + } + + return solution; + } + + template + void + fundamentalFromEssential( const Mat_ &E, + const Mat_ &K1, + const Mat_ &K2, + Mat_ F ) + { + F = K2.inv().t() * E * K1.inv(); + } + + void + fundamentalFromEssential( InputArray _E, + InputArray _K1, + InputArray _K2, + OutputArray _F ) + { + const Mat E = _E.getMat(), K1 = _K1.getMat(), K2 = _K2.getMat(); + const int depth = E.depth(); + CV_Assert(E.cols == 3 && E.rows == 3 && E.size() == _K1.size() && E.size() == _K2.size() && (depth == CV_32F || depth == CV_64F)); + + _F.create(3, 3, depth); + + Mat F = _F.getMat(); + + // type + if( depth == CV_32F ) + { + fundamentalFromEssential(E, K1, K2, F); + } + else + { + fundamentalFromEssential(E, K1, K2, F); + } + + } + + template + void + essentialFromFundamental( const Mat_ &F, + const Mat_ &K1, + const Mat_ &K2, + Mat_ E ) + { + E = K2.t() * F * K1; + } + + void + essentialFromFundamental( InputArray _F, + InputArray _K1, + InputArray _K2, + OutputArray _E ) + { + const Mat F = _F.getMat(), K1 = _K1.getMat(), K2 = _K2.getMat(); + const int depth = F.depth(); + CV_Assert(F.cols == 3 && F.rows == 3 && F.size() == _K1.size() && F.size() == _K2.size() && (depth == CV_32F || depth == CV_64F)); + + _E.create(3, 3, depth); + + Mat E = _E.getMat(); + + // type + if( depth == CV_32F ) + { + essentialFromFundamental(F, K1, K2, E); + } + else + { + essentialFromFundamental(F, K1, K2, E); + } + } + + template + void + essentialFromRt( const Mat_ &_R1, + const Mat_ &_t1, + const Mat_ &_R2, + const Mat_ &_t2, + Mat_ _E ) + { + libmv::Mat3 E; + libmv::Mat3 R1, R2; + libmv::Vec3 t1, t2; + + cv2eigen( _R1, R1 ); + cv2eigen( _t1, t1 ); + cv2eigen( _R2, R2 ); + cv2eigen( _t2, t2 ); + + libmv::EssentialFromRt( R1, t1, R2, t2, &E ); + + eigen2cv( E, _E ); + } + + void + essentialFromRt( InputArray _R1, + InputArray _t1, + InputArray _R2, + InputArray _t2, + OutputArray _E ) + { + const Mat R1 = _R1.getMat(), t1 = _t1.getMat(), R2 = _R2.getMat(), t2 = _t2.getMat(); + const int depth = R1.depth(); + CV_Assert((R1.cols == 3 && R1.rows == 3) && (R1.size() == R2.size())); + CV_Assert((t1.cols == 1 && t1.rows == 3) && (t1.size() == t2.size())); + CV_Assert((depth == CV_32F || depth == CV_64F) && depth == R2.depth() && depth == t1.depth() && depth == t2.depth()); + + _E.create(3, 3, depth); + + Mat E = _E.getMat(); + + // type + if( depth == CV_32F ) + { + essentialFromRt(R1, t1, R2, t2, E); + } + else + { + essentialFromRt(R1, t1, R2, t2, E); + } + + } + + template + void + normalizeFundamental( const Mat_ &F, Mat_ F_normalized ) + { + F_normalized = F * (1.0/norm(F,NORM_L2)); // Frobenius Norm + + if ( F_normalized(2,2) < 0 ) + { + F_normalized *= -1; + } + } + + void + normalizeFundamental( InputArray _F, + OutputArray _F_normalized ) + { + const Mat F = _F.getMat(); + const int depth = F.depth(); + CV_Assert(F.cols == 3 && F.rows == 3 && (depth == CV_32F || depth == CV_64F)); + + _F_normalized.create(3, 3, depth); + + Mat F_normalized = _F_normalized.getMat(); + + // type + if( depth == CV_32F ) + { + normalizeFundamental(F, F_normalized); + } + else + { + normalizeFundamental(F, F_normalized); + } + } + + template + void + computeOrientation( const Mat_ &x1, + const Mat_ &x2, + Mat_ R, + Mat_ t, + T s ) + { + Mat_ rr, rl, rt, lt; + normalizePoints(x1, rr, rt); + normalizePoints(x2, rl, lt); + + Mat_ rrBar, rlBar, rVar, lVar; + meanAndVarianceAlongRows(rr, rrBar, rVar); + meanAndVarianceAlongRows(rl, rlBar, lVar); + + Mat_ rrp, rlp; + rrp = rr - repeat(rrBar, x1.rows, x1.cols); + rlp = rl - repeat(rlBar, x2.rows, x2.cols); + + // TODO: finish implementation + // https://github.com/vrabaud/sfm_toolbox/blob/master/sfm/computeOrientation.m#L44 + } + + void + computeOrientation( InputArrayOfArrays _x1, + InputArrayOfArrays _x2, + OutputArray _R, + OutputArray _t, + double s ) + { + const Mat x1 = _x1.getMat(), x2 = _x2.getMat(); + const int depth = x1.depth(); + CV_Assert(x1.size() == x2.size() && (depth == CV_32F || depth == CV_64F)); + + _R.create(3, 3, depth); + _t.create(3, 1, depth); + + Mat R = _R.getMat(), t = _t.getMat(); + + // type + if( depth == CV_32F ) + { + computeOrientation(x1, x2, R, t, s); + } + else + { + computeOrientation(x1, x2, R, t, s); + } + } + +} /* namespace sfm */ +} /* namespace cv */ diff --git a/modules/sfm/src/libmv_capi.h b/modules/sfm/src/libmv_capi.h new file mode 100644 index 00000000000..606bed1a22f --- /dev/null +++ b/modules/sfm/src/libmv_capi.h @@ -0,0 +1,435 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// + // + // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. + // + // By downloading, copying, installing or using the software you agree to this license. + // If you do not agree to this license, do not download, install, + // copy or use the software. + // + // + // License Agreement + // For Open Source Computer Vision Library + // + // Copyright (C) 2015, OpenCV Foundation, all rights reserved. + // Third party copyrights are property of their respective owners. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistribution's of source code must retain the above copyright notice, + // this list of conditions and the following disclaimer. + // + // * Redistribution's in binary form must reproduce the above copyright notice, + // this list of conditions and the following disclaimer in the documentation + // and/or other materials provided with the distribution. + // + // * The name of the copyright holders may not be used to endorse or promote products + // derived from this software without specific prior written permission. + // + // This software is provided by the copyright holders and contributors "as is" and + // any express or implied warranties, including, but not limited to, the implied + // warranties of merchantability and fitness for a particular purpose are disclaimed. + // In no event shall the Intel Corporation or contributors be liable for any direct, + // indirect, incidental, special, exemplary, or consequential damages + // (including, but not limited to, procurement of substitute goods or services; + // loss of use, data, or profits; or business interruption) however caused + // and on any theory of liability, whether in contract, strict liability, + // or tort (including negligence or otherwise) arising in any way out of + // the use of this software, even if advised of the possibility of such damage. + // + //M*/ + +#ifndef __OPENCV_SFM_LIBMV_CAPI__ +#define __OPENCV_SFM_LIBMV_CAPI__ + +#include "libmv/logging/logging.h" + +#include "libmv/correspondence/feature.h" +#include "libmv/correspondence/feature_matching.h" +#include "libmv/correspondence/matches.h" +#include "libmv/correspondence/nRobustViewMatching.h" + +#include "libmv/simple_pipeline/bundle.h" +#include "libmv/simple_pipeline/camera_intrinsics.h" +#include "libmv/simple_pipeline/keyframe_selection.h" +#include "libmv/simple_pipeline/initialize_reconstruction.h" +#include "libmv/simple_pipeline/pipeline.h" +#include "libmv/simple_pipeline/reconstruction_scale.h" +#include "libmv/simple_pipeline/tracks.h" + +using namespace cv; +using namespace cv::sfm; +using namespace libmv; + + +//////////////////////////////////////// +// Based on 'libmv_capi' (blender API) +/////////////////////////////////////// + +struct libmv_Reconstruction { + EuclideanReconstruction reconstruction; + /* Used for per-track average error calculation after reconstruction */ + Tracks tracks; + CameraIntrinsics *intrinsics; + double error; + bool is_valid; +}; + + +////////////////////////////////////// +// Based on 'libmv_capi' (blender API) +///////////////////////////////////// + +void libmv_initLogging(const char* argv0) { + // Make it so FATAL messages are always print into console. + char severity_fatal[32]; + snprintf(severity_fatal, sizeof(severity_fatal), "%d", + google::GLOG_FATAL); + + google::InitGoogleLogging(argv0); + google::SetCommandLineOption("logtostderr", "1"); + google::SetCommandLineOption("v", "0"); + google::SetCommandLineOption("stderrthreshold", severity_fatal); + google::SetCommandLineOption("minloglevel", severity_fatal); +} + +void libmv_startDebugLogging(void) { + google::SetCommandLineOption("logtostderr", "1"); + google::SetCommandLineOption("v", "2"); + google::SetCommandLineOption("stderrthreshold", "1"); + google::SetCommandLineOption("minloglevel", "0"); +} + +void libmv_setLoggingVerbosity(int verbosity) { + char val[10]; + snprintf(val, sizeof(val), "%d", verbosity); + google::SetCommandLineOption("v", val); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// Based on the 'selectTwoKeyframesBasedOnGRICAndVariance()' function from 'libmv_capi' (blender API) +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* Select the two keyframes that give a lower reprojection error + */ + +bool selectTwoKeyframesBasedOnGRICAndVariance( + Tracks& tracks, + Tracks& normalized_tracks, + CameraIntrinsics& camera_intrinsics, + int& keyframe1, + int& keyframe2) { + + libmv::vector keyframes; + + /* Get list of all keyframe candidates first. */ + SelectKeyframesBasedOnGRICAndVariance(normalized_tracks, + camera_intrinsics, + keyframes); + + if (keyframes.size() < 2) { + LG << "Not enough keyframes detected by GRIC"; + return false; + } else if (keyframes.size() == 2) { + keyframe1 = keyframes[0]; + keyframe2 = keyframes[1]; + return true; + } + + /* Now choose two keyframes with minimal reprojection error after initial + * reconstruction choose keyframes with the least reprojection error after + * solving from two candidate keyframes. + * + * In fact, currently libmv returns single pair only, so this code will + * not actually run. But in the future this could change, so let's stay + * prepared. + */ + int previous_keyframe = keyframes[0]; + double best_error = std::numeric_limits::max(); + for (int i = 1; i < keyframes.size(); i++) { + EuclideanReconstruction reconstruction; + int current_keyframe = keyframes[i]; + libmv::vector keyframe_markers = + normalized_tracks.MarkersForTracksInBothImages(previous_keyframe, + current_keyframe); + + Tracks keyframe_tracks(keyframe_markers); + + /* get a solution from two keyframes only */ + EuclideanReconstructTwoFrames(keyframe_markers, &reconstruction); + EuclideanBundle(keyframe_tracks, &reconstruction); + EuclideanCompleteReconstruction(keyframe_tracks, + &reconstruction, + NULL); + + double current_error = EuclideanReprojectionError(tracks, + reconstruction, + camera_intrinsics); + + LG << "Error between " << previous_keyframe + << " and " << current_keyframe + << ": " << current_error; + + if (current_error < best_error) { + best_error = current_error; + keyframe1 = previous_keyframe; + keyframe2 = current_keyframe; + } + + previous_keyframe = current_keyframe; + } + + return true; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Based on the 'libmv_cameraIntrinsicsFillFromOptions()' function from 'libmv_capi' (blender API) +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/* Fill the camera intrinsics parameters given the camera instrinsics + * options values. + */ + +static void libmv_cameraIntrinsicsFillFromOptions( + const libmv_CameraIntrinsicsOptions* camera_intrinsics_options, + CameraIntrinsics* camera_intrinsics) { + camera_intrinsics->SetFocalLength(camera_intrinsics_options->focal_length, + camera_intrinsics_options->focal_length); + + camera_intrinsics->SetPrincipalPoint( + camera_intrinsics_options->principal_point_x, + camera_intrinsics_options->principal_point_y); + + camera_intrinsics->SetImageSize(camera_intrinsics_options->image_width, + camera_intrinsics_options->image_height); + + switch (camera_intrinsics_options->distortion_model) { + case SFM_DISTORTION_MODEL_POLYNOMIAL: + { + PolynomialCameraIntrinsics *polynomial_intrinsics = + static_cast(camera_intrinsics); + + polynomial_intrinsics->SetRadialDistortion( + camera_intrinsics_options->polynomial_k1, + camera_intrinsics_options->polynomial_k2, + camera_intrinsics_options->polynomial_k3); + + break; + } + + case SFM_DISTORTION_MODEL_DIVISION: + { + DivisionCameraIntrinsics *division_intrinsics = + static_cast(camera_intrinsics); + + division_intrinsics->SetDistortion( + camera_intrinsics_options->division_k1, + camera_intrinsics_options->division_k2); + break; + } + + default: + assert(!"Unknown distortion model"); + } +} + + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Based on the 'libmv_cameraIntrinsicsCreateFromOptions()' function from 'libmv_capi' (blender API) +////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* Create the camera intrinsics model given the camera instrinsics + * options values. + */ + +CameraIntrinsics* libmv_cameraIntrinsicsCreateFromOptions( + const libmv_CameraIntrinsicsOptions* camera_intrinsics_options) { + CameraIntrinsics *camera_intrinsics = NULL; + switch (camera_intrinsics_options->distortion_model) { + case SFM_DISTORTION_MODEL_POLYNOMIAL: + camera_intrinsics = new PolynomialCameraIntrinsics(); + break; + case SFM_DISTORTION_MODEL_DIVISION: + camera_intrinsics = new DivisionCameraIntrinsics(); + break; + default: + assert(!"Unknown distortion model"); + } + libmv_cameraIntrinsicsFillFromOptions(camera_intrinsics_options, + camera_intrinsics); + return camera_intrinsics; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// Based on the 'libmv_getNormalizedTracks()' function from 'libmv_capi' (blender API) +//////////////////////////////////////////////////////////////////////////////////////// + +/* Normalizes the tracks given the camera intrinsics parameters + */ + +void +libmv_getNormalizedTracks(const libmv::Tracks &tracks, + const libmv::CameraIntrinsics &camera_intrinsics, + libmv::Tracks *normalized_tracks) { + libmv::vector markers = tracks.AllMarkers(); + for (int i = 0; i < markers.size(); ++i) { + libmv::Marker &marker = markers[i]; + camera_intrinsics.InvertIntrinsics(marker.x, marker.y, + &marker.x, &marker.y); + normalized_tracks->Insert(marker.image, + marker.track, + marker.x, marker.y, + marker.weight); + } +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// Based on the 'libmv_solveRefineIntrinsics()' function from 'libmv_capi' (blender API) +////////////////////////////////////////////////////////////////////////////////////////// + +/* Refine the final solution using Bundle Adjustment + */ + +void libmv_solveRefineIntrinsics( + const Tracks &tracks, + const int refine_intrinsics, + const int bundle_constraints, + EuclideanReconstruction* reconstruction, + CameraIntrinsics* intrinsics) { + /* only a few combinations are supported but trust the caller/ */ + int bundle_intrinsics = 0; + + if (refine_intrinsics & SFM_REFINE_FOCAL_LENGTH) { + bundle_intrinsics |= libmv::BUNDLE_FOCAL_LENGTH; + } + if (refine_intrinsics & SFM_REFINE_PRINCIPAL_POINT) { + bundle_intrinsics |= libmv::BUNDLE_PRINCIPAL_POINT; + } + if (refine_intrinsics & SFM_REFINE_RADIAL_DISTORTION_K1) { + bundle_intrinsics |= libmv::BUNDLE_RADIAL_K1; + } + if (refine_intrinsics & SFM_REFINE_RADIAL_DISTORTION_K2) { + bundle_intrinsics |= libmv::BUNDLE_RADIAL_K2; + } + + EuclideanBundleCommonIntrinsics(tracks, + bundle_intrinsics, + bundle_constraints, + reconstruction, + intrinsics); +} + + +/////////////////////////////////////////////////////////////////////////////////// +// Based on the 'finishReconstruction()' function from 'libmv_capi' (blender API) +/////////////////////////////////////////////////////////////////////////////////// + +/* Finish the reconstrunction and computes the final reprojection error + */ + +void finishReconstruction( + const Tracks &tracks, + const CameraIntrinsics &camera_intrinsics, + libmv_Reconstruction *libmv_reconstruction) { + EuclideanReconstruction &reconstruction = + libmv_reconstruction->reconstruction; + + /* Reprojection error calculation. */ + libmv_reconstruction->tracks = tracks; + libmv_reconstruction->error = EuclideanReprojectionError(tracks, + reconstruction, + camera_intrinsics); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// Based on the 'libmv_solveReconstruction()' function from 'libmv_capi' (blender API) +//////////////////////////////////////////////////////////////////////////////////////// + +/* Perform the complete reconstruction process + */ + +libmv_Reconstruction *libmv_solveReconstruction( + const Tracks &libmv_tracks, + const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options, + libmv_ReconstructionOptions* libmv_reconstruction_options) { + libmv_Reconstruction *libmv_reconstruction = + new libmv_Reconstruction(); + + Tracks tracks = libmv_tracks; + EuclideanReconstruction &reconstruction = + libmv_reconstruction->reconstruction; + + /* Retrieve reconstruction options from C-API to libmv API. */ + CameraIntrinsics *camera_intrinsics; + camera_intrinsics = libmv_reconstruction->intrinsics = + libmv_cameraIntrinsicsCreateFromOptions(libmv_camera_intrinsics_options); + + /* Invert the camera intrinsics/ */ + Tracks normalized_tracks; + libmv_getNormalizedTracks(tracks, *camera_intrinsics, &normalized_tracks); + + /* keyframe selection. */ + int keyframe1 = libmv_reconstruction_options->keyframe1, + keyframe2 = libmv_reconstruction_options->keyframe2; + + if (libmv_reconstruction_options->select_keyframes) { + LG << "Using automatic keyframe selection"; + + selectTwoKeyframesBasedOnGRICAndVariance(tracks, + normalized_tracks, + *camera_intrinsics, + keyframe1, + keyframe2); + + /* so keyframes in the interface would be updated */ + libmv_reconstruction_options->keyframe1 = keyframe1; + libmv_reconstruction_options->keyframe2 = keyframe2; + } + + /* Actual reconstruction. */ + LG << "frames to init from: " << keyframe1 << " " << keyframe2; + + libmv::vector keyframe_markers = + normalized_tracks.MarkersForTracksInBothImages(keyframe1, keyframe2); + + LG << "number of markers for init: " << keyframe_markers.size(); + + if (keyframe_markers.size() < 8) { + LG << "No enough markers to initialize from"; + libmv_reconstruction->is_valid = false; + return libmv_reconstruction; + } + + EuclideanReconstructTwoFrames(keyframe_markers, &reconstruction); + EuclideanBundle(normalized_tracks, &reconstruction); + EuclideanCompleteReconstruction(normalized_tracks, + &reconstruction, + NULL); + + /* Refinement/ */ + if (libmv_reconstruction_options->refine_intrinsics) { + libmv_solveRefineIntrinsics( + tracks, + libmv_reconstruction_options->refine_intrinsics, + libmv::BUNDLE_NO_CONSTRAINTS, + &reconstruction, + camera_intrinsics); + } + + /* Set reconstruction scale to unity. */ + EuclideanScaleToUnity(&reconstruction); + + finishReconstruction(tracks, + *camera_intrinsics, + libmv_reconstruction); + + libmv_reconstruction->is_valid = true; + return (libmv_Reconstruction *) libmv_reconstruction; +} + +#endif \ No newline at end of file diff --git a/modules/sfm/src/libmv_light/CMake/Installation.cmake b/modules/sfm/src/libmv_light/CMake/Installation.cmake new file mode 100644 index 00000000000..910df4c1ead --- /dev/null +++ b/modules/sfm/src/libmv_light/CMake/Installation.cmake @@ -0,0 +1,9 @@ +#Install macro for libmv libraries +MACRO (LIBMV_INSTALL_LIB name) + + set_target_properties( ${name} + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib + ) + +ENDMACRO (LIBMV_INSTALL_LIB) \ No newline at end of file diff --git a/modules/sfm/src/libmv_light/CMakeLists.txt b/modules/sfm/src/libmv_light/CMakeLists.txt new file mode 100644 index 00000000000..4feead2b53e --- /dev/null +++ b/modules/sfm/src/libmv_light/CMakeLists.txt @@ -0,0 +1,5 @@ +# installation rules +include(CMake/Installation.cmake) + +set(BUILD_SHARED_LIBS OFF) # Force static libs for 3rdparty dependencies +add_subdirectory(libmv) \ No newline at end of file diff --git a/modules/sfm/src/libmv_light/libmv/CMakeLists.txt b/modules/sfm/src/libmv_light/libmv/CMakeLists.txt new file mode 100644 index 00000000000..103b7288263 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(correspondence) +add_subdirectory(multiview) +add_subdirectory(numeric) + +if ( Ceres_FOUND ) + add_subdirectory(simple_pipeline) +endif () \ No newline at end of file diff --git a/modules/sfm/src/libmv_light/libmv/base/CMakeLists.txt b/modules/sfm/src/libmv_light/libmv/base/CMakeLists.txt new file mode 100644 index 00000000000..5c54b13649e --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/base/CMakeLists.txt @@ -0,0 +1,9 @@ +# define the source files +SET(BASE_SRC ) + +# define the header files (make the headers appear in IDEs.) +FILE(GLOB BASE_HDRS *.h) + +ADD_LIBRARY(base STATIC ${BASE_SRC} ${BASE_HDRS}) + +LIBMV_INSTALL_LIB(base) \ No newline at end of file diff --git a/modules/sfm/src/libmv_light/libmv/base/vector.h b/modules/sfm/src/libmv_light/libmv/base/vector.h new file mode 100644 index 00000000000..1931fb0b1f9 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/base/vector.h @@ -0,0 +1,176 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +// Get an aligned vector implementation. Must be included before . The +// Eigen guys went through some trouble to make a portable override for the +// fixed size vector types. + +#ifndef LIBMV_BASE_VECTOR_H +#define LIBMV_BASE_VECTOR_H + +#include +#include + +#include + +namespace libmv { + +// A simple container class, which guarantees 16 byte alignment needed for most +// vectorization. Don't use this container for classes that cannot be copied +// via memcpy. +// FIXME: this class has some issues: +// - doesn't support iterators. +// - impede compatibility with code using STL. +// - the STL already provide support for custom allocators +// it could be replaced with a simple +// template class vector : std::vector {} declaration +// provided it doesn't break code relying on libmv::vector specific behavior +template > +class vector { + public: + ~vector() { clear(); } + + vector() { init(); } + vector(int size) { init(); resize(size); } + vector(int size, const T & val) { + init(); + resize(size); + std::fill(data_, data_+size_, val); } + + // Copy constructor and assignment. + vector(const vector &rhs) { + init(); + copy(rhs); + } + vector &operator=(const vector &rhs) { + if (&rhs != this) { + copy(rhs); + } + return *this; + } + + /// Swaps the contents of two vectors in constant time. + void swap(vector &other) { + std::swap(allocator_, other.allocator_); + std::swap(size_, other.size_); + std::swap(capacity_, other.capacity_); + std::swap(data_, other.data_); + } + + T *data() const { return data_; } + int size() const { return size_; } + int capacity() const { return capacity_; } + const T& back() const { return data_[size_ - 1]; } + T& back() { return data_[size_ - 1]; } + const T& front() const { return data_[0]; } + T& front() { return data_[0]; } + const T& operator[](int n) const { return data_[n]; } + T& operator[](int n) { return data_[n]; } + const T& at(int n) const { return data_[n]; } + T& at(int n) { return data_[n]; } + const T * begin() const { return data_; } + const T * end() const { return data_+size_; } + T * begin() { return data_; } + T * end() { return data_+size_; } + + void resize(size_t size) { + reserve(size); + if (size > size_) { + construct(size_, size); + } else if (size < size_) { + destruct(size, size_); + } + size_ = size; + } + + void push_back(const T &value) { + if (size_ == capacity_) { + reserve(size_ ? 2 * size_ : 1); + } + new (&data_[size_++]) T(value); + } + + void pop_back() { + resize(size_ - 1); + } + + void clear() { + destruct(0, size_); + deallocate(); + init(); + } + + void reserve(unsigned int size) { + if (size > size_) { + T *data = static_cast(allocate(size)); + memcpy(data, data_, sizeof(*data)*size_); + allocator_.deallocate(data_, capacity_); + data_ = data; + capacity_ = size; + } + } + + bool empty() { + return size_ == 0; + } + + private: + void construct(int start, int end) { + for (int i = start; i < end; ++i) { + new (&data_[i]) T; + } + } + void destruct(int start, int end) { + for (int i = start; i < end; ++i) { + data_[i].~T(); + } + } + void init() { + size_ = 0; + data_ = 0; + capacity_ = 0; + } + + void *allocate(int size) { + return size ? allocator_.allocate(size) : 0; + } + + void deallocate() { + allocator_.deallocate(data_, size_); + data_ = 0; + } + + void copy(const vector &rhs) { + resize(rhs.size()); + for (int i = 0; i < rhs.size(); ++i) { + (*this)[i] = rhs[i]; + } + } + + Allocator allocator_; + size_t size_; + size_t capacity_; + T *data_; +}; + +} // namespace libmv + +#endif // LIBMV_BASE_VECTOR_H diff --git a/modules/sfm/src/libmv_light/libmv/base/vector_utils.h b/modules/sfm/src/libmv_light/libmv/base/vector_utils.h new file mode 100644 index 00000000000..c71e1bea951 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/base/vector_utils.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + + +#ifndef LIBMV_BASE_VECTOR_UTILS_H_ +#define LIBMV_BASE_VECTOR_UTILS_H_ + +/// Delete the contents of a container. +template +void DeleteElements(Array *array) { + for (int i = 0; i < array->size(); ++i) { + delete (*array)[i]; + } + array->clear(); +} + +#endif // LIBMV_BASE_VECTOR_UTILS_H_ diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/CMakeLists.txt b/modules/sfm/src/libmv_light/libmv/correspondence/CMakeLists.txt new file mode 100644 index 00000000000..5aac7bab6fc --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/CMakeLists.txt @@ -0,0 +1,13 @@ +# define the source files +SET(CORRESPONDENCE_SRC feature_matching.cc + matches.cc + nRobustViewMatching.cc) + +# define the header files (make the headers appear in IDEs.) +FILE(GLOB CORRESPONDENCE_HDRS *.h) + +ADD_LIBRARY(correspondence STATIC ${CORRESPONDENCE_SRC} ${CORRESPONDENCE_HDRS}) + +TARGET_LINK_LIBRARIES(correspondence multiview) + +LIBMV_INSTALL_LIB(correspondence) \ No newline at end of file diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/bipartite_graph.h b/modules/sfm/src/libmv_light/libmv/correspondence/bipartite_graph.h new file mode 100644 index 00000000000..e11fb3e29cb --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/bipartite_graph.h @@ -0,0 +1,139 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_CORRESPONDENCE_INT_BIPARTITE_GRAPH_H_ +#define LIBMV_CORRESPONDENCE_INT_BIPARTITE_GRAPH_H_ + +#include +#include +#include +#include + +namespace libmv { + +// A bipartite graph with labelled edges. +template +class BipartiteGraph { + public: + typedef std::map, EdgeT> EdgeMap; + + void Insert(const T &left, const T &right, const EdgeT &edge) { + left_to_right_[std::make_pair(left, right)] = edge; + right_to_left_[std::make_pair(right, left)] = edge; + } + void Remove(const T &left, const T &right) { + typename EdgeMap::iterator iter = + left_to_right_.find(std::make_pair(left, right)); + if (iter != left_to_right_.end()) + left_to_right_.erase(iter); + iter = right_to_left_.find(std::make_pair(right, left)); + if (iter != right_to_left_.end()) + right_to_left_.erase(iter); + } + + int NumLeftLeft(T left) const { + int n = 0; + typename EdgeMap::const_iterator it; + for (it = left_to_right_.begin(); it != left_to_right_.end(); ++it) { + if (it->first.first == left) + n++; + } + return n; + } + + int NumLeftRight(T right) const { + int n = 0; + typename EdgeMap::const_iterator it; + for (it = left_to_right_.begin(); it != left_to_right_.end(); ++it) { + if (it->first.second == right) + n++; + } + return n; + } + + // Erases all the elements. + // Note that this function does not desallocate pointers + void Clear() { + left_to_right_.clear(); + right_to_left_.clear(); + } + class Range { + friend class BipartiteGraph; + public: + T left() const { return reversed_ ? it_->first.second : it_->first.first; } + T right() const { return reversed_ ? it_->first.first : it_->first.second;} + EdgeT edge() const { return it_->second; } + + void operator++() { ++it_; } + EdgeT operator*() { return it_->second; } + operator bool() const { return it_ != end_; } + + private: + Range(typename EdgeMap::const_iterator it, + typename EdgeMap::const_iterator end, + bool reversed) + : reversed_(reversed), it_(it), end_(end) {} + + bool reversed_; + typename EdgeMap::const_iterator it_, end_; + }; + + Range All() const { + return Range(left_to_right_.begin(), left_to_right_.end(), false); + } + + Range AllReversed() const { + return Range(right_to_left_.begin(), right_to_left_.end(), true); + } + + Range ToLeft(T left) const { + return Range(left_to_right_.lower_bound(Lower(left)), + left_to_right_.upper_bound(Upper(left)), false); + } + + Range ToRight(T right) const { + return Range(right_to_left_.lower_bound(Lower(right)), + right_to_left_.upper_bound(Upper(right)), true); + } + + // Find a pointer to the edge, or NULL if not found. + const EdgeT *Edge(T left, T right) const { + typename EdgeMap::const_iterator it = + left_to_right_.find(std::make_pair(left, right)); + if (it != left_to_right_.end()) { + return &(it->second); + } + return NULL; + } + + private: + std::pair Lower(T first) const { + return std::make_pair(first, std::numeric_limits::min()); + } + std::pair Upper(T first) const { + return std::make_pair(first, std::numeric_limits::max()); + } + EdgeMap left_to_right_; + EdgeMap right_to_left_; +}; + +} // namespace libmv + +#endif // LIBMV_CORRESPONDENCE_BIPARTITE_GRAPH_H_ diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/feature.h b/modules/sfm/src/libmv_light/libmv/correspondence/feature.h new file mode 100644 index 00000000000..3ec97bbf4c8 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/feature.h @@ -0,0 +1,72 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_CORRESPONDENCE_FEATURE_H_ +#define LIBMV_CORRESPONDENCE_FEATURE_H_ + +#include + +#include "libmv/numeric/numeric.h" + +namespace libmv +{ + class Feature + { + public: + virtual + ~Feature() {}; + }; + +class PointFeature : public Feature { + public: + PointFeature(float xx=0.0f, float yy=0.0f) { + coords[0] = xx; + coords[1] = yy; + scale = 0.0; + orientation = 0.0; + } + + PointFeature &operator=(const PointFeature &other) + { + if (this == &other) + return *this; + scale = other.scale; + orientation = other.orientation; + coords = other.coords; + return *this; + } + + PointFeature(const cv::KeyPoint & keypoint) { + coords[0] = keypoint.pt.x; + coords[1] = keypoint.pt.y; + scale = keypoint.octave; + orientation = keypoint.angle; + } + float x() const { return coords(0); } + float y() const { return coords(1); } + + Vec2f coords; // (x, y), i.e. (column, row). + float scale; // In pixels. + float orientation; // In radians. +}; + +} // namespace libmv + +#endif // LIBMV_CORRESPONDENCE_FEATURE_H_ diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/feature_matching.cc b/modules/sfm/src/libmv_light/libmv/correspondence/feature_matching.cc new file mode 100644 index 00000000000..c59db04dccc --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/feature_matching.cc @@ -0,0 +1,143 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include + +#include "libmv/correspondence/feature_matching.h" + +// Compute candidate matches between 2 sets of features. Two features A and B +// are a candidate match if A is the nearest neighbor of B and B is the nearest +// neighbor of A. +void FindCandidateMatches(const FeatureSet &left, + const FeatureSet &right, + Matches *matches) { + if (left.features.empty() || + right.features.empty() ) { + return; + } + + cv::FlannBasedMatcher matcherA; + cv::FlannBasedMatcher matcherB; + + // Paste the necessary data in contiguous arrays. + cv::Mat arrayA = FeatureSet::FeatureSetDescriptorsToContiguousArray(left); + cv::Mat arrayB = FeatureSet::FeatureSetDescriptorsToContiguousArray(right); + + matcherA.add(std::vector(1, arrayB)); + matcherB.add(std::vector(1, arrayA)); + std::vector matchesA, matchesB; + matcherA.match(arrayA, matchesA); + matcherB.match(arrayB, matchesB); + + // From putative matches get symmetric matches. + int max_track_number = 0; + for (size_t i = 0; i < matchesA.size(); ++i) + { + // Add the match only if we have a symmetric result. + if (i == matchesB[matchesA[i].trainIdx].trainIdx) + { + matches->Insert(0, max_track_number, &left.features[i]); + matches->Insert(1, max_track_number, &right.features[matchesA[i].trainIdx]); + ++max_track_number; + } + } +} + +cv::Mat FeatureSet::FeatureSetDescriptorsToContiguousArray + ( const FeatureSet & featureSet ) { + + if (featureSet.features.empty()) { + return cv::Mat(); + } + int descriptorSize = featureSet.features[0].descriptor.cols; + // Allocate and paste the necessary data. + cv::Mat array(featureSet.features.size(), descriptorSize, CV_32F); + + //-- Paste data in the contiguous array : + for (int i = 0; i < (int)featureSet.features.size(); ++i) { + featureSet.features[i].descriptor.copyTo(array.row(i)); + } + return array; +} + +// Compute candidate matches between 2 sets of features with a ratio. +void FindCandidateMatches_Ratio(const FeatureSet &left, + const FeatureSet &right, + Matches *matches, + float fRatio) { + if (left.features.empty() || right.features.empty()) + return; + + cv::FlannBasedMatcher matcherA; + + // Paste the necessary data in contiguous arrays. + cv::Mat arrayA = FeatureSet::FeatureSetDescriptorsToContiguousArray(left); + cv::Mat arrayB = FeatureSet::FeatureSetDescriptorsToContiguousArray(right); + + matcherA.add(std::vector(1, arrayB)); + std::vector < std::vector > matchesA; + matcherA.knnMatch(arrayA, matchesA, 2); + + // From putative matches get matches that fit the "Ratio" heuristic. + int max_track_number = 0; + for (size_t i = 0; i < matchesA.size(); ++i) + { + float distance0 = matchesA[i][0].distance; + float distance1 = matchesA[i][1].distance; + // Add the match only if we have a symmetric result. + if (distance0 < fRatio * distance1) + { + { + matches->Insert(0, max_track_number, &left.features[i]); + matches->Insert(1, max_track_number, &right.features[matchesA[i][0].trainIdx]); + ++max_track_number; + } + } + } +} + +// Compute correspondences that match between 2 sets of features with a ratio. +void FindCorrespondences(const FeatureSet &left, + const FeatureSet &right, + std::map *correspondences, + float fRatio) { + if (left.features.empty() || right.features.empty()) + return; + + cv::FlannBasedMatcher matcherA; + + // Paste the necessary data in contiguous arrays. + cv::Mat arrayA = FeatureSet::FeatureSetDescriptorsToContiguousArray(left); + cv::Mat arrayB = FeatureSet::FeatureSetDescriptorsToContiguousArray(right); + + matcherA.add(std::vector(1, arrayB)); + std::vector < std::vector > matchesA; + matcherA.knnMatch(arrayA, matchesA, 2); + + // From putative matches get matches that fit the "Ratio" heuristic. + for (size_t i = 0; i < matchesA.size(); ++i) + { + float distance0 = matchesA[i][0].distance; + float distance1 = matchesA[i][1].distance; + // Add the match only if we have a symmetric result. + if (distance0 < fRatio * distance1) + (*correspondences)[i] = matchesA[i][0].trainIdx; + } +} diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/feature_matching.h b/modules/sfm/src/libmv_light/libmv/correspondence/feature_matching.h new file mode 100644 index 00000000000..0552f28506d --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/feature_matching.h @@ -0,0 +1,96 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + + +#ifndef LIBMV_CORRESPONDENCE_FEATURE_MATCHING_H_ +#define LIBMV_CORRESPONDENCE_FEATURE_MATCHING_H_ + +#include + +#include + +#include "libmv/base/vector.h" +#include "libmv/correspondence/feature.h" +#include "libmv/correspondence/matches.h" + +using namespace libmv; + +/// Define the description of a feature described by : +/// A PointFeature (x,y,scale,orientation), +/// And a descriptor (a vector of floats). +class KeypointFeature : public ::PointFeature { + public: + virtual ~KeypointFeature(){}; + + void set(const PointFeature &feature,const cv::Mat &descriptor) + { + PointFeature::operator=(feature); + descriptor.copyTo(this->descriptor); + } + + // Match kdtree traits: with this, the Feature can act as a kdtree point. + float operator[](int i) const { + if (descriptor.depth() != CV_32F) + std::cerr << "KeypointFeature does not contain floats" << std::endl; + return descriptor.at(i); + } + + cv::Mat descriptor; +}; + +/// FeatureSet : Store an array of KeypointFeature ( Keypoint and descriptor). +struct FeatureSet { + std::vector features; + + /// return a float * containing the concatenation of descriptor data. + /// Must be deleted with [] + static cv::Mat FeatureSetDescriptorsToContiguousArray + ( const FeatureSet & featureSet ); +}; + +// Compute candidate matches between 2 sets of features. Two features a and b +// are a candidate match if a is the nearest neighbor of b and b is the nearest +// neighbor of a. +void FindCandidateMatches(const FeatureSet &left, + const FeatureSet &right, + Matches *matches); + +// Compute candidate matches between 2 sets of features. +// Keep only strong and distinctive matches by using the Davide Lowe's ratio +// method. +// I.E: A match is considered as strong if the following test is true : +// I.E distance[0] < fRatio * distances[1]. +// From David Lowe “Distinctive Image Features from Scale-Invariant Keypoints”. +// You can use David Lowe's magic ratio (0.6 or 0.8). +// 0.8 allow to remove 90% of the false matches while discarding less than 5% +// of the correct matches. +void FindCandidateMatches_Ratio(const FeatureSet &left, + const FeatureSet &right, + Matches *matches, + float fRatio = 0.8f); +// TODO(pmoulon) Add Lowe's ratio symmetric match method. +// Compute correspondences that match between 2 sets of features with a ratio. + +void FindCorrespondences(const FeatureSet &left, + const FeatureSet &right, + std::map *correspondences, + float fRatio = 0.8f); + +#endif //LIBMV_CORRESPONDENCE_FEATURE_MATCHING_H_ diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/matches.cc b/modules/sfm/src/libmv_light/libmv/correspondence/matches.cc new file mode 100644 index 00000000000..2e1a89e4eb2 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/matches.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/correspondence/matches.h" +#include "libmv/correspondence/feature.h" + +namespace libmv { + +Matches::~Matches() {} + +void DeleteMatchFeatures(Matches *matches) { + (void) matches; + // XXX + /* + for (Correspondences::FeatureIterator it = correspondences->ScanAllFeatures(); + !it.Done(); it.Next()) { + delete const_cast(it.feature()); + } + */ +} + +int Matches::GetNumberOfMatches(ImageID id1,ImageID id2) const +{ + Features features1 = InImage(id1); + Features features2 = InImage(id2); + int count = 0; + for(int i1=0;features1;++features1,++i1) + { + Features temp = features2; + for(int i2=0;temp;++temp,++i2) + { + if(features1.track() == temp.track()) + { + ++count; + break; + } + } + } + return count; +} + +void Matches::DrawMatches(ImageID image_id1,const cv::Mat &image1,ImageID image_id2,const cv::Mat &image2, cv::Mat &out)const +{ + std::vector points1; + std::vector points2; + std::vector matches; + KeyPoints(image_id1,points1); + KeyPoints(image_id2,points2); + MatchesTwo(image_id1,image_id2,matches); + cv::drawMatches(image1,points1,image2,points2,matches,out); +} + +void Matches::MatchesTwo(ImageID image1,ImageID image2,std::vector &matches)const +{ + Features features1 = InImage(image1); + Features features2 = InImage(image2); + for(int i1=0;features1;++features1,++i1) + { + Features temp = features2; + for(int i2=0;temp;++temp,++i2) + { + if(features1.track() == temp.track()) + { + matches.push_back(cv::DMatch(i1,i2,std::numeric_limits::max())); + break; + } + } + } +} + +void Matches::KeyPoints(ImageID image,std::vector &keypoints)const +{ + PointFeatures2KeyPoints(InImage(image),keypoints); +} + +void Matches::PointFeatures2KeyPoints(Features features,std::vector &keypoints)const +{ + for(;features;++features) + keypoints.push_back(cv::KeyPoint(features.feature()->x(),features.feature()->y(),1)); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/matches.h b/modules/sfm/src/libmv_light/libmv/correspondence/matches.h new file mode 100644 index 00000000000..23871d1bdcb --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/matches.h @@ -0,0 +1,319 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_CORRESPONDENCE_MATCHES_H_ +#define LIBMV_CORRESPONDENCE_MATCHES_H_ + +#include +#include + +#include +#include + +#include "libmv/base/vector.h" +// TODO(julien) use the bipartite_graph_new.h now +#include "libmv/correspondence/bipartite_graph.h" +#include "libmv/logging/logging.h" +#include "libmv/correspondence/feature.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +class Matches { + public: + typedef int ImageID; + typedef int TrackID; + typedef BipartiteGraph Graph; + + ~Matches(); + + // Iterate over features, silently skiping any that are not FeatureT or + // derived from FeatureT. + template + class Features { + public: + ImageID image() const { return r_.left(); } + TrackID track() const { return r_.right(); } + const FeatureT *feature() const { + return static_cast(r_.edge()); + } + operator bool() const { return r_; } + void operator++() { ++r_; Skip(); } + Features(Graph::Range range) : r_(range) { Skip(); } + + private: + void Skip() { + while (r_ && !dynamic_cast (r_.edge())) ++r_; + } + Graph::Range r_; + }; + typedef Features Points; + + template + Features All() const { return Features(graph_.All()); } + + template + Features AllReversed() const { return Features(graph_.AllReversed()); } + + template + Features InImage(ImageID image) const { + return Features(graph_.ToLeft(image)); + } + + template + Features InTrack(TrackID track) const { + return Features(graph_.ToRight(track)); + } + + void PointFeatures2KeyPoints(Features features,std::vector &keypoints)const; + void KeyPoints(ImageID image,std::vector &keypoints)const; + void MatchesTwo(ImageID image1,ImageID image2,std::vector &matches)const; + void DrawMatches(ImageID image_id1,const cv::Mat &image1,ImageID image_id2,const cv::Mat &image2, cv::Mat &out)const; + + // Does not take ownership of feature. + void Insert(ImageID image, TrackID track, const Feature *feature) { + graph_.Insert(image, track, feature); + images_.insert(image); + tracks_.insert(track); + } + + void Remove(ImageID image, TrackID track) { + graph_.Remove(image, track); + } + + // Erases all the elements. + // Note that this function does not desallocate features + void Clear() { + graph_.Clear(); + images_.clear(); + tracks_.clear(); + } + // Insert all elements of matches (images, tracks, feature) as new data + void Insert(const Matches &matches) { + size_t max_images = GetMaxImageID(); + size_t max_tracks = GetMaxTrackID(); + std::map new_image_ids; + std::map new_track_ids; + std::set::const_iterator iter_image; + std::set::const_iterator iter_track; + + ImageID image_id; + iter_image = matches.images_.begin(); + for (; iter_image != matches.images_.end(); ++iter_image) { + image_id = ++max_images; + new_image_ids[*iter_image] = image_id; + images_.insert(image_id); + } + TrackID track_id; + iter_track = matches.tracks_.begin(); + for (; iter_track != matches.tracks_.end(); ++iter_track) { + track_id = ++max_tracks; + new_track_ids[*iter_track] = track_id; + tracks_.insert(track_id); + } + iter_image = matches.images_.begin(); + for (; iter_image != matches.images_.end(); ++iter_image) { + iter_track = matches.tracks_.begin(); + for (; iter_track != matches.tracks_.end(); ++iter_track) { + const Feature * feature = matches.Get(*iter_image, *iter_track); + image_id = new_image_ids[*iter_image]; + track_id = new_track_ids[*iter_track]; + graph_.Insert(image_id, track_id, feature); + } + } + } + // Merge common elements add new data (image, track, feature). + void Merge(const Matches &matches) { + std::map new_track_ids; + std::set::const_iterator iter_image; + std::set::const_iterator iter_track; + //Find non common elements and add them into new_matches + std::set::const_iterator found_image; + std::set::const_iterator found_track; + iter_image = matches.images_.begin(); + for (; iter_image != matches.images_.end(); ++iter_image) { + found_image = images_.find(*iter_image); + if (found_image == images_.end()) { + images_.insert(*iter_image); + } + iter_track = matches.tracks_.begin(); + for (; iter_track != matches.tracks_.end(); ++iter_track) { + found_track = tracks_.find(*iter_track); + if (found_track == tracks_.end() + && new_track_ids.find(*iter_track) == new_track_ids.end()) { + new_track_ids[*iter_track] = *iter_track; + tracks_.insert(*iter_track); + } + const Feature * feature = matches.Get(*iter_image, *iter_track); + graph_.Insert(*iter_image, *iter_track, feature); + } + } + } + + const Feature *Get(ImageID image, TrackID track) const { + const Feature *const *f = graph_.Edge(image, track); + return f ? *f : NULL; + } + + ImageID GetMaxImageID() const { + ImageID max_images = -1; + std::set::const_iterator iter_image = + std::max_element (images_.begin(), images_.end()); + if (iter_image != images_.end()) { + max_images = *iter_image; + } + return max_images; + } + + TrackID GetMaxTrackID() const { + TrackID max_tracks = -1; + std::set::const_iterator iter_track = + std::max_element (tracks_.begin(), tracks_.end()); + if (iter_track != tracks_.end()) { + max_tracks = *iter_track; + } + return max_tracks; + } + + int GetNumberOfMatches(ImageID id1,ImageID id2) const; + + const std::set &get_images() const { + return images_; + } + const std::set &get_tracks() const { + return tracks_; + } + + int NumFeatureImage(ImageID image_id) const { + return graph_.NumLeftLeft(image_id); + } + + int NumFeatureTrack(TrackID track_id) const { + return graph_.NumLeftRight(track_id); + } + + + size_t NumTracks() const { return tracks_.size(); } + size_t NumImages() const { return images_.size(); } + + private: + Graph graph_; + std::set images_; + std::set tracks_; +}; + + +/** + * Intersect sorted lists. Destroys originals; leaves results as the single + * entry in sorted_items. + */ +template +void Intersect(std::vector< std::vector > *sorted_items) { + std::vector tmp; + while (sorted_items->size() > 1) { + int n = sorted_items->size(); + std::vector &s1 = (*sorted_items)[n - 1]; + std::vector &s2 = (*sorted_items)[n - 2]; + tmp.resize(std::min(s1.size(), s2.size())); + typename std::vector::iterator it = std::set_intersection( + s1.begin(), s1.end(), s2.begin(), s2.end(), tmp.begin()); + tmp.resize(int(it - tmp.begin())); + std::swap(tmp, s2); + tmp.resize(0); + sorted_items->pop_back(); + } +} + +/** + * Extract matrices from a set of matches, containing the point locations. Only + * points for tracks which appear in all images are returned in tracks. + * + * \param matches The matches from which to extract the points. + * \param images Which images to extract the points from. + * \param xs The resulting matrices containing the points. The entries will + * match the ordering of images. + */ +inline void TracksInAllImages(const Matches &matches, + const vector &images, + vector *tracks) { + if (!images.size()) { + return; + } + std::vector > all_tracks; + all_tracks.resize(images.size()); + for (int i = 0; i < images.size(); ++i) { + for (Matches::Points r = matches.InImage(images[i]); r; ++r) { + all_tracks[i].push_back(r.track()); + } + } + Intersect(&all_tracks); + CHECK(all_tracks.size() == 1); + for (size_t i = 0; i < all_tracks[0].size(); ++i) { + tracks->push_back(all_tracks[0][i]); + } +} + +/** + * Extract matrices from a set of matches, containing the point locations. Only + * points for tracks which appear in all images are returned in xs. Each output + * matrix is of size 2 x N, where N is the number of tracks that are in all the + * images. + * + * \param matches The matches from which to extract the points. + * \param images Which images to extract the points from. + * \param xs The resulting matrices containing the points. The entries will + * match the ordering of images. + */ +inline void PointMatchMatrices(const Matches &matches, + const vector &images, + vector *tracks, + vector *xs) { + TracksInAllImages(matches, images, tracks); + + xs->resize(images.size()); + for (int i = 0; i < images.size(); ++i) { + (*xs)[i].resize(2, tracks->size()); + for (int j = 0; j < tracks->size(); ++j) { + const PointFeature *f = static_cast( + matches.Get(images[i], (*tracks)[j])); + (*xs)[i](0, j) = f->x(); + (*xs)[i](1, j) = f->y(); + } + } +} + +inline void TwoViewPointMatchMatrices(const Matches &matches, + Matches::ImageID image_id1, + Matches::ImageID image_id2, + vector *xs) { + vector tracks; + vector images; + images.push_back(image_id1); + images.push_back(image_id2); + PointMatchMatrices(matches, images, &tracks, xs); +} + +// Delete the features in a correspondences. Uses const_cast to avoid the +// constness problems. This is more intended for tests than for actual use. +void DeleteMatchFeatures(Matches *matches); + +} // namespace libmv + +#endif // LIBMV_CORRESPONDENCE_MATCHES_H_ diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/nRobustViewMatching.cc b/modules/sfm/src/libmv_light/libmv/correspondence/nRobustViewMatching.cc new file mode 100644 index 00000000000..86673d49ffc --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/nRobustViewMatching.cc @@ -0,0 +1,303 @@ +// Copyright (c) 2010 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include + +#include "libmv/base/vector_utils.h" +#include "libmv/correspondence/feature.h" +#include "libmv/correspondence/feature_matching.h" +#include "libmv/correspondence/nRobustViewMatching.h" +#include "libmv/multiview/robust_fundamental.h" + +using namespace libmv; +using namespace correspondence; +using namespace std; + +nRobustViewMatching::nRobustViewMatching(){ +#ifdef CV_VERSION_EPOCH + m_pDescriber = NULL; +#endif +} + +nRobustViewMatching::nRobustViewMatching( + cv::Ptr pDetector, + cv::Ptr pDescriber){ + m_pDetector = pDetector; + m_pDescriber = pDescriber; +} + +/** + * Compute the data and store it in the class map + * + * \param[in] filename The file from which the data will be extracted. + * + * \return True if success. + */ +bool nRobustViewMatching::computeData(const string & filename) +{ + cv::Mat im_cv = cv::imread(filename, 0); + if (im_cv.empty()) { + LOG(FATAL) << "Failed loading image: " << filename; + return false; + } + else + { + libmv::vector features; + std::vector features_cv; + m_pDetector->detect( im_cv, features_cv ); + features.resize(features_cv.size()); + for(size_t i=0; icompute(im_cv, features_cv, descriptors); + + // Copy data. + m_ViewData.insert( make_pair(filename,FeatureSet()) ); + FeatureSet & KeypointData = m_ViewData[filename]; + KeypointData.features.resize(descriptors.rows); + for(int i = 0;i < descriptors.rows; ++i) + { + KeypointFeature & feat = KeypointData.features[i]; + descriptors.row(i).copyTo(feat.descriptor); + *(PointFeature*)(&feat) = *(PointFeature*)features[i]; + } + + DeleteElements(&features); + + return true; + } +} + +/** +* Compute the putative match between data computed from element A and B +* Store the match data internally in the class +* map< , MatchObject > +* +* \param[in] The name of the filename A (use computed data for this element) +* \param[in] The name of the filename B (use computed data for this element) +* +* \return True if success. +*/ +bool nRobustViewMatching::MatchData(const string & dataA, const string & dataB) +{ + // Check input data + if ( find(m_vec_InputNames.begin(), m_vec_InputNames.end(), dataA) + == m_vec_InputNames.end() || + find(m_vec_InputNames.begin(), m_vec_InputNames.end(), dataB) + == m_vec_InputNames.end()) + { + LOG(INFO) << "[nViewMatching::MatchData] " + << "Could not identify one of the input name."; + return false; + } + if (m_ViewData.find(dataA) == m_ViewData.end() || + m_ViewData.find(dataB) == m_ViewData.end()) + { + LOG(INFO) << "[nViewMatching::MatchData] " + << "Could not identify data for one of the input name."; + return false; + } + + // Computed data exist for the given name + int iDataA = find(m_vec_InputNames.begin(), m_vec_InputNames.end(), dataA) + - m_vec_InputNames.begin(); + int iDataB = find(m_vec_InputNames.begin(), m_vec_InputNames.end(), dataB) + - m_vec_InputNames.begin(); + + Matches matches; + //TODO(pmoulon) make FindCandidatesMatches a parameter. + FindCandidateMatches_Ratio(m_ViewData[dataA], + m_ViewData[dataB], + &matches); + Matches consistent_matches; + if (computeConstrainMatches(matches,iDataA,iDataB,&consistent_matches)) + { + matches = consistent_matches; + } + if (matches.NumTracks() > 0) + { + m_sharedData.insert( + make_pair( + make_pair(m_vec_InputNames[iDataA],m_vec_InputNames[iDataB]), + matches) + ); + } + + return true; +} + +/** +* From a series of element it computes the cross putative match list. +* +* \param[in] vec_data The data on which we want compute cross matches. +* +* \return True if success (and any matches was found). +*/ +bool nRobustViewMatching::computeCrossMatch( const std::vector & vec_data) +{ + if (m_pDetector == NULL || m_pDescriber == NULL) { + LOG(FATAL) << "Invalid Detector or Describer."; + return false; + } + + m_vec_InputNames = vec_data; + bool bRes = true; + for (int i=0; i < vec_data.size(); ++i) { + bRes &= computeData(vec_data[i]); + } + + bool bRes2 = true; + for (int i=0; i < vec_data.size(); ++i) { + for (int j=0; j < i; ++j) + { + if (m_ViewData.find(vec_data[i]) != m_ViewData.end() && + m_ViewData.find(vec_data[j]) != m_ViewData.end()) + { + bRes2 &= this->MatchData( vec_data[i], vec_data[j]); + } + } + } + return bRes2; +} + +bool nRobustViewMatching::computeRelativeMatch( + const std::vector& vec_data) { + if (m_pDetector == NULL || m_pDescriber == NULL) { + LOG(FATAL) << "Invalid Detector or Describer."; + return false; + } + + m_vec_InputNames = vec_data; + bool bRes = true; + for (int i=0; i < vec_data.size(); ++i) { + bRes &= computeData(vec_data[i]); + } + + bool bRes2 = true; + for (int i=1; i < vec_data.size(); ++i) { + if (m_ViewData.find(vec_data[i-1]) != m_ViewData.end() && + m_ViewData.find(vec_data[i]) != m_ViewData.end()) + { + bRes2 &= this->MatchData(vec_data[i-1], vec_data[i]); + } + } + // Match the first and the last images (in order to detect loop) + bRes2 &= this->MatchData(vec_data[0], vec_data[vec_data.size() - 1]); + return bRes2; +} + +/** +* Give the posibility to constrain the matches list. +* +* \param[in] matchIn The input match data between indexA and indexB. +* \param[in] dataAindex The reference index for element A. +* \param[in] dataBindex The reference index for element B. +* \param[out] matchesOut The output match that satisfy the internal constraint. +* +* \return True if success. +*/ +bool nRobustViewMatching::computeConstrainMatches(const Matches & matchIn, + int dataAindex, + int dataBindex, + Matches * matchesOut) +{ + if (matchesOut == NULL) + { + LOG(INFO) << "[nViewMatching::computeConstrainMatches]" + << " Could not export constrained matches."; + return false; + } + libmv::vector x; + libmv::vector tracks, images; + images.push_back(0); + images.push_back(1); + PointMatchMatrices(matchIn, images, &tracks, &x); + + libmv::vector inliers; + Mat3 H; + // TODO(pmoulon) Make the Correspondence filter a parameter. + //HomographyFromCorrespondences2PointRobust(x[0], x[1], 0.3, &H, &inliers); + //HomographyFromCorrespondences4PointRobust(x[0], x[1], 0.3, &H, &inliers); + //AffineFromCorrespondences2PointRobust(x[0], x[1], 1, &H, &inliers); + FundamentalFromCorrespondences7PointRobust(x[0], x[1], 1.0, &H, &inliers); + + //TODO(pmoulon) insert an optimization phase. + // Rerun Robust correspondance on the inliers. + // it will allow to compute a better model and filter ugly fitting. + + //-- Assert that the output of the model is consistent : + // As much as the minimal points are inliers. + if (inliers.size() > 7 * 2) { //2* [nbPoints required by the estimator] + // If tracks table is empty initialize it + if (m_featureToTrackTable.size() == 0) { + // Build new correspondence graph containing only inliers. + for (int l = 0; l < inliers.size(); ++l) { + const int k = inliers[l]; + m_featureToTrackTable[matchIn.Get(0, tracks[k])] = l; + m_featureToTrackTable[matchIn.Get(1, tracks[k])] = l; + m_tracks.Insert(dataAindex, l, + matchIn.Get(dataBindex, tracks[k])); + m_tracks.Insert(dataBindex, l, + matchIn.Get(dataAindex, tracks[k])); + } + } + else { + // Else update the tracks + for (int l = 0; l < inliers.size(); ++l) { + const int k = inliers[l]; + map::const_iterator iter = + m_featureToTrackTable.find(matchIn.Get(1, tracks[k])); + + if (iter!=m_featureToTrackTable.end()) { + // Add a feature to the existing track + const int trackIndex = iter->second; + m_featureToTrackTable[matchIn.Get(0, tracks[k])] = trackIndex; + m_tracks.Insert(dataAindex, trackIndex, + matchIn.Get(0, tracks[k])); + } + else { + // It's a new track + const int trackIndex = m_tracks.NumTracks(); + m_featureToTrackTable[matchIn.Get(0, tracks[k])] = trackIndex; + m_featureToTrackTable[matchIn.Get(1, tracks[k])] = trackIndex; + m_tracks.Insert(dataAindex, trackIndex, + matchIn.Get(0, tracks[k])); + m_tracks.Insert(dataBindex, trackIndex, + matchIn.Get(1, tracks[k])); + } + } + } + // Export common feature between the two view + if (matchesOut) { + Matches & consistent_matches = *matchesOut; + // Build new correspondence graph containing only inliers. + for (int l = 0; l < inliers.size(); ++l) { + int k = inliers[l]; + for (int i = 0; i < 2; ++i) { + consistent_matches.Insert(images[i], tracks[k], + matchIn.Get(images[i], tracks[k])); + } + } + } + } + return true; +} + diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/nRobustViewMatching.h b/modules/sfm/src/libmv_light/libmv/correspondence/nRobustViewMatching.h new file mode 100644 index 00000000000..faddbc3ed23 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/nRobustViewMatching.h @@ -0,0 +1,138 @@ +// Copyright (c) 2010 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_CORRESPONDENCE_N_ROBUST_VIEW_MATCHING_INTERFACE_H_ +#define LIBMV_CORRESPONDENCE_N_ROBUST_VIEW_MATCHING_INTERFACE_H_ + +struct FeatureSet; +#include + +#include "libmv/correspondence/feature.h" +#include "libmv/correspondence/matches.h" +#include "libmv/correspondence/nViewMatchingInterface.h" + +namespace libmv { +namespace correspondence { + +using namespace std; + +class nRobustViewMatching :public nViewMatchingInterface { + + public: + nRobustViewMatching(); + // Constructor (Specify a detector and a describer interface) + // The class do not handle memory management over this two parameter. + nRobustViewMatching(cv::Ptr pDetector, + cv::Ptr pDescriber); + //TODO(pmoulon) Add a constructor with a Detector and a Descriptor + // Add also a Template function to make the match robust.. + ~nRobustViewMatching(){}; + + /** + * Compute the data and store it in the class map + * + * \param[in] filename The file from which the data will be extracted. + * + * \return True if success. + */ + bool computeData(const string & filename); + + /** + * Compute the putative match between data computed from element A and B + * Store the match data internally in the class + * map< , MatchObject > + * + * \param[in] The name of the filename A (use computed data for this element) + * \param[in] The name of the filename B (use computed data for this element) + * + * \return True if success. + */ + bool MatchData(const string & dataA, const string & dataB); + + /** + * From a series of element it computes the cross putative match list. + * + * \param[in] vec_data The data on which we want compute cross matches. + * + * \return True if success (and any matches was found). + */ + bool computeCrossMatch( const std::vector & vec_data); + + + /** + * From a series of element it computes the incremental putative match list. + * (only locally, in the relative neighborhood) + * + * \param[in] vec_data The data on which we want compute matches. + * + * \return True if success (and any matches was found). + */ + bool computeRelativeMatch( const std::vector & vec_data); + + /** + * Give the posibility to constrain the matches list. + * + * \param[in] matchIn The input match data between indexA and indexB. + * \param[in] dataAindex The reference index for element A. + * \param[in] dataBindex The reference index for element B. + * \param[out] matchesOut The output match that satisfy the internal constraint. + * + * \return True if success. + */ + bool computeConstrainMatches(const Matches & matchIn, + int dataAindex, + int dataBindex, + Matches * matchesOut); + + /// Return pairwise correspondence ( geometrically filtered ) + const map< pair, Matches> & getSharedData() const + { return m_sharedData; } + /// Return extracted feature over the given image. + const map & getViewData() const + { return m_ViewData; } + /// Return detected geometrical consistent matches + const Matches & getMatches() const + { return m_tracks; } + +private : + /// Input data names + std::vector m_vec_InputNames; + /// Data that represent each named element. + map m_ViewData; + /// Matches between element named element . + map< pair, Matches> m_sharedData; + + /// LookUpTable to make the crossCorrespondence easier between tracks + /// and feature. + map m_featureToTrackTable; + + /// Matches between all the view. + Matches m_tracks; + + /// Interface to detect Keypoint. + cv::Ptr m_pDetector; + /// Interface to describe Keypoint. + cv::Ptr m_pDescriber; +}; + +} // using namespace correspondence +} // using namespace libmv + +#endif // LIBMV_CORRESPONDENCE_N_ROBUST_VIEW_MATCHING_INTERFACE_H_ diff --git a/modules/sfm/src/libmv_light/libmv/correspondence/nViewMatchingInterface.h b/modules/sfm/src/libmv_light/libmv/correspondence/nViewMatchingInterface.h new file mode 100644 index 00000000000..2810b2f2064 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/correspondence/nViewMatchingInterface.h @@ -0,0 +1,70 @@ +// Copyright (c) 2010 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_CORRESPONDENCE_N_VIEW_MATCHING_INTERFACE_H_ +#define LIBMV_CORRESPONDENCE_N_VIEW_MATCHING_INTERFACE_H_ + +#include + +namespace libmv { +namespace correspondence { + + using namespace std; + +class nViewMatchingInterface { + + public: + virtual ~nViewMatchingInterface() {}; + + /** + * Compute the data and store it in the class map + * + * \param[in] filename The file from which the data will be extracted. + * + * \return True if success. + */ + virtual bool computeData(const string & filename)=0; + + /** + * Compute the putative match between data computed from element A and B + * Store the match data internally in the class + * map< , MatchObject > + * + * \param[in] The name of the filename A (use computed data for this element) + * \param[in] The name of the filename B (use computed data for this element) + * + * \return True if success. + */ + virtual bool MatchData(const string & dataA, const string & dataB)=0; + + /** + * From a series of element it compute the cross putative match list. + * + * \param[in] vec_data The data on which we want compute cross matches. + * + * \return True if success (and any matches was found). + */ + virtual bool computeCrossMatch( const std::vector & vec_data)=0; +}; + +} // using namespace correspondence +} // using namespace libmv + +#endif // LIBMV_CORRESPONDENCE_N_VIEW_MATCHING_INTERFACE_H_ diff --git a/modules/sfm/src/libmv_light/libmv/logging/logging.h b/modules/sfm/src/libmv_light/libmv/logging/logging.h new file mode 100644 index 00000000000..776d9d52f7a --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/logging/logging.h @@ -0,0 +1,31 @@ +// Copyright (c) 2007, 2008, 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_LOGGING_LOGGING_H +#define LIBMV_LOGGING_LOGGING_H + +#include + +#define LG LOG(INFO) +#define V0 LOG(INFO) +#define V1 LOG(INFO) +#define V2 LOG(INFO) + +#endif // LIBMV_LOGGING_LOGGING_H diff --git a/modules/sfm/src/libmv_light/libmv/multiview/CMakeLists.txt b/modules/sfm/src/libmv_light/libmv/multiview/CMakeLists.txt new file mode 100644 index 00000000000..3fc7e4b5f10 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/CMakeLists.txt @@ -0,0 +1,22 @@ +# define the source files +SET(MULTIVIEW_SRC conditioning.cc + euclidean_resection.cc + fundamental.cc + fundamental_kernel.cc + homography.cc + panography.cc + panography_kernel.cc + projection.cc + robust_estimation.cc + robust_fundamental.cc + robust_resection.cc + triangulation.cc + twoviewtriangulation.cc) + +# define the header files (make the headers appear in IDEs.) +FILE(GLOB MULTIVIEW_HDRS *.h) + +ADD_LIBRARY(multiview STATIC ${MULTIVIEW_SRC} ${MULTIVIEW_HDRS}) +TARGET_LINK_LIBRARIES(multiview glog numeric) + +LIBMV_INSTALL_LIB(multiview) diff --git a/modules/sfm/src/libmv_light/libmv/multiview/conditioning.cc b/modules/sfm/src/libmv_light/libmv/multiview/conditioning.cc new file mode 100644 index 00000000000..0afbf119ea3 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/conditioning.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2010 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/multiview/conditioning.h" +#include "libmv/multiview/projection.h" + +namespace libmv { + +// HZ 4.4.4 pag.109: Point conditioning (non isotropic) +void PreconditionerFromPoints(const Mat &points, Mat3 *T) { + Vec mean, variance; + MeanAndVarianceAlongRows(points, &mean, &variance); + + double xfactor = sqrt(2.0 / variance(0)); + double yfactor = sqrt(2.0 / variance(1)); + + // If variance is equal to 0.0 set scaling factor to identity. + // -> Else it will provide nan value (because division by 0). + if (variance(0) < 1e-8) + xfactor = mean(0) = 1.0; + if (variance(1) < 1e-8) + yfactor = mean(1) = 1.0; + + *T << xfactor, 0, -xfactor * mean(0), + 0, yfactor, -yfactor * mean(1), + 0, 0, 1; +} +// HZ 4.4.4 pag.107: Point conditioning (isotropic) +void IsotropicPreconditionerFromPoints(const Mat &points, Mat3 *T) { + Vec mean, variance; + MeanAndVarianceAlongRows(points, &mean, &variance); + + double var_norm = variance.norm(); + double factor = sqrt(2.0 / var_norm); + + // If variance is equal to 0.0 set scaling factor to identity. + // -> Else it will provide nan value (because division by 0). + if (var_norm < 1e-8) { + factor = 1.0; + mean.setOnes(); + } + + *T << factor, 0, -factor * mean(0), + 0, factor, -factor * mean(1), + 0, 0, 1; +} + +void ApplyTransformationToPoints(const Mat &points, + const Mat3 &T, + Mat *transformed_points) { + int n = points.cols(); + transformed_points->resize(2, n); + Mat3X p(3, n); + EuclideanToHomogeneous(points, &p); + p = T * p; + HomogeneousToEuclidean(p, transformed_points); +} + +void NormalizePoints(const Mat &points, + Mat *normalized_points, + Mat3 *T) { + PreconditionerFromPoints(points, T); + ApplyTransformationToPoints(points, *T, normalized_points); +} + +void NormalizeIsotropicPoints(const Mat &points, + Mat *normalized_points, + Mat3 *T) { + IsotropicPreconditionerFromPoints(points, T); + ApplyTransformationToPoints(points, *T, normalized_points); +} + +// Denormalize the results. See HZ page 109. +void UnnormalizerT::Unnormalize(const Mat3 &T1, const Mat3 &T2, Mat3 *H) { + *H = T2.transpose() * (*H) * T1; +} + +void UnnormalizerI::Unnormalize(const Mat3 &T1, const Mat3 &T2, Mat3 *H) { + *H = T2.inverse() * (*H) * T1; +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/conditioning.h b/modules/sfm/src/libmv_light/libmv/multiview/conditioning.h new file mode 100644 index 00000000000..8f3e3a76070 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/conditioning.h @@ -0,0 +1,59 @@ +// Copyright (c) 2010 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_CONDITIONNING_H_ +#define LIBMV_MULTIVIEW_CONDITIONNING_H_ + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +// Point conditioning (non isotropic) +void PreconditionerFromPoints(const Mat &points, Mat3 *T); +// Point conditioning (isotropic) +void IsotropicPreconditionerFromPoints(const Mat &points, Mat3 *T); + +void ApplyTransformationToPoints(const Mat &points, + const Mat3 &T, + Mat *transformed_points); + +void NormalizePoints(const Mat &points, + Mat *normalized_points, + Mat3 *T); + +void NormalizeIsotropicPoints(const Mat &points, + Mat *normalized_points, + Mat3 *T); + +/// Use inverse for unnormalize +struct UnnormalizerI { + // Denormalize the results. See HZ page 109. + static void Unnormalize(const Mat3 &T1, const Mat3 &T2, Mat3 *H); +}; + +/// Use transpose for unnormalize +struct UnnormalizerT { + // Denormalize the results. See HZ page 109. + static void Unnormalize(const Mat3 &T1, const Mat3 &T2, Mat3 *H); +}; + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_CONDITIONNING_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/euclidean_resection.cc b/modules/sfm/src/libmv_light/libmv/multiview/euclidean_resection.cc new file mode 100644 index 00000000000..03ddf5e96f6 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/euclidean_resection.cc @@ -0,0 +1,774 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/multiview/euclidean_resection.h" + +#include +#include + +#include +#include + +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" +#include "libmv/multiview/projection.h" + +namespace libmv { +namespace euclidean_resection { + +typedef unsigned int uint; + +bool EuclideanResection(const Mat2X &x_camera, + const Mat3X &X_world, + Mat3 *R, Vec3 *t, + ResectionMethod method) { + switch (method) { + case RESECTION_ANSAR_DANIILIDIS: + EuclideanResectionAnsarDaniilidis(x_camera, X_world, R, t); + break; + case RESECTION_EPNP: + return EuclideanResectionEPnP(x_camera, X_world, R, t); + break; + case RESECTION_PPNP: + return EuclideanResectionPPnP(x_camera, X_world, R, t); + break; + default: + LOG(FATAL) << "Unknown resection method."; + } + return false; +} + +bool EuclideanResection(const Mat &x_image, + const Mat3X &X_world, + const Mat3 &K, + Mat3 *R, Vec3 *t, + ResectionMethod method) { + CHECK(x_image.rows() == 2 || x_image.rows() == 3) + << "Invalid size for x_image: " + << x_image.rows() << "x" << x_image.cols(); + + Mat2X x_camera; + if (x_image.rows() == 2) { + EuclideanToNormalizedCamera(x_image, K, &x_camera); + } else if (x_image.rows() == 3) { + HomogeneousToNormalizedCamera(x_image, K, &x_camera); + } + return EuclideanResection(x_camera, X_world, R, t, method); +} + +void AbsoluteOrientation(const Mat3X &X, + const Mat3X &Xp, + Mat3 *R, + Vec3 *t) { + int num_points = X.cols(); + Vec3 C = X.rowwise().sum() / num_points; // Centroid of X. + Vec3 Cp = Xp.rowwise().sum() / num_points; // Centroid of Xp. + + // Normalize the two point sets. + Mat3X Xn(3, num_points), Xpn(3, num_points); + for (int i = 0; i < num_points; ++i) { + Xn.col(i) = X.col(i) - C; + Xpn.col(i) = Xp.col(i) - Cp; + } + + // Construct the N matrix (pg. 635). + double Sxx = Xn.row(0).dot(Xpn.row(0)); + double Syy = Xn.row(1).dot(Xpn.row(1)); + double Szz = Xn.row(2).dot(Xpn.row(2)); + double Sxy = Xn.row(0).dot(Xpn.row(1)); + double Syx = Xn.row(1).dot(Xpn.row(0)); + double Sxz = Xn.row(0).dot(Xpn.row(2)); + double Szx = Xn.row(2).dot(Xpn.row(0)); + double Syz = Xn.row(1).dot(Xpn.row(2)); + double Szy = Xn.row(2).dot(Xpn.row(1)); + + Mat4 N; + N << Sxx + Syy + Szz, Syz - Szy, Szx - Sxz, Sxy - Syx, + Syz - Szy, Sxx - Syy - Szz, Sxy + Syx, Szx + Sxz, + Szx - Sxz, Sxy + Syx, -Sxx + Syy - Szz, Syz + Szy, + Sxy - Syx, Szx + Sxz, Syz + Szy, -Sxx - Syy + Szz; + + // Find the unit quaternion q that maximizes qNq. It is the eigenvector + // corresponding to the lagest eigenvalue. + Vec4 q = N.jacobiSvd(Eigen::ComputeFullU).matrixU().col(0); + + // Retrieve the 3x3 rotation matrix. + Vec4 qq = q.array() * q.array(); + double q0q1 = q(0) * q(1); + double q0q2 = q(0) * q(2); + double q0q3 = q(0) * q(3); + double q1q2 = q(1) * q(2); + double q1q3 = q(1) * q(3); + double q2q3 = q(2) * q(3); + + (*R) << qq(0) + qq(1) - qq(2) - qq(3), + 2 * (q1q2 - q0q3), + 2 * (q1q3 + q0q2), + 2 * (q1q2+ q0q3), + qq(0) - qq(1) + qq(2) - qq(3), + 2 * (q2q3 - q0q1), + 2 * (q1q3 - q0q2), + 2 * (q2q3 + q0q1), + qq(0) - qq(1) - qq(2) + qq(3); + + // Fix the handedness of the R matrix. + if (R->determinant() < 0) { + R->row(2) = -R->row(2); + } + // Compute the final translation. + *t = Cp - *R * C; +} + +// Convert i and j indices of the original variables into their quadratic +// permutation single index. It follows that t_ij = t_ji. +static int IJToPointIndex(int i, int j, int num_points) { + // Always make sure that j is bigger than i. This handles t_ij = t_ji. + if (j < i) { + std::swap(i, j); + } + int idx; + int num_permutation_rows = num_points * (num_points - 1) / 2; + + // All t_ii's are located at the end of the t vector after all t_ij's. + if (j == i) { + idx = num_permutation_rows + i; + } else { + int offset = (num_points - i - 1) * (num_points - i) / 2; + idx = (num_permutation_rows - offset + j - i - 1); + } + return idx; +}; + +// Convert i and j indexes of the solution for lambda to their linear indexes. +static int IJToIndex(int i, int j, int num_lambda) { + if (j < i) { + std::swap(i, j); + } + int A = num_lambda * (num_lambda + 1) / 2; + int B = num_lambda - i; + int C = B * (B + 1) / 2; + int idx = A - C + j - i; + return idx; +}; + +static int Sign(double value) { + return (value < 0) ? -1 : 1; +}; + +// Organizes a square matrix into a single row constraint on the elements of +// Lambda to create the constraints in equation (5) in "Linear Pose Estimation +// from Points or Lines", by Ansar, A. and Daniilidis, PAMI 2003. vol. 25, no. +// 5. +static Vec MatrixToConstraint(const Mat &A, + int num_k_columns, + int num_lambda) { + Vec C(num_k_columns); + C.setZero(); + int idx = 0; + for (int i = 0; i < num_lambda; ++i) { + for (int j = i; j < num_lambda; ++j) { + C(idx) = A(i, j); + if (i != j) { + C(idx) += A(j, i); + } + ++idx; + } + } + return C; +} + +// Normalizes the columns of vectors. +static void NormalizeColumnVectors(Mat3X *vectors) { + int num_columns = vectors->cols(); + for (int i = 0; i < num_columns; ++i) { + vectors->col(i).normalize(); + } +} + +void EuclideanResectionAnsarDaniilidis(const Mat2X &x_camera, + const Mat3X &X_world, + Mat3 *R, + Vec3 *t) { + CHECK(x_camera.cols() == X_world.cols()); + CHECK(x_camera.cols() > 3); + + int num_points = x_camera.cols(); + + // Copy the normalized camera coords into 3 vectors and normalize them so + // that they are unit vectors from the camera center. + Mat3X x_camera_unit(3, num_points); + x_camera_unit.block(0, 0, 2, num_points) = x_camera; + x_camera_unit.row(2).setOnes(); + NormalizeColumnVectors(&x_camera_unit); + + int num_m_rows = num_points * (num_points - 1) / 2; + int num_tt_variables = num_points * (num_points + 1) / 2; + int num_m_columns = num_tt_variables + 1; + Mat M(num_m_columns, num_m_columns); + M.setZero(); + Matu ij_index(num_tt_variables, 2); + + // Create the constraint equations for the t_ij variables (7) and arrange + // them into the M matrix (8). Also store the initial (i, j) indices. + int row = 0; + for (int i = 0; i < num_points; ++i) { + for (int j = i+1; j < num_points; ++j) { + M(row, row) = -2 * x_camera_unit.col(i).dot(x_camera_unit.col(j)); + M(row, num_m_rows + i) = x_camera_unit.col(i).dot(x_camera_unit.col(i)); + M(row, num_m_rows + j) = x_camera_unit.col(j).dot(x_camera_unit.col(j)); + Vec3 Xdiff = X_world.col(i) - X_world.col(j); + double center_to_point_distance = Xdiff.norm(); + M(row, num_m_columns - 1) = + - center_to_point_distance * center_to_point_distance; + ij_index(row, 0) = i; + ij_index(row, 1) = j; + ++row; + } + ij_index(i + num_m_rows, 0) = i; + ij_index(i + num_m_rows, 1) = i; + } + + int num_lambda = num_points + 1; // Dimension of the null space of M. + Mat V = M.jacobiSvd(Eigen::ComputeFullV).matrixV().block(0, + num_m_rows, + num_m_columns, + num_lambda); + + // TODO(vess): The number of constraint equations in K (num_k_rows) must be + // (num_points + 1) * (num_points + 2)/2. This creates a performance issue + // for more than 4 points. It is fine for 4 points at the moment with 18 + // instead of 15 equations. + int num_k_rows = num_m_rows + num_points * + (num_points*(num_points-1)/2 - num_points+1); + int num_k_columns = num_lambda * (num_lambda + 1) / 2; + Mat K(num_k_rows, num_k_columns); + K.setZero(); + + // Construct the first part of the K matrix corresponding to (t_ii, t_jk) for + // i != j. + int counter_k_row = 0; + for (int idx1 = num_m_rows; idx1 < num_tt_variables; ++idx1) { + for (int idx2 = 0; idx2 < num_m_rows; ++idx2) { + unsigned int i = ij_index(idx1, 0); + unsigned int j = ij_index(idx2, 0); + unsigned int k = ij_index(idx2, 1); + + if (i != j && i != k) { + int idx3 = IJToPointIndex(i, j, num_points); + int idx4 = IJToPointIndex(i, k, num_points); + + K.row(counter_k_row) = + MatrixToConstraint(V.row(idx1).transpose() * V.row(idx2)- + V.row(idx3).transpose() * V.row(idx4), + num_k_columns, + num_lambda); + ++counter_k_row; + } + } + } + + // Construct the second part of the K matrix corresponding to (t_ii,t_jk) for + // j==k. + for (int idx1 = num_m_rows; idx1 < num_tt_variables; ++idx1) { + for (int idx2 = idx1 + 1; idx2 < num_tt_variables; ++idx2) { + unsigned int i = ij_index(idx1, 0); + unsigned int j = ij_index(idx2, 0); + unsigned int k = ij_index(idx2, 1); + + int idx3 = IJToPointIndex(i, j, num_points); + int idx4 = IJToPointIndex(i, k, num_points); + + K.row(counter_k_row) = + MatrixToConstraint(V.row(idx1).transpose() * V.row(idx2)- + V.row(idx3).transpose() * V.row(idx4), + num_k_columns, + num_lambda); + ++counter_k_row; + } + } + Vec L_sq = K.jacobiSvd(Eigen::ComputeFullV).matrixV().col(num_k_columns - 1); + + // Pivot on the largest element for numerical stability. Afterwards recover + // the sign of the lambda solution. + double max_L_sq_value = fabs(L_sq(IJToIndex(0, 0, num_lambda))); + int max_L_sq_index = 1; + for (int i = 1; i < num_lambda; ++i) { + double abs_sq_value = fabs(L_sq(IJToIndex(i, i, num_lambda))); + if (max_L_sq_value < abs_sq_value) { + max_L_sq_value = abs_sq_value; + max_L_sq_index = i; + } + } + // Ensure positiveness of the largest value corresponding to lambda_ii. + L_sq = L_sq * Sign(L_sq(IJToIndex(max_L_sq_index, + max_L_sq_index, + num_lambda))); + + Vec L(num_lambda); + L(max_L_sq_index) = sqrt(L_sq(IJToIndex(max_L_sq_index, + max_L_sq_index, + num_lambda))); + + for (int i = 0; i < num_lambda; ++i) { + if (i != max_L_sq_index) { + L(i) = L_sq(IJToIndex(max_L_sq_index, i, num_lambda)) / L(max_L_sq_index); + } + } + + // Correct the scale using the fact that the last constraint is equal to 1. + L = L / (V.row(num_m_columns - 1).dot(L)); + Vec X = V * L; + + // Recover the distances from the camera center to the 3D points Q. + Vec d(num_points); + d.setZero(); + for (int c_point = num_m_rows; c_point < num_tt_variables; ++c_point) { + d(c_point - num_m_rows) = sqrt(X(c_point)); + } + + // Create the 3D points in the camera system. + Mat X_cam(3, num_points); + for (int c_point = 0; c_point < num_points; ++c_point) { + X_cam.col(c_point) = d(c_point) * x_camera_unit.col(c_point); + } + // Recover the camera translation and rotation. + AbsoluteOrientation(X_world, X_cam, R, t); +} + +// Selects 4 virtual control points using mean and PCA. +static void SelectControlPoints(const Mat3X &X_world, + Mat *X_centered, + Mat34 *X_control_points) { + size_t num_points = X_world.cols(); + + // The first virtual control point, C0, is the centroid. + Vec mean, variance; + MeanAndVarianceAlongRows(X_world, &mean, &variance); + X_control_points->col(0) = mean; + + // Computes PCA + X_centered->resize(3, num_points); + for (size_t c = 0; c < num_points; c++) { + X_centered->col(c) = X_world.col(c) - mean; + } + Mat3 X_centered_sq = (*X_centered) * X_centered->transpose(); + Eigen::JacobiSVD X_centered_sq_svd(X_centered_sq, Eigen::ComputeFullU); + Vec3 w = X_centered_sq_svd.singularValues(); + Mat3 u = X_centered_sq_svd.matrixU(); + for (size_t c = 0; c < 3; c++) { + double k = sqrt(w(c) / num_points); + X_control_points->col(c + 1) = mean + k * u.col(c); + } +} + +// Computes the barycentric coordinates for all real points +static void ComputeBarycentricCoordinates(const Mat3X &X_world_centered, + const Mat34 &X_control_points, + Mat4X *alphas) { + size_t num_points = X_world_centered.cols(); + Mat3 C2; + for (size_t c = 1; c < 4; c++) { + C2.col(c-1) = X_control_points.col(c) - X_control_points.col(0); + } + + Mat3 C2inv = C2.inverse(); + Mat3X a = C2inv * X_world_centered; + + alphas->resize(4, num_points); + alphas->setZero(); + alphas->block(1, 0, 3, num_points) = a; + for (size_t c = 0; c < num_points; c++) { + (*alphas)(0, c) = 1.0 - alphas->col(c).sum(); + } +} + +// Estimates the coordinates of all real points in the camera coordinate frame +static void ComputePointsCoordinatesInCameraFrame( + const Mat4X &alphas, + const Vec4 &betas, + const Eigen::Matrix &U, + Mat3X *X_camera) { + size_t num_points = alphas.cols(); + + // Estimates the control points in the camera reference frame. + Mat34 C2b; C2b.setZero(); + for (size_t cu = 0; cu < 4; cu++) { + for (size_t c = 0; c < 4; c++) { + C2b.col(c) += betas(cu) * U.block(11 - cu, c * 3, 1, 3).transpose(); + } + } + + // Estimates the 3D points in the camera reference frame + X_camera->resize(3, num_points); + for (size_t c = 0; c < num_points; c++) { + X_camera->col(c) = C2b * alphas.col(c); + } + + // Check the sign of the z coordinate of the points (should be positive) + uint num_z_neg = 0; + for (size_t i = 0; i < X_camera->cols(); ++i) { + if ((*X_camera)(2, i) < 0) { + num_z_neg++; + } + } + + // If more than 50% of z are negative, we change the signs + if (num_z_neg > 0.5 * X_camera->cols()) { + C2b = -C2b; + *X_camera = -(*X_camera); + } +} + +bool EuclideanResectionEPnP(const Mat2X &x_camera, + const Mat3X &X_world, + Mat3 *R, Vec3 *t) { + CHECK(x_camera.cols() == X_world.cols()); + CHECK(x_camera.cols() > 3); + size_t num_points = X_world.cols(); + + // Select the control points. + Mat34 X_control_points; + Mat X_centered; + SelectControlPoints(X_world, &X_centered, &X_control_points); + + // Compute the barycentric coordinates. + Mat4X alphas(4, num_points); + ComputeBarycentricCoordinates(X_centered, X_control_points, &alphas); + + // Estimates the M matrix with the barycentric coordinates + Mat M(2 * num_points, 12); + Eigen::Matrix sub_M; + for (size_t c = 0; c < num_points; c++) { + double a0 = alphas(0, c); + double a1 = alphas(1, c); + double a2 = alphas(2, c); + double a3 = alphas(3, c); + double ui = x_camera(0, c); + double vi = x_camera(1, c); + M.block(2*c, 0, 2, 12) << a0, 0, + a0*(-ui), a1, 0, + a1*(-ui), a2, 0, + a2*(-ui), a3, 0, + a3*(-ui), 0, + a0, a0*(-vi), 0, + a1, a1*(-vi), 0, + a2, a2*(-vi), 0, + a3, a3*(-vi); + } + + // TODO(julien): Avoid the transpose by rewriting the u2.block() calls. + Eigen::JacobiSVD MtMsvd(M.transpose()*M, Eigen::ComputeFullU); + Eigen::Matrix u2 = MtMsvd.matrixU().transpose(); + + // Estimate the L matrix. + Eigen::Matrix dv1; + Eigen::Matrix dv2; + Eigen::Matrix dv3; + Eigen::Matrix dv4; + + dv1.row(0) = u2.block(11, 0, 1, 3) - u2.block(11, 3, 1, 3); + dv1.row(1) = u2.block(11, 0, 1, 3) - u2.block(11, 6, 1, 3); + dv1.row(2) = u2.block(11, 0, 1, 3) - u2.block(11, 9, 1, 3); + dv1.row(3) = u2.block(11, 3, 1, 3) - u2.block(11, 6, 1, 3); + dv1.row(4) = u2.block(11, 3, 1, 3) - u2.block(11, 9, 1, 3); + dv1.row(5) = u2.block(11, 6, 1, 3) - u2.block(11, 9, 1, 3); + dv2.row(0) = u2.block(10, 0, 1, 3) - u2.block(10, 3, 1, 3); + dv2.row(1) = u2.block(10, 0, 1, 3) - u2.block(10, 6, 1, 3); + dv2.row(2) = u2.block(10, 0, 1, 3) - u2.block(10, 9, 1, 3); + dv2.row(3) = u2.block(10, 3, 1, 3) - u2.block(10, 6, 1, 3); + dv2.row(4) = u2.block(10, 3, 1, 3) - u2.block(10, 9, 1, 3); + dv2.row(5) = u2.block(10, 6, 1, 3) - u2.block(10, 9, 1, 3); + dv3.row(0) = u2.block(9, 0, 1, 3) - u2.block(9, 3, 1, 3); + dv3.row(1) = u2.block(9, 0, 1, 3) - u2.block(9, 6, 1, 3); + dv3.row(2) = u2.block(9, 0, 1, 3) - u2.block(9, 9, 1, 3); + dv3.row(3) = u2.block(9, 3, 1, 3) - u2.block(9, 6, 1, 3); + dv3.row(4) = u2.block(9, 3, 1, 3) - u2.block(9, 9, 1, 3); + dv3.row(5) = u2.block(9, 6, 1, 3) - u2.block(9, 9, 1, 3); + dv4.row(0) = u2.block(8, 0, 1, 3) - u2.block(8, 3, 1, 3); + dv4.row(1) = u2.block(8, 0, 1, 3) - u2.block(8, 6, 1, 3); + dv4.row(2) = u2.block(8, 0, 1, 3) - u2.block(8, 9, 1, 3); + dv4.row(3) = u2.block(8, 3, 1, 3) - u2.block(8, 6, 1, 3); + dv4.row(4) = u2.block(8, 3, 1, 3) - u2.block(8, 9, 1, 3); + dv4.row(5) = u2.block(8, 6, 1, 3) - u2.block(8, 9, 1, 3); + + Eigen::Matrix L; + for (size_t r = 0; r < 6; r++) { + L.row(r) << dv1.row(r).dot(dv1.row(r)), + 2.0 * dv1.row(r).dot(dv2.row(r)), + dv2.row(r).dot(dv2.row(r)), + 2.0 * dv1.row(r).dot(dv3.row(r)), + 2.0 * dv2.row(r).dot(dv3.row(r)), + dv3.row(r).dot(dv3.row(r)), + 2.0 * dv1.row(r).dot(dv4.row(r)), + 2.0 * dv2.row(r).dot(dv4.row(r)), + 2.0 * dv3.row(r).dot(dv4.row(r)), + dv4.row(r).dot(dv4.row(r)); + } + Vec6 rho; + rho << (X_control_points.col(0) - X_control_points.col(1)).squaredNorm(), + (X_control_points.col(0) - X_control_points.col(2)).squaredNorm(), + (X_control_points.col(0) - X_control_points.col(3)).squaredNorm(), + (X_control_points.col(1) - X_control_points.col(2)).squaredNorm(), + (X_control_points.col(1) - X_control_points.col(3)).squaredNorm(), + (X_control_points.col(2) - X_control_points.col(3)).squaredNorm(); + + // There are three possible solutions based on the three approximations of L + // (betas). Below, each one is solved for then the best one is chosen. + Mat3X X_camera; + Mat3 K; K.setIdentity(); + vector Rs(3); + vector ts(3); + Vec rmse(3); + + // At one point this threshold was 1e-3, and caused no end of problems in + // Blender by causing frames to not resect when they would have worked fine. + // When the resect failed, the projective followup is run leading to worse + // results, and often the dreaded "flipping" where objects get flipped + // between frames. Instead, disable the check for now, always succeeding. The + // ultimate check is always reprojection error anyway. + // + // TODO(keir): Decide if setting this to infinity, effectively disabling the + // check, is the right approach. So far this seems the case. + double kSuccessThreshold = std::numeric_limits::max(); + + // Find the first possible solution for R, t corresponding to: + // Betas = [b00 b01 b11 b02 b12 b22 b03 b13 b23 b33] + // Betas_approx_1 = [b00 b01 b02 b03] + Vec4 betas = Vec4::Zero(); + Eigen::Matrix l_6x4; + for (size_t r = 0; r < 6; r++) { + l_6x4.row(r) << L(r, 0), L(r, 1), L(r, 3), L(r, 6); + } + Eigen::JacobiSVD svd_of_l4(l_6x4, + Eigen::ComputeFullU | Eigen::ComputeFullV); + Vec4 b4 = svd_of_l4.solve(rho); + if ((l_6x4 * b4).isApprox(rho, kSuccessThreshold)) { + if (b4(0) < 0) { + b4 = -b4; + } + b4(0) = std::sqrt(b4(0)); + betas << b4(0), b4(1) / b4(0), b4(2) / b4(0), b4(3) / b4(0); + ComputePointsCoordinatesInCameraFrame(alphas, betas, u2, &X_camera); + AbsoluteOrientation(X_world, X_camera, &Rs[0], &ts[0]); + rmse(0) = RootMeanSquareError(x_camera, X_world, K, Rs[0], ts[0]); + } else { + LOG(ERROR) << "First approximation of beta not good enough."; + ts[0].setZero(); + rmse(0) = std::numeric_limits::max(); + } + + // Find the second possible solution for R, t corresponding to: + // Betas = [b00 b01 b11 b02 b12 b22 b03 b13 b23 b33] + // Betas_approx_2 = [b00 b01 b11] + betas.setZero(); + Eigen::Matrix l_6x3; + l_6x3 = L.block(0, 0, 6, 3); + Eigen::JacobiSVD svdOfL3(l_6x3, + Eigen::ComputeFullU | Eigen::ComputeFullV); + Vec3 b3 = svdOfL3.solve(rho); + VLOG(2) << " rho = " << rho; + VLOG(2) << " l_6x3 * b3 = " << l_6x3 * b3; + if ((l_6x3 * b3).isApprox(rho, kSuccessThreshold)) { + if (b3(0) < 0) { + betas(0) = std::sqrt(-b3(0)); + betas(1) = (b3(2) < 0) ? std::sqrt(-b3(2)) : 0; + } else { + betas(0) = std::sqrt(b3(0)); + betas(1) = (b3(2) > 0) ? std::sqrt(b3(2)) : 0; + } + if (b3(1) < 0) { + betas(0) = -betas(0); + } + betas(2) = 0; + betas(3) = 0; + ComputePointsCoordinatesInCameraFrame(alphas, betas, u2, &X_camera); + AbsoluteOrientation(X_world, X_camera, &Rs[1], &ts[1]); + rmse(1) = RootMeanSquareError(x_camera, X_world, K, Rs[1], ts[1]); + } else { + LOG(ERROR) << "Second approximation of beta not good enough."; + ts[1].setZero(); + rmse(1) = std::numeric_limits::max(); + } + + // Find the third possible solution for R, t corresponding to: + // Betas = [b00 b01 b11 b02 b12 b22 b03 b13 b23 b33] + // Betas_approx_3 = [b00 b01 b11 b02 b12] + betas.setZero(); + Eigen::Matrix l_6x5; + l_6x5 = L.block(0, 0, 6, 5); + Eigen::JacobiSVD svdOfL5(l_6x5, + Eigen::ComputeFullU | Eigen::ComputeFullV); + Vec5 b5 = svdOfL5.solve(rho); + if ((l_6x5 * b5).isApprox(rho, kSuccessThreshold)) { + if (b5(0) < 0) { + betas(0) = std::sqrt(-b5(0)); + if (b5(2) < 0) { + betas(1) = std::sqrt(-b5(2)); + } else { + b5(2) = 0; + } + } else { + betas(0) = std::sqrt(b5(0)); + if (b5(2) > 0) { + betas(1) = std::sqrt(b5(2)); + } else { + b5(2) = 0; + } + } + if (b5(1) < 0) { + betas(0) = -betas(0); + } + betas(2) = b5(3) / betas(0); + betas(3) = 0; + ComputePointsCoordinatesInCameraFrame(alphas, betas, u2, &X_camera); + AbsoluteOrientation(X_world, X_camera, &Rs[2], &ts[2]); + rmse(2) = RootMeanSquareError(x_camera, X_world, K, Rs[2], ts[2]); + } else { + LOG(ERROR) << "Third approximation of beta not good enough."; + ts[2].setZero(); + rmse(2) = std::numeric_limits::max(); + } + + // Finally, with all three solutions, select the (R, t) with the best RMSE. + VLOG(2) << "RMSE for solution 0: " << rmse(0); + VLOG(2) << "RMSE for solution 1: " << rmse(1); + VLOG(2) << "RMSE for solution 2: " << rmse(2); + size_t n = 0; + if (rmse(1) < rmse(0)) { + n = 1; + } + if (rmse(2) < rmse(n)) { + n = 2; + } + if (rmse(n) == std::numeric_limits::max()) { + LOG(ERROR) << "All three possibilities failed. Reporting failure."; + return false; + } + + VLOG(1) << "RMSE for best solution #" << n << ": " << rmse(n); + *R = Rs[n]; + *t = ts[n]; + + // TODO(julien): Improve the solutions with non-linear refinement. + return true; +} + +/* + + Straight from the paper: + http://www.diegm.uniud.it/fusiello/papers/3dimpvt12-b.pdf + + function [R T] = ppnp(P,S,tol) + % input + % P : matrix (nx3) image coordinates in camera reference [u v 1] + % S : matrix (nx3) coordinates in world reference [X Y Z] + % tol: exit threshold + % + % output + % R : matrix (3x3) rotation (world-to-camera) + % T : vector (3x1) translation (world-to-camera) + % + n = size(P,1); + Z = zeros(n); + e = ones(n,1); + A = eye(n)-((e*e’)./n); + II = e./n; + err = +Inf; + E_old = 1000*ones(n,3); + while err>tol + [U,˜,V] = svd(P’*Z*A*S); + VT = V’; + R=U*[1 0 0; 0 1 0; 0 0 det(U*VT)]*VT; + PR = P*R; + c = (S-Z*PR)’*II; + Y = S-e*c’; + Zmindiag = diag(PR*Y’)./(sum(P.*P,2)); + Zmindiag(Zmindiag<0)=0; + Z = diag(Zmindiag); + E = Y-Z*PR; + err = norm(E-E_old,’fro’); + E_old = E; + end + T = -R*c; + end + + */ +// TODO(keir): Re-do all the variable names and add comments matching the paper. +// This implementation has too much of the terseness of the original. On the +// other hand, it did work on the first try. +bool EuclideanResectionPPnP(const Mat2X &x_camera, + const Mat3X &X_world, + Mat3 *R, Vec3 *t) { + int n = x_camera.cols(); + Mat Z = Mat::Zero(n, n); + Vec e = Vec::Ones(n); + Mat A = Mat::Identity(n, n) - (e * e.transpose() / n); + Vec II = e / n; + + Mat P(n, 3); + P.col(0) = x_camera.row(0); + P.col(1) = x_camera.row(1); + P.col(2).setConstant(1.0); + + Mat S = X_world.transpose(); + + double error = std::numeric_limits::infinity(); + Mat E_old = 1000 * Mat::Ones(n, 3); + + Vec3 c; + Mat E(n, 3); + + int iteration = 0; + double tolerance = 1e-5; + // TODO(keir): The limit of 100 can probably be reduced, but this will require + // some investigation. + while (error > tolerance && iteration < 100) { + Mat3 tmp = P.transpose() * Z * A * S; + Eigen::JacobiSVD svd(tmp, Eigen::ComputeFullU | Eigen::ComputeFullV); + Mat3 U = svd.matrixU(); + Mat3 VT = svd.matrixV().transpose(); + Vec3 s; + s << 1, 1, (U * VT).determinant(); + *R = U * s.asDiagonal() * VT; + Mat PR = P * *R; // n x 3 + c = (S - Z*PR).transpose() * II; + Mat Y = S - e*c.transpose(); // n x 3 + Vec Zmindiag = (PR * Y.transpose()).diagonal() + .cwiseQuotient(P.rowwise().squaredNorm()); + for (int i = 0; i < n; ++i) { + Zmindiag[i] = std::max(Zmindiag[i], 0.0); + } + Z = Zmindiag.asDiagonal(); + E = Y - Z*PR; + error = (E - E_old).norm(); + LOG(INFO) << "PPnP error(" << (iteration++) << "): " << error; + E_old = E; + } + *t = -*R*c; + + // TODO(keir): Figure out what the failure cases are. Is it too many + // iterations? Spend some time going through the math figuring out if there + // is some way to detect that the algorithm is going crazy, and return false. + return true; +} + + +} // namespace resection +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/euclidean_resection.h b/modules/sfm/src/libmv_light/libmv/multiview/euclidean_resection.h new file mode 100644 index 00000000000..40eedadd1a5 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/euclidean_resection.h @@ -0,0 +1,148 @@ +// Copyright (c) 2010 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_ +#define LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_ + +#include "libmv/numeric/numeric.h" +#include "libmv/multiview/projection.h" + +namespace libmv { +namespace euclidean_resection { + +enum ResectionMethod { + RESECTION_ANSAR_DANIILIDIS, + + // The "EPnP" algorithm by Lepetit et al. + // http://cvlab.epfl.ch/~lepetit/papers/lepetit_ijcv08.pdf + RESECTION_EPNP, + + // The Procrustes PNP algorithm ("PPnP") + // http://www.diegm.uniud.it/fusiello/papers/3dimpvt12-b.pdf + RESECTION_PPNP +}; + +/** + * Computes the extrinsic parameters, R and t for a calibrated camera + * from 4 or more 3D points and their normalized images. + * + * \param x_camera Image points in normalized camera coordinates e.g. x_camera + * = inv(K) * x_image. + * \param X_world 3D points in the world coordinate system + * \param R Solution for the camera rotation matrix + * \param t Solution for the camera translation vector + * \param method The resection method to use. + */ +bool EuclideanResection(const Mat2X &x_camera, + const Mat3X &X_world, + Mat3 *R, Vec3 *t, + ResectionMethod method = RESECTION_EPNP); + +/** + * Computes the extrinsic parameters, R and t for a calibrated camera + * from 4 or more 3D points and their images. + * + * \param x_image Image points in non-normalized image coordinates. The + * coordates are laid out one per row. The matrix can be Nx2 + * or Nx3 for euclidean or homogenous 2D coordinates. + * \param X_world 3D points in the world coordinate system + * \param K Intrinsic parameters camera matrix + * \param R Solution for the camera rotation matrix + * \param t Solution for the camera translation vector + * \param method Resection method + */ +bool EuclideanResection(const Mat &x_image, + const Mat3X &X_world, + const Mat3 &K, + Mat3 *R, Vec3 *t, + ResectionMethod method = RESECTION_EPNP); + +/** + * The absolute orientation algorithm recovers the transformation between a set + * of 3D points, X and Xp such that: + * + * Xp = R*X + t + * + * The recovery of the absolute orientation is implemented after this article: + * Horn, Hilden, "Closed-form solution of absolute orientation using + * orthonormal matrices" + */ +void AbsoluteOrientation(const Mat3X &X, + const Mat3X &Xp, + Mat3 *R, + Vec3 *t); + +/** + * Computes the extrinsic parameters, R and t for a calibrated camera from 4 or + * more 3D points and their images. + * + * \param x_camera Image points in normalized camera coordinates, e.g. + * x_camera=inv(K)*x_image + * \param X_world 3D points in the world coordinate system + * \param R Solution for the camera rotation matrix + * \param t Solution for the camera translation vector + * + * This is the algorithm described in: "Linear Pose Estimation from Points or + * Lines", by Ansar, A. and Daniilidis, PAMI 2003. vol. 25, no. 5. + */ +void EuclideanResectionAnsarDaniilidis(const Mat2X &x_camera, + const Mat3X &X_world, + Mat3 *R, Vec3 *t); +/** + * Computes the extrinsic parameters, R and t for a calibrated camera from 4 or + * more 3D points and their images. + * + * \param x_camera Image points in normalized camera coordinates, + * e.g. x_camera = inv(K) * x_image + * \param X_world 3D points in the world coordinate system + * \param R Solution for the camera rotation matrix + * \param t Solution for the camera translation vector + * + * This is the algorithm described in: + * "{EP$n$P: An Accurate $O(n)$ Solution to the P$n$P Problem", by V. Lepetit + * and F. Moreno-Noguer and P. Fua, IJCV 2009. vol. 81, no. 2 + * \note: the non-linear optimization is not implemented here. + */ +bool EuclideanResectionEPnP(const Mat2X &x_camera, + const Mat3X &X_world, + Mat3 *R, Vec3 *t); + +/** + * Computes the extrinsic parameters, R and t for a calibrated camera from 4 or + * more 3D points and their images. + * + * \param x_camera Image points in normalized camera coordinates, + * e.g. x_camera = inv(K) * x_image + * \param X_world 3D points in the world coordinate system + * \param R Solution for the camera rotation matrix + * \param t Solution for the camera translation vector + * + * Straight from the paper: + * http://www.diegm.uniud.it/fusiello/papers/3dimpvt12-b.pdf + */ +bool EuclideanResectionPPnP(const Mat2X &x_camera, + const Mat3X &X_world, + Mat3 *R, Vec3 *t); + +} // namespace euclidean_resection +} // namespace libmv + + +#endif /* LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_ */ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/fundamental.cc b/modules/sfm/src/libmv_light/libmv/multiview/fundamental.cc new file mode 100644 index 00000000000..a18dab0ffdf --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/fundamental.cc @@ -0,0 +1,551 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/multiview/fundamental.h" + +#if CERES_FOUND +#include "ceres/ceres.h" +#endif +#include "libmv/logging/logging.h" +#include "libmv/numeric/numeric.h" +#include "libmv/numeric/poly.h" +#include "libmv/multiview/conditioning.h" +#include "libmv/multiview/projection.h" +#include "libmv/multiview/triangulation.h" + +namespace libmv { + +static void EliminateRow(const Mat34 &P, int row, Mat *X) { + X->resize(2, 4); + + int first_row = (row + 1) % 3; + int second_row = (row + 2) % 3; + + for (int i = 0; i < 4; ++i) { + (*X)(0, i) = P(first_row, i); + (*X)(1, i) = P(second_row, i); + } +} + +void ProjectionsFromFundamental(const Mat3 &F, Mat34 *P1, Mat34 *P2) { + *P1 << Mat3::Identity(), Vec3::Zero(); + Vec3 e2; + Mat3 Ft = F.transpose(); + Nullspace(&Ft, &e2); + *P2 << CrossProductMatrix(e2) * F, e2; +} + +// Addapted from vgg_F_from_P. +void FundamentalFromProjections(const Mat34 &P1, const Mat34 &P2, Mat3 *F) { + Mat X[3]; + Mat Y[3]; + Mat XY; + + for (int i = 0; i < 3; ++i) { + EliminateRow(P1, i, X + i); + EliminateRow(P2, i, Y + i); + } + + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + VerticalStack(X[j], Y[i], &XY); + (*F)(i, j) = XY.determinant(); + } + } +} + +// HZ 11.1 pag.279 (x1 = x, x2 = x') +// http://www.cs.unc.edu/~marc/tutorial/node54.html +static double EightPointSolver(const Mat &x1, const Mat &x2, Mat3 *F) { + DCHECK_EQ(x1.rows(), 2); + DCHECK_GE(x1.cols(), 8); + DCHECK_EQ(x1.rows(), x2.rows()); + DCHECK_EQ(x1.cols(), x2.cols()); + + int n = x1.cols(); + Mat A(n, 9); + for (int i = 0; i < n; ++i) { + A(i, 0) = x2(0, i) * x1(0, i); + A(i, 1) = x2(0, i) * x1(1, i); + A(i, 2) = x2(0, i); + A(i, 3) = x2(1, i) * x1(0, i); + A(i, 4) = x2(1, i) * x1(1, i); + A(i, 5) = x2(1, i); + A(i, 6) = x1(0, i); + A(i, 7) = x1(1, i); + A(i, 8) = 1; + } + + Vec9 f; + double smaller_singular_value = Nullspace(&A, &f); + *F = Map(f.data()); + return smaller_singular_value; +} + +// HZ 11.1.1 pag.280 +void EnforceFundamentalRank2Constraint(Mat3 *F) { + Eigen::JacobiSVD USV(*F, Eigen::ComputeFullU | Eigen::ComputeFullV); + Vec3 d = USV.singularValues(); + d(2) = 0.0; + *F = USV.matrixU() * d.asDiagonal() * USV.matrixV().transpose(); +} + +// HZ 11.2 pag.281 (x1 = x, x2 = x') +double NormalizedEightPointSolver(const Mat &x1, + const Mat &x2, + Mat3 *F) { + DCHECK_EQ(x1.rows(), 2); + DCHECK_GE(x1.cols(), 8); + DCHECK_EQ(x1.rows(), x2.rows()); + DCHECK_EQ(x1.cols(), x2.cols()); + + // Normalize the data. + Mat3 T1, T2; + PreconditionerFromPoints(x1, &T1); + PreconditionerFromPoints(x2, &T2); + Mat x1_normalized, x2_normalized; + ApplyTransformationToPoints(x1, T1, &x1_normalized); + ApplyTransformationToPoints(x2, T2, &x2_normalized); + + // Estimate the fundamental matrix. + double smaller_singular_value = + EightPointSolver(x1_normalized, x2_normalized, F); + EnforceFundamentalRank2Constraint(F); + + // Denormalize the fundamental matrix. + *F = T2.transpose() * (*F) * T1; + + return smaller_singular_value; +} + +// Seven-point algorithm. +// http://www.cs.unc.edu/~marc/tutorial/node55.html +double FundamentalFrom7CorrespondencesLinear(const Mat &x1, + const Mat &x2, + std::vector *F) { + DCHECK_EQ(x1.rows(), 2); + DCHECK_EQ(x1.cols(), 7); + DCHECK_EQ(x1.rows(), x2.rows()); + DCHECK_EQ(x2.cols(), x2.cols()); + + // Build a 9 x n matrix from point matches, where each row is equivalent to + // the equation x'T*F*x = 0 for a single correspondence pair (x', x). The + // domain of the matrix is a 9 element vector corresponding to F. The + // nullspace should be rank two; the two dimensions correspond to the set of + // F matrices satisfying the epipolar geometry. + Matrix A; + for (int ii = 0; ii < 7; ++ii) { + A(ii, 0) = x1(0, ii) * x2(0, ii); // 0 represents x coords, + A(ii, 1) = x1(1, ii) * x2(0, ii); // 1 represents y coords. + A(ii, 2) = x2(0, ii); + A(ii, 3) = x1(0, ii) * x2(1, ii); + A(ii, 4) = x1(1, ii) * x2(1, ii); + A(ii, 5) = x2(1, ii); + A(ii, 6) = x1(0, ii); + A(ii, 7) = x1(1, ii); + A(ii, 8) = 1.0; + } + + // Find the two F matrices in the nullspace of A. + Vec9 f1, f2; + double s = Nullspace2(&A, &f1, &f2); + Mat3 F1 = Map(f1.data()); + Mat3 F2 = Map(f2.data()); + + // Then, use the condition det(F) = 0 to determine F. In other words, solve + // det(F1 + a*F2) = 0 for a. + double a = F1(0, 0), j = F2(0, 0), + b = F1(0, 1), k = F2(0, 1), + c = F1(0, 2), l = F2(0, 2), + d = F1(1, 0), m = F2(1, 0), + e = F1(1, 1), n = F2(1, 1), + f = F1(1, 2), o = F2(1, 2), + g = F1(2, 0), p = F2(2, 0), + h = F1(2, 1), q = F2(2, 1), + i = F1(2, 2), r = F2(2, 2); + + // Run fundamental_7point_coeffs.py to get the below coefficients. + // The coefficients are in ascending powers of alpha, i.e. P[N]*x^N. + double P[4] = { + a*e*i + b*f*g + c*d*h - a*f*h - b*d*i - c*e*g, + a*e*r + a*i*n + b*f*p + b*g*o + c*d*q + c*h*m + d*h*l + e*i*j + f*g*k - + a*f*q - a*h*o - b*d*r - b*i*m - c*e*p - c*g*n - d*i*k - e*g*l - f*h*j, + a*n*r + b*o*p + c*m*q + d*l*q + e*j*r + f*k*p + g*k*o + h*l*m + i*j*n - + a*o*q - b*m*r - c*n*p - d*k*r - e*l*p - f*j*q - g*l*n - h*j*o - i*k*m, + j*n*r + k*o*p + l*m*q - j*o*q - k*m*r - l*n*p, + }; + + // Solve for the roots of P[3]*x^3 + P[2]*x^2 + P[1]*x + P[0] = 0. + double roots[3]; + int num_roots = SolveCubicPolynomial(P, roots); + + // Build the fundamental matrix for each solution. + for (int kk = 0; kk < num_roots; ++kk) { + F->push_back(F1 + roots[kk] * F2); + } + return s; +} + +double FundamentalFromCorrespondences7Point(const Mat &x1, + const Mat &x2, + std::vector *F) { + DCHECK_EQ(x1.rows(), 2); + DCHECK_GE(x1.cols(), 7); + DCHECK_EQ(x1.rows(), x2.rows()); + DCHECK_EQ(x1.cols(), x2.cols()); + + // Normalize the data. + Mat3 T1, T2; + PreconditionerFromPoints(x1, &T1); + PreconditionerFromPoints(x2, &T2); + Mat x1_normalized, x2_normalized; + ApplyTransformationToPoints(x1, T1, &x1_normalized); + ApplyTransformationToPoints(x2, T2, &x2_normalized); + + // Estimate the fundamental matrix. + double smaller_singular_value = + FundamentalFrom7CorrespondencesLinear(x1_normalized, x2_normalized, &(*F)); + + for (int k = 0; k < F->size(); ++k) { + Mat3 & Fmat = (*F)[k]; + // Denormalize the fundamental matrix. + Fmat = T2.transpose() * Fmat * T1; + } + return smaller_singular_value; +} + +void NormalizeFundamental(const Mat3 &F, Mat3 *F_normalized) { + *F_normalized = F / FrobeniusNorm(F); + if ((*F_normalized)(2, 2) < 0) { + *F_normalized *= -1; + } +} + +double SampsonDistance(const Mat &F, const Vec2 &x1, const Vec2 &x2) { + Vec3 x(x1(0), x1(1), 1.0); + Vec3 y(x2(0), x2(1), 1.0); + + Vec3 F_x = F * x; + Vec3 Ft_y = F.transpose() * y; + double y_F_x = y.dot(F_x); + + return Square(y_F_x) / ( F_x.head<2>().squaredNorm() + + Ft_y.head<2>().squaredNorm()); +} + +double SymmetricEpipolarDistance(const Mat &F, const Vec2 &x1, const Vec2 &x2) { + Vec3 x(x1(0), x1(1), 1.0); + Vec3 y(x2(0), x2(1), 1.0); + + Vec3 F_x = F * x; + Vec3 Ft_y = F.transpose() * y; + double y_F_x = y.dot(F_x); + + return Square(y_F_x) * ( 1 / F_x.head<2>().squaredNorm() + + 1 / Ft_y.head<2>().squaredNorm()); +} + +// HZ 9.6 pag 257 (formula 9.12) +void EssentialFromFundamental(const Mat3 &F, + const Mat3 &K1, + const Mat3 &K2, + Mat3 *E) { + *E = K2.transpose() * F * K1; +} + +// HZ 9.6 pag 257 (formula 9.12) +// Or http://ai.stanford.edu/~birch/projective/node20.html +void FundamentalFromEssential(const Mat3 &E, + const Mat3 &K1, + const Mat3 &K2, + Mat3 *F) { + *F = K2.inverse().transpose() * E * K1.inverse(); +} + +void RelativeCameraMotion(const Mat3 &R1, + const Vec3 &t1, + const Mat3 &R2, + const Vec3 &t2, + Mat3 *R, + Vec3 *t) { + *R = R2 * R1.transpose(); + *t = t2 - (*R) * t1; +} + +// HZ 9.6 pag 257 +void EssentialFromRt(const Mat3 &R1, + const Vec3 &t1, + const Mat3 &R2, + const Vec3 &t2, + Mat3 *E) { + Mat3 R; + Vec3 t; + RelativeCameraMotion(R1, t1, R2, t2, &R, &t); + Mat3 Tx = CrossProductMatrix(t); + *E = Tx * R; +} + +// HZ 9.6 pag 259 (Result 9.19) +void MotionFromEssential(const Mat3 &E, + std::vector *Rs, + std::vector *ts) { + Eigen::JacobiSVD USV(E, Eigen::ComputeFullU | Eigen::ComputeFullV); + Mat3 U = USV.matrixU(); + Mat3 Vt = USV.matrixV().transpose(); + + // Last column of U is undetermined since d = (a a 0). + if (U.determinant() < 0) { + U.col(2) *= -1; + } + // Last row of Vt is undetermined since d = (a a 0). + if (Vt.determinant() < 0) { + Vt.row(2) *= -1; + } + + Mat3 W; + W << 0, -1, 0, + 1, 0, 0, + 0, 0, 1; + + Mat3 U_W_Vt = U * W * Vt; + Mat3 U_Wt_Vt = U * W.transpose() * Vt; + + Rs->resize(4); + (*Rs)[0] = U_W_Vt; + (*Rs)[1] = U_W_Vt; + (*Rs)[2] = U_Wt_Vt; + (*Rs)[3] = U_Wt_Vt; + + ts->resize(4); + (*ts)[0] = U.col(2); + (*ts)[1] = -U.col(2); + (*ts)[2] = U.col(2); + (*ts)[3] = -U.col(2); +} + +int MotionFromEssentialChooseSolution(const std::vector &Rs, + const std::vector &ts, + const Mat3 &K1, + const Vec2 &x1, + const Mat3 &K2, + const Vec2 &x2) { + DCHECK_EQ(4, Rs.size()); + DCHECK_EQ(4, ts.size()); + + Mat34 P1, P2; + Mat3 R1; + Vec3 t1; + R1.setIdentity(); + t1.setZero(); + P_From_KRt(K1, R1, t1, &P1); + for (int i = 0; i < 4; ++i) { + const Mat3 &R2 = Rs[i]; + const Vec3 &t2 = ts[i]; + P_From_KRt(K2, R2, t2, &P2); + Vec3 X; + TriangulateDLT(P1, x1, P2, x2, &X); + double d1 = Depth(R1, t1, X); + double d2 = Depth(R2, t2, X); + // Test if point is front to the two cameras. + if (d1 > 0 && d2 > 0) { + return i; + } + } + return -1; +} + +bool MotionFromEssentialAndCorrespondence(const Mat3 &E, + const Mat3 &K1, + const Vec2 &x1, + const Mat3 &K2, + const Vec2 &x2, + Mat3 *R, + Vec3 *t) { + std::vector Rs; + std::vector ts; + MotionFromEssential(E, &Rs, &ts); + int solution = MotionFromEssentialChooseSolution(Rs, ts, K1, x1, K2, x2); + if (solution >= 0) { + *R = Rs[solution]; + *t = ts[solution]; + return true; + } else { + return false; + } +} + +void FundamentalToEssential(const Mat3 &F, Mat3 *E) { + Eigen::JacobiSVD svd(F, Eigen::ComputeFullU | Eigen::ComputeFullV); + + // See Hartley & Zisserman page 294, result 11.1, which shows how to get the + // closest essential matrix to a matrix that is "almost" an essential matrix. + double a = svd.singularValues()(0); + double b = svd.singularValues()(1); + double s = (a + b) / 2.0; + + LG << "Initial reconstruction's rotation is non-euclidean by " + << (((a - b) / std::max(a, b)) * 100) << "%; singular values:" + << svd.singularValues().transpose(); + + Vec3 diag; + diag << s, s, 0; + + *E = svd.matrixU() * diag.asDiagonal() * svd.matrixV().transpose(); +} + +// Default settings for fundamental estimation which should be suitable +// for a wide range of use cases. +EstimateFundamentalOptions::EstimateFundamentalOptions(void) : + max_num_iterations(50), + expected_average_symmetric_distance(1e-16) { +} + +namespace { +// Cost functor which computes symmetric epipolar distance +// used for fundamental matrix refinement. +class FundamentalSymmetricEpipolarCostFunctor { + public: + FundamentalSymmetricEpipolarCostFunctor(const Vec2 &x, + const Vec2 &y) + : x_(x), y_(y) {} + + template + bool operator()(const T *fundamental_parameters, T *residuals) const { + typedef Eigen::Matrix Mat3; + typedef Eigen::Matrix Vec3; + + Mat3 F(fundamental_parameters); + + Vec3 x(T(x_(0)), T(x_(1)), T(1.0)); + Vec3 y(T(y_(0)), T(y_(1)), T(1.0)); + + Vec3 F_x = F * x; + Vec3 Ft_y = F.transpose() * y; + T y_F_x = y.dot(F_x); + + residuals[0] = y_F_x * T(1) / F_x.head(2).norm(); + residuals[1] = y_F_x * T(1) / Ft_y.head(2).norm(); + + return true; + } + + const Mat x_; + const Mat y_; +}; + +#if CERES_FOUND +// Termination checking callback used for fundamental estimation. +// It finished the minimization as soon as actual average of +// symmetric epipolar distance is less or equal to the expected +// average value. +class TerminationCheckingCallback : public ceres::IterationCallback { + public: + TerminationCheckingCallback(const Mat &x1, const Mat &x2, + const EstimateFundamentalOptions &options, + Mat3 *F) + : options_(options), x1_(x1), x2_(x2), F_(F) {} + + virtual ceres::CallbackReturnType operator()( + const ceres::IterationSummary& summary) { + // If the step wasn't successful, there's nothing to do. + if (!summary.step_is_successful) { + return ceres::SOLVER_CONTINUE; + } + + // Calculate average of symmetric epipolar distance. + double average_distance = 0.0; + for (int i = 0; i < x1_.cols(); i++) { + average_distance = SymmetricEpipolarDistance(*F_, + x1_.col(i), + x2_.col(i)); + } + average_distance /= x1_.cols(); + + if (average_distance <= options_.expected_average_symmetric_distance) { + return ceres::SOLVER_TERMINATE_SUCCESSFULLY; + } + + return ceres::SOLVER_CONTINUE; + } + + private: + const EstimateFundamentalOptions &options_; + const Mat &x1_; + const Mat &x2_; + Mat3 *F_; +}; +#endif // CERES_FOUND +} // namespace + +/* Fundamental transformation estimation. */ +bool EstimateFundamentalFromCorrespondences( + const Mat &x1, + const Mat &x2, + const EstimateFundamentalOptions &options, + Mat3 *F) { + // Step 1: Algebraic fundamental estimation. + + // Assume algebraic estiation always succeeds, + NormalizedEightPointSolver(x1, x2, F); + +#if CERES_FOUND + LG << "Estimated matrix after algebraic estimation:\n" << *F; + + // Step 2: Refine matrix using Ceres minimizer. + ceres::Problem problem; + for (int i = 0; i < x1.cols(); i++) { + FundamentalSymmetricEpipolarCostFunctor + *fundamental_symmetric_epipolar_cost_function = + new FundamentalSymmetricEpipolarCostFunctor(x1.col(i), + x2.col(i)); + + problem.AddResidualBlock( + new ceres::AutoDiffCostFunction< + FundamentalSymmetricEpipolarCostFunctor, + 2, // num_residuals + 9>(fundamental_symmetric_epipolar_cost_function), + NULL, + F->data()); + } + + // Configure the solve. + ceres::Solver::Options solver_options; + solver_options.linear_solver_type = ceres::DENSE_QR; + solver_options.max_num_iterations = options.max_num_iterations; + solver_options.update_state_every_iteration = true; + + // Terminate if the average symmetric distance is good enough. + TerminationCheckingCallback callback(x1, x2, options, F); + solver_options.callbacks.push_back(&callback); + + // Run the solve. + ceres::Solver::Summary summary; + ceres::Solve(solver_options, &problem, &summary); + + VLOG(1) << "Summary:\n" << summary.FullReport(); + + LG << "Final refined matrix:\n" << *F; + + return summary.IsSolutionUsable(); +#endif // CERES_FOUND + return true; +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/fundamental.h b/modules/sfm/src/libmv_light/libmv/multiview/fundamental.h new file mode 100644 index 00000000000..51067aefc2b --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/fundamental.h @@ -0,0 +1,187 @@ +// Copyright (c) 2007, 2008, 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_FUNDAMENTAL_H_ +#define LIBMV_MULTIVIEW_FUNDAMENTAL_H_ + +#include + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +void ProjectionsFromFundamental(const Mat3 &F, Mat34 *P1, Mat34 *P2); +void FundamentalFromProjections(const Mat34 &P1, const Mat34 &P2, Mat3 *F); + +/** + * The normalized 8-point fundamental matrix solver. + */ +double NormalizedEightPointSolver(const Mat &x1, + const Mat &x2, + Mat3 *F); + +/** + * 7 points (minimal case, points coordinates must be normalized before): + */ +double FundamentalFrom7CorrespondencesLinear(const Mat &x1, + const Mat &x2, + std::vector *F); + +/** + * 7 points (points coordinates must be in image space): + */ +double FundamentalFromCorrespondences7Point(const Mat &x1, + const Mat &x2, + std::vector *F); + +/** + * 8 points (points coordinates must be in image space): + */ +double NormalizedEightPointSolver(const Mat &x1, + const Mat &x2, + Mat3 *F); + +/** + * Fundamental matrix utility function: + */ +void EnforceFundamentalRank2Constraint(Mat3 *F); + +void NormalizeFundamental(const Mat3 &F, Mat3 *F_normalized); + +/** + * Approximate squared reprojection errror. + * + * See page 287 of HZ equation 11.9. This avoids triangulating the point, + * relying only on the entries in F. + */ +double SampsonDistance(const Mat &F, const Vec2 &x1, const Vec2 &x2); + +/** + * Calculates the sum of the distances from the points to the epipolar lines. + * + * See page 288 of HZ equation 11.10. + */ +double SymmetricEpipolarDistance(const Mat &F, const Vec2 &x1, const Vec2 &x2); + +/** + * Compute the relative camera motion between two cameras. + * + * Given the motion parameters of two cameras, computes the motion parameters + * of the second one assuming the first one to be at the origin. + * If T1 and T2 are the camera motions, the computed relative motion is + * T = T2 T1^{-1} + */ +void RelativeCameraMotion(const Mat3 &R1, + const Vec3 &t1, + const Mat3 &R2, + const Vec3 &t2, + Mat3 *R, + Vec3 *t); + +void EssentialFromFundamental(const Mat3 &F, + const Mat3 &K1, + const Mat3 &K2, + Mat3 *E); + +void FundamentalFromEssential(const Mat3 &E, + const Mat3 &K1, + const Mat3 &K2, + Mat3 *F); + +void EssentialFromRt(const Mat3 &R1, + const Vec3 &t1, + const Mat3 &R2, + const Vec3 &t2, + Mat3 *E); + +void MotionFromEssential(const Mat3 &E, + std::vector *Rs, + std::vector *ts); + +/** + * Choose one of the four possible motion solutions from an essential matrix. + * + * Decides the right solution by checking that the triangulation of a match + * x1--x2 lies in front of the cameras. See HZ 9.6 pag 259 (9.6.3 Geometrical + * interpretation of the 4 solutions) + * + * \return index of the right solution or -1 if no solution. + */ +int MotionFromEssentialChooseSolution(const std::vector &Rs, + const std::vector &ts, + const Mat3 &K1, + const Vec2 &x1, + const Mat3 &K2, + const Vec2 &x2); + +bool MotionFromEssentialAndCorrespondence(const Mat3 &E, + const Mat3 &K1, + const Vec2 &x1, + const Mat3 &K2, + const Vec2 &x2, + Mat3 *R, + Vec3 *t); + +/** + * Find closest essential matrix E to fundamental F + */ +void FundamentalToEssential(const Mat3 &F, Mat3 *E); + +/** + * This structure contains options that controls how the fundamental + * estimation operates. + * + * Defaults should be suitable for a wide range of use cases, but + * better performance and accuracy might require tweaking/ + */ +struct EstimateFundamentalOptions { + // Default constructor which sets up a options for generic usage. + EstimateFundamentalOptions(void); + + // Maximal number of iterations for refinement step. + int max_num_iterations; + + // Expected average of symmetric epipolar distance between + // actual destination points and original ones transformed by + // estimated fundamental matrix. + // + // Refinement will finish as soon as average of symmetric + // epipolar distance is less or equal to this value. + // + // This distance is measured in the same units as input points are. + double expected_average_symmetric_distance; +}; + +/** + * Fundamental transformation estimation. + * + * This function estimates the fundamental transformation from a list of 2D + * correspondences by doing algebraic estimation first followed with result + * refinement. + */ +bool EstimateFundamentalFromCorrespondences( + const Mat &x1, + const Mat &x2, + const EstimateFundamentalOptions &options, + Mat3 *F); + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_FUNDAMENTAL_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/fundamental_kernel.cc b/modules/sfm/src/libmv_light/libmv/multiview/fundamental_kernel.cc new file mode 100644 index 00000000000..df130b50641 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/fundamental_kernel.cc @@ -0,0 +1,110 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include + +// TODO(keir): This code is plain unfinished! Doesn't even compile! + +#include "libmv/base/vector.h" +#include "libmv/multiview/fundamental_kernel.h" +#include "libmv/numeric/numeric.h" +#include "libmv/numeric/poly.h" +#include "libmv/logging/logging.h" + +namespace libmv { +namespace fundamental { +namespace kernel { + +void SevenPointSolver::Solve(const Mat &x1, const Mat &x2, vector *F) { + assert(2 == x1.rows()); + assert(7 <= x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + + // Set up the homogeneous system Af = 0 from the equations x'T*F*x = 0. + MatX9 A(x1.cols(), 9); + EncodeEpipolarEquation(x1, x2, &A); + + // Find the two F matrices in the nullspace of A. + Vec9 f1, f2; + Nullspace2(&A, &f1, &f2); + Mat3 F1 = Map(f1.data()); + Mat3 F2 = Map(f2.data()); + + // Then, use the condition det(F) = 0 to determine F. In other words, solve + // det(F1 + a*F2) = 0 for a. + double a = F1(0, 0), j = F2(0, 0), + b = F1(0, 1), k = F2(0, 1), + c = F1(0, 2), l = F2(0, 2), + d = F1(1, 0), m = F2(1, 0), + e = F1(1, 1), n = F2(1, 1), + f = F1(1, 2), o = F2(1, 2), + g = F1(2, 0), p = F2(2, 0), + h = F1(2, 1), q = F2(2, 1), + i = F1(2, 2), r = F2(2, 2); + + // Run fundamental_7point_coeffs.py to get the below coefficients. + // The coefficients are in ascending powers of alpha, i.e. P[N]*x^N. + double P[4] = { + a*e*i + b*f*g + c*d*h - a*f*h - b*d*i - c*e*g, + a*e*r + a*i*n + b*f*p + b*g*o + c*d*q + c*h*m + d*h*l + e*i*j + f*g*k - + a*f*q - a*h*o - b*d*r - b*i*m - c*e*p - c*g*n - d*i*k - e*g*l - f*h*j, + a*n*r + b*o*p + c*m*q + d*l*q + e*j*r + f*k*p + g*k*o + h*l*m + i*j*n - + a*o*q - b*m*r - c*n*p - d*k*r - e*l*p - f*j*q - g*l*n - h*j*o - i*k*m, + j*n*r + k*o*p + l*m*q - j*o*q - k*m*r - l*n*p, + }; + + // Solve for the roots of P[3]*x^3 + P[2]*x^2 + P[1]*x + P[0] = 0. + double roots[3]; + int num_roots = SolveCubicPolynomial(P, roots); + + // Build the fundamental matrix for each solution. + for (int kk = 0; kk < num_roots; ++kk) { + F->push_back(F1 + roots[kk] * F2); + } +} + +//template +void EightPointSolver::Solve(const Mat &x1, const Mat &x2, vector *Fs) { + assert(2 == x1.rows()); + assert(8 <= x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + + MatX9 A(x1.cols(), 9); + EncodeEpipolarEquation(x1, x2, &A); + + Vec9 f; + Nullspace(&A, &f); + Mat3 F = Map(f.data()); + + // Force the fundamental property if the A matrix has full rank. + if (x1.cols() > 8) { + Eigen::JacobiSVD USV(F, Eigen::ComputeFullU | Eigen::ComputeFullV); + Vec3 d = USV.singularValues(); + d[2] = 0.0; + F = USV.matrixU() * d.asDiagonal() * USV.matrixV().transpose(); + } + Fs->push_back(F); +} + +} // namespace kernel +} // namespace fundamental +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/fundamental_kernel.h b/modules/sfm/src/libmv_light/libmv/multiview/fundamental_kernel.h new file mode 100644 index 00000000000..5fd40516115 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/fundamental_kernel.h @@ -0,0 +1,148 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +// TODO(keir): This code is plain unfinished! Doesn't even compile! + +#ifndef LIBMV_MULTIVIEW_FUNDAMENTAL_KERNEL_H_ +#define LIBMV_MULTIVIEW_FUNDAMENTAL_KERNEL_H_ + +#include "libmv/base/vector.h" +#include "libmv/multiview/conditioning.h" +#include "libmv/multiview/two_view_kernel.h" +#include "libmv/numeric/numeric.h" +#include "libmv/logging/logging.h" + +namespace libmv { +namespace fundamental { +namespace kernel { + +// TODO(keir): Templatize error functions to work with autodiff (only F). +struct SampsonError { + static double Error(const Mat3 &F, const Vec2 &x1, const Vec2 &x2) { + Vec3 x(x1(0), x1(1), 1.0); + Vec3 y(x2(0), x2(1), 1.0); + // See page 287 equation (11.9) of HZ. + Vec3 F_x = F * x; + Vec3 Ft_y = F.transpose() * y; + return Square(y.dot(F_x)) / ( F_x.head<2>().squaredNorm() + + Ft_y.head<2>().squaredNorm()); + } +}; + +struct SymmetricEpipolarDistanceError { + static double Error(const Mat3 &F, const Vec2 &x1, const Vec2 &x2) { + Vec3 x(x1(0), x1(1), 1.0); + Vec3 y(x2(0), x2(1), 1.0); + // See page 288 equation (11.10) of HZ. + Vec3 F_x = F * x; + Vec3 Ft_y = F.transpose() * y; + return Square(y.dot(F_x)) * ( 1 / F_x.head<2>().squaredNorm() + + 1 / Ft_y.head<2>().squaredNorm()) + / 4.0; // The divide by 4 is to make this match the sampson distance. + } +}; + +/** + * Seven-point algorithm for solving for the fundamental matrix from point + * correspondences. See page 281 in HZ, though oddly they use a different + * equation: \f$det(\alpha F_1 + (1-\alpha)F_2) = 0\f$. Since \f$F_1\f$ and + * \f$F2\f$ are projective, there's no need to balance the relative scale. + * Instead, here, the simpler equation is solved: \f$det(F_1 + \alpha F_2) = + * 0\f$. + * + * \see http://www.cs.unc.edu/~marc/tutorial/node55.html + */ +struct SevenPointSolver { + enum { MINIMUM_SAMPLES = 7 }; + static void Solve(const Mat &x1, const Mat &x2, vector *F); +}; + +struct EightPointSolver { + enum { MINIMUM_SAMPLES = 8 }; + static void Solve(const Mat &x1, const Mat &x2, vector *Fs); +}; + +typedef two_view::kernel::Kernel + SevenPointKernel; + +typedef two_view::kernel::Kernel + EightPointKernel; + +typedef two_view::kernel::Kernel< + two_view::kernel::NormalizedSolver, + SampsonError, + Mat3> + NormalizedSevenPointKernel; + +typedef two_view::kernel::Kernel< + two_view::kernel::NormalizedSolver, + SampsonError, + Mat3> + NormalizedEightPointKernel; + +// Set the default kernel to normalized 7 point, because it is the fastest (in +// a robust estimation context) and most robust of the above kernels. +typedef NormalizedSevenPointKernel Kernel; + +// TODO(keir): Convert this to a solver that enforces the essential +// constraints; in particular det(F) = 0 and the two nonzero singular values +// are equal. +typedef two_view::kernel::Kernel + EssentialKernel; + +/** + * Build a 9 x n matrix from point matches, where each row is equivalent to the + * equation x'T*F*x = 0 for a single correspondence pair (x', x). The domain of + * the matrix is a 9 element vector corresponding to F. In other words, set up + * the linear system + * + * Af = 0, + * + * where f is the F matrix as a 9-vector rather than a 3x3 matrix (row + * major). If the points are well conditioned and there are 8 or more, then + * the nullspace should be rank one. If the nullspace is two dimensional, + * then the rank 2 constraint must be enforced to identify the appropriate F + * matrix. + * + * Note that this does not resize the matrix A; it is expected to have the + * appropriate size already. + */ +template +inline void EncodeEpipolarEquation(const TMatX &x1, const TMatX &x2, TMatA *A) { + for (int i = 0; i < x1.cols(); ++i) { + (*A)(i, 0) = x2(0, i) * x1(0, i); // 0 represents x coords, + (*A)(i, 1) = x2(0, i) * x1(1, i); // 1 represents y coords. + (*A)(i, 2) = x2(0, i); + (*A)(i, 3) = x2(1, i) * x1(0, i); + (*A)(i, 4) = x2(1, i) * x1(1, i); + (*A)(i, 5) = x2(1, i); + (*A)(i, 6) = x1(0, i); + (*A)(i, 7) = x1(1, i); + (*A)(i, 8) = 1.0; + } +} + +} // namespace kernel +} // namespace fundamental +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_FUNDAMENTAL_KERNEL_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/homography.cc b/modules/sfm/src/libmv_light/libmv/multiview/homography.cc new file mode 100644 index 00000000000..9a9d22b43d7 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/homography.cc @@ -0,0 +1,477 @@ +// Copyright (c) 2008, 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/multiview/homography.h" + +#if CERES_FOUND +#include "ceres/ceres.h" +#endif +#include "libmv/logging/logging.h" +#include "libmv/multiview/conditioning.h" +#include "libmv/multiview/homography_parameterization.h" + +namespace libmv { +/** 2D Homography transformation estimation in the case that points are in + * euclidean coordinates. + * + * x = H y + * x and y vector must have the same direction, we could write + * crossproduct(|x|, * H * |y| ) = |0| + * + * | 0 -1 x2| |a b c| |y1| |0| + * | 1 0 -x1| * |d e f| * |y2| = |0| + * |-x2 x1 0| |g h 1| |1 | |0| + * + * That gives : + * + * (-d+x2*g)*y1 + (-e+x2*h)*y2 + -f+x2 |0| + * (a-x1*g)*y1 + (b-x1*h)*y2 + c-x1 = |0| + * (-x2*a+x1*d)*y1 + (-x2*b+x1*e)*y2 + -x2*c+x1*f |0| + */ +static bool Homography2DFromCorrespondencesLinearEuc( + const Mat &x1, + const Mat &x2, + Mat3 *H, + double expected_precision) { + assert(2 == x1.rows()); + assert(4 <= x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + + int n = x1.cols(); + MatX8 L = Mat::Zero(n * 3, 8); + Mat b = Mat::Zero(n * 3, 1); + for (int i = 0; i < n; ++i) { + int j = 3 * i; + L(j, 0) = x1(0, i); // a + L(j, 1) = x1(1, i); // b + L(j, 2) = 1.0; // c + L(j, 6) = -x2(0, i) * x1(0, i); // g + L(j, 7) = -x2(0, i) * x1(1, i); // h + b(j, 0) = x2(0, i); // i + + ++j; + L(j, 3) = x1(0, i); // d + L(j, 4) = x1(1, i); // e + L(j, 5) = 1.0; // f + L(j, 6) = -x2(1, i) * x1(0, i); // g + L(j, 7) = -x2(1, i) * x1(1, i); // h + b(j, 0) = x2(1, i); // i + + // This ensures better stability + // TODO(julien) make a lite version without this 3rd set + ++j; + L(j, 0) = x2(1, i) * x1(0, i); // a + L(j, 1) = x2(1, i) * x1(1, i); // b + L(j, 2) = x2(1, i); // c + L(j, 3) = -x2(0, i) * x1(0, i); // d + L(j, 4) = -x2(0, i) * x1(1, i); // e + L(j, 5) = -x2(0, i); // f + } + // Solve Lx=B + Vec h = L.fullPivLu().solve(b); + Homography2DNormalizedParameterization::To(h, H); + if ((L * h).isApprox(b, expected_precision)) { + return true; + } else { + return false; + } +} + +/** 2D Homography transformation estimation in the case that points are in + * homogeneous coordinates. + * + * | 0 -x3 x2| |a b c| |y1| -x3*d+x2*g -x3*e+x2*h -x3*f+x2*1 |y1| (-x3*d+x2*g)*y1 (-x3*e+x2*h)*y2 (-x3*f+x2*1)*y3 |0| + * | x3 0 -x1| * |d e f| * |y2| = x3*a-x1*g x3*b-x1*h x3*c-x1*1 * |y2| = (x3*a-x1*g)*y1 (x3*b-x1*h)*y2 (x3*c-x1*1)*y3 = |0| + * |-x2 x1 0| |g h 1| |y3| -x2*a+x1*d -x2*b+x1*e -x2*c+x1*f |y3| (-x2*a+x1*d)*y1 (-x2*b+x1*e)*y2 (-x2*c+x1*f)*y3 |0| + * X = |a b c d e f g h|^t + */ +bool Homography2DFromCorrespondencesLinear(const Mat &x1, + const Mat &x2, + Mat3 *H, + double expected_precision) { + if (x1.rows() == 2) { + return Homography2DFromCorrespondencesLinearEuc(x1, x2, H, + expected_precision); + } + assert(3 == x1.rows()); + assert(4 <= x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + + const int x = 0; + const int y = 1; + const int w = 2; + int n = x1.cols(); + MatX8 L = Mat::Zero(n * 3, 8); + Mat b = Mat::Zero(n * 3, 1); + for (int i = 0; i < n; ++i) { + int j = 3 * i; + L(j, 0) = x2(w, i) * x1(x, i); // a + L(j, 1) = x2(w, i) * x1(y, i); // b + L(j, 2) = x2(w, i) * x1(w, i); // c + L(j, 6) = -x2(x, i) * x1(x, i); // g + L(j, 7) = -x2(x, i) * x1(y, i); // h + b(j, 0) = x2(x, i) * x1(w, i); + + ++j; + L(j, 3) = x2(w, i) * x1(x, i); // d + L(j, 4) = x2(w, i) * x1(y, i); // e + L(j, 5) = x2(w, i) * x1(w, i); // f + L(j, 6) = -x2(y, i) * x1(x, i); // g + L(j, 7) = -x2(y, i) * x1(y, i); // h + b(j, 0) = x2(y, i) * x1(w, i); + + // This ensures better stability + ++j; + L(j, 0) = x2(y, i) * x1(x, i); // a + L(j, 1) = x2(y, i) * x1(y, i); // b + L(j, 2) = x2(y, i) * x1(w, i); // c + L(j, 3) = -x2(x, i) * x1(x, i); // d + L(j, 4) = -x2(x, i) * x1(y, i); // e + L(j, 5) = -x2(x, i) * x1(w, i); // f + } + // Solve Lx=B + Vec h = L.fullPivLu().solve(b); + if ((L * h).isApprox(b, expected_precision)) { + Homography2DNormalizedParameterization::To(h, H); + return true; + } else { + return false; + } +} + +// Default settings for homography estimation which should be suitable +// for a wide range of use cases. +EstimateHomographyOptions::EstimateHomographyOptions(void) : + use_normalization(true), + max_num_iterations(50), + expected_average_symmetric_distance(1e-16) { +} + +namespace { +void GetNormalizedPoints(const Mat &original_points, + Mat *normalized_points, + Mat3 *normalization_matrix) { + IsotropicPreconditionerFromPoints(original_points, normalization_matrix); + ApplyTransformationToPoints(original_points, + *normalization_matrix, + normalized_points); +} + +// Cost functor which computes symmetric geometric distance +// used for homography matrix refinement. +class HomographySymmetricGeometricCostFunctor { + public: + HomographySymmetricGeometricCostFunctor(const Vec2 &x, + const Vec2 &y) { + xx_ = x(0); + xy_ = x(1); + yx_ = y(0); + yy_ = y(1); + } + + template + bool operator()(const T *homography_parameters, T *residuals) const { + typedef Eigen::Matrix Mat3; + typedef Eigen::Matrix Vec3; + + Mat3 H(homography_parameters); + + Vec3 x(T(xx_), T(xy_), T(1.0)); + Vec3 y(T(yx_), T(yy_), T(1.0)); + + Vec3 H_x = H * x; + Vec3 Hinv_y = H.inverse() * y; + + H_x /= H_x(2); + Hinv_y /= Hinv_y(2); + + // This is a forward error. + residuals[0] = H_x(0) - T(yx_); + residuals[1] = H_x(1) - T(yy_); + + // This is a backward error. + residuals[2] = Hinv_y(0) - T(xx_); + residuals[3] = Hinv_y(1) - T(xy_); + + return true; + } + + // TODO(sergey): Think of better naming. + double xx_, xy_; + double yx_, yy_; +}; + +#if CERES_FOUND +// Termination checking callback used for homography estimation. +// It finished the minimization as soon as actual average of +// symmetric geometric distance is less or equal to the expected +// average value. +class TerminationCheckingCallback : public ceres::IterationCallback { + public: + TerminationCheckingCallback(const Mat &x1, const Mat &x2, + const EstimateHomographyOptions &options, + Mat3 *H) + : options_(options), x1_(x1), x2_(x2), H_(H) {} + + virtual ceres::CallbackReturnType operator()( + const ceres::IterationSummary& summary) { + // If the step wasn't successful, there's nothing to do. + if (!summary.step_is_successful) { + return ceres::SOLVER_CONTINUE; + } + + // Calculate average of symmetric geometric distance. + double average_distance = 0.0; + for (int i = 0; i < x1_.cols(); i++) { + average_distance = SymmetricGeometricDistance(*H_, + x1_.col(i), + x2_.col(i)); + } + average_distance /= x1_.cols(); + + if (average_distance <= options_.expected_average_symmetric_distance) { + return ceres::SOLVER_TERMINATE_SUCCESSFULLY; + } + + return ceres::SOLVER_CONTINUE; + } + + private: + const EstimateHomographyOptions &options_; + const Mat &x1_; + const Mat &x2_; + Mat3 *H_; +}; +#endif // CERES_FOUND +} // namespace + +/** 2D Homography transformation estimation in the case that points are in + * euclidean coordinates. + */ +bool EstimateHomography2DFromCorrespondences( + const Mat &x1, + const Mat &x2, + const EstimateHomographyOptions &options, + Mat3 *H) { + // TODO(sergey): Support homogenous coordinates, not just euclidean. + + assert(2 == x1.rows()); + assert(4 <= x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + + Mat3 T1 = Mat3::Identity(), + T2 = Mat3::Identity(); + + // Step 1: Algebraic homography estimation. + Mat x1_normalized, x2_normalized; + + if (options.use_normalization) { + LG << "Estimating homography using normalization."; + GetNormalizedPoints(x1, &x1_normalized, &T1); + GetNormalizedPoints(x2, &x2_normalized, &T2); + } else { + x1_normalized = x1; + x2_normalized = x2; + } + + // Assume algebraic estiation always suceeds, + Homography2DFromCorrespondencesLinear(x1_normalized, x2_normalized, H); + + // Denormalize the homography matrix. + if (options.use_normalization) { + *H = T2.inverse() * (*H) * T1; + } + + LG << "Estimated matrix after algebraic estimation:\n" << *H; + +#if CERES_FOUND + // Step 2: Refine matrix using Ceres minimizer. + ceres::Problem problem; + for (int i = 0; i < x1.cols(); i++) { + HomographySymmetricGeometricCostFunctor + *homography_symmetric_geometric_cost_function = + new HomographySymmetricGeometricCostFunctor(x1.col(i), + x2.col(i)); + + problem.AddResidualBlock( + new ceres::AutoDiffCostFunction< + HomographySymmetricGeometricCostFunctor, + 4, // num_residuals + 9>(homography_symmetric_geometric_cost_function), + NULL, + H->data()); + } + + // Configure the solve. + ceres::Solver::Options solver_options; + solver_options.linear_solver_type = ceres::DENSE_QR; + solver_options.max_num_iterations = options.max_num_iterations; + solver_options.update_state_every_iteration = true; + + // Terminate if the average symmetric distance is good enough. + TerminationCheckingCallback callback(x1, x2, options, H); + solver_options.callbacks.push_back(&callback); + + // Run the solve. + ceres::Solver::Summary summary; + ceres::Solve(solver_options, &problem, &summary); + + VLOG(1) << "Summary:\n" << summary.FullReport(); + + LG << "Final refined matrix:\n" << *H; + + return summary.IsSolutionUsable(); +#endif // CERES_FOUND + return true; +} + +/** + * x2 ~ A * x1 + * x2^t * Hi * A *x1 = 0 + * H1 = H2 = H3 = + * | 0 0 0 1| |-x2w| |0 0 0 0| | 0 | | 0 0 1 0| |-x2z| + * | 0 0 0 0| -> | 0 | |0 0 1 0| -> |-x2z| | 0 0 0 0| -> | 0 | + * | 0 0 0 0| | 0 | |0-1 0 0| | x2y| |-1 0 0 0| | x2x| + * |-1 0 0 0| | x2x| |0 0 0 0| | 0 | | 0 0 0 0| | 0 | + * H4 = H5 = H6 = + * |0 0 0 0| | 0 | | 0 1 0 0| |-x2y| |0 0 0 0| | 0 | + * |0 0 0 1| -> |-x2w| |-1 0 0 0| -> | x2x| |0 0 0 0| -> | 0 | + * |0 0 0 0| | 0 | | 0 0 0 0| | 0 | |0 0 0 1| |-x2w| + * |0-1 0 0| | x2y| | 0 0 0 0| | 0 | |0 0-1 0| | x2z| + * |a b c d| + * A = |e f g h| + * |i j k l| + * |m n o 1| + * + * x2^t * H1 * A *x1 = (-x2w*a +x2x*m )*x1x + (-x2w*b +x2x*n )*x1y + (-x2w*c +x2x*o )*x1z + (-x2w*d +x2x*1 )*x1w = 0 + * x2^t * H2 * A *x1 = (-x2z*e +x2y*i )*x1x + (-x2z*f +x2y*j )*x1y + (-x2z*g +x2y*k )*x1z + (-x2z*h +x2y*l )*x1w = 0 + * x2^t * H3 * A *x1 = (-x2z*a +x2x*i )*x1x + (-x2z*b +x2x*j )*x1y + (-x2z*c +x2x*k )*x1z + (-x2z*d +x2x*l )*x1w = 0 + * x2^t * H4 * A *x1 = (-x2w*e +x2y*m )*x1x + (-x2w*f +x2y*n )*x1y + (-x2w*g +x2y*o )*x1z + (-x2w*h +x2y*1 )*x1w = 0 + * x2^t * H5 * A *x1 = (-x2y*a +x2x*e )*x1x + (-x2y*b +x2x*f )*x1y + (-x2y*c +x2x*g )*x1z + (-x2y*d +x2x*h )*x1w = 0 + * x2^t * H6 * A *x1 = (-x2w*i +x2z*m )*x1x + (-x2w*j +x2z*n )*x1y + (-x2w*k +x2z*o )*x1z + (-x2w*l +x2z*1 )*x1w = 0 + * + * X = |a b c d e f g h i j k l m n o|^t +*/ +bool Homography3DFromCorrespondencesLinear(const Mat &x1, + const Mat &x2, + Mat4 *H, + double expected_precision) { + assert(4 == x1.rows()); + assert(5 <= x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + const int x = 0; + const int y = 1; + const int z = 2; + const int w = 3; + int n = x1.cols(); + MatX15 L = Mat::Zero(n * 6, 15); + Mat b = Mat::Zero(n * 6, 1); + for (int i = 0; i < n; ++i) { + int j = 6 * i; + L(j, 0) = -x2(w, i) * x1(x, i); // a + L(j, 1) = -x2(w, i) * x1(y, i); // b + L(j, 2) = -x2(w, i) * x1(z, i); // c + L(j, 3) = -x2(w, i) * x1(w, i); // d + L(j, 12) = x2(x, i) * x1(x, i); // m + L(j, 13) = x2(x, i) * x1(y, i); // n + L(j, 14) = x2(x, i) * x1(z, i); // o + b(j, 0) = -x2(x, i) * x1(w, i); + + ++j; + L(j, 4) = -x2(z, i) * x1(x, i); // e + L(j, 5) = -x2(z, i) * x1(y, i); // f + L(j, 6) = -x2(z, i) * x1(z, i); // g + L(j, 7) = -x2(z, i) * x1(w, i); // h + L(j, 8) = x2(y, i) * x1(x, i); // i + L(j, 9) = x2(y, i) * x1(y, i); // j + L(j, 10) = x2(y, i) * x1(z, i); // k + L(j, 11) = x2(y, i) * x1(w, i); // l + + ++j; + L(j, 0) = -x2(z, i) * x1(x, i); // a + L(j, 1) = -x2(z, i) * x1(y, i); // b + L(j, 2) = -x2(z, i) * x1(z, i); // c + L(j, 3) = -x2(z, i) * x1(w, i); // d + L(j, 8) = x2(x, i) * x1(x, i); // i + L(j, 9) = x2(x, i) * x1(y, i); // j + L(j, 10) = x2(x, i) * x1(z, i); // k + L(j, 11) = x2(x, i) * x1(w, i); // l + + ++j; + L(j, 4) = -x2(w, i) * x1(x, i); // e + L(j, 5) = -x2(w, i) * x1(y, i); // f + L(j, 6) = -x2(w, i) * x1(z, i); // g + L(j, 7) = -x2(w, i) * x1(w, i); // h + L(j, 12) = x2(y, i) * x1(x, i); // m + L(j, 13) = x2(y, i) * x1(y, i); // n + L(j, 14) = x2(y, i) * x1(z, i); // o + b(j, 0) = -x2(y, i) * x1(w, i); + + ++j; + L(j, 0) = -x2(y, i) * x1(x, i); // a + L(j, 1) = -x2(y, i) * x1(y, i); // b + L(j, 2) = -x2(y, i) * x1(z, i); // c + L(j, 3) = -x2(y, i) * x1(w, i); // d + L(j, 4) = x2(x, i) * x1(x, i); // e + L(j, 5) = x2(x, i) * x1(y, i); // f + L(j, 6) = x2(x, i) * x1(z, i); // g + L(j, 7) = x2(x, i) * x1(w, i); // h + + ++j; + L(j, 8) = -x2(w, i) * x1(x, i); // i + L(j, 9) = -x2(w, i) * x1(y, i); // j + L(j, 10) = -x2(w, i) * x1(z, i); // k + L(j, 11) = -x2(w, i) * x1(w, i); // l + L(j, 12) = x2(z, i) * x1(x, i); // m + L(j, 13) = x2(z, i) * x1(y, i); // n + L(j, 14) = x2(z, i) * x1(z, i); // o + b(j, 0) = -x2(z, i) * x1(w, i); + } + // Solve Lx=B + Vec h = L.fullPivLu().solve(b); + if ((L * h).isApprox(b, expected_precision)) { + Homography3DNormalizedParameterization::To(h, H); + return true; + } else { + return false; + } +} + +double SymmetricGeometricDistance(const Mat3 &H, + const Vec2 &x1, + const Vec2 &x2) { + Vec3 x(x1(0), x1(1), 1.0); + Vec3 y(x2(0), x2(1), 1.0); + + Vec3 H_x = H * x; + Vec3 Hinv_y = H.inverse() * y; + + H_x /= H_x(2); + Hinv_y /= Hinv_y(2); + + return (H_x.head<2>() - y.head<2>()).squaredNorm() + + (Hinv_y.head<2>() - x.head<2>()).squaredNorm(); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/homography.h b/modules/sfm/src/libmv_light/libmv/multiview/homography.h new file mode 100644 index 00000000000..7d64b086ddb --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/homography.h @@ -0,0 +1,145 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_HOMOGRAPHY_H_ +#define LIBMV_MULTIVIEW_HOMOGRAPHY_H_ + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +/** + * 2D homography transformation estimation. + * + * This function estimates the homography transformation from a list of 2D + * correspondences which represents either: + * + * - 3D points on a plane, with a general moving camera. + * - 3D points with a rotating camera (pure rotation). + * - 3D points + different planar projections + * + * \param x1 The first 2xN or 3xN matrix of euclidean or homogeneous points. + * \param x2 The second 2xN or 3xN matrix of euclidean or homogeneous points. + * \param H The 3x3 homography transformation matrix (8 dof) such that + * x2 = H * x1 with |a b c| + * H = |d e f| + * |g h 1| + * \param expected_precision The expected precision in order for instance + * to accept almost homography matrices. + * + * \return True if the transformation estimation has succeeded. + * \note There must be at least 4 non-colinear points. + */ +bool Homography2DFromCorrespondencesLinear(const Mat &x1, + const Mat &x2, + Mat3 *H, + double expected_precision = + EigenDouble::dummy_precision()); + +/** + * This structure contains options that controls how the homography + * estimation operates. + * + * Defaults should be suitable for a wide range of use cases, but + * better performance and accuracy might require tweaking/ + */ +struct EstimateHomographyOptions { + // Default constructor which sets up a options for generic usage. + EstimateHomographyOptions(void); + + // Normalize correspondencies before estimating the homography + // in order to increase estimation stability. + // + // Normaliztion will make it so centroid od correspondences + // is the coordinate origin and their average distance from + // the origin is sqrt(2). + // + // See: + // - R. Hartley and A. Zisserman. Multiple View Geometry in Computer + // Vision. Cambridge University Press, second edition, 2003. + // - https://www.cs.ubc.ca/grads/resources/thesis/May09/Dubrofsky_Elan.pdf + bool use_normalization; + + // Maximal number of iterations for the refinement step. + int max_num_iterations; + + // Expected average of symmetric geometric distance between + // actual destination points and original ones transformed by + // estimated homography matrix. + // + // Refinement will finish as soon as average of symmetric + // geometric distance is less or equal to this value. + // + // This distance is measured in the same units as input points are. + double expected_average_symmetric_distance; +}; + +/** + * 2D homography transformation estimation. + * + * This function estimates the homography transformation from a list of 2D + * correspondences by doing algebraic estimation first followed with result + * refinement. + */ +bool EstimateHomography2DFromCorrespondences( + const Mat &x1, + const Mat &x2, + const EstimateHomographyOptions &options, + Mat3 *H); + +/** + * 3D Homography transformation estimation. + * + * This function can be used in order to estimate the homography transformation + * from a list of 3D correspondences. + * + * \param[in] x1 The first 4xN matrix of homogeneous points + * \param[in] x2 The second 4xN matrix of homogeneous points + * \param[out] H The 4x4 homography transformation matrix (15 dof) such that + * x2 = H * x1 with |a b c d| + * H = |e f g h| + * |i j k l| + * |m n o 1| + * \param[in] expected_precision The expected precision in order for instance + * to accept almost homography matrices. + * + * \return true if the transformation estimation has succeeded + * + * \note Need at least 5 non coplanar points + * \note Points coordinates must be in homogeneous coordinates + */ +bool Homography3DFromCorrespondencesLinear(const Mat &x1, + const Mat &x2, + Mat4 *H, + double expected_precision = + EigenDouble::dummy_precision()); + +/** + * Calculate symmetric geometric cost: + * + * D(H * x1, x2)^2 + D(H^-1 * x2, x1) + */ +double SymmetricGeometricDistance(const Mat3 &H, + const Vec2 &x1, + const Vec2 &x2); + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_HOMOGRAPHY_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/homography_error.h b/modules/sfm/src/libmv_light/libmv/multiview/homography_error.h new file mode 100644 index 00000000000..0bcf5b70c92 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/homography_error.h @@ -0,0 +1,248 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_ +#define LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_ + +#include "libmv/multiview/projection.h" + +namespace libmv { +namespace homography { +namespace homography2D { + + /** + * Structure for estimating the asymmetric error between a vector x2 and the + * transformed x1 such that + * Error = ||x2 - Psi(H * x1)||^2 + * where Psi is the function that transforms homogeneous to euclidean coords. + * \note It should be distributed as Chi-squared with k = 2. + */ +struct AsymmetricError { + /** + * Computes the asymmetric residuals between a set of 2D points x2 and the + * transformed 2D point set x1 such that + * Residuals_i = x2_i - Psi(H * x1_i) + * where Psi is the function that transforms homogeneous to euclidean coords. + * + * \param[in] H The 3x3 homography matrix. + * The estimated homography should approximatelly hold the condition y = H x. + * \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors). + * \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors). + * \param[out] dx A 2xN matrix of column vectors of residuals errors + */ + static void Residuals(const Mat &H, const Mat &x1, + const Mat &x2, Mat2X *dx) { + dx->resize(2, x1.cols()); + Mat3X x2h_est; + if (x1.rows() == 2) + x2h_est = H * EuclideanToHomogeneous(static_cast(x1)); + else + x2h_est = H * x1; + dx->row(0) = x2h_est.row(0).array() / x2h_est.row(2).array(); + dx->row(1) = x2h_est.row(1).array() / x2h_est.row(2).array(); + if (x2.rows() == 2) + *dx = x2 - *dx; + else + *dx = HomogeneousToEuclidean(static_cast(x2)) - *dx; + } + /** + * Computes the asymmetric residuals between a 2D point x2 and the transformed + * 2D point x1 such that + * Residuals = x2 - Psi(H * x1) + * where Psi is the function that transforms homogeneous to euclidean coords. + * + * \param[in] H The 3x3 homography matrix. + * The estimated homography should approximatelly hold the condition y = H x. + * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \param[out] dx A vector of size 2 of the residual error + */ + static void Residuals(const Mat &H, const Vec &x1, + const Vec &x2, Vec2 *dx) { + Vec3 x2h_est; + if (x1.rows() == 2) + x2h_est = H * EuclideanToHomogeneous(static_cast(x1)); + else + x2h_est = H * x1; + if (x2.rows() == 2) + *dx = x2 - x2h_est.head<2>() / x2h_est[2]; + else + *dx = HomogeneousToEuclidean(static_cast(x2)) - + x2h_est.head<2>() / x2h_est[2]; + } + /** + * Computes the squared norm of the residuals between a set of 2D points x2 + * and the transformed 2D point set x1 such that + * Error = || x2 - Psi(H * x1) ||^2 + * where Psi is the function that transforms homogeneous to euclidean coords. + * + * \param[in] H The 3x3 homography matrix. + * The estimated homography should approximatelly hold the condition y = H x. + * \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors). + * \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors). + * \return The squared norm of the asymmetric residuals errors + */ + static double Error(const Mat &H, const Mat &x1, const Mat &x2) { + Mat2X dx; + Residuals(H, x1, x2, &dx); + return dx.squaredNorm(); + } + /** + * Computes the squared norm of the residuals between a 2D point x2 and the + * transformed 2D point x1 such that rms = || x2 - Psi(H * x1) ||^2 + * where Psi is the function that transforms homogeneous to euclidean coords. + * + * \param[in] H The 3x3 homography matrix. + * The estimated homography should approximatelly hold the condition y = H x. + * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \return The squared norm of the asymmetric residual error + */ + static double Error(const Mat &H, const Vec &x1, const Vec &x2) { + Vec2 dx; + Residuals(H, x1, x2, &dx); + return dx.squaredNorm(); + } +}; + + /** + * Structure for estimating the symmetric error + * between a vector x2 and the transformed x1 such that + * Error = ||x2 - Psi(H * x1)||^2 + ||x1 - Psi(H^-1 * x2)||^2 + * where Psi is the function that transforms homogeneous to euclidean coords. + * \note It should be distributed as Chi-squared with k = 4. + */ +struct SymmetricError { + /** + * Computes the squared norm of the residuals between x2 and the + * transformed x1 such that + * Error = ||x2 - Psi(H * x1)||^2 + ||x1 - Psi(H^-1 * x2)||^2 + * where Psi is the function that transforms homogeneous to euclidean coords. + * + * \param[in] H The 3x3 homography matrix. + * The estimated homography should approximatelly hold the condition y = H x. + * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \return The squared norm of the symmetric residuals errors + */ + static double Error(const Mat &H, const Vec &x1, const Vec &x2) { + // TODO(keir): This is awesomely inefficient because it does a 3x3 + // inversion for each evaluation. + Mat3 Hinv = H.inverse(); + return AsymmetricError::Error(H, x1, x2) + + AsymmetricError::Error(Hinv, x2, x1); + } + // TODO(julien) Add residuals function \see AsymmetricError +}; + /** + * Structure for estimating the algebraic error (cross product) + * between a vector x2 and the transformed x1 such that + * Error = ||[x2] * H * x1||^^2 + * where [x2] is the skew matrix of x2. + */ +struct AlgebraicError { + // TODO(julien) Make an AlgebraicError2Rows and AlgebraicError3Rows + + /** + * Computes the algebraic residuals (cross product) between a set of 2D + * points x2 and the transformed 2D point set x1 such that + * [x2] * H * x1 where [x2] is the skew matrix of x2. + * + * \param[in] H The 3x3 homography matrix. + * The estimated homography should approximatelly hold the condition y = H x. + * \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors). + * \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors). + * \param[out] dx A 3xN matrix of column vectors of residuals errors + */ + static void Residuals(const Mat &H, const Mat &x1, + const Mat &x2, Mat3X *dx) { + dx->resize(3, x1.cols()); + Vec3 col; + for (int i = 0; i < x1.cols(); ++i) { + Residuals(H, x1.col(i), x2.col(i), &col); + dx->col(i) = col; + } + } + /** + * Computes the algebraic residuals (cross product) between a 2D point x2 + * and the transformed 2D point x1 such that + * [x2] * H * x1 where [x2] is the skew matrix of x2. + * + * \param[in] H The 3x3 homography matrix. + * The estimated homography should approximatelly hold the condition y = H x. + * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \param[out] dx A vector of size 3 of the residual error + */ + static void Residuals(const Mat &H, const Vec &x1, + const Vec &x2, Vec3 *dx) { + Vec3 x2h_est; + if (x1.rows() == 2) + x2h_est = H * EuclideanToHomogeneous(static_cast(x1)); + else + x2h_est = H * x1; + if (x2.rows() == 2) + *dx = SkewMat(EuclideanToHomogeneous(static_cast(x2))) * x2h_est; + else + *dx = SkewMat(x2) * x2h_est; + // TODO(julien) This is inefficient since it creates an + // identical 3x3 skew matrix for each evaluation. + } + /** + * Computes the squared norm of the algebraic residuals between a set of 2D + * points x2 and the transformed 2D point set x1 such that + * [x2] * H * x1 where [x2] is the skew matrix of x2. + * + * \param[in] H The 3x3 homography matrix. + * The estimated homography should approximatelly hold the condition y = H x. + * \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors). + * \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors). + * \return The squared norm of the asymmetric residuals errors + */ + static double Error(const Mat &H, const Mat &x1, const Mat &x2) { + Mat3X dx; + Residuals(H, x1, x2, &dx); + return dx.squaredNorm(); + } + /** + * Computes the squared norm of the algebraic residuals between a 2D point x2 + * and the transformed 2D point x1 such that + * [x2] * H * x1 where [x2] is the skew matrix of x2. + * + * \param[in] H The 3x3 homography matrix. + * The estimated homography should approximatelly hold the condition y = H x. + * \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous)) + * \return The squared norm of the asymmetric residual error + */ + static double Error(const Mat &H, const Vec &x1, const Vec &x2) { + Vec3 dx; + Residuals(H, x1, x2, &dx); + return dx.squaredNorm(); + } +}; +// TODO(keir): Add error based on ideal points. + +} // namespace homography2D +// TODO(julien) add homography3D errors +} // namespace homography +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/homography_parameterization.h b/modules/sfm/src/libmv_light/libmv/multiview/homography_parameterization.h new file mode 100644 index 00000000000..5a42a20b065 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/homography_parameterization.h @@ -0,0 +1,91 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_HOMOGRAPHY_PARAMETERIZATION_H_ +#define LIBMV_MULTIVIEW_HOMOGRAPHY_PARAMETERIZATION_H_ + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +/** A parameterization of the 2D homography matrix that uses 8 parameters so + * that the matrix is normalized (H(2,2) == 1). + * The homography matrix H is built from a list of 8 parameters (a, b,...g, h) + * as follows + * |a b c| + * H = |d e f| + * |g h 1| + */ +template +class Homography2DNormalizedParameterization { + public: + typedef Eigen::Matrix Parameters; // a, b, ... g, h + typedef Eigen::Matrix Parameterized; // H + + /// Convert from the 8 parameters to a H matrix. + static void To(const Parameters &p, Parameterized *h) { + *h << p(0), p(1), p(2), + p(3), p(4), p(5), + p(6), p(7), 1.0; + } + + /// Convert from a H matrix to the 8 parameters. + static void From(const Parameterized &h, Parameters *p) { + *p << h(0, 0), h(0, 1), h(0, 2), + h(1, 0), h(1, 1), h(1, 2), + h(2, 0), h(2, 1); + } +}; + +/** A parameterization of the 2D homography matrix that uses 15 parameters so + * that the matrix is normalized (H(3,3) == 1). + * The homography matrix H is built from a list of 15 parameters (a, b,...n, o) + * as follows + * |a b c d| + * H = |e f g h| + * |i j k l| + * |m n o 1| + */ +template +class Homography3DNormalizedParameterization { + public: + typedef Eigen::Matrix Parameters; // a, b, ... n, o + typedef Eigen::Matrix Parameterized; // H + + /// Convert from the 15 parameters to a H matrix. + static void To(const Parameters &p, Parameterized *h) { + *h << p(0), p(1), p(2), p(3), + p(4), p(5), p(6), p(7), + p(8), p(9), p(10), p(11), + p(12), p(13), p(14), 1.0; + } + + /// Convert from a H matrix to the 15 parameters. + static void From(const Parameterized &h, Parameters *p) { + *p << h(0, 0), h(0, 1), h(0, 2), h(0, 3), + h(1, 0), h(1, 1), h(1, 2), h(1, 3), + h(2, 0), h(2, 1), h(2, 2), h(2, 3), + h(3, 0), h(3, 1), h(3, 2); + } +}; + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_HOMOGRAPHY_PARAMETERIZATION_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/nviewtriangulation.h b/modules/sfm/src/libmv_light/libmv/multiview/nviewtriangulation.h new file mode 100644 index 00000000000..f4614ab1a5c --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/nviewtriangulation.h @@ -0,0 +1,80 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +// Compute a 3D position of a point from several images of it. In particular, +// compute the projective point X in R^4 such that x = PX. +// +// Algorithm is the standard DLT; for derivation see appendix of Keir's thesis. + +#ifndef LIBMV_MULTIVIEW_NVIEWTRIANGULATION_H +#define LIBMV_MULTIVIEW_NVIEWTRIANGULATION_H + +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +// x's are 2D coordinates (x,y,1) in each image; Ps are projective cameras. The +// output, X, is a homogeneous four vectors. +template +void NViewTriangulate(const Matrix &x, + const vector > &Ps, + Matrix *X) { + int nviews = x.cols(); + assert(nviews == Ps.size()); + + Matrix design(3*nviews, 4 + nviews); + design.setConstant(0.0); + for (int i = 0; i < nviews; i++) { + design.template block<3, 4>(3*i, 0) = -Ps[i]; + design(3*i + 0, 4 + i) = x(0, i); + design(3*i + 1, 4 + i) = x(1, i); + design(3*i + 2, 4 + i) = 1.0; + } + Matrix X_and_alphas; + Nullspace(&design, &X_and_alphas); + X->resize(4); + *X = X_and_alphas.head(4); +} + +// x's are 2D coordinates (x,y,1) in each image; Ps are projective cameras. The +// output, X, is a homogeneous four vectors. +// This method uses the algebraic distance approximation. +// Note that this method works better when the 2D points are normalized +// with an isotopic normalization. +template +void NViewTriangulateAlgebraic(const Matrix &x, + const vector > &Ps, + Matrix *X) { + int nviews = x.cols(); + assert(nviews == Ps.size()); + + Matrix design(2*nviews, 4); + for (int i = 0; i < nviews; i++) { + design.template block<2, 4>(2*i, 0) = SkewMatMinimal(x.col(i)) * Ps[i]; + } + X->resize(4); + Nullspace(&design, X); +} + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_RESECTION_H diff --git a/modules/sfm/src/libmv_light/libmv/multiview/panography.cc b/modules/sfm/src/libmv_light/libmv/multiview/panography.cc new file mode 100644 index 00000000000..b62802948c4 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/panography.cc @@ -0,0 +1,125 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#include "libmv/multiview/panography.h" + +namespace libmv { + +static bool Build_Minimal2Point_PolynomialFactor( + const Mat & x1, const Mat & x2, + double * P) { // P must be a double[4] + assert(2 == x1.rows()); + assert(2 == x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + + // Setup the variable of the input problem: + Vec xx1 = (x1.col(0)).transpose(); + Vec yx1 = (x1.col(1)).transpose(); + + double a12 = xx1.dot(yx1); + Vec xx2 = (x2.col(0)).transpose(); + Vec yx2 = (x2.col(1)).transpose(); + double b12 = xx2.dot(yx2); + + double a1 = xx1.squaredNorm(); + double a2 = yx1.squaredNorm(); + + double b1 = xx2.squaredNorm(); + double b2 = yx2.squaredNorm(); + + // Build the 3rd degre polynomial in F^2. + // + // f^6 * p + f^4 * q + f^2* r + s = 0; + // + // Coefficients in ascending powers of alpha, i.e. P[N]*x^N. + // Run panography_coeffs.py to get the below coefficients. + P[0] = b1*b2*a12*a12-a1*a2*b12*b12; + P[1] = -2*a1*a2*b12+2*a12*b1*b2+b1*a12*a12+b2*a12*a12-a1*b12*b12-a2*b12*b12; + P[2] = b1*b2-a1*a2-2*a1*b12-2*a2*b12+2*a12*b1+2*a12*b2+a12*a12-b12*b12; + P[3] = b1+b2-2*b12-a1-a2+2*a12; + + // If P[3] equal to 0 we get ill conditionned data + return (P[3] != 0.0); +} + +// This implements a minimal solution (2 points) for panoramic stitching: +// +// http://www.cs.ubc.ca/~mbrown/minimal/minimal.html +// +// [1] M. Brown and R. Hartley and D. Nister. Minimal Solutions for Panoramic +// Stitching. CVPR07. +void F_FromCorrespondance_2points(const Mat &x1, const Mat &x2, + vector *fs) { + // Build Polynomial factor to get squared focal value. + double P[4]; + Build_Minimal2Point_PolynomialFactor(x1, x2, &P[0]); + + // Solve it by using F = f^2 and a Cubic polynomial solver + // + // F^3 * p + F^2 * q + F^1 * r + s = 0 + // + double roots[3]; + int num_roots = SolveCubicPolynomial(P, roots); + for (int i = 0; i < num_roots; ++i) { + if (roots[i] > 0.0) { + fs->push_back(sqrt(roots[i])); + } + } +} + +// Compute the 3x3 rotation matrix that fits two 3D point clouds in the least +// square sense. The method is from: +// +// K. Arun,T. Huand and D. Blostein. Least-squares fitting of 2 3-D point +// sets. IEEE Transactions on Pattern Analysis and Machine Intelligence, +// 9:698-700, 1987. +void GetR_FixedCameraCenter(const Mat &x1, const Mat &x2, + const double focal, + Mat3 *R) { + assert(3 == x1.rows()); + assert(2 <= x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + + // Build simplified K matrix + Mat3 K(Mat3::Identity() * 1.0/focal); + K(2, 2)= 1.0; + + // Build the correlation matrix; equation (22) in [1]. + Mat3 C = Mat3::Zero(); + for (int i = 0; i < x1.cols(); ++i) { + Mat r1i = (K * x1.col(i)).normalized(); + Mat r2i = (K * x2.col(i)).normalized(); + C += r2i * r1i.transpose(); + } + + // Solve for rotation. Equations (24) and (25) in [1]. + Eigen::JacobiSVD svd(C, Eigen::ComputeThinU | Eigen::ComputeThinV); + Mat3 scale = Mat3::Identity(); + scale(2, 2) = ((svd.matrixU() * svd.matrixV().transpose()).determinant() > 0.0) + ? 1.0 + : -1.0; + + (*R) = svd.matrixU() * scale * svd.matrixV().transpose(); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/panography.h b/modules/sfm/src/libmv_light/libmv/multiview/panography.h new file mode 100644 index 00000000000..6e87bd71304 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/panography.h @@ -0,0 +1,99 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef LIBMV_MULTIVIEW_PANOGRAPHY_H +#define LIBMV_MULTIVIEW_PANOGRAPHY_H + +#include "libmv/numeric/numeric.h" +#include "libmv/numeric/poly.h" +#include "libmv/base/vector.h" + +namespace libmv { + +// This implements a minimal solution (2 points) for panoramic stitching: +// +// http://www.cs.ubc.ca/~mbrown/minimal/minimal.html +// +// [1] M. Brown and R. Hartley and D. Nister. Minimal Solutions for Panoramic +// Stitching. CVPR07. +// +// The 2-point algorithm solves for the rotation of the camera with a single +// focal length (4 degrees of freedom). +// +// Compute from 1 to 3 possible focal lenght for 2 point correspondences. +// Suppose that the cameras share the same optical center and focal lengths: +// +// Image 1 => H*x = x' => Image 2 +// x (u1j) x' (u2j) +// a (u11) a' (u21) +// b (u12) b' (u22) +// +// The return values are 1 to 3 possible values for the focal lengths such +// that: +// +// [f 0 0] +// K = [0 f 0] +// [0 0 1] +// +void F_FromCorrespondance_2points(const Mat &x1, const Mat &x2, + vector *fs); + +// Compute the 3x3 rotation matrix that fits two 3D point clouds in the least +// square sense. The method is from: +// +// K. Arun,T. Huand and D. Blostein. Least-squares fitting of 2 3-D point +// sets. IEEE Transactions on Pattern Analysis and Machine Intelligence, +// 9:698-700, 1987. +// +// Given the calibration matrices K1, K2 solve for the rotation from +// corresponding image rays. +// +// R = min || X2 - R * x1 ||. +// +// In case of panography, which is for a camera that shares the same camera +// center, +// +// H = K2 * R * K1.inverse(); +// +// For the full explanation, see Section 8, Solving for Rotation from [1]. +// +// Parameters: +// +// x1 : Point cloud A (3D coords) +// x2 : Point cloud B (3D coords) +// +// [f 0 0] +// K1 = [0 f 0] +// [0 0 1] +// +// K2 (the same form as K1, but may have different f) +// +// Returns: A rotation matrix that minimizes +// +// R = arg min || X2 - R * x1 || +// +void GetR_FixedCameraCenter(const Mat &x1, const Mat &x2, + const double focal, + Mat3 *R); + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_PANOGRAPHY_H diff --git a/modules/sfm/src/libmv_light/libmv/multiview/panography_kernel.cc b/modules/sfm/src/libmv_light/libmv/multiview/panography_kernel.cc new file mode 100644 index 00000000000..8fdc9e79aed --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/panography_kernel.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2008, 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/multiview/panography_kernel.h" +#include "libmv/multiview/panography.h" + +namespace libmv { +namespace panography { +namespace kernel { + +void TwoPointSolver::Solve(const Mat &x1, const Mat &x2, vector *Hs) { + // Solve for the focal lengths. + vector fs; + F_FromCorrespondance_2points(x1, x2, &fs); + + // Then solve for the rotations and homographies. + Mat x1h, x2h; + EuclideanToHomogeneous(x1, &x1h); + EuclideanToHomogeneous(x2, &x2h); + for (int i = 0; i < fs.size(); ++i) { + Mat3 K1 = Mat3::Identity() * fs[i]; + K1(2, 2) = 1.0; + + Mat3 R; + GetR_FixedCameraCenter(x1h, x2h, fs[i], &R); + R /= R(2, 2); + + (*Hs).push_back(K1 * R * K1.inverse()); + } +} + +} // namespace kernel +} // namespace panography +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/panography_kernel.h b/modules/sfm/src/libmv_light/libmv/multiview/panography_kernel.h new file mode 100644 index 00000000000..a6adbd54b20 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/panography_kernel.h @@ -0,0 +1,54 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H +#define LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H + +#include "libmv/base/vector.h" +#include "libmv/multiview/conditioning.h" +#include "libmv/multiview/projection.h" +#include "libmv/multiview/two_view_kernel.h" +#include "libmv/multiview/homography_error.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { +namespace panography { +namespace kernel { + +struct TwoPointSolver { + enum { MINIMUM_SAMPLES = 2 }; + static void Solve(const Mat &x1, const Mat &x2, vector *Hs); +}; + +typedef two_view::kernel::Kernel< + TwoPointSolver, homography::homography2D::AsymmetricError, Mat3> + UnnormalizedKernel; + +typedef two_view::kernel::Kernel< + two_view::kernel::NormalizedSolver, + homography::homography2D::AsymmetricError, + Mat3> + Kernel; + +} // namespace kernel +} // namespace panography +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H diff --git a/modules/sfm/src/libmv_light/libmv/multiview/projection.cc b/modules/sfm/src/libmv_light/libmv/multiview/projection.cc new file mode 100644 index 00000000000..f8bece3de68 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/projection.cc @@ -0,0 +1,224 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/multiview/projection.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +void P_From_KRt(const Mat3 &K, const Mat3 &R, const Vec3 &t, Mat34 *P) { + P->block<3, 3>(0, 0) = R; + P->col(3) = t; + (*P) = K * (*P); +} + +void KRt_From_P(const Mat34 &P, Mat3 *Kp, Mat3 *Rp, Vec3 *tp) { + // Decompose using the RQ decomposition HZ A4.1.1 pag.579. + Mat3 K = P.block(0, 0, 3, 3); + + Mat3 Q; + Q.setIdentity(); + + // Set K(2,1) to zero. + if (K(2, 1) != 0) { + double c = -K(2, 2); + double s = K(2, 1); + double l = sqrt(c * c + s * s); + c /= l; + s /= l; + Mat3 Qx; + Qx << 1, 0, 0, + 0, c, -s, + 0, s, c; + K = K * Qx; + Q = Qx.transpose() * Q; + } + // Set K(2,0) to zero. + if (K(2, 0) != 0) { + double c = K(2, 2); + double s = K(2, 0); + double l = sqrt(c * c + s * s); + c /= l; + s /= l; + Mat3 Qy; + Qy << c, 0, s, + 0, 1, 0, + -s, 0, c; + K = K * Qy; + Q = Qy.transpose() * Q; + } + // Set K(1,0) to zero. + if (K(1, 0) != 0) { + double c = -K(1, 1); + double s = K(1, 0); + double l = sqrt(c * c + s * s); + c /= l; + s /= l; + Mat3 Qz; + Qz << c, -s, 0, + s, c, 0, + 0, 0, 1; + K = K * Qz; + Q = Qz.transpose() * Q; + } + + Mat3 R = Q; + + // Ensure that the diagonal is positive. + // TODO(pau) Change this to ensure that: + // - K(0,0) > 0 + // - K(2,2) = 1 + // - det(R) = 1 + if (K(2, 2) < 0) { + K = -K; + R = -R; + } + if (K(1, 1) < 0) { + Mat3 S; + S << 1, 0, 0, + 0, -1, 0, + 0, 0, 1; + K = K * S; + R = S * R; + } + if (K(0, 0) < 0) { + Mat3 S; + S << -1, 0, 0, + 0, 1, 0, + 0, 0, 1; + K = K * S; + R = S * R; + } + + // Compute translation. + Vec p(3); + p << P(0, 3), P(1, 3), P(2, 3); + // TODO(pau) This sould be done by a SolveLinearSystem(A, b, &x) call. + // TODO(keir) use the eigen LU solver syntax... + Vec3 t = K.inverse() * p; + + // scale K so that K(2,2) = 1 + K = K / K(2, 2); + + *Kp = K; + *Rp = R; + *tp = t; +} + +void ProjectionShiftPrincipalPoint(const Mat34 &P, + const Vec2 &principal_point, + const Vec2 &principal_point_new, + Mat34 *P_new) { + Mat3 T; + T << 1, 0, principal_point_new(0) - principal_point(0), + 0, 1, principal_point_new(1) - principal_point(1), + 0, 0, 1; + *P_new = T * P; +} + +void ProjectionChangeAspectRatio(const Mat34 &P, + const Vec2 &principal_point, + double aspect_ratio, + double aspect_ratio_new, + Mat34 *P_new) { + Mat3 T; + T << 1, 0, 0, + 0, aspect_ratio_new / aspect_ratio, 0, + 0, 0, 1; + Mat34 P_temp; + + ProjectionShiftPrincipalPoint(P, principal_point, Vec2(0, 0), &P_temp); + P_temp = T * P_temp; + ProjectionShiftPrincipalPoint(P_temp, Vec2(0, 0), principal_point, P_new); +} + +void HomogeneousToEuclidean(const Mat &H, Mat *X) { + int d = H.rows() - 1; + int n = H.cols(); + X->resize(d, n); + for (size_t i = 0; i < n; ++i) { + double h = H(d, i); + for (int j = 0; j < d; ++j) { + (*X)(j, i) = H(j, i) / h; + } + } +} + +void HomogeneousToEuclidean(const Mat3X &h, Mat2X *e) { + e->resize(2, h.cols()); + e->row(0) = h.row(0).array() / h.row(2).array(); + e->row(1) = h.row(1).array() / h.row(2).array(); +} +void HomogeneousToEuclidean(const Mat4X &h, Mat3X *e) { + e->resize(3, h.cols()); + e->row(0) = h.row(0).array() / h.row(3).array(); + e->row(1) = h.row(1).array() / h.row(3).array(); + e->row(2) = h.row(2).array() / h.row(3).array(); +} + +void HomogeneousToEuclidean(const Vec3 &H, Vec2 *X) { + double w = H(2); + *X << H(0) / w, H(1) / w; +} + +void HomogeneousToEuclidean(const Vec4 &H, Vec3 *X) { + double w = H(3); + *X << H(0) / w, H(1) / w, H(2) / w; +} + +void EuclideanToHomogeneous(const Mat &X, Mat *H) { + int d = X.rows(); + int n = X.cols(); + H->resize(d + 1, n); + H->block(0, 0, d, n) = X; + H->row(d).setOnes(); +} + +void EuclideanToHomogeneous(const Vec2 &X, Vec3 *H) { + *H << X(0), X(1), 1; +} + +void EuclideanToHomogeneous(const Vec3 &X, Vec4 *H) { + *H << X(0), X(1), X(2), 1; +} + +// TODO(julien) Call conditioning.h/ApplyTransformationToPoints ? +void EuclideanToNormalizedCamera(const Mat2X &x, const Mat3 &K, Mat2X *n) { + Mat3X x_image_h; + EuclideanToHomogeneous(x, &x_image_h); + Mat3X x_camera_h = K.inverse() * x_image_h; + HomogeneousToEuclidean(x_camera_h, n); +} + +void HomogeneousToNormalizedCamera(const Mat3X &x, const Mat3 &K, Mat2X *n) { + Mat3X x_camera_h = K.inverse() * x; + HomogeneousToEuclidean(x_camera_h, n); +} + +double Depth(const Mat3 &R, const Vec3 &t, const Vec3 &X) { + return (R*X)(2) + t(2); +} + +double Depth(const Mat3 &R, const Vec3 &t, const Vec4 &X) { + Vec3 Xe = X.head<3>() / X(3); + return Depth(R, t, Xe); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/projection.h b/modules/sfm/src/libmv_light/libmv/multiview/projection.h new file mode 100644 index 00000000000..3220bc2dbbc --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/projection.h @@ -0,0 +1,231 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_PROJECTION_H_ +#define LIBMV_MULTIVIEW_PROJECTION_H_ + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +void P_From_KRt(const Mat3 &K, const Mat3 &R, const Vec3 &t, Mat34 *P); +void KRt_From_P(const Mat34 &P, Mat3 *K, Mat3 *R, Vec3 *t); + +// Applies a change of basis to the image coordinates of the projection matrix +// so that the principal point becomes principal_point_new. +void ProjectionShiftPrincipalPoint(const Mat34 &P, + const Vec2 &principal_point, + const Vec2 &principal_point_new, + Mat34 *P_new); + +// Applies a change of basis to the image coordinates of the projection matrix +// so that the aspect ratio becomes aspect_ratio_new. This is done by +// stretching the y axis. The aspect ratio is defined as the quotient between +// the focal length of the y and the x axis. +void ProjectionChangeAspectRatio(const Mat34 &P, + const Vec2 &principal_point, + double aspect_ratio, + double aspect_ratio_new, + Mat34 *P_new); + +void HomogeneousToEuclidean(const Mat &H, Mat *X); +void HomogeneousToEuclidean(const Mat3X &h, Mat2X *e); +void HomogeneousToEuclidean(const Mat4X &h, Mat3X *e); +void HomogeneousToEuclidean(const Vec3 &H, Vec2 *X); +void HomogeneousToEuclidean(const Vec4 &H, Vec3 *X); +inline Vec2 HomogeneousToEuclidean(const Vec3 &h) { + return h.head<2>() / h(2); +} +inline Vec3 HomogeneousToEuclidean(const Vec4 &h) { + return h.head<3>() / h(3); +} +inline Mat2X HomogeneousToEuclidean(const Mat3X &h) { + Mat2X e(2, h.cols()); + e.row(0) = h.row(0).array() / h.row(2).array(); + e.row(1) = h.row(1).array() / h.row(2).array(); + return e; +} + +void EuclideanToHomogeneous(const Mat &X, Mat *H); +inline Mat3X EuclideanToHomogeneous(const Mat2X &x) { + Mat3X h(3, x.cols()); + h.block(0, 0, 2, x.cols()) = x; + h.row(2).setOnes(); + return h; +} +inline void EuclideanToHomogeneous(const Mat2X &x, Mat3X *h) { + h->resize(3, x.cols()); + h->block(0, 0, 2, x.cols()) = x; + h->row(2).setOnes(); +} +inline Mat4X EuclideanToHomogeneous(const Mat3X &x) { + Mat4X h(4, x.cols()); + h.block(0, 0, 3, x.cols()) = x; + h.row(3).setOnes(); + return h; +} +inline void EuclideanToHomogeneous(const Mat3X &x, Mat4X *h) { + h->resize(4, x.cols()); + h->block(0, 0, 3, x.cols()) = x; + h->row(3).setOnes(); +} +void EuclideanToHomogeneous(const Vec2 &X, Vec3 *H); +void EuclideanToHomogeneous(const Vec3 &X, Vec4 *H); +inline Vec3 EuclideanToHomogeneous(const Vec2 &x) { + return Vec3(x(0), x(1), 1); +} +inline Vec4 EuclideanToHomogeneous(const Vec3 &x) { + return Vec4(x(0), x(1), x(2), 1); +} +// Conversion from image coordinates to normalized camera coordinates +void EuclideanToNormalizedCamera(const Mat2X &x, const Mat3 &K, Mat2X *n); +void HomogeneousToNormalizedCamera(const Mat3X &x, const Mat3 &K, Mat2X *n); + +inline Vec2 Project(const Mat34 &P, const Vec3 &X) { + Vec4 HX; + HX << X, 1.0; + Vec3 hx = P * HX; + return hx.head<2>() / hx(2); +} + +inline void Project(const Mat34 &P, const Vec4 &X, Vec3 *x) { + *x = P * X; +} + +inline void Project(const Mat34 &P, const Vec4 &X, Vec2 *x) { + Vec3 hx = P * X; + *x = hx.head<2>() / hx(2); +} + +inline void Project(const Mat34 &P, const Vec3 &X, Vec3 *x) { + Vec4 HX; + HX << X, 1.0; + Project(P, HX, x); +} + +inline void Project(const Mat34 &P, const Vec3 &X, Vec2 *x) { + Vec3 hx; + Project(P, X, x); + *x = hx.head<2>() / hx(2); +} + +inline void Project(const Mat34 &P, const Mat4X &X, Mat2X *x) { + x->resize(2, X.cols()); + for (int c = 0; c < X.cols(); ++c) { + Vec3 hx = P * X.col(c); + x->col(c) = hx.head<2>() / hx(2); + } +} + +inline Mat2X Project(const Mat34 &P, const Mat4X &X) { + Mat2X x; + Project(P, X, &x); + return x; +} + +inline void Project(const Mat34 &P, const Mat3X &X, Mat2X *x) { + x->resize(2, X.cols()); + for (int c = 0; c < X.cols(); ++c) { + Vec4 HX; + HX << X.col(c), 1.0; + Vec3 hx = P * HX; + x->col(c) = hx.head<2>() / hx(2); + } +} + +inline void Project(const Mat34 &P, const Mat3X &X, const Vecu &ids, Mat2X *x) { + x->resize(2, ids.size()); + Vec4 HX; + Vec3 hx; + for (int c = 0; c < ids.size(); ++c) { + HX << X.col(ids[c]), 1.0; + hx = P * HX; + x->col(c) = hx.head<2>() / hx(2); + } +} + +inline Mat2X Project(const Mat34 &P, const Mat3X &X) { + Mat2X x(2, X.cols()); + Project(P, X, &x); + return x; +} + +inline Mat2X Project(const Mat34 &P, const Mat3X &X, const Vecu &ids) { + Mat2X x(2, ids.size()); + Project(P, X, ids, &x); + return x; +} + +double Depth(const Mat3 &R, const Vec3 &t, const Vec3 &X); +double Depth(const Mat3 &R, const Vec3 &t, const Vec4 &X); + +/** +* Returns true if the homogenious 3D point X is in front of +* the camera P. +*/ +inline bool isInFrontOfCamera(const Mat34 &P, const Vec4 &X) { + double condition_1 = P.row(2).dot(X) * X[3]; + double condition_2 = X[2] * X[3]; + if (condition_1 > 0 && condition_2 > 0) + return true; + else + return false; +} + +inline bool isInFrontOfCamera(const Mat34 &P, const Vec3 &X) { + Vec4 X_homo; + X_homo.segment<3>(0) = X; + X_homo(3) = 1; + return isInFrontOfCamera( P, X_homo); +} + +/** +* Transforms a 2D point from pixel image coordinates to a 2D point in +* normalized image coordinates. +*/ +inline Vec2 ImageToNormImageCoordinates(Mat3 &Kinverse, Vec2 &x) { + Vec3 x_h = Kinverse*EuclideanToHomogeneous(x); + return HomogeneousToEuclidean( x_h ); +} + +/// Estimates the root mean square error (2D) +inline double RootMeanSquareError(const Mat2X &x_image, + const Mat4X &X_world, + const Mat34 &P) { + size_t num_points = x_image.cols(); + Mat2X dx = Project(P, X_world) - x_image; + return dx.norm() / num_points; +} + +/// Estimates the root mean square error (2D) +inline double RootMeanSquareError(const Mat2X &x_image, + const Mat3X &X_world, + const Mat3 &K, + const Mat3 &R, + const Vec3 &t) { + Mat34 P; + P_From_KRt(K, R, t, &P); + size_t num_points = x_image.cols(); + Mat2X dx = Project(P, X_world) - x_image; + return dx.norm() / num_points; +} +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_PROJECTION_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/random_sample.h b/modules/sfm/src/libmv_light/libmv/multiview/random_sample.h new file mode 100644 index 00000000000..198173c57b7 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/random_sample.h @@ -0,0 +1,63 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_RANDOM_SAMPLE_H_ +#define LIBMV_MULTIVIEW_RANDOM_SAMPLE_H_ + +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" + +namespace libmv { + +/*! + Pick a random subset of the integers [0, total), in random order. Note that + this can behave badly if num_samples is close to total; runtime could be + unlimited! + + This uses a quadratic rejection strategy and should only be used for small + num_samples. + + \param num_samples The number of samples to produce. + \param total_samples The number of samples available. + \param samples num_samples of numbers in [0, total_samples) is placed + here on return. +*/ +static void UniformSample(int num_samples, + int total_samples, + vector *samples) { + samples->resize(0); + while (samples->size() < num_samples) { + int sample = rand() % total_samples; + bool found = false; + for (int j = 0; j < samples->size(); ++j) { + found = (*samples)[j] == sample; + if (found) { + break; + } + } + if (!found) { + samples->push_back(sample); + } + } +} + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_RANDOM_SAMPLE_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/resection.h b/modules/sfm/src/libmv_light/libmv/multiview/resection.h new file mode 100644 index 00000000000..c142d6deeb2 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/resection.h @@ -0,0 +1,62 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +// Compute the projection matrix from a set of 3D points X and their +// projections x = PX in 2D. This is useful if a point cloud is reconstructed. +// +// Algorithm is the standard DLT as described in Hartley & Zisserman, page 179. + +#ifndef LIBMV_MULTIVIEW_RESECTION_H +#define LIBMV_MULTIVIEW_RESECTION_H + +#include "libmv/logging/logging.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { +namespace resection { + +// x's are 2D image coordinates, (x,y,1), and X's are homogeneous four vectors. +template +void Resection(const Matrix &x, + const Matrix &X, + Matrix *P) { + int N = x.cols(); + assert(X.cols() == N); + + Matrix design(2*N, 12); + design.setZero(); + for (int i = 0; i < N; i++) { + T xi = x(0, i); + T yi = x(1, i); + // See equation (7.2) on page 179 of H&Z. + design.template block<1, 4>(2*i, 4) = -X.col(i).transpose(); + design.template block<1, 4>(2*i, 8) = yi*X.col(i).transpose(); + design.template block<1, 4>(2*i + 1, 0) = X.col(i).transpose(); + design.template block<1, 4>(2*i + 1, 8) = -xi*X.col(i).transpose(); + } + Matrix p; + Nullspace(&design, &p); + reshape(p, 3, 4, P); +} + +} // namespace resection +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_RESECTION_H diff --git a/modules/sfm/src/libmv_light/libmv/multiview/resection_kernel.h b/modules/sfm/src/libmv_light/libmv/multiview/resection_kernel.h new file mode 100644 index 00000000000..ed749c70c27 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/resection_kernel.h @@ -0,0 +1,66 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_RESECTION_KERNEL_H +#define LIBMV_MULTIVIEW_RESECTION_KERNEL_H + +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" +#include "libmv/multiview/resection.h" +#include "libmv/multiview/projection.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { +namespace resection { +namespace kernel { + +class Kernel { + public: + typedef Mat34 Model; + enum { MINIMUM_SAMPLES = 6 }; + + Kernel(const Mat2X &x, const Mat4X &X) : x_(x), X_(X) { + CHECK(x.cols() == X.cols()); + } + void Fit(const vector &samples, vector *models) const { + Mat2X x = ExtractColumns(x_, samples); + Mat4X X = ExtractColumns(X_, samples); + Mat34 P; + Resection(x, X, &P); + models->push_back(P); + } + double Error(int sample, const Model &model) const { + Mat4X X = X_.col(sample); + Mat2X error = Project(model, X) - x_.col(sample); + return error.col(0).squaredNorm(); + } + int NumSamples() const { + return x_.cols(); + } + private: + const Mat2X &x_; + const Mat4X &X_; +}; + +} // namespace kernel +} // namespace resection +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_RESECTION_KERNEL_H diff --git a/modules/sfm/src/libmv_light/libmv/multiview/robust_estimation.cc b/modules/sfm/src/libmv_light/libmv/multiview/robust_estimation.cc new file mode 100644 index 00000000000..78f74450e0d --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/robust_estimation.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include +#include +#include +#include + +#include "libmv/multiview/robust_estimation.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/robust_estimation.h b/modules/sfm/src/libmv_light/libmv/multiview/robust_estimation.h new file mode 100644 index 00000000000..a677c5db889 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/robust_estimation.h @@ -0,0 +1,154 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_ROBUST_ESTIMATION_H_ +#define LIBMV_MULTIVIEW_ROBUST_ESTIMATION_H_ + +#include + +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" +#include "libmv/multiview/random_sample.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +template +class MLEScorer { + public: + MLEScorer(double threshold) : threshold_(threshold) {} + double Score(const Kernel &kernel, + const typename Kernel::Model &model, + const vector &samples, + vector *inliers) const { + double cost = 0.0; + for (int j = 0; j < samples.size(); ++j) { + double error = kernel.Error(samples[j], model); + if (error < threshold_) { + cost += error; + inliers->push_back(samples[j]); + } else { + cost += threshold_; + } + } + return cost; + } + private: + double threshold_; +}; + +static uint IterationsRequired(int min_samples, + double outliers_probability, + double inlier_ratio) { + return static_cast( + log(outliers_probability) / log(1.0 - pow(inlier_ratio, min_samples))); +} + +// 1. The model. +// 2. The minimum number of samples needed to fit. +// 3. A way to convert samples to a model. +// 4. A way to convert samples and a model to an error. +// +// 1. Kernel::Model +// 2. Kernel::MINIMUM_SAMPLES +// 3. Kernel::Fit(vector, vector *) +// 4. Kernel::Error(Model, int) -> error +template +typename Kernel::Model Estimate(const Kernel &kernel, + const Scorer &scorer, + vector *best_inliers = NULL, + double *best_score = NULL, + double outliers_probability = 1e-2) { + CHECK(outliers_probability < 1.0); + CHECK(outliers_probability > 0.0); + size_t iteration = 0; + const size_t min_samples = Kernel::MINIMUM_SAMPLES; + const size_t total_samples = kernel.NumSamples(); + + size_t max_iterations = 100; + const size_t really_max_iterations = 1000; + + int best_num_inliers = 0; + double best_cost = HUGE_VAL; + double best_inlier_ratio = 0.0; + typename Kernel::Model best_model; + + // Test if we have sufficient points to for the kernel. + if (total_samples < min_samples) { + if (best_inliers) { + best_inliers->resize(0); + } + return best_model; + } + + // In this robust estimator, the scorer always works on all the data points + // at once. So precompute the list ahead of time. + vector all_samples; + for (int i = 0; i < total_samples; ++i) { + all_samples.push_back(i); + } + + vector sample; + for (iteration = 0; + iteration < max_iterations && + iteration < really_max_iterations; ++iteration) { + UniformSample(min_samples, total_samples, &sample); + + vector models; + kernel.Fit(sample, &models); + VLOG(4) << "Fitted subset; found " << models.size() << " model(s)."; + + // Compute costs for each fit. + for (int i = 0; i < models.size(); ++i) { + vector inliers; + double cost = scorer.Score(kernel, models[i], all_samples, &inliers); + VLOG(5) << "Fit cost: " << cost + << ", number of inliers: " << inliers.size(); + + if (cost < best_cost) { + best_cost = cost; + best_inlier_ratio = inliers.size() / double(total_samples); + best_num_inliers = inliers.size(); + best_model = models[i]; + if (best_inliers) { + best_inliers->swap(inliers); + } + VLOG(4) << "New best cost: " << best_cost << " with " + << best_num_inliers << " inlying of " + << total_samples << " total samples."; + } + if (best_inlier_ratio) { + max_iterations = IterationsRequired(min_samples, + outliers_probability, + best_inlier_ratio); + } + + VLOG(5) << "Max iterations needed given best inlier ratio: " + << max_iterations << "; best inlier ratio: " << best_inlier_ratio; + } + } + if (best_score) + *best_score = best_cost; + return best_model; +} + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_ROBUST_ESTIMATION_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/robust_fundamental.cc b/modules/sfm/src/libmv_light/libmv/multiview/robust_fundamental.cc new file mode 100644 index 00000000000..0462411a87e --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/robust_fundamental.cc @@ -0,0 +1,69 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/multiview/fundamental_kernel.h" +#include "libmv/multiview/robust_estimation.h" +#include "libmv/multiview/robust_fundamental.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +// TODO(keir): This interface is a bit ugly; consider fixing it. +double FundamentalFromCorrespondences8PointRobust(const Mat &x1, + const Mat &x2, + double max_error, + Mat3 *F, + vector *inliers, + double outliers_probability) { + // The threshold is on the sum of the squared errors in the two images. + // Actually, Sampson's approximation of this error. + double threshold = 2 * Square(max_error); + double best_score = HUGE_VAL; + typedef fundamental::kernel::NormalizedEightPointKernel Kernel; + Kernel kernel(x1, x2); + *F = Estimate(kernel, MLEScorer(threshold), inliers, + &best_score, outliers_probability); + if (best_score == HUGE_VAL) + return HUGE_VAL; + else + return std::sqrt(best_score / 2.0); +} + +double FundamentalFromCorrespondences7PointRobust(const Mat &x1, + const Mat &x2, + double max_error, + Mat3 * F, + vector *inliers, + double outliers_probability) { + // The threshold is on the sum of the squared errors in the two images. + // Actually, Sampson's approximation of this error. + double threshold = 2 * Square(max_error); + double best_score = HUGE_VAL; + typedef fundamental::kernel::NormalizedSevenPointKernel Kernel; + Kernel kernel(x1, x2); + *F = Estimate(kernel, MLEScorer(threshold), inliers, + &best_score, outliers_probability); + if (best_score == HUGE_VAL) + return HUGE_VAL; + else + return std::sqrt(best_score / 2.0); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/robust_fundamental.h b/modules/sfm/src/libmv_light/libmv/multiview/robust_fundamental.h new file mode 100644 index 00000000000..52ccb03d135 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/robust_fundamental.h @@ -0,0 +1,53 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_ROBUST_FUNDAMENTAL_H_ +#define LIBMV_MULTIVIEW_ROBUST_FUNDAMENTAL_H_ + +#include "libmv/base/vector.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +// Estimate robustly the fundamental matrix between two dataset of 2D point +// (image coords space). The fundamental solver relies on the 8 point solution. +// Returns the score associated to the solution F +double FundamentalFromCorrespondences8PointRobust( + const Mat &x1, + const Mat &x2, + double max_error, + Mat3 *F, + vector *inliers = NULL, + double outliers_probability = 1e-2); + +// Estimate robustly the fundamental matrix between two dataset of 2D point +// (image coords space). The fundamental solver relies on the 7 point solution. +// Returns the score associated to the solution F +double FundamentalFromCorrespondences7PointRobust( + const Mat &x1, + const Mat &x2, + double max_error, + Mat3 * F, + vector *inliers = NULL, + double outliers_probability = 1e-2); + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_ROBUST_FUNDAMENTAL_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/robust_resection.cc b/modules/sfm/src/libmv_light/libmv/multiview/robust_resection.cc new file mode 100644 index 00000000000..40be83a79be --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/robust_resection.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2010 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/multiview/resection_kernel.h" +#include "libmv/multiview/robust_estimation.h" +#include "libmv/multiview/robust_resection.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { +// Estimate robustly the the projection matrix of a uncalibrated +// camera from 6 or more 3D points and their images. +double ResectionRobust(const Mat2X &x_image, + const Mat4X &X_world, + double max_error, + Mat34 *P, + vector *inliers, + double outliers_probability) { + // The threshold is on the sum of the squared errors. + double threshold = Square(max_error); + double best_score = HUGE_VAL; + typedef libmv::resection::kernel::Kernel Kernel; + Kernel kernel(x_image, X_world); + *P = Estimate(kernel, MLEScorer(threshold), inliers, + &best_score, outliers_probability); + if (best_score == HUGE_VAL) + return HUGE_VAL; + else + return std::sqrt(best_score / 2.0); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/robust_resection.h b/modules/sfm/src/libmv_light/libmv/multiview/robust_resection.h new file mode 100644 index 00000000000..2520789fd7f --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/robust_resection.h @@ -0,0 +1,41 @@ +// Copyright (c) 2010 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_ROBUST_RESECTION_H_ +#define LIBMV_MULTIVIEW_ROBUST_RESECTION_H_ + +#include "libmv/base/vector.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +// Estimate robustly the the projection matrix of a uncalibrated +// camera from 6 or more 3D points and their images. +// Returns the score associated to the solution P +double ResectionRobust(const Mat2X &x_image, + const Mat4X &X_world, + double max_error, + Mat34 *P, + vector *inliers = NULL, + double outliers_probability = 1e-2); + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_ROBUST_RESECTION_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/triangulation.cc b/modules/sfm/src/libmv_light/libmv/multiview/triangulation.cc new file mode 100644 index 00000000000..4d146c8f21b --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/triangulation.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/multiview/triangulation.h" + +#include "libmv/numeric/numeric.h" +#include "libmv/multiview/projection.h" + +namespace libmv { + +// HZ 12.2 pag.312 +void TriangulateDLT(const Mat34 &P1, const Vec2 &x1, + const Mat34 &P2, const Vec2 &x2, + Vec4 *X_homogeneous) { + Mat4 design; + for (int i = 0; i < 4; ++i) { + design(0, i) = x1(0) * P1(2, i) - P1(0, i); + design(1, i) = x1(1) * P1(2, i) - P1(1, i); + design(2, i) = x2(0) * P2(2, i) - P2(0, i); + design(3, i) = x2(1) * P2(2, i) - P2(1, i); + } + Nullspace(&design, X_homogeneous); +} + +void TriangulateDLT(const Mat34 &P1, const Vec2 &x1, + const Mat34 &P2, const Vec2 &x2, + Vec3 *X_euclidean) { + Vec4 X_homogeneous; + TriangulateDLT(P1, x1, P2, x2, &X_homogeneous); + HomogeneousToEuclidean(X_homogeneous, X_euclidean); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/triangulation.h b/modules/sfm/src/libmv_light/libmv/multiview/triangulation.h new file mode 100644 index 00000000000..be878890242 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/triangulation.h @@ -0,0 +1,38 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_TRIANGULATION_H_ +#define LIBMV_MULTIVIEW_TRIANGULATION_H_ + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +void TriangulateDLT(const Mat34 &P1, const Vec2 &x1, + const Mat34 &P2, const Vec2 &x2, + Vec4 *X_homogeneous); + +void TriangulateDLT(const Mat34 &P1, const Vec2 &x1, + const Mat34 &P2, const Vec2 &x2, + Vec3 *X_euclidean); + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_TRIANGULATION_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/two_view_kernel.h b/modules/sfm/src/libmv_light/libmv/multiview/two_view_kernel.h new file mode 100644 index 00000000000..7af0ed5ddab --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/two_view_kernel.h @@ -0,0 +1,137 @@ +// Copyright (c) 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_MULTIVIEW_TWO_VIEW_KERNEL_H_ +#define LIBMV_MULTIVIEW_TWO_VIEW_KERNEL_H_ + +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" +#include "libmv/multiview/conditioning.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { +namespace two_view { +namespace kernel { + +template +struct NormalizedSolver { + enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES }; + static void Solve(const Mat &x1, const Mat &x2, vector *models) { + assert(2 == x1.rows()); + assert(MINIMUM_SAMPLES <= x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + + // Normalize the data. + Mat3 T1, T2; + Mat x1_normalized, x2_normalized; + NormalizePoints(x1, &x1_normalized, &T1); + NormalizePoints(x2, &x2_normalized, &T2); + + Solver::Solve(x1_normalized, x2_normalized, models); + + for (int i = 0; i < models->size(); ++i) { + Unnormalizer::Unnormalize(T1, T2, &(*models)[i]); + } + } +}; + +template +struct IsotropicNormalizedSolver { + enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES }; + static void Solve(const Mat &x1, const Mat &x2, vector *models) { + assert(2 == x1.rows()); + assert(MINIMUM_SAMPLES <= x1.cols()); + assert(x1.rows() == x2.rows()); + assert(x1.cols() == x2.cols()); + + // Normalize the data. + Mat3 T1, T2; + Mat x1_normalized, x2_normalized; + NormalizeIsotropicPoints(x1, &x1_normalized, &T1); + NormalizeIsotropicPoints(x2, &x2_normalized, &T2); + + Solver::Solve(x1_normalized, x2_normalized, models); + + for (int i = 0; i < models->size(); ++i) { + Unnormalizer::Unnormalize(T1, T2, &(*models)[i]); + } + } +}; +// This is one example (targeted at solvers that operate on correspondences +// between two views) that shows the "kernel" part of a robust fitting +// problem: +// +// 1. The model; Mat3 in the case of the F or H matrix. +// 2. The minimum number of samples needed to fit; 7 or 8 (or 4). +// 3. A way to convert samples to a model. +// 4. A way to convert a sample and a model to an error. +// +// Of particular note is that the kernel does not expose what the samples are. +// All the robust fitting algorithm sees is that there is some number of +// samples; it is able to fit subsets of them (via the kernel) and check their +// error, but can never access the samples themselves. +// +// The Kernel objects must follow the following concept so that the robust +// fitting alogrithm can fit this type of relation: +// +// 1. Kernel::Model +// 2. Kernel::MINIMUM_SAMPLES +// 3. Kernel::Fit(vector, vector *) +// 4. Kernel::Error(int, Model) -> error +// +// The fit routine must not clear existing entries in the vector of models; it +// should append new solutions to the end. +template +class Kernel { + public: + Kernel(const Mat &x1, const Mat &x2) : x1_(x1), x2_(x2) {} + typedef SolverArg Solver; + typedef ModelArg Model; + enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES }; + void Fit(const vector &samples, vector *models) const { + Mat x1 = ExtractColumns(x1_, samples); + Mat x2 = ExtractColumns(x2_, samples); + Solver::Solve(x1, x2, models); + } + double Error(int sample, const Model &model) const { + return ErrorArg::Error(model, + static_cast(x1_.col(sample)), + static_cast(x2_.col(sample))); + } + int NumSamples() const { + return x1_.cols(); + } + static void Solve(const Mat &x1, const Mat &x2, vector *models) { + // By offering this, Kernel types can be passed to templates. + Solver::Solve(x1, x2, models); + } + protected: + const Mat &x1_; + const Mat &x2_; +}; + +} // namespace kernel +} // namespace two_view +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_TWO_VIEW_KERNEL_H_ diff --git a/modules/sfm/src/libmv_light/libmv/multiview/twoviewtriangulation.cc b/modules/sfm/src/libmv_light/libmv/multiview/twoviewtriangulation.cc new file mode 100644 index 00000000000..805bc73e90a --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/twoviewtriangulation.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/numeric/numeric.h" +#include "libmv/multiview/projection.h" +#include "libmv/multiview/twoviewtriangulation.h" +#include "libmv/multiview/projection.h" + + +namespace libmv { + +void TwoViewTriangulationByPlanes(const Vec3 &x1, const Vec3 &x2, + const Mat34 &P, const Mat3 &E, + Vec4 *X) { + Vec3 a = E.transpose() * x2; + Vec3 a0 = a; + a0(2) = 0; + Vec3 b = x1; + b = b.cross(a0); + + Vec3 c = E * x1; + c(2) = 0; + c = c.cross(x2); + Vec4 C = P.transpose() * c; + + Vec3 d = a.cross(b); + Vec3 q = d * C[3]; + (*X)[0] = q[0]; + (*X)[1] = q[1]; + (*X)[2] = q[2]; + (*X)[3] = -(d[0] * C[0] + d[1] * C[1] + d[2] * C[2]); +} + +void TwoViewTriangulationByPlanes(const Vec2 &x1, const Vec2 &x2, + const Mat34 &P,const Mat3 &E, + Vec3 *X) { + Vec3 x1_homogenious = EuclideanToHomogeneous(x1); + Vec3 x2_homogenious = EuclideanToHomogeneous(x2); + Vec4 X_homogenious; + TwoViewTriangulationByPlanes(x1_homogenious, + x2_homogenious, + P, E, &X_homogenious); + (*X) = HomogeneousToEuclidean(X_homogenious); +} + +void TwoViewTriangulationIdeal(const Vec3 &x1, const Vec3 &x2, + const Mat34 &P, const Mat3 &E, + Vec4 *X){ + Vec3 c = E * x1; + c(2) = 0; + c = c.cross(x2); + Vec4 C = P.transpose() * c; + + Vec3 q = x1 * C[3]; + (*X)[0] = q[0]; + (*X)[1] = q[1]; + (*X)[2] = q[2]; + (*X)[3] = -(x1[0] * C[0] + x1[1] * C[1] + x1[2] * C[2]); +} + +void TwoViewTriangulationIdeal(const Vec2 &x1, const Vec2 &x2, + const Mat34 &P, const Mat3 &E, + Vec3 *X) { + Vec3 x1_homogenious = EuclideanToHomogeneous(x1); + Vec3 x2_homogenious = EuclideanToHomogeneous(x2); + Vec4 X_homogenious; + TwoViewTriangulationIdeal(x1_homogenious, + x2_homogenious, + P, E, &X_homogenious); + (*X) = HomogeneousToEuclidean(X_homogenious); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/multiview/twoviewtriangulation.h b/modules/sfm/src/libmv_light/libmv/multiview/twoviewtriangulation.h new file mode 100644 index 00000000000..384620798fb --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/multiview/twoviewtriangulation.h @@ -0,0 +1,82 @@ +// Copyright (c) 2010 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +// Compute a 3D position of a point from several images of it. In particular, +// compute the projective point X in R^4 such that x = PX. +// +// Algorithm is the standard DLT; for derivation see appendix of Keir's thesis. + +#ifndef LIBMV_TWOVIEW_NVIEWTRIANGULATION_H +#define LIBMV_TWOVIEW_NVIEWTRIANGULATION_H + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +/** +* Two view triangulation for cameras in canonical form, +* where the reference camera is in the form [I|0] and P is in +* the form [R|t]. The algorithm minimizes the re-projection error +* in the first image only, i.e. the error in the second image is 0 +* while the point in the first image is the point lying on the +* epipolar line that is closest to x1. +* +* \param x1 The normalized image point in the first camera +* (inv(K1)*x1_image) +* \param x2 The normalized image point in the second camera +* (inv(K2)*x2_image) +* \param P The second camera matrix in the form [R|t] +* \param E The essential matrix between the two cameras +* \param X The 3D homogeneous point +* +* This is the algorithm described in Appendix A in: +* "An efficient solution to the five-point relative pose problem", +* by D. Nist\'er, IEEE PAMI, vol. 26 +*/ +void TwoViewTriangulationByPlanes(const Vec3 &x1, const Vec3 &x2, + const Mat34 &P,const Mat3 &E, Vec4 *X); +void TwoViewTriangulationByPlanes(const Vec2 &x1, const Vec2 &x2, + const Mat34 &P,const Mat3 &E, Vec3 *X); + +/** +* The same algorithm as above generalized for ideal points, +* e.i. where x1*E*x2' = 0. This will not work if the points are +* not ideal. In the case of measured image points it is best to +* either use the TwoViewTriangulationByPlanes function or correct +* the points so that they lay on the corresponding epipolar lines. +* +* \param x1 The normalized image point in the first camera +* (inv(K1)*x1_image) +* \param x2 The normalized image point in the second camera +* (inv(K2)*x2_image) +* \param P The second camera matrix in the form [R|t] +* \param E The essential matrix between the two cameras +* \param X The 3D homogeneous point +*/ +void TwoViewTriangulationIdeal(const Vec3 &x1, const Vec3 &x2, + const Mat34 &P, const Mat3 &E, + Vec4 *X); +void TwoViewTriangulationIdeal(const Vec2 &x1, const Vec2 &x2, + const Mat34 &P, const Mat3 &E, + Vec3 *X); + +} // namespace libmv + +#endif // LIBMV_MULTIVIEW_RESECTION_H diff --git a/modules/sfm/src/libmv_light/libmv/numeric/CMakeLists.txt b/modules/sfm/src/libmv_light/libmv/numeric/CMakeLists.txt new file mode 100644 index 00000000000..5ecd541ccee --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/numeric/CMakeLists.txt @@ -0,0 +1,12 @@ +# define the source files +SET(NUMERIC_SRC numeric.cc + poly.cc) + +# define the header files (make the headers appear in IDEs.) +FILE(GLOB NUMERIC_HDRS *.h) + +ADD_LIBRARY(numeric STATIC ${NUMERIC_SRC} ${NUMERIC_HDRS}) + +TARGET_LINK_LIBRARIES(numeric) + +LIBMV_INSTALL_LIB(numeric) \ No newline at end of file diff --git a/modules/sfm/src/libmv_light/libmv/numeric/function_derivative.h b/modules/sfm/src/libmv_light/libmv/numeric/function_derivative.h new file mode 100644 index 00000000000..9820885f04e --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/numeric/function_derivative.h @@ -0,0 +1,107 @@ +// Copyright (c) 2007, 2008, 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_NUMERIC_DERIVATIVE_H +#define LIBMV_NUMERIC_DERIVATIVE_H + +#include + +#include "libmv/numeric/numeric.h" +#include "libmv/logging/logging.h" + +namespace libmv { + +// Numeric derivative of a function. +// TODO(keir): Consider adding a quadratic approximation. + +enum NumericJacobianMode { + CENTRAL, + FORWARD, +}; + +template +class NumericJacobian { + public: + typedef typename Function::XMatrixType Parameters; + typedef typename Function::XMatrixType::RealScalar XScalar; + typedef typename Function::FMatrixType FMatrixType; + typedef Matrix + JMatrixType; + + NumericJacobian(const Function &f) : f_(f) {} + + // TODO(keir): Perhaps passing the jacobian back by value is not a good idea. + JMatrixType operator()(const Parameters &x) { + // Empirically determined constant. + Parameters eps = x.array().abs() * XScalar(1e-5); + // To handle cases where a paremeter is exactly zero, instead use the mean + // eps for the other dimensions. + XScalar mean_eps = eps.sum() / eps.rows(); + if (mean_eps == XScalar(0)) { + // TODO(keir): Do something better here. + mean_eps = 1e-8; // ~sqrt(machine precision). + } + // TODO(keir): Elimininate this needless function evaluation for the + // central difference case. + FMatrixType fx = f_(x); + const int rows = fx.rows(); + const int cols = x.rows(); + JMatrixType jacobian(rows, cols); + Parameters x_plus_delta = x; + for (int c = 0; c < cols; ++c) { + if (eps(c) == XScalar(0)) { + eps(c) = mean_eps; + } + x_plus_delta(c) = x(c) + eps(c); + jacobian.col(c) = f_(x_plus_delta); + + XScalar one_over_h = 1 / eps(c); + if (mode == CENTRAL) { + x_plus_delta(c) = x(c) - eps(c); + jacobian.col(c) -= f_(x_plus_delta); + one_over_h /= 2; + } else { + jacobian.col(c) -= fx; + } + x_plus_delta(c) = x(c); + jacobian.col(c) = jacobian.col(c) * one_over_h; + } + return jacobian; + } + private: + const Function &f_; +}; + +template +bool CheckJacobian(const Function &f, const typename Function::XMatrixType &x) { + Jacobian j_analytic(f); + NumericJacobian j_numeric(f); + + typename NumericJacobian::JMatrixType J_numeric = j_numeric(x); + typename NumericJacobian::JMatrixType J_analytic = j_analytic(x); + LG << J_numeric - J_analytic; + return true; +} + +} // namespace libmv + +#endif // LIBMV_NUMERIC_DERIVATIVE_H diff --git a/modules/sfm/src/libmv_light/libmv/numeric/levenberg_marquardt.h b/modules/sfm/src/libmv_light/libmv/numeric/levenberg_marquardt.h new file mode 100644 index 00000000000..2af9a62cf7b --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/numeric/levenberg_marquardt.h @@ -0,0 +1,183 @@ +// Copyright (c) 2007, 2008, 2009 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +// A simple implementation of levenberg marquardt. +// +// [1] K. Madsen, H. Nielsen, O. Tingleoff. Methods for Non-linear Least +// Squares Problems. +// http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf +// +// TODO(keir): Cite the Lourakis' dogleg paper. + +#ifndef LIBMV_NUMERIC_LEVENBERG_MARQUARDT_H +#define LIBMV_NUMERIC_LEVENBERG_MARQUARDT_H + +#include + +#include "libmv/numeric/numeric.h" +#include "libmv/numeric/function_derivative.h" +#include "libmv/logging/logging.h" + +namespace libmv { + +template, + typename Solver = Eigen::PartialPivLU< + Matrix > > +class LevenbergMarquardt { + public: + typedef typename Function::XMatrixType::RealScalar Scalar; + typedef typename Function::FMatrixType FVec; + typedef typename Function::XMatrixType Parameters; + typedef Matrix JMatrixType; + typedef Matrix AMatrixType; + + // TODO(keir): Some of these knobs can be derived from each other and + // removed, instead of requiring the user to set them. + enum Status { + RUNNING, + GRADIENT_TOO_SMALL, // eps > max(J'*f(x)) + RELATIVE_STEP_SIZE_TOO_SMALL, // eps > ||dx|| / ||x|| + ERROR_TOO_SMALL, // eps > ||f(x)|| + HIT_MAX_ITERATIONS, + }; + + LevenbergMarquardt(const Function &f) + : f_(f), df_(f) {} + + struct SolverParameters { + SolverParameters() + : gradient_threshold(1e-16), + relative_step_threshold(1e-16), + error_threshold(1e-16), + initial_scale_factor(1e-3), + max_iterations(100) {} + Scalar gradient_threshold; // eps > max(J'*f(x)) + Scalar relative_step_threshold; // eps > ||dx|| / ||x|| + Scalar error_threshold; // eps > ||f(x)|| + Scalar initial_scale_factor; // Initial u for solving normal equations. + int max_iterations; // Maximum number of solver iterations. + }; + + struct Results { + Scalar error_magnitude; // ||f(x)|| + Scalar gradient_magnitude; // ||J'f(x)|| + int iterations; + Status status; + }; + + Status Update(const Parameters &x, const SolverParameters ¶ms, + JMatrixType *J, AMatrixType *A, FVec *error, Parameters *g) { + *J = df_(x); + *A = (*J).transpose() * (*J); + *error = -f_(x); + *g = (*J).transpose() * *error; + if (g->array().abs().maxCoeff() < params.gradient_threshold) { + return GRADIENT_TOO_SMALL; + } else if (error->norm() < params.error_threshold) { + return ERROR_TOO_SMALL; + } + return RUNNING; + } + + Results minimize(Parameters *x_and_min) { + SolverParameters params; + minimize(params, x_and_min); + } + + Results minimize(const SolverParameters ¶ms, Parameters *x_and_min) { + Parameters &x = *x_and_min; + JMatrixType J; + AMatrixType A; + FVec error; + Parameters g; + + Results results; + results.status = Update(x, params, &J, &A, &error, &g); + + Scalar u = Scalar(params.initial_scale_factor*A.diagonal().maxCoeff()); + Scalar v = 2; + + Parameters dx, x_new; + int i; + for (i = 0; results.status == RUNNING && i < params.max_iterations; ++i) { + VLOG(3) << "iteration: " << i; + VLOG(3) << "||f(x)||: " << f_(x).norm(); + VLOG(3) << "max(g): " << g.array().abs().maxCoeff(); + VLOG(3) << "u: " << u; + VLOG(3) << "v: " << v; + + AMatrixType A_augmented = A + u*AMatrixType::Identity(J.cols(), J.cols()); + Solver solver(A_augmented); + dx = solver.solve(g); + bool solved = (A_augmented * dx).isApprox(g); + if (!solved) { + LOG(ERROR) << "Failed to solve"; + } + if (solved && dx.norm() <= params.relative_step_threshold * x.norm()) { + results.status = RELATIVE_STEP_SIZE_TOO_SMALL; + break; + } + if (solved) { + x_new = x + dx; + // Rho is the ratio of the actual reduction in error to the reduction + // in error that would be obtained if the problem was linear. + // See [1] for details. + Scalar rho((error.squaredNorm() - f_(x_new).squaredNorm()) + / dx.dot(u*dx + g)); + if (rho > 0) { + // Accept the Gauss-Newton step because the linear model fits well. + x = x_new; + results.status = Update(x, params, &J, &A, &error, &g); + Scalar tmp = Scalar(2*rho-1); + u = u*std::max(1/3., 1 - (tmp*tmp*tmp)); + v = 2; + continue; + } + } + // Reject the update because either the normal equations failed to solve + // or the local linear model was not good (rho < 0). Instead, increase u + // to move closer to gradient descent. + u *= v; + v *= 2; + } + if (results.status == RUNNING) { + results.status = HIT_MAX_ITERATIONS; + } + results.error_magnitude = error.norm(); + results.gradient_magnitude = g.norm(); + results.iterations = i; + return results; + } + + private: + const Function &f_; + Jacobian df_; +}; + +} // namespace mv + +#endif // LIBMV_NUMERIC_LEVENBERG_MARQUARDT_H diff --git a/modules/sfm/src/libmv_light/libmv/numeric/numeric.cc b/modules/sfm/src/libmv_light/libmv/numeric/numeric.cc new file mode 100644 index 00000000000..9007663c8e2 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/numeric/numeric.cc @@ -0,0 +1,136 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +Mat3 RotationAroundX(double angle) { + double c, s; + sincos(angle, &s, &c); + Mat3 R; + R << 1, 0, 0, + 0, c, -s, + 0, s, c; + return R; +} + +Mat3 RotationAroundY(double angle) { + double c, s; + sincos(angle, &s, &c); + Mat3 R; + R << c, 0, s, + 0, 1, 0, + -s, 0, c; + return R; +} + +Mat3 RotationAroundZ(double angle) { + double c, s; + sincos(angle, &s, &c); + Mat3 R; + R << c, -s, 0, + s, c, 0, + 0, 0, 1; + return R; +} + + +Mat3 RotationRodrigues(const Vec3 &axis) { + double theta = axis.norm(); + Vec3 w = axis / theta; + Mat3 W = CrossProductMatrix(w); + + return Mat3::Identity() + sin(theta) * W + (1 - cos(theta)) * W * W; +} + + +Mat3 LookAt(Vec3 center) { + Vec3 zc = center.normalized(); + Vec3 xc = Vec3::UnitY().cross(zc).normalized(); + Vec3 yc = zc.cross(xc); + Mat3 R; + R.row(0) = xc; + R.row(1) = yc; + R.row(2) = zc; + return R; +} + +Mat3 CrossProductMatrix(const Vec3 &x) { + Mat3 X; + X << 0, -x(2), x(1), + x(2), 0, -x(0), + -x(1), x(0), 0; + return X; +} + +void MeanAndVarianceAlongRows(const Mat &A, + Vec *mean_pointer, + Vec *variance_pointer) { + Vec &mean = *mean_pointer; + Vec &variance = *variance_pointer; + int n = A.rows(); + int m = A.cols(); + mean.resize(n); + variance.resize(n); + + for (int i = 0; i < n; ++i) { + mean(i) = 0; + variance(i) = 0; + for (int j = 0; j < m; ++j) { + double x = A(i, j); + mean(i) += x; + variance(i) += x * x; + } + } + + mean /= m; + for (int i = 0; i < n; ++i) { + variance(i) = variance(i) / m - Square(mean(i)); + } +} + +void HorizontalStack(const Mat &left, const Mat &right, Mat *stacked) { + assert(left.rows() == left.rows()); + int n = left.rows(); + int m1 = left.cols(); + int m2 = right.cols(); + + stacked->resize(n, m1 + m2); + stacked->block(0, 0, n, m1) = left; + stacked->block(0, m1, n, m2) = right; +} + +void MatrixColumn(const Mat &A, int i, Vec2 *v) { + assert(A.rows() == 2); + *v << A(0, i), A(1, i); +} +void MatrixColumn(const Mat &A, int i, Vec3 *v) { + assert(A.rows() == 3); + *v << A(0, i), A(1, i), A(2, i); +} +void MatrixColumn(const Mat &A, int i, Vec4 *v) { + assert(A.rows() == 4); + *v << A(0, i), A(1, i), A(2, i), A(3, i); +} + +} // namespace libmv + diff --git a/modules/sfm/src/libmv_light/libmv/numeric/numeric.h b/modules/sfm/src/libmv_light/libmv/numeric/numeric.h new file mode 100644 index 00000000000..55d4c7d4651 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/numeric/numeric.h @@ -0,0 +1,502 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +// Matrix and vector classes, based on Eigen2. +// +// Avoid using Eigen2 classes directly; instead typedef them here. + +#ifndef LIBMV_NUMERIC_NUMERIC_H +#define LIBMV_NUMERIC_NUMERIC_H + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__MINGW64__) +# if defined(_WIN32) || defined(__APPLE__) || \ + defined(__FreeBSD__) || defined(__NetBSD__) +static void sincos(double x, double *sinx, double *cosx) { + *sinx = sin(x); + *cosx = cos(x); +} +# endif +#endif // !__MINGW64__ + +#if (defined(WIN32) || defined(WIN64)) && !defined(__MINGW32__) +inline long lround(double d) { + return (long)(d>0 ? d+0.5 : ceil(d-0.5)); +} +# if _MSC_VER < 1800 +inline int round(double d) { + return (d>0) ? int(d+0.5) : int(d-0.5); +} +# endif // _MSC_VER < 1800 +typedef unsigned int uint; +#endif // _WIN32 + +namespace libmv { + +typedef Eigen::MatrixXd Mat; +typedef Eigen::VectorXd Vec; + +typedef Eigen::MatrixXf Matf; +typedef Eigen::VectorXf Vecf; + +typedef Eigen::Matrix Matu; +typedef Eigen::Matrix Vecu; +typedef Eigen::Matrix Vec2u; + +typedef Eigen::Matrix Mat2; +typedef Eigen::Matrix Mat23; +typedef Eigen::Matrix Mat3; +typedef Eigen::Matrix Mat34; +typedef Eigen::Matrix Mat35; +typedef Eigen::Matrix Mat41; +typedef Eigen::Matrix Mat43; +typedef Eigen::Matrix Mat4; +typedef Eigen::Matrix Mat46; +typedef Eigen::Matrix Mat2f; +typedef Eigen::Matrix Mat23f; +typedef Eigen::Matrix Mat3f; +typedef Eigen::Matrix Mat34f; +typedef Eigen::Matrix Mat35f; +typedef Eigen::Matrix Mat43f; +typedef Eigen::Matrix Mat4f; +typedef Eigen::Matrix Mat46f; + +typedef Eigen::Matrix RMat3; +typedef Eigen::Matrix RMat4; + +typedef Eigen::Matrix Mat2X; +typedef Eigen::Matrix Mat3X; +typedef Eigen::Matrix Mat4X; +typedef Eigen::Matrix MatX2; +typedef Eigen::Matrix MatX3; +typedef Eigen::Matrix MatX4; +typedef Eigen::Matrix MatX5; +typedef Eigen::Matrix MatX6; +typedef Eigen::Matrix MatX7; +typedef Eigen::Matrix MatX8; +typedef Eigen::Matrix MatX9; +typedef Eigen::Matrix MatX15; +typedef Eigen::Matrix MatX16; + +typedef Eigen::Vector2d Vec2; +typedef Eigen::Vector3d Vec3; +typedef Eigen::Vector4d Vec4; +typedef Eigen::Matrix Vec5; +typedef Eigen::Matrix Vec6; +typedef Eigen::Matrix Vec7; +typedef Eigen::Matrix Vec8; +typedef Eigen::Matrix Vec9; +typedef Eigen::Matrix Vec10; +typedef Eigen::Matrix Vec11; +typedef Eigen::Matrix Vec12; +typedef Eigen::Matrix Vec13; +typedef Eigen::Matrix Vec14; +typedef Eigen::Matrix Vec15; +typedef Eigen::Matrix Vec16; +typedef Eigen::Matrix Vec17; +typedef Eigen::Matrix Vec18; +typedef Eigen::Matrix Vec19; +typedef Eigen::Matrix Vec20; + +typedef Eigen::Vector2f Vec2f; +typedef Eigen::Vector3f Vec3f; +typedef Eigen::Vector4f Vec4f; + +typedef Eigen::VectorXi VecXi; + +typedef Eigen::Vector2i Vec2i; +typedef Eigen::Vector3i Vec3i; +typedef Eigen::Vector4i Vec4i; + +typedef Eigen::Matrix RMatf; + +typedef Eigen::NumTraits EigenDouble; + +using Eigen::Map; +using Eigen::Dynamic; +using Eigen::Matrix; + +// Find U, s, and VT such that +// +// A = U * diag(s) * VT +// +template +inline void SVD(TMat *A, Vec *s, Mat *U, Mat *VT) { + assert(0); +} + +// Solve the linear system Ax = 0 via SVD. Store the solution in x, such that +// ||x|| = 1.0. Return the singluar value corresponding to the solution. +// Destroys A and resizes x if necessary. +// TODO(maclean): Take the SVD of the transpose instead of this zero padding. +template +double Nullspace(TMat *A, TVec *nullspace) { + Eigen::JacobiSVD svd(*A, Eigen::ComputeFullV); + (*nullspace) = svd.matrixV().col(A->cols()-1); + if (A->rows() >= A->cols()) + return svd.singularValues()(A->cols()-1); + else + return 0.0; +} + +// Solve the linear system Ax = 0 via SVD. Finds two solutions, x1 and x2, such +// that x1 is the best solution and x2 is the next best solution (in the L2 +// norm sense). Store the solution in x1 and x2, such that ||x|| = 1.0. Return +// the singluar value corresponding to the solution x1. Destroys A and resizes +// x if necessary. +template +double Nullspace2(TMat *A, TVec1 *x1, TVec2 *x2) { + Eigen::JacobiSVD svd(*A, Eigen::ComputeFullV); + *x1 = svd.matrixV().col(A->cols() - 1); + *x2 = svd.matrixV().col(A->cols() - 2); + if (A->rows() >= A->cols()) + return svd.singularValues()(A->cols()-1); + else + return 0.0; +} + +// In place transpose for square matrices. +template +inline void TransposeInPlace(TA *A) { + *A = A->transpose().eval(); +} + +template +inline double NormL1(const TVec &x) { + return x.array().abs().sum(); +} + +template +inline double NormL2(const TVec &x) { + return x.norm(); +} + +template +inline double NormLInfinity(const TVec &x) { + return x.array().abs().maxCoeff(); +} + +template +inline double DistanceL1(const TVec &x, const TVec &y) { + return (x - y).array().abs().sum(); +} + +template +inline double DistanceL2(const TVec &x, const TVec &y) { + return (x - y).norm(); +} +template +inline double DistanceLInfinity(const TVec &x, const TVec &y) { + return (x - y).array().abs().maxCoeff(); +} + +// Normalize a vector with the L1 norm, and return the norm before it was +// normalized. +template +inline double NormalizeL1(TVec *x) { + double norm = NormL1(*x); + *x /= norm; + return norm; +} + +// Normalize a vector with the L2 norm, and return the norm before it was +// normalized. +template +inline double NormalizeL2(TVec *x) { + double norm = NormL2(*x); + *x /= norm; + return norm; +} + +// Normalize a vector with the L^Infinity norm, and return the norm before it +// was normalized. +template +inline double NormalizeLInfinity(TVec *x) { + double norm = NormLInfinity(*x); + *x /= norm; + return norm; +} + +// Return the square of a number. +template +inline T Square(T x) { + return x * x; +} + +Mat3 RotationAroundX(double angle); +Mat3 RotationAroundY(double angle); +Mat3 RotationAroundZ(double angle); + +// Returns the rotation matrix of a rotation of angle |axis| around axis. +// This is computed using the Rodrigues formula, see: +// http://mathworld.wolfram.com/RodriguesRotationFormula.html +Mat3 RotationRodrigues(const Vec3 &axis); + +// Make a rotation matrix such that center becomes the direction of the +// positive z-axis, and y is oriented close to up. +Mat3 LookAt(Vec3 center); + +// Return a diagonal matrix from a vector containg the diagonal values. +template +inline Mat Diag(const TVec &x) { + return x.asDiagonal(); +} + +template +inline double FrobeniusNorm(const TMat &A) { + return sqrt(A.array().abs2().sum()); +} + +template +inline double FrobeniusDistance(const TMat &A, const TMat &B) { + return FrobeniusNorm(A - B); +} + +inline Vec3 CrossProduct(const Vec3 &x, const Vec3 &y) { + return x.cross(y); +} + +Mat3 CrossProductMatrix(const Vec3 &x); + +void MeanAndVarianceAlongRows(const Mat &A, + Vec *mean_pointer, + Vec *variance_pointer); + +#if _WIN32 + // TODO(bomboze): un-#if this for both platforms once tested under Windows + /* This solution was extensively discussed here + http://forum.kde.org/viewtopic.php?f=74&t=61940 */ + #define SUM_OR_DYNAMIC(x, y) (x == Eigen::Dynamic || y == Eigen::Dynamic) ? Eigen::Dynamic : (x+y) + + template + struct hstack_return { + typedef typename Derived1::Scalar Scalar; + enum { + RowsAtCompileTime = Derived1::RowsAtCompileTime, + ColsAtCompileTime = SUM_OR_DYNAMIC(Derived1::ColsAtCompileTime, + Derived2::ColsAtCompileTime), + Options = Derived1::Flags&Eigen::RowMajorBit ? Eigen::RowMajor : 0, + MaxRowsAtCompileTime = Derived1::MaxRowsAtCompileTime, + MaxColsAtCompileTime = SUM_OR_DYNAMIC(Derived1::MaxColsAtCompileTime, + Derived2::MaxColsAtCompileTime) + }; + typedef Eigen::Matrix type; + }; + + template + typename hstack_return::type + HStack(const Eigen::MatrixBase& lhs, + const Eigen::MatrixBase& rhs) { + typename hstack_return::type res; + res.resize(lhs.rows(), lhs.cols()+rhs.cols()); + res << lhs, rhs; + return res; + }; + + + template + struct vstack_return { + typedef typename Derived1::Scalar Scalar; + enum { + RowsAtCompileTime = SUM_OR_DYNAMIC(Derived1::RowsAtCompileTime, + Derived2::RowsAtCompileTime), + ColsAtCompileTime = Derived1::ColsAtCompileTime, + Options = Derived1::Flags&Eigen::RowMajorBit ? Eigen::RowMajor : 0, + MaxRowsAtCompileTime = SUM_OR_DYNAMIC(Derived1::MaxRowsAtCompileTime, + Derived2::MaxRowsAtCompileTime), + MaxColsAtCompileTime = Derived1::MaxColsAtCompileTime + }; + typedef Eigen::Matrix type; + }; + + template + typename vstack_return::type + VStack(const Eigen::MatrixBase& lhs, + const Eigen::MatrixBase& rhs) { + typename vstack_return::type res; + res.resize(lhs.rows()+rhs.rows(), lhs.cols()); + res << lhs, rhs; + return res; + }; + + +#else // _WIN32 + + // Since it is not possible to typedef privately here, use a macro. + // Always take dynamic columns if either side is dynamic. + #define COLS \ + ((ColsLeft == Eigen::Dynamic || ColsRight == Eigen::Dynamic) \ + ? Eigen::Dynamic : (ColsLeft + ColsRight)) + + // Same as above, except that prefer fixed size if either is fixed. + #define ROWS \ + ((RowsLeft == Eigen::Dynamic && RowsRight == Eigen::Dynamic) \ + ? Eigen::Dynamic \ + : ((RowsLeft == Eigen::Dynamic) \ + ? RowsRight \ + : RowsLeft \ + ) \ + ) + + // TODO(keir): Add a static assert if both rows are at compiletime. + template + Eigen::Matrix + HStack(const Eigen::Matrix &left, + const Eigen::Matrix &right) { + assert(left.rows() == right.rows()); + int n = left.rows(); + int m1 = left.cols(); + int m2 = right.cols(); + + Eigen::Matrix stacked(n, m1 + m2); + stacked.block(0, 0, n, m1) = left; + stacked.block(0, m1, n, m2) = right; + return stacked; + } + + // Reuse the above macros by swapping the order of Rows and Cols. Nasty, but + // the duplication is worse. + // TODO(keir): Add a static assert if both rows are at compiletime. + // TODO(keir): Mail eigen list about making this work for general expressions + // rather than only matrix types. + template + Eigen::Matrix + VStack(const Eigen::Matrix &top, + const Eigen::Matrix &bottom) { + assert(top.cols() == bottom.cols()); + int n1 = top.rows(); + int n2 = bottom.rows(); + int m = top.cols(); + + Eigen::Matrix stacked(n1 + n2, m); + stacked.block(0, 0, n1, m) = top; + stacked.block(n1, 0, n2, m) = bottom; + return stacked; + } + #undef COLS + #undef ROWS +#endif // _WIN32 + + + +void HorizontalStack(const Mat &left, const Mat &right, Mat *stacked); + +template +void VerticalStack(const TTop &top, const TBot &bottom, TStacked *stacked) { + assert(top.cols() == bottom.cols()); + int n1 = top.rows(); + int n2 = bottom.rows(); + int m = top.cols(); + + stacked->resize(n1 + n2, m); + stacked->block(0, 0, n1, m) = top; + stacked->block(n1, 0, n2, m) = bottom; +} + +void MatrixColumn(const Mat &A, int i, Vec2 *v); +void MatrixColumn(const Mat &A, int i, Vec3 *v); +void MatrixColumn(const Mat &A, int i, Vec4 *v); + +template +TMat ExtractColumns(const TMat &A, const TCols &columns) { + TMat compressed(A.rows(), columns.size()); + for (int i = 0; i < columns.size(); ++i) { + compressed.col(i) = A.col(columns[i]); + } + return compressed; +} + +template +void reshape(const TMat &a, int rows, int cols, TDest *b) { + assert(a.rows()*a.cols() == rows*cols); + b->resize(rows, cols); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + (*b)(i, j) = a[cols*i + j]; + } + } +} + +inline bool isnan(double i) { +#ifdef WIN32 + return _isnan(i) > 0; +#else + return std::isnan(i); +#endif +} + +/// Ceil function that has the same behaviour for positive +/// and negative values +template +FloatType ceil0(const FloatType& value) { + FloatType result = std::ceil(std::fabs(value)); + return (value < 0.0) ? -result : result; +} + +/// Returns the skew anti-symmetric matrix of a vector +inline Mat3 SkewMat(const Vec3 &x) { + Mat3 skew; + skew << 0 , -x(2), x(1), + x(2), 0 , -x(0), + -x(1), x(0), 0; + return skew; +} +/// Returns the skew anti-symmetric matrix of a vector with only +/// the first two (independent) lines +inline Mat23 SkewMatMinimal(const Vec2 &x) { + Mat23 skew; + skew << 0, -1, x(1), + 1, 0, -x(0); + return skew; +} + +/// Returns the rotaiton matrix built from given vector of euler angles +inline Mat3 RotationFromEulerVector(Vec3 euler_vector) { + double theta = euler_vector.norm(); + if (theta == 0.0) { + return Mat3::Identity(); + } + Vec3 w = euler_vector / theta; + Mat3 w_hat = CrossProductMatrix(w); + return Mat3::Identity() + w_hat*sin(theta) + w_hat*w_hat*(1 - cos(theta)); +} +} // namespace libmv + +#endif // LIBMV_NUMERIC_NUMERIC_H diff --git a/modules/sfm/src/libmv_light/libmv/numeric/poly.cc b/modules/sfm/src/libmv_light/libmv/numeric/poly.cc new file mode 100644 index 00000000000..376403616c3 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/numeric/poly.cc @@ -0,0 +1,23 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +// Routines for solving polynomials. + +// TODO(keir): Add a solver for degree > 3 polynomials. diff --git a/modules/sfm/src/libmv_light/libmv/numeric/poly.h b/modules/sfm/src/libmv_light/libmv/numeric/poly.h new file mode 100644 index 00000000000..76ba062d475 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/numeric/poly.h @@ -0,0 +1,123 @@ +// Copyright (c) 2007, 2008 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_NUMERIC_POLY_H_ +#define LIBMV_NUMERIC_POLY_H_ + +#include +#include + +namespace libmv { + +// Solve the cubic polynomial +// +// x^3 + a*x^2 + b*x + c = 0 +// +// The number of roots (from zero to three) is returned. If the number of roots +// is less than three, then higher numbered x's are not changed. For example, +// if there are 2 roots, only x0 and x1 are set. +// +// The GSL cubic solver was used as a reference for this routine. +template +int SolveCubicPolynomial(Real a, Real b, Real c, + Real *x0, Real *x1, Real *x2) { + Real q = a * a - 3 * b; + Real r = 2 * a * a * a - 9 * a * b + 27 * c; + + Real Q = q / 9; + Real R = r / 54; + + Real Q3 = Q * Q * Q; + Real R2 = R * R; + + Real CR2 = 729 * r * r; + Real CQ3 = 2916 * q * q * q; + + if (R == 0 && Q == 0) { + // Tripple root in one place. + *x0 = *x1 = *x2 = -a / 3; + return 3; + + } else if (CR2 == CQ3) { + // This test is actually R2 == Q3, written in a form suitable for exact + // computation with integers. + // + // Due to finite precision some double roots may be missed, and considered + // to be a pair of complex roots z = x +/- epsilon i close to the real + // axis. + Real sqrtQ = sqrt(Q); + if (R > 0) { + *x0 = -2 * sqrtQ - a / 3; + *x1 = sqrtQ - a / 3; + *x2 = sqrtQ - a / 3; + } else { + *x0 = -sqrtQ - a / 3; + *x1 = -sqrtQ - a / 3; + *x2 = 2 * sqrtQ - a / 3; + } + return 3; + + } else if (CR2 < CQ3) { + // This case is equivalent to R2 < Q3. + Real sqrtQ = sqrt(Q); + Real sqrtQ3 = sqrtQ * sqrtQ * sqrtQ; + Real theta = acos(R / sqrtQ3); + Real norm = -2 * sqrtQ; + *x0 = norm * cos(theta / 3) - a / 3; + *x1 = norm * cos((theta + 2.0 * M_PI) / 3) - a / 3; + *x2 = norm * cos((theta - 2.0 * M_PI) / 3) - a / 3; + + // Put the roots in ascending order. + if (*x0 > *x1) { + std::swap(*x0, *x1); + } + if (*x1 > *x2) { + std::swap(*x1, *x2); + if (*x0 > *x1) { + std::swap(*x0, *x1); + } + } + return 3; + } + Real sgnR = (R >= 0 ? 1 : -1); + Real A = -sgnR * pow(fabs(R) + sqrt(R2 - Q3), 1.0/3.0); + Real B = Q / A; + *x0 = A + B - a / 3; + return 1; +} + +// The coefficients are in ascending powers, i.e. coeffs[N]*x^N. +template +int SolveCubicPolynomial(const Real *coeffs, Real *solutions) { + if (coeffs[0] == 0.0) { + // TODO(keir): This is a quadratic not a cubic. Implement a quadratic + // solver! + return 0; + } + Real a = coeffs[2] / coeffs[3]; + Real b = coeffs[1] / coeffs[3]; + Real c = coeffs[0] / coeffs[3]; + return SolveCubicPolynomial(a, b, c, + solutions + 0, + solutions + 1, + solutions + 2); +} +} // namespace libmv +#endif // LIBMV_NUMERIC_POLY_H_ diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/CMakeLists.txt b/modules/sfm/src/libmv_light/libmv/simple_pipeline/CMakeLists.txt new file mode 100644 index 00000000000..42e37919800 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/CMakeLists.txt @@ -0,0 +1,22 @@ +SET(SIMPLE_PIPELINE_SRC + bundle.cc + camera_intrinsics.cc + distortion_models.cc + initialize_reconstruction.cc + intersect.cc + keyframe_selection.cc + pipeline.cc + reconstruction.cc + reconstruction_scale.cc + resect.cc + tracks.cc +) + +# Define the header files so that they appear in IDEs. +FILE(GLOB SIMPLE_PIPELINE_HDRS *.h) + +ADD_LIBRARY(simple_pipeline STATIC ${SIMPLE_PIPELINE_SRC} ${SIMPLE_PIPELINE_HDRS}) + +TARGET_LINK_LIBRARIES(simple_pipeline multiview ${CERES_LIBRARIES}) + +LIBMV_INSTALL_LIB(simple_pipeline) \ No newline at end of file diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/bundle.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/bundle.cc new file mode 100644 index 00000000000..e61650fb371 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/bundle.cc @@ -0,0 +1,658 @@ +// Copyright (c) 2011, 2012, 2013 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/bundle.h" + +#include + +#include "ceres/ceres.h" +#include "ceres/rotation.h" +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" +#include "libmv/multiview/fundamental.h" +#include "libmv/multiview/projection.h" +#include "libmv/numeric/numeric.h" +#include "libmv/simple_pipeline/camera_intrinsics.h" +#include "libmv/simple_pipeline/reconstruction.h" +#include "libmv/simple_pipeline/tracks.h" +#include "libmv/simple_pipeline/distortion_models.h" + +#ifdef _OPENMP +# include +#endif + +namespace libmv { + +// The intrinsics need to get combined into a single parameter block; use these +// enums to index instead of numeric constants. +enum { + // Camera calibration values. + OFFSET_FOCAL_LENGTH, + OFFSET_PRINCIPAL_POINT_X, + OFFSET_PRINCIPAL_POINT_Y, + + // Distortion model coefficients. + OFFSET_K1, + OFFSET_K2, + OFFSET_K3, + OFFSET_P1, + OFFSET_P2, + + // Maximal possible offset. + OFFSET_MAX, +}; + +#define FIRST_DISTORTION_COEFFICIENT OFFSET_K1 +#define LAST_DISTORTION_COEFFICIENT OFFSET_P2 +#define NUM_DISTORTION_COEFFICIENTS \ + (LAST_DISTORTION_COEFFICIENT - FIRST_DISTORTION_COEFFICIENT + 1) + +namespace { + +// Cost functor which computes reprojection error of 3D point X +// on camera defined by angle-axis rotation and it's translation +// (which are in the same block due to optimization reasons). +// +// This functor uses a radial distortion model. +struct OpenCVReprojectionError { + OpenCVReprojectionError(const DistortionModelType distortion_model, + const double observed_x, + const double observed_y, + const double weight) + : distortion_model_(distortion_model), + observed_x_(observed_x), observed_y_(observed_y), + weight_(weight) {} + + template + bool operator()(const T* const intrinsics, + const T* const R_t, // Rotation denoted by angle axis + // followed with translation + const T* const X, // Point coordinates 3x1. + T* residuals) const { + // Unpack the intrinsics. + const T& focal_length = intrinsics[OFFSET_FOCAL_LENGTH]; + const T& principal_point_x = intrinsics[OFFSET_PRINCIPAL_POINT_X]; + const T& principal_point_y = intrinsics[OFFSET_PRINCIPAL_POINT_Y]; + + // Compute projective coordinates: x = RX + t. + T x[3]; + + ceres::AngleAxisRotatePoint(R_t, X, x); + x[0] += R_t[3]; + x[1] += R_t[4]; + x[2] += R_t[5]; + + // Prevent points from going behind the camera. + if (x[2] < T(0)) { + return false; + } + + // Compute normalized coordinates: x /= x[2]. + T xn = x[0] / x[2]; + T yn = x[1] / x[2]; + + T predicted_x, predicted_y; + + // Apply distortion to the normalized points to get (xd, yd). + // TODO(keir): Do early bailouts for zero distortion; these are expensive + // jet operations. + switch (distortion_model_) { + case DISTORTION_MODEL_POLYNOMIAL: + { + const T& k1 = intrinsics[OFFSET_K1]; + const T& k2 = intrinsics[OFFSET_K2]; + const T& k3 = intrinsics[OFFSET_K3]; + const T& p1 = intrinsics[OFFSET_P1]; + const T& p2 = intrinsics[OFFSET_P2]; + + ApplyPolynomialDistortionModel(focal_length, + focal_length, + principal_point_x, + principal_point_y, + k1, k2, k3, + p1, p2, + xn, yn, + &predicted_x, + &predicted_y); + break; + } + case DISTORTION_MODEL_DIVISION: + { + const T& k1 = intrinsics[OFFSET_K1]; + const T& k2 = intrinsics[OFFSET_K2]; + + ApplyDivisionDistortionModel(focal_length, + focal_length, + principal_point_x, + principal_point_y, + k1, k2, + xn, yn, + &predicted_x, + &predicted_y); + break; + } + default: + LOG(FATAL) << "Unknown distortion model"; + } + + // The error is the difference between the predicted and observed position. + residuals[0] = (predicted_x - T(observed_x_)) * weight_; + residuals[1] = (predicted_y - T(observed_y_)) * weight_; + return true; + } + + const DistortionModelType distortion_model_; + const double observed_x_; + const double observed_y_; + const double weight_; +}; + +// Print a message to the log which camera intrinsics are gonna to be optimixed. +void BundleIntrinsicsLogMessage(const int bundle_intrinsics) { + if (bundle_intrinsics == BUNDLE_NO_INTRINSICS) { + LOG(INFO) << "Bundling only camera positions."; + } else { + std::string bundling_message = ""; + +#define APPEND_BUNDLING_INTRINSICS(name, flag) \ + if (bundle_intrinsics & flag) { \ + if (!bundling_message.empty()) { \ + bundling_message += ", "; \ + } \ + bundling_message += name; \ + } (void)0 + + APPEND_BUNDLING_INTRINSICS("f", BUNDLE_FOCAL_LENGTH); + APPEND_BUNDLING_INTRINSICS("px, py", BUNDLE_PRINCIPAL_POINT); + APPEND_BUNDLING_INTRINSICS("k1", BUNDLE_RADIAL_K1); + APPEND_BUNDLING_INTRINSICS("k2", BUNDLE_RADIAL_K2); + APPEND_BUNDLING_INTRINSICS("p1", BUNDLE_TANGENTIAL_P1); + APPEND_BUNDLING_INTRINSICS("p2", BUNDLE_TANGENTIAL_P2); + + LOG(INFO) << "Bundling " << bundling_message << "."; + } +} + +// Pack intrinsics from object to an array for easier +// and faster minimization. +void PackIntrinisicsIntoArray(const CameraIntrinsics &intrinsics, + double ceres_intrinsics[OFFSET_MAX]) { + ceres_intrinsics[OFFSET_FOCAL_LENGTH] = intrinsics.focal_length(); + ceres_intrinsics[OFFSET_PRINCIPAL_POINT_X] = intrinsics.principal_point_x(); + ceres_intrinsics[OFFSET_PRINCIPAL_POINT_Y] = intrinsics.principal_point_y(); + + int num_distortion_parameters = intrinsics.num_distortion_parameters(); + assert(num_distortion_parameters <= NUM_DISTORTION_COEFFICIENTS); + + const double *distortion_parameters = intrinsics.distortion_parameters(); + for (int i = 0; i < num_distortion_parameters; ++i) { + ceres_intrinsics[FIRST_DISTORTION_COEFFICIENT + i] = + distortion_parameters[i]; + } +} + +// Unpack intrinsics back from an array to an object. +void UnpackIntrinsicsFromArray(const double ceres_intrinsics[OFFSET_MAX], + CameraIntrinsics *intrinsics) { + intrinsics->SetFocalLength(ceres_intrinsics[OFFSET_FOCAL_LENGTH], + ceres_intrinsics[OFFSET_FOCAL_LENGTH]); + + intrinsics->SetPrincipalPoint(ceres_intrinsics[OFFSET_PRINCIPAL_POINT_X], + ceres_intrinsics[OFFSET_PRINCIPAL_POINT_Y]); + + int num_distortion_parameters = intrinsics->num_distortion_parameters(); + assert(num_distortion_parameters <= NUM_DISTORTION_COEFFICIENTS); + + double *distortion_parameters = intrinsics->distortion_parameters(); + for (int i = 0; i < num_distortion_parameters; ++i) { + distortion_parameters[i] = + ceres_intrinsics[FIRST_DISTORTION_COEFFICIENT + i]; + } +} + +// Get a vector of camera's rotations denoted by angle axis +// conjuncted with translations into single block +// +// Element with index i matches to a rotation+translation for +// camera at image i. +vector PackCamerasRotationAndTranslation( + const Tracks &tracks, + const EuclideanReconstruction &reconstruction) { + vector all_cameras_R_t; + int max_image = tracks.MaxImage(); + + all_cameras_R_t.resize(max_image + 1); + + for (int i = 0; i <= max_image; i++) { + const EuclideanCamera *camera = reconstruction.CameraForImage(i); + + if (!camera) { + continue; + } + + ceres::RotationMatrixToAngleAxis(&camera->R(0, 0), + &all_cameras_R_t[i](0)); + all_cameras_R_t[i].tail<3>() = camera->t; + } + return all_cameras_R_t; +} + +// Convert cameras rotations fro mangle axis back to rotation matrix. +void UnpackCamerasRotationAndTranslation( + const Tracks &tracks, + const vector &all_cameras_R_t, + EuclideanReconstruction *reconstruction) { + int max_image = tracks.MaxImage(); + + for (int i = 0; i <= max_image; i++) { + EuclideanCamera *camera = reconstruction->CameraForImage(i); + + if (!camera) { + continue; + } + + ceres::AngleAxisToRotationMatrix(&all_cameras_R_t[i](0), + &camera->R(0, 0)); + camera->t = all_cameras_R_t[i].tail<3>(); + } +} + +// Converts sparse CRSMatrix to Eigen matrix, so it could be used +// all over in the pipeline. +// +// TODO(sergey): currently uses dense Eigen matrices, best would +// be to use sparse Eigen matrices +void CRSMatrixToEigenMatrix(const ceres::CRSMatrix &crs_matrix, + Mat *eigen_matrix) { + eigen_matrix->resize(crs_matrix.num_rows, crs_matrix.num_cols); + eigen_matrix->setZero(); + + for (int row = 0; row < crs_matrix.num_rows; ++row) { + int start = crs_matrix.rows[row]; + int end = crs_matrix.rows[row + 1] - 1; + + for (int i = start; i <= end; i++) { + int col = crs_matrix.cols[i]; + double value = crs_matrix.values[i]; + + (*eigen_matrix)(row, col) = value; + } + } +} + +void EuclideanBundlerPerformEvaluation(const Tracks &tracks, + EuclideanReconstruction *reconstruction, + vector *all_cameras_R_t, + ceres::Problem *problem, + BundleEvaluation *evaluation) { + int max_track = tracks.MaxTrack(); + // Number of camera rotations equals to number of translation, + int num_cameras = all_cameras_R_t->size(); + int num_points = 0; + + vector minimized_points; + for (int i = 0; i <= max_track; i++) { + EuclideanPoint *point = reconstruction->PointForTrack(i); + if (point) { + // We need to know whether the track is constant zero weight, + // and it so it wouldn't have parameter block in the problem. + // + // Getting all markers for track is not so bac currently since + // this code is only used by keyframe selection when there are + // not so much tracks and only 2 frames anyway. + vector markera_of_track = tracks.MarkersForTrack(i); + for (int j = 0; j < markera_of_track.size(); j++) { + if (markera_of_track.at(j).weight != 0.0) { + minimized_points.push_back(point); + num_points++; + break; + } + } + } + } + + LG << "Number of cameras " << num_cameras; + LG << "Number of points " << num_points; + + evaluation->num_cameras = num_cameras; + evaluation->num_points = num_points; + + if (evaluation->evaluate_jacobian) { // Evaluate jacobian matrix. + ceres::CRSMatrix evaluated_jacobian; + ceres::Problem::EvaluateOptions eval_options; + + // Cameras goes first in the ordering. + int max_image = tracks.MaxImage(); + for (int i = 0; i <= max_image; i++) { + const EuclideanCamera *camera = reconstruction->CameraForImage(i); + if (camera) { + double *current_camera_R_t = &(*all_cameras_R_t)[i](0); + + // All cameras are variable now. + problem->SetParameterBlockVariable(current_camera_R_t); + + eval_options.parameter_blocks.push_back(current_camera_R_t); + } + } + + // Points goes at the end of ordering, + for (int i = 0; i < minimized_points.size(); i++) { + EuclideanPoint *point = minimized_points.at(i); + eval_options.parameter_blocks.push_back(&point->X(0)); + } + + problem->Evaluate(eval_options, + NULL, NULL, NULL, + &evaluated_jacobian); + + CRSMatrixToEigenMatrix(evaluated_jacobian, &evaluation->jacobian); + } +} + +// This is an utility function to only bundle 3D position of +// given markers list. +// +// Main purpose of this function is to adjust positions of tracks +// which does have constant zero weight and so far only were using +// algebraic intersection to obtain their 3D positions. +// +// At this point we only need to bundle points positions, cameras +// are to be totally still here. +void EuclideanBundlePointsOnly(const DistortionModelType distortion_model, + const vector &markers, + vector &all_cameras_R_t, + double ceres_intrinsics[OFFSET_MAX], + EuclideanReconstruction *reconstruction) { + ceres::Problem::Options problem_options; + ceres::Problem problem(problem_options); + int num_residuals = 0; + for (int i = 0; i < markers.size(); ++i) { + const Marker &marker = markers[i]; + EuclideanCamera *camera = reconstruction->CameraForImage(marker.image); + EuclideanPoint *point = reconstruction->PointForTrack(marker.track); + if (camera == NULL || point == NULL) { + continue; + } + + // Rotation of camera denoted in angle axis followed with + // camera translaiton. + double *current_camera_R_t = &all_cameras_R_t[camera->image](0); + + problem.AddResidualBlock(new ceres::AutoDiffCostFunction< + OpenCVReprojectionError, 2, OFFSET_MAX, 6, 3>( + new OpenCVReprojectionError( + distortion_model, + marker.x, + marker.y, + 1.0)), + NULL, + ceres_intrinsics, + current_camera_R_t, + &point->X(0)); + + problem.SetParameterBlockConstant(current_camera_R_t); + num_residuals++; + } + + LG << "Number of residuals: " << num_residuals; + if (!num_residuals) { + LG << "Skipping running minimizer with zero residuals"; + return; + } + + problem.SetParameterBlockConstant(ceres_intrinsics); + + // Configure the solver. + ceres::Solver::Options options; + options.use_nonmonotonic_steps = true; + options.preconditioner_type = ceres::SCHUR_JACOBI; + options.linear_solver_type = ceres::ITERATIVE_SCHUR; + options.use_explicit_schur_complement = true; + options.use_inner_iterations = true; + options.max_num_iterations = 100; + +#ifdef _OPENMP + options.num_threads = omp_get_max_threads(); + options.num_linear_solver_threads = omp_get_max_threads(); +#endif + + // Solve! + ceres::Solver::Summary summary; + ceres::Solve(options, &problem, &summary); + + LG << "Final report:\n" << summary.FullReport(); + +} + +} // namespace + +void EuclideanBundle(const Tracks &tracks, + EuclideanReconstruction *reconstruction) { + PolynomialCameraIntrinsics empty_intrinsics; + EuclideanBundleCommonIntrinsics(tracks, + BUNDLE_NO_INTRINSICS, + BUNDLE_NO_CONSTRAINTS, + reconstruction, + &empty_intrinsics, + NULL); +} + +void EuclideanBundleCommonIntrinsics( + const Tracks &tracks, + const int bundle_intrinsics, + const int bundle_constraints, + EuclideanReconstruction *reconstruction, + CameraIntrinsics *intrinsics, + BundleEvaluation *evaluation) { + LG << "Original intrinsics: " << *intrinsics; + vector markers = tracks.AllMarkers(); + + // N-th element denotes whether track N is a constant zero-weigthed track. + vector zero_weight_tracks_flags(tracks.MaxTrack() + 1, true); + + // Residual blocks with 10 parameters are unwieldly with Ceres, so pack the + // intrinsics into a single block and rely on local parameterizations to + // control which intrinsics are allowed to vary. + double ceres_intrinsics[OFFSET_MAX]; + PackIntrinisicsIntoArray(*intrinsics, ceres_intrinsics); + + // Convert cameras rotations to angle axis and merge with translation + // into single parameter block for maximal minimization speed. + // + // Block for minimization has got the following structure: + // <3 elements for angle-axis> <3 elements for translation> + vector all_cameras_R_t = + PackCamerasRotationAndTranslation(tracks, *reconstruction); + + // Parameterization used to restrict camera motion for modal solvers. + ceres::SubsetParameterization *constant_translation_parameterization = NULL; + if (bundle_constraints & BUNDLE_NO_TRANSLATION) { + std::vector constant_translation; + + // First three elements are rotation, ast three are translation. + constant_translation.push_back(3); + constant_translation.push_back(4); + constant_translation.push_back(5); + + constant_translation_parameterization = + new ceres::SubsetParameterization(6, constant_translation); + } + + // Add residual blocks to the problem. + ceres::Problem::Options problem_options; + ceres::Problem problem(problem_options); + int num_residuals = 0; + bool have_locked_camera = false; + for (int i = 0; i < markers.size(); ++i) { + const Marker &marker = markers[i]; + EuclideanCamera *camera = reconstruction->CameraForImage(marker.image); + EuclideanPoint *point = reconstruction->PointForTrack(marker.track); + if (camera == NULL || point == NULL) { + continue; + } + + // Rotation of camera denoted in angle axis followed with + // camera translaiton. + double *current_camera_R_t = &all_cameras_R_t[camera->image](0); + + // Skip residual block for markers which does have absolutely + // no affect on the final solution. + // This way ceres is not gonna to go crazy. + if (marker.weight != 0.0) { + problem.AddResidualBlock(new ceres::AutoDiffCostFunction< + OpenCVReprojectionError, 2, OFFSET_MAX, 6, 3>( + new OpenCVReprojectionError( + intrinsics->GetDistortionModelType(), + marker.x, + marker.y, + marker.weight)), + NULL, + ceres_intrinsics, + current_camera_R_t, + &point->X(0)); + + // We lock the first camera to better deal with scene orientation ambiguity. + if (!have_locked_camera) { + problem.SetParameterBlockConstant(current_camera_R_t); + have_locked_camera = true; + } + + if (bundle_constraints & BUNDLE_NO_TRANSLATION) { + problem.SetParameterization(current_camera_R_t, + constant_translation_parameterization); + } + + zero_weight_tracks_flags[marker.track] = false; + num_residuals++; + } + } + LG << "Number of residuals: " << num_residuals; + + if (!num_residuals) { + LG << "Skipping running minimizer with zero residuals"; + return; + } + + if (intrinsics->GetDistortionModelType() == DISTORTION_MODEL_DIVISION && + (bundle_intrinsics & BUNDLE_TANGENTIAL) != 0) { + LOG(FATAL) << "Division model doesn't support bundling " + "of tangential distortion"; + } + + BundleIntrinsicsLogMessage(bundle_intrinsics); + + if (bundle_intrinsics == BUNDLE_NO_INTRINSICS) { + // No camera intrinsics are being refined, + // set the whole parameter block as constant for best performance. + problem.SetParameterBlockConstant(ceres_intrinsics); + } else { + // Set the camera intrinsics that are not to be bundled as + // constant using some macro trickery. + + std::vector constant_intrinsics; +#define MAYBE_SET_CONSTANT(bundle_enum, offset) \ + if (!(bundle_intrinsics & bundle_enum)) { \ + constant_intrinsics.push_back(offset); \ + } + MAYBE_SET_CONSTANT(BUNDLE_FOCAL_LENGTH, OFFSET_FOCAL_LENGTH); + MAYBE_SET_CONSTANT(BUNDLE_PRINCIPAL_POINT, OFFSET_PRINCIPAL_POINT_X); + MAYBE_SET_CONSTANT(BUNDLE_PRINCIPAL_POINT, OFFSET_PRINCIPAL_POINT_Y); + MAYBE_SET_CONSTANT(BUNDLE_RADIAL_K1, OFFSET_K1); + MAYBE_SET_CONSTANT(BUNDLE_RADIAL_K2, OFFSET_K2); + MAYBE_SET_CONSTANT(BUNDLE_TANGENTIAL_P1, OFFSET_P1); + MAYBE_SET_CONSTANT(BUNDLE_TANGENTIAL_P2, OFFSET_P2); +#undef MAYBE_SET_CONSTANT + + // Always set K3 constant, it's not used at the moment. + constant_intrinsics.push_back(OFFSET_K3); + + ceres::SubsetParameterization *subset_parameterization = + new ceres::SubsetParameterization(OFFSET_MAX, constant_intrinsics); + + problem.SetParameterization(ceres_intrinsics, subset_parameterization); + } + + // Configure the solver. + ceres::Solver::Options options; + options.use_nonmonotonic_steps = true; + options.preconditioner_type = ceres::SCHUR_JACOBI; + options.linear_solver_type = ceres::ITERATIVE_SCHUR; + options.use_explicit_schur_complement = true; + options.use_inner_iterations = true; + options.max_num_iterations = 100; + +#ifdef _OPENMP + options.num_threads = omp_get_max_threads(); + options.num_linear_solver_threads = omp_get_max_threads(); +#endif + + // Solve! + ceres::Solver::Summary summary; + ceres::Solve(options, &problem, &summary); + + LG << "Final report:\n" << summary.FullReport(); + + // Copy rotations and translations back. + UnpackCamerasRotationAndTranslation(tracks, + all_cameras_R_t, + reconstruction); + + // Copy intrinsics back. + if (bundle_intrinsics != BUNDLE_NO_INTRINSICS) + UnpackIntrinsicsFromArray(ceres_intrinsics, intrinsics); + + LG << "Final intrinsics: " << *intrinsics; + + if (evaluation) { + EuclideanBundlerPerformEvaluation(tracks, reconstruction, &all_cameras_R_t, + &problem, evaluation); + } + + // Separate step to adjust positions of tracks which are + // constant zero-weighted. + vector zero_weight_markers; + for (int track = 0; track < tracks.MaxTrack(); ++track) { + if (zero_weight_tracks_flags[track]) { + vector current_markers = tracks.MarkersForTrack(track); + zero_weight_markers.reserve(zero_weight_markers.size() + + current_markers.size()); + for (int i = 0; i < current_markers.size(); ++i) { + zero_weight_markers.push_back(current_markers[i]); + } + } + } + + if (zero_weight_markers.size()) { + LG << "Refining position of constant zero-weighted tracks"; + EuclideanBundlePointsOnly(intrinsics->GetDistortionModelType(), + zero_weight_markers, + all_cameras_R_t, + ceres_intrinsics, + reconstruction); + } +} + +void ProjectiveBundle(const Tracks & /*tracks*/, + ProjectiveReconstruction * /*reconstruction*/) { + // TODO(keir): Implement this! This can't work until we have a better bundler + // than SSBA, since SSBA has no support for projective bundling. +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/bundle.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/bundle.h new file mode 100644 index 00000000000..06aafdf876e --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/bundle.h @@ -0,0 +1,147 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_BUNDLE_H +#define LIBMV_SIMPLE_PIPELINE_BUNDLE_H + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +class CameraIntrinsics; +class EuclideanReconstruction; +class ProjectiveReconstruction; +class Tracks; + +struct BundleEvaluation { + BundleEvaluation() : + num_cameras(0), + num_points(0), + evaluate_jacobian(false) { + } + + // Number of cameras appeared in bundle adjustment problem + int num_cameras; + + // Number of points appeared in bundle adjustment problem + int num_points; + + // When set to truth, jacobian of the problem after optimization + // will be evaluated and stored in \parameter jacobian + bool evaluate_jacobian; + + // Contains evaluated jacobian of the problem. + // Parameters are ordered in the following way: + // - Intrinsics block + // - Cameras (for each camera rotation goes first, then translation) + // - Points + Mat jacobian; +}; + +/*! + Refine camera poses and 3D coordinates using bundle adjustment. + + This routine adjusts all cameras and points in \a *reconstruction. This + assumes a full observation for reconstructed tracks; this implies that if + there is a reconstructed 3D point (a bundle) for a track, then all markers + for that track will be included in the minimization. \a tracks should + contain markers used in the initial reconstruction. + + The cameras and bundles (3D points) are refined in-place. + + \note This assumes an outlier-free set of markers. + \note This assumes a calibrated reconstruction, e.g. the markers are + already corrected for camera intrinsics and radial distortion. + + \sa EuclideanResect, EuclideanIntersect, EuclideanReconstructTwoFrames +*/ +void EuclideanBundle(const Tracks &tracks, + EuclideanReconstruction *reconstruction); + +/*! + Refine camera poses and 3D coordinates using bundle adjustment. + + This routine adjusts all cameras positions, points, and the camera + intrinsics (assumed common across all images) in \a *reconstruction. This + assumes a full observation for reconstructed tracks; this implies that if + there is a reconstructed 3D point (a bundle) for a track, then all markers + for that track will be included in the minimization. \a tracks should + contain markers used in the initial reconstruction. + + The cameras, bundles, and intrinsics are refined in-place. + + Constraints denotes which blocks to keep constant during bundling. + For example it is useful to keep camera translations constant + when bundling tripod motions. + + If evaluaiton is not null, different evaluation statistics is filled in + there, plus all the requested additional information (like jacobian) is + also calculating there. Also see comments for BundleEvaluation. + + \note This assumes an outlier-free set of markers. + + \sa EuclideanResect, EuclideanIntersect, EuclideanReconstructTwoFrames +*/ +enum BundleIntrinsics { + BUNDLE_NO_INTRINSICS = 0, + BUNDLE_FOCAL_LENGTH = 1, + BUNDLE_PRINCIPAL_POINT = 2, + BUNDLE_RADIAL_K1 = 4, + BUNDLE_RADIAL_K2 = 8, + BUNDLE_RADIAL = 12, + BUNDLE_TANGENTIAL_P1 = 16, + BUNDLE_TANGENTIAL_P2 = 32, + BUNDLE_TANGENTIAL = 48, +}; +enum BundleConstraints { + BUNDLE_NO_CONSTRAINTS = 0, + BUNDLE_NO_TRANSLATION = 1, +}; +void EuclideanBundleCommonIntrinsics( + const Tracks &tracks, + const int bundle_intrinsics, + const int bundle_constraints, + EuclideanReconstruction *reconstruction, + CameraIntrinsics *intrinsics, + BundleEvaluation *evaluation = NULL); + +/*! + Refine camera poses and 3D coordinates using bundle adjustment. + + This routine adjusts all cameras and points in \a *reconstruction. This + assumes a full observation for reconstructed tracks; this implies that if + there is a reconstructed 3D point (a bundle) for a track, then all markers + for that track will be included in the minimization. \a tracks should + contain markers used in the initial reconstruction. + + The cameras and bundles (homogeneous 3D points) are refined in-place. + + \note This assumes an outlier-free set of markers. + \note This assumes that radial distortion is already corrected for, but + does not assume that that other intrinsics are. + + \sa ProjectiveResect, ProjectiveIntersect, ProjectiveReconstructTwoFrames +*/ +void ProjectiveBundle(const Tracks &tracks, + ProjectiveReconstruction *reconstruction); + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_BUNDLE_H \ No newline at end of file diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/callbacks.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/callbacks.h new file mode 100644 index 00000000000..58f7b0d3cc9 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/callbacks.h @@ -0,0 +1,34 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_CALLBACKS_H_ +#define LIBMV_SIMPLE_PIPELINE_CALLBACKS_H_ + +namespace libmv { + +class ProgressUpdateCallback { + public: + virtual ~ProgressUpdateCallback() {} + virtual void invoke(double progress, const char *message) = 0; +}; + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_MARKERS_H_ diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/camera_intrinsics.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/camera_intrinsics.cc new file mode 100644 index 00000000000..5e4e07b3c4c --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/camera_intrinsics.cc @@ -0,0 +1,293 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/camera_intrinsics.h" + +#include "libmv/logging/logging.h" +#include "libmv/simple_pipeline/distortion_models.h" + +namespace libmv { + +namespace internal { + +LookupWarpGrid::LookupWarpGrid() + : offset_(NULL), + width_(0), + height_(0), + overscan_(0.0), + threads_(1) {} + +LookupWarpGrid::LookupWarpGrid(const LookupWarpGrid &from) + : offset_(NULL), + width_(from.width_), + height_(from.height_), + overscan_(from.overscan_), + threads_(from.threads_) { + if (from.offset_) { + offset_ = new Offset[width_ * height_]; + memcpy(offset_, from.offset_, sizeof(Offset) * width_ * height_); + } +} + +LookupWarpGrid::~LookupWarpGrid() { + delete [] offset_; +} + +void LookupWarpGrid::Reset() { + delete [] offset_; + offset_ = NULL; +} + +// Set number of threads used for threaded buffer distortion/undistortion. +void LookupWarpGrid::SetThreads(int threads) { + threads_ = threads; +} + +} // namespace internal + +CameraIntrinsics::CameraIntrinsics() + : image_width_(0), + image_height_(0), + K_(Mat3::Identity()) {} + +CameraIntrinsics::CameraIntrinsics(const CameraIntrinsics &from) + : image_width_(from.image_width_), + image_height_(from.image_height_), + K_(from.K_), + distort_(from.distort_), + undistort_(from.undistort_) {} + +// Set the image size in pixels. +void CameraIntrinsics::SetImageSize(int width, int height) { + image_width_ = width; + image_height_ = height; + ResetLookupGrids(); +} + +// Set the entire calibration matrix at once. +void CameraIntrinsics::SetK(const Mat3 new_k) { + K_ = new_k; + ResetLookupGrids(); +} + +// Set both x and y focal length in pixels. +void CameraIntrinsics::SetFocalLength(double focal_x, + double focal_y) { + K_(0, 0) = focal_x; + K_(1, 1) = focal_y; + ResetLookupGrids(); +} + +// Set principal point in pixels. +void CameraIntrinsics::SetPrincipalPoint(double cx, + double cy) { + K_(0, 2) = cx; + K_(1, 2) = cy; + ResetLookupGrids(); +} + +// Set number of threads used for threaded buffer distortion/undistortion. +void CameraIntrinsics::SetThreads(int threads) { + distort_.SetThreads(threads); + undistort_.SetThreads(threads); +} + +void CameraIntrinsics::ImageSpaceToNormalized(double image_x, + double image_y, + double *normalized_x, + double *normalized_y) const { + *normalized_x = (image_x - principal_point_x()) / focal_length_x(); + *normalized_y = (image_y - principal_point_y()) / focal_length_y(); +} + +void CameraIntrinsics::NormalizedToImageSpace(double normalized_x, + double normalized_y, + double *image_x, + double *image_y) const { + *image_x = normalized_x * focal_length_x() + principal_point_x(); + *image_y = normalized_y * focal_length_y() + principal_point_y(); +} + +// Reset lookup grids after changing the distortion model. +void CameraIntrinsics::ResetLookupGrids() { + distort_.Reset(); + undistort_.Reset(); +} + +PolynomialCameraIntrinsics::PolynomialCameraIntrinsics() + : CameraIntrinsics() { + SetRadialDistortion(0.0, 0.0, 0.0); + SetTangentialDistortion(0.0, 0.0); +} + +PolynomialCameraIntrinsics::PolynomialCameraIntrinsics( + const PolynomialCameraIntrinsics &from) + : CameraIntrinsics(from) { + SetRadialDistortion(from.k1(), from.k2(), from.k3()); + SetTangentialDistortion(from.p1(), from.p2()); +} + +void PolynomialCameraIntrinsics::SetRadialDistortion(double k1, + double k2, + double k3) { + parameters_[OFFSET_K1] = k1; + parameters_[OFFSET_K2] = k2; + parameters_[OFFSET_K3] = k3; + ResetLookupGrids(); +} + +void PolynomialCameraIntrinsics::SetTangentialDistortion(double p1, + double p2) { + parameters_[OFFSET_P1] = p1; + parameters_[OFFSET_P2] = p2; + ResetLookupGrids(); +} + +void PolynomialCameraIntrinsics::ApplyIntrinsics(double normalized_x, + double normalized_y, + double *image_x, + double *image_y) const { + ApplyPolynomialDistortionModel(focal_length_x(), + focal_length_y(), + principal_point_x(), + principal_point_y(), + k1(), k2(), k3(), + p1(), p2(), + normalized_x, + normalized_y, + image_x, + image_y); +} + +void PolynomialCameraIntrinsics::InvertIntrinsics( + double image_x, + double image_y, + double *normalized_x, + double *normalized_y) const { + InvertPolynomialDistortionModel(focal_length_x(), + focal_length_y(), + principal_point_x(), + principal_point_y(), + k1(), k2(), k3(), + p1(), p2(), + image_x, + image_y, + normalized_x, + normalized_y); +} + +DivisionCameraIntrinsics::DivisionCameraIntrinsics() + : CameraIntrinsics() { + SetDistortion(0.0, 0.0); +} + +DivisionCameraIntrinsics::DivisionCameraIntrinsics( + const DivisionCameraIntrinsics &from) + : CameraIntrinsics(from) { + SetDistortion(from.k1(), from.k1()); +} + +void DivisionCameraIntrinsics::SetDistortion(double k1, + double k2) { + parameters_[OFFSET_K1] = k1; + parameters_[OFFSET_K2] = k2; + ResetLookupGrids(); +} + +void DivisionCameraIntrinsics::ApplyIntrinsics(double normalized_x, + double normalized_y, + double *image_x, + double *image_y) const { + ApplyDivisionDistortionModel(focal_length_x(), + focal_length_y(), + principal_point_x(), + principal_point_y(), + k1(), k2(), + normalized_x, + normalized_y, + image_x, + image_y); +} + +void DivisionCameraIntrinsics::InvertIntrinsics(double image_x, + double image_y, + double *normalized_x, + double *normalized_y) const { + InvertDivisionDistortionModel(focal_length_x(), + focal_length_y(), + principal_point_x(), + principal_point_y(), + k1(), k2(), + image_x, + image_y, + normalized_x, + normalized_y); +} + +std::ostream& operator <<(std::ostream &os, + const CameraIntrinsics &intrinsics) { + if (intrinsics.focal_length_x() == intrinsics.focal_length_x()) { + os << "f=" << intrinsics.focal_length(); + } else { + os << "fx=" << intrinsics.focal_length_x() + << " fy=" << intrinsics.focal_length_y(); + } + os << " cx=" << intrinsics.principal_point_x() + << " cy=" << intrinsics.principal_point_y() + << " w=" << intrinsics.image_width() + << " h=" << intrinsics.image_height(); + +#define PRINT_NONZERO_COEFFICIENT(intrinsics, coeff) \ + { \ + if (intrinsics->coeff() != 0.0) { \ + os << " " #coeff "=" << intrinsics->coeff(); \ + } \ + } (void) 0 + + switch (intrinsics.GetDistortionModelType()) { + case DISTORTION_MODEL_POLYNOMIAL: + { + const PolynomialCameraIntrinsics *polynomial_intrinsics = + static_cast(&intrinsics); + PRINT_NONZERO_COEFFICIENT(polynomial_intrinsics, k1); + PRINT_NONZERO_COEFFICIENT(polynomial_intrinsics, k2); + PRINT_NONZERO_COEFFICIENT(polynomial_intrinsics, k3); + PRINT_NONZERO_COEFFICIENT(polynomial_intrinsics, p1); + PRINT_NONZERO_COEFFICIENT(polynomial_intrinsics, p2); + break; + } + case DISTORTION_MODEL_DIVISION: + { + const DivisionCameraIntrinsics *division_intrinsics = + static_cast(&intrinsics); + PRINT_NONZERO_COEFFICIENT(division_intrinsics, k1); + PRINT_NONZERO_COEFFICIENT(division_intrinsics, k2); + break; + } + default: + LOG(FATAL) << "Unknown distortion model."; + } + +#undef PRINT_NONZERO_COEFFICIENT + + return os; +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/camera_intrinsics.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/camera_intrinsics.h new file mode 100644 index 00000000000..6a3ade81089 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/camera_intrinsics.h @@ -0,0 +1,406 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_CAMERA_INTRINSICS_H_ +#define LIBMV_SIMPLE_PIPELINE_CAMERA_INTRINSICS_H_ + +#include +#include + +#include + +#include "libmv/numeric/numeric.h" +#include "libmv/simple_pipeline/distortion_models.h" + +namespace libmv { + +class CameraIntrinsics; + +namespace internal { + +// This class is responsible to store a lookup grid to perform +// image warping using this lookup grid. Such a magic is needed +// to make (un)distortion as fast as possible in cases multiple +// images are to be processed. +class LookupWarpGrid { + public: + LookupWarpGrid(); + LookupWarpGrid(const LookupWarpGrid &from); + ~LookupWarpGrid(); + + // Width and height og the image, measured in pixels. + int width() const { return width_; } + int height() const { return height_; } + + // Overscan factor of the image, so that + // - 0.0 is overscan of 0 pixels, + // - 1.0 is overscan of image weight pixels in horizontal direction + // and image height pixels in vertical direction. + double overscan() const { return overscan_; } + + // Update lookup grid in order to be sure it's calculated for + // given image width, height and overscan. + // + // See comment for CameraIntrinsics::DistortBuffer to get more + // details about what overscan is. + template + void Update(const CameraIntrinsics &intrinsics, + int width, + int height, + double overscan); + + // Apply coordinate lookup grid on a giver input buffer. + // + // See comment for CameraIntrinsics::DistortBuffer to get more + // details about template type. + template + void Apply(const PixelType *input_buffer, + int width, + int height, + int channels, + PixelType *output_buffer); + + // Reset lookup grids. + // This will tag the grid for update without re-computing it. + void Reset(); + + // Set number of threads used for threaded buffer distortion/undistortion. + void SetThreads(int threads); + + private: + // This structure contains an offset in both x,y directions + // in an optimized way sawing some bytes per pixel in the memory. + // + // TODO(sergey): This is rather questionable optimizations, memory + // is cheap nowadays and storing offset in such a way implies much + // more operations comparing to using bare floats. + struct Offset { + // Integer part of the offset. + short ix, iy; + + // Float part of an offset, to get a real float value divide this + // value by 255. + unsigned char fx, fy; + }; + + // Compute coordinate lookup grid using a giver warp functor. + // + // width and height corresponds to a size of buffer which will + // be warped later. + template + void Compute(const CameraIntrinsics &intrinsics, + int width, + int height, + double overscan); + + // This is a buffer which contains per-pixel offset of the + // pixels from input buffer to correspond the warping function. + Offset *offset_; + + // Dimensions of the image this lookup grid processes. + int width_, height_; + + // Overscan of the image being processed by this grid. + double overscan_; + + // Number of threads which will be used for buffer istortion/undistortion. + int threads_; +}; + +} // namespace internal + +class CameraIntrinsics { + public: + CameraIntrinsics(); + CameraIntrinsics(const CameraIntrinsics &from); + virtual ~CameraIntrinsics() {} + + virtual DistortionModelType GetDistortionModelType() const = 0; + + int image_width() const { return image_width_; } + int image_height() const { return image_height_; } + + const Mat3 &K() const { return K_; } + + double focal_length() const { return K_(0, 0); } + double focal_length_x() const { return K_(0, 0); } + double focal_length_y() const { return K_(1, 1); } + + double principal_point_x() const { return K_(0, 2); } + double principal_point_y() const { return K_(1, 2); } + + virtual int num_distortion_parameters() const = 0; + virtual double *distortion_parameters() = 0; + virtual const double *distortion_parameters() const = 0; + + // Set the image size in pixels. + // Image is the size of image camera intrinsics were calibrated with. + void SetImageSize(int width, int height); + + // Set the entire calibration matrix at once. + void SetK(const Mat3 new_k); + + // Set both x and y focal length in pixels. + void SetFocalLength(double focal_x, double focal_y); + + // Set principal point in pixels. + void SetPrincipalPoint(double cx, double cy); + + // Set number of threads used for threaded buffer distortion/undistortion. + void SetThreads(int threads); + + // Convert image space coordinates to normalized. + void ImageSpaceToNormalized(double image_x, + double image_y, + double *normalized_x, + double *normalized_y) const; + + // Convert normalized coordinates to image space. + void NormalizedToImageSpace(double normalized_x, + double normalized_y, + double *image_x, + double *image_y) const; + + // Apply camera intrinsics to the normalized point to get image coordinates. + // + // This applies the lens distortion to a point which is in normalized + // camera coordinates (i.e. the principal point is at (0, 0)) to get image + // coordinates in pixels. + virtual void ApplyIntrinsics(double normalized_x, + double normalized_y, + double *image_x, + double *image_y) const = 0; + + // Invert camera intrinsics on the image point to get normalized coordinates. + // + // This reverses the effect of lens distortion on a point which is in image + // coordinates to get normalized camera coordinates. + virtual void InvertIntrinsics(double image_x, + double image_y, + double *normalized_x, + double *normalized_y) const = 0; + + // Distort an image using the current camera instrinsics + // + // The distorted image is computed in output_buffer using samples from + // input_buffer. Both buffers should be width x height x channels sized. + // + // Overscan is a percentage value of how much overcan the image have. + // For example overscal value of 0.2 means 20% of overscan in the + // buffers. + // + // Overscan is usually used in cases when one need to distort an image + // and don't have a barrel in the distorted buffer. For example, when + // one need to render properly distorted FullHD frame without barrel + // visible. For such cases renderers usually renders bigger images and + // crops them after the distortion. + // + // This method is templated to be able to distort byte and float buffers + // without having separate methods for this two types. So basically only + // + // But in fact PixelType might be any type for which multiplication by + // a scalar and addition are implemented. For example PixelType might be + // Vec3 as well. + template + void DistortBuffer(const PixelType *input_buffer, + int width, + int height, + double overscan, + int channels, + PixelType *output_buffer); + + // Undistort an image using the current camera instrinsics + // + // The undistorted image is computed in output_buffer using samples from + // input_buffer. Both buffers should be width x height x channels sized. + // + // Overscan is a percentage value of how much overcan the image have. + // For example overscal value of 0.2 means 20% of overscan in the + // buffers. + // + // Overscan is usually used in cases when one need to distort an image + // and don't have a barrel in the distorted buffer. For example, when + // one need to render properly distorted FullHD frame without barrel + // visible. For such cases renderers usually renders bigger images and + // crops them after the distortion. + // + // This method is templated to be able to distort byte and float buffers + // without having separate methods for this two types. So basically only + // + // But in fact PixelType might be any type for which multiplication by + // a scalar and addition are implemented. For example PixelType might be + // Vec3 as well. + template + void UndistortBuffer(const PixelType *input_buffer, + int width, + int height, + double overscan, + int channels, + PixelType *output_buffer); + + private: + // This is the size of the image. This is necessary to, for example, handle + // the case of processing a scaled image. + int image_width_; + int image_height_; + + // The traditional intrinsics matrix from x = K[R|t]X. + Mat3 K_; + + // Coordinate lookup grids for distortion and undistortion. + internal::LookupWarpGrid distort_; + internal::LookupWarpGrid undistort_; + + protected: + // Reset lookup grids after changing the distortion model. + void ResetLookupGrids(); +}; + +class PolynomialCameraIntrinsics : public CameraIntrinsics { + public: + // This constants defines an offset of corresponding coefficients + // in the arameters_ array. + enum { + OFFSET_K1, + OFFSET_K2, + OFFSET_K3, + OFFSET_P1, + OFFSET_P2, + + // This defines the size of array which we need to have in order + // to store all the coefficients. + NUM_PARAMETERS, + }; + + PolynomialCameraIntrinsics(); + PolynomialCameraIntrinsics(const PolynomialCameraIntrinsics &from); + + DistortionModelType GetDistortionModelType() const { + return DISTORTION_MODEL_POLYNOMIAL; + } + + int num_distortion_parameters() const { return NUM_PARAMETERS; } + double *distortion_parameters() { return parameters_; }; + const double *distortion_parameters() const { return parameters_; }; + + double k1() const { return parameters_[OFFSET_K1]; } + double k2() const { return parameters_[OFFSET_K2]; } + double k3() const { return parameters_[OFFSET_K3]; } + double p1() const { return parameters_[OFFSET_P1]; } + double p2() const { return parameters_[OFFSET_P2]; } + + // Set radial distortion coeffcients. + void SetRadialDistortion(double k1, double k2, double k3); + + // Set tangential distortion coeffcients. + void SetTangentialDistortion(double p1, double p2); + + // Apply camera intrinsics to the normalized point to get image coordinates. + // + // This applies the lens distortion to a point which is in normalized + // camera coordinates (i.e. the principal point is at (0, 0)) to get image + // coordinates in pixels. + void ApplyIntrinsics(double normalized_x, + double normalized_y, + double *image_x, + double *image_y) const; + + // Invert camera intrinsics on the image point to get normalized coordinates. + // + // This reverses the effect of lens distortion on a point which is in image + // coordinates to get normalized camera coordinates. + void InvertIntrinsics(double image_x, + double image_y, + double *normalized_x, + double *normalized_y) const; + + private: + // OpenCV's distortion model with third order polynomial radial distortion + // terms and second order tangential distortion. The distortion is applied to + // the normalized coordinates before the focal length, which makes them + // independent of image size. + double parameters_[NUM_PARAMETERS]; +}; + +class DivisionCameraIntrinsics : public CameraIntrinsics { + public: + // This constants defines an offset of corresponding coefficients + // in the arameters_ array. + enum { + OFFSET_K1, + OFFSET_K2, + + // This defines the size of array which we need to have in order + // to store all the coefficients. + NUM_PARAMETERS, + }; + + DivisionCameraIntrinsics(); + DivisionCameraIntrinsics(const DivisionCameraIntrinsics &from); + + DistortionModelType GetDistortionModelType() const { + return DISTORTION_MODEL_DIVISION; + } + + int num_distortion_parameters() const { return NUM_PARAMETERS; } + double *distortion_parameters() { return parameters_; }; + const double *distortion_parameters() const { return parameters_; }; + + double k1() const { return parameters_[OFFSET_K1]; } + double k2() const { return parameters_[OFFSET_K2]; } + + // Set radial distortion coeffcients. + void SetDistortion(double k1, double k2); + + // Apply camera intrinsics to the normalized point to get image coordinates. + // + // This applies the lens distortion to a point which is in normalized + // camera coordinates (i.e. the principal point is at (0, 0)) to get image + // coordinates in pixels. + void ApplyIntrinsics(double normalized_x, + double normalized_y, + double *image_x, + double *image_y) const; + + // Invert camera intrinsics on the image point to get normalized coordinates. + // + // This reverses the effect of lens distortion on a point which is in image + // coordinates to get normalized camera coordinates. + void InvertIntrinsics(double image_x, + double image_y, + double *normalized_x, + double *normalized_y) const; + + private: + // Double-parameter division distortion model. + double parameters_[NUM_PARAMETERS]; +}; + +/// A human-readable representation of the camera intrinsic parameters. +std::ostream& operator <<(std::ostream &os, + const CameraIntrinsics &intrinsics); + +} // namespace libmv + +// Include implementation of all templated methods here, +// so they're visible to the compiler. +#include "libmv/simple_pipeline/camera_intrinsics_impl.h" + +#endif // LIBMV_SIMPLE_PIPELINE_CAMERA_INTRINSICS_H_ diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/camera_intrinsics_impl.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/camera_intrinsics_impl.h new file mode 100644 index 00000000000..97abee7ab01 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/camera_intrinsics_impl.h @@ -0,0 +1,192 @@ +// Copyright (c) 2014 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +namespace libmv { + +namespace { + +// FIXME: C++ templates limitations makes thing complicated, +// but maybe there is a simpler method. +struct ApplyIntrinsicsFunction { + ApplyIntrinsicsFunction(const CameraIntrinsics &intrinsics, + double x, + double y, + double *warp_x, + double *warp_y) { + double normalized_x, normalized_y; + intrinsics.ImageSpaceToNormalized(x, y, &normalized_x, &normalized_y); + intrinsics.ApplyIntrinsics(normalized_x, normalized_y, warp_x, warp_y); + } +}; + +struct InvertIntrinsicsFunction { + InvertIntrinsicsFunction(const CameraIntrinsics &intrinsics, + double x, + double y, + double *warp_x, + double *warp_y) { + double normalized_x, normalized_y; + intrinsics.InvertIntrinsics(x, y, &normalized_x, &normalized_y); + intrinsics.NormalizedToImageSpace(normalized_x, normalized_y, warp_x, warp_y); + } +}; + +} // namespace + +namespace internal { + +// TODO(MatthiasF): downsample lookup +template +void LookupWarpGrid::Compute(const CameraIntrinsics &intrinsics, + int width, + int height, + double overscan) { + double w = (double) width / (1.0 + overscan); + double h = (double) height / (1.0 + overscan); + double aspx = (double) w / intrinsics.image_width(); + double aspy = (double) h / intrinsics.image_height(); +#if defined(_OPENMP) +# pragma omp parallel for schedule(dynamic) num_threads(threads_) \ + if (threads_ > 1 && height > 100) +#endif + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + double src_x = (x - 0.5 * overscan * w) / aspx, + src_y = (y - 0.5 * overscan * h) / aspy; + double warp_x, warp_y; + WarpFunction(intrinsics, src_x, src_y, &warp_x, &warp_y); + warp_x = warp_x * aspx + 0.5 * overscan * w; + warp_y = warp_y * aspy + 0.5 * overscan * h; + int ix = int(warp_x), iy = int(warp_y); + int fx = round((warp_x - ix) * 256), fy = round((warp_y - iy) * 256); + if (fx == 256) { fx = 0; ix++; } // NOLINT + if (fy == 256) { fy = 0; iy++; } // NOLINT + // Use nearest border pixel + if (ix < 0) { ix = 0, fx = 0; } // NOLINT + if (iy < 0) { iy = 0, fy = 0; } // NOLINT + if (ix >= width - 2) ix = width - 2; + if (iy >= height - 2) iy = height - 2; + + Offset offset = { (short) (ix - x), + (short) (iy - y), + (unsigned char) fx, + (unsigned char) fy }; + offset_[y * width + x] = offset; + } + } +} + +template +void LookupWarpGrid::Update(const CameraIntrinsics &intrinsics, + int width, + int height, + double overscan) { + if (width_ != width || + height_ != height || + overscan_ != overscan) { + Reset(); + } + + if (offset_ == NULL) { + offset_ = new Offset[width * height]; + Compute(intrinsics, + width, + height, + overscan); + } + + width_ = width; + height_ = height; + overscan_ = overscan; +} + +// TODO(MatthiasF): cubic B-Spline image sampling, bilinear lookup +template +void LookupWarpGrid::Apply(const PixelType *input_buffer, + int width, + int height, + int channels, + PixelType *output_buffer) { +#if defined(_OPENMP) +# pragma omp parallel for schedule(dynamic) num_threads(threads_) \ + if (threads_ > 1 && height > 100) +#endif + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + Offset offset = offset_[y * width + x]; + const int pixel_index = ((y + offset.iy) * width + + (x + offset.ix)) * channels; + const PixelType *s = &input_buffer[pixel_index]; + for (int i = 0; i < channels; i++) { + output_buffer[(y * width + x) * channels + i] = + ((s[i] * (256 - offset.fx) + + s[channels + i] * offset.fx) * (256 - offset.fy) + + (s[width * channels + i] * (256 - offset.fx) + + s[width * channels + channels + i] * offset.fx) * offset.fy) + / (256 * 256); + } + } + } +} + +} // namespace internal + +template +void CameraIntrinsics::DistortBuffer(const PixelType *input_buffer, + int width, + int height, + double overscan, + int channels, + PixelType *output_buffer) { + assert(channels >= 1); + assert(channels <= 4); + distort_.Update(*this, + width, + height, + overscan); + distort_.Apply(input_buffer, + width, + height, + channels, + output_buffer); +} + +template +void CameraIntrinsics::UndistortBuffer(const PixelType *input_buffer, + int width, + int height, + double overscan, + int channels, + PixelType *output_buffer) { + assert(channels >= 1); + assert(channels <= 4); + undistort_.Update(*this, + width, + height, + overscan); + + undistort_.Apply(input_buffer, + width, + height, + channels, + output_buffer); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/distortion_models.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/distortion_models.cc new file mode 100644 index 00000000000..9b6dca2678a --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/distortion_models.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2014 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/distortion_models.h" +#include "libmv/numeric/levenberg_marquardt.h" + +namespace libmv { + +namespace { + +struct InvertPolynomialIntrinsicsCostFunction { + public: + typedef Vec2 FMatrixType; + typedef Vec2 XMatrixType; + + InvertPolynomialIntrinsicsCostFunction(const double focal_length_x, + const double focal_length_y, + const double principal_point_x, + const double principal_point_y, + const double k1, + const double k2, + const double k3, + const double p1, + const double p2, + const double image_x, + const double image_y) + : focal_length_x_(focal_length_x), + focal_length_y_(focal_length_y), + principal_point_x_(principal_point_x), + principal_point_y_(principal_point_y), + k1_(k1), k2_(k2), k3_(k3), + p1_(p1), p2_(p2), + x_(image_x), y_(image_y) {} + + Vec2 operator()(const Vec2 &u) const { + double xx, yy; + + ApplyPolynomialDistortionModel(focal_length_x_, + focal_length_y_, + principal_point_x_, + principal_point_y_, + k1_, k2_, k3_, + p1_, p2_, + u(0), u(1), + &xx, &yy); + + Vec2 fx; + fx << (xx - x_), (yy - y_); + return fx; + } + double focal_length_x_; + double focal_length_y_; + double principal_point_x_; + double principal_point_y_; + double k1_, k2_, k3_; + double p1_, p2_; + double x_, y_; +}; + +struct InvertDivisionIntrinsicsCostFunction { + public: + typedef Vec2 FMatrixType; + typedef Vec2 XMatrixType; + + InvertDivisionIntrinsicsCostFunction(const double focal_length_x, + const double focal_length_y, + const double principal_point_x, + const double principal_point_y, + const double k1, + const double k2, + const double image_x, + const double image_y) + : focal_length_x_(focal_length_x), + focal_length_y_(focal_length_y), + principal_point_x_(principal_point_x), + principal_point_y_(principal_point_y), + k1_(k1), k2_(k2), + x_(image_x), y_(image_y) {} + + Vec2 operator()(const Vec2 &u) const { + double xx, yy; + + ApplyDivisionDistortionModel(focal_length_x_, + focal_length_y_, + principal_point_x_, + principal_point_y_, + k1_, k2_, + u(0), u(1), + &xx, &yy); + + Vec2 fx; + fx << (xx - x_), (yy - y_); + return fx; + } + double focal_length_x_; + double focal_length_y_; + double principal_point_x_; + double principal_point_y_; + double k1_, k2_; + double x_, y_; +}; + +} // namespace + +void InvertPolynomialDistortionModel(const double focal_length_x, + const double focal_length_y, + const double principal_point_x, + const double principal_point_y, + const double k1, + const double k2, + const double k3, + const double p1, + const double p2, + const double image_x, + const double image_y, + double *normalized_x, + double *normalized_y) { + // Compute the initial guess. For a camera with no distortion, this will also + // be the final answer; the LM iteration will terminate immediately. + Vec2 normalized; + normalized(0) = (image_x - principal_point_x) / focal_length_x; + normalized(1) = (image_y - principal_point_y) / focal_length_y; + + typedef LevenbergMarquardt Solver; + + InvertPolynomialIntrinsicsCostFunction intrinsics_cost(focal_length_x, + focal_length_y, + principal_point_x, + principal_point_y, + k1, k2, k3, + p1, p2, + image_x, image_y); + Solver::SolverParameters params; + Solver solver(intrinsics_cost); + + /*Solver::Results results =*/ solver.minimize(params, &normalized); + + // TODO(keir): Better error handling. + + *normalized_x = normalized(0); + *normalized_y = normalized(1); +} + +void InvertDivisionDistortionModel(const double focal_length_x, + const double focal_length_y, + const double principal_point_x, + const double principal_point_y, + const double k1, + const double k2, + const double image_x, + const double image_y, + double *normalized_x, + double *normalized_y) { + // Compute the initial guess. For a camera with no distortion, this will also + // be the final answer; the LM iteration will terminate immediately. + Vec2 normalized; + normalized(0) = (image_x - principal_point_x) / focal_length_x; + normalized(1) = (image_y - principal_point_y) / focal_length_y; + + // TODO(sergey): Use Ceres minimizer instead. + typedef LevenbergMarquardt Solver; + + InvertDivisionIntrinsicsCostFunction intrinsics_cost(focal_length_x, + focal_length_y, + principal_point_x, + principal_point_y, + k1, k2, + image_x, image_y); + Solver::SolverParameters params; + Solver solver(intrinsics_cost); + + /*Solver::Results results =*/ solver.minimize(params, &normalized); + + // TODO(keir): Better error handling. + + *normalized_x = normalized(0); + *normalized_y = normalized(1); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/distortion_models.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/distortion_models.h new file mode 100644 index 00000000000..4f8e2295a0e --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/distortion_models.h @@ -0,0 +1,131 @@ +// Copyright (c) 2014 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_DISTORTION_MODELS_H_ +#define LIBMV_SIMPLE_PIPELINE_DISTORTION_MODELS_H_ + +namespace libmv { + +enum DistortionModelType { + DISTORTION_MODEL_POLYNOMIAL, + DISTORTION_MODEL_DIVISION +}; + +// Invert camera intrinsics on the image point to get normalized coordinates. +// This inverts the radial lens distortion to a point which is in image pixel +// coordinates to get normalized coordinates. +void InvertPolynomialDistortionModel(const double focal_length_x, + const double focal_length_y, + const double principal_point_x, + const double principal_point_y, + const double k1, + const double k2, + const double k3, + const double p1, + const double p2, + const double image_x, + const double image_y, + double *normalized_x, + double *normalized_y); + +// Apply camera intrinsics to the normalized point to get image coordinates. +// This applies the radial lens distortion to a point which is in normalized +// camera coordinates (i.e. the principal point is at (0, 0)) to get image +// coordinates in pixels. Templated for use with autodifferentiation. +template +inline void ApplyPolynomialDistortionModel(const T &focal_length_x, + const T &focal_length_y, + const T &principal_point_x, + const T &principal_point_y, + const T &k1, + const T &k2, + const T &k3, + const T &p1, + const T &p2, + const T &normalized_x, + const T &normalized_y, + T *image_x, + T *image_y) { + T x = normalized_x; + T y = normalized_y; + + // Apply distortion to the normalized points to get (xd, yd). + T r2 = x*x + y*y; + T r4 = r2 * r2; + T r6 = r4 * r2; + T r_coeff = (T(1) + k1*r2 + k2*r4 + k3*r6); + T xd = x * r_coeff + T(2)*p1*x*y + p2*(r2 + T(2)*x*x); + T yd = y * r_coeff + T(2)*p2*x*y + p1*(r2 + T(2)*y*y); + + // Apply focal length and principal point to get the final image coordinates. + *image_x = focal_length_x * xd + principal_point_x; + *image_y = focal_length_y * yd + principal_point_y; +} + +// Invert camera intrinsics on the image point to get normalized coordinates. +// This inverts the radial lens distortion to a point which is in image pixel +// coordinates to get normalized coordinates. +// +// Uses division distortion model. +void InvertDivisionDistortionModel(const double focal_length_x, + const double focal_length_y, + const double principal_point_x, + const double principal_point_y, + const double k1, + const double k2, + const double image_x, + const double image_y, + double *normalized_x, + double *normalized_y); + +// Apply camera intrinsics to the normalized point to get image coordinates. +// This applies the radial lens distortion to a point which is in normalized +// camera coordinates (i.e. the principal point is at (0, 0)) to get image +// coordinates in pixels. Templated for use with autodifferentiation. +// +// Uses division distortion model. +template +inline void ApplyDivisionDistortionModel(const T &focal_length_x, + const T &focal_length_y, + const T &principal_point_x, + const T &principal_point_y, + const T &k1, + const T &k2, + const T &normalized_x, + const T &normalized_y, + T *image_x, + T *image_y) { + + T x = normalized_x; + T y = normalized_y; + T r2 = x*x + y*y; + T r4 = r2 * r2; + + T xd = x / (T(1) + k1 * r2 + k2 * r4); + T yd = y / (T(1) + k1 * r2 + k2 * r4); + + // Apply focal length and principal point to get the final image coordinates. + *image_x = focal_length_x * xd + principal_point_x; + *image_y = focal_length_y * yd + principal_point_y; +} + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_DISTORTION_MODELS_H_ diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/initialize_reconstruction.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/initialize_reconstruction.cc new file mode 100644 index 00000000000..7a086c375d5 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/initialize_reconstruction.cc @@ -0,0 +1,195 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/initialize_reconstruction.h" + +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" +#include "libmv/multiview/fundamental.h" +#include "libmv/multiview/projection.h" +#include "libmv/numeric/levenberg_marquardt.h" +#include "libmv/numeric/numeric.h" +#include "libmv/simple_pipeline/reconstruction.h" +#include "libmv/simple_pipeline/tracks.h" + +namespace libmv { +namespace { + +void GetImagesInMarkers(const vector &markers, + int *image1, int *image2) { + if (markers.size() < 2) { + return; + } + *image1 = markers[0].image; + for (int i = 1; i < markers.size(); ++i) { + if (markers[i].image != *image1) { + *image2 = markers[i].image; + return; + } + } + *image2 = -1; + LOG(FATAL) << "Only one image in the markers."; +} + +} // namespace + +bool EuclideanReconstructTwoFrames(const vector &markers, + EuclideanReconstruction *reconstruction) { + if (markers.size() < 16) { + LG << "Not enough markers to initialize from two frames: " << markers.size(); + return false; + } + + int image1, image2; + GetImagesInMarkers(markers, &image1, &image2); + + Mat x1, x2; + CoordinatesForMarkersInImage(markers, image1, &x1); + CoordinatesForMarkersInImage(markers, image2, &x2); + + Mat3 F; + NormalizedEightPointSolver(x1, x2, &F); + + // The F matrix should be an E matrix, but squash it just to be sure. + Mat3 E; + FundamentalToEssential(F, &E); + + // Recover motion between the two images. Since this function assumes a + // calibrated camera, use the identity for K. + Mat3 R; + Vec3 t; + Mat3 K = Mat3::Identity(); + if (!MotionFromEssentialAndCorrespondence(E, + K, x1.col(0), + K, x2.col(0), + &R, &t)) { + LG << "Failed to compute R and t from E and K."; + return false; + } + + // Image 1 gets the reference frame, image 2 gets the relative motion. + reconstruction->InsertCamera(image1, Mat3::Identity(), Vec3::Zero()); + reconstruction->InsertCamera(image2, R, t); + + LG << "From two frame reconstruction got:\nR:\n" << R + << "\nt:" << t.transpose(); + return true; +} + +namespace { + +Mat3 DecodeF(const Vec9 &encoded_F) { + // Decode F and force it to be rank 2. + Map full_rank_F(encoded_F.data(), 3, 3); + Eigen::JacobiSVD svd(full_rank_F, + Eigen::ComputeFullU | Eigen::ComputeFullV); + Vec3 diagonal = svd.singularValues(); + diagonal(2) = 0; + Mat3 F = svd.matrixU() * diagonal.asDiagonal() * svd.matrixV().transpose(); + return F; +} + +// This is the stupidest way to refine F known to mankind, since it requires +// doing a full SVD of F at each iteration. This uses sampson error. +struct FundamentalSampsonCostFunction { + public: + typedef Vec FMatrixType; + typedef Vec9 XMatrixType; + + // Assumes markers are ordered by track. + explicit FundamentalSampsonCostFunction(const vector &markers) + : markers(markers) {} + + Vec operator()(const Vec9 &encoded_F) const { + // Decode F and force it to be rank 2. + Mat3 F = DecodeF(encoded_F); + + Vec residuals(markers.size() / 2); + residuals.setZero(); + for (int i = 0; i < markers.size() / 2; ++i) { + const Marker &marker1 = markers[2*i + 0]; + const Marker &marker2 = markers[2*i + 1]; + CHECK_EQ(marker1.track, marker2.track); + Vec2 x1(marker1.x, marker1.y); + Vec2 x2(marker2.x, marker2.y); + + residuals[i] = SampsonDistance(F, x1, x2); + } + return residuals; + } + const vector &markers; +}; + +} // namespace + +bool ProjectiveReconstructTwoFrames(const vector &markers, + ProjectiveReconstruction *reconstruction) { + if (markers.size() < 16) { + return false; + } + + int image1, image2; + GetImagesInMarkers(markers, &image1, &image2); + + Mat x1, x2; + CoordinatesForMarkersInImage(markers, image1, &x1); + CoordinatesForMarkersInImage(markers, image2, &x2); + + Mat3 F; + NormalizedEightPointSolver(x1, x2, &F); + + // XXX Verify sampson distance. +#if 0 + // Refine the resulting projection fundamental matrix using Sampson's + // approximation of geometric error. This avoids having to do a full bundle + // at the cost of some accuracy. + // + // TODO(keir): After switching to a better bundling library, use a proper + // full bundle adjust here instead of this lame bundle adjustment. + typedef LevenbergMarquardt Solver; + + FundamentalSampsonCostFunction fundamental_cost(markers); + + // Pack the initial P matrix into a size-12 vector.. + Vec9 encoded_F = Map(F.data(), 3, 3); + + Solver solver(fundamental_cost); + + Solver::SolverParameters params; + Solver::Results results = solver.minimize(params, &encoded_F); + // TODO(keir): Check results to ensure clean termination. + + // Recover F from the minimization. + F = DecodeF(encoded_F); +#endif + + // Image 1 gets P = [I|0], image 2 gets arbitrary P. + Mat34 P1 = Mat34::Zero(); + P1.block<3, 3>(0, 0) = Mat3::Identity(); + Mat34 P2; + ProjectionsFromFundamental(F, &P1, &P2); + + reconstruction->InsertCamera(image1, P1); + reconstruction->InsertCamera(image2, P2); + + LG << "From two frame reconstruction got P2:\n" << P2; + return true; +} +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/initialize_reconstruction.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/initialize_reconstruction.h new file mode 100644 index 00000000000..744436246b0 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/initialize_reconstruction.h @@ -0,0 +1,74 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_INITIALIZE_RECONSTRUCTION_H +#define LIBMV_SIMPLE_PIPELINE_INITIALIZE_RECONSTRUCTION_H + +#include "libmv/base/vector.h" + +namespace libmv { + +struct Marker; +class EuclideanReconstruction; +class ProjectiveReconstruction; + +/*! + Initialize the \link EuclideanReconstruction reconstruction \endlink using + two frames. + + \a markers should contain all \l Marker markers \endlink belonging to + tracks visible in both frames. The pose estimation of the camera for + these frames will be inserted into \a *reconstruction. + + \note The two frames need to have both enough parallax and enough common tracks + for accurate reconstruction. At least 8 tracks are suggested. + \note The origin of the coordinate system is defined to be the camera of + the first keyframe. + \note This assumes a calibrated reconstruction, e.g. the markers are + already corrected for camera intrinsics and radial distortion. + \note This assumes an outlier-free set of markers. + + \sa EuclideanResect, EuclideanIntersect, EuclideanBundle +*/ +bool EuclideanReconstructTwoFrames(const vector &markers, + EuclideanReconstruction *reconstruction); + +/*! + Initialize the \link ProjectiveReconstruction reconstruction \endlink using + two frames. + + \a markers should contain all \l Marker markers \endlink belonging to + tracks visible in both frames. An estimate of the projection matrices for + the two frames will get added to the reconstruction. + + \note The two frames need to have both enough parallax and enough common tracks + for accurate reconstruction. At least 8 tracks are suggested. + \note The origin of the coordinate system is defined to be the camera of + the first keyframe. + \note This assumes the markers are already corrected for radial distortion. + \note This assumes an outlier-free set of markers. + + \sa ProjectiveResect, ProjectiveIntersect, ProjectiveBundle +*/ +bool ProjectiveReconstructTwoFrames(const vector &markers, + ProjectiveReconstruction *reconstruction); +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_INITIALIZE_RECONSTRUCTION_H diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/intersect.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/intersect.cc new file mode 100644 index 00000000000..d625c111951 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/intersect.cc @@ -0,0 +1,254 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/intersect.h" + +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" +#include "libmv/multiview/projection.h" +#include "libmv/multiview/triangulation.h" +#include "libmv/multiview/nviewtriangulation.h" +#include "libmv/numeric/numeric.h" +#include "libmv/numeric/levenberg_marquardt.h" +#include "libmv/simple_pipeline/reconstruction.h" +#include "libmv/simple_pipeline/tracks.h" + +#include "ceres/ceres.h" + +namespace libmv { + +namespace { + +class EuclideanIntersectCostFunctor { + public: + EuclideanIntersectCostFunctor(const Marker &marker, + const EuclideanCamera &camera) + : marker_(marker), camera_(camera) {} + + template + bool operator()(const T *X, T *residuals) const { + typedef Eigen::Matrix Mat3; + typedef Eigen::Matrix Vec3; + + Vec3 x(X); + Mat3 R(camera_.R.cast()); + Vec3 t(camera_.t.cast()); + + Vec3 projected = R * x + t; + projected /= projected(2); + + residuals[0] = (projected(0) - T(marker_.x)) * marker_.weight; + residuals[1] = (projected(1) - T(marker_.y)) * marker_.weight; + + return true; + } + + const Marker &marker_; + const EuclideanCamera &camera_; +}; + +} // namespace + +bool EuclideanIntersect(const vector &markers, + EuclideanReconstruction *reconstruction) { + if (markers.size() < 2) { + return false; + } + + // Compute projective camera matrices for the cameras the intersection is + // going to use. + Mat3 K = Mat3::Identity(); + vector cameras; + Mat34 P; + for (int i = 0; i < markers.size(); ++i) { + EuclideanCamera *camera = reconstruction->CameraForImage(markers[i].image); + P_From_KRt(K, camera->R, camera->t, &P); + cameras.push_back(P); + } + + // Stack the 2D coordinates together as required by NViewTriangulate. + Mat2X points(2, markers.size()); + for (int i = 0; i < markers.size(); ++i) { + points(0, i) = markers[i].x; + points(1, i) = markers[i].y; + } + + Vec4 Xp; + LG << "Intersecting with " << markers.size() << " markers."; + NViewTriangulateAlgebraic(points, cameras, &Xp); + + // Get euclidean version of the homogeneous point. + Xp /= Xp(3); + Vec3 X = Xp.head<3>(); + + ceres::Problem problem; + + // Add residual blocks to the problem. + int num_residuals = 0; + for (int i = 0; i < markers.size(); ++i) { + const Marker &marker = markers[i]; + if (marker.weight != 0.0) { + const EuclideanCamera &camera = + *reconstruction->CameraForImage(marker.image); + + problem.AddResidualBlock( + new ceres::AutoDiffCostFunction< + EuclideanIntersectCostFunctor, + 2, /* num_residuals */ + 3>(new EuclideanIntersectCostFunctor(marker, camera)), + NULL, + &X(0)); + num_residuals++; + } + } + + // TODO(sergey): Once we'll update Ceres to the next version + // we wouldn't need this check anymore -- Ceres will deal with + // zero-sized problems nicely. + LG << "Number of residuals: " << num_residuals; + if (!num_residuals) { + LG << "Skipping running minimizer with zero residuals"; + + // We still add 3D point for the track regardless it was + // optimized or not. If track is a constant zero it'll use + // algebraic intersection result as a 3D coordinate. + + Vec3 point = X.head<3>(); + reconstruction->InsertPoint(markers[0].track, point); + + return true; + } + + // Configure the solve. + ceres::Solver::Options solver_options; + solver_options.linear_solver_type = ceres::DENSE_QR; + solver_options.max_num_iterations = 50; + solver_options.update_state_every_iteration = true; + solver_options.parameter_tolerance = 1e-16; + solver_options.function_tolerance = 1e-16; + + // Run the solve. + ceres::Solver::Summary summary; + ceres::Solve(solver_options, &problem, &summary); + + VLOG(1) << "Summary:\n" << summary.FullReport(); + + // Try projecting the point; make sure it's in front of everyone. + for (int i = 0; i < cameras.size(); ++i) { + const EuclideanCamera &camera = + *reconstruction->CameraForImage(markers[i].image); + Vec3 x = camera.R * X + camera.t; + if (x(2) < 0) { + LOG(ERROR) << "POINT BEHIND CAMERA " << markers[i].image + << ": " << x.transpose(); + return false; + } + } + + Vec3 point = X.head<3>(); + reconstruction->InsertPoint(markers[0].track, point); + + // TODO(keir): Add proper error checking. + return true; +} + +namespace { + +struct ProjectiveIntersectCostFunction { + public: + typedef Vec FMatrixType; + typedef Vec4 XMatrixType; + + ProjectiveIntersectCostFunction( + const vector &markers, + const ProjectiveReconstruction &reconstruction) + : markers(markers), reconstruction(reconstruction) {} + + Vec operator()(const Vec4 &X) const { + Vec residuals(2 * markers.size()); + residuals.setZero(); + for (int i = 0; i < markers.size(); ++i) { + const ProjectiveCamera &camera = + *reconstruction.CameraForImage(markers[i].image); + Vec3 projected = camera.P * X; + projected /= projected(2); + residuals[2*i + 0] = projected(0) - markers[i].x; + residuals[2*i + 1] = projected(1) - markers[i].y; + } + return residuals; + } + const vector &markers; + const ProjectiveReconstruction &reconstruction; +}; + +} // namespace + +bool ProjectiveIntersect(const vector &markers, + ProjectiveReconstruction *reconstruction) { + if (markers.size() < 2) { + return false; + } + + // Get the cameras to use for the intersection. + vector cameras; + for (int i = 0; i < markers.size(); ++i) { + ProjectiveCamera *camera = reconstruction->CameraForImage(markers[i].image); + cameras.push_back(camera->P); + } + + // Stack the 2D coordinates together as required by NViewTriangulate. + Mat2X points(2, markers.size()); + for (int i = 0; i < markers.size(); ++i) { + points(0, i) = markers[i].x; + points(1, i) = markers[i].y; + } + + Vec4 X; + LG << "Intersecting with " << markers.size() << " markers."; + NViewTriangulateAlgebraic(points, cameras, &X); + X /= X(3); + + typedef LevenbergMarquardt Solver; + + ProjectiveIntersectCostFunction triangulate_cost(markers, *reconstruction); + Solver::SolverParameters params; + Solver solver(triangulate_cost); + + Solver::Results results = solver.minimize(params, &X); + (void) results; // TODO(keir): Ensure results are good. + + // Try projecting the point; make sure it's in front of everyone. + for (int i = 0; i < cameras.size(); ++i) { + const ProjectiveCamera &camera = + *reconstruction->CameraForImage(markers[i].image); + Vec3 x = camera.P * X; + if (x(2) < 0) { + LOG(ERROR) << "POINT BEHIND CAMERA " << markers[i].image + << ": " << x.transpose(); + } + } + + reconstruction->InsertPoint(markers[0].track, X); + + // TODO(keir): Add proper error checking. + return true; +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/intersect.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/intersect.h new file mode 100644 index 00000000000..3a0ffa7418b --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/intersect.h @@ -0,0 +1,77 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_INTERSECT_H +#define LIBMV_SIMPLE_PIPELINE_INTERSECT_H + +#include "libmv/base/vector.h" +#include "libmv/simple_pipeline/tracks.h" +#include "libmv/simple_pipeline/reconstruction.h" + +namespace libmv { + +/*! + Estimate the 3D coordinates of a track by intersecting rays from images. + + This takes a set of markers, where each marker is for the same track but + different images, and reconstructs the 3D position of that track. Each of + the frames for which there is a marker for that track must have a + corresponding reconstructed camera in \a *reconstruction. + + \a markers should contain all \l Marker markers \endlink belonging to + tracks visible in all frames. + \a reconstruction should contain the cameras for all frames. + The new \l Point points \endlink will be inserted in \a reconstruction. + + \note This assumes a calibrated reconstruction, e.g. the markers are + already corrected for camera intrinsics and radial distortion. + \note This assumes an outlier-free set of markers. + + \sa EuclideanResect +*/ +bool EuclideanIntersect(const vector &markers, + EuclideanReconstruction *reconstruction); + +/*! + Estimate the homogeneous coordinates of a track by intersecting rays. + + This takes a set of markers, where each marker is for the same track but + different images, and reconstructs the homogeneous 3D position of that + track. Each of the frames for which there is a marker for that track must + have a corresponding reconstructed camera in \a *reconstruction. + + \a markers should contain all \l Marker markers \endlink belonging to + tracks visible in all frames. + \a reconstruction should contain the cameras for all frames. + The new \l Point points \endlink will be inserted in \a reconstruction. + + \note This assumes that radial distortion is already corrected for, but + does not assume that e.g. focal length and principal point are + accounted for. + \note This assumes an outlier-free set of markers. + + \sa Resect +*/ +bool ProjectiveIntersect(const vector &markers, + ProjectiveReconstruction *reconstruction); + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_INTERSECT_H diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/keyframe_selection.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/keyframe_selection.cc new file mode 100644 index 00000000000..241b5600505 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/keyframe_selection.cc @@ -0,0 +1,450 @@ +// Copyright (c) 2012 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/keyframe_selection.h" + +#include "libmv/numeric/numeric.h" +#include "ceres/ceres.h" +#include "libmv/logging/logging.h" +#include "libmv/multiview/homography.h" +#include "libmv/multiview/fundamental.h" +#include "libmv/simple_pipeline/intersect.h" +#include "libmv/simple_pipeline/bundle.h" + +#include + +namespace libmv { +namespace { + +Mat3 IntrinsicsNormalizationMatrix(const CameraIntrinsics &intrinsics) { + Mat3 T = Mat3::Identity(), S = Mat3::Identity(); + + T(0, 2) = -intrinsics.principal_point_x(); + T(1, 2) = -intrinsics.principal_point_y(); + + S(0, 0) /= intrinsics.focal_length_x(); + S(1, 1) /= intrinsics.focal_length_y(); + + return S * T; +} + +// P.H.S. Torr +// Geometric Motion Segmentation and Model Selection +// +// http://reference.kfupm.edu.sa/content/g/e/geometric_motion_segmentation_and_model__126445.pdf +// +// d is the number of dimensions modeled +// (d = 3 for a fundamental matrix or 2 for a homography) +// k is the number of degrees of freedom in the model +// (k = 7 for a fundamental matrix or 8 for a homography) +// r is the dimension of the data +// (r = 4 for 2D correspondences between two frames) +double GRIC(const Vec &e, int d, int k, int r) { + int n = e.rows(); + double lambda1 = log(static_cast(r)); + double lambda2 = log(static_cast(r * n)); + + // lambda3 limits the residual error, and this paper + // http://elvera.nue.tu-berlin.de/files/0990Knorr2006.pdf + // suggests using lambda3 of 2 + // same value is used in Torr's Problem of degeneracy in structure + // and motion recovery from uncalibrated image sequences + // http://www.robots.ox.ac.uk/~vgg/publications/papers/torr99.ps.gz + double lambda3 = 2.0; + + // Variance of tracker position. Physically, this is typically about 0.1px, + // and when squared becomes 0.01 px^2. + double sigma2 = 0.01; + + // Finally, calculate the GRIC score. + double gric = 0.0; + for (int i = 0; i < n; i++) { + gric += std::min(e(i) * e(i) / sigma2, lambda3 * (r - d)); + } + gric += lambda1 * d * n; + gric += lambda2 * k; + return gric; +} + +// Compute a generalized inverse using eigen value decomposition, clamping the +// smallest eigenvalues if requested. This is needed to compute the variance of +// reconstructed 3D points. +// +// TODO(keir): Consider moving this into the numeric code, since this is not +// related to keyframe selection. +Mat PseudoInverseWithClampedEigenvalues(const Mat &matrix, + int num_eigenvalues_to_clamp) { + Eigen::EigenSolver eigen_solver(matrix); + Mat D = eigen_solver.pseudoEigenvalueMatrix(); + Mat V = eigen_solver.pseudoEigenvectors(); + + // Clamp too-small singular values to zero to prevent numeric blowup. + double epsilon = std::numeric_limits::epsilon(); + for (int i = 0; i < D.cols(); ++i) { + if (D(i, i) > epsilon) { + D(i, i) = 1.0 / D(i, i); + } else { + D(i, i) = 0.0; + } + } + + // Apply the clamp. + for (int i = D.cols() - num_eigenvalues_to_clamp; i < D.cols(); ++i) { + D(i, i) = 0.0; + } + return V * D * V.inverse(); +} + +void FilterZeroWeightMarkersFromTracks(const Tracks &tracks, + Tracks *filtered_tracks) { + vector all_markers = tracks.AllMarkers(); + + for (int i = 0; i < all_markers.size(); ++i) { + Marker &marker = all_markers[i]; + if (marker.weight != 0.0) { + filtered_tracks->Insert(marker.image, + marker.track, + marker.x, + marker.y, + marker.weight); + } + } +} + +} // namespace + +void SelectKeyframesBasedOnGRICAndVariance(const Tracks &_tracks, + const CameraIntrinsics &intrinsics, + vector &keyframes) { + // Mirza Tahir Ahmed, Matthew N. Dailey + // Robust key frame extraction for 3D reconstruction from video streams + // + // http://www.cs.ait.ac.th/~mdailey/papers/Tahir-KeyFrame.pdf + + Tracks filtered_tracks; + FilterZeroWeightMarkersFromTracks(_tracks, &filtered_tracks); + + int max_image = filtered_tracks.MaxImage(); + int next_keyframe = 1; + int number_keyframes = 0; + + // Limit correspondence ratio from both sides. + // On the one hand if number of correspondent features is too low, + // triangulation will suffer. + // On the other hand high correspondence likely means short baseline. + // which also will affect om accuracy + const double Tmin = 0.8; + const double Tmax = 1.0; + + Mat3 N = IntrinsicsNormalizationMatrix(intrinsics); + Mat3 N_inverse = N.inverse(); + + double Sc_best = std::numeric_limits::max(); + double success_intersects_factor_best = 0.0f; + + while (next_keyframe != -1) { + int current_keyframe = next_keyframe; + double Sc_best_candidate = std::numeric_limits::max(); + + LG << "Found keyframe " << next_keyframe; + + number_keyframes++; + next_keyframe = -1; + + for (int candidate_image = current_keyframe + 1; + candidate_image <= max_image; + candidate_image++) { + // Conjunction of all markers from both keyframes + vector all_markers = + filtered_tracks.MarkersInBothImages(current_keyframe, + candidate_image); + + // Match keypoints between frames current_keyframe and candidate_image + vector tracked_markers = + filtered_tracks.MarkersForTracksInBothImages(current_keyframe, + candidate_image); + + // Correspondences in normalized space + Mat x1, x2; + CoordinatesForMarkersInImage(tracked_markers, current_keyframe, &x1); + CoordinatesForMarkersInImage(tracked_markers, candidate_image, &x2); + + LG << "Found " << x1.cols() + << " correspondences between " << current_keyframe + << " and " << candidate_image; + + // Not enough points to construct fundamental matrix + if (x1.cols() < 8 || x2.cols() < 8) + continue; + + // STEP 1: Correspondence ratio constraint + int Tc = tracked_markers.size(); + int Tf = all_markers.size(); + double Rc = static_cast(Tc) / Tf; + + LG << "Correspondence between " << current_keyframe + << " and " << candidate_image + << ": " << Rc; + + if (Rc < Tmin || Rc > Tmax) + continue; + + Mat3 H, F; + + // Estimate homography using default options. + EstimateHomographyOptions estimate_homography_options; + EstimateHomography2DFromCorrespondences(x1, + x2, + estimate_homography_options, + &H); + + // Convert homography to original pixel space. + H = N_inverse * H * N; + + EstimateFundamentalOptions estimate_fundamental_options; + EstimateFundamentalFromCorrespondences(x1, + x2, + estimate_fundamental_options, + &F); + + // Convert fundamental to original pixel space. + F = N_inverse * F * N; + + // TODO(sergey): STEP 2: Discard outlier matches + + // STEP 3: Geometric Robust Information Criteria + + // Compute error values for homography and fundamental matrices + Vec H_e, F_e; + H_e.resize(x1.cols()); + F_e.resize(x1.cols()); + for (int i = 0; i < x1.cols(); i++) { + Vec2 current_x1, current_x2; + + intrinsics.NormalizedToImageSpace(x1(0, i), x1(1, i), + ¤t_x1(0), ¤t_x1(1)); + + intrinsics.NormalizedToImageSpace(x2(0, i), x2(1, i), + ¤t_x2(0), ¤t_x2(1)); + + H_e(i) = SymmetricGeometricDistance(H, current_x1, current_x2); + F_e(i) = SymmetricEpipolarDistance(F, current_x1, current_x2); + } + + LG << "H_e: " << H_e.transpose(); + LG << "F_e: " << F_e.transpose(); + + // Degeneracy constraint + double GRIC_H = GRIC(H_e, 2, 8, 4); + double GRIC_F = GRIC(F_e, 3, 7, 4); + + LG << "GRIC values for frames " << current_keyframe + << " and " << candidate_image + << ", H-GRIC: " << GRIC_H + << ", F-GRIC: " << GRIC_F; + + if (GRIC_H <= GRIC_F) + continue; + + // TODO(sergey): STEP 4: PELC criterion + + // STEP 5: Estimation of reconstruction error + // + // Uses paper Keyframe Selection for Camera Motion and Structure + // Estimation from Multiple Views + // Uses ftp://ftp.tnt.uni-hannover.de/pub/papers/2004/ECCV2004-TTHBAW.pdf + // Basically, equation (15) + // + // TODO(sergey): separate all the constraints into functions, + // this one is getting to much cluttered already + + // Definitions in equation (15): + // - I is the number of 3D feature points + // - A is the number of essential parameters of one camera + + EuclideanReconstruction reconstruction; + + // The F matrix should be an E matrix, but squash it just to be sure + + // Reconstruction should happen using normalized fundamental matrix + Mat3 F_normal = N * F * N_inverse; + + Mat3 E; + FundamentalToEssential(F_normal, &E); + + // Recover motion between the two images. Since this function assumes a + // calibrated camera, use the identity for K + Mat3 R; + Vec3 t; + Mat3 K = Mat3::Identity(); + + if (!MotionFromEssentialAndCorrespondence(E, + K, x1.col(0), + K, x2.col(0), + &R, &t)) { + LG << "Failed to compute R and t from E and K"; + continue; + } + + LG << "Camera transform between frames " << current_keyframe + << " and " << candidate_image + << ":\nR:\n" << R + << "\nt:" << t.transpose(); + + // First camera is identity, second one is relative to it + reconstruction.InsertCamera(current_keyframe, + Mat3::Identity(), + Vec3::Zero()); + reconstruction.InsertCamera(candidate_image, R, t); + + // Reconstruct 3D points + int intersects_total = 0, intersects_success = 0; + for (int i = 0; i < tracked_markers.size(); i++) { + if (!reconstruction.PointForTrack(tracked_markers[i].track)) { + vector reconstructed_markers; + + int track = tracked_markers[i].track; + + reconstructed_markers.push_back(tracked_markers[i]); + + // We know there're always only two markers for a track + // Also, we're using brute-force search because we don't + // actually know about markers layout in a list, but + // at this moment this cycle will run just once, which + // is not so big deal + + for (int j = i + 1; j < tracked_markers.size(); j++) { + if (tracked_markers[j].track == track) { + reconstructed_markers.push_back(tracked_markers[j]); + break; + } + } + + intersects_total++; + + if (EuclideanIntersect(reconstructed_markers, &reconstruction)) { + LG << "Ran Intersect() for track " << track; + intersects_success++; + } else { + LG << "Filed to intersect track " << track; + } + } + } + + double success_intersects_factor = + (double) intersects_success / intersects_total; + + if (success_intersects_factor < success_intersects_factor_best) { + LG << "Skip keyframe candidate because of " + "lower successful intersections ratio"; + + continue; + } + + success_intersects_factor_best = success_intersects_factor; + + Tracks two_frames_tracks(tracked_markers); + PolynomialCameraIntrinsics empty_intrinsics; + BundleEvaluation evaluation; + evaluation.evaluate_jacobian = true; + + EuclideanBundleCommonIntrinsics(two_frames_tracks, + BUNDLE_NO_INTRINSICS, + BUNDLE_NO_CONSTRAINTS, + &reconstruction, + &empty_intrinsics, + &evaluation); + + Mat &jacobian = evaluation.jacobian; + + Mat JT_J = jacobian.transpose() * jacobian; + // There are 7 degrees of freedom, so clamp them out. + Mat JT_J_inv = PseudoInverseWithClampedEigenvalues(JT_J, 7); + + Mat temp_derived = JT_J * JT_J_inv * JT_J; + bool is_inversed = (temp_derived - JT_J).cwiseAbs2().sum() < + 1e-4 * std::min(temp_derived.cwiseAbs2().sum(), + JT_J.cwiseAbs2().sum()); + + LG << "Check on inversed: " << (is_inversed ? "true" : "false" ) + << ", det(JT_J): " << JT_J.determinant(); + + if (!is_inversed) { + LG << "Ignoring candidature due to poor jacobian stability"; + continue; + } + + Mat Sigma_P; + Sigma_P = JT_J_inv.bottomRightCorner(evaluation.num_points * 3, + evaluation.num_points * 3); + + int I = evaluation.num_points; + int A = 12; + + double Sc = static_cast(I + A) / Square(3 * I) * Sigma_P.trace(); + + LG << "Expected estimation error between " + << current_keyframe << " and " + << candidate_image << ": " << Sc; + + // Pairing with a lower Sc indicates a better choice + if (Sc > Sc_best_candidate) + continue; + + Sc_best_candidate = Sc; + + next_keyframe = candidate_image; + } + + // This is a bit arbitrary and main reason of having this is to deal + // better with situations when there's no keyframes were found for + // current keyframe this could happen when there's no so much parallax + // in the beginning of image sequence and then most of features are + // getting occluded. In this case there could be good keyframe pair in + // the middle of the sequence + // + // However, it's just quick hack and smarter way to do this would be nice + if (next_keyframe == -1) { + next_keyframe = current_keyframe + 10; + number_keyframes = 0; + + if (next_keyframe >= max_image) + break; + + LG << "Starting searching for keyframes starting from " << next_keyframe; + } else { + // New pair's expected reconstruction error is lower + // than existing pair's one. + // + // For now let's store just one candidate, easy to + // store more candidates but needs some thoughts + // how to choose best one automatically from them + // (or allow user to choose pair manually). + if (Sc_best > Sc_best_candidate) { + keyframes.clear(); + keyframes.push_back(current_keyframe); + keyframes.push_back(next_keyframe); + Sc_best = Sc_best_candidate; + } + } + } +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/keyframe_selection.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/keyframe_selection.h new file mode 100644 index 00000000000..aa3eeaf193d --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/keyframe_selection.h @@ -0,0 +1,53 @@ +// Copyright (c) 2010, 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_KEYFRAME_SELECTION_H_ +#define LIBMV_SIMPLE_PIPELINE_KEYFRAME_SELECTION_H_ + +#include "libmv/base/vector.h" +#include "libmv/simple_pipeline/tracks.h" +#include "libmv/simple_pipeline/camera_intrinsics.h" + +namespace libmv { + +// Get list of all images which are good enough to be as keyframes from +// camera reconstruction. Based on GRIC criteria and uses Pollefeys' +// approach for correspondence ratio constraint. +// +// As an additional, additional criteria based on reconstruction +// variance is used. This means if correspondence and GRIC criteria +// are passed, two-frames reconstruction using candidate keyframes +// happens. After reconstruction variance of 3D points is calculating +// and if expected error estimation is too large, keyframe candidate +// is rejecting. +// +// \param tracks contains all tracked correspondences between frames +// expected to be undistorted and normalized +// \param intrinsics is camera intrinsics +// \param keyframes will contain all images number which are considered +// good to be used for reconstruction +void SelectKeyframesBasedOnGRICAndVariance( + const Tracks &tracks, + const CameraIntrinsics &intrinsics, + vector &keyframes); + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_KEYFRAME_SELECTION_H_ diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/pipeline.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/pipeline.cc new file mode 100644 index 00000000000..6c8592baa00 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/pipeline.cc @@ -0,0 +1,368 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/pipeline.h" + +#include + +#include "libmv/logging/logging.h" +#include "libmv/simple_pipeline/bundle.h" +#include "libmv/simple_pipeline/intersect.h" +#include "libmv/simple_pipeline/resect.h" +#include "libmv/simple_pipeline/reconstruction.h" +#include "libmv/simple_pipeline/tracks.h" +#include "libmv/simple_pipeline/camera_intrinsics.h" + +#ifdef _MSC_VER +# define snprintf _snprintf +#endif + +namespace libmv { +namespace { + +// These are "strategy" classes which make it possible to use the same code for +// both projective and euclidean reconstruction. +// FIXME(MatthiasF): OOP would achieve the same goal while avoiding +// template bloat and making interface changes much easier. +struct EuclideanPipelineRoutines { + typedef EuclideanReconstruction Reconstruction; + typedef EuclideanCamera Camera; + typedef EuclideanPoint Point; + + static void Bundle(const Tracks &tracks, + EuclideanReconstruction *reconstruction) { + EuclideanBundle(tracks, reconstruction); + } + + static bool Resect(const vector &markers, + EuclideanReconstruction *reconstruction, bool final_pass) { + return EuclideanResect(markers, reconstruction, final_pass); + } + + static bool Intersect(const vector &markers, + EuclideanReconstruction *reconstruction) { + return EuclideanIntersect(markers, reconstruction); + } + + static Marker ProjectMarker(const EuclideanPoint &point, + const EuclideanCamera &camera, + const CameraIntrinsics &intrinsics) { + Vec3 projected = camera.R * point.X + camera.t; + projected /= projected(2); + + Marker reprojected_marker; + intrinsics.ApplyIntrinsics(projected(0), + projected(1), + &reprojected_marker.x, + &reprojected_marker.y); + + reprojected_marker.image = camera.image; + reprojected_marker.track = point.track; + return reprojected_marker; + } +}; + +struct ProjectivePipelineRoutines { + typedef ProjectiveReconstruction Reconstruction; + typedef ProjectiveCamera Camera; + typedef ProjectivePoint Point; + + static void Bundle(const Tracks &tracks, + ProjectiveReconstruction *reconstruction) { + ProjectiveBundle(tracks, reconstruction); + } + + static bool Resect(const vector &markers, + ProjectiveReconstruction *reconstruction, bool final_pass) { + (void) final_pass; // Ignored. + + return ProjectiveResect(markers, reconstruction); + } + + static bool Intersect(const vector &markers, + ProjectiveReconstruction *reconstruction) { + return ProjectiveIntersect(markers, reconstruction); + } + + static Marker ProjectMarker(const ProjectivePoint &point, + const ProjectiveCamera &camera, + const CameraIntrinsics &intrinsics) { + Vec3 projected = camera.P * point.X; + projected /= projected(2); + + Marker reprojected_marker; + intrinsics.ApplyIntrinsics(projected(0), + projected(1), + &reprojected_marker.x, + &reprojected_marker.y); + + reprojected_marker.image = camera.image; + reprojected_marker.track = point.track; + return reprojected_marker; + } +}; + +} // namespace + +static void CompleteReconstructionLogProgress( + ProgressUpdateCallback *update_callback, + double progress, + const char *step = NULL) { + if (update_callback) { + char message[256]; + + if (step) + snprintf(message, sizeof(message), "Completing solution %d%% | %s", + (int)(progress*100), step); + else + snprintf(message, sizeof(message), "Completing solution %d%%", + (int)(progress*100)); + + update_callback->invoke(progress, message); + } +} + +template +void InternalCompleteReconstruction( + const Tracks &tracks, + typename PipelineRoutines::Reconstruction *reconstruction, + ProgressUpdateCallback *update_callback = NULL) { + int max_track = tracks.MaxTrack(); + int max_image = tracks.MaxImage(); + int num_resects = -1; + int num_intersects = -1; + int tot_resects = 0; + LG << "Max track: " << max_track; + LG << "Max image: " << max_image; + LG << "Number of markers: " << tracks.NumMarkers(); + while (num_resects != 0 || num_intersects != 0) { + // Do all possible intersections. + num_intersects = 0; + for (int track = 0; track <= max_track; ++track) { + if (reconstruction->PointForTrack(track)) { + LG << "Skipping point: " << track; + continue; + } + vector all_markers = tracks.MarkersForTrack(track); + LG << "Got " << all_markers.size() << " markers for track " << track; + + vector reconstructed_markers; + for (int i = 0; i < all_markers.size(); ++i) { + if (reconstruction->CameraForImage(all_markers[i].image)) { + reconstructed_markers.push_back(all_markers[i]); + } + } + LG << "Got " << reconstructed_markers.size() + << " reconstructed markers for track " << track; + if (reconstructed_markers.size() >= 2) { + CompleteReconstructionLogProgress(update_callback, + (double)tot_resects/(max_image)); + if (PipelineRoutines::Intersect(reconstructed_markers, + reconstruction)) { + num_intersects++; + LG << "Ran Intersect() for track " << track; + } else { + LG << "Failed Intersect() for track " << track; + } + } + } + if (num_intersects) { + CompleteReconstructionLogProgress(update_callback, + (double)tot_resects/(max_image), + "Bundling..."); + PipelineRoutines::Bundle(tracks, reconstruction); + LG << "Ran Bundle() after intersections."; + } + LG << "Did " << num_intersects << " intersects."; + + // Do all possible resections. + num_resects = 0; + for (int image = 0; image <= max_image; ++image) { + if (reconstruction->CameraForImage(image)) { + LG << "Skipping frame: " << image; + continue; + } + vector all_markers = tracks.MarkersInImage(image); + LG << "Got " << all_markers.size() << " markers for image " << image; + + vector reconstructed_markers; + for (int i = 0; i < all_markers.size(); ++i) { + if (reconstruction->PointForTrack(all_markers[i].track)) { + reconstructed_markers.push_back(all_markers[i]); + } + } + LG << "Got " << reconstructed_markers.size() + << " reconstructed markers for image " << image; + if (reconstructed_markers.size() >= 5) { + CompleteReconstructionLogProgress(update_callback, + (double)tot_resects/(max_image)); + if (PipelineRoutines::Resect(reconstructed_markers, + reconstruction, false)) { + num_resects++; + tot_resects++; + LG << "Ran Resect() for image " << image; + } else { + LG << "Failed Resect() for image " << image; + } + } + } + if (num_resects) { + CompleteReconstructionLogProgress(update_callback, + (double)tot_resects/(max_image), + "Bundling..."); + PipelineRoutines::Bundle(tracks, reconstruction); + } + LG << "Did " << num_resects << " resects."; + } + + // One last pass... + num_resects = 0; + for (int image = 0; image <= max_image; ++image) { + if (reconstruction->CameraForImage(image)) { + LG << "Skipping frame: " << image; + continue; + } + vector all_markers = tracks.MarkersInImage(image); + + vector reconstructed_markers; + for (int i = 0; i < all_markers.size(); ++i) { + if (reconstruction->PointForTrack(all_markers[i].track)) { + reconstructed_markers.push_back(all_markers[i]); + } + } + if (reconstructed_markers.size() >= 5) { + CompleteReconstructionLogProgress(update_callback, + (double)tot_resects/(max_image)); + if (PipelineRoutines::Resect(reconstructed_markers, + reconstruction, true)) { + num_resects++; + LG << "Ran final Resect() for image " << image; + } else { + LG << "Failed final Resect() for image " << image; + } + } + } + if (num_resects) { + CompleteReconstructionLogProgress(update_callback, + (double)tot_resects/(max_image), + "Bundling..."); + PipelineRoutines::Bundle(tracks, reconstruction); + } +} + +template +double InternalReprojectionError( + const Tracks &image_tracks, + const typename PipelineRoutines::Reconstruction &reconstruction, + const CameraIntrinsics &intrinsics) { + int num_skipped = 0; + int num_reprojected = 0; + double total_error = 0.0; + vector markers = image_tracks.AllMarkers(); + for (int i = 0; i < markers.size(); ++i) { + double weight = markers[i].weight; + const typename PipelineRoutines::Camera *camera = + reconstruction.CameraForImage(markers[i].image); + const typename PipelineRoutines::Point *point = + reconstruction.PointForTrack(markers[i].track); + if (!camera || !point || weight == 0.0) { + num_skipped++; + continue; + } + num_reprojected++; + + Marker reprojected_marker = + PipelineRoutines::ProjectMarker(*point, *camera, intrinsics); + double ex = (reprojected_marker.x - markers[i].x) * weight; + double ey = (reprojected_marker.y - markers[i].y) * weight; + + const int N = 100; + char line[N]; + snprintf(line, N, + "image %-3d track %-3d " + "x %7.1f y %7.1f " + "rx %7.1f ry %7.1f " + "ex %7.1f ey %7.1f" + " e %7.1f", + markers[i].image, + markers[i].track, + markers[i].x, + markers[i].y, + reprojected_marker.x, + reprojected_marker.y, + ex, + ey, + sqrt(ex*ex + ey*ey)); + VLOG(1) << line; + + total_error += sqrt(ex*ex + ey*ey); + } + LG << "Skipped " << num_skipped << " markers."; + LG << "Reprojected " << num_reprojected << " markers."; + LG << "Total error: " << total_error; + LG << "Average error: " << (total_error / num_reprojected) << " [pixels]."; + return total_error / num_reprojected; +} + +double EuclideanReprojectionError(const Tracks &image_tracks, + const EuclideanReconstruction &reconstruction, + const CameraIntrinsics &intrinsics) { + return InternalReprojectionError(image_tracks, + reconstruction, + intrinsics); +} + +double ProjectiveReprojectionError( + const Tracks &image_tracks, + const ProjectiveReconstruction &reconstruction, + const CameraIntrinsics &intrinsics) { + return InternalReprojectionError(image_tracks, + reconstruction, + intrinsics); +} + +void EuclideanCompleteReconstruction(const Tracks &tracks, + EuclideanReconstruction *reconstruction, + ProgressUpdateCallback *update_callback) { + InternalCompleteReconstruction(tracks, + reconstruction, + update_callback); +} + +void ProjectiveCompleteReconstruction(const Tracks &tracks, + ProjectiveReconstruction *reconstruction) { + InternalCompleteReconstruction(tracks, + reconstruction); +} + +void InvertIntrinsicsForTracks(const Tracks &raw_tracks, + const CameraIntrinsics &camera_intrinsics, + Tracks *calibrated_tracks) { + vector markers = raw_tracks.AllMarkers(); + for (int i = 0; i < markers.size(); ++i) { + camera_intrinsics.InvertIntrinsics(markers[i].x, + markers[i].y, + &(markers[i].x), + &(markers[i].y)); + } + *calibrated_tracks = Tracks(markers); +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/pipeline.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/pipeline.h new file mode 100644 index 00000000000..4d1bd00c51f --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/pipeline.h @@ -0,0 +1,98 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_PIPELINE_H_ +#define LIBMV_SIMPLE_PIPELINE_PIPELINE_H_ + +#include "libmv/simple_pipeline/callbacks.h" +#include "libmv/simple_pipeline/tracks.h" +#include "libmv/simple_pipeline/reconstruction.h" + +namespace libmv { + +/*! + Estimate camera poses and scene 3D coordinates for all frames and tracks. + + This method should be used once there is an initial reconstruction in + place, for example by reconstructing from two frames that have a sufficient + baseline and number of tracks in common. This function iteratively + triangulates points that are visible by cameras that have their pose + estimated, then resections (i.e. estimates the pose) of cameras that are + not estimated yet that can see triangulated points. This process is + repeated until all points and cameras are estimated. Periodically, bundle + adjustment is run to ensure a quality reconstruction. + + \a tracks should contain markers used in the reconstruction. + \a reconstruction should contain at least some 3D points or some estimated + cameras. The minimum number of cameras is two (with no 3D points) and the + minimum number of 3D points (with no estimated cameras) is 5. + + \sa EuclideanResect, EuclideanIntersect, EuclideanBundle +*/ +void EuclideanCompleteReconstruction( + const Tracks &tracks, + EuclideanReconstruction *reconstruction, + ProgressUpdateCallback *update_callback = NULL); + +/*! + Estimate camera matrices and homogeneous 3D coordinates for all frames and + tracks. + + This method should be used once there is an initial reconstruction in + place, for example by reconstructing from two frames that have a sufficient + baseline and number of tracks in common. This function iteratively + triangulates points that are visible by cameras that have their pose + estimated, then resections (i.e. estimates the pose) of cameras that are + not estimated yet that can see triangulated points. This process is + repeated until all points and cameras are estimated. Periodically, bundle + adjustment is run to ensure a quality reconstruction. + + \a tracks should contain markers used in the reconstruction. + \a reconstruction should contain at least some 3D points or some estimated + cameras. The minimum number of cameras is two (with no 3D points) and the + minimum number of 3D points (with no estimated cameras) is 5. + + \sa ProjectiveResect, ProjectiveIntersect, ProjectiveBundle +*/ +void ProjectiveCompleteReconstruction(const Tracks &tracks, + ProjectiveReconstruction *reconstruction); + + +class CameraIntrinsics; + +// TODO(keir): Decide if we want these in the public API, and if so, what the +// appropriate include file is. + +double EuclideanReprojectionError(const Tracks &image_tracks, + const EuclideanReconstruction &reconstruction, + const CameraIntrinsics &intrinsics); + +double ProjectiveReprojectionError( + const Tracks &image_tracks, + const ProjectiveReconstruction &reconstruction, + const CameraIntrinsics &intrinsics); + +void InvertIntrinsicsForTracks(const Tracks &raw_tracks, + const CameraIntrinsics &camera_intrinsics, + Tracks *calibrated_tracks); + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_PIPELINE_H_ diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction.cc new file mode 100644 index 00000000000..65e5dd27d5d --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction.cc @@ -0,0 +1,191 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/reconstruction.h" +#include "libmv/numeric/numeric.h" +#include "libmv/logging/logging.h" + +namespace libmv { + +EuclideanReconstruction::EuclideanReconstruction() {} +EuclideanReconstruction::EuclideanReconstruction( + const EuclideanReconstruction &other) { + cameras_ = other.cameras_; + points_ = other.points_; +} + +EuclideanReconstruction &EuclideanReconstruction::operator=( + const EuclideanReconstruction &other) { + if (&other != this) { + cameras_ = other.cameras_; + points_ = other.points_; + } + return *this; +} + +void EuclideanReconstruction::InsertCamera(int image, + const Mat3 &R, + const Vec3 &t) { + LG << "InsertCamera " << image << ":\nR:\n"<< R << "\nt:\n" << t; + if (image >= cameras_.size()) { + cameras_.resize(image + 1); + } + cameras_[image].image = image; + cameras_[image].R = R; + cameras_[image].t = t; +} + +void EuclideanReconstruction::InsertPoint(int track, const Vec3 &X) { + LG << "InsertPoint " << track << ":\n" << X; + if (track >= points_.size()) { + points_.resize(track + 1); + } + points_[track].track = track; + points_[track].X = X; +} + +EuclideanCamera *EuclideanReconstruction::CameraForImage(int image) { + return const_cast( + static_cast( + this)->CameraForImage(image)); +} + +const EuclideanCamera *EuclideanReconstruction::CameraForImage( + int image) const { + if (image < 0 || image >= cameras_.size()) { + return NULL; + } + const EuclideanCamera *camera = &cameras_[image]; + if (camera->image == -1) { + return NULL; + } + return camera; +} + +vector EuclideanReconstruction::AllCameras() const { + vector cameras; + for (int i = 0; i < cameras_.size(); ++i) { + if (cameras_[i].image != -1) { + cameras.push_back(cameras_[i]); + } + } + return cameras; +} + +EuclideanPoint *EuclideanReconstruction::PointForTrack(int track) { + return const_cast( + static_cast(this)->PointForTrack(track)); +} + +const EuclideanPoint *EuclideanReconstruction::PointForTrack(int track) const { + if (track < 0 || track >= points_.size()) { + return NULL; + } + const EuclideanPoint *point = &points_[track]; + if (point->track == -1) { + return NULL; + } + return point; +} + +vector EuclideanReconstruction::AllPoints() const { + vector points; + for (int i = 0; i < points_.size(); ++i) { + if (points_[i].track != -1) { + points.push_back(points_[i]); + } + } + return points; +} + +void ProjectiveReconstruction::InsertCamera(int image, + const Mat34 &P) { + LG << "InsertCamera " << image << ":\nP:\n"<< P; + if (image >= cameras_.size()) { + cameras_.resize(image + 1); + } + cameras_[image].image = image; + cameras_[image].P = P; +} + +void ProjectiveReconstruction::InsertPoint(int track, const Vec4 &X) { + LG << "InsertPoint " << track << ":\n" << X; + if (track >= points_.size()) { + points_.resize(track + 1); + } + points_[track].track = track; + points_[track].X = X; +} + +ProjectiveCamera *ProjectiveReconstruction::CameraForImage(int image) { + return const_cast( + static_cast( + this)->CameraForImage(image)); +} + +const ProjectiveCamera *ProjectiveReconstruction::CameraForImage( + int image) const { + if (image < 0 || image >= cameras_.size()) { + return NULL; + } + const ProjectiveCamera *camera = &cameras_[image]; + if (camera->image == -1) { + return NULL; + } + return camera; +} + +vector ProjectiveReconstruction::AllCameras() const { + vector cameras; + for (int i = 0; i < cameras_.size(); ++i) { + if (cameras_[i].image != -1) { + cameras.push_back(cameras_[i]); + } + } + return cameras; +} + +ProjectivePoint *ProjectiveReconstruction::PointForTrack(int track) { + return const_cast( + static_cast(this)->PointForTrack(track)); +} + +const ProjectivePoint *ProjectiveReconstruction::PointForTrack(int track) const { + if (track < 0 || track >= points_.size()) { + return NULL; + } + const ProjectivePoint *point = &points_[track]; + if (point->track == -1) { + return NULL; + } + return point; +} + +vector ProjectiveReconstruction::AllPoints() const { + vector points; + for (int i = 0; i < points_.size(); ++i) { + if (points_[i].track != -1) { + points.push_back(points_[i]); + } + } + return points; +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction.h new file mode 100644 index 00000000000..947a0636476 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction.h @@ -0,0 +1,217 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_RECONSTRUCTION_H_ +#define LIBMV_SIMPLE_PIPELINE_RECONSTRUCTION_H_ + +#include "libmv/base/vector.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +/*! + A EuclideanCamera is the location and rotation of the camera viewing \a image. + + \a image identify which image from \l Tracks this camera represents. + \a R is a 3x3 matrix representing the rotation of the camera. + \a t is a translation vector representing its positions. + + \sa Reconstruction +*/ +struct EuclideanCamera { + EuclideanCamera() : image(-1) {} + EuclideanCamera(const EuclideanCamera &c) : image(c.image), R(c.R), t(c.t) {} + + int image; + Mat3 R; + Vec3 t; +}; + +/*! + A Point is the 3D location of a track. + + \a track identify which track from \l Tracks this point corresponds to. + \a X represents the 3D position of the track. + + \sa Reconstruction +*/ +struct EuclideanPoint { + EuclideanPoint() : track(-1) {} + EuclideanPoint(const EuclideanPoint &p) : track(p.track), X(p.X) {} + int track; + Vec3 X; +}; + +/*! + The EuclideanReconstruction class stores \link EuclideanCamera cameras + \endlink and \link EuclideanPoint points \endlink. + + The EuclideanReconstruction container is intended as the store of 3D + reconstruction data to be used with the MultiView API. + + The container has lookups to query a \a EuclideanCamera from an \a image or + a \a EuclideanPoint from a \a track. + + \sa Camera, Point +*/ +class EuclideanReconstruction { + public: + // Default constructor starts with no cameras. + EuclideanReconstruction(); + + /// Copy constructor. + EuclideanReconstruction(const EuclideanReconstruction &other); + + EuclideanReconstruction &operator=(const EuclideanReconstruction &other); + + /*! + Insert a camera into the set. If there is already a camera for the given + \a image, the existing camera is replaced. If there is no camera for the + given \a image, a new one is added. + + \a image is the key used to retrieve the cameras with the other methods + in this class. + + \note You should use the same \a image identifier as in \l Tracks. + */ + void InsertCamera(int image, const Mat3 &R, const Vec3 &t); + + /*! + Insert a point into the reconstruction. If there is already a point for + the given \a track, the existing point is replaced. If there is no point + for the given \a track, a new one is added. + + \a track is the key used to retrieve the points with the + other methods in this class. + + \note You should use the same \a track identifier as in \l Tracks. + */ + void InsertPoint(int track, const Vec3 &X); + + /// Returns a pointer to the camera corresponding to \a image. + EuclideanCamera *CameraForImage(int image); + const EuclideanCamera *CameraForImage(int image) const; + + /// Returns all cameras. + vector AllCameras() const; + + /// Returns a pointer to the point corresponding to \a track. + EuclideanPoint *PointForTrack(int track); + const EuclideanPoint *PointForTrack(int track) const; + + /// Returns all points. + vector AllPoints() const; + + private: + vector cameras_; + vector points_; +}; + +/*! + A ProjectiveCamera is the projection matrix for the camera of \a image. + + \a image identify which image from \l Tracks this camera represents. + \a P is the 3x4 projection matrix. + + \sa ProjectiveReconstruction +*/ +struct ProjectiveCamera { + ProjectiveCamera() : image(-1) {} + ProjectiveCamera(const ProjectiveCamera &c) : image(c.image), P(c.P) {} + + int image; + Mat34 P; +}; + +/*! + A Point is the 3D location of a track. + + \a track identifies which track from \l Tracks this point corresponds to. + \a X is the homogeneous 3D position of the track. + + \sa Reconstruction +*/ +struct ProjectivePoint { + ProjectivePoint() : track(-1) {} + ProjectivePoint(const ProjectivePoint &p) : track(p.track), X(p.X) {} + int track; + Vec4 X; +}; + +/*! + The ProjectiveReconstruction class stores \link ProjectiveCamera cameras + \endlink and \link ProjectivePoint points \endlink. + + The ProjectiveReconstruction container is intended as the store of 3D + reconstruction data to be used with the MultiView API. + + The container has lookups to query a \a ProjectiveCamera from an \a image or + a \a ProjectivePoint from a \a track. + + \sa Camera, Point +*/ +class ProjectiveReconstruction { + public: + /*! + Insert a camera into the set. If there is already a camera for the given + \a image, the existing camera is replaced. If there is no camera for the + given \a image, a new one is added. + + \a image is the key used to retrieve the cameras with the other methods + in this class. + + \note You should use the same \a image identifier as in \l Tracks. + */ + void InsertCamera(int image, const Mat34 &P); + + /*! + Insert a point into the reconstruction. If there is already a point for + the given \a track, the existing point is replaced. If there is no point + for the given \a track, a new one is added. + + \a track is the key used to retrieve the points with the + other methods in this class. + + \note You should use the same \a track identifier as in \l Tracks. + */ + void InsertPoint(int track, const Vec4 &X); + + /// Returns a pointer to the camera corresponding to \a image. + ProjectiveCamera *CameraForImage(int image); + const ProjectiveCamera *CameraForImage(int image) const; + + /// Returns all cameras. + vector AllCameras() const; + + /// Returns a pointer to the point corresponding to \a track. + ProjectivePoint *PointForTrack(int track); + const ProjectivePoint *PointForTrack(int track) const; + + /// Returns all points. + vector AllPoints() const; + + private: + vector cameras_; + vector points_; +}; + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_RECONSTRUCTION_H_ diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction_scale.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction_scale.cc new file mode 100644 index 00000000000..40ac23be7a2 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction_scale.cc @@ -0,0 +1,68 @@ +// Copyright (c) 2013 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/reconstruction_scale.h" +#include "libmv/logging/logging.h" + +namespace libmv { + +void EuclideanScaleToUnity(EuclideanReconstruction *reconstruction) { + vector all_cameras = reconstruction->AllCameras(); + vector all_points = reconstruction->AllPoints(); + + // Calculate center of the mass of all cameras. + Vec3 cameras_mass_center = Vec3::Zero(); + for (int i = 0; i < all_cameras.size(); ++i) { + cameras_mass_center += all_cameras[i].t; + } + cameras_mass_center /= all_cameras.size(); + + // Find the most distant camera from the mass center. + double max_distance = 0.0; + for (int i = 0; i < all_cameras.size(); ++i) { + double distance = (all_cameras[i].t - cameras_mass_center).squaredNorm(); + if (distance > max_distance) { + max_distance = distance; + } + } + + if (max_distance == 0.0) { + LG << "Cameras position variance is too small, can not rescale"; + return; + } + + double scale_factor = 1.0 / sqrt(max_distance); + + // Rescale cameras positions. + for (int i = 0; i < all_cameras.size(); ++i) { + int image = all_cameras[i].image; + EuclideanCamera *camera = reconstruction->CameraForImage(image); + camera->t = camera->t * scale_factor; + } + + // Rescale points positions. + for (int i = 0; i < all_points.size(); ++i) { + int track = all_points[i].track; + EuclideanPoint *point = reconstruction->PointForTrack(track); + point->X = point->X * scale_factor; + } +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction_scale.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction_scale.h new file mode 100644 index 00000000000..f2349ff5146 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/reconstruction_scale.h @@ -0,0 +1,36 @@ +// Copyright (c) 2013 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_RECONSTRUCTION_SCALE_H_ +#define LIBMV_SIMPLE_PIPELINE_RECONSTRUCTION_SCALE_H_ + +#include "libmv/simple_pipeline/reconstruction.h" + +namespace libmv { + +/*! + Scale euclidean reconstruction in a way variance of + camera centers equals to one. + */ +void EuclideanScaleToUnity(EuclideanReconstruction *reconstruction); + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_RECONSTRUCTION_SCALE_H_ diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/resect.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/resect.cc new file mode 100644 index 00000000000..e73fc44df2a --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/resect.cc @@ -0,0 +1,270 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/resect.h" + +#include + +#include "libmv/base/vector.h" +#include "libmv/logging/logging.h" +#include "libmv/multiview/euclidean_resection.h" +#include "libmv/multiview/resection.h" +#include "libmv/multiview/projection.h" +#include "libmv/numeric/numeric.h" +#include "libmv/numeric/levenberg_marquardt.h" +#include "libmv/simple_pipeline/reconstruction.h" +#include "libmv/simple_pipeline/tracks.h" + +namespace libmv { +namespace { + +Mat2X PointMatrixFromMarkers(const vector &markers) { + Mat2X points(2, markers.size()); + for (int i = 0; i < markers.size(); ++i) { + points(0, i) = markers[i].x; + points(1, i) = markers[i].y; + } + return points; +} + +// Uses an incremental rotation: +// +// x = R' * R * X + t; +// +// to avoid issues with the rotation representation. R' is derived from a +// euler vector encoding the rotation in 3 parameters; the direction is the +// axis to rotate around and the magnitude is the amount of the rotation. +struct EuclideanResectCostFunction { + public: + typedef Vec FMatrixType; + typedef Vec6 XMatrixType; + + EuclideanResectCostFunction(const vector &markers, + const EuclideanReconstruction &reconstruction, + const Mat3 &initial_R) + : markers(markers), + reconstruction(reconstruction), + initial_R(initial_R) {} + + // dRt has dR (delta R) encoded as a euler vector in the first 3 parameters, + // followed by t in the next 3 parameters. + Vec operator()(const Vec6 &dRt) const { + // Unpack R, t from dRt. + Mat3 R = RotationFromEulerVector(dRt.head<3>()) * initial_R; + Vec3 t = dRt.tail<3>(); + + // Compute the reprojection error for each coordinate. + Vec residuals(2 * markers.size()); + residuals.setZero(); + for (int i = 0; i < markers.size(); ++i) { + const EuclideanPoint &point = + *reconstruction.PointForTrack(markers[i].track); + Vec3 projected = R * point.X + t; + projected /= projected(2); + residuals[2*i + 0] = projected(0) - markers[i].x; + residuals[2*i + 1] = projected(1) - markers[i].y; + } + return residuals; + } + + const vector &markers; + const EuclideanReconstruction &reconstruction; + const Mat3 &initial_R; +}; + +} // namespace + +bool EuclideanResect(const vector &markers, + EuclideanReconstruction *reconstruction, bool final_pass) { + if (markers.size() < 5) { + return false; + } + Mat2X points_2d = PointMatrixFromMarkers(markers); + Mat3X points_3d(3, markers.size()); + for (int i = 0; i < markers.size(); i++) { + points_3d.col(i) = reconstruction->PointForTrack(markers[i].track)->X; + } + LG << "Points for resect:\n" << points_2d; + + Mat3 R; + Vec3 t; + + if (0 || !euclidean_resection::EuclideanResection( + points_2d, points_3d, &R, &t, + euclidean_resection::RESECTION_EPNP)) { + // printf("Resection for image %d failed\n", markers[0].image); + LG << "Resection for image " << markers[0].image << " failed;" + << " trying fallback projective resection."; + + LG << "No fallback; failing resection for " << markers[0].image; + return false; + + if (!final_pass) return false; + // Euclidean resection failed. Fall back to projective resection, which is + // less reliable but better conditioned when there are many points. + Mat34 P; + Mat4X points_3d_homogeneous(4, markers.size()); + for (int i = 0; i < markers.size(); i++) { + points_3d_homogeneous.col(i).head<3>() = points_3d.col(i); + points_3d_homogeneous(3, i) = 1.0; + } + resection::Resection(points_2d, points_3d_homogeneous, &P); + if ((P * points_3d_homogeneous.col(0))(2) < 0) { + LG << "Point behind camera; switch sign."; + P = -P; + } + + Mat3 ignored; + KRt_From_P(P, &ignored, &R, &t); + + // The R matrix should be a rotation, but don't rely on it. + Eigen::JacobiSVD svd(R, Eigen::ComputeFullU | Eigen::ComputeFullV); + + LG << "Resection rotation is: " << svd.singularValues().transpose(); + LG << "Determinant is: " << R.determinant(); + + // Correct to make R a rotation. + R = svd.matrixU() * svd.matrixV().transpose(); + + Vec3 xx = R * points_3d.col(0) + t; + if (xx(2) < 0.0) { + LG << "Final point is still behind camera..."; + } + // XXX Need to check if error is horrible and fail here too in that case. + } + + // Refine the result. + typedef LevenbergMarquardt Solver; + + // Give the cost our initial guess for R. + EuclideanResectCostFunction resect_cost(markers, *reconstruction, R); + + // Encode the initial parameters: start with zero delta rotation, and the + // guess for t obtained from resection. + Vec6 dRt = Vec6::Zero(); + dRt.tail<3>() = t; + + Solver solver(resect_cost); + + Solver::SolverParameters params; + /* Solver::Results results = */ solver.minimize(params, &dRt); + LG << "LM found incremental rotation: " << dRt.head<3>().transpose(); + // TODO(keir): Check results to ensure clean termination. + + // Unpack the rotation and translation. + R = RotationFromEulerVector(dRt.head<3>()) * R; + t = dRt.tail<3>(); + + LG << "Resection for image " << markers[0].image << " got:\n" + << "R:\n" << R << "\nt:\n" << t; + reconstruction->InsertCamera(markers[0].image, R, t); + return true; +} + +namespace { + +// Directly parameterize the projection matrix, P, which is a 12 parameter +// homogeneous entry. In theory P should be parameterized with only 11 +// parametetrs, but in practice it works fine to let the extra degree of +// freedom drift. +struct ProjectiveResectCostFunction { + public: + typedef Vec FMatrixType; + typedef Vec12 XMatrixType; + + ProjectiveResectCostFunction(const vector &markers, + const ProjectiveReconstruction &reconstruction) + : markers(markers), + reconstruction(reconstruction) {} + + Vec operator()(const Vec12 &vector_P) const { + // Unpack P from vector_P. + Map P(vector_P.data(), 3, 4); + + // Compute the reprojection error for each coordinate. + Vec residuals(2 * markers.size()); + residuals.setZero(); + for (int i = 0; i < markers.size(); ++i) { + const ProjectivePoint &point = + *reconstruction.PointForTrack(markers[i].track); + Vec3 projected = P * point.X; + projected /= projected(2); + residuals[2*i + 0] = projected(0) - markers[i].x; + residuals[2*i + 1] = projected(1) - markers[i].y; + } + return residuals; + } + + const vector &markers; + const ProjectiveReconstruction &reconstruction; +}; + +} // namespace + +bool ProjectiveResect(const vector &markers, + ProjectiveReconstruction *reconstruction) { + if (markers.size() < 5) { + return false; + } + + // Stack the homogeneous 3D points as the columns of a matrix. + Mat2X points_2d = PointMatrixFromMarkers(markers); + Mat4X points_3d_homogeneous(4, markers.size()); + for (int i = 0; i < markers.size(); i++) { + points_3d_homogeneous.col(i) = + reconstruction->PointForTrack(markers[i].track)->X; + } + LG << "Points for resect:\n" << points_2d; + + // Resection the point. + Mat34 P; + resection::Resection(points_2d, points_3d_homogeneous, &P); + + // Flip the sign of P if necessary to keep the point in front of the camera. + if ((P * points_3d_homogeneous.col(0))(2) < 0) { + LG << "Point behind camera; switch sign."; + P = -P; + } + + // TODO(keir): Check if error is horrible and fail in that case. + + // Refine the resulting projection matrix using geometric error. + typedef LevenbergMarquardt Solver; + + ProjectiveResectCostFunction resect_cost(markers, *reconstruction); + + // Pack the initial P matrix into a size-12 vector.. + Vec12 vector_P = Map(P.data()); + + Solver solver(resect_cost); + + Solver::SolverParameters params; + /* Solver::Results results = */ solver.minimize(params, &vector_P); + // TODO(keir): Check results to ensure clean termination. + + // Unpack the projection matrix. + P = Map(vector_P.data(), 3, 4); + + LG << "Resection for image " << markers[0].image << " got:\n" + << "P:\n" << P; + reconstruction->InsertCamera(markers[0].image, P); + return true; +} +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/resect.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/resect.h new file mode 100644 index 00000000000..7ca3237437e --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/resect.h @@ -0,0 +1,86 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_RESECT_H +#define LIBMV_SIMPLE_PIPELINE_RESECT_H + +#include "libmv/base/vector.h" +#include "libmv/simple_pipeline/tracks.h" +#include "libmv/simple_pipeline/reconstruction.h" + +namespace libmv { + +/*! + Estimate the Euclidean pose of a camera from 2D to 3D correspondences. + + This takes a set of markers visible in one frame (which is the one to + resection), such that the markers are also reconstructed in 3D in the + reconstruction object, and solves for the pose and orientation of the + camera for that frame. + + \a markers should contain \l Marker markers \endlink belonging to tracks + visible in the one frame to be resectioned. Each of the tracks associated + with the markers must have a corresponding reconstructed 3D position in the + \a *reconstruction object. + + \a *reconstruction should contain the 3D points associated with the tracks + for the markers present in \a markers. + + \note This assumes a calibrated reconstruction, e.g. the markers are + already corrected for camera intrinsics and radial distortion. + \note This assumes an outlier-free set of markers. + + \return True if the resection was successful, false otherwise. + + \sa EuclideanIntersect, EuclideanReconstructTwoFrames +*/ +bool EuclideanResect(const vector &markers, + EuclideanReconstruction *reconstruction, bool final_pass); + +/*! + Estimate the projective pose of a camera from 2D to 3D correspondences. + + This takes a set of markers visible in one frame (which is the one to + resection), such that the markers are also reconstructed in a projective + frame in the reconstruction object, and solves for the projective matrix of + the camera for that frame. + + \a markers should contain \l Marker markers \endlink belonging to tracks + visible in the one frame to be resectioned. Each of the tracks associated + with the markers must have a corresponding reconstructed homogeneous 3D + position in the \a *reconstruction object. + + \a *reconstruction should contain the homogeneous 3D points associated with + the tracks for the markers present in \a markers. + + \note This assumes radial distortion has already been corrected, but + otherwise works for uncalibrated sequences. + \note This assumes an outlier-free set of markers. + + \return True if the resection was successful, false otherwise. + + \sa ProjectiveIntersect, ProjectiveReconstructTwoFrames +*/ +bool ProjectiveResect(const vector &markers, + ProjectiveReconstruction *reconstruction); + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_RESECT_H diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/tracks.cc b/modules/sfm/src/libmv_light/libmv/simple_pipeline/tracks.cc new file mode 100644 index 00000000000..d5d009708ba --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/tracks.cc @@ -0,0 +1,187 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#include "libmv/simple_pipeline/tracks.h" + +#include +#include +#include + +#include "libmv/numeric/numeric.h" + +namespace libmv { + +Tracks::Tracks(const Tracks &other) { + markers_ = other.markers_; +} + +Tracks::Tracks(const vector &markers) : markers_(markers) {} + +void Tracks::Insert(int image, int track, double x, double y, double weight) { + // TODO(keir): Wow, this is quadratic for repeated insertions. Fix this by + // adding a smarter data structure like a set<>. + for (int i = 0; i < markers_.size(); ++i) { + if (markers_[i].image == image && + markers_[i].track == track) { + markers_[i].x = x; + markers_[i].y = y; + return; + } + } + Marker marker = { image, track, x, y, weight }; + markers_.push_back(marker); +} + +vector Tracks::AllMarkers() const { + return markers_; +} + +vector Tracks::MarkersInImage(int image) const { + vector markers; + for (int i = 0; i < markers_.size(); ++i) { + if (image == markers_[i].image) { + markers.push_back(markers_[i]); + } + } + return markers; +} + +vector Tracks::MarkersForTrack(int track) const { + vector markers; + for (int i = 0; i < markers_.size(); ++i) { + if (track == markers_[i].track) { + markers.push_back(markers_[i]); + } + } + return markers; +} + +vector Tracks::MarkersInBothImages(int image1, int image2) const { + vector markers; + for (int i = 0; i < markers_.size(); ++i) { + int image = markers_[i].image; + if (image == image1 || image == image2) + markers.push_back(markers_[i]); + } + return markers; +} + +vector Tracks::MarkersForTracksInBothImages(int image1, + int image2) const { + std::vector image1_tracks; + std::vector image2_tracks; + + for (int i = 0; i < markers_.size(); ++i) { + int image = markers_[i].image; + if (image == image1) { + image1_tracks.push_back(markers_[i].track); + } else if (image == image2) { + image2_tracks.push_back(markers_[i].track); + } + } + + std::sort(image1_tracks.begin(), image1_tracks.end()); + std::sort(image2_tracks.begin(), image2_tracks.end()); + + std::vector intersection; + std::set_intersection(image1_tracks.begin(), image1_tracks.end(), + image2_tracks.begin(), image2_tracks.end(), + std::back_inserter(intersection)); + + vector markers; + for (int i = 0; i < markers_.size(); ++i) { + if ((markers_[i].image == image1 || markers_[i].image == image2) && + std::binary_search(intersection.begin(), intersection.end(), + markers_[i].track)) { + markers.push_back(markers_[i]); + } + } + return markers; +} + +Marker Tracks::MarkerInImageForTrack(int image, int track) const { + for (int i = 0; i < markers_.size(); ++i) { + if (markers_[i].image == image && markers_[i].track == track) { + return markers_[i]; + } + } + Marker null = { -1, -1, -1, -1, 0.0 }; + return null; +} + +void Tracks::RemoveMarkersForTrack(int track) { + int size = 0; + for (int i = 0; i < markers_.size(); ++i) { + if (markers_[i].track != track) { + markers_[size++] = markers_[i]; + } + } + markers_.resize(size); +} + +void Tracks::RemoveMarker(int image, int track) { + int size = 0; + for (int i = 0; i < markers_.size(); ++i) { + if (markers_[i].image != image || markers_[i].track != track) { + markers_[size++] = markers_[i]; + } + } + markers_.resize(size); +} + +int Tracks::MaxImage() const { + // TODO(MatthiasF): maintain a max_image_ member (updated on Insert) + int max_image = 0; + for (int i = 0; i < markers_.size(); ++i) { + max_image = std::max(markers_[i].image, max_image); + } + return max_image; +} + +int Tracks::MaxTrack() const { + // TODO(MatthiasF): maintain a max_track_ member (updated on Insert) + int max_track = 0; + for (int i = 0; i < markers_.size(); ++i) { + max_track = std::max(markers_[i].track, max_track); + } + return max_track; +} + +int Tracks::NumMarkers() const { + return markers_.size(); +} + +void CoordinatesForMarkersInImage(const vector &markers, + int image, + Mat *coordinates) { + vector coords; + for (int i = 0; i < markers.size(); ++i) { + const Marker &marker = markers[i]; + if (markers[i].image == image) { + coords.push_back(Vec2(marker.x, marker.y)); + } + } + coordinates->resize(2, coords.size()); + for (int i = 0; i < coords.size(); i++) { + coordinates->col(i) = coords[i]; + } +} + +} // namespace libmv diff --git a/modules/sfm/src/libmv_light/libmv/simple_pipeline/tracks.h b/modules/sfm/src/libmv_light/libmv/simple_pipeline/tracks.h new file mode 100644 index 00000000000..a54a43659b7 --- /dev/null +++ b/modules/sfm/src/libmv_light/libmv/simple_pipeline/tracks.h @@ -0,0 +1,138 @@ +// Copyright (c) 2011 libmv authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#ifndef LIBMV_SIMPLE_PIPELINE_TRACKS_H_ +#define LIBMV_SIMPLE_PIPELINE_TRACKS_H_ + +#include "libmv/base/vector.h" +#include "libmv/numeric/numeric.h" + +namespace libmv { + +/*! + A Marker is the 2D location of a tracked point in an image. + + \a x, \a y is the position of the marker in pixels from the top left corner + in the image identified by \a image. All markers for to the same target + form a track identified by a common \a track number. + + \a weight is used by bundle adjustment and weight means how much the + track affects on a final solution. + + \note Markers are typically aggregated with the help of the \l Tracks class. + + \sa Tracks +*/ +// TODO(sergey): Consider using comment for every member separately +// instead of having one giantic comment block. +struct Marker { + int image; + int track; + double x, y; + double weight; +}; + +/*! + The Tracks class stores \link Marker reconstruction markers \endlink. + + The Tracks container is intended as the store of correspondences between + images, which must get created before any 3D reconstruction can take place. + + The container has several fast lookups for queries typically needed for + structure from motion algorithms, such as \l MarkersForTracksInBothImages(). + + \sa Marker +*/ +class Tracks { + public: + Tracks() { } + + // Copy constructor for a tracks object. + Tracks(const Tracks &other); + + /// Construct a new tracks object using the given markers to start. + explicit Tracks(const vector &markers); + + /*! + Inserts a marker into the set. If there is already a marker for the given + \a image and \a track, the existing marker is replaced. If there is no + marker for the given \a image and \a track, a new one is added. + + \a image and \a track are the keys used to retrieve the markers with the + other methods in this class. + + \a weight is used by bundle adjustment and weight means how much the + track affects on a final solution. + + \note To get an identifier for a new track, use \l MaxTrack() + 1. + */ + // TODO(sergey): Consider using InsetWeightedMarker istead of using + // stupid default value? + void Insert(int image, int track, double x, double y, double weight = 1.0); + + /// Returns all the markers. + vector AllMarkers() const; + + /// Returns all the markers belonging to a track. + vector MarkersForTrack(int track) const; + + /// Returns all the markers visible in \a image. + vector MarkersInImage(int image) const; + + /// Returns all the markers visible in \a image1 and \a image2. + vector MarkersInBothImages(int image1, int image2) const; + + /*! + Returns the markers in \a image1 and \a image2 which have a common track. + + This is not the same as the union of the markers in \a image1 and \a + image2; each marker is for a track that appears in both images. + */ + vector MarkersForTracksInBothImages(int image1, int image2) const; + + /// Returns the marker in \a image belonging to \a track. + Marker MarkerInImageForTrack(int image, int track) const; + + /// Removes all the markers belonging to \a track. + void RemoveMarkersForTrack(int track); + + /// Removes the marker in \a image belonging to \a track. + void RemoveMarker(int image, int track); + + /// Returns the maximum image identifier used. + int MaxImage() const; + + /// Returns the maximum track identifier used. + int MaxTrack() const; + + /// Returns the number of markers. + int NumMarkers() const; + + private: + vector markers_; +}; + +void CoordinatesForMarkersInImage(const vector &markers, + int image, + Mat *coordinates); + +} // namespace libmv + +#endif // LIBMV_SIMPLE_PIPELINE_MARKERS_H_ diff --git a/modules/sfm/src/numeric.cpp b/modules/sfm/src/numeric.cpp new file mode 100644 index 00000000000..221afcf95c8 --- /dev/null +++ b/modules/sfm/src/numeric.cpp @@ -0,0 +1,173 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "precomp.hpp" + +// Eigen +#include + +// OpenCV +#include +#include + +// libmv headers +#include "libmv/numeric/numeric.h" + +#include + +namespace cv +{ +namespace sfm +{ + +template +void +meanAndVarianceAlongRows( const Mat_ &A, + Mat_ mean, + Mat_ variance ) +{ + const int n = A.rows, m = A.cols; + + for( int i = 0; i < n; ++i ) + { + mean(i) = 0; + variance(i) = 0; + + for( int j = 0; j < m; ++j ) + { + T x = A(i,j); + mean(i) += x; + variance(i) += x*x; + } + } + + mean /= m; + for (int i = 0; i < n; ++i) { + variance(i) = variance(i) / m - (mean(i)*mean(i)); + } +} + +void +meanAndVarianceAlongRows( InputArray _A, + OutputArray _mean, + OutputArray _variance ) +{ + const Mat A = _A.getMat(); + const int depth = A.depth(); + CV_Assert( depth == CV_32F || depth == CV_64F ); + + _mean.create(A.rows, 1, depth); + _variance.create(A.rows, 1, depth); + + Mat mean = _mean.getMat(), variance = _variance.getMat(); + + if( depth == CV_32F ) + { + meanAndVarianceAlongRows( A, mean, variance ); + } + else + { + meanAndVarianceAlongRows( A, mean, variance ); + } +} + + +//template +//inline Mat +//skewMatMinimal( const Mat_ &x ) +//{ +// Mat_ skew(2,3); +// skew << 0, -1, x(1), +// 1, 0, -x(0); +// return skew; +//} +// +//Mat +//skewMatMinimal( InputArray _x ) +//{ +// Mat x = _x.getMat(); +// CV_Assert( x.rows == 3 && x.cols == 1 ); +// +// int depth = x.depth(); +// if( depth == CV_32F ) +// { +// return skewMatMinimal(x); +// } +// else +// { +// return skewMatMinimal(x); +// } +//} + +template +Mat +skewMat( const Mat_ &x ) +{ + Mat_ skew(3,3); + skew << 0 , -x(2), x(1), + x(2), 0 , -x(0), + -x(1), x(0), 0; + + return skew; +} + +Mat +skew( InputArray _x ) +{ + const Mat x = _x.getMat(); + const int depth = x.depth(); + CV_Assert( x.size() == Size(3,1) || x.size() == Size(1,3) ); + CV_Assert( depth == CV_32F || depth == CV_64F ); + + Mat skewMatrix; + if( depth == CV_32F ) + { + skewMatrix = skewMat(x); + } + else if( depth == CV_64F ) + { + skewMatrix = skewMat(x); + } + else + { + //CV_Error(CV_StsBadArg, "The DataType must be CV_32F or CV_64F"); + } + + return skewMatrix; +} + + +} /* namespace sfm */ +} /* namespace cv */ diff --git a/modules/sfm/src/precomp.hpp b/modules/sfm/src/precomp.hpp new file mode 100644 index 00000000000..15d5b8739bc --- /dev/null +++ b/modules/sfm/src/precomp.hpp @@ -0,0 +1,47 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// + // + // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. + // + // By downloading, copying, installing or using the software you agree to this license. + // If you do not agree to this license, do not download, install, + // copy or use the software. + // + // + // License Agreement + // For Open Source Computer Vision Library + // + // Copyright (C) 2015, OpenCV Foundation, all rights reserved. + // Third party copyrights are property of their respective owners. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistribution's of source code must retain the above copyright notice, + // this list of conditions and the following disclaimer. + // + // * Redistribution's in binary form must reproduce the above copyright notice, + // this list of conditions and the following disclaimer in the documentation + // and/or other materials provided with the distribution. + // + // * The name of the copyright holders may not be used to endorse or promote products + // derived from this software without specific prior written permission. + // + // This software is provided by the copyright holders and contributors "as is" and + // any express or implied warranties, including, but not limited to, the implied + // warranties of merchantability and fitness for a particular purpose are disclaimed. + // In no event shall the Intel Corporation or contributors be liable for any direct, + // indirect, incidental, special, exemplary, or consequential damages + // (including, but not limited to, procurement of substitute goods or services; + // loss of use, data, or profits; or business interruption) however caused + // and on any theory of liability, whether in contract, strict liability, + // or tort (including negligence or otherwise) arising in any way out of + // the use of this software, even if advised of the possibility of such damage. + // + //M*/ + +#ifndef __OPENCV_SFM_PRECOMP__ +#define __OPENCV_SFM_PRECOMP__ + +#include + +#endif diff --git a/modules/sfm/src/projection.cpp b/modules/sfm/src/projection.cpp new file mode 100644 index 00000000000..756e9bb5584 --- /dev/null +++ b/modules/sfm/src/projection.cpp @@ -0,0 +1,223 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "precomp.hpp" + +// Eigen +#include + +// OpenCV +#include +#include +#include + +// libmv headers +#include "libmv/multiview/projection.h" + +#include + +namespace cv +{ +namespace sfm +{ + +template +void +homogeneousToEuclidean(const Mat & _X, Mat & _x) +{ + int d = _X.rows - 1; + + const Mat_ & X_rows = _X.rowRange(0,d); + const Mat_ h = _X.row(d); + + const T * h_ptr = h[0], *h_ptr_end = h_ptr + h.cols; + const T * X_ptr = X_rows[0]; + T * x_ptr = _x.ptr(0); + for (; h_ptr != h_ptr_end; ++h_ptr, ++X_ptr, ++x_ptr) + { + const T * X_col_ptr = X_ptr; + T * x_col_ptr = x_ptr, *x_col_ptr_end = x_col_ptr + d * _x.step1(); + for (; x_col_ptr != x_col_ptr_end; X_col_ptr+=X_rows.step1(), x_col_ptr+=_x.step1() ) + *x_col_ptr = (*X_col_ptr) / (*h_ptr); + } +} + +void +homogeneousToEuclidean(const InputArray _X, OutputArray _x) +{ + // src + const Mat X = _X.getMat(); + + // dst + _x.create(X.rows-1, X.cols, X.type()); + Mat x = _x.getMat(); + + // type + if( X.depth() == CV_32F ) + { + homogeneousToEuclidean(X,x); + } + else + { + homogeneousToEuclidean(X,x); + } +} + +void +euclideanToHomogeneous(const InputArray _x, OutputArray _X) +{ + const Mat x = _x.getMat(); + const Mat last_row = Mat::ones(1, x.cols, x.type()); + vconcat(x, last_row, _X); +} + +template +void +projectionFromKRt(const Mat_ &K, const Mat_ &R, const Mat_ &t, Mat_ P) +{ + hconcat( K*R, K*t, P ); +} + +void +projectionFromKRt(InputArray _K, InputArray _R, InputArray _t, OutputArray _P) +{ + const Mat K = _K.getMat(), R = _R.getMat(), t = _t.getMat(); + const int depth = K.depth(); + CV_Assert((K.cols == 3 && K.rows == 3) && (t.cols == 1 && t.rows == 3) && (K.size() == R.size())); + CV_Assert((depth == CV_32F || depth == CV_64F) && depth == R.depth() && depth == t.depth()); + + _P.create(3, 4, depth); + + Mat P = _P.getMat(); + + // type + if( depth == CV_32F ) + { + projectionFromKRt(K, R, t, P); + } + else + { + projectionFromKRt(K, R, t, P); + } + +} + +template +void +KRtFromProjection( const Mat_ &_P, Mat_ _K, Mat_ _R, Mat_ _t ) +{ + libmv::Mat34 P; + libmv::Mat3 K, R; + libmv::Vec3 t; + + cv2eigen( _P, P ); + + libmv::KRt_From_P( P, &K, &R, &t ); + + eigen2cv( K, _K ); + eigen2cv( R, _R ); + eigen2cv( t, _t ); +} + +void +KRtFromProjection( InputArray _P, OutputArray _K, OutputArray _R, OutputArray _t ) +{ + const Mat P = _P.getMat(); + const int depth = P.depth(); + CV_Assert((P.cols == 4 && P.rows == 3) && (depth == CV_32F || depth == CV_64F)); + + _K.create(3, 3, depth); + _R.create(3, 3, depth); + _t.create(3, 1, depth); + + Mat K = _K.getMat(), R = _R.getMat(), t = _t.getMat(); + + // type + if( depth == CV_32F ) + { + KRtFromProjection(P, K, R, t); + } + else + { + KRtFromProjection(P, K, R, t); + } +} + +template +T +depthValue( const Mat_ &_R, const Mat_ &_t, const Mat_ &_X ) +{ + Matx R(_R); + Vec t(_t); + + if ( _X.rows == 3) + { + Vec X(_X); + return (R*X)(2) + t(2); + } + else + { + Vec X(_X); + Vec Xe; + homogeneousToEuclidean(X,Xe); + return depthValue( Mat(R), Mat(t), Mat(Xe) ); + } +} + +double +depth( InputArray _R, InputArray _t, InputArray _X) +{ + const Mat R = _R.getMat(), t = _t.getMat(), X = _X.getMat(); + const int depth = R.depth(); + CV_Assert( R.rows == 3 && R.cols == 3 && t.rows == 3 && t.cols == 1 ); + CV_Assert( (X.rows == 3 && X.cols == 1) || (X.rows == 4 && X.cols == 1) ); + CV_Assert( depth == CV_32F || depth == CV_64F ); + + double depth_value = 0.0; + + if ( depth == CV_32F ) + { + depth_value = static_cast(depthValue(R, t, X)); + } + else + { + depth_value = depthValue(R, t, X); + } + + return depth_value; +} + +} /* namespace sfm */ +} /* namespace cv */ diff --git a/modules/sfm/src/reconstruct.cpp b/modules/sfm/src/reconstruct.cpp new file mode 100644 index 00000000000..1d40f23491b --- /dev/null +++ b/modules/sfm/src/reconstruct.cpp @@ -0,0 +1,258 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "precomp.hpp" + +#if CERES_FOUND + +// Eigen +#include + +// OpenCV +#include + +#include + +using namespace cv; +using namespace cv::sfm; +using namespace std; + +namespace cv +{ +namespace sfm +{ + + template + void + reconstruct_(const T &input, OutputArray Rs, OutputArray Ts, InputOutputArray K, OutputArray points3d, const bool refinement=true) + { + // Initial reconstruction + const int keyframe1 = 1, keyframe2 = 2; + const int select_keyframes = 1; // enable automatic keyframes selection + const int verbosity_level = -1; // mute libmv logs + + // Refinement parameters + const int refine_intrinsics = ( !refinement ) ? 0 : + SFM_REFINE_FOCAL_LENGTH | SFM_REFINE_PRINCIPAL_POINT | SFM_REFINE_RADIAL_DISTORTION_K1 | SFM_REFINE_RADIAL_DISTORTION_K2; + + // Camera data + Matx33d Ka = K.getMat(); + const double focal_length = Ka(0,0); + const double principal_x = Ka(0,2), principal_y = Ka(1,2), k1 = 0, k2 = 0, k3 = 0; + + // Set reconstruction options + libmv_ReconstructionOptions reconstruction_options(keyframe1, keyframe2, refine_intrinsics, select_keyframes, verbosity_level); + + libmv_CameraIntrinsicsOptions camera_instrinsic_options = + libmv_CameraIntrinsicsOptions(SFM_DISTORTION_MODEL_POLYNOMIAL, + focal_length, principal_x, principal_y, + k1, k2, k3); + + //-- Instantiate reconstruction pipeline + Ptr reconstruction = + SFMLibmvEuclideanReconstruction::create(camera_instrinsic_options, reconstruction_options); + + //-- Run reconstruction pipeline + reconstruction->run(input, K, Rs, Ts, points3d); + + } + + + // Reconstruction function for API + void + reconstruct(InputArrayOfArrays points2d, OutputArray Ps, OutputArray points3d, InputOutputArray K, + bool is_projective) + { + const int nviews = points2d.total(); + CV_Assert( nviews >= 2 ); + + // OpenCV data types + std::vector pts2d; + points2d.getMatVector(pts2d); + const int depth = pts2d[0].depth(); + + Matx33d Ka = K.getMat(); + + // Projective reconstruction + + if (is_projective) + { + + if ( nviews == 2 ) + { + // Get Projection matrices + Matx33d F; + Matx34d P, Pp; + + normalizedEightPointSolver(pts2d[0], pts2d[1], F); + projectionsFromFundamental(F, P, Pp); + Ps.create(2, 1, depth); + Mat(P).copyTo(Ps.getMatRef(0)); + Mat(Pp).copyTo(Ps.getMatRef(1)); + + // Triangulate and find 3D points using inliers + triangulatePoints(points2d, Ps, points3d); + } + else + { + std::vector Rs, Ts; + reconstruct(points2d, Rs, Ts, Ka, points3d, is_projective); + + // From Rs and Ts, extract Ps + const int nviews = Rs.size(); + Ps.create(nviews, 1, depth); + + Matx34d P; + for (size_t i = 0; i < nviews; ++i) + { + projectionFromKRt(Ka, Rs[i], Vec3d(Ts[i]), P); + Mat(P).copyTo(Ps.getMatRef(i)); + } + + Mat(Ka).copyTo(K.getMat()); + } + + } + + + // Affine reconstruction + + else + { + // TODO: implement me + } + + } + + + void + reconstruct(InputArrayOfArrays points2d, OutputArray Rs, OutputArray Ts, InputOutputArray K, + OutputArray points3d, bool is_projective) + { + const int nviews = points2d.total(); + CV_Assert( nviews >= 2 ); + + + // Projective reconstruction + + if (is_projective) + { + + // calls simple pipeline + reconstruct_(points2d, Rs, Ts, K, points3d); + + } + + // Affine reconstruction + + else + { + // TODO: implement me + } + + } + + + void + reconstruct(const std::vector images, OutputArray Ps, OutputArray points3d, + InputOutputArray K, bool is_projective) + { + const int nviews = static_cast(images.size()); + CV_Assert( nviews >= 2 ); + + Matx33d Ka = K.getMat(); + const int depth = Mat(Ka).depth(); + + // Projective reconstruction + + if ( is_projective ) + { + std::vector Rs, Ts; + reconstruct(images, Rs, Ts, Ka, points3d, is_projective); + + // From Rs and Ts, extract Ps + + const int nviews_est = Rs.size(); + Ps.create(nviews_est, 1, depth); + + Matx34d P; + for (size_t i = 0; i < nviews_est; ++i) + { + projectionFromKRt(Ka, Rs[i], Vec3d(Ts[i]), P); + Mat(P).copyTo(Ps.getMatRef(i)); + } + + Mat(Ka).copyTo(K.getMat()); + } + + + // Affine reconstruction + + else + { + // TODO: implement me + } + + } + + + void + reconstruct(const std::vector images, OutputArray Rs, OutputArray Ts, + InputOutputArray K, OutputArray points3d, bool is_projective) + { + const int nviews = static_cast(images.size()); + CV_Assert( nviews >= 2 ); + + // Projective reconstruction + + if ( is_projective ) + { + reconstruct_(images, Rs, Ts, K, points3d, false); + } + + + // Affine reconstruction + + else + { + // TODO: implement me + } + + } + +} // namespace sfm +} // namespace cv + +#endif /* HAVE_CERES */ \ No newline at end of file diff --git a/modules/sfm/src/robust.cpp b/modules/sfm/src/robust.cpp new file mode 100644 index 00000000000..5116485bae0 --- /dev/null +++ b/modules/sfm/src/robust.cpp @@ -0,0 +1,195 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "precomp.hpp" + +// Eigen +#include + +// OpenCV +#include +#include +#include + +// libmv headers +#include "libmv/multiview/robust_fundamental.h" + +using namespace std; + +namespace cv +{ +namespace sfm +{ + +// TODO: unify algorithms +template +double +fundamentalFromCorrespondences8PointRobust( const Mat_ &_x1, + const Mat_ &_x2, + const double max_error, + Mat_ _F, + std::vector &_inliers, + const double outliers_probability ) +{ + libmv::Mat x1, x2; + libmv::Mat3 F; + libmv::vector inliers; + + cv2eigen( _x1, x1 ); + cv2eigen( _x2, x2 ); + + T solution_error = + libmv::FundamentalFromCorrespondences8PointRobust( x1, x2, max_error, &F, &inliers, outliers_probability ); + + eigen2cv( F, _F ); + + // transform from libmv::vector to std::vector + int n = inliers.size(); + _inliers.resize(n); + for( int i=0; i < n; ++i ) + { + _inliers[i] = inliers.at(i); + } + + return static_cast(solution_error); +} + + +double +fundamentalFromCorrespondences8PointRobust( InputArray _x1, + InputArray _x2, + double max_error, + OutputArray _F, + OutputArray _inliers, + double outliers_probability ) +{ + const Mat x1 = _x1.getMat(), x2 = _x2.getMat(); + const int depth = x1.depth(); + CV_Assert(x1.size() == x2.size() && (depth == CV_32F || depth == CV_64F)); + + _F.create(3, 3, depth); + + Mat F = _F.getMat(); + std::vector& inliers = *(std::vector*)_inliers.getObj(); + + double solution_error = 0.0; + + // type + if( depth == CV_32F ) + { + solution_error = + fundamentalFromCorrespondences8PointRobust( + x1, x2, max_error, F, inliers, outliers_probability); + } + else + { + solution_error = + fundamentalFromCorrespondences8PointRobust( + x1, x2, max_error, F, inliers, outliers_probability); + } + + return solution_error; +} + +template +double +fundamentalFromCorrespondences7PointRobust( const Mat_ &_x1, + const Mat_ &_x2, + const double max_error, + Mat_ _F, + std::vector &_inliers, + const double outliers_probability ) +{ + libmv::Mat x1, x2; + libmv::Mat3 F; + libmv::vector inliers; + + cv2eigen( _x1, x1 ); + cv2eigen( _x2, x2 ); + + T solution_error = + libmv::FundamentalFromCorrespondences7PointRobust( x1, x2, max_error, &F, &inliers, outliers_probability ); + + eigen2cv( F, _F ); + + // transform from libmv::vector to std::vector + int n = inliers.size(); + _inliers.resize(n); + for( int i=0; i < n; ++i ) + { + _inliers[i] = inliers.at(i); + } + + return static_cast(solution_error); +} + +double +fundamentalFromCorrespondences7PointRobust( InputArray _x1, + InputArray _x2, + double max_error, + OutputArray _F, + OutputArray _inliers, + double outliers_probability ) +{ + const Mat x1 = _x1.getMat(), x2 = _x2.getMat(); + const int depth = x1.depth(); + CV_Assert(x1.size() == x2.size() && (depth == CV_32F || depth == CV_64F)); + + _F.create(3, 3, depth); + + Mat F = _F.getMat(); + std::vector& inliers = *(std::vector*)_inliers.getObj(); + + double solution_error = 0.0; + + // type + if( depth == CV_32F ) + { + solution_error = + fundamentalFromCorrespondences7PointRobust( + x1, x2, max_error, F, inliers, outliers_probability); + } + else + { + solution_error = + fundamentalFromCorrespondences7PointRobust( + x1, x2, max_error, F, inliers, outliers_probability); + } + + return solution_error; +} + +} /* namespace sfm */ +} /* namespace cv */ \ No newline at end of file diff --git a/modules/sfm/src/simple_pipeline.cpp b/modules/sfm/src/simple_pipeline.cpp new file mode 100644 index 00000000000..06940578261 --- /dev/null +++ b/modules/sfm/src/simple_pipeline.cpp @@ -0,0 +1,319 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "precomp.hpp" + +#if CERES_FOUND + +#include +#include + +#include "libmv_capi.h" + +using namespace std; + +namespace cv +{ +namespace sfm +{ + +/* Parses a given array of 2d points into the libmv tracks structure + */ + +void +parser_2D_tracks( const std::vector &points2d, libmv::Tracks &tracks ) +{ + const int nframes = static_cast(points2d.size()); + for (int frame = 0; frame < nframes; ++frame) { + const int ntracks = points2d[frame].cols; + for (int track = 0; track < ntracks; ++track) { + const Vec2d track_pt = points2d[frame].col(track); + if ( track_pt[0] > 0 && track_pt[1] > 0 ) + tracks.Insert(frame, track, track_pt[0], track_pt[1]); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +/* Parses a given set of matches into the libmv tracks structure + */ + +void +parser_2D_tracks( const libmv::Matches &matches, libmv::Tracks &tracks ) +{ + std::set::const_iterator iter_image = + matches.get_images().begin(); + + bool is_first_time = true; + + for (; iter_image != matches.get_images().end(); ++iter_image) { + // Exports points + Matches::Features pfeatures = + matches.InImage(*iter_image); + + while(pfeatures) { + + double x = pfeatures.feature()->x(), + y = pfeatures.feature()->y(); + + // valid marker + if ( x > 0 && y > 0 ) + { + tracks.Insert(*iter_image, pfeatures.track(), x, y); + + if ( is_first_time ) + is_first_time = false; + } + + // lost track + else if ( x < 0 && y < 0 ) + { + is_first_time = true; + } + + pfeatures.operator++(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +/* Computes the 2d features matches between a given set of images and call the + * reconstruction pipeline. + */ + +libmv_Reconstruction *libmv_solveReconstructionImpl( + const std::vector &images, + const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options, + libmv_ReconstructionOptions* libmv_reconstruction_options) +{ + Ptr edetector = ORB::create(10000); + Ptr edescriber = xfeatures2d::DAISY::create(); + //Ptr edescriber = xfeatures2d::LATCH::create(64, true, 4); + + cout << "Initialize nViewMatcher ... "; + libmv::correspondence::nRobustViewMatching nViewMatcher(edetector, edescriber); + + cout << "OK" << endl << "Performing Cross Matching ... "; + nViewMatcher.computeCrossMatch(images); cout << "OK" << endl; + + // Building tracks + libmv::Tracks tracks; + libmv::Matches matches = nViewMatcher.getMatches(); + parser_2D_tracks( matches, tracks ); + + // Perform reconstruction + return libmv_solveReconstruction(tracks, + libmv_camera_intrinsics_options, + libmv_reconstruction_options); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +template +class SFMLibmvReconstructionImpl : public T +{ +public: + SFMLibmvReconstructionImpl(const libmv_CameraIntrinsicsOptions &camera_instrinsic_options, + const libmv_ReconstructionOptions &reconstruction_options) : + libmv_reconstruction_options_(reconstruction_options), + libmv_camera_intrinsics_options_(camera_instrinsic_options) {} + + /* Run the pipeline given 2d points + */ + + virtual void run(InputArrayOfArrays _points2d) + { + std::vector points2d; + _points2d.getMatVector(points2d); + CV_Assert( _points2d.total() >= 2 ); + + // Parse 2d points to Tracks + Tracks tracks; + parser_2D_tracks(points2d, tracks); + + // Set libmv logs level + libmv_initLogging(""); + + if (libmv_reconstruction_options_.verbosity_level >= 0) + { + libmv_startDebugLogging(); + libmv_setLoggingVerbosity( + libmv_reconstruction_options_.verbosity_level); + } + + // Perform reconstruction + libmv_reconstruction_ = + *libmv_solveReconstruction(tracks, + &libmv_camera_intrinsics_options_, + &libmv_reconstruction_options_); + } + + virtual void run(InputArrayOfArrays points2d, InputOutputArray K, OutputArray Rs, + OutputArray Ts, OutputArray points3d) + { + // Run the pipeline + run(points2d); + + // Extract Data + extractLibmvReconstructionData(K, Rs, Ts, points3d); + } + + + /* Run the pipeline given a set of images + */ + + virtual void run(const std::vector &images) + { + // Set libmv logs level + libmv_initLogging(""); + + if (libmv_reconstruction_options_.verbosity_level >= 0) + { + libmv_startDebugLogging(); + libmv_setLoggingVerbosity( + libmv_reconstruction_options_.verbosity_level); + } + + // Perform reconstruction + + libmv_reconstruction_ = + *libmv_solveReconstructionImpl(images, + &libmv_camera_intrinsics_options_, + &libmv_reconstruction_options_); + } + + + virtual void run(const std::vector &images, InputOutputArray K, OutputArray Rs, + OutputArray Ts, OutputArray points3d) + { + // Run the pipeline + run(images); + + // Extract Data + extractLibmvReconstructionData(K, Rs, Ts, points3d); + } + + virtual double getError() const { return libmv_reconstruction_.error; } + + virtual void + getPoints(OutputArray points3d) { + const size_t n_points = + libmv_reconstruction_.reconstruction.AllPoints().size(); + + points3d.create(n_points, 1, CV_64F); + + Vec3d point3d; + for ( size_t i = 0; i < n_points; ++i ) + { + for ( int j = 0; j < 3; ++j ) + point3d[j] = + libmv_reconstruction_.reconstruction.AllPoints()[i].X[j]; + Mat(point3d).copyTo(points3d.getMatRef(i)); + } + + } + + virtual cv::Mat getIntrinsics() const { + Mat K; + eigen2cv(libmv_reconstruction_.intrinsics->K(), K); + return K; + } + + virtual void + getCameras(OutputArray Rs, OutputArray Ts) { + const size_t n_views = + libmv_reconstruction_.reconstruction.AllCameras().size(); + + Rs.create(n_views, 1, CV_64F); + Ts.create(n_views, 1, CV_64F); + + Matx33d R; + Vec3d t; + for(size_t i = 0; i < n_views; ++i) + { + eigen2cv(libmv_reconstruction_.reconstruction.AllCameras()[i].R, R); + eigen2cv(libmv_reconstruction_.reconstruction.AllCameras()[i].t, t); + Mat(R).copyTo(Rs.getMatRef(i)); + Mat(t).copyTo(Ts.getMatRef(i)); + } + } + + virtual void setReconstructionOptions( + const libmv_ReconstructionOptions &libmv_reconstruction_options) { + libmv_reconstruction_options_ = libmv_reconstruction_options; + } + + virtual void setCameraIntrinsicOptions( + const libmv_CameraIntrinsicsOptions &libmv_camera_intrinsics_options) { + libmv_camera_intrinsics_options_ = libmv_camera_intrinsics_options; + } + +private: + + void + extractLibmvReconstructionData(InputOutputArray K, + OutputArray Rs, + OutputArray Ts, + OutputArray points3d) + { + getCameras(Rs, Ts); + getPoints(points3d); + getIntrinsics().copyTo(K.getMat()); + } + + libmv_Reconstruction libmv_reconstruction_; + libmv_ReconstructionOptions libmv_reconstruction_options_; + libmv_CameraIntrinsicsOptions libmv_camera_intrinsics_options_; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////// + +Ptr +SFMLibmvEuclideanReconstruction::create(const libmv_CameraIntrinsicsOptions &camera_instrinsic_options, + const libmv_ReconstructionOptions &reconstruction_options) +{ + return makePtr >(camera_instrinsic_options,reconstruction_options); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +} /* namespace cv */ +} /* namespace sfm */ + +#endif + +/* End of file. */ \ No newline at end of file diff --git a/modules/sfm/src/triangulation.cpp b/modules/sfm/src/triangulation.cpp new file mode 100644 index 00000000000..a77aa751898 --- /dev/null +++ b/modules/sfm/src/triangulation.cpp @@ -0,0 +1,196 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "precomp.hpp" + +// Eigen +#include + +// OpenCV +#include +#include +#include + +// libmv headers +#include "libmv/multiview/twoviewtriangulation.h" +#include "libmv/multiview/fundamental.h" + +using namespace cv; +using namespace std; + +namespace cv +{ +namespace sfm +{ + +/** @brief Triangulates the a 3d position between two 2d correspondences, using the DLT. + @param xl Input vector with first 2d point. + @param xr Input vector with second 2d point. + @param Pl Input 3x4 first projection matrix. + @param Pr Input 3x4 second projection matrix. + @param objectPoint Output vector with computed 3d point. + + Reference: @cite HartleyZ00 12.2 pag.312 + */ +void +triangulateDLT( const Vec2d &xl, const Vec2d &xr, + const Matx34d &Pl, const Matx34d &Pr, + Vec3d &point3d ) +{ + Matx44d design; + for (int i = 0; i < 4; ++i) + { + design(0,i) = xl(0) * Pl(2,i) - Pl(0,i); + design(1,i) = xl(1) * Pl(2,i) - Pl(1,i); + design(2,i) = xr(0) * Pr(2,i) - Pr(0,i); + design(3,i) = xr(1) * Pr(2,i) - Pr(1,i); + } + + Vec4d XHomogeneous; + cv::SVD::solveZ(design, XHomogeneous); + + homogeneousToEuclidean(XHomogeneous, point3d); +} + + +/** @brief Triangulates the 3d position of 2d correspondences between n images, using the DLT + * @param x Input vectors of 2d points (the inner vector is per image). Has to be 2xN + * @param Ps Input vector with 3x4 projections matrices of each image. + * @param X Output vector with computed 3d point. + + * Reference: it is the standard DLT; for derivation see appendix of Keir's thesis + */ +void +triangulateNViews(const Mat_ &x, const std::vector &Ps, Vec3d &X) +{ + CV_Assert(x.rows == 2); + unsigned nviews = x.cols; + CV_Assert(nviews == Ps.size()); + + cv::Mat_ design = cv::Mat_::zeros(3*nviews, 4 + nviews); + for (unsigned i=0; i < nviews; ++i) { + for(char jj=0; jj<3; ++jj) + for(char ii=0; ii<4; ++ii) + design(3*i+jj, ii) = -Ps[i](jj, ii); + design(3*i + 0, 4 + i) = x(0, i); + design(3*i + 1, 4 + i) = x(1, i); + design(3*i + 2, 4 + i) = 1.0; + } + + Mat X_and_alphas; + cv::SVD::solveZ(design, X_and_alphas); + homogeneousToEuclidean(X_and_alphas.rowRange(0, 4), X); +} + + +void +triangulatePoints(InputArrayOfArrays _points2d, InputArrayOfArrays _projection_matrices, + OutputArray _points3d) +{ + // check + size_t nviews = (unsigned) _points2d.total(); + CV_Assert(nviews >= 2 && nviews == _projection_matrices.total()); + + // inputs + size_t n_points; + std::vector > points2d(nviews); + std::vector projection_matrices(nviews); + { + std::vector points2d_tmp; + _points2d.getMatVector(points2d_tmp); + n_points = points2d_tmp[0].cols; + + std::vector projection_matrices_tmp; + _projection_matrices.getMatVector(projection_matrices_tmp); + + // Make sure the dimensions are right + for(size_t i=0; i &xl = points2d[0], &xr = points2d[1]; + + const Matx34d & Pl = projection_matrices[0]; // left matrix projection + const Matx34d & Pr = projection_matrices[1]; // right matrix projection + + // triangulate + for( unsigned i = 0; i < n_points; ++i ) + { + Vec3d point3d; + triangulateDLT( Vec2d(xl(0,i), xl(1,i)), Vec2d(xr(0,i), xr(1,i)), Pl, Pr, point3d ); + for(char j=0; j<3; ++j) + points3d.at(j, i) = point3d[j]; + } + } + else if( nviews > 2 ) + { + // triangulate + for( unsigned i=0; i < n_points; ++i ) + { + // build x matrix (one point per view) + Mat_ x( 2, nviews ); + for( unsigned k=0; k < nviews; ++k ) + { + points2d.at(k).col(i).copyTo( x.col(k) ); + } + + Vec3d point3d; + triangulateNViews( x, projection_matrices, point3d ); + for(char j=0; j<3; ++j) + points3d.at(j, i) = point3d[j]; + } + } +} + +} /* namespace sfm */ +} /* namespace cv */ \ No newline at end of file diff --git a/modules/sfm/test/scene.cpp b/modules/sfm/test/scene.cpp new file mode 100644 index 00000000000..922d32260a7 --- /dev/null +++ b/modules/sfm/test/scene.cpp @@ -0,0 +1,122 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include "test_precomp.hpp" + +cv::Matx33d +randomK(bool is_projective) +{ + static cv::RNG rng; + + cv::Matx33d K = cv::Matx33d::zeros(); + K(0, 0) = rng.uniform(100, 1000); + K(1, 1) = rng.uniform(100, 1000); + if (is_projective) + { + K(0, 2) = rng.uniform(-100, 100); + K(1, 2) = rng.uniform(-100, 100); + } + K(2, 2) = 1.0; + + return K; +} + +void +generateScene(size_t n_views, size_t n_points, bool is_projective, cv::Matx33d & K, std::vector & R, + std::vector & t, std::vector & P, cv::Mat_ & points3d, + std::vector > & points2d) +{ + R.resize(n_views); + t.resize(n_views); + + cv::RNG rng; + + // Generate a bunch of random 3d points in a 0, 1 cube + points3d.create(3, n_points); + rng.fill(points3d, cv::RNG::UNIFORM, 0, 1); + + // Generate random intrinsics + K = randomK(is_projective); + + // Generate random camera poses + // TODO deal with smooth camera poses (e.g. from a video sequence) + for (size_t i = 0; i < n_views; ++i) + { + // Get a random rotation axis + cv::Vec3d vec; + rng.fill(vec, cv::RNG::UNIFORM, 0, 1); + // Give a random angle to the rotation vector + vec = vec / cv::norm(vec) * rng.uniform(0.0f, float(2 * CV_PI)); + cv::Rodrigues(vec, R[i]); + // Create a random translation + t[i] = cv::Vec3d(rng.uniform(-0.5f, 0.5f), rng.uniform(-0.5f, 0.5f), rng.uniform(1.0f, 2.0f)); + // Make sure the shape is in front of the camera + cv::Mat_ points3d_transformed = cv::Mat(R[i]) * points3d + cv::Mat(t[i]) * cv::Mat_ ::ones(1, n_points); + double min_dist, max_dist; + cv::minMaxIdx(points3d_transformed.row(2), &min_dist, &max_dist); + if (min_dist < 0) + t[i][2] = t[i][2] - min_dist + 1.0; + } + + // Compute projection matrices + P.resize(n_views); + for (size_t i = 0; i < n_views; ++i) + { + cv::Matx33d K3 = K, R3 = R[i]; + cv::Vec3d t3 = t[i]; + cv::sfm::projectionFromKRt(K3, R3, t3, P[i]); + } + + // Compute homogeneous 3d points + cv::Mat_ points3d_homogeneous(4, n_points); + points3d.copyTo(points3d_homogeneous.rowRange(0, 3)); + points3d_homogeneous.row(3).setTo(1); + // Project those points for every view + points2d.resize(n_views); + for (size_t i = 0; i < n_views; ++i) + { + cv::Mat_ points2d_tmp = cv::Mat(P[i]) * points3d_homogeneous; + points2d[i].create(2, n_points); + for (unsigned char j = 0; j < 2; ++j) + cv::Mat(points2d_tmp.row(j) / points2d_tmp.row(2)).copyTo(points2d[i].row(j)); + } + +// TODO: remove a certain number of points per view +// TODO: add a certain number of outliers per view + +} diff --git a/modules/sfm/test/scene.h b/modules/sfm/test/scene.h new file mode 100644 index 00000000000..239f34e4c54 --- /dev/null +++ b/modules/sfm/test/scene.h @@ -0,0 +1,41 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +void +generateScene(size_t n_views, size_t n_points, bool is_projective, cv::Matx33d & K, std::vector & R, + std::vector & t, std::vector & P, cv::Mat_ & points3d, + std::vector > & points2d); diff --git a/modules/sfm/test/test_common.cpp b/modules/sfm/test/test_common.cpp new file mode 100644 index 00000000000..19e91051d4b --- /dev/null +++ b/modules/sfm/test/test_common.cpp @@ -0,0 +1,132 @@ +#include "test_precomp.hpp" + +#include +#include + +using namespace cv; +using namespace cv::sfm; +using namespace std; + +namespace cvtest +{ + +void generateTwoViewRandomScene( cvtest::TwoViewDataSet &data ) +{ + vector > points2d; + vector Rs; + vector ts; + vector Ps; + Matx33d K; + Mat_ points3d; + + int nviews = 2; + int npoints = 30; + bool is_projective = true; + + generateScene(nviews, npoints, is_projective, K, Rs, ts, Ps, points3d, points2d); + + // Internal parameters (same K) + data.K1 = K; + data.K2 = K; + + // Rotation + data.R1 = Rs[0]; + data.R2 = Rs[1]; + + // Translation + data.t1 = ts[0]; + data.t2 = ts[1]; + + // Projection matrix, P = K(R|t) + data.P1 = Ps[0]; + data.P2 = Ps[1]; + + // Fundamental matrix + fundamentalFromProjections( data.P1, data.P2, data.F ); + + // 3D points + data.X = points3d; + + // Projected points + data.x1 = points2d[0]; + data.x2 = points2d[1]; +} + +/** Check the properties of a fundamental matrix: +* +* 1. The determinant is 0 (rank deficient) +* 2. The condition x'T*F*x = 0 is satisfied to precision. +*/ +void +expectFundamentalProperties( const cv::Matx33d &F, + const cv::Mat_ &ptsA, + const cv::Mat_ &ptsB, + double precision ) +{ + EXPECT_NEAR( 0, determinant(F), precision ); + + int n = ptsA.cols; + EXPECT_EQ( n, ptsB.cols ); + + cv::Mat_ x1, x2; + euclideanToHomogeneous( ptsA, x1 ); + euclideanToHomogeneous( ptsB, x2 ); + + for( int i = 0; i < n; ++i ) + { + double residual = Vec3d(x2(0,i),x2(1,i),x2(2,i)).ddot( F * Vec3d(x1(0,i),x1(1,i),x1(2,i)) ); + EXPECT_NEAR( 0.0, residual, precision ); + } +} + + +void +parser_2D_tracks(const string &_filename, std::vector &points2d ) +{ + ifstream myfile(_filename.c_str()); + + if (!myfile.is_open()) + { + cout << "Unable to read file: " << _filename << endl; + exit(0); + + } else { + + double x, y; + string line_str; + Mat nan_mat = Mat(2, 1 , CV_64F, -1); + int n_frames = 0, n_tracks = 0, track = 0; + + while ( getline(myfile, line_str) ) + { + istringstream line(line_str); + + if ( track > n_tracks ) + { + n_tracks = track; + + for (int i = 0; i < n_frames; ++i) + cv::hconcat(points2d[i], nan_mat, points2d[i]); + } + + for (int frame = 1; line >> x >> y; ++frame) + { + if ( frame > n_frames ) + { + n_frames = frame; + points2d.push_back(nan_mat); + } + + points2d[frame-1].at(0,track) = x; + points2d[frame-1].at(1,track) = y; + } + + ++track; + } + + myfile.close(); + } + +} + +} // namespace cvtest diff --git a/modules/sfm/test/test_conditioning.cpp b/modules/sfm/test/test_conditioning.cpp new file mode 100644 index 00000000000..5e5194dc9ec --- /dev/null +++ b/modules/sfm/test/test_conditioning.cpp @@ -0,0 +1,64 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace cv::sfm; +using namespace std; + +TEST(Sfm_conditioning, normalizePoints) +{ + int n = 4; + Mat_ points(2, n); + points << 0, 0, 1, 1, + 0, 2, 1, 3; + + Mat_ T, normalized_points; + normalizePoints( points, normalized_points, T ); + + Mat_ mean, variance; + meanAndVarianceAlongRows(normalized_points, mean, variance); + + EXPECT_NEAR(0, mean(0), 1e-8); + EXPECT_NEAR(0, mean(1), 1e-8); + EXPECT_NEAR(2, variance(0), 1e-8); + EXPECT_NEAR(2, variance(1), 1e-8); +} + +TEST(Sfm_conditioning, normalizeIsotropicPoints) +{ + //TODO: implement me +} diff --git a/modules/sfm/test/test_fundamental.cpp b/modules/sfm/test/test_fundamental.cpp new file mode 100644 index 00000000000..02f28981ecc --- /dev/null +++ b/modules/sfm/test/test_fundamental.cpp @@ -0,0 +1,176 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace cv::sfm; +using namespace cvtest; +using namespace std; + +TEST(Sfm_fundamental, fundamentalFromProjections) +{ + double tolerance_prop = 1e-7; + double tolerance_near = 1e-15; + + Matx34d P1_gt, P2_gt; + P1_gt << 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0; + P2_gt << 1, 1, 1, 3, + 0, 2, 0, 3, + 0, 1, 1, 0; + + Matx33d F_gt; + fundamentalFromProjections(P1_gt, P2_gt, F_gt); + + Matx34d P1, P2; + projectionsFromFundamental(F_gt, P1, P2); + + Matx33d F; + fundamentalFromProjections(P1, P2, F); + + Matx33d F_gt_norm, F_norm; + normalizeFundamental(F_gt, F_gt_norm); + normalizeFundamental(F, F_norm); + + EXPECT_MATRIX_PROP(F_gt, F, tolerance_prop); + EXPECT_MATRIX_NEAR(F_gt_norm, F_norm, tolerance_near); +} + + +TEST(Sfm_fundamental, normalizedEightPointSolver) +{ + double tolerance = 1e-14; + + TwoViewDataSet d; + generateTwoViewRandomScene( d ); + + Matx33d F; + normalizedEightPointSolver( d.x1, d.x2, F ); + expectFundamentalProperties( F, d.x1, d.x2, tolerance ); +} + + +TEST(Sfm_fundamental, motionFromEssential) +{ + double tolerance = 1e-8; + + cvtest::TwoViewDataSet d; + generateTwoViewRandomScene(d); + + Matx33d E; + essentialFromRt(d.R1, d.t1, d.R2, d.t2, E); + + Matx33d R; + cv::Vec3d t; + relativeCameraMotion(d.R1, d.t1, d.R2, d.t2, R, t); + cv::normalize(t, t); + + std::vector Rs; + std::vector ts; + motionFromEssential(E, Rs, ts); + bool one_solution_is_correct = false; + for ( int i = 0; i < Rs.size(); ++i ) + { + if ( (norm(Rs[i], R) < tolerance) && (norm(ts[i], t) < tolerance) ) + { + one_solution_is_correct = true; + break; + } + } + EXPECT_TRUE(one_solution_is_correct); +} + + +TEST(Sfm_fundamental, fundamentalToAndFromEssential) +{ + double tolerance = 1e-15; + TwoViewDataSet d; + generateTwoViewRandomScene(d); + + Matx33d F, E; + essentialFromFundamental(d.F, d.K1, d.K2, E); + fundamentalFromEssential(E, d.K1, d.K2, F); + + Matx33d F_gt_norm, F_norm; + normalizeFundamental(d.F, F_gt_norm); + normalizeFundamental(F, F_norm); + + EXPECT_MATRIX_NEAR(F_gt_norm, F_norm, tolerance); +} + + +TEST(Sfm_fundamental, essentialFromFundamental) +{ + TwoViewDataSet d; + generateTwoViewRandomScene(d); + + Matx33d E_from_Rt; + essentialFromRt(d.R1, d.t1, d.R2, d.t2, E_from_Rt); + + Matx33d E_from_F; + essentialFromFundamental(d.F, d.K1, d.K2, E_from_F); + + EXPECT_MATRIX_PROP(E_from_Rt, E_from_F, 1e-6); +} + + +TEST(Sfm_fundamental, motionFromEssentialChooseSolution) +{ + TwoViewDataSet d; + generateTwoViewRandomScene(d); + + Matx33d E; + essentialFromRt(d.R1, d.t1, d.R2, d.t2, E); + + Matx33d R; + cv::Vec3d t; + relativeCameraMotion(d.R1, d.t1, d.R2, d.t2, R, t); + normalize(t, t); + + std::vector < Mat > Rs; + std::vector < cv::Mat > ts; + motionFromEssential(E, Rs, ts); + + cv::Vec2d x1(d.x1(0, 0), d.x1(1, 0)); + cv::Vec2d x2(d.x2(0, 0), d.x2(1, 0)); + int solution = motionFromEssentialChooseSolution(Rs, ts, d.K1, x1, d.K2, x2); + + EXPECT_LE(0, solution); + EXPECT_LE(solution, 3); + EXPECT_LE(norm(Rs[solution]-Mat(R)), 1e-8); + EXPECT_LE(norm(ts[solution]-Mat(t)), 1e-8); +} \ No newline at end of file diff --git a/modules/sfm/test/test_main.cpp b/modules/sfm/test/test_main.cpp new file mode 100644 index 00000000000..74b896b3d1c --- /dev/null +++ b/modules/sfm/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") \ No newline at end of file diff --git a/modules/sfm/test/test_numeric.cpp b/modules/sfm/test/test_numeric.cpp new file mode 100644 index 00000000000..dc28e6d65a0 --- /dev/null +++ b/modules/sfm/test/test_numeric.cpp @@ -0,0 +1,91 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace cv::sfm; +using namespace std; + + +template +static void +test_meanAndVarianceAlongRows( void ) +{ + int n = 4; + Mat_ points(2,n); + points << 0, 0, 1, 1, + 0, 2, 1, 3; + + Mat_ mean, variance; + meanAndVarianceAlongRows(points, mean, variance); + + EXPECT_NEAR(0.5, mean(0), 1e-8); + EXPECT_NEAR(1.5, mean(1), 1e-8); + EXPECT_NEAR(0.25, variance(0), 1e-8); + EXPECT_NEAR(1.25, variance(1), 1e-8); +} + +TEST(Sfm_numeric, meanAndVarianceAlongRows) +{ + test_meanAndVarianceAlongRows(); + test_meanAndVarianceAlongRows(); +} + + +TEST(Sfm_numeric, skewMat) +{ + // Testing with floats + Vec3f a; + a << 1,2,3; + + Matx33f ax = skew(a); + + EXPECT_FLOAT_EQ( 0, trace(ax) ); + EXPECT_FLOAT_EQ( ax(0,1), -ax(1,0) ); + EXPECT_FLOAT_EQ( ax(0,2), -ax(2,0) ); + EXPECT_FLOAT_EQ( ax(1,2), -ax(2,1) ); + + // Testing with doubles + Vec3d b; + b << 1,2,3; + + Matx33d bx = skew(b); + + EXPECT_DOUBLE_EQ( 0, trace(bx) ); + EXPECT_DOUBLE_EQ( bx(0,1), -bx(1,0) ); + EXPECT_DOUBLE_EQ( bx(0,2), -bx(2,0) ); + EXPECT_DOUBLE_EQ( bx(1,2), -bx(2,1) ); +} diff --git a/modules/sfm/test/test_precomp.hpp b/modules/sfm/test/test_precomp.hpp new file mode 100644 index 00000000000..6002f30ab7f --- /dev/null +++ b/modules/sfm/test/test_precomp.hpp @@ -0,0 +1,140 @@ +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +#endif + +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include +#include +#include +#include + +#include "scene.h" + +#define OPEN_TESTFILE(FNAME,FS) \ + FS.open(FNAME, FileStorage::READ); \ + if (!FS.isOpened())\ + {\ + std::cerr << "Cannot find file: " << FNAME << std::endl;\ + return;\ + } + +namespace cvtest +{ + + template + inline void + EXPECT_MATRIX_NEAR(const T a, const T b, double tolerance) + { + bool dims_match = (a.rows == b.rows) && (a.cols == b.cols); + EXPECT_EQ((int)a.rows, (int)b.rows); + EXPECT_EQ((int)a.cols, (int)b.cols); + + if (dims_match) + { + for (int r = 0; r < a.rows; ++r) + { + for (int c = 0; c < a.cols; ++c) + { + EXPECT_NEAR(a(r, c), b(r, c), tolerance) << "r=" << r << ", c=" << c << "."; + } + } + } + } + + template + inline void + EXPECT_VECTOR_NEAR(const T a, const T b, double tolerance) + { + bool dims_match = (a.rows == b.rows); + EXPECT_EQ((int)a.rows,(int)b.rows) << "Matrix rows don't match."; + + if (dims_match) + { + for (int r = 0; r < a.rows; ++r) + { + EXPECT_NEAR(a(r), b(r), tolerance) << "r=" << r << "."; + } + } + } + + template + inline double + cosinusBetweenMatrices(const T &a, const T &b) + { + double s = cv::sum( a.mul(b) )[0]; + return ( s / norm(a) / norm(b) ); + } + + // Check that sin(angle(a, b)) < tolerance + template + inline void + EXPECT_MATRIX_PROP(const T a, const T b, double tolerance) + { + bool dims_match = (a.rows == b.rows) && (a.cols == b.cols); + EXPECT_EQ((int)a.rows, (int)b.rows); + EXPECT_EQ((int)a.cols, (int)b.cols); + + if (dims_match) + { + double c = cosinusBetweenMatrices(a, b); + if (c * c < 1) + { + double s = sqrt(1 - c * c); + EXPECT_NEAR(0, s, tolerance); + } + } + } + + + + + struct TwoViewDataSet + { + cv::Matx33d K1, K2; // Internal parameters + cv::Matx33d R1, R2; // Rotation + cv::Vec3d t1, t2; // Translation + cv::Matx34d P1, P2; // Projection matrix, P = K(R|t) + cv::Matx33d F; // Fundamental matrix + cv::Mat_ X; // 3D points + cv::Mat_ x1, x2; // Projected points + }; + + void + generateTwoViewRandomScene(TwoViewDataSet &data); + + /** Check the properties of a fundamental matrix: + * + * 1. The determinant is 0 (rank deficient) + * 2. The condition x'T*F*x = 0 is satisfied to precision. + */ + void + expectFundamentalProperties( const cv::Matx33d &F, + const cv::Mat_ &ptsA, + const cv::Mat_ &ptsB, + double precision = 1e-9 ); + + /** + * 2D tracked points + * ----------------- + * + * The format is: + * + * row1 : x1 y1 x2 y2 ... x36 y36 for track 1 + * row2 : x1 y1 x2 y2 ... x36 y36 for track 2 + * etc + * + * i.e. a row gives the 2D measured position of a point as it is tracked + * through frames 1 to 36. If there is no match found in a view then x + * and y are -1. + * + * Each row corresponds to a different point. + * + */ + void + parser_2D_tracks(const std::string &_filename, std::vector &points2d ); + +} // namespace cvtest + +#endif diff --git a/modules/sfm/test/test_projection.cpp b/modules/sfm/test/test_projection.cpp new file mode 100644 index 00000000000..c6d8830f993 --- /dev/null +++ b/modules/sfm/test/test_projection.cpp @@ -0,0 +1,113 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test_precomp.hpp" +#include + +using namespace cv; +using namespace cv::sfm; +using namespace cvtest; +using namespace std; + +TEST(Sfm_projection, homogeneousToEuclidean) +{ + Matx33f X(1, 2, 3, + 4, 5, 6, + 2, 1, 0); + + Matx23f XEuclidean; + homogeneousToEuclidean(X,XEuclidean); + + EXPECT_EQ((int) X.rows-1,(int) XEuclidean.rows ); + + for(int y=0;y& Ps, + const std::vector >& xs, float err_max2d) +{ + cv::Mat X; + euclideanToHomogeneous(X_estimated, X); // 3D point + + for (int m = 0; m < xs.size(); ++m) + { + cv::Mat x; + homogeneousToEuclidean(cv::Mat(Ps[m]) * X, x); // 2d projection + cv::Mat projerr = xs[m] - x; + + for (int n = 0; n < projerr.cols; ++n) + { + double d = cv::norm(projerr.col(n)); + EXPECT_NEAR(0, d, err_max2d); + } + } +} + +#if CERES_FOUND + +TEST(Sfm_reconstruct, twoViewProjectiveOutliers) +{ + float err_max2d = 1e-7; + int nviews = 2; + int npoints = 50; + bool is_projective = true; + + std::vector > points2d; + std::vector Rs; + std::vector ts; + std::vector Ps; + Matx33d K; + Mat_ points3d; + generateScene(nviews, npoints, is_projective, K, Rs, ts, Ps, points3d, points2d); + + Mat_ points3d_estimated; + std::vector Ps_estimated; + reconstruct(points2d, Ps_estimated, points3d_estimated, K, is_projective); + + /* Check projection errors on GT */ + check_projection_errors(points3d, Ps, points2d, err_max2d); + + /* Check projection errors on estimates */ + std::vector Ps_estimated_d; + Ps_estimated_d.resize(Ps_estimated.size()); + for(size_t i=0; i x1(2,n); + x1 << 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 5; + + Mat_ x2 = x1.clone(); + for (int i = 0; i < n; ++i) + { + x2(0,i) += i % 2; // Multiple horizontal disparities. + } + x2(0,n - 1) = 10; + x2(1,n - 1) = 10; // The outlier has vertical disparity. + + Matx33d F; + vector inliers; + fundamentalFromCorrespondences8PointRobust(x1, x2, 0.1, F, inliers); + + // F should be 0, 0, 0, + // 0, 0, -1, + // 0, 1, 0 + EXPECT_NEAR(0.0, F(0,0), tolerance); + EXPECT_NEAR(0.0, F(0,1), tolerance); + EXPECT_NEAR(0.0, F(0,2), tolerance); + EXPECT_NEAR(0.0, F(1,0), tolerance); + EXPECT_NEAR(0.0, F(1,1), tolerance); + EXPECT_NEAR(0.0, F(2,0), tolerance); + EXPECT_NEAR(0.0, F(2,2), tolerance); + EXPECT_NEAR(F(1,2), -F(2,1), tolerance); + + EXPECT_EQ(n - 1, inliers.size()); +} + + +TEST(Sfm_robust, fundamentalFromCorrespondences8PointRealisticNoOutliers) +{ + double tolerance = 1e-8; + cvtest::TwoViewDataSet d; + generateTwoViewRandomScene(d); + + Matx33d F_estimated; + + vector inliers; + fundamentalFromCorrespondences8PointRobust(d.x1, d.x2, 3.0, F_estimated, inliers); + EXPECT_EQ(d.x1.cols, inliers.size()); + + // Normalize. + Matx33d F_gt_norm, F_estimated_norm; + normalizeFundamental(d.F, F_gt_norm); + normalizeFundamental(F_estimated, F_estimated_norm); + EXPECT_MATRIX_NEAR(F_gt_norm, F_estimated_norm, tolerance); + + // Check fundamental properties. + expectFundamentalProperties( F_estimated, d.x1, d.x2, tolerance); +} + + +TEST(Sfm_robust, fundamentalFromCorrespondences7PointRobust) +{ + double tolerance = 1e-8; + const int n = 16; + Mat_ x1(2,n); + x1 << 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 5; + + Mat_ x2 = x1.clone(); + for (int i = 0; i < n; ++i) + { + x2(0,i) += i % 2; // Multiple horizontal disparities. + } + x2(0,n - 1) = 10; + x2(1,n - 1) = 10; // The outlier has vertical disparity. + + Matx33d F; + vector inliers; + fundamentalFromCorrespondences7PointRobust(x1, x2, 0.1, F, inliers); + + // F should be 0, 0, 0, + // 0, 0, -1, + // 0, 1, 0 + EXPECT_NEAR(0.0, F(0,0), tolerance); + EXPECT_NEAR(0.0, F(0,1), tolerance); + EXPECT_NEAR(0.0, F(0,2), tolerance); + EXPECT_NEAR(0.0, F(1,0), tolerance); + EXPECT_NEAR(0.0, F(1,1), tolerance); + EXPECT_NEAR(0.0, F(2,0), tolerance); + EXPECT_NEAR(0.0, F(2,2), tolerance); + EXPECT_NEAR(F(1,2), -F(2,1), tolerance); + + EXPECT_EQ(n - 1, inliers.size()); +} + + +TEST(Sfm_robust, fundamentalFromCorrespondences7PointRealisticNoOutliers) +{ + double tolerance = 1e-8; + cvtest::TwoViewDataSet d; + generateTwoViewRandomScene(d); + + Matx33d F_estimated; + + vector inliers; + fundamentalFromCorrespondences7PointRobust(d.x1, d.x2, 3.0, F_estimated, inliers); + EXPECT_EQ(d.x1.cols, inliers.size()); + + // Normalize. + Matx33d F_gt_norm, F_estimated_norm; + normalizeFundamental(d.F, F_gt_norm); + normalizeFundamental(F_estimated, F_estimated_norm); + EXPECT_MATRIX_NEAR(F_gt_norm, F_estimated_norm, tolerance); + + // Check fundamental properties. + expectFundamentalProperties( F_estimated, d.x1, d.x2, tolerance); +} \ No newline at end of file diff --git a/modules/sfm/test/test_simple_pipeline.cpp b/modules/sfm/test/test_simple_pipeline.cpp new file mode 100644 index 00000000000..db269c14755 --- /dev/null +++ b/modules/sfm/test/test_simple_pipeline.cpp @@ -0,0 +1,89 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#if CERES_FOUND + +#include "test_precomp.hpp" + +#include + +using namespace cv; +using namespace cv::sfm; +using namespace cvtest; +using namespace std; + +const string SFM_DIR = "sfm"; +const string TRACK_FILENAME = "backyard_tracks.txt"; + +TEST(Sfm_simple_pipeline, backyard) +{ + string trackFilename = + string(TS::ptr()->get_data_path()) + SFM_DIR + "/" + TRACK_FILENAME; + + // Get tracks from file: check backyard.blend file + std::vector points2d; + parser_2D_tracks( trackFilename, points2d ); + + // Initial reconstruction + int keyframe1 = 1, keyframe2 = 30; + + // Camera data + double focal_length = 860.986572265625; // f = 24mm (checked debugging blender) + double principal_x = 400, principal_y = 225, k1 = -0.158, k2 = 0.131, k3 = 0; + + int refine_intrinsics = SFM_REFINE_FOCAL_LENGTH | SFM_REFINE_PRINCIPAL_POINT | SFM_REFINE_RADIAL_DISTORTION_K1 | SFM_REFINE_RADIAL_DISTORTION_K2; + int select_keyframes = 0; // disable automatic keyframes selection + int verbosity_level = -1; // mute logs + + libmv_CameraIntrinsicsOptions camera_instrinsic_options = + libmv_CameraIntrinsicsOptions(SFM_DISTORTION_MODEL_POLYNOMIAL, + focal_length, principal_x, principal_y, + k1, k2, k3); + libmv_ReconstructionOptions reconstruction_options(keyframe1, keyframe2, refine_intrinsics, select_keyframes, verbosity_level); + + Ptr euclidean_reconstruction = + SFMLibmvEuclideanReconstruction::create(camera_instrinsic_options, reconstruction_options); + + // Run reconstruction pipeline + euclidean_reconstruction->run(points2d); + + double error = euclidean_reconstruction->getError(); + //cout << "euclidean_reconstruction error = " << error << endl; + + EXPECT_LE( error, 1.4 ); // actually 1.38671 + // UPDATE: 1.38894 +} + +#endif /* CERES_FOUND */ diff --git a/modules/sfm/test/test_triangulation.cpp b/modules/sfm/test/test_triangulation.cpp new file mode 100644 index 00000000000..d1bf6d82594 --- /dev/null +++ b/modules/sfm/test/test_triangulation.cpp @@ -0,0 +1,96 @@ +/* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009, Willow Garage, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Willow Garage, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test_precomp.hpp" + +using namespace cv; +using namespace cv::sfm; +using namespace std; + +static void +checkTriangulation(int nviews, int npoints, bool is_projective, float err_max2d, float err_max3d) +{ + std::vector > points2d; + std::vector Rs; + std::vector ts; + std::vector Ps; + Matx33d K; + Mat_ points3d; + generateScene(nviews, npoints, is_projective, K, Rs, ts, Ps, points3d, points2d); + + // get 3d points + cv::Mat X, X_homogeneous; + std::vector > Ps_d(Ps.size()); + for(size_t i=0; i(Ps[i]); + triangulatePoints(points2d, Ps_d, X); + euclideanToHomogeneous(X, X_homogeneous); + + for (int i = 0; i < npoints; ++i) + { + for (int k = 0; k < nviews; ++k) + { + cv::Mat x_reprojected; + homogeneousToEuclidean( cv::Mat(Ps[k])*X_homogeneous.col(i), x_reprojected ); + + // Check reprojection error. Should be nearly zero. + double error = norm( x_reprojected - points2d[k].col(i) ); + EXPECT_LE(error*error, err_max2d); + } + + // Check 3d error. Should be nearly zero. + double error = norm( X.col(i) - points3d.col(i) ); + EXPECT_LE(error*error, err_max3d); + } +} + + +TEST(Sfm_triangulate, TriangulateDLT) +{ + int nviews = 2; + int npoints = 30; + bool is_projective = true; + + checkTriangulation(nviews, npoints, is_projective, 1e-7, 1e-9); +} + +TEST(Sfm_triangulate, NViewTriangulate_FiveViews) +{ + int nviews = 5; + int npoints = 6; + bool is_projective = true; + + checkTriangulation(nviews, npoints, is_projective, 1e-7, 1e-9); +} diff --git a/modules/sfm/tutorials/sfm_installation/sfm_installation.markdown b/modules/sfm/tutorials/sfm_installation/sfm_installation.markdown new file mode 100644 index 00000000000..7b7d5df1f98 --- /dev/null +++ b/modules/sfm/tutorials/sfm_installation/sfm_installation.markdown @@ -0,0 +1,63 @@ +SFM module installation {#tutorial_sfm_installation} +======================= + +Dependencies +------------ + +The Structure from Motion module depends on some open source libraries. + + - [Eigen](http://eigen.tuxfamily.org) 3.2.2 or later. \b Required + - [GLog](http://code.google.com/p/google-glog) 0.3.1 or later. \b Required + - [GFlags](http://code.google.com/p/gflags). \b Required + - [Ceres Solver](http://ceres-solver.org). Needed by the reconstruction API in order to solve part of the Bundle Adjustment plus the points Intersect. If Ceres Solver is not installed on your system, the reconstruction funcionality will be disabled. \b Recommended + +@note The module is only available for Linux/GNU systems. + + +Installation +------------ + +__Required Dependencies__ + +In case you are on [Ubuntu](http://www.ubuntu.com) you can simply install the required dependencies by typing the following command: + +@code{.bash} + sudo apt-get install libeigen3-dev libgflags-dev libgoogle-glog-dev +@endcode + +__Ceres Solver__ + +Start by installing all the dependencies: + +@code{.bash} + # CMake + sudo apt-get install cmake + # google-glog + gflags + sudo apt-get install libgoogle-glog-dev + # BLAS & LAPACK + sudo apt-get install libatlas-base-dev + # Eigen3 + sudo apt-get install libeigen3-dev + # SuiteSparse and CXSparse (optional) + # - If you want to build Ceres as a *static* library (the default) + # you can use the SuiteSparse package in the main Ubuntu package + # repository: + sudo apt-get install libsuitesparse-dev + # - However, if you want to build Ceres as a *shared* library, you must + # add the following PPA: + sudo add-apt-repository ppa:bzindovic/suitesparse-bugfix-1319687 + sudo apt-get update + sudo apt-get install libsuitesparse-dev +@endcode + +We are now ready to build, test, and install Ceres: + +@code{.bash} + git clone https://ceres-solver.googlesource.com/ceres-solver + cd ceres-solver + mkdir build && cd build + cmake .. + make -j4 + make test + sudo make install +@endcode \ No newline at end of file diff --git a/modules/sfm/tutorials/sfm_scene reconstruction/sfm_scene_reconstruction.markdown b/modules/sfm/tutorials/sfm_scene reconstruction/sfm_scene_reconstruction.markdown new file mode 100644 index 00000000000..bf9ed7b2380 --- /dev/null +++ b/modules/sfm/tutorials/sfm_scene reconstruction/sfm_scene_reconstruction.markdown @@ -0,0 +1,104 @@ +Scene Reconstruction {#tutorial_sfm_scene_reconstruction} +==================== + +Goal +---- + +In this tutorial you will learn how to use the reconstruction api for sparse reconstruction: + +- Load and file with a list of image paths. +- Run libmv reconstruction pipeline. +- Show obtained results using Viz. + + +Code +---- + +@include sfm/samples/scene_reconstruction.cpp + +Explanation +----------- + +Firstly, we need to load the file containing list of image paths in order to feed the reconstruction api: + +@code{.cpp} + /home/eriba/software/opencv_contrib/modules/sfm/samples/data/images/resized_IMG_2889.jpg + /home/eriba/software/opencv_contrib/modules/sfm/samples/data/images/resized_IMG_2890.jpg + /home/eriba/software/opencv_contrib/modules/sfm/samples/data/images/resized_IMG_2891.jpg + /home/eriba/software/opencv_contrib/modules/sfm/samples/data/images/resized_IMG_2892.jpg + + ... + + int getdir(const string _filename, vector &files) + { + ifstream myfile(_filename.c_str()); + if (!myfile.is_open()) { + cout << "Unable to read file: " << _filename << endl; + exit(0); + } else { + string line_str; + while ( getline(myfile, line_str) ) + files.push_back(line_str); + } + return 1; + } +@endcode + +Secondly, the built container will be used to feed the reconstruction api. It is important outline that the estimated results must be stored in a vector. In this +case is called the overloaded signature for real images which from the images, internally extracts and compute the sparse 2d features using DAISY descriptors in order to be matched using FlannBasedMatcher and build the tracks structure. + +@code{.cpp} + bool is_projective = true; + vector Rs_est, ts_est, points3d_estimated; + reconstruct(images_paths, Rs_est, ts_est, K, points3d_estimated, is_projective); + + // Print output + + cout << "\n----------------------------\n" << endl; + cout << "Reconstruction: " << endl; + cout << "============================" << endl; + cout << "Estimated 3D points: " << points3d_estimated.size() << endl; + cout << "Estimated cameras: " << Rs_est.size() << endl; + cout << "Refined intrinsics: " << endl << K << endl << endl; +@endcode + +Finally, the obtained results will be shown in Viz. + +Usage and Results +----------------- + +In order to run this sample we need to specify the path to the image paths files, the focal lenght of the camera in addition to the center projection coordinates (in pixels). + +**1. Middlebury temple** + +Using following image sequence [1] and the followings camera parameters we can compute the sparse 3d reconstruction: + +@code{.bash} + ./example_sfm_scene_reconstruction image_paths_file.txt 800 400 225 +@endcode + +![](pics/temple_input.jpg) + +The following picture shows the obtained camera motion in addition to the estimated sparse 3d reconstruction: + +![](pics/temple_reconstruction.jpg) + + +**2. Sagrada Familia** + +Using following image sequence [2] and the followings camera parameters we can compute the sparse 3d reconstruction: + +@code{.bash} + ./example_sfm_scene_reconstruction image_paths_file.txt 350 240 360 +@endcode + +![](pics/sagrada_familia_input.jpg) + +The following picture shows the obtained camera motion in addition to the estimated sparse 3d reconstruction: + +![](pics/sagrada_familia_reconstruction.jpg) + +[1] [http://vision.middlebury.edu/mview/data](http://vision.middlebury.edu/mview/data) + +[2] Penate Sanchez, A. and Moreno-Noguer, F. and Andrade Cetto, J. and Fleuret, F. (2014). LETHA: Learning from High Quality Inputs for 3D Pose Estimation in Low Quality Images. Proceedings of the International Conference on 3D vision (3DV). +[URL](http://www.iri.upc.edu/research/webprojects/pau/datasets/sagfam) diff --git a/modules/sfm/tutorials/sfm_trajectory_estimation/sfm_trajectory_estimation.markdown b/modules/sfm/tutorials/sfm_trajectory_estimation/sfm_trajectory_estimation.markdown new file mode 100644 index 00000000000..f39bba23cf7 --- /dev/null +++ b/modules/sfm/tutorials/sfm_trajectory_estimation/sfm_trajectory_estimation.markdown @@ -0,0 +1,82 @@ +Camera Motion Estimation {#tutorial_sfm_trajectory_estimation} +======================== + +Goal +---- + +In this tutorial you will learn how to use the reconstruction api for camera motion estimation: + +- Load and file with the tracked 2d points and build the container over all the frames. +- Run libmv reconstruction pipeline. +- Show obtained results using Viz. + + +Code +---- + +@include sfm/samples/trajectory_reconstruccion.cpp + +Explanation +----------- + +Firstly, we need to load the file containing the 2d points tracked over all the frames and construct the container to feed the reconstruction api. In this case the tracked 2d points will have the following structure, a vector of 2d points array, where each inner array represents a different frame. Every frame is composed by a list of 2d points which e.g. the first point in frame 1 is the same point in frame 2. If there is no point in a frame the assigned value will be (-1,-1): + +@code{.cpp} + /* Build the following structure data + * + * frame1 frame2 frameN + * track1 | (x11,y11) | -> | (x12,y12) | -> | (x1N,y1N) | + * track2 | (x21,y11) | -> | (x22,y22) | -> | (x2N,y2N) | + * trackN | (xN1,yN1) | -> | (xN2,yN2) | -> | (xNN,yNN) | + * + * + * In case a marker (x,y) does not appear in a frame its + * values will be (-1,-1). + */ + + ... + + for (int i = 0; i < n_frames; ++i) + { + Mat_ frame(2, n_tracks); + + for (int j = 0; j < n_tracks; ++j) + { + frame(0,j) = tracks[j][i][0]; + frame(1,j) = tracks[j][i][1]; + } + points2d.push_back(Mat(frame)); + } +@endcode + +Secondly, the built container will be used to feed the reconstruction api. It is important outline that the estimated results must be stored in a vector: + +@code{.cpp} + bool is_projective = true; + vector Rs_est, ts_est, points3d_estimated; + reconstruct(points2d, Rs_est, ts_est, K, points3d_estimated, is_projective); + + // Print output + + cout << "\n----------------------------\n" << endl; + cout << "Reconstruction: " << endl; + cout << "============================" << endl; + cout << "Estimated 3D points: " << points3d_estimated.size() << endl; + cout << "Estimated cameras: " << Rs_est.size() << endl; + cout << "Refined intrinsics: " << endl << K << endl << endl; +@endcode + +Finally, the obtained results will be shown in Viz, in this case reproducing the camera with an oscillation effect. + +Usage and Results +----------------- + +In order to run this sample we need to specify the path to the tracked points file, the focal lenght of the camera in addition to the center projection coordinates (in pixels). You can find a sample file in samples/data/desktop_trakcks.txt + +@code{.bash} + ./example_sfm_trajectory_reconstruction desktop_tracks.txt 1914 640 360 +@endcode + +The following picture shows the obtained camera motion obtained from the tracked 2d points: + +![](pics/desktop_trajectory.png) \ No newline at end of file diff --git a/modules/sfm/tutorials/table_of_content_sfm.markdown b/modules/sfm/tutorials/table_of_content_sfm.markdown new file mode 100644 index 00000000000..7d9423e2dff --- /dev/null +++ b/modules/sfm/tutorials/table_of_content_sfm.markdown @@ -0,0 +1,26 @@ +Structure From Motion {#tutorial_table_of_content_sfm} +===================== + +- @subpage tutorial_sfm_installation + + *Compatibility:* \> OpenCV 3.0 + + *Author:* Edgar Riba + + Instructions in order to properly setup the Structure from Motion module. + +- @subpage tutorial_sfm_trajectory_estimation + + *Compatibility:* \> OpenCV 3.0 + + *Author:* Edgar Riba + + Camera motion estimation from a given set of tracked 2d points. + +- @subpage tutorial_sfm_scene_reconstruction + + *Compatibility:* \> OpenCV 3.0 + + *Author:* Edgar Riba + + Sparse scene reconstruction from a given set of images. \ No newline at end of file