其他分享
首页 > 其他分享> > 捆绑调整(码—opencv)

捆绑调整(码—opencv)

作者:互联网

理论知识参考于基于图像的三维重建—捆绑调整(6)

过程比较复杂,这里也不废话,直接上代码

ba_dense_vector.h

#pragma once
#include <cmath>
#include <stdexcept>
#include <vector>

template <typename T>
class DenseVector
{
public:
	DenseVector(void) = default;
	DenseVector(std::size_t size, T const& value = T(0));
	void resize(std::size_t size, T const& value = T(0));
	void clear(void);
	void fill(T const& value);
	std::size_t size(void) const;

	T* data(void);
	T const* data(void) const;
	T* begin(void);
	T const* begin(void) const;
	T* end(void);
	T const* end(void) const;

	DenseVector operator- (void) const;
	bool operator== (DenseVector const& rhs) const;
	T& operator[] (std::size_t index);
	T const& operator[] (std::size_t index) const;
	T& at(std::size_t index);
	T const& at(std::size_t index) const;

	T norm(void) const;
	T squared_norm(void) const;
	T dot(DenseVector const& rhs) const;
	DenseVector add(DenseVector const& rhs) const;
	DenseVector subtract(DenseVector const& rhs) const;
	DenseVector multiply(T const& factor) const;
	void multiply_self(T const& factor);
	void negate_self(void);

private:
	std::vector<T> values;
};

/* ------------------------ Implementation ------------------------ */

template <typename T>
inline DenseVector<T>::DenseVector(std::size_t size, T const& value)
{
	this->resize(size, value);
}

template <typename T>
inline void DenseVector<T>::resize(std::size_t size, T const& value)
{
	this->values.clear();
	this->values.resize(size, value);
}

template <typename T>
inline void DenseVector<T>::clear(void)
{
	this->values.clear();
}

template <typename T>
inline void DenseVector<T>::fill(T const& value)
{
	std::fill(this->values.begin(), this->values.end(), value);
}

template <typename T>
std::size_t DenseVector<T>::size(void) const
{
	return this->values.size();
}

template <typename T>
T* DenseVector<T>::data(void)
{
	return this->values.data();
}

template <typename T>
T const* DenseVector<T>::data(void) const
{
	return this->values.data();
}

template <typename T>
T* DenseVector<T>::begin(void)
{
	return this->values.data();
}

template <typename T>
T const* DenseVector<T>::begin(void) const
{
	return this->values.data();
}

template <typename T>
T* DenseVector<T>::end(void)
{
	return this->values.data() + this->values.size();
}

template <typename T>
T const* DenseVector<T>::end(void) const
{
	return this->values.data() + this->values.size();
}

template <typename T>
DenseVector<T> DenseVector<T>::operator- (void) const
{
	DenseVector ret(this->size());
	for (std::size_t i = 0; i < this->size(); ++i)
		ret[i] = -this->at(i);
	return ret;
}
template <typename T>
bool DenseVector<T>::operator== (DenseVector const& rhs) const
{
	if (this->size() != rhs.size())
		return false;
	for (std::size_t i = 0; i < this->size(); ++i)
		if (this->at(i) != rhs.at(i))
			return false;
	return true;
}

template <typename T>
T& DenseVector<T>::operator[] (std::size_t index)
{
	return this->values[index];
}

template <typename T>
T const& DenseVector<T>::operator[] (std::size_t index) const
{
	return this->values[index];
}

template <typename T>
T& DenseVector<T>::at(std::size_t index)
{
	return this->values[index];
}

template <typename T>
T const& DenseVector<T>::at(std::size_t index) const
{
	return this->values[index];
}

template <typename T>
inline T DenseVector<T>::norm(void) const
{
	return std::sqrt(this->squared_norm());
}

template <typename T>
T DenseVector<T>::squared_norm(void) const
{
	return this->dot(*this);
}

template <typename T>
T DenseVector<T>::dot(DenseVector<T> const& rhs) const
{
	if (this->size() != rhs.size())
		throw std::invalid_argument("Incompatible vector dimensions");

	T ret(0);
	for (std::size_t i = 0; i < this->size(); ++i)
		ret += this->values[i] * rhs.values[i];
	return ret;
}

template <typename T>
DenseVector<T> DenseVector<T>::subtract(DenseVector<T> const& rhs) const
{
	if (this->size() != rhs.size())
		throw std::invalid_argument("Incompatible vector dimensions");

	DenseVector<T> ret(this->size(), T(0));
	for (std::size_t i = 0; i < this->size(); ++i)
		ret.values[i] = this->values[i] - rhs.values[i];
	return ret;
}

template <typename T>
DenseVector<T> DenseVector<T>::add(DenseVector<T> const& rhs) const
{
	if (this->size() != rhs.size())
		throw std::invalid_argument("Incompatible vector dimensions");

	DenseVector<T> ret(this->size(), T(0));
	for (std::size_t i = 0; i < this->size(); ++i)
		ret.values[i] = this->values[i] + rhs.values[i];
	return ret;
}

template <typename T>
DenseVector<T> DenseVector<T>::multiply(T const& factor) const
{
	DenseVector<T> ret(this->size(), T(0));
	for (std::size_t i = 0; i < this->size(); ++i)
		ret[i] = this->at(i) * factor;
	return ret;
}

template <typename T>
void DenseVector<T>::multiply_self(T const& factor)
{
	for (std::size_t i = 0; i < this->size(); ++i)
		this->at(i) *= factor;
}

template <typename T>
void DenseVector<T>::negate_self(void)
{
	for (std::size_t i = 0; i < this->size(); ++i)
		this->at(i) = -this->at(i);
}

ba_sparse_matrix.h

#pragma once
#include <thread>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <iostream>
#include "ba_dense_vector.h"


/**
 * 耶鲁格式的稀疏矩阵类,用于列主矩阵。
 */
template <typename T>
class SparseMatrix
{
public:
	/** 带行/列索引的三元组,以及实际值。 */
	struct Triplet
	{
		Triplet(void) = default;
		Triplet(std::size_t row, std::size_t col, T const& value);

		std::size_t row;
		std::size_t col;
		T value;
	};

	/** 三元组数组 */
	typedef std::vector<Triplet> Triplets;

public:
	SparseMatrix(void);
	SparseMatrix(std::size_t rows, std::size_t cols);
	void allocate(std::size_t rows, std::size_t cols);
	void reserve(std::size_t num_elements);
	void set_from_triplets(Triplets const& triplets);
	void mult_diagonal(T const& factor);
	void cwise_invert(void);
	void column_nonzeros(std::size_t col, DenseVector<T>* vector) const;

	SparseMatrix transpose(void) const;
	SparseMatrix subtract(SparseMatrix const& rhs) const;
	SparseMatrix multiply(SparseMatrix const& rhs) const;
	SparseMatrix sequential_multiply(SparseMatrix const& rhs) const;
	SparseMatrix parallel_multiply(SparseMatrix const& rhs) const;
	DenseVector<T> multiply(DenseVector<T> const& rhs) const;
	SparseMatrix diagonal_matrix(void) const;

	std::size_t num_non_zero(void) const;
	std::size_t num_rows(void) const;
	std::size_t num_cols(void) const;
	T* begin(void);
	T* end(void);

	void debug(void) const;

private:
	std::size_t rows;
	std::size_t cols;
	std::vector<T> values;
	std::vector<std::size_t> outer;        // outer[i+1] - outer[i] = 第i行有多少个非零值
	std::vector<std::size_t> inner;        // 非零元素的列 id
};



template <typename T>
SparseMatrix<T>::Triplet::Triplet(std::size_t row,
	std::size_t col, T const& value)
	: row(row), col(col), value(value)
{
}

/* --------------------------------------------------------------- */

template <typename T>
SparseMatrix<T>::SparseMatrix(void)
	: rows(0)
	, cols(0)
{
}

template <typename T>
SparseMatrix<T>::SparseMatrix(std::size_t rows, std::size_t cols)
{
	this->allocate(rows, cols);
}

template <typename T>
void SparseMatrix<T>::allocate(std::size_t rows, std::size_t cols)
{
	this->rows = rows;
	this->cols = cols;
	this->values.clear();
	this->outer.clear();
	this->inner.clear();
	this->outer.resize(cols + 1, 0);
}

template <typename T>
void SparseMatrix<T>::reserve(std::size_t num_elements)
{
	this->inner.reserve(num_elements);
	this->values.reserve(num_elements);
}

template <typename T>
void SparseMatrix<T>::set_from_triplets(Triplets const& triplets)
{
	/* 创建临时转置矩阵 */
	SparseMatrix<T> transposed(this->cols, this->rows);
	transposed.values.resize(triplets.size());
	transposed.inner.resize(triplets.size());

	// 统计每一行 Non-zero elements 的个数
	/* 使用大量的内部值初始化外部索引。 */
	for (std::size_t i = 0; i < triplets.size(); ++i)
		transposed.outer[triplets[i].row]++;

	/* Convert amounts to indices with prefix sum. */
	std::size_t sum = 0;
	std::vector<std::size_t> scratch(transposed.outer.size());
	for (std::size_t i = 0; i < transposed.outer.size(); ++i)
	{
		std::size_t const temp = transposed.outer[i];
		transposed.outer[i] = sum;
		scratch[i] = sum;
		sum += temp;
	}

	/* 加上三元组,内部索引将不排序。 */
	for (std::size_t i = 0; i < triplets.size(); ++i)
	{
		Triplet const& t = triplets[i];
		std::size_t pos = scratch[t.row]++;
		transposed.values[pos] = t.value;
		transposed.inner[pos] = t.col;
	}

	/* 转置矩阵,内部索引的隐式排序。 */
	*this = transposed.transpose();
}

