OpenCV下的无标定校正(stereoRectifyUncalibrated)

news2024/11/13 16:06:26

OpenCV下的无标定校正(stereoRectifyUncalibrated)

文章目录

  • 1. 杂话
  • 2. 无标定校正
    • 2.1 先看代码
    • 2.2 一点解释
    • 2.3 findFundamentalMat参数
    • 2.4 stereoRectifyUncalibrated参数
  • 3. 矫正结果

1. 杂话

 咱们在之前的帖子里面讲了一些比较常规的标定和校正OpenCV下的单目标定,双目标定与立体校正(calibrateCamera, stereoCalibrate and stereoRectify),但是,如果说两个相机之间的转角比较大,那么可能并不适合用双目标定+立体校正的步骤来做,参考这个贴子立体校正失效的情况,所以为了解决这个问题,今天咱们就来说说如何使用非标定的校正函数:

 对了,我使用的数据和完整的代码在这里:Repo : Calibrate-and-Rectify

2. 无标定校正

2.1 先看代码

import cv2
import numpy as np
import glob
import matplotlib.pyplot as plt
from PIL import Image

c1_images_names = sorted(glob.glob('demo/left*.jpg'))
c2_images_names = sorted(glob.glob('demo/right*.jpg'))
 
c1_images = []
c2_images = []
for im1, im2 in zip(c1_images_names, c2_images_names):
    _im = cv2.imread(im1, 1)
    c1_images.append(_im)
 
    _im = cv2.imread(im2, 1)
    c2_images.append(_im)

def find_corners(image_names):
    images = []
    for imname in image_names:
        im = cv2.imread(imname, 1)
        images.append(im)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
 
    rows = 6 #number of checkerboard rows.
    columns = 9 #number of checkerboard columns.
 
    #coordinates of squares in the checkerboard world space
    objp = np.zeros((rows*columns,3), np.float32)
    objp[:,:2] = np.mgrid[0:rows,0:columns].T.reshape(-1,2)
 
    #frame dimensions. Frames should be the same size.
    width = images[0].shape[1]
    height = images[0].shape[0]
 
    #Pixel coordinates of checkerboards
    imgpoints = [] # 2d points in image plane.
 
    for frame in images:
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        gray = cv2.rotate(gray, cv2.ROTATE_90_CLOCKWISE)
 
        #find the checkerboard
        ret, corners = cv2.findChessboardCorners(gray, (rows, columns), None)
 
        if ret == True:
            #Convolution size used to improve corner detection. Don't make this too large.
            conv_size = (11, 11)
 
            #opencv can attempt to improve the checkerboard coordinates
            corners = cv2.cornerSubPix(gray, corners, conv_size, (-1, -1), criteria)
            imgpoints.append(corners)
    return imgpoints
 
imgpoints1 = find_corners(c1_images_names)
imgpoints2 = find_corners(c2_images_names)

# 将角点坐标转换为float32格式
points1all = np.array([pt.ravel() for pts in imgpoints1 for pt in pts], dtype=np.float32)
points2all = np.array([pt.ravel() for pts in imgpoints2 for pt in pts], dtype=np.float32)

# 计算基础矩阵
F, mask = cv2.findFundamentalMat(points1all, points2all, cv2.FM_RANSAC)

# 图像大小
img_size = (640, 480)
   
img1 = cv2.imread('demo/left03.jpg', 1)
img2 = cv2.imread('demo/right03.jpg', 1)

img1 = cv2.rotate(img1, cv2.ROTATE_90_CLOCKWISE)
img2 = cv2.rotate(img2, cv2.ROTATE_90_CLOCKWISE)

gray_left = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray_right = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
    
# 找到棋盘格角点
ret_left, corners_left = cv2.findChessboardCorners(gray_left, (6, 9), None)
ret_right, corners_right = cv2.findChessboardCorners(gray_right, (6, 9), None)

criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

print(ret_left, ret_right)
    
if ret_left and ret_right:
    # 提高角点的精度
    corners_left = cv2.cornerSubPix(gray_left, corners_left, (11, 11), (-1, -1), criteria)
    corners_right = cv2.cornerSubPix(gray_right, corners_right, (11, 11), (-1, -1), criteria)

