【OpenCV】双目相机计算深度图和点云

双目相机计算深度图的基本原理是通过两台相机从不同角度拍摄同一场景,然后利用视差来计算物体的距离。本文的Python实现示例,使用OpenCV库来处理图像和计算深度图。

1、数据集介绍

Mobile stereo datasets由Pan Guanghan、Sun Tiansheng、Toby Weed和Daniel Scharstein在2019-2021年期间创建的,使用了Roger Dai、Kyle Meredith、Tommaso Monaco、Nick Mosier和Daniel Scharstein在2017-2018年期间开发的结构化光采集系统。该系统采用安装在UR5机械臂上的移动设备(苹果iPod touch 6G);地基真差是使用[5]中描述的结构化照明管道的一个子集来计算的。这些数据集包括11个场景,在许多不同的照明条件和曝光(包括闪光灯和移动设备的“火炬”照明)下,从1-3个不同的观看方向拍摄。
在这里插入图片描述

数据集基本描述:

Dataset description
Each dataset consists of 2 views taken under several different illuminations and exposures. The files are organized as follows:
SCENE{1,2,3}/                  -- scene imaged from 1-3 viewing directionsambient/                     -- directory of all input views under ambient lighting{F,L,T}{0,1,...}/          -- different lighting conditions (F=flash, L=lighting, T=torch)im0e{0,1,2,...}.png      -- left view under different exposuresim1e{0,1,2,...}.png      -- right view under different exposurescalib.txt                    -- calibration informationim{0,1}.png                  -- default left and right view (typically ambient/L0/im{0,1}e2.png)disp{0,1}.pfm                -- left and right GT disparities
Zip files containing the above files can be downloaded here. "all.zip" contains all 24 scenes (image pair, disparities, calibration file), but not the ambient subdirectories. The latter are available in separate zip files.Calibration file format
Here is a sample calib.txt file:
cam0=[1758.23 0 953.34; 0 1758.23 552.29; 0 0 1]
cam1=[1758.23 0 953.34; 0 1758.23 552.29; 0 0 1]
doffs=0
baseline=111.53
width=1920
height=1080
ndisp=290
isint=0
vmin=75
vmax=262
Explanation:cam0,1:        camera matrices for the rectified views, in the form [f 0 cx; 0 f cy; 0 0 1], wheref:           focal length in pixelscx, cy:      principal pointdoffs:         x-difference of principal points, doffs = cx1 - cx0 (here always == 0)baseline:      camera baseline in mmwidth, height: image sizendisp:         a conservative bound on the number of disparity levels;the stereo algorithm MAY utilize this bound and search from d = 0 .. ndisp-1vmin, vmax:    a tight bound on minimum and maximum disparities, used for color visualization;the stereo algorithm MAY NOT utilize this information
To convert from the floating-point disparity value d [pixels] in the .pfm file to depth Z [mm] the following equation can be used:
Z = baseline * f / (d + doffs)
Note that the image viewer "sv" and mesh viewer "plyv" provided by our software cvkit can read the calib.txt files and provide this conversion automatically when viewing .pfm disparity maps as 3D meshes.

  • 如果使用自己的双目相机,则需要知道相机两个摄像头的内参矩阵cam0,1、基线baseline

2、Python代码

代码使用的双目图像数据是chess2