template <typename T>
SparseMatrix<T> SparseMatrix<T>::transpose(void) const
{
	SparseMatrix ret(this->cols, this->rows);
	ret.values.resize(this->num_non_zero());
	ret.inner.resize(this->num_non_zero());

	/* 计算转置矩阵的内部大小。 */
	for (std::size_t i = 0; i < this->inner.size(); ++i)
		ret.outer[this->inner[i]] += 1;

	/* Compute outer sizes of transposed matrix with prefix sum. */
	std::size_t sum = 0;
	std::vector<std::size_t> scratch(ret.outer.size());
	for (std::size_t i = 0; i < ret.outer.size(); ++i)
	{
		std::size_t const temp = ret.outer[i];
		ret.outer[i] = sum;
		scratch[i] = sum;
		sum += temp;
	}

	/* 写出转置矩阵的内部索引和值。 */
	for (std::size_t i = 0; i < this->outer.size() - 1; ++i)
		for (std::size_t j = this->outer[i]; j < this->outer[i + 1]; ++j)
		{
			std::size_t pos = scratch[this->inner[j]]++;
			ret.inner[pos] = i;
			ret.values[pos] = this->values[j];
		}

	return ret;
}

template <typename T>
SparseMatrix<T> SparseMatrix<T>::subtract(SparseMatrix const& rhs) const
{
	if (this->rows != rhs.rows || this->cols != rhs.cols)
		throw std::invalid_argument("Incompatible matrix dimensions");

	SparseMatrix ret(this->rows, this->cols);
	ret.reserve(this->num_non_zero() + rhs.num_non_zero());

	std::size_t num_outer = this->outer.size() - 1;
	for (std::size_t outer = 0; outer < num_outer; ++outer)
	{
		ret.outer[outer] = ret.values.size();

		std::size_t i1 = this->outer[outer];
		std::size_t i2 = rhs.outer[outer];
		std::size_t const i1_end = this->outer[outer + 1];
		std::size_t const i2_end = rhs.outer[outer + 1];
		while (i1 < i1_end || i2 < i2_end)
		{
			if (i1 >= i1_end)
			{
				ret.values.push_back(-rhs.values[i2]);
				ret.inner.push_back(rhs.inner[i2]);
				i2 += 1;
				continue;
			}
			if (i2 >= i2_end)
			{
				ret.values.push_back(this->values[i1]);
				ret.inner.push_back(this->inner[i1]);
				i1 += 1;
				continue;
			}

			std::size_t id1 = this->inner[i1];
			std::size_t id2 = rhs.inner[i2];

			if (id1 < id2)
				ret.values.push_back(this->values[i1]);
			else if (id2 < id1)
				ret.values.push_back(-rhs.values[i2]);
			else
				ret.values.push_back(this->values[i1] - rhs.values[i2]);

			i1 += static_cast<std::size_t>(id1 <= id2);
			i2 += static_cast<std::size_t>(id2 <= id1);
			ret.inner.push_back(std::min(id1, id2));
		}
	}
	ret.outer.back() = ret.values.size();

	return ret;
}

template <typename T>
SparseMatrix<T> SparseMatrix<T>::multiply(SparseMatrix const& rhs) const
{
#ifdef _OPENMP
	return this->parallel_multiply(rhs);
#else
	return this->sequential_multiply(rhs);
#endif
}

template <typename T>
SparseMatrix<T> SparseMatrix<T>::sequential_multiply(SparseMatrix const& rhs) const
{
	if (this->cols != rhs.rows)
		throw std::invalid_argument("Incompatible matrix dimensions");

	SparseMatrix ret(this->rows, rhs.cols);
	ret.reserve(this->num_non_zero() + rhs.num_non_zero());

	/*  矩阵乘法  */

	std::vector<T> ret_col(ret.rows, T(0));
	std::vector<bool> ret_nonzero(ret.rows, false);
	for (std::size_t col = 0; col < ret.cols; ++col)
	{
		ret.outer[col] = ret.values.size();

		std::fill(ret_col.begin(), ret_col.end(), T(0));
		std::fill(ret_nonzero.begin(), ret_nonzero.end(), false);
		std::size_t rhs_col_begin = rhs.outer[col];
		std::size_t rhs_col_end = rhs.outer[col + 1];
		for (std::size_t i = rhs_col_begin; i < rhs_col_end; ++i)
		{
			T const& rhs_col_value = rhs.values[i];
			std::size_t const lhs_col = rhs.inner[i];
			std::size_t const lhs_col_begin = this->outer[lhs_col];
			std::size_t const lhs_col_end = this->outer[lhs_col + 1];

			for (std::size_t j = lhs_col_begin; j < lhs_col_end; ++j)
			{
				std::size_t const id = this->inner[j];
				ret_col[id] += this->values[j] * rhs_col_value;
				ret_nonzero[id] = true;
			}
		}
		for (std::size_t i = 0; i < ret.rows; ++i)
			if (ret_nonzero[i])
			{
				ret.inner.push_back(i);
				ret.values.push_back(ret_col[i]);
			}
	}
	ret.outer[ret.cols] = ret.values.size();

	return ret;
}

/*         多线程矩阵乘法         */
template <typename T>
SparseMatrix<T> SparseMatrix<T>::parallel_multiply(SparseMatrix const& rhs) const
{
	if (this->cols != rhs.rows)
		throw std::invalid_argument("Incompatible matrix dimensions");

	std::size_t nnz = this->num_non_zero() + rhs.num_non_zero();
	SparseMatrix ret(this->rows, rhs.cols);
	ret.reserve(nnz);
	std::fill(ret.outer.begin(), ret.outer.end(), 0);

	std::size_t const chunk_size = 64;
	std::size_t const num_chunks = ret.cols / chunk_size
		+ (ret.cols % chunk_size != 0);
	std::size_t const max_threads = std::max(1u,
		std::thread::hardware_concurrency());
	std::size_t const num_threads = std::min(num_chunks, max_threads);

#pragma omp parallel num_threads(num_threads)
	{
		/* 矩阵相乘 */
		std::vector<T> ret_col(ret.rows, T(0));
		std::vector<bool> ret_nonzero(ret.rows, false);
		std::vector<T> thread_values;
		thread_values.reserve(nnz / num_chunks);
		std::vector<std::size_t> thread_inner;
		thread_inner.reserve(nnz / num_chunks);

#pragma omp for ordered schedule(static, 1)
		for (std::size_t chunk = 0; chunk < num_chunks; ++chunk)
		{
			thread_inner.clear();
			thread_values.clear();

			std::size_t const begin = chunk * chunk_size;
			std::size_t const end = std::min(begin + chunk_size, ret.cols);
			for (std::size_t col = begin; col < end; ++col)
			{
				std::fill(ret_col.begin(), ret_col.end(), T(0));
				std::fill(ret_nonzero.begin(), ret_nonzero.end(), false);
				std::size_t const rhs_col_begin = rhs.outer[col];
				std::size_t const rhs_col_end = rhs.outer[col + 1];
				for (std::size_t i = rhs_col_begin; i < rhs_col_end; ++i)
				{
					T const& rhs_col_value = rhs.values[i];
					std::size_t const lhs_col = rhs.inner[i];
					std::size_t const lhs_col_begin = this->outer[lhs_col];
					std::size_t const lhs_col_end = this->outer[lhs_col + 1];

					for (std::size_t j = lhs_col_begin; j < lhs_col_end; ++j)
					{
						std::size_t const id = this->inner[j];
						ret_col[id] += this->values[j] * rhs_col_value;
						ret_nonzero[id] = true;
					}
				}
				for (std::size_t i = 0; i < ret.rows; ++i)
					if (ret_nonzero[i])
					{
						ret.outer[col + 1] += 1;
						thread_inner.push_back(i);
						thread_values.push_back(ret_col[i]);
					}
			}

#pragma omp ordered
			{
				ret.inner.insert(ret.inner.end(),
					thread_inner.begin(), thread_inner.end());
				ret.values.insert(ret.values.end(),
					thread_values.begin(), thread_values.end());
			}
		}
	}

	for (std::size_t col = 0; col < ret.cols; ++col)
		ret.outer[col + 1] += ret.outer[col];

	return ret;
}

template<typename T>
DenseVector<T> SparseMatrix<T>::multiply(DenseVector<T> const& rhs) const
{
	if (rhs.size() != this->cols)
		throw std::invalid_argument("Incompatible dimensions");

	DenseVector<T> ret(this->rows, T(0));
	for (std::size_t i = 0; i < this->cols; ++i)
		for (std::size_t id = this->outer[i]; id < this->outer[i + 1]; ++id)
			ret[this->inner[id]] += this->values[id] * rhs[i];
	return ret;
}

template<typename T>
SparseMatrix<T> SparseMatrix<T>::diagonal_matrix(void) const
{
	std::size_t const diag_size = std::min(this->rows, this->cols);
	SparseMatrix ret(diag_size, diag_size);
	ret.reserve(diag_size);
	for (std::size_t i = 0; i < diag_size; ++i)
	{
		ret.outer[i] = ret.values.size();
		for (std::size_t j = this->outer[i]; j < this->outer[i + 1]; ++j)
			if (this->inner[j] == i)
			{
				ret.inner.push_back(i);
				ret.values.push_back(this->values[j]);
			}
			else if (this->inner[j] > i)
				break;
	}
	ret.outer[diag_size] = ret.values.size();
	return ret;
}