# 将角点坐标转换为float32格式
points1 = np.array([pt[0] for pt in corners_left], dtype=np.float32).reshape(-1, 2)
points2 = np.array([pt[0] for pt in corners_right], dtype=np.float32).reshape(-1, 2)

# 进行立体校正
retval, H1, H2 = cv2.stereoRectifyUncalibrated(points1, points2, F, img_size)

# 裁剪图像去除黑边
def crop_image(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    x, y, w, h = cv2.boundingRect(contours[0])
    cropped_image = image[y:y+h, x:x+w]
    return cropped_image

if retval:
    # 计算校正映射
    img1_rectified = cv2.warpPerspective(img1, H1, img_size)
    img2_rectified = cv2.warpPerspective(img2, H2, img_size)

    # 显示校正后的图像
    cv2.imwrite('Rectified Image 1.png', img1_rectified)
    cv2.imwrite('Rectified Image 2.png', img2_rectified)
else:
    print("立体校正失败")
    
plt.figure(figsize=(20, 20))

for i in range(0,1):  # 以第一对图片为例
    im_L=Image.fromarray(img1_rectified) # numpy 转 image类
    im_R=Image.fromarray(img2_rectified) # numpy 转 image 类

    width = im_L.size[0]*2
    height = im_L.size[1]

    img_compare = Image.new('RGBA',(width, height))
    img_compare.paste(im_L,box=(0,0))
    img_compare.paste(im_R,box=(640,0))
    
    #在已经极线对齐的图片上均匀画线
    for i in range(1,20):
        len=480/20
        plt.axhline(y=i*len, color='r', linestyle='-')
    plt.imshow(img_compare)
    plt.savefig('epipolar_lines_ro.png', bbox_inches='tight', pad_inches=0)
    plt.show()

2.2 一点解释

 其实也没啥好解释的吧,一些基础的参数都在上面说的另外一篇帖子里面写好了。主要就是说一下思路,因为是标定图片,所以还是用角点做关键点,之后通过匹配的关键点和findFundamentalMat去计算基础矩阵,描述两个图片的映射关系。然后针对示例图片,应用这个映射,也就是stereoRectifyUncalibrated函数。

2.3 findFundamentalMat参数

 findFundamentalMat 是 OpenCV 中用于计算基础矩阵的函数。基础矩阵描述了两个图像之间的点对应关系,是立体视觉计算中的一个重要概念。以下是该函数的详细解释:

cv2.findFundamentalMat(points1, points2[, method[, ransacReprojThreshold[, confidence[, maxIters]]]]) -> fundamentalMatrix, mask

输入参数
points1:
描述:在第一幅图像中检测到的特征点列表或数组。应该是 Nx2 的二维点坐标列表,其中 N 是特征点的数量。

points2:
描述:在第二幅图像中检测到的特征点列表或数组。应该与 points1 中的点一一对应,且格式相同。

method (可选):
描述:用于估算基础矩阵的方法。可以是以下之一:
cv2.FM_7POINT: 七点算法。要求点数量为 7。
cv2.FM_8POINT: 八点算法。要求点数量大于或等于 8。
cv2.FM_RANSAC: 使用 RANSAC 方法。推荐用于处理带有噪声的点。
cv2.FM_LMEDS: 使用最小中值平方算法。也用于处理带有噪声的点。

ransacReprojThreshold (可选):
描述:RANSAC 算法中的最大重投影误差阈值,仅在使用 RANSAC 方法时有效。默认值为 3。

confidence (可选):
描述:RANSAC 算法中的置信度,仅在使用 RANSAC 方法时有效。默认值为 0.99。

maxIters (可选):
描述:RANSAC 算法的最大迭代次数,仅在使用 RANSAC 方法时有效。默认值为 1000。


输出参数
fundamentalMatrix:
描述:计算得到的基础矩阵,大小为 3x3。

mask:
描述:输出参数,用于标识内点(inliers)。大小为 Nx1 的二值数组,其中 N 是输入特征点的数量。值为 1 的元素表示对应的特征点是内点,值为 0 的元素表示对应的特征点是外点(outliers)。

2.4 stereoRectifyUncalibrated参数

cv2.stereoRectifyUncalibrated(points1, points2, F, imgSize[, H1[, H2[, threshold]]]) -> retval, H1, H2
输入参数

points1:
描述:在左图中检测到的特征点列表或数组。应该是 Nx2 的二维点坐标列表,其中 N 是特征点的数量。

points2:
描述:在右图中检测到的特征点列表或数组。应该与 points1 中的点一一对应,且格式相同。

F:
描述:基础矩阵(3x3),它是在立体图像对之间通过特征点匹配估算得到的。

imgSize:
描述:图像的尺寸,格式为 (width, height),即图像的宽度和高度。

H1 (可选):
描述:输出参数,用于存储左图的 3x3 变换矩阵。如果提供了此参数,函数会将结果写入其中。

H2 (可选):
描述:输出参数,用于存储右图的 3x3 变换矩阵。如果提供了此参数,函数会将结果写入其中。

threshold (可选):
描述:用于 RANSAC 算法的阈值。RANSAC 算法用于估算变换矩阵时的容错范围。如果不提供,则使用默认值。


输出参数
retval:
描述:函数执行成功与否的标志。返回 True 表示成功,返回 False 表示失败。

H1:
描述:左图的 3x3 变换矩阵。只有在调用时提供了相应的参数,且函数执行成功时才会返回。

H2:
描述:右图的 3x3 变换矩阵。只有在调用时提供了相应的参数,且函数执行成功时才会返回。

3. 矫正结果

在这里插入图片描述
 OK,基本符合预期哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101950.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity数据持久化 之 文件操作(增删查改)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ 这里需要弄清几个概念: File:提供文件操作的静态方法,是管理的 Windows.File -…

除浮毛用吸尘器有用吗?除浮毛真正有用浮毛空气净化器总结

我的医生朋友经常给朋友们讲解宠物毛发对呼吸道的潜在影响,这引起了不同的反应。有人采纳了他的建议,采取了防护措施;而有人则认为他在制造恐慌,特别是当听到宠物医生的说法与之相左时。 我曾也心存疑虑,但经过与朋友…

做开发一年多了,分享一下自己的疑惑以及大模型给我的一些建议~

写在最前面,下面的疑问是我自己的一些困惑和想知道背后的答案,回答这块是大模型的一些建议,我觉得对我来说不能说很对,至少给我了启发和思考,分享出来给大家,大家如果也有类似的疑惑,希望能提供到帮助 原先Java生态是出现各种复杂的业务场景,需要使用合理且合适的技术架…

house of cat

文章目录 house of cat概述:_IO_wfile_jumps进入_IO_wfile_seekoffFSOP__malloc_assert 例题:思路:分析:利用: house of cat 概述: house of cat主要的摸底还是覆盖vtable指针,因为在glibc-2.2…

结构型设计模式—桥接模式

结构型设计模式—桥接模式 欢迎长按图片加好友,我会第一时间和你分享持续更多的开发知识,面试资源,学习方法等等。 假设你要买一张新桌子,你有两个选择:一种是木制的桌子,另一种是金属制的桌子。 无论你选…

软件工程知识点总结(1):软件工程概述

1 什么是软件? 定义:计算机系统中的程序及其文档。 ——程序是计算机任务的处理对象和处理规模的描述; ——文档是为了便于了解程序所需要的阐明性资料。 2 软件的特点? 软件是无形的,不可见的逻辑实体 ——它的正确与…

【Python基础】字典类型

本文收录于 《Python编程入门》专栏,从零基础开始,分享一些Python编程基础知识,欢迎关注,谢谢! 文章目录 一、前言二、Python 字典类型2.1 访问字典里的值2.2 修改字典2.3 删除字典元素2.4 字典键值的特性2.5 遍历字典…

免费pdf转word软件,为你整理出8种方法,总有一个适合你

在日常办公和学习中,PDF文档因其格式稳定、不易修改的特性而广受欢迎。然而,有时我们需要对PDF内容进行编辑或格式调整,这时将其转换为Word文档便显得尤为重要。下面给大家介绍8种将PDF转换成Word的方法,包括在线网站、专业软件及…

第四篇——数学思维:数学家如何从逻辑出发想问题?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么? 四、总结五、升华 一、背景介绍 数学思维中的很多方法能够让我们脱离事物的表象去直击本质通过本质进行逻…

在Linux中使用MySQL基础SQL语句及校验规则

卸载内置环境 查看是否存在MySQL ps axj | grep mysql关闭MySQL systemctl stop mysqld MySQL对应的安装文件 rpm -qa | grep mysql 批量卸载 rpm -qa | grep mysql | xargs yum -y remove 上传MySQL rz 查看本地yum源 ls /etc/yum.repos.d/ -a 安装MySQL rpm -ivh…

Linux:手搓shell

之前学了一些和进程有关的特性,什么进程控制啊进程替换啊,我们来尝试自己搓一个shell()吧 首先我们观察shell的界面,发现centos的界面上有命令提示符: [主机名用户名当前路径] 我们可以通过调用系统函数获取当前路径&#xff0…

C语言代码练习(第十二天)

今日练习: 28、(指针)将字符串 a 复制为字符串 b ,然后输出字符串 b 29、改变指针变量的值 30、输入两个整数,然后让用户选择1或者2,选择1是调用 max ,输出两者中的大数,选择2是调用…

Mac M1 安装Hadoop教程(安装包安装)

一、引言 前面一期,我分享了通过homebrew方式安装Hadoop,本期我将通过安装包方式介绍下hadoop如何安装。二、下载open jdk8 官方下载地址 注意如果是x86架构的苹果电脑,Architecture选择x86 64-bit或者 x86-32bit。 下载后,将得…

Axios前后端对接

前端: 通过GET获取元素: console.log(res),接收接口返回的数据并打印出来。 async:是异步的知识。 通过POST修改,更新元素: 后端: 通过前端从后端获取一个对象: 后端执行相应方法: 然后获取L…

Spring6学习笔记2:容器IoC

文章目录 3 容器:IoC3.1 IoC容器3.1.2 依赖注入3.1.3 IoC容器在Spring的实现 3.2 基于XML管理Bean3.2.1 搭建子模块spring6-ioc-xml3.2.2 实验一:获取bean①方式一:根据id获取②方式二:根据类型获取③方式三:根据id和类…

day-48 一个小组的最大实力值

思路 想把所有非零数相乘,再统计负数的个数,如果负数为奇数个,则把乘机除以最大的那个负数即为答案,如果为偶数个,那么乘机即为答案 解题过程 但要考虑特殊情况:1.只有零和一个负数,返回零 2.全…

深入浅出Stream流

Java 8的新特性之一就是流stream,配合同版本出现的 Lambda ,使得操作集合(Collection)提供了极大的便利。 案例引入 在JAVA中,涉及到对数组、Collection等集合类中的元素进行操作的时候,通常会通过循环的…

cuda编程[1]:一二三维网格和块的核函数

目录 前言核函数一维二维三维结果分析 前言 所有的代码下载链接:code。以下代码展示了如何在 CUDA 中打印网格和线程的索引信息。代码包括一维、二维和三维的网格和块的设置,并定义了多个内核函数来输出当前的索引信息。 核函数 打印线程索引 __gl…

七、场景加载

一、新的场景加载方法 Unity在2023更新的一个方法 1、引用命名空间 2、调用代码 传入加载场景SO 注:此方法是 await 方法名 步骤:var s获取返回值;await返回加载内容;if(判断一下) 此时运行会出现:未卸载当前地图…

SPP/SPPF/Focal Module

一、在图像的分类任务重,卷积神经网络(CNN)一般含有5层: 输入层卷积层激活层池化层全连接层 全连接层通常要求输入为一维向量。在CNN中,卷积层和池化层的输出特征图会被展平(flatten)为一维…