GithubHelp home page GithubHelp logo

megviirobot / megba Goto Github PK

View Code? Open in Web Editor NEW
444.0 21.0 60.0 1.33 MB

MegBA: A GPU-Based Distributed Library for Large-Scale Bundle Adjustment

License: Apache License 2.0

CMake 3.61% C++ 36.39% Cuda 60.00%
high-performance distributed bundleadjustment gpu-acceleration cuda graph-optimization

megba's Introduction

MegBA: A High-Performance and Distributed Library for Large-Scale Bundle Adjustment

This repo contains an official implementation of MegBA.

MegBA is a fast and distributed library for large-scale Bundle Adjustment (BA). MegBA has a novel end-to-end vectorised BA algorithm which can fully exploit the massive parallel cores on GPUs, thus speeding up the entire BA computation. It also has a novel distributed BA algorithm that can automatically partition BA problems, and solve BA sub-problems using distributed GPUs. The GPUs synchronise intermediate solving state using network-efficient collective communication, and the synchronisation is designed to minimise communication cost. MegBA has a memory-efficient GPU runtime and it exposes g2o-compatible APIs. Experiments show that MegBA can out-perform state-of-the-art BA libraries (i.e., Ceres and DeepLM) by ~50x and ~5x respectively, in public large-scale BA benchmarks.

Version

  • 2021/12/06 Beta version released! It corresponds to this paper
  • 2022/02/18 Stable version released! We have refactored MegBA and fixed some existing bugs, e.g., incorrect rollback in the LM reject step.
  • 2022/02/25 Analytical differentiation module available; We also provide BAL_X_analytical.cpp under examples/. Compared with automatic diff, time and space are reduced by ~30% and ~40%, respectively.

Todo

  • memory-efficient version with implicit Hessian (TBD)
  • IMU factor, prior factor (TBD)

Paper: https://arxiv.org/abs/2112.01349 (updated version)

Quickstart

Dependencies:

You can also easily install all dependencies with script: script

Demo with BAL dataset:

  • Download any pre.txt.bz2 file from BAL Dataset: https://grail.cs.washington.edu/projects/bal/ and uncompressed.

  • Compile

    If you want to use the distributed feature, use cmake -DMEGBA_ENABLE_NCCL .. instead of cmake ...

    git submodule update --init
    mkdir build
    cd build
    cmake ..  # enable nccl by using cmake -DMEGBA_ENABLE_NCCL ..
    make -j4 BAL_Double
  • Run the demo (Venice-1778)

    cd examples
    ./BAL_Double --path /path/to/your/dataset --world_size 2 --max_iter 100 --solver_tol 1e-1 --solver_refuse_ratio 1 --solver_max_iter 100 --tau 1e4 --epsilon1 1 --epsilon2 1e-10
    • world_size: number of GPUs available
    • max_iter: the maximal number of LM iteration
    • epsilon1 & epsilon2: threshold in LM
    • solver_tol: tolerance of solver (distributed PCG solver)
    • solver_refuse_ratio: early stop for the solver
    • solver_max_iter: the maximal iteration of solver
    • tau: the initial region

Notes for the practitioners

  • Currently, MegBA implements automatic differentiation only for generalizability. Please consider implementing your own analytical differentiation module. Analytical differentiation module is provided.
  • If you use devices without modern inter-device communication (i.e., NVLinks..), you might find the data transfer is the bottleneck.
  • Empirically, we found it is necessary to customize the LM trust-region strategies and tune its hyper-parameters to further boost the performance.

Documentation

Under doc/ (Coming soon...)

Collaborate with Us

Please check here for MegBA's future plan.

If you are interested in MegBA and want to collaborate, you can:

  • Sorry, we can no longer host Interns.
  • As an external collaborator (coding), just fork this repo and send PRs. We will review your PR carefully (and merge it into MegBA).
  • As an algorithm/novelty contributor, please send an email to [email protected].
  • Any new feature request, you can send an email to [email protected] as well. Note that it is not guaranteed the requested feature will be added or added soon

Contact Information:

BibTeX Citation

If you find MegBA useful for your project, please consider citing:

@inproceedings{2021megba,
  title={MegBA: A GPU-Based Distributed Library for Large-Scale Bundle Adjustment}, 
  author={Jie Ren and Wenteng Liang and Ran Yan and Luo Mai and Shiwen Liu and Xiao Liu},
  booktitle={European Conference on Computer Vision},
  year={2022}
}

License

MegBA is licensed under the Apache License, Version 2.0.

megba's People

Contributors

bitterengsci avatar jieren98 avatar khshmt avatar ramadanahmed avatar went-liang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

megba's Issues

CMake Modernization

Hi Everyone,

I'm very interested in this project. I've primarily been a user of Colmap, but have been searching for a way to speed up the global bundle adjustment. I only came across the paper by accident. The results look very promising! I'd like to contribute where I can.

Would you mind if I spent a few minutes modernizing the CMake?

memory leak when solve multi-time GBA

Hi thanks for your amazing work and open sourced code

Start with error: 0.105238, log error: -0.977827, elapsed 4 ms
CUDA error 700 [/usr/local/cuda-11.3/include/cub/block/../iterator/../util_device.cuh, 635]: an illegal memory access was encountered
CUDA error 700 [/usr/local/cuda-11.3/include/thrust/system/cuda/detail/extrema.h, 210]: an illegal memory access was encountered
terminate called after throwing an instance of 'thrust::system::system_error'
  what():  extrema failed on 1st step: cudaErrorIllegalAddress: an illegal memory access was encountered

I have a cuda error 700, when I construct solver and solve GBA multi-times, this error would happen. It seems that there is some raw pointer error. Could you plz to help me for this problem?

fatal error: nccl.h: No such file or directory

In file included from /home/pengshuxue/Downloads/MegBA/src/algo/lm_algo.cu:15:
/home/pengshuxue/Downloads/MegBA/include/resource/handle_manager.h:11:10: fatal error: nccl.h: No such file or directory
11 | #include <nccl.h>
| ^~~~~~~~
compilation terminated.
CMake Error at lm_algo_CUDA_generated_lm_algo.cu.o.cmake:220 (message):
Error generating
/home/pengshuxue/Downloads/MegBA/build/src/algo/CMakeFiles/lm_algo_CUDA.dir//./lm_algo_CUDA_generated_lm_algo.cu.o

