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

OpenCV相机标定与3D重建(61)处理未校准的立体图像对函数stereoRectifyUncalibrated()的使用

  • 操作系统:ubuntu22.04
  • OpenCV版本:OpenCV4.9
  • IDE:Visual Studio Code
  • 编程语言:C++11

算法描述

为未校准的立体相机计算一个校正变换。
cv::stereoRectifyUncalibrated 是 OpenCV 库中的一个函数,用于处理未校准的立体图像对。该函数尝试在没有内参和畸变参数的情况下,通过找到两个视图之间的单应性矩阵(Homography Matrix)来实现立体图像的校正。这个过程是基于给定的匹配点对和基础矩阵(Fundamental Matrix)。

函数原型


bool cv::stereoRectifyUncalibrated	
(InputArray 	points1,InputArray 	points2,InputArray 	F,Size 	imgSize,OutputArray 	H1,OutputArray 	H2,double 	threshold = 5 
)		

参数

  • 参数points1 第一图像中的特征点数组。
  • 参数points2 第二图像中对应的特征点。支持的格式与 findFundamentalMat 中相同。
  • 参数F 输入的基础矩阵(Fundamental Matrix)。可以使用相同的点对通过 findFundamentalMat 计算得到。
  • 参数imgSize 图像尺寸。
  • 参数H1 第一图像的输出校正单应性矩阵(Homography Matrix)。
  • 参数H2 第二图像的输出校正单应性矩阵(Homography Matrix)。
  • 参数threshold 可选阈值,用于过滤外点。如果该参数大于零,则所有不符合极线几何条件的点对(即 |points2[i]T⋅F⋅points1[i]| > threshold 的点)在计算单应性矩阵之前会被拒绝。否则,所有点都被视为内点。

功能描述
cv::stereoRectifyUncalibrated 函数旨在不依赖相机内参和它们在空间中的相对位置的情况下计算校正变换,这就是为什么函数名中有 “uncalibrated” 后缀的原因。它与 stereoRectify 的一个主要区别在于,它输出的是编码在单应性矩阵 H1 和 H2 中的平面透视变换,而不是三维空间中的校正变换。

该函数实现了算法 [116],用于从未经校准的立体图像中提取有用的几何信息,并生成可用于后续处理(如视差计算)的校正图像。

注意事项
尽管该算法不需要知道相机的内参,但它严重依赖于极线几何。因此,如果相机镜头存在显著的畸变,最好在计算基础矩阵和调用此函数之前进行校正。例如,可以分别使用 calibrateCamera 为每个立体相机头估计畸变系数,然后使用 undistort 校正图像,或者仅使用 undistortPoints 校正点坐标。

