diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e3ae3bd5..351291b6 100755 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v2 # required to mount the Github Workspace to a volume - name: Run clang-format-lint - uses: DoozyX/clang-format-lint-action@v0.11 + uses: DoozyX/clang-format-lint-action@v0.18.1 with: source: '.' exclude: './third_party ./external .git' @@ -42,7 +42,7 @@ jobs: - name: Run cppcheck uses: addnab/docker-run-action@v3 with: - image: bailool/mc-calib-dev:latest + image: bailool/mc-calib-dev:opencv410 options: -v ${{ github.workspace }}:/home/MC-Calib run: | cppcheck MC-Calib/McCalib/include @@ -58,7 +58,7 @@ jobs: - name: Run clang-tidy uses: addnab/docker-run-action@v3 with: - image: bailool/mc-calib-dev:latest + image: bailool/mc-calib-dev:opencv410 options: -v ${{ github.workspace }}:/home/MC-Calib run: | mkdir MC-Calib/build && cd MC-Calib/build @@ -90,7 +90,7 @@ jobs: - name: Run tests uses: addnab/docker-run-action@v3 with: - image: bailool/mc-calib-dev:latest + image: bailool/mc-calib-dev:opencv410 options: -v ${{ github.workspace }}:/home/MC-Calib run: | mkdir MC-Calib/build && cd MC-Calib/build @@ -122,7 +122,7 @@ jobs: - name: Run tests uses: addnab/docker-run-action@v3 with: - image: bailool/mc-calib-dev:latest + image: bailool/mc-calib-dev:opencv410 options: -v ${{ github.workspace }}:/home/MC-Calib run: | mkdir MC-Calib/build && cd MC-Calib/build @@ -165,7 +165,7 @@ jobs: - name: Run tests uses: addnab/docker-run-action@v3 with: - image: bailool/mc-calib-prod:latest + image: bailool/mc-calib-prod:opencv410 options: -v ${{ github.workspace }}:/home/MC-Calib:rw run: | mkdir MC-Calib/build && cd MC-Calib/build @@ -228,7 +228,7 @@ jobs: - name: Run tests uses: addnab/docker-run-action@v3 with: - image: bailool/mc-calib-dev:latest + image: bailool/mc-calib-dev:opencv410 options: -v ${{ github.workspace }}:${{ github.workspace }} run: | mkdir ${{ github.workspace }}/build && cd ${{ github.workspace }}/build @@ -266,7 +266,7 @@ jobs: - name: Run python linters uses: addnab/docker-run-action@v3 with: - image: bailool/mc-calib-dev:latest + image: bailool/mc-calib-dev:opencv410 options: -v ${{ github.workspace }}:/home/MC-Calib run: | cd MC-Calib/python_utils @@ -292,7 +292,7 @@ jobs: - name: Run python utils uses: addnab/docker-run-action@v3 with: - image: bailool/mc-calib-prod:latest + image: bailool/mc-calib-prod:opencv410 options: -v ${{ github.workspace }}:/home/MC-Calib:rw run: | cd MC-Calib/python_utils diff --git a/CMakeLists.txt b/CMakeLists.txt index 4137fe09..8c1919c0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,5 @@ endif (DOXYGEN_FOUND) ########################################################################## add_subdirectory(apps/create_charuco_boards) add_subdirectory(McCalib) -find_library(McCalib REQUIRED) add_subdirectory(apps/calibrate) add_subdirectory(tests) diff --git a/Dockerfile b/Dockerfile index a9db8e55..f0800cec 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ -FROM ubuntu:20.04 as prod +FROM ubuntu:24.04 as prod ENV DEBIAN_FRONTEND noninteractive RUN apt update && apt install -y --no-install-recommends apt-utils && \ apt upgrade -y && apt autoremove -y && \ - apt install -y build-essential cmake && \ + apt install -y build-essential cmake wget unzip git && \ apt install -y python-is-python3 python3-pip && \ rm -rf /var/lib/apt/lists/* @@ -15,7 +15,14 @@ WORKDIR /home #------------------------------ # # INSTALL OPENCV 4 # #------------------------------ # -RUN apt update && apt install -y libopencv-dev && \ +RUN wget -O opencv.zip https://github.com/opencv/opencv/archive/4.10.0.zip && \ + wget -O opencv_contrib.zip https://github.com/opencv/opencv_contrib/archive/4.10.0.zip && \ + unzip opencv.zip && unzip opencv_contrib.zip && \ + mkdir -p build && cd build && \ + cmake -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib-4.10.0/modules ../opencv-4.10.0 && \ + cmake --build . --target install && \ + cd /home && rm opencv.zip && rm opencv_contrib.zip && \ + rm -rf opencv-4.10.0 && rm -rf opencv_contrib-4.10.0 && rm -rf build && \ rm -rf /var/lib/apt/lists/* #------------------------------ # @@ -27,36 +34,36 @@ RUN apt update && apt install -y libboost-all-dev && \ #------------------------------ # # Install Ceres # #------------------------------ # -RUN apt update && \ - apt install -y libgoogle-glog-dev libgflags-dev libatlas-base-dev libeigen3-dev libsuitesparse-dev && \ +RUN apt update && apt install -y libgoogle-glog-dev libgflags-dev libatlas-base-dev libeigen3-dev libsuitesparse-dev && \ rm -rf /var/lib/apt/lists/* -RUN apt update && apt install -y git && \ - /bin/bash -c "git clone https://ceres-solver.googlesource.com/ceres-solver && \ +RUN git clone --branch 20240722.0 --single-branch https://github.com/abseil/abseil-cpp.git && \ + cd abseil-cpp && mkdir build && cd build && \ + cmake .. && cmake --build . --target install && \ + cd /home && rm -rf abseil-cpp && \ + rm -rf /var/lib/apt/lists/* +RUN git clone --branch 2.2.0 --single-branch https://github.com/ceres-solver/ceres-solver.git && \ cd ceres-solver && mkdir build && cd build && cmake .. && \ make && make install && \ - cd /home && rm -rf ceres-solver" && \ + cd /home && rm -rf ceres-solver && \ rm -rf /var/lib/apt/lists/* # Install python requirements for python_utils scripts RUN --mount=type=bind,source=python_utils/requirements_prod.txt,target=/tmp/requirements.txt \ - pip install --requirement /tmp/requirements.txt && \ + python -m pip install --requirement /tmp/requirements.txt --break-system-packages && \ rm -rf /var/lib/apt/lists/* FROM prod as dev -RUN apt update && apt install -y python3-opencv && \ - rm -rf /var/lib/apt/lists/* - #------------------------------ # # Doxygen # #------------------------------ # RUN apt update && apt install -y flex bison && \ - /bin/bash -c "git clone https://github.com/doxygen/doxygen.git && \ + git clone https://github.com/doxygen/doxygen.git && \ cd doxygen && mkdir build && cd build &&\ cmake -G 'Unix Makefiles' .. && \ make && \ make install && \ - cd /home && rm -rf doxygen" && \ + cd /home && rm -rf doxygen && \ rm -rf /var/lib/apt/lists/* #------------------------------ # @@ -67,5 +74,5 @@ RUN apt update && apt install -y cppcheck clang-tidy valgrind lcov && \ # Install python requirements for python_utils scripts RUN --mount=type=bind,source=python_utils/requirements_dev.txt,target=/tmp/requirements.txt \ - pip install --requirement /tmp/requirements.txt && \ + python -m pip install --requirement /tmp/requirements.txt --break-system-packages && \ rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/McCalib/include/Board.hpp b/McCalib/include/Board.hpp index e69e5a1f..7fc2f003 100644 --- a/McCalib/include/Board.hpp +++ b/McCalib/include/Board.hpp @@ -6,6 +6,8 @@ #include #include +namespace McCalib { + class BoardObs; class Frame; @@ -40,7 +42,7 @@ class Board final { std::map> frames_; // Charuco board - cv::Ptr charuco_board_; // vector of charuco boards + cv::Ptr charuco_board_; // Functions Board() = delete; @@ -49,3 +51,5 @@ class Board final { void insertNewBoard(std::shared_ptr new_board); void insertNewFrame(std::shared_ptr new_frame); }; + +} // namespace McCalib diff --git a/McCalib/include/BoardObs.hpp b/McCalib/include/BoardObs.hpp index 7a09395e..0a4c4726 100644 --- a/McCalib/include/BoardObs.hpp +++ b/McCalib/include/BoardObs.hpp @@ -7,6 +7,8 @@ #include #include +namespace McCalib { + class Camera; class Board; @@ -60,3 +62,5 @@ class BoardObs final { cv::Mat getRotVec() const; cv::Mat getTransVec() const; }; + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/include/Camera.hpp b/McCalib/include/Camera.hpp index 5ae777c9..a845bef0 100644 --- a/McCalib/include/Camera.hpp +++ b/McCalib/include/Camera.hpp @@ -11,6 +11,8 @@ #include "Frame.hpp" #include "Object3DObs.hpp" +namespace McCalib { + /** * @class Camera * @@ -64,3 +66,5 @@ class Camera final { void setIntrinsics(const cv::Mat K, const cv::Mat distortion_vector); bool checkBorderToleranceFisheye(std::shared_ptr board_obs); }; + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/include/CameraGroup.hpp b/McCalib/include/CameraGroup.hpp index e2069af3..aa2f5be7 100644 --- a/McCalib/include/CameraGroup.hpp +++ b/McCalib/include/CameraGroup.hpp @@ -12,6 +12,8 @@ #include "Frame.hpp" #include "Object3DObs.hpp" +namespace McCalib { + /** * @class CameraGroup * @@ -63,3 +65,5 @@ class CameraGroup final { void refineCameraGroupAndObjects(const int nb_iterations); void refineCameraGroupAndObjectsAndIntrinsics(const int nb_iterations); }; + +} // namespace McCalib diff --git a/McCalib/include/CameraGroupObs.hpp b/McCalib/include/CameraGroupObs.hpp index 6185fdbf..a722df36 100644 --- a/McCalib/include/CameraGroupObs.hpp +++ b/McCalib/include/CameraGroupObs.hpp @@ -8,6 +8,8 @@ #include #include +namespace McCalib { + class CameraGroup; /** @@ -50,3 +52,5 @@ class CameraGroupObs final { cv::Mat getObjectTransVec(int object_id); void updateObjObsPose(); }; + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/include/CameraObs.hpp b/McCalib/include/CameraObs.hpp index 5dd80b14..c620e130 100644 --- a/McCalib/include/CameraObs.hpp +++ b/McCalib/include/CameraObs.hpp @@ -8,6 +8,8 @@ #include #include +namespace McCalib { + class Camera; /** @@ -37,3 +39,5 @@ class CameraObs final { void insertNewBoard(std::shared_ptr new_board); void insertNewObject(std::shared_ptr new_object); }; + +} // namespace McCalib diff --git a/McCalib/include/Frame.hpp b/McCalib/include/Frame.hpp index 8ae282c9..c01b7399 100644 --- a/McCalib/include/Frame.hpp +++ b/McCalib/include/Frame.hpp @@ -11,6 +11,8 @@ #include "CameraObs.hpp" #include "Object3DObs.hpp" +namespace McCalib { + /** * @class Frame * @@ -57,3 +59,5 @@ class Frame final { insertNewCameraGroupObs(std::shared_ptr new_cam_group_obs, const int camera_group_idx); }; + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/include/Graph.hpp b/McCalib/include/Graph.hpp index 68339176..eb94d583 100644 --- a/McCalib/include/Graph.hpp +++ b/McCalib/include/Graph.hpp @@ -45,4 +45,4 @@ class Graph final { std::vector getPath(const std::vector &p_map, const Vertex &source, const Vertex &destination); -}; +}; \ No newline at end of file diff --git a/McCalib/include/McCalib.hpp b/McCalib/include/McCalib.hpp index ea27ebf6..11856d80 100644 --- a/McCalib/include/McCalib.hpp +++ b/McCalib/include/McCalib.hpp @@ -35,11 +35,12 @@ class Calibration final { public: // Parameters unsigned int nb_camera_, nb_board_; - cv::Ptr dict_ = cv::aruco::getPredefinedDictionary( + cv::aruco::Dictionary dict_ = cv::aruco::getPredefinedDictionary( cv::aruco::DICT_6X6_1000); // load the dictionary that correspond to the // charuco board - cv::Ptr charuco_params_ = - cv::aruco::DetectorParameters::create(); // parameters for detection + + cv::aruco::DetectorParameters charuco_params_ = + cv::aruco::DetectorParameters(); // parameters for detection float min_perc_pts_; // images path diff --git a/McCalib/include/Object3D.hpp b/McCalib/include/Object3D.hpp index db5febef..608acd41 100644 --- a/McCalib/include/Object3D.hpp +++ b/McCalib/include/Object3D.hpp @@ -6,6 +6,8 @@ #include #include +namespace McCalib { + class Board; class BoardObs; class Object3DObs; @@ -68,3 +70,5 @@ class Object3D final { void refineObject(const int nb_iterations); void updateObjectPts(); }; + +} // namespace McCalib diff --git a/McCalib/include/Object3DObs.hpp b/McCalib/include/Object3DObs.hpp index 3c16af1c..4500acc7 100644 --- a/McCalib/include/Object3DObs.hpp +++ b/McCalib/include/Object3DObs.hpp @@ -8,6 +8,8 @@ #include #include +namespace McCalib { + class Camera; class Board; class BoardObs; @@ -82,3 +84,5 @@ class Object3DObs final { cv::Mat getRotInGroupVec() const; cv::Mat getTransInGroupVec() const; }; + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/include/OptimizationCeres.h b/McCalib/include/OptimizationCeres.h index b3985059..2ad5961b 100755 --- a/McCalib/include/OptimizationCeres.h +++ b/McCalib/include/OptimizationCeres.h @@ -662,104 +662,4 @@ struct ReprojectionError_CameraGroupAndObjectRefAndIntrinsics { bool refine_camera; bool refine_board; int distortion_type; -}; - -/* -template -Eigen::Matrix -AngleAxisToRotationMatrix(const Eigen::Matrix& rvec) { - T angle = rvec.norm(); - if (angle == T(0)) { - return Eigen::Matrix::Identity(); - } - - Eigen::Matrix axis; - axis = rvec.normalized(); - - Eigen::Matrix rmat; - rmat = Eigen::AngleAxis(angle, axis); - - return rmat; -} - - - -// Refine handeye calibration -struct TransformationError { - TransformationError(double r1_x, double r1_y, double r1_z, double t1_x, double -t1_y, double t1_z, double r2_x, double r2_y, double r2_z, double t2_x, double -t2_y, double t2_z) : r1_x(r1_x), r1_y(r1_y), r1_z(r1_z), t1_x(t1_x), t1_y(t1_y), -t1_z(t1_z), r2_x(r2_x), r2_y(r2_y), r2_z(r2_z), t2_x(t2_x), t2_y(t2_y), -t2_z(t2_z) {} - - template - bool operator()(const T *const transform, - T *residuals) const { - - // 1. convert to rotation matrix for relative 1 and 2 - Eigen::Matrix r1, r2; - r1(0,0) = r1_x; r1(1,0) = r1_y; r1(2,0) = r1_z; - r2(0,0) = r2_x; r2(1,0) = r2_y; r2(2,0) = r2_z; - Eigen::Matrix R1 = AngleAxisToRotationMatrix(r1); - Eigen::Matrix R2 = AngleAxisToRotationMatrix(r2); - Eigen::Matrix t1, t2; - t1(0,0) = t1_x; t1(1,0) = t1_y; t1(2,0) = t1_z; - t2(0,0) = t2_x; t2(1,0) = t2_y; t2(2,0) = t2_z; - - Eigen::Matrix4d Trans1; // Your Transformation Matrix - Trans1.setIdentity(); // Set to Identity to make bottom row of Matrix -0,0,0,1 Trans1.block<3,3>(0,0) = R1; Trans1.block<3,1>(0,3) = t1; - Eigen::Matrix4d Trans2; // Your Transformation Matrix - Trans2.setIdentity(); // Set to Identity to make bottom row of Matrix -0,0,0,1 Trans2.block<3,3>(0,0) = R2; Trans2.block<3,1>(0,3) = t2; - - //2. convert the transform - Eigen::Matrix rt; - Eigen::Matrix tt; - rt(0,0) = (transform[0]); rt(1,0) = (transform[1]); rt(2,0) = -(transform[2]); Eigen::Matrix Rt = AngleAxisToRotationMatrix(rt); - tt(0,0) = (transform[3]); tt(1,0) = (transform[4]); tt(2,0) = -(transform[5]); Eigen::Matrix Trans; // Your Transformation Matrix - Trans(0,0) = Rt(0,0); Trans(0,1) = Rt(0,1); Trans(0,2) = Rt(0,2); Trans(0,3) -= tt(0,0); Trans(1,0) = Rt(1,0); Trans(1,1) = Rt(1,1); Trans(1,2) = Rt(1,2); -Trans(1,3) = tt(1,0); Trans(2,0) = Rt(2,0); Trans(2,1) = Rt(2,1); Trans(2,2) = -Rt(2,2); Trans(2,3) = tt(2,0); - - Eigen::Matrix4d Error = (Trans - Trans2*Trans*Trans1.inverse()); - //r1.row(0) << r1_x, r1_y, r1_z; - //Map r1; //(r11); - - - //AngleAxisToRotationMatrix(axis_angle, matrix); - // 2. convert the "transform" ro rotation matrix - - // 2. Compute errors matrix - residuals[0] = T(Error(0,0)); - residuals[1] = T(Error(1,0)); - residuals[2] = T(Error(2,0)); - residuals[3] = T(Error(3,0)); - residuals[4] = T(Error(0,1)); - residuals[5] = T(Error(1,1)); - residuals[6] = T(Error(2,1)); - residuals[7] = T(Error(3,1)); - residuals[8] = T(Error(0,2)); - residuals[9] = T(Error(1,2)); - residuals[10] = T(Error(2,2)); - residuals[11] = T(Error(3,2)); - - return true; - } - - static ceres::CostFunction *Create(const double r1_x, const double r1_y, const -double r1_z, const double t1_x, const double t1_y, const double t1_z, const -double r2_x, const double r2_y, const double r2_z, const double t2_x, const -double t2_y, const double t2_z) { return (new -ceres::AutoDiffCostFunction( new -TransformationError(r1_x, r1_y, r1_z, t1_x, t1_y, t1_z, r2_x, r2_y, r2_z, t2_x, -t2_y, t2_z))); - } - double r1_x, r1_y, r1_z; - double t1_x, t1_y, t1_z; - double r2_x, r2_y, r2_z; - double t2_x, t2_y, t2_z; -};*/ \ No newline at end of file +}; \ No newline at end of file diff --git a/McCalib/include/geometrytools.hpp b/McCalib/include/geometrytools.hpp index e53c4e5c..03fd6244 100644 --- a/McCalib/include/geometrytools.hpp +++ b/McCalib/include/geometrytools.hpp @@ -6,6 +6,8 @@ #include #include +namespace McCalib { + cv::Mat RT2Proj(cv::Mat R, cv::Mat T); cv::Mat RVecT2Proj(cv::Mat RVec, cv::Mat T); cv::Mat RVecT2ProjInt(cv::Mat RVec, cv::Mat T, cv::Mat K); @@ -33,11 +35,13 @@ cv::Mat ransacP3P(const std::vector &scene_points, const double p = 0.99, bool refine = true); std::vector transform3DPts(std::vector pts3D, cv::Mat rot, cv::Mat trans); -cv::Mat handeyeCalibration(std::vector pose_abs_1, - std::vector pose_abs_2); -cv::Mat handeyeBootstratpTranslationCalibration( - unsigned int nb_cluster, unsigned int nb_it, - std::vector pose_abs_1, std::vector pose_abs_2); +cv::Mat handeyeCalibration(const std::vector &pose_abs_1, + const std::vector &pose_abs_2); +cv::Mat +handeyeBootstratpTranslationCalibration(const unsigned int nb_cluster, + const unsigned int nb_it, + const std::vector &pose_abs_1, + const std::vector &pose_abs_2); double median(std::vector &v); cv::Mat ransacP3PDistortion(const std::vector &scene_points, const std::vector &image_points, @@ -56,4 +60,6 @@ cv::Mat convertRotationMatrixToQuaternion(cv::Mat R); cv::Mat convertQuaternionToRotationMatrix(const std::array &q); cv::Mat getAverageRotation(std::vector &r1, std::vector &r2, std::vector &r3, - const bool use_quaternion_averaging = true); \ No newline at end of file + const bool use_quaternion_averaging = true); + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/include/point_refinement.h b/McCalib/include/point_refinement.h index 6239c276..0a71a0c8 100644 --- a/McCalib/include/point_refinement.h +++ b/McCalib/include/point_refinement.h @@ -3,7 +3,7 @@ #include #include -/****** ******/ +namespace McCalib { float step_threshold = 0.001; @@ -185,3 +185,5 @@ void saddleSubpixelRefinement(const cv::Mat &input, saddleSubpixelRefinement(smooth, initial, A, valid, refined, window_half_size, max_iterations); } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/Board.cpp b/McCalib/src/Board.cpp index 7e939b8d..dde49110 100644 --- a/McCalib/src/Board.cpp +++ b/McCalib/src/Board.cpp @@ -10,6 +10,8 @@ #include "Frame.hpp" #include "logger.h" +namespace McCalib { + /** * @brief Initialize Board object * @@ -84,3 +86,5 @@ void Board::insertNewBoard(std::shared_ptr new_board) { void Board::insertNewFrame(std::shared_ptr new_frame) { frames_[new_frame->frame_idx_] = new_frame; } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/BoardObs.cpp b/McCalib/src/BoardObs.cpp index 0a32c8ea..763d6494 100644 --- a/McCalib/src/BoardObs.cpp +++ b/McCalib/src/BoardObs.cpp @@ -9,6 +9,8 @@ #include "geometrytools.hpp" #include "logger.h" +namespace McCalib { + /** * @brief Initialize the board observation object * @@ -197,3 +199,5 @@ float BoardObs::computeReprojectionError() { return (error_board_vec.size() > 0) ? sum_err_board / error_board_vec.size() : 0.f; } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/Camera.cpp b/McCalib/src/Camera.cpp index 824821a9..268835a5 100644 --- a/McCalib/src/Camera.cpp +++ b/McCalib/src/Camera.cpp @@ -1,13 +1,18 @@ -#include "opencv2/core/core.hpp" +#include #include +#include +#include + +#include "opencv2/core/core.hpp" #include #include -#include #include "Camera.hpp" #include "OptimizationCeres.h" #include "logger.h" +namespace McCalib { + Camera::Camera(const int cam_idx, const int distortion_model) : cam_idx_(cam_idx), distortion_model_(distortion_model) {} @@ -167,7 +172,9 @@ void Camera::initializeCalibration() { std::srand(unsigned(std::time(0))); std::vector shuffled_board_ind(indbv.size()); std::iota(shuffled_board_ind.begin(), shuffled_board_ind.end(), 0); - random_shuffle(shuffled_board_ind.begin(), shuffled_board_ind.end()); + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(shuffled_board_ind.begin(), shuffled_board_ind.end(), g); // nb of boards used for the initial estimation of intrinsic parameters //(at least 50 boards for perspective) @@ -295,3 +302,5 @@ void Camera::refineIntrinsicCalibration(const int nb_iterations) { LOG_INFO << "distortion vector after optimization :: " << getDistortionVectorVector(); } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/CameraGroup.cpp b/McCalib/src/CameraGroup.cpp index 5d7dc5a6..bdfa39fe 100644 --- a/McCalib/src/CameraGroup.cpp +++ b/McCalib/src/CameraGroup.cpp @@ -9,6 +9,8 @@ #include "CameraGroup.hpp" #include "logger.h" +namespace McCalib { + /** * @brief Initialize the camera group object * @@ -577,3 +579,5 @@ void CameraGroup::refineCameraGroupAndObjectsAndIntrinsics( LOG_INFO << "Camera " << it.first << " :: " << getCameraPoseMat(it.first); } } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/CameraGroupObs.cpp b/McCalib/src/CameraGroupObs.cpp index 5c946c6c..619cf82c 100644 --- a/McCalib/src/CameraGroupObs.cpp +++ b/McCalib/src/CameraGroupObs.cpp @@ -8,6 +8,8 @@ #include "CameraGroupObs.hpp" +namespace McCalib { + /** * @brief Associate this observation with its respective camera group * @@ -211,3 +213,5 @@ void CameraGroupObs::updateObjObsPose() { } } } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/CameraObs.cpp b/McCalib/src/CameraObs.cpp index 602837de..a592c7c6 100755 --- a/McCalib/src/CameraObs.cpp +++ b/McCalib/src/CameraObs.cpp @@ -6,6 +6,8 @@ #include "CameraObs.hpp" +namespace McCalib { + CameraObs::CameraObs(std::shared_ptr new_board) { insertNewBoard(new_board); } @@ -30,3 +32,5 @@ void CameraObs::insertNewObject(std::shared_ptr new_object) { object_observations_[object_observations_.size()] = new_object; object_idx_.push_back(new_object->object_3d_id_); } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/Frame.cpp b/McCalib/src/Frame.cpp index 98ad0e48..13671b0b 100644 --- a/McCalib/src/Frame.cpp +++ b/McCalib/src/Frame.cpp @@ -6,6 +6,8 @@ #include "Frame.hpp" +namespace McCalib { + Frame::Frame(const int frame_idx, const int cam_idx, const std::string frame_path) { frame_idx_ = frame_idx; @@ -54,3 +56,5 @@ void Frame::insertNewCameraGroupObs( cam_group_idx_.push_back(camera_group_idx); cam_group_observations_[cam_group_observations_.size()] = new_cam_group_obs; } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/Graph.cpp b/McCalib/src/Graph.cpp index a985791f..4982d00f 100755 --- a/McCalib/src/Graph.cpp +++ b/McCalib/src/Graph.cpp @@ -150,4 +150,4 @@ std::vector Graph::getPath(const std::vector &p_map, std::reverse(path.begin(), path.end()); // return in v1->v2 order return path; -} +} \ No newline at end of file diff --git a/McCalib/src/McCalib.cpp b/McCalib/src/McCalib.cpp index 8d0f3ad1..ecc8e7d3 100644 --- a/McCalib/src/McCalib.cpp +++ b/McCalib/src/McCalib.cpp @@ -1,18 +1,18 @@ -#include "opencv2/core/core.hpp" #include -#include -#include #include #include +#include + +#include +#include +#include +#include +#include #include "McCalib.hpp" #include "logger.h" #include "point_refinement.h" -#include -#include -#include - namespace McCalib { /** @@ -117,17 +117,30 @@ Calibration::Calibration(const std::string config_path) { std::map> charuco_boards; int offset_count = 0; for (int i = 0; i <= max_board_idx; i++) { - cv::Ptr charuco = cv::aruco::CharucoBoard::create( - number_x_square_per_board_[i], number_y_square_per_board_[i], - length_square, length_marker, dict_); - if (i != 0) // If it is the first board then just use the standard idx - { - int id_offset = charuco_boards[i - 1]->ids.size() + offset_count; + if (i == 0) { + // If it is the first board then just use the standard idx + cv::Ptr charuco = + new cv::aruco::CharucoBoard(cv::Size(number_x_square_per_board_[i], + number_y_square_per_board_[i]), + length_square, length_marker, dict_); + charuco->setLegacyPattern(true); + + charuco_boards[i] = charuco; + } else { + int id_offset = charuco_boards[i - 1]->getIds().size() + offset_count; offset_count = id_offset; - for (auto &id : charuco->ids) - id += id_offset; + const std::size_t num_idxs = charuco_boards[i - 1]->getIds().size(); + std::vector cur_ids(num_idxs); + std::iota(cur_ids.begin(), cur_ids.end(), id_offset); + + cv::Ptr charuco = new cv::aruco::CharucoBoard( + cv::Size(number_x_square_per_board_[i], + number_y_square_per_board_[i]), + length_square, length_marker, dict_, cur_ids); + charuco->setLegacyPattern(true); + + charuco_boards[i] = charuco; } - charuco_boards[i] = charuco; } // Initialize the 3D boards @@ -301,11 +314,12 @@ void Calibration::detectBoardsInImageWithCamera(const std::string frame_path, // key == board id, value == ID corners on checkerboard std::map> charuco_idx; - charuco_params_->adaptiveThreshConstant = 1; + charuco_params_.adaptiveThreshConstant = 1; for (std::size_t i = 0; i < nb_board_; i++) { - cv::aruco::detectMarkers(image, boards_3d_[i]->charuco_board_->dictionary, - marker_corners[i], marker_idx[i], charuco_params_); + cv::aruco::ArucoDetector detector( + boards_3d_[i]->charuco_board_->getDictionary(), charuco_params_); + detector.detectMarkers(image, marker_corners[i], marker_idx[i]); if (marker_corners[i].size() > 0) { cv::aruco::interpolateCornersCharuco(marker_corners[i], marker_idx[i], @@ -1454,9 +1468,10 @@ void Calibration::initNonOverlapPair(const int cam_group_id1, // HANDEYE CALIBRATION cv::Mat pose_g1_g2; if (he_approach_ == 0) { - // Boot strapping technique - int nb_cluster = 20; - int nb_it_he = 200; // Nb of time we apply the handeye calibration + // Bootstrapping technique + const unsigned int nb_cluster = 20u; + // number of iterations of handeye calibration + const unsigned int nb_it_he = 200u; pose_g1_g2 = handeyeBootstratpTranslationCalibration( nb_cluster, nb_it_he, pose_abs_1, pose_abs_2); } else { diff --git a/McCalib/src/Object3D.cpp b/McCalib/src/Object3D.cpp index 9e7a3b3f..bb0802db 100644 --- a/McCalib/src/Object3D.cpp +++ b/McCalib/src/Object3D.cpp @@ -11,6 +11,8 @@ #include "geometrytools.hpp" #include "logger.h" +namespace McCalib { + /** * @brief Insert a new object observation for this 3D object * @@ -262,3 +264,5 @@ void Object3D::updateObjectPts() { } } } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/Object3DObs.cpp b/McCalib/src/Object3DObs.cpp index 50cd8a3a..94f0bdb3 100644 --- a/McCalib/src/Object3DObs.cpp +++ b/McCalib/src/Object3DObs.cpp @@ -9,6 +9,8 @@ #include "geometrytools.hpp" #include "logger.h" +namespace McCalib { + /** * @brief initialize the object in the observation (what board is observed) * @@ -285,3 +287,5 @@ float Object3DObs::computeReprojectionError() const { return error_object_vec.size() > 0 ? sum_err_object / error_object_vec.size() : sum_err_object; } + +} // namespace McCalib \ No newline at end of file diff --git a/McCalib/src/geometrytools.cpp b/McCalib/src/geometrytools.cpp index 3176e044..1299dbee 100644 --- a/McCalib/src/geometrytools.cpp +++ b/McCalib/src/geometrytools.cpp @@ -1,11 +1,15 @@ #include "opencv2/core/core.hpp" +#include #include #include +#include #include #include "geometrytools.hpp" #include "logger.h" +namespace McCalib { + // Tools for rotation and projection matrix cv::Mat RT2Proj(cv::Mat R, cv::Mat T) { cv::Mat Proj = (cv::Mat_(4, 4) << 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, @@ -165,7 +169,9 @@ void ransacTriangulation(std::vector point2d, // Ransac iterations while (N > trialcount && countit < it) { // pick 2 points - std::random_shuffle(myvector.begin(), myvector.end()); + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(myvector.begin(), myvector.end(), g); std::array idx = {myvector[0], myvector[1], myvector[2], myvector[3]}; @@ -245,9 +251,10 @@ cv::Mat ransacP3P(const std::vector &scenePoints, // Ransac iterations while (N > trialcount && countit < it) { - // pick 4 points - std::random_shuffle(myvector.begin(), myvector.end()); + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(myvector.begin(), myvector.end(), g); std::array idx = {myvector[0], myvector[1], myvector[2], myvector[3]}; @@ -350,14 +357,14 @@ std::vector transform3DPts(std::vector pts3D, * */ -cv::Mat handeyeCalibration(const std::vector pose_abs_1, - const std::vector pose_abs_2) { +cv::Mat handeyeCalibration(const std::vector &pose_abs_1, + const std::vector &pose_abs_2) { // Prepare the poses for handeye calibration - const size_t num_poses = pose_abs_1.size(); + const size_t num_poses = std::min(pose_abs_1.size(), pose_abs_2.size()); std::vector r_cam_group_1(num_poses), t_cam_group_1(num_poses), r_cam_group_2(num_poses), t_cam_group_2(num_poses); - for (size_t i = 0; i < num_poses; i++) { + for (std::size_t i = 0; i < num_poses; i++) { // get the poses cv::Mat pose_cam_group_1 = pose_abs_1[i].inv(); cv::Mat pose_cam_group_2 = pose_abs_2[i]; @@ -386,26 +393,14 @@ cv::Mat handeyeCalibration(const std::vector pose_abs_1, } /** - * @brief Calibrate 2 cameras with handeye calibration + * @brief Prepare the translational component of the cameras to be clustered * - * In this function only N pairs of images are used - * These pair of images are selected with a clustering technique - * The clustering is achieved via the translation of cameras - * The process is repeated multiple time on subset of the poses - * A test of consistency is performed, all potentially valid poses are saved - * The mean value of valid poses is returned */ -cv::Mat handeyeBootstratpTranslationCalibration( - unsigned int nb_cluster, unsigned int nb_it, - std::vector pose_abs_1, std::vector pose_abs_2) { - // N clusters but less if less images available - nb_cluster = - (pose_abs_1.size() < nb_cluster) ? pose_abs_1.size() : nb_cluster; - - // Prepare the translational component of the cameras to be clustered - cv::Mat position_1_2; // concatenation of the translation of pose 1 and 2 for - // clustering - for (unsigned int i = 0; i < pose_abs_1.size(); i++) { +cv::Mat getTranslationsForClustering(const std::vector &pose_abs_1, + const std::vector &pose_abs_2, + const std::size_t num_poses) { + cv::Mat position_1_2; + for (std::size_t i = 0u; i < num_poses; i++) { cv::Mat trans_1, trans_2, rot_1, rot_2; Proj2RT(pose_abs_1[i], rot_1, trans_1); Proj2RT(pose_abs_2[i], rot_2, trans_2); @@ -414,98 +409,161 @@ cv::Mat handeyeBootstratpTranslationCalibration( position_1_2.push_back(concat_trans_1_2); } position_1_2.convertTo(position_1_2, CV_32F); + return position_1_2; +} - // Cluster the observation to select the most diverse poses +cv::Mat clusterTranslations(const cv::Mat &position_1_2, + const unsigned int num_clusters) { + const unsigned int nb_kmean_iterations = 5; cv::Mat labels; cv::Mat centers; - int nb_kmean_iterations = 5; std::ignore = - cv::kmeans(position_1_2, nb_cluster, labels, + cv::kmeans(position_1_2, num_clusters, labels, cv::TermCriteria( cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 10, 0.01), nb_kmean_iterations, cv::KMEANS_PP_CENTERS, centers); labels.convertTo(labels, CV_32S); + return labels; +} + +std::vector selectClusters(const unsigned int num_clusters, + const unsigned int nb_clust_pick) { + // pick from n clusters randomly + std::vector shuffled_ind(num_clusters); + std::iota(shuffled_ind.begin(), shuffled_ind.end(), 0); + assert(shuffled_ind.size() == num_clusters); + + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(shuffled_ind.begin(), shuffled_ind.end(), g); + std::vector cluster_select; + cluster_select.reserve(nb_clust_pick); + for (unsigned int k = 0; k < nb_clust_pick; ++k) { + cluster_select.push_back(shuffled_ind[k]); + } + + assert(cluster_select.size() == nb_clust_pick); + return cluster_select; +} + +std::vector +selectPoses(const cv::Mat &clusters_lables, + const std::vector &selected_cluster_idxs, + const unsigned int num_poses) { + // Select one pair of pose (indexes) from each cluster + std::vector selected_poses_idxs; + selected_poses_idxs.reserve(selected_cluster_idxs.size()); + for (const unsigned int cluster_idx : selected_cluster_idxs) { + std::vector pose_idxs; + for (unsigned int pose_idx = 0u; pose_idx < num_poses; pose_idx++) { + if (clusters_lables.at(pose_idx) == cluster_idx) { + pose_idxs.push_back(pose_idx); + } + } + // randomly select an index in the occurrences of the cluster + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, pose_idxs.size() - 1); + unsigned int random_idx = dis(gen); + selected_poses_idxs.push_back(pose_idxs[random_idx]); + } + + assert(selected_poses_idxs.size() == selected_cluster_idxs.size()); + return selected_poses_idxs; +} + +void preparePosesForHandEyeCalibration( + const std::vector &pose_abs_1, + const std::vector &pose_abs_2, + const std::vector &selected_poses_idxs, + std::vector &r_cam_group_1, std::vector &t_cam_group_1, + std::vector &r_cam_group_2, std::vector &t_cam_group_2) { + const std::size_t nb_clust_pick = selected_poses_idxs.size(); + r_cam_group_1.reserve(nb_clust_pick); + t_cam_group_1.reserve(nb_clust_pick); + r_cam_group_2.reserve(nb_clust_pick); + t_cam_group_2.reserve(nb_clust_pick); + for (const unsigned int pose_ind_i : selected_poses_idxs) { + // get the poses + const cv::Mat pose_cam_group_1 = pose_abs_1[pose_ind_i].inv(); + const cv::Mat pose_cam_group_2 = pose_abs_2[pose_ind_i]; + + // save in datastruct + cv::Mat r_1, r_2, t_1, t_2; + Proj2RT(pose_cam_group_1, r_1, t_1); + Proj2RT(pose_cam_group_2, r_2, t_2); + cv::Mat r_1_mat, r_2_mat; + cv::Rodrigues(r_1, r_1_mat); + cv::Rodrigues(r_2, r_2_mat); + r_cam_group_1.push_back(r_1_mat); + t_cam_group_1.push_back(t_1); + r_cam_group_2.push_back(r_2_mat); + t_cam_group_2.push_back(t_2); + } +} + +/** + * @brief Calibrate 2 cameras with handeye calibration + * + * In this function only N pairs of images are used + * These pair of images are selected with a clustering technique + * The clustering is achieved via the translation of cameras + * The process is repeated multiple time on subset of the poses + * A test of consistency is performed, all potentially valid poses are saved + * The mean value of valid poses is returned + */ +cv::Mat handeyeBootstratpTranslationCalibration( + const unsigned int nb_cluster, const unsigned int nb_it, + const std::vector &pose_abs_1, + const std::vector &pose_abs_2) { + + // concat of the translation of pose 1 and 2 for clustering + const std::size_t num_poses = std::min(pose_abs_1.size(), pose_abs_2.size()); + cv::Mat position_1_2 = + getTranslationsForClustering(pose_abs_1, pose_abs_2, num_poses); + + // Cluster the observation to select the most diverse poses + const unsigned int num_clusters = + std::min(nb_cluster, static_cast( + std::min(pose_abs_1.size(), pose_abs_2.size()))); + const cv::Mat clusters_lables = + clusterTranslations(position_1_2, num_clusters); // Iterate n times the std::vector r1_he, r2_he, r3_he; // structure to save valid rot std::vector t1_he, t2_he, t3_he; // structure to save valid trans - unsigned int nb_clust_pick = 6; + const unsigned int nb_clust_pick = 6; unsigned int nb_success = 0; for (unsigned int iter = 0; iter < nb_it; iter++) { + const std::vector selected_cluster_idxs = + selectClusters(num_clusters, nb_clust_pick); - // pick from n of these clusters randomly - std::vector shuffled_ind(nb_cluster); - std::iota(shuffled_ind.begin(), shuffled_ind.end(), 0); - std::random_device rd; // initialize random number generator - std::mt19937 g(rd()); - std::shuffle(shuffled_ind.begin(), shuffled_ind.end(), g); - std::vector cluster_select; - cluster_select.reserve(nb_clust_pick); - for (unsigned int k = 0; k < nb_clust_pick; ++k) { - cluster_select.push_back(shuffled_ind[k]); - } - - // Select one pair of pose for each cluster - std::vector pose_ind; - pose_ind.reserve(cluster_select.size()); - for (const unsigned int &clust_ind : cluster_select) { - std::vector idx; - for (unsigned int j = 0; j < pose_abs_2.size(); j++) { - if (labels.at(j) == clust_ind) { - idx.push_back(j); - } - } - // randomly select an index in the occurrences of the cluster - srand(time(NULL)); - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> dis(0, idx.size() - 1); - unsigned int cluster_idx = dis(gen); - pose_ind.push_back(idx[cluster_idx]); - } + const std::vector selected_poses_idxs = + selectPoses(clusters_lables, selected_cluster_idxs, num_poses); // Prepare the poses for handeye calibration std::vector r_cam_group_1, t_cam_group_1, r_cam_group_2, t_cam_group_2; - r_cam_group_1.reserve(pose_ind.size()); - t_cam_group_1.reserve(pose_ind.size()); - r_cam_group_2.reserve(pose_ind.size()); - t_cam_group_2.reserve(pose_ind.size()); - for (const auto &pose_ind_i : pose_ind) { - // get the poses - cv::Mat pose_cam_group_1 = pose_abs_1[pose_ind_i].inv(); - cv::Mat pose_cam_group_2 = pose_abs_2[pose_ind_i]; - - // save in datastruct - cv::Mat r_1, r_2, t_1, t_2; - Proj2RT(pose_cam_group_1, r_1, t_1); - Proj2RT(pose_cam_group_2, r_2, t_2); - cv::Mat r_1_mat, r_2_mat; - cv::Rodrigues(r_1, r_1_mat); - cv::Rodrigues(r_2, r_2_mat); - r_cam_group_1.push_back(r_1_mat); - t_cam_group_1.push_back(t_1); - r_cam_group_2.push_back(r_2_mat); - t_cam_group_2.push_back(t_2); - } + preparePosesForHandEyeCalibration( + pose_abs_1, pose_abs_2, selected_poses_idxs, r_cam_group_1, + t_cam_group_1, r_cam_group_2, t_cam_group_2); // Hand-eye calibration cv::Mat r_g1_g2, t_g1_g2; cv::calibrateHandEye(r_cam_group_1, t_cam_group_1, r_cam_group_2, t_cam_group_2, r_g1_g2, t_g1_g2, cv::CALIB_HAND_EYE_TSAI); - // cv::CALIB_HAN cv::Mat pose_g1_g2 = RT2Proj(r_g1_g2, t_g1_g2); // Check the consistency of the set double max_error = 0; - for (unsigned int i = 0; i < pose_ind.size(); i++) { - cv::Mat pose_cam_group_1_1 = pose_abs_1[pose_ind[i]]; - cv::Mat pose_cam_group_2_1 = pose_abs_2[pose_ind[i]]; - for (unsigned int j = 0; j < pose_ind.size(); j++) { + for (unsigned int i = 0; i < selected_poses_idxs.size(); i++) { + cv::Mat pose_cam_group_1_1 = pose_abs_1[selected_poses_idxs[i]]; + cv::Mat pose_cam_group_2_1 = pose_abs_2[selected_poses_idxs[i]]; + for (unsigned int j = 0; j < selected_poses_idxs.size(); j++) { if (i != j) { - cv::Mat pose_cam_group_1_2 = pose_abs_1[pose_ind[i]]; - cv::Mat pose_cam_group_2_2 = pose_abs_2[pose_ind[i]]; + cv::Mat pose_cam_group_1_2 = pose_abs_1[selected_poses_idxs[i]]; + cv::Mat pose_cam_group_2_2 = pose_abs_2[selected_poses_idxs[i]]; cv::Mat PP1 = pose_cam_group_1_2.inv() * pose_cam_group_1_1; cv::Mat PP2 = pose_cam_group_2_1.inv() * pose_cam_group_2_2; cv::Mat ErrMat = PP2.inv() * pose_g1_g2 * PP1 * pose_g1_g2; @@ -516,14 +574,13 @@ cv::Mat handeyeBootstratpTranslationCalibration( double traceRot = cv::trace(ErrRotMat)[0] - std::numeric_limits::epsilon(); double err_degree = std::acos(0.5 * (traceRot - 1.0)) * 180.0 / M_PI; - if (err_degree > max_error) - max_error = err_degree; + max_error = std::max(max_error, err_degree); } } } if (max_error < 15) { nb_success++; - // if it is a sucess then add to our valid pose evector + // if it is a sucess then add to our valid pose vector cv::Mat rot_temp, trans_temp; Proj2RT(pose_g1_g2, rot_temp, trans_temp); r1_he.push_back(rot_temp.at(0)); @@ -544,8 +601,8 @@ cv::Mat handeyeBootstratpTranslationCalibration( t_he.at(2) = median(t3_he); cv::Mat pose_g1_g2 = RVecT2Proj(r_he, t_he); return pose_g1_g2; - } else // else run the normal handeye calibration on all the samples - { + } else { + LOG_DEBUG << "Run the normal handeye calibration on all the samples"; cv::Mat pose_g1_g2 = handeyeCalibration(pose_abs_1, pose_abs_1); return pose_g1_g2; } @@ -553,9 +610,12 @@ cv::Mat handeyeBootstratpTranslationCalibration( // median of the vector but modifies original vector double median(std::vector &v) { - size_t n = v.size() / 2; - std::nth_element(v.begin(), v.begin() + n, v.end()); - return v[n]; + const std::size_t n_elements = v.size(); + if (n_elements == 0) + return 0.f; + std::sort(v.begin(), v.end()); + const std::size_t mid = n_elements / 2; + return n_elements % 2 == 0 ? (v[mid] + v[mid - 1]) / 2 : v[mid]; } // RANSAC algorithm @@ -744,4 +804,6 @@ cv::Mat getAverageRotation(std::vector &r1, std::vector &r2, } return average_rotation; -} \ No newline at end of file +} + +} // namespace McCalib \ No newline at end of file diff --git a/README.md b/README.md index 916a31e6..fdc331d4 100755 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Toolbox described in the paper ["MC-Calib: A generic and robust calibration tool # Installation -Requirements: Ceres, Boost, OpenCV {4.2.x, 4.5.x}, c++17 +Requirements: c++17, Ceres==2.2.0, Boost==1.83, OpenCV==4.10.0 For Windows users, follow [this installation guide](/docs/Windows.md) @@ -20,8 +20,8 @@ There are several ways to get the environment ready. Choose any of them: - Pull the image: ```bash - docker pull bailool/mc-calib-prod # production environment - docker pull bailool/mc-calib-dev # development environment + docker pull bailool/mc-calib-prod:opencv410 # production environment + docker pull bailool/mc-calib-dev:opencv410 # development environment ``` - Run pulled image (set `PATH_TO_REPO_ROOT` and `PATH_TO_DATA` appropriately): @@ -36,11 +36,11 @@ There are several ways to get the environment ready. Choose any of them: 2. It is also possible to build the docker environment manually (see [instructions](/docs/Docker.md)) -3. Alternatively, every dependency can be installed independently without docker: +3. Alternatively, every dependency can be installed manually without docker: - - [Install](https://docs.opencv.org/4.5.2/d7/d9f/tutorial_linux_install.html) OpenCV 4.5.x. Either instal system-wide with `sudo make install` or link to your `build` in `CmakeLists.txt`. + - [Install](https://docs.opencv.org/4.x/d7/d9f/tutorial_linux_install.html) OpenCV - - Follow [installation guidelines](http://ceres-solver.org/installation.html#linux) to install Ceres. + - Follow [installation guidelines](http://ceres-solver.org/installation.html#linux) to install Ceres - Install boost: diff --git a/apps/create_charuco_boards/src/create_charuco_boards.cpp b/apps/create_charuco_boards/src/create_charuco_boards.cpp index 85c5905d..60d0ae89 100644 --- a/apps/create_charuco_boards/src/create_charuco_boards.cpp +++ b/apps/create_charuco_boards/src/create_charuco_boards.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "boost/filesystem.hpp" @@ -59,27 +60,38 @@ int main(int argc, char *argv[]) { } // Create the charuco - cv::Ptr dict = + const cv::aruco::Dictionary dict = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_1000); - std::vector> charucoBoards; + std::vector charuco_boards; int offset_count = 0; for (std::size_t i = 0; i < num_board; i++) { - // declare the board - cv::Ptr charuco = cv::aruco::CharucoBoard::create( - number_x_square_per_board[i], number_y_square_per_board[i], - length_square, length_marker, dict); - // If it is the first board then just use the standard idx - if (i != 0) { - int id_offset = charucoBoards[i - 1]->ids.size() + offset_count; + if (i == 0) { + // if it is the first board then just use the standard idx + cv::aruco::CharucoBoard charuco = cv::aruco::CharucoBoard( + cv::Size(number_x_square_per_board[i], number_y_square_per_board[i]), + length_square, length_marker, dict); + charuco.setLegacyPattern(true); + + charuco_boards.push_back(charuco); + } else { + int id_offset = charuco_boards[i - 1].getIds().size() + offset_count; offset_count = id_offset; - for (auto &id : charuco->ids) { - id += id_offset; - } + + const std::size_t num_idxs = charuco_boards[i - 1].getIds().size(); + std::vector cur_ids(num_idxs); + std::iota(cur_ids.begin(), cur_ids.end(), id_offset); + + cv::aruco::CharucoBoard charuco = cv::aruco::CharucoBoard( + cv::Size(number_x_square_per_board[i], number_y_square_per_board[i]), + length_square, length_marker, dict, cur_ids); + charuco.setLegacyPattern(true); + + charuco_boards.push_back(charuco); } + // create the charuco board - charucoBoards.push_back(charuco); cv::Mat boardImage; - charucoBoards[i]->draw( + charuco_boards[i].generateImage( cv::Size(resolution_x_per_board[i], resolution_y_per_board[i]), boardImage, 10, 1); // Display marker diff --git a/docs/Documentation.md b/docs/Documentation.md index fa463350..7881013d 100644 --- a/docs/Documentation.md +++ b/docs/Documentation.md @@ -4,7 +4,7 @@ - It is also possible to generate Doxygen documentation locally: - - Install [Doxygen](https://www.doxygen.nl/download.html) or use `bailool/mc-calib-dev` docker image: + - Install [Doxygen](https://www.doxygen.nl/download.html) or use `bailool/mc-calib-dev:opencv410` docker image: ```bash sudo apt install flex diff --git a/docs/Windows.md b/docs/Windows.md index c62fe8c3..90a57b65 100644 --- a/docs/Windows.md +++ b/docs/Windows.md @@ -28,8 +28,8 @@ This documentation is meant to guide the installation of the MC-Calib toolbox, s Then pull the docker image using either one of the commands given below. ```bash -docker pull bailool/mc-calib-prod # production environment -docker pull bailool/mc-calib-dev # development environment +docker pull bailool/mc-calib-prod:opencv410 # production environment +docker pull bailool/mc-calib-dev:opencv410 # development environment ``` 4. **Running Pulled Image using Docker** @@ -41,7 +41,7 @@ Docker run ` -ti --rm ` --volume=”$PATH_TO_REPO_ROOT:/home/MC-Calib” ` --volume=”$PATH_TO_DATA:/home/MC-Calib/data” ` - bailool/mc-calib-prod + bailool/mc-calib-prod:opencv410 ``` ### User Personalization diff --git a/python_utils/requirements_dev.txt b/python_utils/requirements_dev.txt index 2f2858b1..b5cb3329 100644 --- a/python_utils/requirements_dev.txt +++ b/python_utils/requirements_dev.txt @@ -1,4 +1,4 @@ isort==5.13.2 -black==24.4.2 -mypy==1.10.0 -pylint==3.2.2 \ No newline at end of file +black==24.8.0 +mypy==1.11.2 +pylint==3.2.6 \ No newline at end of file diff --git a/python_utils/requirements_prod.txt b/python_utils/requirements_prod.txt index 158559c4..fba3388c 100755 --- a/python_utils/requirements_prod.txt +++ b/python_utils/requirements_prod.txt @@ -1,3 +1,3 @@ -numpy==1.24.4 -matplotlib==3.7.5 -opencv-python==4.9.0.80 +numpy==2.1.0 +matplotlib==3.9.2 +opencv-python==4.10.0.84 diff --git a/tests/simple_unit_tests_example.cpp b/tests/simple_unit_tests_example.cpp index 74a38d14..53a5dc71 100644 --- a/tests/simple_unit_tests_example.cpp +++ b/tests/simple_unit_tests_example.cpp @@ -10,7 +10,7 @@ BOOST_AUTO_TEST_SUITE(CheckGeometryTools) BOOST_AUTO_TEST_CASE(ProjToVecAllZeros) { cv::Mat proj_matrix = (cv::Mat_(4, 4) << 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); - std::array output = ProjToVec(proj_matrix); + std::array output = McCalib::ProjToVec(proj_matrix); std::array answer = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; diff --git a/tests/test_calibration.cpp b/tests/test_calibration.cpp index 9b1ba430..d90101d5 100644 --- a/tests/test_calibration.cpp +++ b/tests/test_calibration.cpp @@ -80,7 +80,7 @@ void calibrateAndCheckGt(std::string config_path, std::string gt_path) { camera_pose_matrix_gt = transform * camera_pose_matrix_gt * transform; double fx_gt = camera_matrix_gt.at(0, 0); - double fy_gt = camera_matrix_gt.at(1, 0); + double fy_gt = camera_matrix_gt.at(1, 1); double cx_gt = camera_matrix_gt.at(0, 2); double cy_gt = camera_matrix_gt.at(1, 2); cv::Mat rot_gt(3, 3, CV_64F); @@ -89,14 +89,14 @@ void calibrateAndCheckGt(std::string config_path, std::string gt_path) { tran_gt = camera_pose_matrix_gt(cv::Range(0, 3), cv::Range(3, 4)); // get calibrated values - std::shared_ptr cur_cam = Calib.cams_[camera_idx - 1]; + std::shared_ptr cur_cam = Calib.cams_[camera_idx - 1]; int camera_group_idx = 0; // specific to the setup with single camera group cv::Mat camera_matrix_pred = cur_cam->getCameraMat(); cv::Mat camera_pose_matrix_pred = Calib.cam_group_[camera_group_idx]->getCameraPoseMat(camera_idx - 1); double fx_pred = camera_matrix_pred.at(0, 0); - double fy_pred = camera_matrix_pred.at(1, 0); + double fy_pred = camera_matrix_pred.at(1, 1); double cx_pred = camera_matrix_pred.at(0, 2); double cy_pred = camera_matrix_pred.at(1, 2); cv::Mat rot_pred(3, 3, CV_64F); @@ -123,6 +123,10 @@ BOOST_AUTO_TEST_CASE(CheckBlenderDatasetIsPlacedCorrectly) { std::string blender_images_path = "../data/Blender_Images"; bool is_path_existent = boost::filesystem::exists(blender_images_path); BOOST_REQUIRE_EQUAL(is_path_existent, true); + + std::cout << cv::getVersionMajor() << std::endl; + std::cout << cv::getVersionMinor() << std::endl; + std::cout << cv::getVersionRevision() << std::endl; } BOOST_AUTO_TEST_CASE(CheckCalibrationSyntheticScenario1) { diff --git a/tests/test_geometrytools.cpp b/tests/test_geometrytools.cpp index 3b4be207..e6c0ea79 100644 --- a/tests/test_geometrytools.cpp +++ b/tests/test_geometrytools.cpp @@ -9,7 +9,8 @@ double INTRINSICS_TOLERANCE = 1.0; // in percentage BOOST_AUTO_TEST_CASE(CheckRotationMatrixToQuaternionConversion1) { std::array rot_matrix_data = {1, 0, 0, 0, 1, 0, 0, 0, 1}; const cv::Mat rot_matrix = cv::Mat(3, 3, CV_64F, rot_matrix_data.data()); - const cv::Mat quaternion_pred = convertRotationMatrixToQuaternion(rot_matrix); + const cv::Mat quaternion_pred = + McCalib::convertRotationMatrixToQuaternion(rot_matrix); std::array quaternion_gt_data = {0, 0, 0, 1}; const cv::Mat quaternion_gt = @@ -28,7 +29,8 @@ BOOST_AUTO_TEST_CASE(CheckRotationMatrixToQuaternionConversion2) { 0.1998, 0.6000, -0.8000, 0.3996, -0.8000, -0.5999}; const cv::Mat rot_matrix = cv::Mat(3, 3, CV_64F, rot_matrix_data.data()); - const cv::Mat quaternion_pred = convertRotationMatrixToQuaternion(rot_matrix); + const cv::Mat quaternion_pred = + McCalib::convertRotationMatrixToQuaternion(rot_matrix); std::array quaternion_gt_data = {0, 0.8944, -0.4472, -0.2234}; // x y z w @@ -48,7 +50,8 @@ BOOST_AUTO_TEST_CASE(CheckQuaternionToRotationMatrixConversion) { -0.2092473}; // x y z w const cv::Mat quaternion = cv::Mat(1, 4, CV_64F, quaternion_data.data()); - cv::Mat rot_matrix_pred = convertQuaternionToRotationMatrix(quaternion); + cv::Mat rot_matrix_pred = + McCalib::convertQuaternionToRotationMatrix(quaternion); std::array rot_matrix_gt_data = { -0.7545152, 0.2955056, -0.5859892, 0.6460947, 0.4911810, @@ -77,9 +80,9 @@ BOOST_AUTO_TEST_CASE(CheckRotationMatrixToQuaternionAndBackConversion) { cv::Mat rot_matrix_before; cv::Rodrigues(rot_vec, rot_matrix_before); const cv::Mat quaternion = - convertRotationMatrixToQuaternion(rot_matrix_before); + McCalib::convertRotationMatrixToQuaternion(rot_matrix_before); cv::Mat rot_matrix_after = - convertQuaternionToRotationMatrix(quaternion); + McCalib::convertQuaternionToRotationMatrix(quaternion); BOOST_REQUIRE_EQUAL(rot_matrix_after.size[0], rot_matrix_before.size[0]);