template<typename T>
void SparseMatrix<T>::mult_diagonal(T const& factor)
{
	for (std::size_t i = 0; i < this->outer.size() - 1; ++i)
		for (std::size_t j = this->outer[i]; j < this->outer[i + 1]; ++j)
		{
			if (this->inner[j] == i)
				this->values[j] *= factor;
			if (this->inner[j] >= i)
				break;
		}
}

template<typename T>
void SparseMatrix<T>::cwise_invert(void)
{
	for (std::size_t i = 0; i < this->values.size(); ++i)
		this->values[i] = T(1) / this->values[i];
}

template<typename T>
void SparseMatrix<T>::column_nonzeros(std::size_t col, DenseVector<T>* vector) const
{
	std::size_t const start = this->outer[col];
	std::size_t const end = this->outer[col + 1];
	vector->resize(end - start);
	for (std::size_t row = start, i = 0; row < end; ++row, ++i)
		vector->at(i) = this->values[row];
}

template<typename T>
inline std::size_t SparseMatrix<T>::num_non_zero(void) const
{
	return this->values.size();
}

template<typename T>
inline std::size_t SparseMatrix<T>::num_rows(void) const
{
	return this->rows;
}

template<typename T>
inline std::size_t SparseMatrix<T>::num_cols(void) const
{
	return this->cols;
}

template<typename T>
inline T* SparseMatrix<T>::begin(void)
{
	return this->values.data();
}

template<typename T>
inline T* SparseMatrix<T>::end(void)
{
	return this->values.data() + this->values.size();
}

template<typename T>
void SparseMatrix<T>::debug(void) const
{
	std::cout << "SparseMatrix ("
		<< this->rows << " rows, " << this->cols << " cols, "
		<< this->num_non_zero() << " values)" << std::endl;
	std::cout << "  Values:";
	for (std::size_t i = 0; i < this->values.size(); ++i)
		std::cout << " " << this->values[i];
	std::cout << std::endl << "  Inner:";
	for (std::size_t i = 0; i < this->inner.size(); ++i)
		std::cout << " " << this->inner[i];
	std::cout << std::endl << "  Outer:";
	for (std::size_t i = 0; i < this->outer.size(); ++i)
		std::cout << " " << this->outer[i];
	std::cout << std::endl;
}

camera_pose.h

#pragma once
#include<opencv2/opencv.hpp>
using namespace cv;


Mat hstack(Mat const & K, Mat const & t)
{
	int rows = K.rows;
	int cols = K.cols + t.cols;
	Mat P(rows, cols, CV_64FC1);
	for (int i = 0; i < K.rows; i++)
		for (int j = 0; j < K.cols; j++)
			P.at<double>(i, j) = K.at<double>(i, j);
	for (int i = 0; i < t.rows; i++)
		for (int j = 0; j < t.cols; j++)
			P.at<double>(i, K.cols + j) = t.at<double>(i, j);
	return P;
}

struct CameraPose
{
public:
	CameraPose(void);

	/** Initializes R with identity and t with zeros. */
	void init_canonical_form(void);
	/** Returns the P matrix as the product K [R | t]. */
	void fill_p_matrix(Mat* result) const;
	/** Initializes the K matrix from focal length and principal point. */
	void set_k_matrix(double flen, double px, double py);
	/** Returns the focal length as average of x and y focal length. */
	double get_focal_length(void) const;
	/** Returns the camera position (requires valid camera). */
	void fill_camera_pos(Mat* camera_pos) const;
	//void fill_camera_pos(math::Vector<double, 3>* camera_pos) const;
	/** Returns true if K matrix is valid (non-zero focal length). */
	bool is_valid(void) const;

public:
	Mat K;
	Mat R;
	Mat t;
	/*math::Matrix<double, 3, 3> K;
	math::Matrix<double, 3, 3> R;
	math::Vector<double, 3> t;*/
};

/* ------------------------ Implementation ------------------------ */

inline
CameraPose::CameraPose(void)
{
	K = Mat::zeros(3, 3, CV_64FC1);
	R = Mat::zeros(3, 3, CV_64FC1);
	t = Mat::zeros(3, 1, CV_64FC1);
}

inline void
CameraPose::init_canonical_form(void)
{
	for (int i = 0; i < 3 * 3; i++)
		R.at<double>(i) = 1;
	t = Mat::zeros(3, 1, CV_64FC1);
}

inline void
CameraPose::fill_p_matrix(Mat* P) const
{
	Mat KR = this->K * this->R;
	Mat Kt = this->K*this->t;

	*P = hstack(KR,Kt);
}

inline void
CameraPose::set_k_matrix(double flen, double px, double py)
{
	K = Mat::zeros(3, 3, CV_64FC1);
	this->K.at<double>(0) = flen; this->K.at<double>(2) = px;
	this->K.at<double>(4) = flen; this->K.at<double>(5) = py;
	this->K.at<double>(8) = 1.0;
}

inline double
CameraPose::get_focal_length(void) const
{
	return (this->K.at<double>(0) + this->K.at<double>(4)) / 2.0;
}

inline void
CameraPose::fill_camera_pos(Mat* camera_pos) const
{
	*camera_pos = -this->R.t().mul(this->t);
}

inline bool
CameraPose::is_valid(void) const
{
	return this->K.at<double>(0) != 0.0;
}

ba_linear_solver.h

#pragma once

#include <vector>

#include "ba_sparse_matrix.h"
#include "ba_dense_vector.h"


class LinearSolver
{
public:
	struct Options
	{
		Options(void);

		double trust_region_radius;
		int cg_max_iterations;
		int camera_block_dim;
	};

	struct Status
	{
		Status(void);

		double predicted_error_decrease;
		int num_cg_iterations;
		bool success;
	};

	// 稀疏矩阵设置为double类型
	typedef SparseMatrix<double> SparseMatrixType;

	// 向量设置为double类型
	typedef DenseVector<double> DenseVectorType;

public:
	LinearSolver(Options const& options);


	// 解正规方程J^TJ delta_x = -J^T f
	/**
	 * Solve the system J^T J x = -J^T f based on the bundle adjustment mode.
	 * If the Jacobian for cameras is empty, only points are optimized.
	 * If the Jacobian for points is empty, only cameras are optimized.
	 * If both, Jacobian for cams and points is given, the Schur complement
	 * trick is used to solve the linear system.
	 */
	Status solve(SparseMatrixType const& jac_cams,
		SparseMatrixType const& jac_points,
		DenseVectorType const& vector_f,
		DenseVectorType* delta_x);

private:

	// 舒尔补
	/**
	 * Conjugate Gradient on Schur-complement by exploiting the block
	 * structure of H = J^T * J.
	 */
	Status solve_schur(SparseMatrixType const& jac_cams,
		SparseMatrixType const& jac_points,
		DenseVectorType const& values,
		DenseVectorType* delta_x);


	// block size = 0的时候
		/**
		 * J is the Jacobian of the problem. If H = J^T * J has a block diagonal
		 * structure (e.g. 'motion only' or 'structure only' problems in BA),
		 * block_size can be used to directly invert H. If block_size is 0
		 * the diagonal of H is used as a preconditioner and the linear system
		 * is solved via conjugate gradient.
		 */
	Status solve(SparseMatrixType const& J,
		DenseVectorType const& vector_f,
		DenseVectorType* delta_x,
		std::size_t block_size = 0);

private:
	Options opts;
};

/* ------------------------ Implementation ------------------------ */

inline LinearSolver::Options::Options(void)
	: trust_region_radius(1.0)
	, cg_max_iterations(1000)
{
}

inline LinearSolver::Status::Status(void)
	: predicted_error_decrease(0.0)
	, num_cg_iterations(0)
	, success(false)
{
}

inline LinearSolver::LinearSolver(Options const& options)
	: opts(options)
{
}

ba_conjugate_gradient.h


#pragma once
#include "ba_dense_vector.h"
#include "ba_sparse_matrix.h"

template <typename T>
class ConjugateGradient
{
public:
	typedef SparseMatrix<T> Matrix;
	typedef DenseVector<T> Vector;

	enum ReturnInfo
	{
		CG_CONVERGENCE,
		CG_MAX_ITERATIONS,
		CG_INVALID_INPUT
	};

	struct Options
	{
		Options(void);
		int max_iterations;
		T tolerance;
	};

	struct Status
	{
		Status(void);
		int num_iterations;
		ReturnInfo info;
	};

	class Functor
	{
	public:
		virtual Vector multiply(Vector const& x) const = 0;
		virtual std::size_t input_size(void) const = 0;
		virtual std::size_t output_size(void) const = 0;
	};

public:
	ConjugateGradient(Options const& opts);

	Status solve(Matrix const& A, Vector const& b, Vector* x,
		Matrix const* P = nullptr);

	Status solve(Functor const& A, Vector const& b, Vector* x,
		Functor const* P = nullptr);

private:
	Options opts;
	Status status;
};

template <typename T>
class CGBasicMatrixFunctor : public ConjugateGradient<T>::Functor
{
public:
	CGBasicMatrixFunctor(SparseMatrix<T> const& A);
	DenseVector<T> multiply(DenseVector<T> const& x) const;
	std::size_t input_size(void) const;
	std::size_t output_size(void) const;

private:
	SparseMatrix<T> const* A;
};