make[2]: *** [src/algo/CMakeFiles/lm_algo_CUDA.dir/build.make:65: src/algo/CMakeFiles/lm_algo_CUDA.dir/lm_algo_CUDA_generated_lm_algo.cu.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:340: src/algo/CMakeFiles/lm_algo_CUDA.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

Mrs J, what should I do?

feedback of demo

Hello, Your work is pretty well that compared with ceres. I tried your demo of BAL_Float with Venice-52. I noticed that only few iteration of LM sucessed and when I print the deltaXL2 and xL2, the number is very big .

solving problem-52-64053-pre.txt, world_size: 1, solver iter: 100, solver_tol: 0.01, solver_refuse_ratio: 1, solver_max_iter: 100, tau: 10000, epsilon1: 1, epsilon2: 1e-10
52 64053 347173
0 1 2 3 4 start with error: 1.11521e+07, log error: 7.04736
deltaXL2 = 795.057990 xL2 = 20297.274566
1-th iter error: 967452, log error: 5.98563
deltaXL2 = 641.070984 xL2 = 20457.752299
2-th iter error: 504157, log error: 5.70257
deltaXL2 = 900.042953 xL2 = 20451.053379
3th iter rollbackLM
deltaXL2 = 546.620354 xL2 = 20451.053379
4th iter rollbackLM
deltaXL2 = 284.468891 xL2 = 20451.053379
5-th iter error: 400720, log error: 5.60284
deltaXL2 = 374.414253 xL2 = 20427.049189
6-th iter error: 364873, log error: 5.56214
deltaXL2 = 370.487802 xL2 = 20488.023926
7th iter rollbackLM
deltaXL2 = 2287.042375 xL2 = 20488.023926
8th iter rollbackLM
deltaXL2 = 71488.917241 xL2 = 20488.023926
9-th iter error: 362014, log error: 5.55872
deltaXL2 = 137.477325 xL2 = 74207.259595
10-th iter error: 291625, log error: 5.46482
deltaXL2 = 201.875231 xL2 = 74267.585371
11-th iter error: 277062, log error: 5.44258
deltaXL2 = 181.679374 xL2 = 74428.300552
12-th iter error: 272821, log error: 5.43588
deltaXL2 = 195.595084 xL2 = 74569.198498
13th iter rollbackLM
deltaXL2 = 574978812.009305 xL2 = 74569.198498
14th iter rollbackLM
deltaXL2 = 553295309151825.250000 xL2 = 74569.198498
15th iter rollbackLM
deltaXL2 = 67003134385564172288.000000 xL2 = 74569.198498
16th iter rollbackLM
deltaXL2 = 558190831280728100044800.000000 xL2 = 74569.198498
17th iter rollbackLM
deltaXL2 = 223324689881403556926324736.000000 xL2 = 74569.198498
18th iter rollbackLM
deltaXL2 = 1540883170175477071243902976.000000 xL2 = 74569.198498
19th iter rollbackLM
deltaXL2 = 83242234827980244867088384.000000 xL2 = 74569.198498
20th iter rollbackLM
deltaXL2 = 17566485752858001014784.000000 xL2 = 74569.198498
21th iter rollbackLM
deltaXL2 = 7240292771779925.000000 xL2 = 74569.198498
22th iter rollbackLM
deltaXL2 = 1833498346.973379 xL2 = 74569.198498
23th iter rollbackLM
deltaXL2 = 895261.733858 xL2 = 74569.198498
24th iter rollbackLM
deltaXL2 = 218.569759 xL2 = 74569.198498
25th iter rollbackLM
deltaXL2 = 0.026681 xL2 = 74569.198498
26-th iter error: 272818, log error: 5.43587
deltaXL2 = 0.000000 xL2 = 74569.197054
deltaXL2 <= epsilon2*(xL2+epsilon2) deltaXL2 = 0.000000 xL2 = 74569.197054
solve dur: 3219 ms

dentifier "CUSPARSE_SPMV_ALG_DEFAULT" is undefined

home/pengshuxue/Downloads/MegBA/src/solver/schur_pcg_solver.cu(470): error: identifier "CUSPARSE_SPMV_ALG_DEFAULT" is undefined
detected during:
instantiation of "__nv_bool MegBA::::SchurPCGSolverDistributed(const MegBA::SolverOption::SolverOptionPCG &, const std::vector<T *, std::allocator<T *>> &, const std::vector<T *, std::allocator<T *>> &, const std::vector<T *, std::allocator<T *>> &, const std::vector<int *, std::allocator<int *>> &, const std::vector<int *, std::allocator<int *>> &, const std::vector<T *, std::allocator<T *>> &, const std::vector<int *, std::allocator<int *>> &, const std::vector<int *, std::allocator<int *>> &, const std::vector<T *, std::allocator<T *>> &, int, int, int, int, const std::vector<int, std::allocator> &, int, int, const std::vector<T *, std::allocator<T *>> &) [with T=double]"
(658): here
instantiation of "void MegBA::SchurPCGSolver::solve(const MegBA::BaseLinearSystem &) [with T=double]"
(661): here

/home/pengshuxue/Downloads/MegBA/src/solver/schur_pcg_solver.cu(201): error: identifier "CUSPARSE_SPMV_ALG_DEFAULT" is undefined
detected during:
instantiation of "__nv_bool MegBA::::SchurPCGSolverDistributed(const MegBA::SolverOption::SolverOptionPCG &, const std::vector<T *, std::allocator<T *>> &, const std::vector<T *, std::allocator<T *>> &, const std::vector<T *, std::allocator<T *>> &, const std::vector<int *, std::allocator<int *>> &, const std::vector<int *, std::allocator<int *>> &, const std::vector<T *, std::allocator<T *>> &, const std::vector<int *, std::allocator<int *>> &, const std::vector<int *, std::allocator<int *>> &, const

Problem building on Windows

Hi,
Thanks for this great contribution, I'm trying to build the repo on windows but I found out that NCCL is not supported on Windows system so I can't build it.
If there is a idea to build library for single gpu without support of NCCL and adding support to windows I would like to contribute for that

在视觉惯性SLAM系统中的应用

作者您好,请问在ORB-SLAM3和VINS系统里面可以使用这个对BA加速嘛?会不会因为数据量较小导致没有cpu快?MegBA有和g2o的优化速度做过比较嘛,差异有多大?论文似乎未提及g2o的速度,感谢回答。

The error of the misaligned address appears on testing a very small SFM problem.

I test the MegBA using a very small SFM problem. It has two cameras, 600 3D points, and 1200 observations.
The SFM problem has the format of Bundle Adjustment in the Large. BAL
The BAL_Double aborts after the error of " cudaErrorMisalignedAddress: misaligned address".

The input command is
" ./BAL_Double --path ~/Data/problem-2-599-pre.txt --world_size 1 --max_iter 10 --solver_tol 1e-1 --solver_refuse_ratio 1 --solver_max_iter 10 --tau 1e4 --epsilon1 1 --epsilon2 1e-10" .

The whole output on the screen is
"solving /Data/problem-2-599-pre.txt, world_size: 1, max iter: 10, solver_tol: 0.1, solver_refuse_ratio: 1, solver_max_iter: 10, tau: 10000, epsilon1: 1, epsilon2: 1e-10
Start with error: 45932, log error: 4.66212, elapsed 8 ms
terminate called after throwing an instance of 'thrust::system::system_error'
what(): after reduction step 1: cudaErrorMisalignedAddress: misaligned address
Aborted".

But when I remove one 3D point and its 2 observations, the MegBA works.
The edited SFM problem has two cameras, 599 3D points, and 1198 observations.

The output on the screen is
"solving /home/machen/Documents/Data/ColMap_MegBA/problem-2-599-pre.txt, world_size: 1, max iter: 10, solver_tol: 0.1, solver_refuse_ratio: 1, solver_max_iter: 10, tau: 10000, epsilon1: 1, epsilon2: 1e-10
Start with error: 45867, log error: 4.6615, elapsed 8 ms
Iter 1 error: 150.088, log error: 2.17635, elapsed 14 ms
Iter 2 error: 146.737, log error: 2.16654, elapsed 19 ms
Iter 3 error: 145.811, log error: 2.16379, elapsed 24 ms
Iter 4 error: 144.645, log error: 2.1603, elapsed 28 ms
Iter 5 error: 143.325, log error: 2.15632, elapsed 33 ms
Iter 6 error: 142.183, log error: 2.15285, elapsed 38 ms
Iter 7 error: 141.412, log error: 2.15049, elapsed 42 ms
Iter 8 error: 140.979, log error: 2.14915, elapsed 47 ms
Iter 9 error: 140.773, log error: 2.14852, elapsed 51 ms
Iter 10 error: 140.686, log error: 2.14825, elapsed 56 ms
Finished".

Could you give any suggestions about the error?

Cannot Reproduce Results on Venice Dataset

Hello!

Thanks for the amazing work. I was trying to reproduce the results on the Venice 1778 Dataset.

I ran Venice 1778 on Ceres 2.0 and I got the following results in around 101 seconds (on a CPU):

iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time
0 2.563973e+08 0.00e+00 3.19e+15 0.00e+00 0.00e+00 1.00e+04 0 2.83e+00 2.37e+01
1 1.500306e+07 2.41e+08 2.06e+14 1.29e+05 9.48e-01 3.00e+04 1 1.89e+01 4.27e+01
2 1.892564e+06 1.31e+07 2.19e+13 4.68e+05 9.85e-01 9.00e+04 1 2.03e+01 6.30e+01
3 1.739562e+06 1.53e+05 2.43e+14 1.48e+06 7.06e-01 9.68e+04 1 1.52e+01 7.82e+01
4 1.682929e+06 5.66e+04 6.77e+13 1.54e+06 8.04e-01 1.25e+05 1 1.95e+01 9.77e+01
5 1.675773e+06 7.16e+03 8.45e+12 1.78e+06 3.73e-01 1.23e+05 1 1.70e+01 1.15e+02

As you can see, it has the error of 1.675773e+06 and it took 115 seconds to reach it.

I ran the same experiment with MegBA:

Iter 2000 error: 1.9772e+06, log error: 6.29605, elapsed 202173 ms

I tried to get the error in a similar range as Ceres but couldn't get it even after 2000 iterations and 202 seconds.
I've used the same hyperparameters as on MegBA ReadMe.

The system I'm running this on has Nvidia A5000 and A6000 GPUs.

Can you give me an idea of what can I do to get similar results as Ceres?

some feedback of demo

Hello, I tried your demo of BAL_Double.
I have 2 questions of it.

  1. The program will take 100% of my cpu even I have no data (Venice). There is no strategy of data checking.
  2. The program will take 100% of my cpu when I input params world_size==2. But I have only 1 gpu. The program stay at that progress and use my cpu. When I input right gpu number of 1, the program solved problem very fast.
    Finally, I suggest the program should more robust of the main progress. For example, some checking and logs let us know what happened. And I also want to know why it take all of my cpu when nothing to solve.
    Many thanks for watching that! Your work is pretty well that compared with ceres. I think there are still some small problem to solve on the level of program.

sqrt API not exposed

Hi ,really appreciate for your amazing work.

I found a bug when i self-implement a vertex, "cuda 700 error". It seems that the sqrt interface not exposed out.

"misaligned address"

Hi,

I have tried multiple small BAL datasets, multiple examples (Double, Float, analytical) and all end with :

Start with error: 963739, log error: 5.98396, elapsed 3 ms
CUDA error 716 [/usr/local/cuda/targets/x86_64-linux/include/cub/util_device.cuh, 351]: misaligned address
CUDA error 716 [/usr/local/cuda/targets/x86_64-linux/include/cub/util_device.cuh, 433]: misaligned address
CUDA error 716 [/usr/local/cuda/targets/x86_64-linux/include/cub/device/dispatch/dispatch_reduce.cuh, 902]: misaligned address
terminate called after throwing an instance of 'thrust::system::system_error'
  what():  after reduction step 1: cudaErrorMisalignedAddress: misaligned address
Aborted

The error appears to be in l2NormPow2() function (https://github.com/MegviiRobot/MegBA/blob/main/src/algo/lm_algo.cu#L54)

My config:

  • Ubuntu 22.04
  • Cuda 12.2
  • 1x GPU: RTX 2070 (laptop) 8Go VRAM

Could you help me please?
The issue #40 does not help me a lot :)

Thanks !

What if all cameras share the same intrinsics?

Thanks for sharing your excellent work.

In SfM, it is a very common scenario that all (part of, at least) cameras share the same camera intrinsics Matrix and Undistort parameters(k1, k2, p1, etc...), and these camera intrinsics are also variables to be estimated. In another words, like ceres did, if the element groups include 3 types: "camera_intrinsics, image_poses, points", Will the system generalize to this kind of problem?

How to do this? Hope to receiving your advice.

Generating problem

While compiling MegBA. Some problem occured.

:~/MegBA/build$ make
[  7%] Built target cuManager
[  9%] Building NVCC (Device) object src/operator/CMakeFiles/math_function_Jet_Vector_CUDA.dir/math_function_Jet_Vector_CUDA_generated_math_function_Jet_Vector_CUDA.cu.o
CMake Error at math_function_Jet_Vector_CUDA_generated_math_function_Jet_Vector_CUDA.cu.o.cmake:219 (message):
  Error generating
  /home/wlh/MegBA/build/src/operator/CMakeFiles/math_function_Jet_Vector_CUDA.dir//./math_function_Jet_Vector_CUDA_generated_math_function_Jet_Vector_CUDA.cu.o


src/operator/CMakeFiles/math_function_Jet_Vector_CUDA.dir/build.make:598: recipe for target 'src/operator/CMakeFiles/math_function_Jet_Vector_CUDA.dir/math_function_Jet_Vector_CUDA_generated_math_function_Jet_Vector_CUDA.cu.o' failed
make[2]: *** [src/operator/CMakeFiles/math_function_Jet_Vector_CUDA.dir/math_function_Jet_Vector_CUDA_generated_math_function_Jet_Vector_CUDA.cu.o] Error 1
CMakeFiles/Makefile2:183: recipe for target 'src/operator/CMakeFiles/math_function_Jet_Vector_CUDA.dir/all' failed
make[1]: *** [src/operator/CMakeFiles/math_function_Jet_Vector_CUDA.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2

I am using cuda 11.6 with nvidia-driver-510.

Adding camera-camera edge in case of stereo camera.

Hi guys, Thanks for your great contribution.

I want to ask about the possibility of adding an extra type of edge between stereo-camera pairs(camera-camera edge), and if there is a guideline to do that if it is possible

Thanks in advance

How to access and save the results of the MegBA?

Hi, thanks for your amazing work and open-sourced code.
Following the guidance, I tested the MegBA with the Ladybug dataset by running the ./BAL_Double. The speed is super fast.
But I did not see any output of the result.
In the BAL_Double.cpp, the options have :
DEFINE_int32(world_size, 1, "World size");
DEFINE_string(path, "", "Path to your dataset");
DEFINE_int32(max_iter, 20, "LM solve iteration");
DEFINE_int32(solver_max_iter, 50, "Linear solver iteration");
DEFINE_double(solver_tol, 10., "The tolerance of the linear solver");
DEFINE_double(solver_refuse_ratio, 1., "The refuse ratio of the linear solver");
DEFINE_double(tau, 1., "Initial trust region");
DEFINE_double(epsilon1, 1., "Parameter of LM");
DEFINE_double(epsilon2, 1e-10, "Parameter of LM");
They are not related to saving the output.
I have a question about how I can access the result. And do you have tutorials about MegBA's APIs?

colmap to megba problem, coordinate systems ? not sure cause it getting wrong results with original datasets too

okay'ish positions but rotation still off

#include <gflags/gflags.h>

#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <algorithm>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <opencv2/core.hpp>
#include <opencv2/opencv.hpp>
#include <set>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>

#include "algo/lm_algo.h"
#include "edge/base_edge.h"
#include "geo/geo.cuh"
#include "linear_system/schur_LM_linear_system.h"
#include "problem/base_problem.h"
#include "solver/schur_pcg_solver.h"
#include "vertex/base_vertex.h"

using namespace Eigen;

// Define M_PI if not defined
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

// Structs to hold parsed data
struct Camera {
  int id = 0;
  std::string model = "";
  int width = 0;
  int height = 0;
  std::vector<double> params;
  double fx = 0.0;
  double fy = 0.0;
  double cx = 0.0;
  double cy = 0.0;
  double k1 = 0.0;
  double k2 = 0.0;
};
struct Image {
  int id = 0;
  Eigen::Quaterniond quaternion;
  Eigen::Vector3d translation;
  int camera_id = 0;
  std::string name = "";
  std::vector<Eigen::Vector2d> keypoints;
  std::vector<int> point3D_ids;
  int width = 0;
  int height = 0;
  double fx = 0.0;
  double fy = 0.0;
  double cx = 0.0;
  double cy = 0.0;
  double k1 = 0.0;
  double k2 = 0.0;
};

struct Point3D {
  int id = 0;
  Eigen::Vector3d position = Eigen::Vector3d::Zero();
  Eigen::Vector3i color = Eigen::Vector3i::Zero();
  double error = 0.0;
  std::vector<std::pair<int, int>> observations;
};
struct Observation {
  int image_id;
  int point3D_id;
  Eigen::Vector2d keypoint;
  // For sorting
  bool operator<(const Observation& other) const {
    if (point3D_id != other.point3D_id) return point3D_id < other.point3D_id;
    return image_id < other.image_id;
  }
};
// Function prototypes
bool readCameras(const std::string& path, std::vector<Camera>& cameras);
bool readImages(const std::string& path, std::vector<Image>& images,
                const std::vector<Camera>& cameras, int& nameCount);
bool readPoints3D(const std::string& path, std::vector<Point3D>& points3D);
int countPoints3D(const std::vector<Point3D>& points3D);
int countObservations(const std::vector<Point3D>& points3D);
void generateSummary(int nameCount, int numPoints3D, int numObservations,
                     std::ostringstream& summaryOutput);
std::vector<Observation> collectObservations(
    const std::vector<Image>& images, const std::vector<Point3D>& points3D);
void generateObservations(const std::vector<Observation>& observations,
                          std::ostringstream& observationsOutput);
void generateCameraDetails(const std::vector<Image>& images,
                           const std::vector<Camera>& cameras,
                           std::ostringstream& cameraDetailsOutput);
void generatePoints3D(const std::vector<Point3D>& points3D,
                      std::ostringstream& points3DOutput);
void renumberPoint3DIds(std::vector<Observation>& observations);
void saveToFile(const std::string& summaryPath,
                const std::string& observationsPath,
                const std::string& cameraDetailsPath,
                const std::string& points3DPath,
                const std::ostringstream& summaryOutput,
                const std::ostringstream& observationsOutput,
                const std::ostringstream& cameraDetailsOutput,
                const std::ostringstream& points3DOutput);
bool readCameras(const std::string& path, std::vector<Camera>& cameras) {
  std::ifstream fin(path);
  if (!fin) {
    std::cerr << "Error opening file: " << path << std::endl;
    return false;
  }
  std::string line;
  while (std::getline(fin, line)) {
    if (line.empty() || line[0] == '#') continue;
    std::istringstream iss(line);
    Camera cam;
    iss >> cam.id >> cam.model >> cam.width >> cam.height;
    cam.params.assign(std::istream_iterator<double>(iss),
                      std::istream_iterator<double>());
    // Set default values if parameters are missing
    cam.fx = (cam.params.size() > 0) ? cam.params[0] : 1000.0;
    cam.fy = (cam.params.size() > 1) ? cam.params[1] : cam.fx;
    cam.cx = (cam.params.size() > 2) ? cam.params[2] : cam.width / 2.0;
    cam.cy = (cam.params.size() > 3) ? cam.params[3] : cam.height / 2.0;
    cam.k1 = (cam.params.size() > 4) ? cam.params[4] : 0.0;
    cam.k2 = (cam.params.size() > 5) ? cam.params[5] : 0.0;
    cameras.push_back(cam);
  }
  return true;
}

// Funkcja czytająca obrazy z pliku
bool readImages(const std::string& path, std::vector<Image>& images,
                const std::vector<Camera>& cameras, int& nameCount) {
  std::ifstream fin(path);
  if (!fin) {
    std::cerr << "Error opening file: " << path << std::endl;
    return false;
  }
  std::string line;
  std::set<std::string> uniqueNames;
  while (std::getline(fin, line)) {
    if (line.empty() || line[0] == '#') continue;
    std::istringstream iss(line);
    Image img;

    double qw, qx, qy, qz;

    // Read quaternion components
    iss >> img.id >> qw >> qx >> qy >> qz;
    img.quaternion = Eigen::Quaterniond(qw, qx, qy, qz);

    // Store the quaternion in the img object

    iss >> img.translation.x() >> img.translation.y() >> img.translation.z();
    iss >> img.camera_id;
    iss >> img.name;
    uniqueNames.insert(img.name);

    // Przeczytaj kluczowe punkty
    std::string keypoints_line;
    if (std::getline(fin, keypoints_line)) {
      std::istringstream keypoints_iss(keypoints_line);
      double x, y;
      int pt3d_id;
      while (keypoints_iss >> x >> y >> pt3d_id) {
        img.keypoints.emplace_back(x, y);
        img.point3D_ids.push_back(pt3d_id);
      }
    }

    // Pobierz parametry kamery i ustaw domyślne, jeśli to konieczne
    auto it = std::find_if(cameras.begin(), cameras.end(),
                           [camera_id = img.camera_id](const Camera& cam) {
                             return cam.id == camera_id;
                           });
    if (it != cameras.end()) {
      const Camera& cam = *it;
      img.fx = (cam.params.size() > 0) ? cam.params[0] : 1000.0;
      img.fy = (cam.params.size() > 1) ? cam.params[1] : img.fx;
      img.cx = (cam.params.size() > 2) ? cam.params[2]
                                       : 1920;  // Przykład szerokości obrazu
      img.cy = (cam.params.size() > 3) ? cam.params[3]
                                       : 1080;  // Przykład wysokości obrazu
      img.k1 = (cam.params.size() > 4) ? cam.params[4] : 0.0;
      img.k2 = (cam.params.size() > 5) ? cam.params[5] : 0.0;
    } else {
      img.fx = img.fy = img.cx = img.cy = img.k1 = img.k2 = 0.0;
    }

    images.push_back(img);
  }
  nameCount = static_cast<int>(uniqueNames.size());
  return true;
}
bool readPoints3D(const std::string& path, std::vector<Point3D>& points3D) {
  std::ifstream fin(path);
  if (!fin) {
    std::cerr << "Error opening file: " << path << std::endl;
    return false;
  }
  std::string line;
  while (std::getline(fin, line)) {
    if (line.empty() || line[0] == '#') continue;
    std::istringstream iss(line);
    Point3D pt3d;
    iss >> pt3d.id >> pt3d.position.x() >> pt3d.position.y() >>
        pt3d.position.z();
    int image_id, keypoint_index;
    while (iss >> image_id >> keypoint_index) {
      pt3d.observations.emplace_back(image_id, keypoint_index);
    }
    points3D.push_back(pt3d);
  }
  return true;
}
int countPoints3D(const std::vector<Point3D>& points3D) {
  return static_cast<int>(points3D.size());
}
int countObservations(const std::vector<Observation>& observations) {
  return static_cast<int>(observations.size());
}
void generateSummary(int nameCount, int numPoints3D, int numObservations,
                     std::ostringstream& summaryOutput) {
  summaryOutput << nameCount << ' ' << numPoints3D << ' ' << numObservations;
}
std::vector<Observation> collectObservations(
    const std::vector<Image>& images, const std::vector<Point3D>& points3D) {
  std::vector<Observation> observations;
  for (const auto& image : images) {
    for (size_t i = 0; i < image.keypoints.size(); ++i) {
      const auto& pt3D_id = image.point3D_ids[i];
      if (pt3D_id >= 0 && pt3D_id < points3D.size()) {
        observations.push_back({image.id, pt3D_id, image.keypoints[i]});
      }
    }
  }
  return observations;
}

void generateObservations(const std::vector<Observation>& observations,
                          std::ostringstream& observationsOutput,
                          const std::vector<Image>& images) {
  for (const auto& observation : observations) {
    const auto& image = images[observation.image_id];
    // Przesunięcie współrzędnych o cx i cy
    double adjusted_x = observation.keypoint.x();
    double adjusted_y = observation.keypoint.y();

    // Decrement image_id by 1 (jeśli jest to wymagane przez format wyjściowy)
    observationsOutput << (observation.image_id - 1) << ' '
                       << observation.point3D_id << ' ' << adjusted_x << ' '
                       << adjusted_y << '\n';
  }
}

using namespace cv;
using namespace std;
// Define the function to convert Rodrigues' rotation vector to a rotation
// matrix
Eigen::Matrix3d fromRodrigues(const Eigen::Vector3d& x) {
  double theta2 = x.squaredNorm();
  if (theta2 > std::numeric_limits<double>::epsilon()) {
    double angle = x.norm();
    Eigen::Vector3d axis = x.normalized();
    Eigen::AngleAxisd rotation(angle, axis);
    return rotation.toRotationMatrix();
  } else {
    // Taylor series approximation from ceres-solver
    Eigen::Matrix3d rotation;
    rotation << 1.0, x.z(), -x.y(), -x.z(), 1.0, x.x(), x.y(), -x.x(), 1.0;
    return rotation;
  }
}

// Function to convert rotation matrix to quaternion
Eigen::Quaternion<double> rotationMatrixToQuaternion(
    const Eigen::Matrix3d& rotation) {
  return Eigen::Quaternion<double>(rotation);
}

Vector3d QuaternionToRodrigues(const Quaterniond& quat) {
  // Krok 1: Konwertuj kwaternion na macierz rotacji
  Matrix3d rotationMatrix = quat.toRotationMatrix();

  // Krok 2: Oblicz wektor Rodrigueza z macierzy rotacji
  // Wektor Rodrigueza jest definiowany przez cosinus kąta rotacji oraz oś
  // rotacji
  Vector3d rodrigues;

  double angle = acos((rotationMatrix.trace() - 1) / 2.0);
  if (angle != 0) {
    double sin_angle = sin(angle);
    rodrigues(0) =
        (rotationMatrix(2, 1) - rotationMatrix(1, 2)) / (2 * sin_angle);
    rodrigues(1) =
        (rotationMatrix(0, 2) - rotationMatrix(2, 0)) / (2 * sin_angle);
    rodrigues(2) =
        (rotationMatrix(1, 0) - rotationMatrix(0, 1)) / (2 * sin_angle);
    rodrigues *= angle;
  } else {
    rodrigues << 0, 0, 0;
  }

  return rodrigues;
}
void cvMatToEigen(const cv::Mat& cvMat, Eigen::Matrix3d& eigenMat) {
  assert(cvMat.rows == 3 && cvMat.cols == 3 && cvMat.type() == CV_64F);
  // Kopiuj wartości z cv::Mat do Eigen::Matrix3d
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      eigenMat(i, j) = cvMat.at<double>(i, j);
    }
  }
}
// Function to convert Eigen quaternion to (w, x, y, z) format
void quaternionToWXYZ(const Eigen::Quaterniond& quat, double& w, double& x,
                      double& y, double& z) {
  w = quat.w();
  x = quat.x();
  y = quat.y();
  z = quat.z();
}

template <typename T>
T clamp(T value, T low, T high) {
  return (value < low) ? low : (value > high) ? high : value;
}


// Function to generate camera details
void generateCameraDetails(const vector<Image>& images,
                           const vector<Camera>& cameras,
                           ostringstream& cameraDetailsOutput) {
  unordered_map<int, string> imageIdToName;
  for (const auto& image : images) {
    imageIdToName[image.id] = image.name;
  }

  unordered_map<int, int> idMap;
  int newId = 0;
  for (const auto& image : images) {
    idMap[image.id] = newId++;
  }

  unordered_map<int, Camera> cameraMap;
  for (const auto& cam : cameras) {
    cameraMap[cam.id] = cam;
  }

  for (const auto& image : images) {
    int newImageId = idMap[image.id];
    const auto& cam = cameraMap[image.camera_id];

  
    Quaterniond quat(image.quaternion);
    Matrix3d rotationMatrix = quat.toRotationMatrix();

  
    Matrix4d w2c = Matrix4d::Identity();
    w2c.block<3, 3>(0, 0) = rotationMatrix;
    w2c.block<3, 1>(0, 3) = image.translation;

   
    Matrix4d c2w = w2c.inverse();
  
    Vector3d translationFromMatrix = c2w.block<3, 1>(0, 3);
    Matrix3d rotationMatrixWorld = c2w.block<3, 3>(0, 0);

    Vector3d rodrigues = QuaternionToRodrigues(quat);


    cameraDetailsOutput << rodrigues[0] << '\n'
                        << rodrigues[1] << '\n'
                        << rodrigues[2] << '\n'
                        << translationFromMatrix[0] << '\n'
                        << translationFromMatrix[1] << '\n'
                        << translationFromMatrix[2] << '\n'
                        << (cam.fx + cam.fy) / 2.0 << '\n'
                        << cam.k1 << '\n'
                        << cam.k2 << '\n';
  }
}

// Funkcja generująca punkty 3D
void generatePoints3D(const std::vector<Point3D>& points3D,
                      std::ostringstream& points3DOutput) {
  for (const auto& point3D : points3D) {
    Eigen::Vector3d position = point3D.position;

    points3DOutput << position.x() << '\n'
                   << position.y() << '\n'
                   << position.z() << '\n';
  }
}
void saveToFile(const std::string& outputPath,
                const std::ostringstream& summaryOutput,
                const std::ostringstream& observationsOutput,
                const std::ostringstream& cameraDetailsOutput,
                const std::ostringstream& points3DOutput) {
  // Open the output file
  std::ofstream fout(outputPath);
  if (!fout) {
    std::cerr << "Error opening file for writing: " << outputPath << std::endl;
    return;
  }

  // Write summary
  fout << summaryOutput.str();
  fout << std::endl;

  // Write observations
  fout << observationsOutput.str();

  // Write camera details
  fout << cameraDetailsOutput.str();

  // Write points3D
  fout << points3DOutput.str();
  fout.close();
}
void renumberPoint3DIds(std::vector<Observation>& observations) {
  std::unordered_map<int, int> oldToNewIdMap;
  int newId = 0;
  for (const auto& obs : observations) {
    if (oldToNewIdMap.find(obs.point3D_id) == oldToNewIdMap.end()) {
      oldToNewIdMap[obs.point3D_id] = newId++;
    }
  }
  for (auto& obs : observations) {
    obs.point3D_id = oldToNewIdMap[obs.point3D_id];
  }
}
void saveToFile(const std::string& summaryPath,
                const std::string& observationsPath,
                const std::string& cameraDetailsPath,
                const std::string& points3DPath,
                const std::ostringstream& summaryOutput,
                const std::ostringstream& observationsOutput,
                const std::ostringstream& cameraDetailsOutput,
                const std::ostringstream& points3DOutput) {
  // Save summary
  std::ofstream summaryFile(summaryPath);
  if (summaryFile) {
    summaryFile << summaryOutput.str();
    summaryFile.close();
  } else {
    std::cerr << "Error opening file for writing: " << summaryPath << std::endl;
  }
  // Save observations
  std::ofstream observationsFile(observationsPath);
  if (observationsFile) {
    observationsFile << observationsOutput.str();
    observationsFile.close();
  } else {
    std::cerr << "Error opening file for writing: " << observationsPath
              << std::endl;
  }
  // Save camera details
  std::ofstream cameraDetailsFile(cameraDetailsPath);
  if (cameraDetailsFile) {
    cameraDetailsFile << cameraDetailsOutput.str();
    cameraDetailsFile.close();
  } else {
    std::cerr << "Error opening file for writing: " << cameraDetailsPath
              << std::endl;
  }
  // Save points3D
  std::ofstream points3DFile(points3DPath);
  if (points3DFile) {
    points3DFile << points3DOutput.str();
    points3DFile.close();
  } else {
    std::cerr << "Error opening file for writing: " << points3DPath
              << std::endl;
  }
}
template <typename T>
class BAL_Edge : public MegBA::BaseEdge<T> {
 public:
  MegBA::JVD<T> forward() override {
    using MappedJVD = Eigen::Map<const MegBA::geo::JVD<T>>;
    const auto& Vertices = this->getVertices();
    MappedJVD angle_axisd{&Vertices[0].getEstimation()(0, 0), 3, 1};
    MappedJVD t{&Vertices[0].getEstimation()(3, 0), 3, 1};
    MappedJVD intrinsics{&Vertices[0].getEstimation()(6, 0), 3, 1};
    const auto& point_xyz = Vertices[1].getEstimation();
    const auto& obs_uv = this->getMeasurement();
    auto R = MegBA::geo::AngleAxisToRotationKernelMatrix(angle_axisd);
    Eigen::Matrix<MegBA::JetVector<T>, 3, 1> re_projection = R * point_xyz + t;
    re_projection = -re_projection / re_projection(2);
    // f, k1, k2 = intrinsics
    auto fr = MegBA::geo::RadialDistortion(re_projection, intrinsics);
    MegBA::JVD<T> error = fr * re_projection.head(2) - obs_uv;
    return error;
  }
};

namespace {
template <typename Derived>
bool writeVector(std::ostream& os, const Eigen::DenseBase<Derived>& b) {
  for (int i = 0; i < b.size(); i++) os << b(i) << " ";
  return os.good();
}
template <typename Derived>
bool readVector(std::istream& is, Eigen::DenseBase<Derived>& b) {
  for (int i = 0; i < b.size() && is.good(); i++) is >> b(i);
  return is.good() || is.eof();
}
}  // namespace
DEFINE_int32(world_size, 1, "World size");
DEFINE_string(out_path, "output.txt", "Path to save the results");
DEFINE_string(path, "", "Path to your dataset");
DEFINE_int32(max_iter, 20, "LM solve iteration");
DEFINE_int32(solver_max_iter, 50, "Linear solver iteration");
DEFINE_double(solver_tol, 10., "The tolerance of the linear solver");
DEFINE_double(solver_refuse_ratio, 1., "The refuse ratio of the linear solver");
DEFINE_double(tau, 1., "Initial trust region");
DEFINE_double(epsilon1, 1., "Parameter of LM");
DEFINE_double(epsilon2, 1e-10, "Parameter of LM");
int main(int argc, char* argv[]) {
  GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
  std::cout << "solving " << FLAGS_path << ", world_size: " << FLAGS_world_size
            << ", max iter: " << FLAGS_max_iter
            << ", solver_tol: " << FLAGS_solver_tol
            << ", solver_refuse_ratio: " << FLAGS_solver_refuse_ratio
            << ", solver_max_iter: " << FLAGS_solver_max_iter
            << ", tau: " << FLAGS_tau << ", epsilon1: " << FLAGS_epsilon1
            << ", epsilon2: " << FLAGS_epsilon2 << std::endl;
  typedef double T;
  const std::string cameras_path = "cameras.txt";
  const std::string images_path = "images.txt";
  const std::string points3D_path = "points3D.txt";
  std::vector<Camera> cameras;
  std::vector<Image> images;
  std::vector<Point3D> points3D;
  int nameCount = 0;

  if (!readCameras(cameras_path, cameras)) {
    std::cerr << "Error reading cameras file" << std::endl;
    return 1;
  }
  if (!readImages(images_path, images, cameras, nameCount)) {
    std::cerr << "Error reading images file" << std::endl;
    return 1;
  }
  if (!readPoints3D(points3D_path, points3D)) {
    std::cerr << "Error reading points3D file" << std::endl;
    return 1;
  }

  auto observations = collectObservations(images, points3D);
  std::sort(observations.begin(), observations.end());

  // Renumber the point3D_ids
  renumberPoint3DIds(observations);

  int numPoints3D = countPoints3D(points3D);
  int numObservations = countObservations(observations);

  std::ostringstream summaryOutput;
  std::ostringstream observationsOutput;
  std::ostringstream cameraDetailsOutput;
  std::ostringstream points3DOutput;

  generateSummary(nameCount, numPoints3D, numObservations, summaryOutput);
  generateObservations(observations, observationsOutput, images);
  generateCameraDetails(images, cameras, cameraDetailsOutput);
  generatePoints3D(points3D, points3DOutput);

  // Save all data to a single file
  saveToFile(FLAGS_out_path, summaryOutput, observationsOutput,
             cameraDetailsOutput, points3DOutput);
  std::ifstream fin(FLAGS_path);
  if (!fin) {
    std::cerr << "Error opening input file: " << FLAGS_path << std::endl;
    return 1;
  }
  fin >> nameCount;
  fin >> numPoints3D;
  fin >> numObservations;
  MegBA::ProblemOption problemOption{};
  problemOption.nItem = numObservations;
  problemOption.N = 12;
  for (int i = 0; i < FLAGS_world_size; ++i) {
    problemOption.deviceUsed.push_back(i);
  }
  MegBA::SolverOption solverOption{};
  solverOption.solverOptionPCG.maxIter = FLAGS_solver_max_iter;
  solverOption.solverOptionPCG.tol = FLAGS_solver_tol;
  solverOption.solverOptionPCG.refuseRatio = FLAGS_solver_refuse_ratio;
  MegBA::AlgoOption algoOption{};
  algoOption.algoOptionLM.maxIter = FLAGS_max_iter;
  algoOption.algoOptionLM.initialRegion = FLAGS_tau;
  algoOption.algoOptionLM.epsilon1 = FLAGS_epsilon1;
  algoOption.algoOptionLM.epsilon2 = FLAGS_epsilon2;
  std::unique_ptr<MegBA::BaseAlgo<T>> algo{
      new MegBA::LMAlgo<T>{problemOption, algoOption}};
  std::unique_ptr<MegBA::BaseSolver<T>> solver{
      new MegBA::SchurPCGSolver<T>{problemOption, solverOption}};
  std::unique_ptr<MegBA::BaseLinearSystem<T>> linearSystem{
      new MegBA::SchurLMLinearSystem<T>{problemOption, std::move(solver)}};
  MegBA::BaseProblem<T> problem{problemOption, std::move(algo),
                                std::move(linearSystem)};
  std::vector<std::tuple<int, int, Eigen::Matrix<T, 2, 1>>> edge;
  std::vector<std::tuple<int, Eigen::Matrix<T, 9, 1>>> camera_vertices;
  std::vector<std::tuple<int, Eigen::Matrix<T, 3, 1>>> point_vertices;
  int counter = 0;
  // Read edges
  while (!fin.eof()) {
    if (counter < numObservations) {
      int idx1, idx2;
      fin >> idx1 >> idx2;
      idx2 += nameCount;
      Eigen::Matrix<T, 2, 1> observations;
      readVector(fin, observations);
      edge.emplace_back(idx1, idx2, std::move(observations));
    } else {
      break;
    }
    counter++;
  }
  // Read vertices and modify camera orientation
  counter = 0;
  while (!fin.eof()) {
    int idx = counter;
    if (counter < nameCount) {
      Eigen::Matrix<T, 9, 1> camera_vector9;
      readVector(fin, camera_vector9);
      camera_vertices.emplace_back(idx, std::move(camera_vector9));
    } else {
      Eigen::Matrix<T, 3, 1> point_Vector3;
      readVector(fin, point_Vector3);
      point_vertices.emplace_back(idx, std::move(point_Vector3));
    }
    counter++;
    if (!fin.good()) break;
  }
  for (int n = 0; n < nameCount; ++n) {
    problem.appendVertex(std::get<0>(camera_vertices[n]),
                         new MegBA::CameraVertex<T>());
    problem.getVertex(std::get<0>(camera_vertices[n]))
        .setEstimation(std::get<1>(std::move(camera_vertices[n])));
  }
  for (int n = 0; n < numPoints3D; ++n) {
    problem.appendVertex(std::get<0>(point_vertices[n]),
                         new MegBA::PointVertex<T>());
    problem.getVertex(std::get<0>(point_vertices[n]))
        .setEstimation(std::get<1>(std::move(point_vertices[n])));
  }
  for (int j = 0; j < numObservations; ++j) {
    auto edgePtr = new BAL_Edge<T>;
    edgePtr->appendVertex(&problem.getVertex(std::get<0>(edge[j])));
    edgePtr->appendVertex(&problem.getVertex(std::get<1>(edge[j])));
    edgePtr->setMeasurement(std::get<2>(std::move(edge[j])));
    problem.appendEdge(*edgePtr);
  }
  problem.solve();
  // Write results to output file
  std::ofstream fout(FLAGS_out_path);
  if (!fout) {
    std::cerr << "Error opening output file: " << FLAGS_out_path << std::endl;
    return 1;
  }
  // Write number of cameras, points, and observations in one line
  // Write number of cameras, points, and observations in one line
  fout << nameCount << " " << numPoints3D << " " << numObservations
       << std::endl;

  // Write observations (edges) first
  for (const auto& e : edge) {
    fout << std::get<0>(e) << " " << std::get<1>(e) << " ";
    writeVector(fout, std::get<2>(e));
    fout << std::endl;
  }

    for (const auto& camera_vertex : camera_vertices) {
    Eigen::Matrix<double, 9, 1> camera_vector = std::get<1>(camera_vertex);

    Eigen::Vector3d rvec(camera_vector(0), camera_vector(1), camera_vector(2));
    Eigen::Vector3d translation(camera_vector(3), camera_vector(4),
                                camera_vector(5));
    Eigen::Vector3d intrinsics(camera_vector(6), camera_vector(7),
                               camera_vector(8));

    // Convert Rodrigues' vector to rotation matrix
    Eigen::Matrix3d rotationMatrix = fromRodrigues(rvec);

    // Convert rotation matrix to quaternion
    Eigen::Quaternion<double> quaternion =
        rotationMatrixToQuaternion(rotationMatrix);

    // Extract quaternion components
    double w = quaternion.w();
    double x = quaternion.x();
    double y = quaternion.y();
    double z = quaternion.z();

    // Write quaternion and other parameters to the file
    fout << w << " " << x << " " << y << " " << z << " ";
    fout << translation.x() << " " << translation.y() << " " << translation.z()
         << " ";
    fout << intrinsics.x() << " " << intrinsics.y() << " " << intrinsics.z()
         << std::endl;
  }

  // Write point vertices
  for (const auto& point_vertex : point_vertices) {
    // Comment out or remove the ID part if not needed
    // fout << std::get<0>(point_vertex) << " ";
    writeVector(fout, std::get<1>(point_vertex));
    fout << std::endl;
  }

  // Clean up and close the file
  gflags::ShutDownCommandLineFlags();
  fout.close();
  return 0;
}

im not sure what am i doing wrong

import numpy as np
import open3d as o3d
from scipy.spatial.transform import Rotation as R

def qvec2rotmat(qvec: np.ndarray) -> np.ndarray:
    """Convert a quaternion vector to a rotation matrix."""
    q0, q1, q2, q3 = qvec
    return np.array([
        [1 - 2 * (q2**2 + q3**2), 2 * (q1 * q2 - q0 * q3), 2 * (q1 * q3 + q0 * q2)],
        [2 * (q1 * q2 + q0 * q3), 1 - 2 * (q1**2 + q3**2), 2 * (q2 * q3 - q0 * q1)],
        [2 * (q1 * q3 - q0 * q2), 2 * (q2 * q3 + q0 * q1), 1 - 2 * (q1**2 + q2**2)]
    ])

def read_bal_data(file_name):
    """
    Reads BAL data from a text file.
    """
    with open(file_name, "r") as file:
        n_cameras, n_points, n_observations = map(int, file.readline().split())

        points_2d = np.empty((n_observations, 2))
        camera_indices = np.empty(n_observations, dtype=int)
        point_indices = np.empty(n_observations, dtype=int)

        points_3d_dict = {}

        for i in range(n_observations):
            try:
                camera_index, point_index, x, y = file.readline().split()
                camera_indices[i] = int(camera_index)
                point_indices[i] = int(point_index)
                points_2d[i] = [float(x), float(y)]
            except ValueError:
                print(f"Warning: Malformed observation line at index {i}")

        camera_params = np.empty((n_cameras, 10))  # 4 quaternion values + 3 translation values + 1 focal + 2 distortion coefficients

        for i in range(n_cameras):
            try:
                line = file.readline().split()
                quaternion = list(map(float, line[0:4]))  # Quaternion (WXYZ)
                translation = list(map(float, line[4:7]))  # Translation
                focal = float(line[7])
                k1 = float(line[8])
                k2 = float(line[9])
                camera_params[i] = quaternion + translation + [focal, k1, k2]
            except ValueError:
                print(f"Warning: Malformed camera parameters line at index {i}")

        for i in range(n_points):
            try:
                line = file.readline().split()
                point_coords = list(map(float, line[0:3]))
                if len(point_coords) == 2:
                    point_coords.append(0.0)
                points_3d_dict[i] = point_coords
            except ValueError:
                print(f"Warning: Malformed 3D point line at index {i}")

        points_3d = np.full((n_points, 3), np.nan)
        for index, coords in points_3d_dict.items():
            points_3d[index] = coords

    return camera_params, points_3d, camera_indices, point_indices, points_2d

def create_camera_frustum():
    """
    Creates a frustum to represent the camera's view.
    """
    # Define frustum corners in camera space
    frustum_points = np.array([
        [0, 0, 0],  # Apex
        [0.05, 0.05, 0.1],  # Near upper right
        [-0.05, 0.05, 0.1],  # Near upper left
        [-0.05, -0.05, 0.1],  # Near lower left
        [0.05, -0.05, 0.1],  # Near lower right
        [0.05, 0.05, 0.2],  # Far upper right
        [-0.05, 0.05, 0.2],  # Far upper left
        [-0.05, -0.05, 0.2],  # Far lower left
        [0.05, -0.05, 0.2],  # Far lower right
    ])

    # Define frustum lines
    frustum_lines = np.array([
        [0, 1], [0, 2], [0, 3], [0, 4],  # Apex to near plane
        [1, 2], [2, 3], [3, 4], [4, 1],  # Near plane
        [5, 6], [6, 7], [7, 8], [8, 5],  # Far plane
        [1, 5], [2, 6], [3, 7], [4, 8]   # Near to far plane
    ])

    lines = o3d.geometry.LineSet()
    lines.points = o3d.utility.Vector3dVector(frustum_points)
    lines.lines = o3d.utility.Vector2iVector(frustum_lines)

    # Set color
    colors = np.array([[0.5, 0.5, 0.5] for _ in frustum_lines])
    lines.colors = o3d.utility.Vector3dVector(colors)

    return lines

def create_global_axes():
    """
    Creates global axes as a reference.
    """
    lines = o3d.geometry.LineSet()

    axis_length = 0.1
    points = np.array([
        [0, 0, 0],  # Origin
        [axis_length, 0, 0],  # X-axis
        [0, axis_length, 0],  # Y-axis
        [0, 0, axis_length]   # Z-axis
    ])

    lines.lines = o3d.utility.Vector2iVector([
        [0, 1],  # X-axis
        [0, 2],  # Y-axis
        [0, 3]   # Z-axis
    ])

    lines.points = o3d.utility.Vector3dVector(points)

    colors = np.array([
        [1, 0, 0],  # X-axis color (red)
        [0, 1, 0],  # Y-axis color (green)
        [0, 0, 1]   # Z-axis color (blue)
    ])
    lines.colors = o3d.utility.Vector3dVector(colors)

    return lines

def visualize_scene(camera_params, points_3d):
    vis = o3d.visualization.Visualizer()
    vis.create_window()

    # Add the 3D points
    point_cloud = o3d.geometry.PointCloud()
    point_cloud.points = o3d.utility.Vector3dVector(points_3d)
    vis.add_geometry(point_cloud)

    # Add global axes
    global_axes = create_global_axes()
    vis.add_geometry(global_axes)

    for camera_id, param in enumerate(camera_params):
        quaternion = param[:4]  # Quaternion (WXYZ)
        translation = param[4:7]  # Translation

        # Convert quaternion to rotation matrix
        rot = R.from_quat(quaternion)  # WXYZ format for conversion
        rotation_matrix = rot.as_matrix()

        frustum = create_camera_frustum()
        frustum.rotate(rotation_matrix, center=(0, 0, 0))
        frustum.translate(translation)

        vis.add_geometry(frustum)

    vis.run()
    vis.destroy_window()

def main():
    # Replace 'path/to/your/data.txt' with the actual path to your BAL file
    file_name = "output.txt"

    # Read BAL data
    camera_params, points_3d, _, _, _ = read_bal_data(file_name)
    
    # Visualize the scene
    visualize_scene(camera_params, points_3d)

if __name__ == "__main__":
    main()


run time error thrust::system::system_error

Hi, Thanks a lot for your great contribution.

I got the below error while running the BAL_Double example,

"solving /home/khshmt/cpp_ws/MegBA/data, world_size: 1, max iter: 20, solver_tol: 10, solver_refuse_ratio: 1, solver_max_iter: 50, tau: 1, epsilon1: 1, epsilon2: 1e-10
Start with error: 0, log error: -inf, elapsed 209 ms
terminate called after throwing an instance of 'thrust::system::system_error'
what(): after reduction step 1: cudaErrorIllegalAddress: an illegal memory access was encountered
Aborted (core dumped)"

linux ==>> 20.04
g++ ==>> 9.4.0
cuda ==>> 11.6
GPU ==>> NVIDIA Quadro M6000
dataset ==>> venice ==>>problem-1778-993923-pre.txt.bz2

cmake configuration and build runs flawless, but i got the above error every time I run knowing It loads all the data(like: num_points, num_cameras, num_observations and so on) from the dataset correctlly.
Screenshot from 2022-07-07 15-37-16

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.