代码示例

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>using namespace cv;
using namespace std;// 生成更复杂的测试图像函数
void generateTestImages( Size imageSize, Mat& img1, Mat& img2 )
{img1 = Mat::zeros( imageSize, CV_8UC1 );img2 = Mat::zeros( imageSize, CV_8UC1 );RNG rng( 12345 );  // 随机数生成器// 在第一张图像上画随机圆,在第二张图像上画稍微偏移的随机圆模拟立体图像for ( int i = 0; i < 100; ++i ){  // 绘制更多随机圆Point center( rng.uniform( 50, imageSize.width - 50 ), rng.uniform( 50, imageSize.height - 50 ) );int radius = rng.uniform( 5, 20 );circle( img1, center, radius, Scalar( 255 ), FILLED );circle( img2, Point( center.x + rng.uniform( -10, 10 ), center.y + rng.uniform( -10, 10 ) ), radius, Scalar( 255 ), FILLED );}
}// 检测并匹配特征点的函数
void detectAndMatchFeatures( const Mat& img1, const Mat& img2, vector< KeyPoint >& keypoints1, vector< KeyPoint >& keypoints2, vector< DMatch >& good_matches )
{Ptr< Feature2D > detector = ORB::create( 1000 );  // 增加检测到的特征点数量// 定义描述符矩阵Mat descriptors1, descriptors2;// 检测关键点并计算描述符detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 );detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 );// 匹配描述符BFMatcher matcher( NORM_HAMMING );vector< vector< DMatch > > matches;matcher.knnMatch( descriptors1, descriptors2, matches, 2 );  // 使用knnMatch找到两个最近邻// 过滤出好的匹配点对(Lowe's ratio test)for ( size_t i = 0; i < matches.size(); ++i ){if ( matches[ i ].size() == 2 && matches[ i ][ 0 ].distance < 0.7 * matches[ i ][ 1 ].distance ){good_matches.push_back( matches[ i ][ 0 ] );}}
}// 使用RANSAC去除异常值并重新计算F矩阵
Mat refineFundamentalMatrix( vector< Point2f >& points1, vector< Point2f >& points2 )
{vector< uchar > inliers( points1.size() );Mat F = findFundamentalMat( points1, points2, FM_RANSAC, 3, 0.99, inliers );vector< Point2f > refinedPoints1, refinedPoints2;for ( size_t i = 0; i < inliers.size(); ++i ){if ( inliers[ i ] ){refinedPoints1.push_back( points1[ i ] );refinedPoints2.push_back( points2[ i ] );}}// 用精炼后的点重新计算F矩阵if ( !refinedPoints1.empty() && !refinedPoints2.empty() ){F = findFundamentalMat( refinedPoints1, refinedPoints2, FM_8POINT );}return F;
}// 检查单应性矩阵是否有效
bool isValidHomography( const Mat& H )
{if ( H.empty() )return false;if ( H.rows != 3 || H.cols != 3 )return false;// Check if the last element is close to 1 (for homography matrix)if ( abs( H.at< double >( 2, 2 ) - 1.0 ) > 1e-6 )return false;return true;
}// 可视化匹配点
void visualizeMatches( const Mat& img1, const Mat& img2, const vector< KeyPoint >& keypoints1, const vector< KeyPoint >& keypoints2, const vector< DMatch >& matches, const string& windowName )
{Mat img_matches;drawMatches( img1, keypoints1, img2, keypoints2, matches, img_matches );imshow( windowName, img_matches );
}// 验证基础矩阵的有效性
bool validateFundamentalMatrix( const Mat& F )
{if ( F.empty() )return false;if ( F.rows != 3 || F.cols != 3 )return false;// Fundamental matrix should have rank 2Mat W, U, Vt;SVD::compute( F, W, U, Vt );double rank = countNonZero( W > 1e-8 );return rank == 2;
}int main()
{// 生成一对测试图像Size imageSize( 640, 480 );Mat img1, img2;generateTestImages( imageSize, img1, img2 );// 提取和匹配特征点vector< KeyPoint > keypoints1, keypoints2;vector< DMatch > good_matches;detectAndMatchFeatures( img1, img2, keypoints1, keypoints2, good_matches );if ( good_matches.size() < 8 ){cerr << "Error: Not enough matches found (" << good_matches.size() << ")." << endl;return -1;}// 将匹配点转换为向量形式vector< Point2f > points1, points2;for ( DMatch match : good_matches ){points1.push_back( keypoints1[ match.queryIdx ].pt );points2.push_back( keypoints2[ match.trainIdx ].pt );}// 可视化匹配点visualizeMatches( img1, img2, keypoints1, keypoints2, good_matches, "Good Matches" );// 使用RANSAC去除异常值并重新计算F矩阵Mat F = refineFundamentalMatrix( points1, points2 );// 验证基础矩阵的有效性if ( !validateFundamentalMatrix( F ) ){cerr << "Error: Invalid fundamental matrix." << endl;return -1;}cout << "Validated Fundamental matrix:\n" << F << endl;// 执行未校准的立体校正Mat H1, H2;bool success = stereoRectifyUncalibrated( points1, points2, F, imageSize, H1, H2, 1 );if ( !success || !isValidHomography( H1 ) || !isValidHomography( H2 ) ){cerr << "Error: Unable to compute valid rectification transformations." << endl;// Print out the homographies for debugging purposescout << "H1:\n" << H1 << endl;cout << "H2:\n" << H2 << endl;return -1;}cout << "Rectification homography matrix for the first image:\n" << H1 << endl;cout << "Rectification homography matrix for the second image:\n" << H2 << endl;// 初始化重映射Mat map1x, map1y, map2x, map2y;initUndistortRectifyMap( Mat(), Mat(), H1, Mat(), imageSize, CV_32FC1, map1x, map1y );initUndistortRectifyMap( Mat(), Mat(), H2, Mat(), imageSize, CV_32FC1, map2x, map2y );// 确保重映射图是有效的if ( map1x.empty() || map1y.empty() || map2x.empty() || map2y.empty() ){cerr << "Error: Failed to initialize remap maps." << endl;return -1;}// 应用重映射Mat rectifiedImg1, rectifiedImg2;try{remap( img1, rectifiedImg1, map1x, map1y, INTER_LINEAR, BORDER_CONSTANT, Scalar() );remap( img2, rectifiedImg2, map2x, map2y, INTER_LINEAR, BORDER_CONSTANT, Scalar() );}catch ( const cv::Exception& e ){cerr << "Error during remap: " << e.what() << endl;return -1;}// 显示结果imshow( "Original Image 1", img1 );imshow( "Original Image 2", img2 );imshow( "Rectified Image 1", rectifiedImg1 );imshow( "Rectified Image 2", rectifiedImg2 );waitKey( 0 );  // 等待按键关闭窗口return 0;
}

