【计算机视觉】直接线性变换(DLT)求解P矩阵(附C++和MATLAB代码)

2023-10-27

引言

本科阶段学习计算机视觉的时候也写过相机检校的程序,里面求解相机变换矩阵的时候使用的就是DLT算法,但这一次的作业只是要求计算投影矩阵(即P矩阵)

原理

讲解DLT算法的原理的帖子已经很多了,推荐下面这个链接:

双目视觉算法研究(二)相机模型和直接线性法(DLT)

代码

这里的C++ 代码中用到了一个常用的矩阵运算库Eigen,这个库只需要把头文件放到 include 文件夹中就可以用了,非常方便。
Eigen的参考文档当中已经说得很清楚了,不过如果不想看英文的话,可以参考这个链接:
C++ 开源矩阵 运算工具——Eigen

C++代码

int  main()
{
	// 将点位数据保存到矩阵当中,6✖5 的矩阵,前三列为空间坐标,后两列表示图像坐标 
	MatrixXd Point_info_mtx = MatrixXd::Zero(6, 5);

	Point_info_mtx << 99.1517085791261, -0.892099762666786, 3.06159510601795, 163.209290388816, 182.199675950568,
		100.558334460204, 0.868021368458366, 2.25981241694746, 608.892566967667, 701.375883991155,
		100.137647321744, -0.0612187178835884, 2.02380413900248, 449.262241640596, 377.206100107244,
		99.6742452887978, -0.675635383613515, 3.58856908136781, 316.188427147902, 257.277246206713,
		99.6224300840896, 0.0570662710124255, 2.33129745899956, 267.575494465364, 430.054554508315,
		100.203963882803, -0.474057430919711, 3.30815819695356, 439.688462560083, 287.229433675101;

	cout << Point_info_mtx << endl;

	// 调用编写好的函数计算P矩阵
	MatrixXd P = MatrixXd::Zero(3, 4);

	bool flag = Compute_P_matrix(Point_info_mtx, P);

	if (flag == 0) { cout << "程序运行失败!!!" << endl; exit(0); }
	else { cout << "P矩阵解求如下:\n" << P << endl; }

	// 使用计算的P矩阵来进行验证
	MatrixXd Points3d = MatrixXd::Zero(4, 6); //前三行表示坐标值,最后一行为 1 ,表示为增广坐标
	MatrixXd Points2d = MatrixXd::Zero(2, 6);
	for (int i = 0; i < 6; i++)
	{
		Points3d.col(i) << Point_info_mtx(i, 0), Point_info_mtx(i, 1), Point_info_mtx(i, 2), 1;
		Points2d.col(i) << Point_info_mtx(i, 3), Point_info_mtx(i, 4);
	}
	MatrixXd Res2d_ = MatrixXd::Zero(3, 6);		// 保存计算得到的结果,这里得到的是增广后的图像坐标 
	MatrixXd Res2d = MatrixXd::Zero(2, 6);		// 图像坐标

	Res2d_ = P*Points3d;

	// 归一化到图像坐标
	for (int i = 0; i < 6; i++)
	{
		Res2d.col(i) << Res2d_(0, i) / Res2d_(2, i), Res2d_(1, i) / Res2d_(2, i);
	}

	// 将理论值和计算值相减
	MatrixXd d = Points2d - Res2d;

	cout << "Res_:\n" << Res2d_ << endl;
	cout << "Res:\n" << Res2d << endl;
	cout << "d:\n" << d << endl;;

	return 0;
}



bool Compute_P_matrix(MatrixXd &points, MatrixXd &P)
{
	// 要用到的临时变量
	double x, y, X, Y, Z;

	//C * L  = I -- > L = inv(Ct * C) * Ct * (I)
	//C为2N行11列,L为11行1列,I为2N行1列
	MatrixXd C = MatrixXd::Zero(2 * 6, 11);
	MatrixXd L = MatrixXd::Zero(11, 1);
	MatrixXd I = MatrixXd::Zero(2 * 6, 1);

	//给C赋值,points为6行5列,0/1/2 三列为 X/Y/Z, 3/4列为 x/y
	// X Y Z 1 0 0 0 0 -x*X -x*Y -x*Z
	// 0 0 0 0 X Y Z 1 -y*X -y*Y -y*Z
	for (int i = 0; i < 6; i++)
	{
		X = points(i, 0);
		Y = points(i, 1);
		Z = points(i, 2);
		x = points(i, 3);
		y = points(i, 4);
		C.row(2 * i) << X, Y, Z, 1, 0, 0, 0, 0, -x*X, -x*Y, -x*Z;
		C.row(2 * i + 1) << 0, 0, 0, 0, X, Y, Z, 1, -y*X, -y*Y, -y*Z;
		I(2 * i) = x;
		I(2 * i + 1) = y;
	}

	MatrixXd Ct = C.transpose();
	MatrixXd CtC = Ct*C;
	L = CtC.inverse()*Ct*I;

	// 给P矩阵赋值
	P << L(0) / L(10), L(1) / L(10), L(2) / L(10),  L(3) / L(10),
		 L(4) / L(10), L(5) / L(10), L(6) / L(10),  L(7) / L(10),
		 L(8) / L(10), L(9) / L(10), L(10) / L(10), 1 / L(10);

	return true;
}

