当前位置: 首页 > article >正文

多视图几何--立体校正--Fusiello方法

1. 坐标系对齐与正交基构造

目标:构建新坐标系基向量 { e 1 , e 2 , e 3 } \{ \mathbf{e}_1, \mathbf{e}_2, \mathbf{e}_3 \} {e1,e2,e3},使成像平面共面且极线水平对齐。

(1) 基线方向 e 1 \mathbf{e}_1 e1
  • 基线向量由左右相机光心平移向量定义: b = C 2 − C 1 = T \mathbf{b} = C_2 - C_1 = \mathbf{T} b=C2C1=T

  • 归一化基线方向
    e 1 = b ∥ b ∥ \mathbf{e}_1 = \frac{\mathbf{b}}{\| \mathbf{b} \|} e1=bb
    (确保 e 1 \mathbf{e}_1 e1与基线平行,为后续极线对齐奠定基础)。

(2) 正交基 e 2 \mathbf{e}_2 e2 构造

e 2 = e 1 × r ( 3 ) e_2 = e_1\times r(3) e2=e1×r(3)

r ( 3 ) r(3) r(3)为左相机的旋转矩阵的第三个列向量。

归一化
e 2 = e 2 ∥ e 2 ∥ \mathbf{e}_2 = \frac{\mathbf{e}_2}{\| \mathbf{e}_2 \|} e2=e2e2
(避免直接使用原坐标系y轴,确保与 e 1 \mathbf{e}_1 e1正交)。

(3) 正交基 e 3 \mathbf{e}_3 e3 构造
  • 通过叉乘生成右手坐标系:
    e 3 = e 1 × e 2 \mathbf{e}_3 = \mathbf{e}_1 \times \mathbf{e}_2 e3=e1×e2
    归一化
    e 3 = e 3 ∥ e 3 ∥ \mathbf{e}_3 = \frac{\mathbf{e}_3}{\| \mathbf{e}_3 \|} e3=e3e3
    (确保 e 3 \mathbf{e}_3 e3垂直于成像平面,构成完整的正交基)。
(4) 新旋转矩阵 R new R_{\text{new}} Rnew

将基向量按列排列,构成新坐标系的外参旋转矩阵:
R new = [ e 1 e 2 e 3 ] ⊤ = [ e 1 x e 1 y e 1 z e 2 x e 2 y e 2 z e 3 x e 3 y e 3 z ] R_{\text{new}} = \begin{bmatrix} \mathbf{e}_1 & \mathbf{e}_2 & \mathbf{e}_3 \end{bmatrix}^\top = \begin{bmatrix} e_{1x} & e_{1y} & e_{1z} \\ e_{2x} & e_{2y} & e_{2z} \\ e_{3x} & e_{3y} & e_{3z} \end{bmatrix} Rnew=[e1e2e3]= e1xe2xe3xe1ye2ye3ye1ze2ze3z
(新旋转矩阵使成像平面共面且极线水平)。

2. 投影矩阵更新

目标:构造左右相机的新投影矩阵 P 1 ′ P_1' P1 P 2 ′ P_2' P2,确保共面成像。

(1) 左相机投影矩阵 P 1 ′ P_1' P1
  • 新外参:位置仍为原点,姿态由 R new R_{\text{new}} Rnew 定义。

  • 投影矩阵
    P 1 ′ = K new ⋅ [ R new 0 ] P_1' = K_{\text{new}} \cdot \begin{bmatrix} R_{\text{new}} & \mathbf{0} \end{bmatrix} P1=Knew[Rnew0]
    其中 K new K_{\text{new}} Knew 为调整后的内参矩阵(通常取左右相机内参的平均值,倾斜因子设为0)。