/* ------------------------ Implementation ------------------------ */

template <typename T>
inline
ConjugateGradient<T>::Options::Options(void)
	: max_iterations(100)
	, tolerance(1e-20)
{
}

template <typename T>
inline
ConjugateGradient<T>::Status::Status(void)
	: num_iterations(0)
{
}

template <typename T>
inline
ConjugateGradient<T>::ConjugateGradient
(Options const& options)
	: opts(options)
{
}

template <typename T>
inline typename ConjugateGradient<T>::Status
ConjugateGradient<T>::solve(Matrix const& A, Vector const& b, Vector* x,
	Matrix const* P)
{
	CGBasicMatrixFunctor<T> A_functor(A);
	CGBasicMatrixFunctor<T> P_functor(*P);
	return this->solve(A_functor, b, x, P == nullptr ? nullptr : &P_functor);
}

template <typename T>
typename ConjugateGradient<T>::Status
ConjugateGradient<T>::solve(Functor const& A, Vector const& b, Vector* x,
	Functor const* P)
{
	if (x == nullptr)
		throw std::invalid_argument("RHS must not be null");

	if (A.output_size() != b.size())
	{
		this->status.info = CG_INVALID_INPUT;
		return this->status;
	}

	/* Set intial x = 0. */
	if (x->size() != A.input_size())
	{
		x->clear();
		x->resize(A.input_size(), T(0));
	}
	else
	{
		x->fill(T(0));
	}

	/* Initial residual is r = b - Ax with x = 0. */
	Vector r = b;

	/* Regular search direction. */
	Vector d;
	/* Preconditioned search direction. */
	Vector z;
	/* Norm of residual. */
	T r_dot_r;

	/* Compute initial search direction. */
	if (P == nullptr)
	{
		r_dot_r = r.dot(r);
		d = r;
	}
	else
	{
		z = (*P).multiply(r);
		r_dot_r = z.dot(r);
		d = z;
	}

	for (this->status.num_iterations = 0;
		this->status.num_iterations < this->opts.max_iterations;
		this->status.num_iterations += 1)
	{
		/* Compute step size in search direction. */
		Vector Ad = A.multiply(d);
		T alpha = r_dot_r / d.dot(Ad);

		/* Update parameter vector. */
		*x = (*x).add(d.multiply(alpha));

		/* Compute new residual and its norm. */
		r = r.subtract(Ad.multiply(alpha));
		T new_r_dot_r = r.dot(r);

		/* Check tolerance condition. */
		if (new_r_dot_r < this->opts.tolerance)
		{
			this->status.info = CG_CONVERGENCE;
			return this->status;
		}

		/* Precondition residual if necessary. */
		if (P != nullptr)
		{
			z = P->multiply(r);
			new_r_dot_r = z.dot(r);
		}

		/*
		 * Update search direction.
		 * The next residual will be orthogonal to new Krylov space.
		 */
		T beta = new_r_dot_r / r_dot_r;
		if (P != nullptr)
			d = z.add(d.multiply(beta));
		else
			d = r.add(d.multiply(beta));

		/* Update residual norm. */
		r_dot_r = new_r_dot_r;
	}

	this->status.info = CG_MAX_ITERATIONS;
	return this->status;
}

/* ---------------------------------------------------------------- */

template <typename T>
inline
CGBasicMatrixFunctor<T>::CGBasicMatrixFunctor(SparseMatrix<T> const& A)
	: A(&A)
{
}

template <typename T>
inline DenseVector<T>
CGBasicMatrixFunctor<T>::multiply(DenseVector<T> const& x) const
{
	return this->A->multiply(x);
}

template <typename T>
inline std::size_t
CGBasicMatrixFunctor<T>::input_size(void) const
{
	return A->num_cols();
}

template <typename T>
inline std::size_t
CGBasicMatrixFunctor<T>::output_size(void) const
{
	return A->num_rows();
}


lm.cpp(源文件)

/*
 * 实现Levenberg-Marquardt算法,该算法又称为 damped least squares 阻尼最小二乘,用来求解非线性最小二乘
 * 问题。LM找到的是局部最小值,该算法介于高斯牛顿法和梯度下降法之间,并通过控制信赖域尺寸的大小,在高斯牛顿法
 * 和梯度下降法之间进行调整。LM 算法比高斯牛顿法速度慢但是更为鲁棒,
 *
 * LM算法的原理是用模型函数f对待估向量p在邻域内做线性估计(泰勒展开),忽略掉二阶以上的导数项,从而转化为线性最小
 * 二乘问题。本质上就是用二次曲面对目标函数进行局部近似。LM算法属于一种信赖域法,即:从初始值开始,先假设一个可以
 * 信赖的最大的位移s, 然后以当前点为中心,以s为半径的区域内,通过寻找目标函数的一个近似二次函数的最优点来求得真正
 * 的位移。在得到位移之后,再计算目标函数值,如果其使得目标函数值得下降满足了一定条件,那么说明这个位移是可靠的
 * 则继续按照此规则迭代计算下去;如果其不能使目标函数的下降满足一定的条件,则应该减少信赖域的范围,重新求解。
 *
 * LM算法的一般流程是:
 *       1) 初始化
 *       2) 计算雅阁比矩阵J,构造正规方程(JTJ + lambdaI) = JTf
 *       3) 求解正规方程(共轭梯度或者预定共轭梯度法)
 *       4) 判断若求解成功
 *               增加信赖域(1/lambda),使得求解算法接近于高斯牛顿法,加快收敛速度
 *               判断终止条件
 *           判断若求解失败
 *               减少信赖域(1/lambda), 使得求解算法解决域梯度下降法
 *       5)  重复1), 2), 3),4)
 *
 * (注意,信赖域的大小为正规方程中lambda的倒数)
 */
#include <iostream>
#include<fstream>
#include<sstream>
#include<cassert>
#include"ba_dense_vector.h"
#include"ba_sparse_matrix.h"
#include"ba_linear_solver.h"
#include"ba_conjugate_gradient.h"
#include"camera_pose.h"

struct Camera
{
	Camera(void);

	double focal_length = 0.0;
	double distortion[2];
	double translation[3];
	double rotation[9];
	bool is_constant = false;
};

struct Point3D
{
	double pos[3];
	bool is_constant = false;
};

struct Observation
{
	double pos[2];
	int camera_id;
	int point_id;
};

inline Camera::Camera(void)
{
	std::fill(this->distortion, this->distortion + 2, 0.0);
	std::fill(this->translation, this->translation + 3, 0.0);
	std::fill(this->rotation, this->rotation + 9, 0.0);
}

std::vector<Camera> cameras;
std::vector<Point3D> points;
std::vector<Observation> observations;
typedef SparseMatrix<double> SparseMatrixType;
typedef DenseVector<double> DenseVectorType;

// lm 算法最多迭代次数
const int lm_max_iterations = 100;

// 信赖域大小
double trust_region_radius = 1000;
int cg_max_iterations = 1000;
//相机参数个数
const int num_cam_params = 9;

#define TRUST_REGION_RADIUS_INIT (1000)
#define TRUST_REGION_RADIUS_DECREMENT (1.0 / 10.0)
#define TRUST_REGION_RADIUS_GAIN (10.0)

// lm 算法终止条件
double lm_mse_threshold = 1e-16;
double lm_delta_threshold = 1e-8;

// mean square error
double initial_mse = 0.0;
double final_mse = 0.0;
int num_lm_iterations = 0;
int num_lm_successful_iterations = 0;
int num_lm_unsuccessful_iterations = 0;

//相机参数个数
int camera_block_dim = 9;

void load_data(const std::string& file_name, std::vector<Camera>&cams, std::vector<Point3D>&pts3D, std::vector<Observation>& observations)
{
	/* 加载数据 */
	std::ifstream in(file_name);
	assert(in.is_open());
	std::string line, word;

	// 加载相机参数
	{
		int n_cams = 0;
		getline(in, line);
		std::stringstream stream(line);
		stream >> word >> n_cams;
		cams.resize(n_cams);
		for (int i = 0; i < cams.size(); i++) {
			getline(in, line);
			std::stringstream stream(line);
			stream >> cams[i].focal_length;
			stream >> cams[i].distortion[0] >> cams[i].distortion[1];
			for (int j = 0; j < 3; j++)stream >> cams[i].translation[j];
			for (int j = 0; j < 9; j++)stream >> cams[i].rotation[j];
		}
	}

	// 加载三维点
	{
		int n_points = 0;
		getline(in, line);
		std::stringstream stream(line);
		stream >> word >> n_points;
		pts3D.resize(n_points);
		for (int i = 0; i < n_points; i++) {
			getline(in, line);
			std::stringstream stream(line);
			stream >> pts3D[i].pos[0] >> pts3D[i].pos[1] >> pts3D[i].pos[2];
		}
	}

	//加载观察点
	{
		int n_observations = 0;
		getline(in, line);
		std::stringstream stream(line);
		stream >> word >> n_observations;
		observations.resize(n_observations);
		for (int i = 0; i < observations.size(); i++) {
			getline(in, line);
			std::stringstream stream(line);
			stream >> observations[i].camera_id
				>> observations[i].point_id
				>> observations[i].pos[0]
				>> observations[i].pos[1];
		}
	}

}

/**
 * /descrition 将角轴法转化成旋转矩阵
 * @param r 角轴向量
 * @param m 旋转矩阵
 */