运行结果

Validated Fundamental matrix:
[-1.69507626646751e-05, 7.362164468986336e-05, -0.1173662750444769;-6.367019516817851e-05, 3.353162138833532e-06, 0.001880485218793337;0.1241506729344979, -0.009417932088193068, 1]
Error: Unable to compute valid rectification transformations.
H1:
[0.02410468346050559, -0.1256463999224076, 64.27174787791705;0.1238268442213851, -0.002949304419357657, -9.446067724507486;3.05103171526221e-05, -8.242409501994642e-05, 0.1300740303808192]
H2:
[0.1930536997260217, -1.169973024903469, 539.0163420645055;1.009344355783741, 0.02531934576861759, -89.06683683526535;9.452570423521679e-05, -0.0005728588691755615, 1.107237903246865]

相关文章:

OpenCV相机标定与3D重建(61)处理未校准的立体图像对函数stereoRectifyUncalibrated()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 为未校准的立体相机计算一个校正变换。 cv::stereoRectifyUncalibrated 是 OpenCV 库中的一个函数&#xff0c;用于处理未校准的立体图像对。该函…...

[cg] glProgramBinary

参考&#xff1a; glProgramBinary - OpenGL 4 Reference Pages opengl 通过gpu编译好的 shader 可以存储到二进制文件中&#xff0c;第二次使用的时候直接加载二进制文件即可&#xff0c; glProgramBinary用于加载shader的二进制数据 实列代码如下&#xff1a; // 假设已经…...

LeetCode hot 力扣热题100 二叉树的最大深度

class Solution { public:int maxDepth(TreeNode* root) {if (root nullptr) {return 0;}int l_depth maxDepth(root->left);int r_depth maxDepth(root->right);return max(l_depth, r_depth) 1;} }; 代码作用 该函数通过递归计算二叉树的最大深度&#xff08;从根节…...

速通Docker === 网络

目录 Docker网络详解 容器之间直接通信的弊端 &#xff08;一&#xff09;启动容器 &#xff08;二&#xff09;进入容器并发起请求 &#xff08;三&#xff09;请求流程 &#xff08;四&#xff09; 弊端分析 一、Docker网络基础 &#xff08;一&#xff09;容器IP分配…...

【MySQL — 数据库基础】深入解析MySQL常用数据类型

常用数据类型 创建完数据库之后&#xff0c;就要在数据库中创建表&#xff0c;表中存储的数据记录&#xff0c;一条记录由不同的列组成&#xff0c;每条列都需要自己的类型&#xff1b;并且表中的多个行对应的列的数据类型&#xff0c;都必须是相同的&#xff1b; 那么每个…...

Linux高级--3.3.1 C++ spdlog 开源异步日志方案

一、基本介绍 spdlog 是由 Gustav S. 在 2015 年开发的一个高性能 C 日志库。开发这个库的主要目的是为了提供一个非常快速、轻量、易于使用的日志工具&#xff0c;特别适合需要高性能、低延迟日志记录的 C 应用程序。&#xff08;由于源码现在比较难下载&#xff0c;我把压缩…...

电梯系统的UML文档05

Dispatcher 不控制实际的电梯组件&#xff0c;但它在软件系统中是重要的。每一个电梯有一个ispatcher&#xff0c;主要功能是计算电梯的移动方向、移动目的地以及保持门的打开时间。它和系统中除灯控制器以外的几乎所有控制对象交互。 安全装置也是一个环境对象&#xff0c;它…...

如何使 LLaMA-Factory 支持 google/gemma-2-2b-jpn-it 的微调

如何使 LLaMA-Factory 支持 google/gemma-2-2b-jpn-it 的微调 追加&#xff0c; "Gemma-2-2B-JPN-Instruct": {DownloadSource.DEFAULT: "google/gemma-2-2b-jpn-it",},修改 constants.py&#xff0c; vi ./src/llamafactory/extras/constants.py---"…...

MySQL中日期和时间戳的转换:字符到DATE和TIMESTAMP的相互转换

在MySQL中&#xff0c;经常需要在 DATE、TIMESTAMP 和字符串之间进行相互转换。以下是一些常见的转换方法&#xff1a; 1. 字符串到日期/时间类型 字符串转 DATE: 使用 STR_TO_DATE() 函数将字符串转换为 DATE 类型。你需要提供字符串的格式。 SELECT STR_TO_DATE(2024-08-24,…...

HarmonyOS NEXT开发进阶(十):UIAbility 组件交互