(2) 右相机投影矩阵 P 2 ′ P_2' P2
  • 基线长度 B = ∥ b ∥ = ∥ T ∥ B = \| \mathbf{b} \| = \| \mathbf{T} \| B=b=T

  • 平移向量:沿新坐标系x轴平移: T new = [ B , 0 , 0 ] ⊤ \mathbf{T}_{\text{new}} = [B, 0, 0]^\top Tnew=[B,0,0]

  • 投影矩阵
    P 2 ′ = K new ⋅ [ R new T new ] P_2' = K_{\text{new}} \cdot \begin{bmatrix} R_{\text{new}} & \mathbf{T}_{\text{new}} \end{bmatrix} P2=Knew[RnewTnew]
    (右相机仅沿新x轴平移,保证极线水平对齐)。


3. 单应性矩阵(Homography)推导

目标:将原始图像像素映射到校正后平面。

(1) 原相机投影模型

原始左相机的投影方程为:
x 1 = K 1 ⋅ [ R 0 ] ⋅ X \mathbf{x}_1 = K_1 \cdot \begin{bmatrix} R & \mathbf{0} \end{bmatrix} \cdot \mathbf{X} x1=K1[R0]X
(其中 R R R 为原相机的旋转矩阵)。

(2) 新相机投影模型

校正后的左相机投影为:
x 1 ′ = K new ⋅ [ R new 0 ] ⋅ X \mathbf{x}_1' = K_{\text{new}} \cdot \begin{bmatrix} R_{\text{new}} & \mathbf{0} \end{bmatrix} \cdot \mathbf{X} x1=Knew[Rnew0]X

(3) 单应性变换关系

联立两式消去 X \mathbf{X} X,得到单应矩阵 H 1 H_1 H1
x 1 ′ = K new R new R − 1 K 1 − 1 ⋅ x 1 \mathbf{x}_1' = K_{\text{new}} R_{\text{new}} R^{-1} K_1^{-1} \cdot \mathbf{x}_1 x1=KnewRnewR1K11x1
即:
H 1 = K new R new R − 1 K 1 − 1 H_1 = K_{\text{new}} R_{\text{new}} R^{-1} K_1^{-1} H1=KnewRnewR1K11
(通过变换矩阵 H 1 H_1 H1 将原图像像素映射到校正后平面)。

(4) 右相机单应矩阵

同理,右相机的单应矩阵为:
H 2 = K new R new R − 1 K 2 − 1 H_2 = K_{\text{new}} R_{\text{new}} R^{-1} K_2^{-1} H2=KnewRnewR1K21
(校正后右相机的极线与左相机严格水平对齐)。


4. 极线约束验证

校正后极线满足水平对齐,对应点视差仅沿x轴:
x 2 ′ = x 1 ′ + [ d 0 0 ] \mathbf{x}_2' = \mathbf{x}_1' + \begin{bmatrix} d \\ 0 \\ 0 \end{bmatrix} x2=x1+ d00
其中视差 d d d 与深度 Z Z Z 的关系为:
Z = B ⋅ f d Z = \frac{B \cdot f}{d} Z=dBf
f f f 为焦距,极线水平化大幅简化立体匹配搜索)。

code:

#include <opencv2/opencv.hpp>
#include <Eigen/Dense>
#include <iostream>using namespace cv;
using namespace Eigen;
using namespace std;// Eigen矩阵与OpenCV Mat互转
Mat eigen2mat(const MatrixXd& m) {Mat mat(m.rows(), m.cols(), CV_64F);for (int i = 0; i < m.rows(); ++i)for (int j = 0; j < m.cols(); ++j)mat.at<double>(i, j) = m(i, j);return mat;
}MatrixXd mat2eigen(const Mat& m) {MatrixXd mat(m.rows, m.cols);for (int i = 0; i < m.rows; ++i)for (int j = 0; j < m.cols; ++j)mat(i, j) = m.at<double>(i, j);return mat;
}// Fusiello立体校正核心函数(修正后)
void fusielloRectify(const Matrix3d& K1, const Matrix3d& K2,const Matrix3d& R, const Vector3d& T,Matrix3d& R_new, Matrix3d& H1, Matrix3d& H2) {// ---- 1. 坐标系对齐 ----Vector3d e1 = T.normalized(); // 基线方向// 构造正交基e2Vector3d e2 = e1.cross(R.col(2));e2.normalize();// 构造正交基e3Vector3d e3 = e1.cross(e2);e3.normalize();// 新旋转矩阵 [e1, e2, e3]^TR_new.col(0) = e1;R_new.col(1) = e2;R_new.col(2) = e3;// ---- 2. 计算单应矩阵 ----Matrix3d R_inv = R.inverse();Matrix3d K1_inv = K1.inverse();H1 = K1 * R_new * K1_inv * R_inv; // 左相机单应H2 = K2 * R_new * K1_inv * R_inv; // 右相机单应
}// 生成重映射表(使用OpenCV)
void computeRemapMaps(const Matrix3d& H, const Size& size,Mat& mapx, Mat& mapy) {Mat H_inv = eigen2mat(H.inverse());mapx.create(size, CV_32FC1);mapy.create(size, CV_32FC1);for (int y = 0; y < size.height; ++y) {for (int x = 0; x < size.width; ++x) {Mat pt = (Mat_<double>(3,1) << x, y, 1);Mat pt_src = H_inv * pt;pt_src /= pt_src.at<double>(2); // 归一化齐次坐标mapx.at<float>(y, x) = pt_src.at<double>(0);mapy.at<float>(y, x) = pt_src.at<double>(1);}}
}int main() {// ---- 标定参数(示例)----Matrix3d K1, K2, R;Vector3d T;K1 << 1000, 0, 320,0, 1000, 240,0, 0, 1;K2 = K1;R << 0.996, -0.087, 0.0,  // 假设左相机有轻微旋转0.087, 0.996, 0.0,0.0,   0.0,   1.0;T << 0.1, 0, 0; // 基线长度0.1米// ---- Fusiello校正 ----Matrix3d R_new, H1, H2;fusielloRectify(K1, K2, R, T, R_new, H1, H2);// ---- 生成重映射表 ----Size imgSize(640, 480);Mat mapx1, mapy1, mapx2, mapy2;computeRemapMaps(H1, imgSize, mapx1, mapy1);computeRemapMaps(H2, imgSize, mapx2, mapy2);// ---- 图像校正测试 ----Mat frame1 = imread("left.jpg");Mat frame2 = imread("right.jpg");Mat rect1, rect2;if (!frame1.empty() && !frame2.empty()) {remap(frame1, rect1, mapx1, mapy1, INTER_LINEAR);remap(frame2, rect2, mapx2, mapy2, INTER_LINEAR);// 绘制水平线验证极线对齐for (int y = 0; y < rect1.rows; y += 20) {line(rect1, Point(0, y), Point(rect1.cols, y), Scalar(0, 255, 0), 1);line(rect2, Point(0, y), Point(rect2.cols, y), Scalar(0, 255, 0), 1);}imshow("Left Rectified", rect1);imshow("Right Rectified", rect2);waitKey(0);} else {cerr << "Failed to load images!" << endl;}return 0;
}

参考:

A Compact Algorithm for Rectification of Stereo Pairs

[1](69. 三维重建4——立体校正(Recitification) - 知乎)

[2](立体视觉入门指南(6):对级约束与Fusiello法极线校正 - 知乎)

相关文章:

多视图几何--立体校正--Fusiello方法

1. 坐标系对齐与正交基构造 目标&#xff1a;构建新坐标系基向量 { e 1 , e 2 , e 3 } \{ \mathbf{e}_1, \mathbf{e}_2, \mathbf{e}_3 \} {e1​,e2​,e3​}&#xff0c;使成像平面共面且极线水平对齐。 (1) 基线方向 e 1 \mathbf{e}_1 e1​ 基线向量由左右相机光心平移向量…...

鸿蒙开发踩坑记录 - 2024S2

wrapBuilder如果想View和ObservedV2做绑定 必须要用 ComponentV2 Param 和 区别 退出两层循环 Builder的传入的参数及时是Trace修饰的也无法刷新组件 折叠屏展开后键盘无法点击 vm是公用的&#xff0c;组件生命周期问题导致 监听键盘高度变化失效 原因&#xff1a;分享面…...

【学Rust写CAD】21 2D 点(point.rs)

源码 //matrix/point.rs use std::ops::Mul; use super::algebraic_units::{Zero, One}; use super::generic::Matrix;/// 点坐标结构体 #[derive(Debug, Clone, Copy, PartialEq)] pub struct Point<X, Y>(Matrix<X, Y, One, Zero, Zero, One>);impl<X, Y>…...

0基础入门scrapy 框架,获取豆瓣top250存入mysql

一、基础教程 创建项目命令 scrapy startproject mySpider --项目名称 创建爬虫文件 scrapy genspider itcast "itcast.cn" --自动生成 itcast.py 文件 爬虫名称 爬虫网址 运行爬虫 scrapy crawl baidu(爬虫名&#xff09; 使用终端运行太麻烦了&#xff0c;而且…...

鸿蒙NEXT小游戏开发:井字棋

1. 引言 井字棋是一款经典的两人对战游戏&#xff0c;简单易懂&#xff0c;适合各个年龄段的玩家。本文将介绍如何使用鸿蒙NEXT框架开发一个井字棋游戏&#xff0c;涵盖游戏逻辑、界面设计及AI对战功能。 2. 开发环境准备 电脑系统&#xff1a;windows 10 开发工具&#xff1a;…...

deep-sync开源程序插件导出您的 DeepSeek 与 public 聊天

一、软件介绍 文末提供下载 deep-sync开源程序插件导出您的 DeepSeek 与 public 聊天&#xff0c;这是一个浏览器扩展&#xff0c;它允许用户公开、私下分享他们的聊天对话&#xff0c;并使用密码或过期链接来增强 Deepseek Web UI。该扩展程序在 Deepseek 界面中添加了一个 “…...

4. 理解Prompt Engineering:如何让模型听懂你的需求

引言:当模型变成“实习生” 想象一下,你新招的实习生总把“帮我写份报告”理解为“做PPT”或“整理数据表”——这正是开发者与大模型对话的日常困境。某金融公司优化提示词后,合同审查准确率从72%飙升至94%。本文将用3个核心法则+5个行业案例,教你用Prompt Engineering让…...

网络编程—网络概念

目录 1 网络分类 1.1 局域网 1.2 广域网 2 常见网络概念 2.1 交换机 2.2 路由器 2.3 集线器 2.4 IP地址 2.5 端口号 2.6 协议 3 网络协议模型 3.1 OSI七层模型 3.2 TCP/IP五层模型 3.3 每层中常见的协议和作用 3.3.1 应用层 3.3.2 传输层 3.3.3 网络层 3.3.4…...

基于Rust与WebAssembly实现高性能前端计算

引言 随着Web应用的复杂性增加&#xff0c;前端开发者经常面临性能瓶颈。传统JavaScript在处理密集型计算任务&#xff08;如大数据处理或实时图像渲染&#xff09;时&#xff0c;往往显得力不从心。而Rust语言凭借其高性能和内存安全特性&#xff0c;结合WebAssembly的接近原生…...

MATLAB 代码学习

1. Cell数组 Cell数组用于存储异构数据&#xff0c;每个元素&#xff08;称为cell&#xff09;可以包含不同类型的数据&#xff08;如数值、字符串、矩阵等&#xff09;。 1.1 创建Cell数组 直接赋值&#xff1a;使用花括号{}定义内容。 students {Alice, 20, [85, 90, 78…...

SELinux

一、selinux技术详解 SELinux 概述 SELinux&#xff0c;即 Security-Enhanced Linux&#xff0c;意为安全强化的 Linux&#xff0c;由美国国家安全局&#xff08;NSA&#xff09;主导开发。开发初衷是防止系统资源被误用。在 Linux 系统中&#xff0c;系统资源的访问均通过程…...

Axios 相关的面试题

在跟着视频教程学习项目的时候使用了axios发送请求&#xff0c;但是只是跟着把代码粘贴上去&#xff0c;一些语法规则根本不太清楚&#xff0c;但是根据之前的博客学习了fetch了之后&#xff0c;一看axios的介绍就明白了。所以就直接展示axios的面试题吧 本文主要内容&#xff…...

Spring Cloud 跨云灾备:如何实现5分钟级区域切换?

引言&#xff1a;云原生时代&#xff0c;区域级故障的致命性与应对 在混合云与多云架构中&#xff0c;单个区域的宕机可能导致全局服务瘫痪&#xff08;如2023年AWS美东区域故障影响超200家金融系统&#xff09;。传统灾备方案依赖手动切换DNS或冷备集群&#xff0c;恢复时间长…...

ES6对函数参数的新设计

ES6 对函数参数进行了新的设计&#xff0c;主要添加了默认参数、不定参数和扩展参数&#xff1a; 不定参数和扩展参数可以认为恰好是相反的两个模式&#xff0c;不定参数是使用数组来表示多个参数&#xff0c;扩展参数则是将多个参数映射到一个数组。 需要注意&#xff1a;不定…...

爬虫【feapder框架】

feapder框架 1、简单介绍 简介 feapder上手简单、功能强大的Python爬虫框架&#xff0c;内置AirSpider、Spider、Task、Spider、BatchSpider四种爬虫解决不同场景的需求支持断点续爬、监控报警、浏览器渲染、海量数据去重等功能更有功能强大的爬虫管理系统feaplat为其提供方…...

python如何提取html中所有的图片链接

在Python中&#xff0c;你可以使用BeautifulSoup库来解析HTML内容&#xff0c;并提取其中所有的图片链接&#xff08;即<img>标签的src属性&#xff09;。以下是一个示例代码&#xff0c;展示了如何做到这一点&#xff1a; 首先&#xff0c;确保你已经安装了BeautifulSo…...

网络协议之系列

网络协议之基础介绍 。 网络协议之清空购物车时都发生了啥&#xff1f; 。...

LLaMA Factory微调后的大模型在vLLM框架中对齐对话模版

LLaMA Factory微调后的大模型Chat对话效果&#xff0c;与该模型使用vLLM推理架构中的对话效果&#xff0c;可能会出现不一致的情况。 下图是LLaMA Factory中的Chat的对话 下图是vLLM中的对话效果。 模型回答不稳定&#xff1a;有一半是对的&#xff0c;有一半是无关的。 1、未…...

群体智能优化算法-鹈鹕优化算法(Pelican Optimization Algorithm, POA,含Matlab源代码)

摘要 鹈鹕优化算法&#xff08;Pelican Optimization Algorithm, POA&#xff09;是一种灵感来自自然界鹈鹕觅食行为的元启发式优化算法。POA 模拟鹈鹕捕食的两个主要阶段&#xff1a;探索阶段和开发阶段。通过模拟鹈鹕追捕猎物的动态行为&#xff0c;该算法在全局探索和局部开…...

代理模式-spring关键设计模式,bean的增强,AOP的实现

以下是一个结合代理模式解决实际问题的Java实现案例&#xff0c;涵盖远程调用、缓存优化、访问控制等场景&#xff0c;包含逐行中文注释&#xff1a; 场景描述 开发一个跨网络的文件查看器&#xff0c;需实现&#xff1a; 远程文件访问&#xff1a;通过代理访问网络文件 缓存…...

前端实现单点登录(SSO)的方案

概念&#xff1a;单点登录&#xff08;Single Sign-On, SSO&#xff09;主要是在多个系统、多个浏览器或多个标签页之间共享登录状态&#xff0c;保证用户只需登录一次&#xff0c;就能访问多个关联应用&#xff0c;而不需要重复登录。 &#x1f4a1; 方案分类 1. 前端级别 SS…...

在 Blazor 中使用 Chart.js 快速创建数据可视化图表

前言 BlazorChartjs 是一个在 Blazor 中使用 Chart.js 的库&#xff08;支持Blazor WebAssembly和Blazor Server两种模式&#xff09;&#xff0c;它提供了简单易用的组件来帮助开发者快速集成数据可视化图表到他们的 Blazor 应用程序中。本文我们将一起来学习一下在 Blazor 中…...

SQL server 2022和SSMS的使用案例1

一&#xff0c;案例讲解 二&#xff0c;实战讲解 实战环境 你需要确保你已经安装完成SQL Server 2022 和SSMS 20.2 管理面板。点此跳转至安装教程 SQL Server2022Windows11 专业工作站SSMS20.2 1&#xff0c;连接数据库 打开SSMS&#xff0c;连接数据库。 正常连接示意图&…...

【每日算法】Day 16-1:跳表(Skip List)——Redis有序集合的核心实现原理(C++手写实现)

解锁O(log n)高效查询的链表奇迹&#xff01;今日深入解析跳表的数据结构设计与实现细节&#xff0c;从基础概念到Redis级优化策略&#xff0c;彻底掌握这一平衡树的优雅替代方案。 一、跳表核心思想 跳表&#xff08;Skip List&#xff09; 是一种基于多层有序链表的概率型数…...

前沿科技:3D生成领域技术与应用分析

以下是关于3D生成领域的详细分析,涵盖技术发展、应用场景、挑战与未来趋势、市场动态及典型案例: 一、技术发展与核心方法 3D表示方法 显式表示:包括点云、网格(三角形或四边形)和分层深度图像(LDI),适合直接操作和渲染,但细节复杂度高。 隐式表示:如神经辐射场(NeR…...

Spring Boot 3.4.3 基于 JSqlParser 和 MyBatis 实现自定义数据权限

前言 在企业级应用中,数据权限控制是保证数据安全的重要环节。本文将详细介绍如何在 Spring Boot 3.4.3 项目中结合 JSqlParser 和 MyBatis 实现灵活的数据权限控制,通过动态 SQL 改写实现多租户、部门隔离等常见数据权限需求。 一、环境准备 确保开发环境满足以下要求: …...

GO语言学习(14)GO并发编程

目录 &#x1f308;前言 1.goroutine&#x1f31f; 2.GMP模型&#x1f31f; 2.1 GMP的由来☀️ 2.2 什么是GMP☀️ 3.channel &#x1f31f; 3.1 通道声明与数据传输&#x1f4a5; 3.2 通道关闭 &#x1f4a5; 3.3 通道遍历 &#x1f4a5; 3.4 Select语句 &#x1f4…...

【Audio开发二】Android原生音量曲线调整说明

一&#xff0c;客制化需求 客户方对于音量加减键从静音到最大音量十五个档位区域的音量变化趋势有定制化需求。 二&#xff0c;音量曲线调试流程 Android根据不同的音频流类型定义不同的曲线&#xff0c;曲线文件存放在/vendor/etc/audio_policy_volumes.xml或者default_volu…...

sass报错,忽略 Sass 弃用警告,降级版本

最有效的方法是创建一个 .sassrc.json 文件来配置 Sass 编译器。告诉 Sass 编译器忽略来自依赖项的警告消息。 解决方案&#xff1a; 1. 在项目根目录创建 .sassrc.json 文件&#xff1a; {"quietDeps": true }这个配置会让 Sass 编译器忽略所有来自依赖项&#x…...

spring-security原理与应用系列:HttpSecurity.filters

目录 AnyRequestMatcher WebSecurityConfig HttpSecurity AbstractInterceptUrlConfigurer AbstractAuthenticationProcessingFilter 类图 在前面的文章《spring-security原理与应用系列&#xff1a;securityFilterChainBuilders》中&#xff0c;我们遗留了一个问题&…...