void rodrigues_to_matrix(double const* r, double* m)
{
	/* Obtain angle from vector length. */
	double a = std::sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]);
	/* Precompute sine and cosine terms. */
	double ct = (a == 0.0) ? 0.5f : (1.0f - std::cos(a)) / (2.0 * a);
	double st = (a == 0.0) ? 1.0 : std::sin(a) / a;
	/* R = I + st * K + ct * K^2 (with cross product matrix K). */
	m[0] = 1.0 - (r[1] * r[1] + r[2] * r[2]) * ct;
	m[1] = r[0] * r[1] * ct - r[2] * st;
	m[2] = r[2] * r[0] * ct + r[1] * st;
	m[3] = r[0] * r[1] * ct + r[2] * st;
	m[4] = 1.0f - (r[2] * r[2] + r[0] * r[0]) * ct;
	m[5] = r[1] * r[2] * ct - r[0] * st;
	m[6] = r[2] * r[0] * ct - r[1] * st;
	m[7] = r[1] * r[2] * ct + r[0] * st;
	m[8] = 1.0 - (r[0] * r[0] + r[1] * r[1]) * ct;
}

template <typename T>
void matrix_multiply(T const* mat_a, int rows_a, int cols_a,
	T const* mat_b, int cols_b, T* mat_res)
{
	std::fill(mat_res, mat_res + rows_a * cols_b, T(0));
	for (int j = 0; j < cols_b; ++j)
	{
		for (int i = 0; i < rows_a; ++i)
		{
			int const ica = i * cols_a;
			int const icb = i * cols_b;
			for (int k = 0; k < cols_a; ++k)
				mat_res[icb + j] += mat_a[ica + k] * mat_b[k * cols_b + j];
		}
	}
}


/**
 * \description 根据求解得到的增量,对相机参数进行更新
 * @param cam
 * @param update
 * @param out
 */
void update_camera(Camera const& cam, double const* update, Camera* out)
{
	out->focal_length = cam.focal_length + update[0];
	out->distortion[0] = cam.distortion[0] + update[1];
	out->distortion[1] = cam.distortion[1] + update[2];

	out->translation[0] = cam.translation[0] + update[3];
	out->translation[1] = cam.translation[1] + update[4];
	out->translation[2] = cam.translation[2] + update[5];

	double rot_orig[9];
	std::copy(cam.rotation, cam.rotation + 9, rot_orig);
	double rot_update[9];
	rodrigues_to_matrix(update + 6, rot_update);
	matrix_multiply(rot_update, 3, 3, rot_orig, 3, out->rotation);
	
}

/**
 * \description 根据求解的增量,对三维点坐标进行更新
 * @param pt
 * @param update
 * @param out
 */
void update_point(Point3D const& pt, double const* update, Point3D* out)
{
	out->pos[0] = pt.pos[0] + update[0];
	out->pos[1] = pt.pos[1] + update[1];
	out->pos[2] = pt.pos[2] + update[2];
}

/**
 * \description 对像素进行径向畸变
 * @param x
 * @param y
 * @param dist
 */
void radial_distort(double* x, double* y, double const* dist)
{
	double const radius2 = *x * *x + *y * *y;
	double const factor = 1.0 + radius2 * (dist[0] + dist[1] * radius2);
	*x *= factor;
	*y *= factor;
}

void compute_reprojection_errors(DenseVectorType* vector_f, DenseVectorType const* delta_x, std::vector<Camera>* cameras,
	std::vector<Point3D> *points, std::vector<Observation> *observations)
{
	if (vector_f->size() != observations->size() * 2)
		vector_f->resize(observations->size() * 2);

#pragma omp parallel for
	for (std::size_t i = 0; i < observations->size(); ++i)
	{
		Observation const& obs = observations->at(i);
		Point3D const& p3d = points->at(obs.point_id);
		Camera const& cam = cameras->at(obs.camera_id);

		double const* flen = &cam.focal_length; // 相机焦距
		double const* dist = cam.distortion;    // 径向畸变系数
		double const* rot = cam.rotation;       // 相机旋转矩阵
		double const* trans = cam.translation;  // 相机平移向量
		double const* point = p3d.pos;          // 三维点坐标

		Point3D new_point;
		Camera new_camera;

		// 如果delta_x 不为空,则先利用delta_x对相机和结构进行更新,然后再计算重投影误差
		if (delta_x != nullptr)
		{
			std::size_t cam_id = obs.camera_id * num_cam_params;
			std::size_t pt_id = obs.point_id * 3;


			update_camera(cam, delta_x->data() + cam_id, &new_camera);
			flen = &new_camera.focal_length;
			dist = new_camera.distortion;
			rot = new_camera.rotation;
			trans = new_camera.translation;
			pt_id += cameras->size() * num_cam_params;


			update_point(p3d, delta_x->data() + pt_id, &new_point);
			point = new_point.pos;
		}


		/* Project point onto image plane. */
		double rp[] = { 0.0, 0.0, 0.0 };
		for (int d = 0; d < 3; ++d)
		{
			rp[0] += rot[0 + d] * point[d];
			rp[1] += rot[3 + d] * point[d];
			rp[2] += rot[6 + d] * point[d];
		}
		rp[2] = (rp[2] + trans[2]);
		rp[0] = (rp[0] + trans[0]) / rp[2];
		rp[1] = (rp[1] + trans[1]) / rp[2];

		/* Distort reprojections. */
		radial_distort(rp + 0, rp + 1, dist);

		/* Compute reprojection error. */
		vector_f->at(i * 2 + 0) = rp[0] * (*flen) - obs.pos[0];
		vector_f->at(i * 2 + 1) = rp[1] * (*flen) - obs.pos[1];
		//std::cout << rp[0] * (*flen) - obs.pos[0] << "  " << rp[1] * (*flen) - obs.pos[1] << std::endl;

	}

}

/*
 * Computes for a given matrix A the square matrix A^T * A for the
 * case that block columns of A only need to be multiplied with itself.
 * Becase the resulting matrix is symmetric, only about half the work
 * needs to be performed.
 */
void matrix_block_column_multiply(SparseMatrix<double> const& A,
	std::size_t block_size, SparseMatrix<double>* B)
{
	SparseMatrix<double>::Triplets triplets;
	triplets.reserve(A.num_cols() * block_size);
	for (std::size_t block = 0; block < A.num_cols(); block += block_size) {
		std::vector<DenseVector<double>> columns(block_size);
		for (std::size_t col = 0; col < block_size; ++col)
			A.column_nonzeros(block + col, &columns[col]);
		for (std::size_t col = 0; col < block_size; ++col) {
			double dot = columns[col].dot(columns[col]);
			triplets.emplace_back(block + col, block + col, dot);
			for (std::size_t row = col + 1; row < block_size; ++row) {
				dot = columns[col].dot(columns[row]);
				triplets.emplace_back(block + row, block + col, dot);
				triplets.emplace_back(block + col, block + row, dot);
			}
		}
	}
	B->allocate(A.num_cols(), A.num_cols());
	B->set_from_triplets(triplets);
}

/** 浮点和双限制以及 epsilon 值 */
#ifndef __FLT_MIN__
#   define __FLT_MIN__ 1.17549435e-38f
#endif
#ifndef __DBL_MIN__
#   define __DBL_MIN__ 2.2250738585072014e-308
#endif
#define MATH_FLT_MIN __FLT_MIN__
#define MATH_DBL_MIN __DBL_MIN__
#define MATH_FLT_EPS (MATH_FLT_MIN * 1e8f) // approx. 1.17e-30
#define MATH_DBL_EPS (MATH_DBL_MIN * 1e58) // approx. 2.22e-250
#define MATH_EPSILON_EQ(x,v,eps) (((v - eps) <= x) && (x <= (v + eps)))
#define MATH_DOUBLE_EQ(x,v) MATH_EPSILON_EQ(x,v,MATH_DBL_EPS)
/*
 * Inverts a matrix with 3x3 bocks on its diagonal. All other entries
 * must be zero. Reading blocks is thus very efficient.
 */
void invert_block_matrix_3x3_inplace(SparseMatrix<double>* A) {
	if (A->num_rows() != A->num_cols())
		throw std::invalid_argument("Block matrix must be square");
	if (A->num_non_zero() != A->num_rows() * 3)
		throw std::invalid_argument("Invalid number of non-zeros");

	for (double* iter = A->begin(); iter != A->end(); )
	{
		double* iter_backup = iter;
		Mat rot(3, 3, CV_64FC1);
		
		for (int i = 0; i < 9; ++i)
			rot.at<double>(i) = *(iter++);

		double det = determinant(rot);
		if (MATH_DOUBLE_EQ(det, 0.0))
			continue;
		rot = rot.inv();
		
		iter = iter_backup;
		for (int i = 0; i < 9; ++i)
			*(iter++) = rot.at<double>(i);
	}
}

#define MATH_POW2(x) ((x) * (x))



void cholesky_decomposition(Mat const* A, int  cols, Mat* L)
{
	/*Mat LL(9, 9, CV_64FC1);
	Mat* out_ptr = &LL;*/
	Mat LL = *A;
	int k = 0;
	for (int r = 0; r < cols; ++r)
	{
		/* Compute left-of-diagonal entries. */
		for (int c = 0; c < r; ++c)
		{
			double result = 0.0;
			for (int ci = 0; ci < c; ++ci)
				result += LL.at<double>(r * cols + ci) * LL.at<double>(c * cols + ci);
			result = A->at<double>(r * cols + c) - result;
			LL.at<double>(k++)= result / LL.at<double>(c * cols + c);
			//(*LL++) = result / L[c * cols + c];
		}

		/* Compute diagonal entry. */
		{
			Mat* L_row_ptr = &LL;
			//Mat* L_row_ptr = &LL + r * cols;
			double result = 0.0;
			for (int c = 0; c < r; ++c)
				result += MATH_POW2(L_row_ptr->at<double>(c + r * cols));
			result = std::max(0.0, A->at<double>(r * cols + r) - result);
			LL.at<double>(k++) = std::sqrt(result);
		}

		/* Set right-of-diagonal entries zero. */
		for (int c = r + 1; c < cols; ++c)
			LL.at<double>(k++) = 0;

	}
	*L = LL;
}