import cv2
import numpy as np
import time
import matplotlib.pyplot as pltdef load_images(left_path, right_path):left_image = cv2.imread(left_path, 0)right_image = cv2.imread(right_path, 0)return left_image, right_image#
def compute_disparity_map(left_image, right_image, cam0, cam1, doffs, ndisp, vmin, vmax, block_size=15):stereo = cv2.StereoSGBM_create(minDisparity=vmin,  # 视差从0开始numDisparities=ndisp,  # 视差范围数量,必须是16的倍数blockSize=block_size,  # 匹配块的大小,奇数P1=8 * 3 * block_size ** 2,  # 平滑惩罚项(第一级)P2=32 * 3 * block_size ** 2,  # 平滑惩罚项(第二级),通常是P1的4倍disp12MaxDiff=1,  # 左右视差检查的最大允许差异uniquenessRatio=10,  # 唯一性比率阈值speckleWindowSize=100,  # 斑点滤波器窗口大小speckleRange=32  # 斑点滤波器最大容许差异)disparity_map = stereo.compute(left_image, right_image).astype(np.float32) / 16.0return disparity_map# 转成深度图
def convert_disparity_to_depth(disparity_map, focal_length_px, baseline_mm):# 避免除以零的情况depth_map = (focal_length_px * baseline_mm) / disparity_mapdepth_map[disparity_map == 0] = 0  # 设置无效区域的深度为0return depth_map# 过滤最远平面
def filter_far_plane(depth_map, max_distance=1500):  depth_map[depth_map > max_distance] = 0return depth_map#生成普通点云
def generate_point_cloud(depth_map, cam0, cx, cy):fx = cam0[0, 0]fy = cam0[1, 1]height, width = depth_map.shape# 创建网格u, v = np.meshgrid(np.arange(width), np.arange(height))# 计算 x, y, z 坐标z = depth_mapx = (u - cx) * z / fxy = (v - cy) * z / fy# 将坐标堆叠成点云数组point_cloud = np.stack((x, y, z), axis=-1)# 过滤掉无效点(深度值为 0 的点)valid_mask = z > 0point_cloud = point_cloud[valid_mask]return point_cloud#生成彩色点云
def generate_colored_point_cloud(depth_map, cam0, cx, cy):  fx = cam0[0, 0]fy = cam0[1, 1]height, width = depth_map.shape# 创建网格u, v = np.meshgrid(np.arange(width), np.arange(height))# 计算x, y, z坐标x = (u - cx) * depth_map / fxy = (v - cy) * depth_map / fyz = depth_map# 将坐标堆叠成点云数组point_cloud = np.stack((x, y, z), axis=-1)# 归一化深度值valid_mask = depth_map > 0normalized_depth = np.zeros_like(depth_map)if valid_mask.any():normalized_depth[valid_mask] = (depth_map[valid_mask] - np.min(depth_map[valid_mask])) / (np.max(depth_map[valid_mask]) - np.min(depth_map[valid_mask]))# 使用jet colormap生成颜色colors = plt.cm.jet(normalized_depth)[:, :, :3]  # 获取RGB颜色,忽略alpha通道# 将颜色值从 [0, 1] 转换为 [0, 255] 并转换为 uint8 类型colors = (colors * 255).astype(np.uint8)return point_cloud, colorsdef save_point_cloud_to_txt(point_cloud, file_path):point_cloud = np.hstack([point_cloud.reshape(-1, 3)])np.savetxt(file_path, point_cloud, fmt='%f %f %f', header='x y z', comments='')def save_point_cloud_to_txt_rgb(point_cloud, colors, file_path):points_with_colors = np.hstack([point_cloud.reshape(-1, 3), colors.reshape(-1, 3)])np.savetxt(file_path, points_with_colors, fmt='%f %f %f %f %f %f', header='x y z r g b', comments='')print(f"Colored point cloud saved to {file_path}")if __name__ == "__main__":left_image_path = 'Stereo/data/chess2/ambient/L0/im0e3.png'  # 替换为你的左眼图像路径right_image_path = 'Stereo/data/chess2/ambient/L0/im1e3.png'  # 替换为你的右眼图像路径left_image, right_image = load_images(left_image_path, right_image_path)# 双目相机参数cam0 = np.array([[1758.23, 0, 872.36], [0, 1758.23, 552.32], [0, 0, 1]])cam1 = np.array([[1758.23, 0, 872.36], [0, 1758.23, 552.32], [0, 0, 1]])doffs = 0baseline_mm = 124.86width = 1920height = 1080ndisp = 310isint = 0vmin=90vmax=280# 计算像素焦距fx = cam0[0, 0]fy = cam0[1, 1]cx = cam0[0, 2]cy = cam1[1, 2]start_time = time.time()disparity_map = compute_disparity_map(left_image, right_image, cam0, cam1, doffs, ndisp, vmin, vmax)depth_map = convert_disparity_to_depth(disparity_map, fx, baseline_mm)depth_map = filter_far_plane(depth_map, max_distance=depth_map.max()-1)  # 过滤最远的平面# point_cloud = generate_point_cloud(depth_map, cam0, cx, cy)point_cloud, colors = generate_colored_point_cloud(depth_map, cam0, cx, cy)end_time = time.time()elapsed_time = end_time - start_timeprint(f"Elapsed Time = {elapsed_time:.4f} seconds")#cv2.imshow('depth',depth_map)#cv2.waitKey(0)#cv2.imwrite('D:/dataset/depth.jpg',depth_map)# 创建 1 行 3 列的子图布局plt.figure(figsize=(12, 4))# 显示第左图plt.subplot(1, 3, 1)plt.imshow(cv2.imread(left_image_path)[:, :, ::-1] ) # 反转通道顺序plt.title('left_image')plt.axis('off')# 显示右图plt.subplot(1, 3, 2)plt.imshow(cv2.imread(right_image_path)[:, :, ::-1] ) # 反转通道顺序plt.title('right_image')plt.axis('off')# 显示深度图plt.subplot(1, 3, 3)plt.imshow(depth_map, cmap='gray')plt.title('depth_map')plt.axis('off')# 调整子图间距plt.tight_layout()# 显示图像plt.show()# 保存最后一次迭代的点云到TXT文件output_file_path = 'point_cloud.txt'save_point_cloud_to_txt(point_cloud, output_file_path)#save_point_cloud_to_txt_rgb(point_cloud, colors, output_file_path)print(f"Point cloud saved to {output_file_path}")

3、运行结果

3.1 处理耗时

Elapsed Time = 1.3469 seconds
Point cloud saved to point_cloud.txt

3.2 左图、右图和计算的深度图

在这里插入图片描述

3.3 保存的点云文件point_cloud.txt

在这里插入图片描述

4、代码说明

函数 compute_disparity_map 的功能是计算双目立体视觉中的视差图(Disparity Map)。视差图是双目立体视觉中的关键输出,它表示左右图像中对应像素点的水平位移(视差),可以用来计算深度信息。
在这里插入图片描述

具体功能:

  1. 输入

    • left_imageright_image:双目相机的左右图像。
    • cam0cam1:左右相机的内参矩阵(虽然函数中未直接使用,但通常在后续深度计算中会用到)。
    • doffs:左右相机光心的水平偏移(通常用于校正后的图像)。
    • ndisp:视差范围的数量(必须是 16 的倍数)。
    • vminvmax:视差的最小值和最大值。
    • block_size:匹配块的大小(奇数)。
  2. 输出

    • disparity_map:视差图,表示每个像素点的视差值。
  3. 核心逻辑

    • 使用 OpenCV 的 cv2.StereoSGBM_create 创建一个半全局块匹配(Semi-Global Block Matching, SGBM)立体匹配器。
    • 调用 stereo.compute 计算左右图像的视差图。
    • 将视差图的值除以 16(OpenCV 的 SGBM 算法返回的视差图是 16 倍的实际值)。

参数详解:

  • minDisparity=vmin:视差的最小值,通常为 0。
  • numDisparities=ndisp:视差范围的数量,必须是 16 的倍数。例如,如果 ndisp=64,则视差范围为 [vmin, vmin + 64]
  • blockSize=block_size:匹配块的大小,必须是奇数。较大的块可以提高鲁棒性,但会降低细节。
  • P1P2:平滑惩罚项,用于控制视差图的平滑程度。P2 通常是 P1 的 4 倍。
  • disp12MaxDiff=1:左右视差检查的最大允许差异,用于过滤不匹配的点。
  • uniquenessRatio=10:唯一性比率阈值,用于过滤非唯一的匹配点。
  • speckleWindowSize=100:斑点滤波器窗口大小,用于去除小的噪声区域。
  • speckleRange=32:斑点滤波器的最大容许差异。

视差图的用途:

视差图可以用于计算深度图(Depth Map),公式为:
t e x t D e p t h = f ⋅ B Disparity text{Depth} = \frac{f \cdot B}{\text{Disparity}} textDepth=DisparityfB
其中:

  • (f) 是相机的焦距(通常从相机内参矩阵中获取)。
  • (B) 是基线长度(左右相机光心之间的距离)。
  • (\text{Disparity}) 是视差值。

函数使用示例代码:

import cv2
import numpy as np# 假设 left_image 和 right_image 是左右图像
left_image = cv2.imread('left.png', cv2.IMREAD_GRAYSCALE)
right_image = cv2.imread('right.png', cv2.IMREAD_GRAYSCALE)# 相机内参和参数
cam0 = np.array([[1000, 0, 320], [0, 1000, 240], [0, 0, 1]])  # 左相机内参
cam1 = np.array([[1000, 0, 320], [0, 1000, 240], [0, 0, 1]])  # 右相机内参
doffs = 0  # 光心偏移
ndisp = 64  # 视差范围
vmin = 0  # 最小视差
vmax = 64  # 最大视差# 计算视差图
disparity_map = compute_disparity_map(left_image, right_image, cam0, cam1, doffs, ndisp, vmin, vmax)# 显示视差图
cv2.imshow('Disparity Map', disparity_map / ndisp)  # 归一化显示
cv2.waitKey(0)
cv2.destroyAllWindows()

注意事项:

  1. 图像输入:左右图像需要是校正后的图像(即极线对齐)。
  2. 视差范围ndisp 的选择需要根据场景的深度范围调整。
  3. 性能:SGBM 算法的计算复杂度较高,对于高分辨率图像可能需要较长时间。

如果有其他问题,欢迎评论!😊

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PT8032 3 通道触摸 IC

1. 概述 PT8032 是一款电容式触摸控制 ASIC ,支持 3 通道触摸输入 ,2 线 BCD 码输出。具有低功耗、 高抗干扰、宽工作电压范围、高穿透力的突出优势。 2. 主要特性 工作电压范围: 2.4~5.5V 待机电流约 9uAV DD5V&CMOD10nF 3 通道触…

像指针操作、像函数操作的类

像指针一样的类。把一个类设计成像一个指针。什么操作符运用到指针上? 使用标准库的时候,里面有个很重要的东西叫容器。容器本身一定带着迭代器。迭代器作为另外一种智能指针。迭代器指向容器里的一个元素。迭代器用来遍历容器。 _list_iterator是链表迭…

Pikachu–XXE漏洞

Pikachu–XXE漏洞 一、XML基础概念 XML文档结构由XML声明&#xff0c;DTD(文档类型定义)&#xff0c;文档元素三部分构成&#xff01; #XML是可扩展标记语言(Extensible Markup Language),是设计用来进行数据的传输与存储。 #eg: <!--XML声明--><!--指明XML文档的版…

matlab-simulink

1、信号到对象解析指示符 代表的意义是&#xff1a;信号名称必须解析为信号对象 2、input inport 双击空白区域输入模块名字&#xff0c;自动联想显示相关模块 没看出太大的差别 3、Stateflow 双击空白区域输入stateflow、或者chart或者常用库里面去查找 4、离散时间积分…

简单几个步骤完成 Oracle 到金仓数据库(KingbaseES)的迁移目标

作为国产数据库的领军选手&#xff0c;金仓数据库&#xff08;KingbaseES&#xff09;凭借其成熟的技术架构和广泛的市场覆盖&#xff0c;在国内众多领域中扮演着至关重要的角色。无论是国家电网、金融行业&#xff0c;还是铁路、医疗等关键领域&#xff0c;金仓数据库都以其卓…

网络安全概论——网络安全基础

一、网络安全引言 信息安全的四个属性&#xff08;信息安全的基本目标 &#xff09; 保密性:信息不会被泄露给非授权用户完整性&#xff1a;保证数据的一致性可用性&#xff1a;合法用户不会被拒绝服务合法使用&#xff1a;不会被非授权用户或以非授权的方式使用 二、网络安全…

数据结构-链式二叉树

文章目录 一、链式二叉树1.1 链式二叉树的创建1.2 根、左子树、右子树1.3 二叉树的前中后序遍历1.3.1前(先)序遍历1.3.2中序遍历1.3.3后序遍历 1.4 二叉树的节点个数1.5 二叉树的叶子结点个数1.6 第K层节点个数1.7 二叉树的高度1.8 查找指定的值(val)1.9 二叉树的销毁 二、层序…

SpringCloud系列教程:微服务的未来(二十三)SpringAMQP快速入门、Work Queues、Fanout交换机

前言 Spring AMQP是Spring框架中用于与消息中间件&#xff08;如RabbitMQ&#xff09;进行交互的一个项目&#xff0c;它简化了消息发送、接收以及消息处理的过程。通过Spring AMQP&#xff0c;开发者可以快速实现基于RabbitMQ的消息传递系统。本文将介绍Spring AMQP的快速入门…

单片机简介

一、单片机简介 电脑和单片机性能对比 二、单片机发展历程 三、CISC VS RISC

Java中面向对象的三大特性 -- 有关多态

学习目标 理解多态掌握instanceof了解抽象类&#xff0c;抽象方法 1.多态(向上转型) ● 现在我们已经学会了继承&#xff08;类与类之间的&#xff09;关系&#xff0c;并且能够在子类继承父类的基础上进一步对子类的数据及操作进行扩展&#xff0c;增加新的成员变量和方法或…

在本地校验密码或弱口令 (windows)

# 0x00 背景 需求是验证服务器的弱口令&#xff0c;如果通过网络侧校验可能会造成账户锁定风险。在本地校验不会有锁定风险或频率限制。 # 0x01 实践 ## 1 使用 net use 命令 可以通过命令行使用 net use 命令来验证本地账户的密码。打开命令提示符&#xff08;CMD&#xff0…

蓝桥杯嵌入式备赛(四)—— 中断 + UART

目录 一、STM32 NVIC中断系统1、NVIC介绍2、Cortex-M4优先级设置 二、UART介绍1、原理图介绍2、原理图介绍及编程步骤&#xff08;1&#xff09;CubeMX设置&#xff08;2&#xff09;UART 发送&#xff08;3&#xff09;UART 接收 一、STM32 NVIC中断系统 1、NVIC介绍 STM32G4…

AI前端开发的学习成本与回报——效率革命的曙光

近年来&#xff0c;人工智能技术飞速发展&#xff0c;深刻地改变着各行各业。在软件开发领域&#xff0c;AI写代码工具的出现更是掀起了一场效率革命。AI前端开发&#xff0c;作为人工智能技术与前端开发技术的完美结合&#xff0c;正展现出巨大的发展潜力&#xff0c;为开发者…

AI前端开发的持续学习策略:拥抱变化,精进技艺

在飞速发展的科技浪潮中&#xff0c;AI前端开发领域正经历着日新月异的变化。作为一名AI前端开发者&#xff0c;你是否感到技术更新迭代之快&#xff0c;对自身持续学习能力提出了更高的要求&#xff1f; 想要在竞争激烈的行业中保持领先地位&#xff0c;持续学习不再是一种选择…

sql盲注脚本

在sqli-labs中的第8题无回显可以尝试盲注的手法获取数据 发现页面加载了3秒左右可以进行盲注 布尔盲注数据库名 import requestsdef inject_database(url):datanamefor i in range(1,15):low 32high 128mid (low high) // 2while low < high:path "id1 and asci…

DeepSeekR1 苹果macbook M1本地可视化运行!

过年了&#xff0c;就带了一台 macbook air 8g&#xff0c;DeepSeekR1的消息还是铺天盖地的来&#xff0c;我就想着在这台电脑上也装一个吧。 经过简单的配置&#xff0c;最终也运行起来了&#xff0c;速度还可以。 我这是首款M系列笔记本&#xff0c;也是现在最低配的 M 系列…

centos 10 离线安装dnf 和 设置dnf镜像源

离线安装dnf可用kimi搜索, centos 使用curl 下载dnf 的rpm包 mkdir ~/dnf_packages cd ~/dnf_packages# CentOS 7 示例 curl -O http://springdale.math.ias.edu/data/puias/unsupported/7/x86_64/dnf-0.6.4-2.sdl7.noarch.rpm curl -O http://springdale.math.ias.edu/data/pu…

Vivado生成edif网表及其使用

介绍如何在Vivado中将模块设为顶层&#xff0c;并生成相应的网表文件&#xff08;Verilog文件和edif文件&#xff09;&#xff0c;该过程适用于需要将一个模块作为顶层设计进行综合&#xff0c;并生成用于其他工程中的网表文件的情况。 例如要将fpga_top模块制作成网表给其它工…

【Python网络爬虫】爬取网站图片实战

【Python网络爬虫】爬取网站图片实战 Scrapying Images on Website in Action By Jackson@ML *声明:本文简要介绍如何利用Python爬取网站数据图片,仅供学习交流。如涉及敏感图片或者违禁事项,请注意规避;笔者不承担相关责任。 1. 创建Python项目 1) 获取和安装最新版…

Python从0到100(八十八):LSTM网络详细介绍及实战指南

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…