基于eigen实现 bundle adjustment

17 篇文章 0 订阅
7 篇文章 0 订阅

纯粹是演示性代码,用于展示BA过程,没有做稀疏矩阵相关的优化

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <Eigen/Core>
#include <Eigen/Dense>

#include "sophus/se3.h"

using namespace std;
using namespace Eigen;
using namespace cv;

typedef vector<Vector3d, Eigen::aligned_allocator<Vector3d>> VecVector3d;
typedef vector<Vector2d, Eigen::aligned_allocator<Vector3d>> VecVector2d;
typedef Matrix<double, 6, 1> Vector6d;

double fx = 520.9, fy = 521.0, cx = 325.1, cy = 249.7;

template<typename T>
Eigen::Matrix<T, 3, 3> RPY2mat(T roll, T pitch, T yaw)
{
    Eigen::Matrix<T, 3, 3> m;

    T cr = cos(roll);
    T sr = sin(roll);
    T cp = cos(pitch);
    T sp = sin(pitch);
    T cy = cos(yaw);
    T sy = sin(yaw);

    m(0,0) = cy * cp;
    m(0,1) = cy * sp * sr - sy * cr;
    m(0,2) = cy * sp * cr + sy * sr;
    m(1,0) = sy * cp;
    m(1,1) = sy * sp * sr + cy * cr;
    m(1,2) = sy * sp * cr - cy * sr;
    m(2,0) = - sp;
    m(2,1) = cp * sr;
    m(2,2) = cp * cr;
    return m;
}

void getPt3d(VecVector3d& p3d, int size)
{
	srand(time(NULL));
	
	for(int i = 0; i < size; i++)
	{
		double x = rand() % 100 - 100;
		double y = rand() % 100 - 100;
		double z = rand() % 100 + 500;
		
		p3d.push_back ( Vector3d( x, y, z) );
	}
}

void outputPt3d(const VecVector3d & p3ds)
{
	for(int i = 0; i < p3ds.size(); i++)
	{
		std::cout << p3ds[i](0) << " " << p3ds[i](0)  << " " << p3ds[i](0) << std::endl;
	}
}

void getUV(VecVector2d & p2ds,
        VecVector3d& p3d,
		Sophus::SE3 & SE3_RT);
		
