输入
数据处理
- 相机是手持的,会导致运动模糊
- 进行模糊评估,从输入图像序列中选取一个子集
- 选定一帧后,在该帧后的
(
t
?
,
t
+
)
(t-,t+)
(t?,t+)时间间隔内选取模糊程度最低的一帧
- 实际中使用
(
t
?
,
t
+
)
=
(
1
,
5
)
s
(t-,t+)=(1,5)s
(t?,t+)=(1,5)s
- 关键帧表示为
I
i
I_i
Ii?
- 将mesh网格渲染到
I
i
I_i
Ii?的图像平面上
- 使用距离
I
i
I_i
Ii?最近的深度图算出相机pose
- 通过比较每个顶点的深度值和相应的深度进行可见性测试,由此可以确定关键帧
I
i
I_i
Ii?对应的顶点子集
- 这个顶点子集进一步通过投影点到图像边缘的距离和深度不连续性进行滤除。保留的顶点表示为
P
i
{P_i}
Pi?
Camera Pose Optimization
论文解读
整体思路
- 输入:重建好的三维点云和一组关键帧,以及每一张关键帧相应的相机姿态
- 原因:重建过程中估计的相机姿态存在一定的误差,直接进行纹理映射会产生重影模糊等问题
- 优化过程
- 首先根据点云3D坐标和关键帧的相机位姿,将3D顶点其投影到2D平面,得到若干张投影图
- 根据每个顶点投影后的uv坐标获取到关键帧相应位置的像素值
- 每个顶点在每个可见该顶点的关键帧中都可以得到一个对应的像素值
Γ
i
(
p
,
T
i
)
\Gamma_i(p, T_i)
Γi?(p,Ti?),将这些像素值求平均就是 C§
- 目标:让 C§
Γ
i
(
p
,
T
i
)
\Gamma_i(p, T_i)
Γi?(p,Ti?)尽可能的接近。
目标函数设置
- 输入
- 关键帧集合
{
I
i
}
\{I_i\}
{Ii?}
- 关键帧对应的顶点子集
{
P
i
}
\{P_i\}
{Pi?}
- 优化物:位姿矩阵
{
T
i
}
\{T_i\}
{Ti?}
- notes:使用齐次坐标
-
P
i
?
P
3
P_i \subset \Bbb P^3
Pi??P3
-
T
i
T_i
Ti?为
4
×
4
4\times4
4×4的矩阵
- 齐次坐标(homogeneous coordinates)
- 在投影空间中,两条平行线会相交于无限远处的一点,在 ( X , Y ) 的基础上增加一个新的分量w,变成 ( x , y , w ) X = x / w , Y = y / w
- 最终目标:
-
?
p
∈
P
\forall p \in P
?p∈P,最大化p在所有相关联图像中
I
p
=
{
I
i
:
p
∈
P
}
I_p = \{I_i : p \in P\}
Ip?={Ii?:p∈P}的颜色一致性
刚体变换(Rigid transformation)
这里
{
T
i
}
\{T_i\}
{Ti?}主要是考虑刚体变换(如旋转,平移),非刚体矫正在下一部分进行考虑
目标函数
目标函数为:
E
(
C
,
T
)
=
∑
i
∑
p
∈
P
(
C
(
p
)
?
Γ
i
(
p
,
T
i
)
)
2
E(C,T)=\sum_i\sum_{p\in P}(C(p)-\Gamma_i(p,T_i))^2
E(C,T)=i∑?p∈P∑?(C(p)?Γi?(p,Ti?))2
-
Γ
i
(
p
,
T
i
)
\Gamma_i(p,T_i)
Γi?(p,Ti?)是点p在图像
I
i
I_i
Ii?对应投影点的颜色值 -
C§是集合
{
Γ
i
(
p
,
T
i
)
}
I
i
∈
I
p
\{\Gamma_i(p,T_i)\}_{I_i\in I_p}
{Γi?(p,Ti?)}Ii?∈Ip?? (所有和点p相关的纹理图像的平均值) -
计算:
Γ
i
(
u
(
g
(
p
,
T
i
)
)
)
\Gamma_i(u(g(p,T_i)))
Γi?(u(g(p,Ti?)))
-
刚体变换 g -
投影变换 u -
颜色变换
Γ
i
\Gamma_i
Γi?
g
(
p
,
T
i
)
=
T
i
p
u
(
g
x
,
g
y
,
g
z
,
g
w
)
=
(
g
x
f
x
g
z
+
c
x
,
g
y
f
y
g
z
+
c
y
)
T
g(p,T_i)= T_i p \\ u(g_x,g_y,g_z,g_w)=(\frac{g_xf_x}{g_z}+c_x,\frac{g_yf_y}{g_z}+c_y)^T
g(p,Ti?)=Ti?pu(gx?,gy?,gz?,gw?)=(gz?gx?fx??+cx?,gz?gy?fy??+cy?)T -
可以推导出最终目标函数和残差项
E
(
C
,
T
)
=
∑
i
∑
p
∈
P
r
i
,
p
2
r
i
,
p
=
C
(
p
)
?
Γ
i
(
u
(
g
(
p
,
T
i
)
)
)
E( C, T)=\sum_i\sum_{ p\in P}r_{i,p}^2 \\ r_{i,p}= C( p)-\Gamma_i( u( g( p, T_i)))
E(C,T)=i∑?p∈P∑?ri,p2?ri,p?=C(p)?Γi?(u(g(p,Ti?)))
高斯牛顿法求解
交替优化法求解
- 目标:高斯牛顿法计算复杂度过高,十分耗时,因此作者提出交替优化法
- 过程
- 当T固定时,非线性最小二乘问题变成了线性最小二乘问题(计算p在各个与其相关的图片上的投影点的颜色平均值)
-
C
(
p
)
=
1
n
P
∑
Γ
i
(
p
,
T
i
)
C(p)=\frac{1}{n_ P}\sum \Gamma_i( p, T_i)
C(p)=nP?1?∑Γi?(p,Ti?)
-
n
P
n_P
nP?是与vertex p相关的图片个数
- 当C固定时,目标函数分解为多个独立的目标函数
-
E
i
(
T
)
=
∑
p
∈
P
i
r
i
,
p
2
E_i( T)=\sum_{ p \in P_i}r_{i, p}^2
Ei?(T)=∑p∈Pi??ri,p2?每
-
E
i
(
T
)
E_i(T)
Ei?(T) 只和
ξ
i
\xi_i
ξi?的六个变量有关,在每次迭代中,使用高斯牛顿法更新这六个变量
- 每个
T
i
T_i
Ti?都是相互独立的,因此可以并行计算
源码解析
注:本文只选取核心实现代码,均来源于open3d开源c++源代码,网址链接
雅各比矩阵具体求解实现方法
文中写得比较含糊其辞,个人认为这位大佬的解答推导很全面,不做更多解读
ColorMapUtils.cpp
std::vector<geometry::Image> CreateDepthBoundaryMasks(
const std::vector<geometry::Image>& images_depth,
double depth_threshold_for_discontinuity_check,
int half_dilation_kernel_size_for_discontinuity_map) {
auto n_images = images_depth.size();
std::vector<geometry::Image> masks;
for (size_t i = 0; i < n_images; i++) {
utility::LogDebug("[MakeDepthMasks] geometry::Image {:d}/{:d}", i,
n_images);
masks.push_back(*images_depth[i].CreateDepthBoundaryMask(
depth_threshold_for_discontinuity_check,
half_dilation_kernel_size_for_discontinuity_map));
}
return masks;
}
std::tuple<std::vector<std::vector<int>>, std::vector<std::vector<int>>>
CreateVertexAndImageVisibility(
const geometry::TriangleMesh& mesh,
const std::vector<geometry::Image>& images_depth,
const std::vector<geometry::Image>& images_mask,
const camera::PinholeCameraTrajectory& camera_trajectory,
double maximum_allowable_depth,
double depth_threshold_for_visibility_check) {
size_t n_camera = camera_trajectory.parameters_.size();
size_t n_vertex = mesh.vertices_.size();
std::vector<std::vector<int>> visibility_image_to_vertex;
visibility_image_to_vertex.resize(n_camera);
std::vector<std::vector<int>> visibility_vertex_to_image;
visibility_vertex_to_image.resize(n_vertex);
#pragma omp parallel for schedule(static) \
num_threads(utility::EstimateMaxThreads())
for (int camera_id = 0; camera_id < int(n_camera); camera_id++) {
for (int vertex_id = 0; vertex_id < int(n_vertex); vertex_id++) {
Eigen::Vector3d X = mesh.vertices_[vertex_id];
float u, v, d;
std::tie(u, v, d) = Project3DPointAndGetUVDepth(
X, camera_trajectory.parameters_[camera_id]);
int u_d = int(round(u));
int v_d = int(round(v));
if (d < 0.0 ||
!images_depth[camera_id].TestImageBoundary(u_d, v_d)) {
continue;
}
float d_sensor =
*images_depth[camera_id].PointerAt<float>(u_d, v_d);
if (d_sensor > maximum_allowable_depth) {
continue;
}
if (*images_mask[camera_id].PointerAt<uint8_t>(u_d, v_d) == 255) {
continue;
}
if (std::fabs(d - d_sensor) >=
depth_threshold_for_visibility_check) {
continue;
}
visibility_image_to_vertex[camera_id].push_back(vertex_id);
#pragma omp critical(CreateVertexAndImageVisibility)
{ visibility_vertex_to_image[vertex_id].push_back(camera_id); }
}
}
for (int camera_id = 0; camera_id < int(n_camera); camera_id++) {
size_t n_visible_vertex = visibility_image_to_vertex[camera_id].size();
utility::LogDebug(
"[cam {:d}]: {:d}/{:d} ({:.5f}%) vertices are visible",
camera_id, n_visible_vertex, n_vertex,
double(n_visible_vertex) / n_vertex * 100);
}
return std::make_tuple(visibility_vertex_to_image,
visibility_image_to_vertex);
}
void SetProxyIntensityForVertex(
const geometry::TriangleMesh& mesh,
const std::vector<geometry::Image>& images_gray,
const utility::optional<std::vector<ImageWarpingField>>& warping_fields,
const camera::PinholeCameraTrajectory& camera_trajectory,
const std::vector<std::vector<int>>& visibility_vertex_to_image,
std::vector<double>& proxy_intensity,
int image_boundary_margin) {
auto n_vertex = mesh.vertices_.size();
proxy_intensity.resize(n_vertex);
#pragma omp parallel for schedule(static) \
num_threads(utility::EstimateMaxThreads())
for (int i = 0; i < int(n_vertex); i++) {
proxy_intensity[i] = 0.0;
float sum = 0.0;
for (size_t iter = 0; iter < visibility_vertex_to_image[i].size();
iter++) {
int j = visibility_vertex_to_image[i][iter];
float gray;
bool valid = false;
if (warping_fields.has_value()) {
std::tie(valid, gray) = QueryImageIntensity<float>(
images_gray[j], warping_fields.value()[j],
mesh.vertices_[i], camera_trajectory.parameters_[j],
utility::nullopt, image_boundary_margin);
} else {
std::tie(valid, gray) = QueryImageIntensity<float>(
images_gray[j], utility::nullopt, mesh.vertices_[i],
camera_trajectory.parameters_[j], utility::nullopt,
image_boundary_margin);
}
if (valid) {
sum += 1.0;
proxy_intensity[i] += gray;
}
}
if (sum > 0) {
proxy_intensity[i] /= sum;
}
}
}
RigidOptimizer.cpp
static void ComputeJacobianAndResidualRigid(
int row,
Eigen::Vector6d& J_r,
double& r,
double& w,
const geometry::TriangleMesh& mesh,
const std::vector<double>& proxy_intensity,
const geometry::Image& images_gray,
const geometry::Image& images_dx,
const geometry::Image& images_dy,
const Eigen::Matrix4d& intrinsic,
const Eigen::Matrix4d& extrinsic,
const std::vector<int>& visibility_image_to_vertex,
const int image_boundary_margin) {
J_r.setZero();
r = 0;
int vid = visibility_image_to_vertex[row];
Eigen::Vector3d x = mesh.vertices_[vid];
Eigen::Vector4d g = extrinsic * Eigen::Vector4d(x(0), x(1), x(2), 1);
Eigen::Vector4d uv = intrinsic * g;
double u = uv(0) / uv(2);
double v = uv(1) / uv(2);
if (!images_gray.TestImageBoundary(u, v, image_boundary_margin)) return;
bool valid;
double gray, dIdx, dIdy;
std::tie(valid, gray) = images_gray.FloatValueAt(u, v);
std::tie(valid, dIdx) = images_dx.FloatValueAt(u, v);
std::tie(valid, dIdy) = images_dy.FloatValueAt(u, v);
if (gray == -1.0) return;
double invz = 1. / g(2);
double v0 = dIdx * intrinsic(0, 0) * invz;
double v1 = dIdy * intrinsic(1, 1) * invz;
double v2 = -(v0 * g(0) + v1 * g(1)) * invz;
J_r(0) = (-g(2) * v1 + g(1) * v2);
J_r(1) = (g(2) * v0 - g(0) * v2);
J_r(2) = (-g(1) * v0 + g(0) * v1);
J_r(3) = v0;
J_r(4) = v1;
J_r(5) = v2;
r = (gray - proxy_intensity[vid]);
w = 1.0;
}
std::pair<geometry::TriangleMesh, camera::PinholeCameraTrajectory>
RunRigidOptimizer(const geometry::TriangleMesh& mesh,
const std::vector<geometry::RGBDImage>& images_rgbd,
const camera::PinholeCameraTrajectory& camera_trajectory,
const RigidOptimizerOption& option) {
geometry::TriangleMesh opt_mesh = mesh;
camera::PinholeCameraTrajectory opt_camera_trajectory = camera_trajectory;
std::vector<geometry::Image> images_gray;
std::vector<geometry::Image> images_dx;
std::vector<geometry::Image> images_dy;
std::vector<geometry::Image> images_color;
std::vector<geometry::Image> images_depth;
std::vector<geometry::Image> images_mask;
std::vector<std::vector<int>> visibility_vertex_to_image;
std::vector<std::vector<int>> visibility_image_to_vertex;
std::vector<double> proxy_intensity;
int total_num_ = 0;
int n_camera = int(opt_camera_trajectory.parameters_.size());
SetProxyIntensityForVertex(opt_mesh, images_gray, utility::nullopt,
opt_camera_trajectory,
visibility_vertex_to_image, proxy_intensity,
option.image_boundary_margin_);
for (int itr = 0; itr < option.maximum_iteration_; itr++) {
utility::LogDebug("[Iteration {:04d}] ", itr + 1);
double residual = 0.0;
total_num_ = 0;
#pragma omp parallel for schedule(static) \
num_threads(utility::EstimateMaxThreads())
for (int c = 0; c < n_camera; c++) {
Eigen::Matrix4d pose;
pose = opt_camera_trajectory.parameters_[c].extrinsic_;
auto intrinsic = opt_camera_trajectory.parameters_[c]
.intrinsic_.intrinsic_matrix_;
auto extrinsic = opt_camera_trajectory.parameters_[c].extrinsic_;
Eigen::Matrix4d intr = Eigen::Matrix4d::Zero();
intr.block<3, 3>(0, 0) = intrinsic;
intr(3, 3) = 1.0;
auto f_lambda = [&](int i, Eigen::Vector6d& J_r, double& r,
double& w) {
w = 1.0;
ComputeJacobianAndResidualRigid(
i, J_r, r, w, opt_mesh, proxy_intensity, images_gray[c],
images_dx[c], images_dy[c], intr, extrinsic,
visibility_image_to_vertex[c],
option.image_boundary_margin_);
};
Eigen::Matrix6d JTJ;
Eigen::Vector6d JTr;
double r2;
std::tie(JTJ, JTr, r2) =
utility::ComputeJTJandJTr<Eigen::Matrix6d, Eigen::Vector6d>(
f_lambda, int(visibility_image_to_vertex[c].size()),
false);
bool is_success;
Eigen::Matrix4d delta;
std::tie(is_success, delta) =
utility::SolveJacobianSystemAndObtainExtrinsicMatrix(JTJ,
pose = delta * pose;
opt_camera_trajectory.parameters_[c].extrinsic_ = pose;
#pragma omp critical(RunRigidOptimizer)
{
residual += r2;
total_num_ += int(visibility_image_to_vertex[c].size());
}
}
return std::make_pair(opt_mesh, opt_camera_trajectory);
}
Non-Rigid Correction
整体思路
在Rigid的基础上,添加一个非刚体矫正(用变形函数F表示),重新通过链式法则进行优化
目标函数
对每一张图片
I
i
I_i
Ii?
-
非刚体校正可以用一个变形函数
F
i
F_i
Fi?表示
F
i
(
v
i
,
l
)
=
v
i
,
l
+
f
i
,
l
F_i( v_{i,l})= v_{i,l}+ f_{i,l}
Fi?(vi,l?)=vi,l?+fi,l?
-
v
i
,
l
∈
V
i
v_{i,l} \in V_i
vi,l?∈Vi?,表示变形的控制范围
-
f
i
,
l
f_{i,l}
fi,l?是一个二维向量
-
图像平面上的所有投影点u通过非刚体矫正后变为
F
i
(
u
)
=
u
+
∑
i
θ
l
(
u
)
F_i( u)= u+\sum_i \theta_l( u)
Fi?(u)=u+i∑?θl?(u)
-
推导可得
F
i
(
u
)
F_i( u)
Fi?(u)是
{
F
i
(
v
i
,
l
)
}
v
i
,
l
∈
V
i
\{ F_i( v_{i,l})\}_{ v_{i,l}\in V_i}
{Fi?(vi,l?)}vi,l?∈Vi??的线性组合 -
V
i
V_i
Vi?是一个
20
×
16
20\times16
20×16的网格(
21
×
17
21\times17
21×17个控制点)。之 -
之前的目标函数通过非刚体变换后变为
E
c
(
C
,
T
,
F
)
=
∑
i
∑
p
∈
P
i
r
i
,
p
2
E_c( C, T, F)=\sum_i\sum_{ p\in P_i}r_{i, p}^2
Ec?(C,T,F)=i∑?p∈Pi?∑?ri,p2? -
残差项为
r
i
,
p
=
C
(
p
)
?
Γ
i
(
F
i
(
u
(
g
(
p
,
T
i
)
)
)
)
r_{i, p}=C( p)-\Gamma_i( F_i( u( g( p, T_i))))
ri,p?=C(p)?Γi?(Fi?(u(g(p,Ti?)))) -
为了防止校正函数漂移,加入
L
2
L^2
L2正则项:
E
r
(
F
)
=
∑
i
∑
l
f
i
,
l
T
E_r( F)=\sum_i\sum_l f_{i,l}^T
Er?(F)=i∑?l∑?fi,lT? -
则完整的目标函数为:
E
(
C
,
T
,
F
)
=
E
c
(
C
,
T
,
F
)
+
λ
E
r
(
F
)
E( C, T, F)=E_c( C, T, F)+\lambda Er( F)
E(C,T,F)=Ec?(C,T,F)+λEr(F) -
实际中作者使用
λ
=
0.1
\lambda=0.1
λ=0.1
源码解析
注:本文只选取核心实现代码,均来源于open3d开源c++源代码,网址链接
NonRigidOptimizer.cpp
static void ComputeJacobianAndResidualNonRigid(
int row,
Eigen::Vector14d& J_r,
double& r,
Eigen::Vector14i& pattern,
const geometry::TriangleMesh& mesh,
const std::vector<double>& proxy_intensity,
const geometry::Image& images_gray,
const geometry::Image& images_dx,
const geometry::Image& images_dy,
const ImageWarpingField& warping_fields,
const Eigen::Matrix4d& intrinsic,
const Eigen::Matrix4d& extrinsic,
const std::vector<int>& visibility_image_to_vertex,
const int image_boundary_margin) {
J_r.setZero();
pattern.setZero();
r = 0;
int anchor_w = warping_fields.anchor_w_;
double anchor_step = warping_fields.anchor_step_;
int vid = visibility_image_to_vertex[row];
Eigen::Vector3d V = mesh.vertices_[vid];
Eigen::Vector4d G = extrinsic * Eigen::Vector4d(V(0), V(1), V(2), 1);
Eigen::Vector4d L = intrinsic * G;
double u = L(0) / L(2);
double v = L(1) / L(2);
if (!images_gray.TestImageBoundary(u, v, image_boundary_margin)) {
return;
}
int ii = (int)(u / anchor_step);
int jj = (int)(v / anchor_step);
if (ii >= warping_fields.anchor_w_ - 1 ||
jj >= warping_fields.anchor_h_ - 1) {
return;
}
double p = (u - ii * anchor_step) / anchor_step;
double q = (v - jj * anchor_step) / anchor_step;
Eigen::Vector2d grids[4] = {
warping_fields.QueryFlow(ii, jj),
warping_fields.QueryFlow(ii, jj + 1),
warping_fields.QueryFlow(ii + 1, jj),
warping_fields.QueryFlow(ii + 1, jj + 1),
};
Eigen::Vector2d uuvv = (1 - p) * (1 - q) * grids[0] +
(1 - p) * (q)*grids[1] + (p) * (1 - q) * grids[2] +
(p) * (q)*grids[3];
double uu = uuvv(0);
double vv = uuvv(1);
if (!images_gray.TestImageBoundary(uu, vv, image_boundary_margin)) {
return;
}
bool valid;
double gray, dIdfx, dIdfy;
std::tie(valid, gray) = images_gray.FloatValueAt(uu, vv);
std::tie(valid, dIdfx) = images_dx.FloatValueAt(uu, vv);
std::tie(valid, dIdfy) = images_dy.FloatValueAt(uu, vv);
Eigen::Vector2d dIdf(dIdfx, dIdfy);
Eigen::Vector2d dfdx =
((grids[2] - grids[0]) * (1 - q) + (grids[3] - grids[1]) * q) /
anchor_step;
Eigen::Vector2d dfdy =
((grids[1] - grids[0]) * (1 - p) + (grids[3] - grids[2]) * p) /
anchor_step;
double dIdx = dIdf.dot(dfdx);
double dIdy = dIdf.dot(dfdy);
double invz = 1. / G(2);
double v0 = dIdx * intrinsic(0, 0) * invz;
double v1 = dIdy * intrinsic(1, 1) * invz;
double v2 = -(v0 * G(0) + v1 * G(1)) * invz;
J_r(0) = -G(2) * v1 + G(1) * v2;
J_r(1) = G(2) * v0 - G(0) * v2;
J_r(2) = -G(1) * v0 + G(0) * v1;
J_r(3) = v0;
J_r(4) = v1;
J_r(5) = v2;
J_r(6) = dIdf(0) * (1 - p) * (1 - q);
J_r(7) = dIdf(1) * (1 - p) * (1 - q);
J_r(8) = dIdf(0) * (1 - p) * (q);
J_r(9) = dIdf(1) * (1 - p) * (q);
J_r(10) = dIdf(0) * (p) * (1 - q);
J_r(11) = dIdf(1) * (p) * (1 - q);
J_r(12) = dIdf(0) * (p) * (q);
J_r(13) = dIdf(1) * (p) * (q);
pattern(0) = 0;
pattern(1) = 1;
pattern(2) = 2;
pattern(3) = 3;
pattern(4) = 4;
pattern(5) = 5;
pattern(6) = 6 + (ii + jj * anchor_w) * 2;
pattern(7) = 6 + (ii + jj * anchor_w) * 2 + 1;
pattern(8) = 6 + (ii + (jj + 1) * anchor_w) * 2;
pattern(9) = 6 + (ii + (jj + 1) * anchor_w) * 2 + 1;
pattern(10) = 6 + ((ii + 1) + jj * anchor_w) * 2;
pattern(11) = 6 + ((ii + 1) + jj * anchor_w) * 2 + 1;
pattern(12) = 6 + ((ii + 1) + (jj + 1) * anchor_w) * 2;
pattern(13) = 6 + ((ii + 1) + (jj + 1) * anchor_w) * 2 + 1;
r = (gray - proxy_intensity[vid]);
}
std::pair<geometry::TriangleMesh, camera::PinholeCameraTrajectory>
RunNonRigidOptimizer(const geometry::TriangleMesh& mesh,
const std::vector<geometry::RGBDImage>& images_rgbd,
const camera::PinholeCameraTrajectory& camera_trajectory,
const NonRigidOptimizerOption& option) {
geometry::TriangleMesh opt_mesh = mesh;
camera::PinholeCameraTrajectory opt_camera_trajectory = camera_trajectory;
std::vector<ImageWarpingField> warping_fields;
warping_fields = CreateWarpingFields(images_gray,
option.number_of_vertical_anchors_);
warping_fields_init = CreateWarpingFields(
images_gray, option.number_of_vertical_anchors_);
#pragma omp parallel for schedule(static) \
num_threads(utility::EstimateMaxThreads())
for (int c = 0; c < n_camera; c++) {
int nonrigidval = warping_fields[c].anchor_w_ *
warping_fields[c].anchor_h_ * 2;
double rr_reg = 0.0;
Eigen::Matrix4d pose;
pose = opt_camera_trajectory.parameters_[c].extrinsic_;
auto intrinsic = opt_camera_trajectory.parameters_[c]
.intrinsic_.intrinsic_matrix_;
auto extrinsic = opt_camera_trajectory.parameters_[c].extrinsic_;
Eigen::Matrix4d intr = Eigen::Matrix4d::Zero();
intr.block<3, 3>(0, 0) = intrinsic;
intr(3, 3) = 1.0;
auto f_lambda = [&](int i, Eigen::Vector14d& J_r, double& r,
Eigen::Vector14i& pattern) {
ComputeJacobianAndResidualNonRigid(
i, J_r, r, pattern, opt_mesh, proxy_intensity,
images_gray[c], images_dx[c], images_dy[c],
warping_fields[c], intr, extrinsic,
visibility_image_to_vertex[c],
option.image_boundary_margin_);
};
Eigen::MatrixXd JTJ;
Eigen::VectorXd JTr;
double r2;
std::tie(JTJ, JTr, r2) =
ComputeJTJandJTrNonRigid<Eigen::Vector14d, Eigen::Vector14i,
Eigen::MatrixXd, Eigen::VectorXd>(
f_lambda, int(visibility_image_to_vertex[c].size()),
nonrigidval, false);
double weight = option.non_rigid_anchor_point_weight_ *
visibility_image_to_vertex[c].size() / n_vertex;
for (int j = 0; j < nonrigidval; j++) {
double r = weight * (warping_fields[c].flow_(j) -
warping_fields_init[c].flow_(j));
JTJ(6 + j, 6 + j) += weight * weight;
JTr(6 + j) += weight * r;
rr_reg += r * r;
}
bool success;
Eigen::VectorXd result;
std::tie(success, result) = utility::SolveLinearSystemPSD(
JTJ, -JTr, false,
false,
false, false);
Eigen::Vector6d result_pose;
result_pose << result.block(0, 0, 6, 1);
auto delta = utility::TransformVector6dToMatrix4d(result_pose);
pose = delta * pose;
for (int j = 0; j < nonrigidval; j++) {
warping_fields[c].flow_(j) += result(6 + j);
}
opt_camera_trajectory.parameters_[c].extrinsic_ = pose;
#pragma omp critical(RunRigidOptimizer)
{
residual += r2;
total_num_ += int(visibility_image_to_vertex[c].size());
}
}
return std::make_pair(opt_mesh, opt_camera_trajectory);
}
python demo运行
import open3d as o3d
import os
import sys
import re
def load_fountain_dataset():
def get_file_list(path, extension=None):
def sorted_alphanum(file_list_ordered):
convert = lambda text: int(text) if text.isdigit() else text
alphanum_key = lambda key: [
convert(c) for c in re.split('([0-9]+)', key)
]
return sorted(file_list_ordered, key=alphanum_key)
if extension is None:
file_list = [
path + f
for f in os.listdir(path)
if os.path.isfile(os.path.join(path, f))
]
else:
file_list = [
path + f
for f in os.listdir(path)
if os.path.isfile(os.path.join(path, f)) and
os.path.splitext(f)[1] == extension
]
file_list = sorted_alphanum(file_list)
return file_list
path = "./data/fountain/"
depth_image_path = get_file_list(os.path.join(path, "depth/"),extension=".png")
color_image_path = get_file_list(os.path.join(path, "image/"),extension=".jpg")
assert (len(depth_image_path) == len(color_image_path))
rgbd_images = []
for i in range(len(depth_image_path)):
depth = o3d.io.read_image(os.path.join(depth_image_path[i]))
color = o3d.io.read_image(os.path.join(color_image_path[i]))
rgbd_image = o3d.geometry.RGBDImage.create_from_color_and_depth(
color, depth, convert_rgb_to_intensity=False)
rgbd_images.append(rgbd_image)
camera_trajectory = o3d.io.read_pinhole_camera_trajectory(
os.path.join(path, "scene/key.log"))
mesh = o3d.io.read_triangle_mesh(
os.path.join(path, "scene", "integrated.ply"))
return mesh, rgbd_images, camera_trajectory
mesh, rgbd_images, camera_trajectory = load_fountain_dataset()
for i in range(len(camera_trajectory.parameters)):
camera_trajectory.parameters[i].intrinsic.intrinsic_matrix=[[4509.06,0,837.755],
[0,4509.06,557.454],
[0,0,1]]
print(camera_trajectory.parameters[0].intrinsic.intrinsic_matrix)
mesh_optimized = o3d.pipelines.color_map.run_rigid_optimizer(
mesh, rgbd_images, camera_trajectory,
o3d.pipelines.color_map.RigidOptimizerOption(maximum_iteration=0))、
o3d.visualization.draw_geometries([mesh_optimized],
zoom=0.5399,
front=[0.0665, -0.1107, -0.9916],
lookat=[0.7353, 0.6537, 1.0521],
up=[0.0136, -0.9936, 0.1118])
is_ci=True
maximum_iteration = 100 if is_ci else 300
with o3d.utility.VerbosityContextManager(
o3d.utility.VerbosityLevel.Debug) as cm:
mesh_optimized = o3d.pipelines.color_map.run_rigid_optimizer(
mesh, rgbd_images, camera_trajectory,
o3d.pipelines.color_map.RigidOptimizerOption(
maximum_iteration=maximum_iteration))
maximum_iteration = 100 if is_ci else 300
with o3d.utility.VerbosityContextManager(
o3d.utility.VerbosityLevel.Debug) as cm:
mesh_optimized = o3d.pipelines.color_map.run_non_rigid_optimizer(
mesh, rgbd_images, camera_trajectory,
o3d.pipelines.color_map.NonRigidOptimizerOption(
maximum_iteration=maximum_iteration))
参考
感谢国内外各位大佬的笔记和数据集以及open3d文档说明
- csdn某大佬的note
- 这位大佬的关于雅各布矩阵的推导求解
- Open3D官方文档
- Zhou大佬的官方主页,可以搜寻到数据集和论文
- 数据集和Log文件格式说明
|