Matlab代码

clc;clear;close all;
%% DLT 算法需要用到 6 对点 ,前三列是 X Y Z 表示空间坐标 ,后两列 x y 表示图像坐标,下面是测试数据
Points = [  99.1517085791261, -0.892099762666786, 3.06159510601795, 163.209290388816, 182.199675950568;
		100.558334460204, 0.868021368458366, 2.25981241694746, 608.892566967667, 701.375883991155;
		100.137647321744, -0.0612187178835884, 2.02380413900248, 449.262241640596, 377.206100107244;
		99.6742452887978, -0.675635383613515, 3.58856908136781, 316.188427147902, 257.277246206713;
		99.6224300840896, 0.0570662710124255, 2.33129745899956, 267.575494465364, 430.054554508315;
		100.203963882803, -0.474057430919711, 3.30815819695356, 439.688462560083, 287.229433675101];

%% 计算部分 C*L + I = 0
% 赋值 有6 个点,所以C的大小为 12×11,  I 为12×1
C = zeros(12,11);I = zeros(12,1);
for i = 1:6
    X = Points(i,1);Y = Points(i,2);Z = Points(i,3);x = Points(i,4);y = Points(i,5);
    C(i*2-1,:) = [X,Y,Z,1,0,0,0,0,-x*X,-x*Y,-x*Z];
    C(i*2,:)   = [0,0,0,0,X,Y,Z,1,-y*X,-y*Y,-y*Z];
    
    I(i*2-1) = x; I(i*2) = y;
end