void invert_lower_diagonal(Mat const* A, int const cols, Mat* A_inv)
{
	if (A == A_inv)
		throw std::invalid_argument("In-place inversion not possible");

	for (int r = 0; r < cols; ++r)
	{
		//Mat const* A_row_ptr = A + r * cols;
		Mat const* A_row_ptr = A ;
		//Mat* A_inv_row_ptr = A_inv + r * cols;
		Mat* A_inv_row_ptr = A_inv ;
		/* Compute left-of-diagonal entries. */
		for (int c = 0; c < r; ++c)
		{
			double result = 0.0;
			for (int ci = 0; ci < r; ++ci)
				result -= A_row_ptr->at<double>(ci + r * cols) * A_inv->at<double>(ci * cols + c);
			A_inv_row_ptr->at<double>(c + r * cols) = result / A_row_ptr->at<double>(r + r * cols);
		}

		/* Compute diagonal entry. */
		A_inv_row_ptr->at<double>(r + r * cols) = 1 / A_row_ptr->at<double>(r + r * cols);

		/* Set right-of-diagonal entries zero. */
		for (int c = r + 1; c < cols; ++c)
			A_inv_row_ptr->at<double>(c + r * cols) = 0;
	}
}


/**
 *用Cholesky变换对称正定矩阵。
 */
void cholesky_invert_inplace(Mat* A, int const cols)
{
	cholesky_decomposition(A, cols, A);
	Mat* tmp = new Mat(cols, cols, CV_64FC1); 
	invert_lower_diagonal(A, cols, tmp);
	*A = (*tmp).t()* (*tmp);
	//math::matrix_transpose_multiply(tmp, cols, cols, A);
	delete tmp;
}


/*
	 * Inverts a symmetric, positive definite matrix with NxN bocks on its
	 * diagonal using Cholesky decomposition. All other entries must be zero.
	 */
void
invert_block_matrix_NxN_inplace(SparseMatrix<double>* A, int blocksize)
{
	if (A->num_rows() != A->num_cols())
		throw std::invalid_argument("Block matrix must be square");
	if (A->num_non_zero() != A->num_rows() * blocksize)
		throw std::invalid_argument("Invalid number of non-zeros");

	int const bs2 = blocksize * blocksize;
	Mat matrix_block(blocksize, blocksize, CV_64FC1);
	//std::vector<double> matrix_block(bs2);
	for (double* iter = A->begin(); iter != A->end(); )
	{
		double* iter_backup = iter;
		for (int i = 0; i < bs2; ++i)
			matrix_block.at<double>(i) = *(iter++);

		cholesky_invert_inplace(&matrix_block, blocksize);

		iter = iter_backup;
		for (int i = 0; i < bs2; ++i)
			if (std::isfinite(matrix_block.at<double>(i)))
				*(iter++) = matrix_block.at<double>(i);
			else
				*(iter++) = 0.0;
	}
}


LinearSolver::Status my_solve_schur(
	SparseMatrixType const& jac_cams,
	SparseMatrixType const& jac_points,
	DenseVectorType const& values,
	DenseVectorType* delta_x) {
	/*
	 *   雅阁比矩阵:
	 *           J = [Jc Jp]
	 *   Jc是与相机相关的模块,Jp是与三维点相关的模块。
	 *   正规方程
	 *          (J^TJ + lambda*I)delta_x = J^T(x - F)
	 *   进一步写为
	 *   [ Jcc+ lambda*Icc   Jcp            ][delta_c]= [v]
	 *   [ Jxp               Jpp+lambda*Ipp ][delta_p]  [w]
	 *
	 *   B = Jcc, E = Jcp, C = Jpp
	 *  其中 Jcc = Jc^T* Jc, Jcx = Jc^T*Jx, Jxc = Jx^TJc, Jxx = Jx^T*Jx
	 *      v = Jc^T(F-x), w = Jx^T(F-x), deta_x = [delta_c; delta_p]
	 */

	 // 误差向量
	DenseVectorType const& F = values;
	// 关于相机的雅阁比矩阵
	SparseMatrixType const& Jc = jac_cams;
	// 关于三维点的雅阁比矩阵
	SparseMatrixType const& Jp = jac_points;
	SparseMatrixType JcT = Jc.transpose();
	SparseMatrixType JpT = Jp.transpose();

	// 构造正规方程
	SparseMatrixType B, C;
	// B = Jc^T* Jc
	matrix_block_column_multiply(Jc, camera_block_dim, &B);
	// C = Jp^T*Jp
	matrix_block_column_multiply(Jp, 3, &C);
	// E = Jc^T*Jp
	SparseMatrixType E = JcT.multiply(Jp);

	/* Assemble two values vectors. */
	DenseVectorType v = JcT.multiply(F);
	DenseVectorType w = JpT.multiply(F);
	v.negate_self();
	w.negate_self();

	/* 以矩阵B和C的对角元素重新构建对角阵*/
	//    SparseMatrixType B_diag = B.diagonal_matrix();
	//    SparseMatrixType C_diag = C.diagonal_matrix();

	/* 添加信赖域 */
	C.mult_diagonal(1.0 + 1.0 / trust_region_radius);
	B.mult_diagonal(1.0 + 1.0 / trust_region_radius);

	/* 求解C矩阵的逆C = inv(Jx^T+Jx + lambda*Ixx)*/
	invert_block_matrix_3x3_inplace(&C);

	/* 计算S矩阵的Schur补用于高斯消元. */
	SparseMatrixType ET = E.transpose();

	// S = (Jcc+lambda*Icc) - Jc^T*Jx*inv(Jxx+ lambda*Ixx)*Jx^T*Jc
	SparseMatrixType S = B.subtract(E.multiply(C).multiply(ET));
	// rhs = v -  Jc^T*Jx*inv(Jxx+ lambda*Ixx)*w
	DenseVectorType rhs = v.subtract(E.multiply(C.multiply(w)));

	/* Compute pre-conditioner for linear system. */
	//SparseMatrixType precond = S.diagonal_matrix();
	//precond.cwise_invert();
	SparseMatrixType precond = B;
	invert_block_matrix_NxN_inplace(&precond, camera_block_dim);

	/* 用共轭梯度法求解相机参数. */
	DenseVectorType delta_y(Jc.num_cols());

	typedef ConjugateGradient<double> CGSolver;
	CGSolver::Options cg_opts;
	cg_opts.max_iterations = cg_max_iterations;
	cg_opts.tolerance = 1e-20;
	CGSolver solver(cg_opts);
	CGSolver::Status cg_status;
	cg_status = solver.solve(S, rhs, &delta_y, &precond);

	LinearSolver::Status status;
	status.num_cg_iterations = cg_status.num_iterations;
	switch (cg_status.info) {
	case CGSolver::CG_CONVERGENCE:
		status.success = true;
		break;
	case CGSolver::CG_MAX_ITERATIONS:
		status.success = true;
		break;
	case CGSolver::CG_INVALID_INPUT:
		std::cout << "BA: CG failed (invalid input)" << std::endl;
		status.success = false;
		return status;
	default:
		break;
	}

	/* 将相机参数带入到第二个方程中,求解三维点的参数. */
	/*E= inv(Jp^T Jp) (JpT.multiply(F)-Jc^T * Jp * delta_y)*/
	DenseVectorType delta_z = C.multiply(w.subtract(ET.multiply(delta_y)));

	/* Fill output vector. */
	std::size_t const jac_cam_cols = Jc.num_cols();
	std::size_t const jac_point_cols = Jp.num_cols();
	std::size_t const jac_cols = jac_cam_cols + jac_point_cols;

	if (delta_x->size() != jac_cols)
		delta_x->resize(jac_cols, 0.0);
	for (std::size_t i = 0; i < jac_cam_cols; ++i)
		delta_x->at(i) = delta_y[i];
	for (std::size_t i = 0; i < jac_point_cols; ++i)
		delta_x->at(jac_cam_cols + i) = delta_z[i];

	return status;
}

/**
 * /description 计算观察点坐标(x,y),相遇对相机参数和三维点坐标的雅阁比矩阵
 * @param cam
 * @param point
 * @param cam_x_ptr
 * @param cam_y_ptr
 * @param point_x_ptr
 * @param point_y_ptr
 */