文章目录 一、前言二、启动应用内的 UIAbility三、启动应用内的UIAbility并获取返回结果四、启动其他应用的UIAbility五、启动其他应用的 UIAbility 并获取返回结果六、启动 UIAbility 的指定页面6.1 调用方 UIAbility 指定启动页面6.2 目标 UIAbility 首次启动6.3 目标UIAbili…...

深入探索Math.NET:开启高效数值计算之旅

一、引言 在当今数字化时代&#xff0c;数值计算已然成为科学研究、工程设计、金融分析等众多领域的核心驱动力。从探索宇宙奥秘的物理学计算&#xff0c;到优化建筑结构的土木工程设计&#xff0c;再到预测市场趋势的金融建模&#xff0c;数值计算的身影无处不在&#xff0c;…...

AI编程工具横向评测--Cloudstudio塑造完全态的jupyter notebook助力数据分析应用开发

AI编程工具横向评测–Cloudstudio塑造完全态的jupyter notebook助力数据分析应用开发 数据分析类应用的开发&#xff0c;指的是首先进行数据分析&#xff0c;比如统计学分析、机器学习模型的构建等&#xff0c;然后将分析的流程开发成数据分析类的工具&#xff0c;或者将数据分…...

【2024 CSDN博客之星】技术洞察类:从DeepSeek-V3的成功,看MoE混合专家网络对深度学习算法领域的影响(MoE代码级实战)

目录 一、引言 1.1 本篇文章侧重点 1.2 技术洞察—MoE&#xff08;Mixture-of-Experts&#xff0c;混合专家网络&#xff09; 二、MoE&#xff08;Mixture-of-Experts&#xff0c;混合专家网络&#xff09; 2.1 技术原理 2.2 技术优缺点 2.3 业务代码实践 2.3.1 业务场…...

Linux——入门基本指令汇总

目录 1. ls指令2. pwd3. whoami指令4. cd指令5. clear指令6. touch指令7. mkdir指令8. rm指令9. man指令10. cp指令11. mv指令12. cat指令13. tac指令14. more指令15. less指令16. head指令17. tail指令18. date指令19. cal指令20. find指令21. which指令22. alias指令23. grep…...

54,【4】BUUCTF WEB GYCTF2020Ezsqli

进入靶场 吓我一跳&#xff0c;但凡放个彭于晏我都不说啥了 提交个1看看 1 and 11 1# 还尝试了很多&#xff0c;不过都被过滤了&#xff0c;头疼 看看别人的WP 竟然要写代码去跑&#xff01;&#xff01;&#xff01;&#xff0c;不会啊&#xff0c;先用别人的代码吧&#xf…...

【Leetcode 热题 100】45. 跳跃游戏 II

问题背景 给定一个长度为 n n n 的 0 0 0 索引 整数数组 n u m s nums nums。初始位置为 n u m s [ 0 ] nums[0] nums[0]。 每个元素 n u m s [ i ] nums[i] nums[i] 表示从索引 i i i 向前跳转的最大长度。换句话说&#xff0c;如果你在 n u m s [ i ] nums[i] nums[i…...

C/C++ 时间复杂度(On)

定义&#xff1a; 在计算机科学中&#xff0c;时间复杂性&#xff0c;又称时间复杂度&#xff0c;算法的时间复杂度是一个函数&#xff0c;它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述&#xff0c;不包括这个函数的低…...

【STM32-学习笔记-10-】BKP备份寄存器+时间戳

文章目录 BKP备份寄存器Ⅰ、BKP简介1. BKP的基本功能2. BKP的存储容量3. BKP的访问和操作4. BKP的应用场景5. BKP的控制寄存器 Ⅱ、BKP基本结构Ⅲ、BKP函数Ⅳ、BKP使用示例 时间戳一、Unix时间戳二、时间戳的转换&#xff08;time.h函数介绍&#xff09;Ⅰ、time()Ⅱ、mktime()…...

React 中hooks之 React.memo 和 useMemo用法总结

1. React.memo 基础 React.memo 是一个高阶组件&#xff08;HOC&#xff09;&#xff0c;用于优化函数组件的性能&#xff0c;通过记忆组件渲染结果来避免不必要的重新渲染。 1.1 基本用法 const MemoizedComponent React.memo(function MyComponent(props) {/* 渲染逻辑 *…...

日志收集Day001

1.ElasticSearch 作用&#xff1a;日志存储和检索 2.单点部署Elasticsearch与基础配置 rpm -ivh elasticsearch-7.17.5-x86_64.rpm 查看配置文件yy /etc/elasticsearch/elasticsearch.yml&#xff08;这里yy做了别名&#xff0c;过滤掉空行和注释行&#xff09; yy /etc/el…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...