L = (C'*C)^(-1) * C' * I ;

P(1:11) = L;P(12) = 1;

P = P/L(11);

P = reshape(P,[4,3])';

%% 验证
Point3d = ones(4,6);
Point3d(1:3,:) = Points(:,1:3)';

Point2d = Points(:,4:5)';

Res = P*Point3d;

Res2d(1,:) = Res(1,:)./Res(3,:);
Res2d(2,:) = Res(2,:)./Res(3,:);

disp(Point2d)
disp(Res2d)

% 计算结果和原本数据的差值,即重投影的误差
diff = Point2d - Res2d;
disp(diff);

补充

到此为止已经计算出了相机的投影矩阵,这里补充部分用于计算相机参数的代码,参考文献为 :
利用二维 DLT 进行数字摄像机标定

注意,这篇文章中的公式和上面的链接中的公式中某些正负号是不同的,参考的时候需要注意,我当时编写的时候是按照老师PPT中的公式写的(下面的代码和上面的代码不是同一个时期写的 >_^ !!!)

内参数矩阵:
在这里插入图片描述

x0 = -(L(0)*L(8) + L(1)*L(9) + L(2)*L(10)) / (pow(L(8), 2) + pow(L(9), 2) + pow(L(10), 2));
y0 = -(L(4)*L(8) + L(5)*L(9) + L(6)*L(10)) / (pow(L(8), 2) + pow(L(9), 2) + pow(L(10), 2));

double fx = sqrt(-pow(x0, 2) + (pow(L(0), 2) + pow(L(1), 2) + pow(L(2), 2)) /
		(pow(L(8), 2) + pow(L(9), 2) + pow(L(10), 2)));
double fy = sqrt(-pow(y0, 2) + (pow(L(4), 2) + pow(L(5), 2) + pow(L(6), 2)) /
		(pow(L(8), 2) + pow(L(9), 2) + pow(L(10), 2)));

运行结果

C++
在这里插入图片描述
Matlab
在这里插入图片描述

结语

其实我们老师要求的是重投影误差要小于 0.01 ,悲剧的是无论是C++还是MATLAB都没达到这个精度>_<||
只能先这样了。。。

12月19日更新:【计算机视觉】直接线性变换(DLT)求解P矩阵(2 使用SVD分解)(附MATLAB代码)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【计算机视觉】直接线性变换(DLT)求解P矩阵(附C++和MATLAB代码) 的相关文章

随机推荐

  • flutter内存泄漏常见分析

    内存泄漏是Flutter中的一个常见问题 以下是一些可能导致内存泄漏的情况和注意事项 未释放控制器 在使用一些控制器 如AnimationController TextEditingController等 时 需要在不需要时及时释放控制器
  • 创建线程的方式打开记事本

    更好的阅读体验 huge color red 更好的阅读体验 更好的阅读体验 今天操作系统课老师讲到进程 提出了一个有趣的小实验 能否以系统调用的方式利用 Windows 创建进程的系统调用函数来打开一个软件 闲着蛋疼的我立马来了兴趣 姑且
  • unity开发VR,没有VR设备解决方式

    文章目录 前言 一 环境搭建 1 普通VR环境搭建 2 虚拟相机搭建 二 模拟相机的操作 总结 前言 开发实例环境为unity2018 4 11 VRTK3 3 0 steamVR1 2 23 当我们身边没有HTC VIVE设备时我们不能去
  • Android Studio中的mavenCentral、jcenter、google仓库

    一 Android Studio中依赖是从哪里得到 是从工程的build gradle里面定义的Maven仓库服务器去下载library的 总的来说 只有两个标准的Android library文件服务器 mavenCentral和jcen
  • AES加密和解密详解

    本文使用的是cyrpto js库 以AES CBC为例 先安装cyrpto js cyrpto js是js专门用来加密和解密用到的一个库 第一步 先确认一下电脑是否有node和npm 输入node version显示 v 版本号就可以下一步
  • RPMB分区介绍

    RPMB Replay Protected Memory Block重放保护内存块 Partition 是 eMMC 中的一个具有安全特性的分区 eMMC 在写入数据到 RPMB 时 会校验数据的合法性 只有指定的 Host 才能够写入 同
  • Java之解压Tar.gz和Gz文件到指定的目录下

    工作中的需求 需要读取指定路径下的压缩文件 然后解压到指定的目录下 引入maven依赖
  • msvcp140.dll丢失的4个解决方法,msvcp140.dll丢失的常见原因

    msvcp140 dll是Windows操作系统中的一个动态链接库文件 由Microsoft Visual C 程序库所提供 它包含了许多C 函数和类的定义 可以为应用程序提供一些基本服务 比如内存管理 文件输入 输出和网络连接等功能 我们
  • phpstorm表单递交post出错get正确的解决方法

    好吧 这是我第二次因为这个问题搞得凌晨才睡觉 这次一定要记录下来 phpstorm版本2016 1 1 问题详细描述 在html写好表单之后以post方式递交给php文件 返回结果在谷歌浏览器是 Automatically populati
  • Allegro如何做镂空丝印操作指导

    Allegro如何做镂空丝印操作指导 在PCB设计丝印的时候 会需要画镂空的丝印 Allegro升级到了172版本的时候 可以画镂空的丝印 如下图 具体操作如下 选择Shape Add Rect命令 Options选择需要画到的层面 比如S
  • nginx文档合集

    1 nginx documentation 2 14个Nginx的核心功能点 建议收藏 3 Nginx之负载均衡模块 ngx http upstream module 途径日暮不赏丶的博客 CSDN博客 4 tomcat redis ses
  • 华为OD机试 - 完美走位(Python)

    完美走位 题目描述 输入一个长度为4的倍数的字符串 字符串中仅包含WASD四个字母 将这个字符串中的连续子串用同等长度的仅包含WASD的字符串替换Q 如果替换后整个字符串中WASD四个字母出现的频数相同 那么我们称替换后的字符串是 完美走位
  • Android Studio 快捷键盘

    Alt 回车 导入包 自动修正 Crtl X 剪贴 删除本行 之前用Eclipse Ctrl D 就是删除 在AndroidStudio 中是复制本行到下一行 找了好久都没找到删除本行快捷键的 汗 Ctrl N 查找类 Ctrl Shift
  • CTO、技术总监、首席架构师的区别

    一 高级程序员 如果你是一个刚刚创业的公司 公司没有专职产品经理和项目经理 你就是公司的产品经理 你如果对你现在的开发员能力不满 那么你只需要的是一个高级程序员 你定义功能 你做计划推进和管理 他可以带1 2个副手把你规划的功能实现了 他是
  • PostgresSQL 用linux命令重启时出错:pg_ctl: server does not shut down

    出错原因 在建一个新的数据库 然后restore好久都没成功 就把服务器直接关掉重启了 然后通过linux去重启数据库就一直不成功 下面是出错信息和解决步骤 用service postgresql restart去重启数据库 总是报以下错误
  • 长/短 链接/轮询 和websocket

    短连接和长连接 短连接 http协议底层基于socket的tcp协议 每次通信都会新建一个TCP连接 即每次请求和响应过程都经历 三次握手 四次挥手 优点 方便管理 缺点 频繁的建立和销毁连接占用资源 长连接 客户端和服务端之间只有一条TC
  • 链表的有序构建和查找

    题目描述 单链表结点的存储结构包含两部分 数据 下一结点指针 默认为空 单链表包含头结点 存储实际数据的结点位置从1开始 现输入一批无序的整数队列 编写程序完成以下要求 1 构建单链表并且把数据按递增顺序插入到链表中 并且统计非空指针发生变
  • vuex有哪几种属性?

    一 vuex是什么 vuex 是 Vue 配套的 公共数据管理工具 它可以把一些共享的数据 保存到 vuex 中 方便整个程序中的任何组件直接获取或修改我们的公共数据 注意点 只有需要共享的才放到vuex上 不需要共享的数据依然放到组件自身
  • Acwing-4455. 出行计划

    暴力解法TLE了 过了70 的数据 include
  • 【计算机视觉】直接线性变换(DLT)求解P矩阵(附C++和MATLAB代码)

    引言 本科阶段学习计算机视觉的时候也写过相机检校的程序 里面求解相机变换矩阵的时候使用的就是DLT算法 但这一次的作业只是要求计算投影矩阵 即P矩阵 原理 讲解DLT算法的原理的帖子已经很多了 推荐下面这个链接 双目视觉算法研究 二 相机模