void my_jacobian(Camera const& cam,Point3D const& point,double* cam_x_ptr, double* cam_y_ptr,
	double* point_x_ptr, double* point_y_ptr)
{
	const double f = cam.focal_length;
	const double *R = cam.rotation;
	const double *t = cam.translation;
	const double *X = point.pos;
	const double k0 = cam.distortion[0];
	const double k1 = cam.distortion[1];

	const double xc = R[0] * X[0] + R[1] * X[1] + R[2] * X[2] + t[0];
	const double yc = R[3] * X[0] + R[4] * X[1] + R[5] * X[2] + t[1];
	const double zc = R[6] * X[0] + R[7] * X[1] + R[8] * X[2] + t[2];

	const double x = xc / zc;
	const double y = yc / zc;

	const double r2 = x * x + y * y;
	const double distort = 1.0 + (k0 + k1 * r2)*r2;

	const double u = f * distort*x;
	const double v = f * distort*y;

	/*关于焦距的偏导数*/
	cam_x_ptr[0] = distort * x;
	cam_y_ptr[0] = distort * y;


	/*计算关于径向畸变函数k0, k1的偏导数*/
	// 计算中间变量
	const double u_deriv_distort = f * x;
	const double v_deriv_distort = f * y;
	const double distort_deriv_k0 = r2;
	const double distort_deriv_k1 = r2 * r2;

	cam_x_ptr[1] = u_deriv_distort * distort_deriv_k0;
	cam_x_ptr[2] = u_deriv_distort * distort_deriv_k1;

	cam_y_ptr[1] = v_deriv_distort * distort_deriv_k0;
	cam_y_ptr[2] = v_deriv_distort * distort_deriv_k1;


	// 计算中间变量 (x,y)关于(xc, yc, zc)的偏导数
	const double x_deriv_xc = 1 / zc; const double x_deriv_yc = 0;    const double x_deriv_zc = -x / zc;
	const double y_deriv_xc = 0; const double y_deriv_yc = 1 / zc; const double y_deriv_zc = -y / zc;

	// 计算u, v关于x, y的偏导数
	const double u_deriv_x = f * distort;
	const double v_deriv_y = f * distort;

	// 计算中间变量distort关于r2的偏导数
	const double distort_deriv_r2 = k0 + 2 * k1*r2;

	// 计算中间变量r2关于xc, yc, zc的偏导数
	const double r2_deriv_xc = 2 * x / zc;
	const double r2_deriv_yc = 2 * y / zc;
	const double r2_deriv_zc = -2 * r2 / zc;

	// 计算中间变量distort关于xc, yc, zc的偏导数
	const double distort_deriv_xc = distort_deriv_r2 * r2_deriv_xc;
	const double distort_deriv_yc = distort_deriv_r2 * r2_deriv_yc;
	const double distort_deriv_zc = distort_deriv_r2 * r2_deriv_zc;

	// 计算(u,v)关于xc, yc, zc的偏导数
	const double u_deriv_xc = u_deriv_distort * distort_deriv_xc + u_deriv_x * x_deriv_xc;
	const double u_deriv_yc = u_deriv_distort * distort_deriv_yc + u_deriv_x * x_deriv_yc;
	const double u_deriv_zc = u_deriv_distort * distort_deriv_zc + u_deriv_x * x_deriv_zc;

	const double v_deriv_xc = v_deriv_distort * distort_deriv_xc + v_deriv_y * y_deriv_xc;
	const double v_deriv_yc = v_deriv_distort * distort_deriv_yc + v_deriv_y * y_deriv_yc;
	const double v_deriv_zc = v_deriv_distort * distort_deriv_zc + v_deriv_y * y_deriv_zc;

	/* 计算关于平移向量的t0, t1, t2的偏导数*/
	const double xc_deriv_t0 = 1;
	const double yc_deriv_t1 = 1;
	const double zc_deriv_t2 = 1;

	cam_x_ptr[3] = u_deriv_xc * xc_deriv_t0;
	cam_x_ptr[4] = u_deriv_yc * yc_deriv_t1;
	cam_x_ptr[5] = u_deriv_zc * zc_deriv_t2;

	cam_y_ptr[3] = v_deriv_xc * xc_deriv_t0;
	cam_y_ptr[4] = v_deriv_yc * yc_deriv_t1;
	cam_y_ptr[5] = v_deriv_zc * zc_deriv_t2;
	/* 计算关于旋转矩阵(表示为角轴向量w0, w1, w2)的偏导数 */
	const double rx = R[0] * X[0] + R[1] * X[1] + R[2] * X[2];
	const double ry = R[3] * X[0] + R[4] * X[1] + R[5] * X[2];
	const double rz = R[6] * X[0] + R[7] * X[1] + R[8] * X[2];
	const double xc_deriv_w0 = 0;   const double xc_deriv_w1 = rz; const double xc_deriv_w2 = -ry;
	const double yc_deriv_w0 = -rz; const double yc_deriv_w1 = 0; const double yc_deriv_w2 = rx;
	const double zc_deriv_w0 = ry;  const double zc_deriv_w1 = -rx; const double zc_deriv_w2 = 0;

	cam_x_ptr[6] = u_deriv_yc * yc_deriv_w0 + u_deriv_zc * zc_deriv_w0;
	cam_x_ptr[7] = u_deriv_xc * xc_deriv_w1 + u_deriv_zc * zc_deriv_w1;
	cam_x_ptr[8] = u_deriv_xc * xc_deriv_w2 + u_deriv_yc * yc_deriv_w2;

	cam_y_ptr[6] = v_deriv_yc * yc_deriv_w0 + v_deriv_zc * zc_deriv_w0;
	cam_y_ptr[7] = v_deriv_xc * xc_deriv_w1 + v_deriv_zc * zc_deriv_w1;
	cam_y_ptr[8] = v_deriv_xc * xc_deriv_w2 + v_deriv_yc * yc_deriv_w2;


	/* 计算关于三维点坐标X,Y,X的偏导数*/
	const double xc_deriv_X = R[0]; const double xc_deriv_Y = R[1]; const double xc_deriv_Z = R[2];
	const double yc_deriv_X = R[3]; const double yc_deriv_Y = R[4]; const double yc_deriv_Z = R[5];
	const double zc_deriv_X = R[6]; const double zc_deriv_Y = R[7]; const double zc_deriv_Z = R[8];

	point_x_ptr[0] = u_deriv_xc * xc_deriv_X + u_deriv_yc * yc_deriv_X + u_deriv_zc * zc_deriv_X;
	point_x_ptr[1] = u_deriv_xc * xc_deriv_Y + u_deriv_yc * yc_deriv_Y + u_deriv_zc * zc_deriv_Y;
	point_x_ptr[2] = u_deriv_xc * xc_deriv_Z + u_deriv_yc * yc_deriv_Z + u_deriv_zc * zc_deriv_Z;

	point_y_ptr[0] = v_deriv_xc * xc_deriv_X + v_deriv_yc * yc_deriv_X + v_deriv_zc * zc_deriv_X;
	point_y_ptr[1] = v_deriv_xc * xc_deriv_Y + v_deriv_yc * yc_deriv_Y + v_deriv_zc * zc_deriv_Y;
	point_y_ptr[2] = v_deriv_xc * xc_deriv_Z + v_deriv_yc * yc_deriv_Z + v_deriv_zc * zc_deriv_Z;

}

/**
 * \description 计算均方误差
 * @param vector_f
 * @return
 */
double compute_mse(DenseVectorType const& vector_f) {
	double mse = 0.0;
	for (std::size_t i = 0; i < vector_f.size(); ++i)
		mse += vector_f[i] * vector_f[i];
	return mse / static_cast<double>(vector_f.size() / 2);
}

/**
 * /descripition 根据求得的delta_x, 更新相机参数和三维点
 * @param delta_x
 * @param cameras
 * @param points
 */
void
update_parameters(DenseVectorType const& delta_x
	, std::vector<Camera>*cameras
	, std::vector<Point3D>*points)
{
	/* Update cameras. */
	std::size_t total_camera_params = 0;
	for (std::size_t i = 0; i < cameras->size(); ++i) {
		update_camera(cameras->at(i),
			delta_x.data() + num_cam_params * i,
			&cameras->at(i));
		total_camera_params = cameras->size() * num_cam_params;
	}

	/* Update points. */
	for (std::size_t i = 0; i < points->size(); ++i) {
		update_point(points->at(i),
			delta_x.data() + total_camera_params + i * 3,
			&points->at(i));
	}
}


/**
 * \description 构造雅阁比矩阵,采用稀疏矩阵形式,
 *       关于相机参数的雅阁比矩阵大小为:(2*observations.size()) x (num_cameras*9)
 *       关于三维点坐标的雅阁比矩阵大小为:(2*observation.size()) x (num_points*3)
 * @param jac_cam-- 观察点相对于相机参数的雅阁比矩阵
 * @param jac_points--观察点相对于三维点的雅阁比矩阵
 */