int main()
{	
    // 1 生成位姿
	Eigen::Matrix3d R = Eigen::AngleAxisd(M_PI/4, Eigen::Vector3d(0, 0, 0.81)).toRotationMatrix();
	//Eigen::Matrix3d R = RPY2mat(0.1, 0.1, 0.0);
	Eigen::Vector3d t(1, 0, 0);
	

	// 从R, t 构造  SE3
	Sophus::SE3 SE3_RT1(R, t);
	
	Eigen::Matrix3d R2 = Eigen::AngleAxisd(M_PI/4, Eigen::Vector3d(0.81, 0, 1)).toRotationMatrix();
	Eigen::Vector3d t2(20, 10, 100);
	
	Sophus::SE3 SE3_RT2(R2, t2);
	
	Eigen::Matrix3d R3 = Eigen::AngleAxisd(M_PI/4, Eigen::Vector3d(0.52, 0, 1)).toRotationMatrix();
	Eigen::Vector3d t3(50, 30, 100);
	
	Sophus::SE3 SE3_RT3(R3, t3);
	
	vector<Sophus::SE3> poses = { SE3_RT1, SE3_RT2, SE3_RT3 };
	//vector<Sophus::SE3> poses = { SE3_RT1};
	
	// 2 生成3D 坐标点
	const int POINT_SIZE = 20;
	
    VecVector3d p3d;
	getPt3d(p3d, POINT_SIZE);
	
	// 3 生成像素坐标点
	vector<VecVector2d> pt2ds(poses.size());
	for(int i = 0; i < poses.size(); i++)
	{
		getUV(pt2ds[i], p3d, poses[i]);
	}
	
	//outputPt3d(p3d);
	
	//对坐标点和误差加 噪声!
	for(int i = 0; i < POINT_SIZE; i++)
	{
		p3d[i](0) += 4;//((i % 2) ==0 ? -4 : 4);
	}
	
	//Eigen::Matrix3d R4 = Eigen::AngleAxisd(M_PI/4, Eigen::Vector3d(0.01, 0.0, 0.81)).toRotationMatrix();
	//Eigen::Matrix3d R4 = RPY2mat(0.15, 0.10, 0.0);
	//Eigen::Vector3d t4(1, 0, 0);
	//Sophus::SE3 SE3_RT4(R4, t4);
	//poses[0] = SE3_RT4;
	
	//outputPt3d(p3d);
	
    //高斯牛顿迭代
    int iterations = 10;

	// Jij 的维度为 2 * (cols)
	// H 的维度为 (cols) * (cols)
	// b 的维度为 cols
	int cols = poses.size() * 6 + p3d.size() * 3;
	
	std::cout << "cols is " << cols << std::endl;
	
	double lastCost = 0;
	double cost = 0;
	
	const int POSE_SIZE = poses.size();
	
	for (int iter = 0; iter < iterations; iter++) 
	{
		MatrixXd H = Eigen::MatrixXd::Zero(cols, cols);
		MatrixXd b = Eigen::MatrixXd::Zero(cols, 1);
		
		cost = 0;		
				
		for(int i = 0; i < poses.size(); i++)
		{
			for(int j = 0; j < p3d.size(); j++)
			{
				// 遍历每一个观测, 每次观测 对 Jij 中的两个块进行赋值
				
				Vector2d p2 = pt2ds[i][j];
				
				Vector3d p3 = p3d[j];
				
				Vector3d P = poses[i] * p3;
				double x = P[0];
				double y = P[1];
				double z = P[2];
				
				Vector2d p2_ = { fx * ( x/z ) + cx, fy * ( y/z ) + cy };
				Vector2d e = p2 - p2_;    //误差error = 观测值 - 估计值
				cost += (e[0]*e[0]+e[1]*e[1]);
				
				MatrixXd J = Eigen::MatrixXd::Zero(2, cols);
				
				// 和位姿相关的块
				// 第一个位姿 要固定
				if(i != 0)
				{					
					J(0,6*i + 0) = -(fx/z);
					J(0,6*i + 1) = 0;
					J(0,6*i + 2) = (fx*x/(z*z));
					J(0,6*i + 3) = (fx*x*y/(z*z));
					J(0,6*i + 4) = -(fx*x*x/(z*z)+fx);
					J(0,6*i + 5) = (fx*y/z);
					J(1,6*i + 0) = 0;
					J(1,6*i + 1) = -(fy/z);
					J(1,6*i + 2) = (fy*y/(z*z));
					J(1,6*i + 3) = (fy*y*y/(z*z)+fy);
					J(1,6*i + 4) = -(fy*x*y/(z*z));
					J(1,6*i + 5) = -(fy*x/z);	
				}				

				
				// 和 3D 坐标相关的块				
				Eigen::Matrix<double, 2, 3> K;
				K << -(fx / z), 0, (fx *x / (z*z)), 0, -(fy / z), fy * y / (z*z);
				Matrix3d R = poses[i].rotation_matrix();
				
				J.block(0, 6 * POSE_SIZE + 3 * j, 2,3) = K * R;				
				
				H += J.transpose() * J;
				b += -J.transpose() * e;

			}			
		}
		
		// solve dx
        VectorXd dx(cols);
        dx = H.ldlt().solve(b);
		
		if (std::isnan(dx[0])) {
            cout << "result is nan!" << endl;
            break;
        }
	
		// 更新 解向量
		// 先更新位姿
		//std::cout << " dx " << dx << std::endl;
		for(int i = 1; i < poses.size(); i++)
		{
			poses[i] = Sophus::SE3::exp(dx.block(i*6, 0, 6, 1)) * poses[i];
		}		
		 
		int startIdx = poses.size() * 6;
		// 再更新 坐标点
		for(int j = 0; j < p3d.size(); j++)
		{
			p3d[j] = dx.block(startIdx + j*3, 0, 3, 1) + p3d[j];
		}
		
		lastCost = cost;
        cout << "iteration " << iter << " cost=" << cost << endl;
	} 
	
	// TODO: 输出结果	
	
	
    return 0;
}

// p2ds 是观察到的像素点, 得保存对应的3D点的信息才行
// 记录每个 p3d 的所有观测数据
// p3d.size()== p2ds.size()
void getUV(VecVector2d & p2ds,
        VecVector3d& p3d,
		Sophus::SE3 & SE3_RT)
{
	//先假设每个点能被每个相机观察到,这样比较简单一点
	for(auto & p3 : p3d)
	{
		Vector3d P = SE3_RT * p3;
		double x = P[0];
		double y = P[1];
		double z = P[2];

		Vector2d p2_ = { fx * ( x/z ) + cx, fy * ( y/z ) + cy };	

		p2ds.push_back(p2_);
	}
	
	return ;
}


### 回答1: 以下是一个基于Eigen实现的简单卷积操作的代码示例: ```c++ #include <Eigen/Dense> #include <iostream> using namespace Eigen; int main() { // 定义输入和卷积核 MatrixXd input(5, 5); input << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25; MatrixXd kernel(3, 3); kernel << 1, 0, 1, 0, 1, 0, 1, 0, 1; // 定义输出 MatrixXd output(input.rows() - kernel.rows() + 1, input.cols() - kernel.cols() + 1); // 进行卷积操作 for (int i = 0; i < output.rows(); ++i) { for (int j = 0; j < output.cols(); ++j) { output(i, j) = (input.block(i, j, kernel.rows(), kernel.cols()) * kernel).sum(); } } // 输出结果 std::cout << "Input:\n" << input << std::endl; std::cout << "Kernel:\n" << kernel << std::endl; std::cout << "Output:\n" << output << std::endl; return 0; } ``` 这个示例中,我们定义了一个 $5 \times 5$ 的输入矩阵和一个 $3 \times 3$ 的卷积核,然后通过两层循环遍历每个卷积窗口,计算其与卷积核的点积和,即可得到输出矩阵。注意,由于卷积操作会导致边缘像素的信息丢失,因此输出矩阵的大小会比输入矩阵小。 ### 回答2: 卷积操作是深度学习中常用的图像处理方法,通过将一个滤波器与输入图像进行卷积运算,实现图像的特征提取和信息提取。 在C语言中使用eigen实现卷积操作的步骤如下: 1. 引入eigen库:在C语言中使用eigen库需要先引入相关的头文件。 2. 定义滤波器和输入图像:在代码中定义滤波器和输入图像的矩阵。 3. 进行卷积运算:使用eigen库提供的矩阵运算函数进行卷积运算。具体实现可以使用嵌套的循环遍历滤波器和输入图像的每一个元素,并进行乘积累加操作。 4. 输出卷积结果:将卷积结果输出保存或打印出来供后续使用。 以下是一个简单的使用eigen实现卷积操作的示例代码: ``` #include <iostream> #include <eigen3/Eigen/Dense> using namespace Eigen; int main() { Matrix3f filter; filter << 1, 0, 1, 0, 1, 0, 1, 0, 1; Matrix4f input; input << 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1; Matrix2f output; output = input.block<2, 2>(0, 0).cwiseProduct(filter).sum(); std::cout << "Output:" << std::endl << output << std::endl; return 0; } ``` 在该示例代码中,我们定义了一个2x2的滤波器和一个4x4的输入图像。使用`.block<2, 2>(0, 0)`选择了输入图像的左上角2x2部分,并将其与滤波器进行逐元素乘积操作,然后使用`.sum()`对乘积结果进行求和得到卷积结果。最后将卷积结果打印出来。 希望以上内容对您有所帮助! ### 回答3: 基于Eigen库的C++实现卷积操作的步骤如下: 步骤1:包含Eigen头文件 使用 `#include <Eigen/Core>` 包含Eigen的核心头文件。 步骤2:定义输入矩阵和卷积核 使用Eigen库的`Matrix`类定义输入矩阵和卷积核。例如: ```cpp Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> input; Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> kernel; ``` 其中,`Eigen::Dynamic`表示矩阵的维度可以动态调整。 步骤3:读取矩阵和卷积核数据 根据实际需求,使用`input`和`kernel`的成员函数(如`resize()`和`row()`)读取矩阵和卷积核数据。 步骤4:进行卷积操作 使用Eigen库的`convolve()`函数进行卷积操作,该函数需要输入参数为两个矩阵。例如: ```cpp Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> result = Eigen::convolve(input, kernel); ``` 上述代码将输入矩阵`input`和卷积核`kernel`进行卷积操作,并将结果存储在`result`矩阵中。 步骤5:输出卷积结果 根据实际需求,可以使用`result`的成员函数(如`row()`和`col()`)输出卷积结果的各个元素。 以上是基于Eigen库进行卷积操作的基本步骤。根据具体应用场景和需求,可能需要进行更多的参数设置和数据处理。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值