void analytic_jacobian(SparseMatrixType* jac_cam, SparseMatrixType* jac_points)
{
	assert(jac_cam);
	assert(jac_points);
	// 相机和三维点jacobian矩阵的行数都是n_observations*2
	// 相机jacobian矩阵jac_cam的列数是n_cameras* n_cam_params
	// 三维点jacobian矩阵jac_points的列数是n_points*3
	std::size_t const camera_cols = cameras.size() * num_cam_params;
	std::size_t const point_cols = points.size() * 3;
	std::size_t const jacobi_rows = observations.size() * 2;

	// 定义稀疏矩阵的基本元素
	SparseMatrixType::Triplets cam_triplets, point_triplets;
	cam_triplets.reserve(observations.size() * 2 * num_cam_params);
	point_triplets.reserve(observations.size() * 2 * 3);


	double cam_x_ptr[9], cam_y_ptr[9], point_x_ptr[3], point_y_ptr[3];
	// 对于每一个观察到的二维点
	for (std::size_t i = 0; i < observations.size(); ++i) {

		// 获取二维点,obs.point_id 三维点的索引,obs.camera_id 相机的索引
		Observation const &obs = observations[i];
		// 三维点坐标
		Point3D const &p3d = points[obs.point_id];
		// 相机参数
		Camera const &cam = cameras[obs.camera_id];

		/*对一个三维点和相机求解偏导数*/
		my_jacobian(cam, p3d, cam_x_ptr, cam_y_ptr, point_x_ptr, point_y_ptr);

		/*观察点对应雅各比矩阵的行,第i个观察点在雅各比矩阵的位置是2*i, 2*i+1*/
		std::size_t row_x = i * 2 + 0;
		std::size_t row_y = i * 2 + 1;

		/*jac_cam中相机对应的列数为camera_id* n_cam_params*/
		std::size_t cam_col = obs.camera_id * num_cam_params;

		/*jac_points中三维点对应的列数为point_id* 3*/
		std::size_t point_col = obs.point_id * 3;

		for (int j = 0; j < num_cam_params; ++j) {
			cam_triplets.push_back(SparseMatrixType::Triplet(row_x, cam_col + j, cam_x_ptr[j]));
			cam_triplets.push_back(SparseMatrixType::Triplet(row_y, cam_col + j, cam_y_ptr[j]));
		}

		for (int j = 0; j < 3; ++j) {
			point_triplets.push_back(SparseMatrixType::Triplet(row_x, point_col + j, point_x_ptr[j]));
			point_triplets.push_back(SparseMatrixType::Triplet(row_y, point_col + j, point_y_ptr[j]));
		}
	}


	if (jac_cam != nullptr) {
		jac_cam->allocate(jacobi_rows, camera_cols);
		jac_cam->set_from_triplets(cam_triplets);
	}

	if (jac_points != nullptr) {
		jac_points->allocate(jacobi_rows, point_cols);
		jac_points->set_from_triplets(point_triplets);
	}
}

void lm_optimization(std::vector<Camera>* cameras, std::vector<Point3D>* points, std::vector<Observation>* observations)
{
	/* 1.0 初始化 */
	DenseVectorType F, F_new;
	// 计算重投影误差
	compute_reprojection_errors(&F, nullptr, cameras, points, observations);// todo F 是误差向量
	// 计算初始的均方误差
	double current_mse = compute_mse(F);
	initial_mse = current_mse;
	final_mse = current_mse;

	std::cout << current_mse;
	

	// 设置共轭梯度法的相关参数
	trust_region_radius = TRUST_REGION_RADIUS_INIT;


	/* Levenberg-Marquard 算法. */
	for (int lm_iter = 0; ; ++lm_iter) 
	{

		// 判断终止条件,均方误差小于一定阈值
		if (current_mse < lm_mse_threshold) {
			std::cout << "BA: Satisfied MSE threshold." << std::endl;
			break;
		}

		//1.0 计算雅阁比矩阵
		SparseMatrixType Jc, Jp;
		analytic_jacobian(&Jc, &Jp);
		//Jc.debug();
		//Jp.debug();

		
		//2.0 预置共轭梯梯度法对正规方程进行求解*/
		DenseVectorType delta_x;
		LinearSolver::Status cg_status = my_solve_schur(Jc, Jp, F, &delta_x);

		//3.0 根据计算得到的偏移量,重新计算冲投影误差和均方误差,用于判断终止条件和更新条件.
		double new_mse, delta_mse, delta_mse_ratio = 1.0;

		// 正规方程求解成功的情况下
		if (cg_status.success) {
			/*重新计算相机和三维点,计算重投影误差,注意原始的相机参数没有被更新*/
			compute_reprojection_errors(&F_new, &delta_x, cameras, points, observations);
			/* 计算新的残差值 */
			new_mse = compute_mse(F_new);
			/* 均方误差的绝对变化值和相对变化率*/
			delta_mse = current_mse - new_mse;
			delta_mse_ratio = 1.0 - new_mse / current_mse;
		}
		// 正规方程求解失败的情况下
		else {
			new_mse = current_mse;
			delta_mse = 0.0;
		}

		// new_mse < current_mse表示残差值减少
		bool successful_iteration = delta_mse > 0.0;

		/*
		 * 如果正规方程求解成功,则更新相机参数和三维点坐标,并且增大信赖域的尺寸,使得求解方式
		 * 趋近于高斯牛顿法
		 */
		if (successful_iteration) {
			std::cout << "BA: #" << std::setw(2) << std::left << lm_iter
				<< " success" << std::right
				<< ", MSE " << std::setw(11) << current_mse
				<< " -> " << std::setw(11) << new_mse
				<< ", CG " << std::setw(3) << cg_status.num_cg_iterations
				<< ", TRR " << trust_region_radius
				<< ", MSE Ratio: " << delta_mse_ratio
				<< std::endl;

			num_lm_iterations += 1;
			num_lm_successful_iterations += 1;

			/* 对相机参数和三点坐标进行更新 */
			update_parameters(delta_x, cameras, points);

			std::swap(F, F_new);
			current_mse = new_mse;

			if (delta_mse_ratio < lm_delta_threshold) {
				std::cout << "BA: Satisfied delta mse ratio threshold of "
					<< lm_delta_threshold << std::endl;
				break;
			}

			// 增大信赖域大小
			trust_region_radius *= TRUST_REGION_RADIUS_GAIN;
		}
		else {
			std::cout << "BA: #" << std::setw(2) << std::left << lm_iter
				<< " failure" << std::right
				<< ", MSE " << std::setw(11) << current_mse
				<< ",    " << std::setw(11) << " "
				<< " CG " << std::setw(3) << cg_status.num_cg_iterations
				<< ", TRR " << trust_region_radius
				<< std::endl;

			num_lm_iterations += 1;
			num_lm_unsuccessful_iterations += 1;
			// 求解失败的减小信赖域尺寸
			trust_region_radius *= TRUST_REGION_RADIUS_DECREMENT;
		}

		/* 判断是否超过最大的迭代次数. */
		if (lm_iter + 1 >= lm_max_iterations) {
			std::cout << "BA: Reached maximum LM iterations of "
				<< lm_max_iterations << std::endl;
			break;
		}
	}

	final_mse = current_mse;
}



int main()
{

    /* 加载数据 */
	load_data("../data/test_ba.txt", cameras, points, observations);

	lm_optimization(&cameras, &points, &observations);

	// 将优化后的结果重新赋值
	std::vector<CameraPose> new_cam_poses(2);
	std::vector<Vec2f> radial_distortion(2);
	std::vector<Vec3f> new_pts_3d(points.size());
	
	for (int i = 0; i < cameras.size(); i++) {
		for (int j = 0; j < 3; j++)
		{
			new_cam_poses[i].t.at<double>(j) = cameras[i].translation[j];
		}
		for (int j = 0; j < 9; j++)
		{
			new_cam_poses[i].R.at<double>(j) = cameras[i].rotation[j];
		}
		//std::copy(cameras[i].translation, cameras[i].translation + 3, new_cam_poses[i].t.begin());
		//std::copy(cameras[i].rotation, cameras[i].rotation + 9, new_cam_poses[i].R.begin());
		radial_distortion[i] = Vec2f(cameras[i].distortion[0], cameras[i].distortion[1]);
		new_cam_poses[i].set_k_matrix(cameras[i].focal_length, 0.0, 0.0);
	}
	for (int i = 0; i < new_pts_3d.size(); i++) {
		for (int j = 0; j < 3; j++)
		{
			new_pts_3d[i] = points[i].pos[j];
		}
	}

	// 输出优化信息
	std::cout << "Params after BA: " << std::endl;
	std::cout << "  f: " << new_cam_poses[0].get_focal_length() << std::endl;
	std::cout << "  distortion: " << radial_distortion[0][0] << ", " << radial_distortion[0][1] << std::endl;
	std::cout << "  R: " << new_cam_poses[0].R << std::endl;
	std::cout << "  t: " << new_cam_poses[0].t << std::endl;

	// 输出优化信息
	std::cout << "Params after BA: " << std::endl;
	std::cout << "  f: " << new_cam_poses[1].get_focal_length() << std::endl;
	std::cout << "  distortion: " << radial_distortion[1][0] << ", " << radial_distortion[1][1] << std::endl;
	std::cout << "  R: " << new_cam_poses[1].R << std::endl;
	std::cout << "  t: " << new_cam_poses[1].t << std::endl;


	std::cout << "points 3d: " << std::endl;
	for (int i = 0; i < points.size(); i++) {
		std::cout << points[i].pos[0] << ", " << points[i].pos[1] << ", " << points[i].pos[2] << std::endl;
	}

	//    Params after BA:
	//    f: 0.919446
	//    distortion: -0.108421, 0.103782
	//    R: 0.999999 -0.00068734 -0.00135363
	//    0.000675175 0.999952 -0.0104268
	//    0.0013597 0.0104261 0.999952
	//    t: 0.00276221 0.0588868 -0.128463

	//    Params after BA:
	//    f: 0.920023
	//    distortion: -0.106701, 0.104344
	//    R: 0.999796 -0.0127484 0.0156791
	//    0.0128673 0.999897 -0.00735337
	//              -0.0155827 0.00755345 0.999857
	//    t: 0.0814124 0.93742 -0.0895658

	//    points 3d:
	//    1.36957, -1.17132, 7.04854
	//    0.0225931, 0.978747, 7.48085


	return 0;
}

标签:std,捆绑,const,deriv,double,void,opencv,调整,size
来源: https://blog.csdn.net/AAAA202012/article/details/118571323