双目测距工程Stereo-Vision-master学习笔记

news2024/9/28 7:20:27

硬件:
首先要要把两个摄像头固定到支架上,并且两个摄像头的间距应该在110mm,两个摄像头没有落差
在这里插入图片描述
相机的内参数包括焦距、主点坐标、像素尺寸等,这些参数决定了相机成像的几何变换关系。内参数是相机固有的属性,不会随着相机在空间中的位置和方向而改变。

相机的外参数包括相机的旋转矩阵和平移向量,用来描述相机在世界坐标系中的位置和方向。相机的外参数可以将相机坐标系中的点转换到世界坐标系中,或者将世界坐标系中的点转换到相机坐标系中。

步骤一:

首先要通过/Take_images_for_calibration_thread.py来保存正畸用的棋盘格图像;代码的逻辑是只要通过findChessboardCorners找到充足且满足预设个数的棋盘格脚点时保存双目的图像。
核心代码

retL, frameL= CamL.read()  
grayR= cv2.cvtColor(frameL,cv2.COLOR_BGR2GRAY)
retL, cornersL = cv2.findChessboardCorners(grayL,(9,6),None) 
cv2.cornerSubPix(grayL,cornersL,(11,11),(-1,-1),criteria)
cv2.drawChessboardCorners(grayR,(9,6),corners2R,retR)

if(retL)& 0xFF == ord('q'):
cv2.imwrite('chessboard-L'+str_id_image+'.png',frameL)

如果存在角点且符合预设个数,会打印绘制完角点图后的图像,并且允许按键保存。
在这里插入图片描述
左摄像头:
在这里插入图片描述
右摄像头:
在这里插入图片描述

步骤二:消除畸变

常见的相机失真类型有径向失真(radial distortion)和切向失真(tangential distortion)。
径向失真包括:桶型和枕性,实际的相机使用弯曲的镜头来形成图像,并且光线通常在这些镜头的边缘弯曲得太多或太少。 这会产生扭曲图像边缘的效果,从而使线条或对象看起来比实际弯曲的程度或大或小。 这称为径向变形 ,这是最常见的变形类型。
在这里插入图片描述
在这里插入图片描述
另一类失真是切向失真 。 当相机镜头未完全平行于相机胶卷或传感器所在的成像平面对齐时,就会发生这种情况。 这会使图像看起来倾斜,从而使某些对象看起来比实际位置更远或更近。

在这里插入图片描述

步骤三 对极几何

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

但是在实际测距中,计算视差被转换成了,计算两个对应像素位置之间的不同(distance),没有解析解,只能算一个数值解。
在这里插入图片描述

参考:
https://www.cnblogs.com/clarenceliang/p/6704970.
htmlhttps://cloud.tencent.com/developer/article/2054308
在这里插入图片描述

在这里插入图片描述

代码

  1. 首先,程序会对摄像头进行畸变校正和立体标定,以获得相机的内参和外参。
  2. 程序会打开两个摄像头,并循环读取左右眼的图像。
  3. 对图像进行矫正,消除旋转和对齐带来的畸变。
  4. 将彩色图像转为灰度图像。
  5. 使用StereoSGBM算法计算出视差图。
  6. 使用WLS滤波器对视差图进行滤波,获得更加准确的深度图。
  7. 对深度图进行颜色映射,显示出深度信息。
  8. 通过鼠标双击深度图中的点,可以在控制台输出该点的实际距离。
  9. 按空格键退出程序。
#      ▄▀▄     ▄▀▄
#     ▄█░░▀▀▀▀▀░░█▄
# ▄▄  █░░░░░░░░░░░█  ▄▄
#█▄▄█ █░░▀░░┬░░▀░░█ █▄▄█

###################################
##### Authors:                #####
##### Stephane Vujasinovic    #####
##### Frederic Uhrweiller     ##### 
#####                         #####
##### Creation: 2017          #####
###################################


#***********************
#**** Main Programm ****
#***********************


# Package importation
import numpy as np
import cv2
from openpyxl import Workbook # Used for writing data into an Excel file
from sklearn.preprocessing import normalize
import time
# Filtering
kernel= np.ones((3,3),np.uint8)

def coords_mouse_disp(event,x,y,flags,param):
    if event == cv2.EVENT_LBUTTONDBLCLK:#左键双击
        #print x,y,disp[y,x],filteredImg[y,x]
        average=0
        for u in range (-1,2):
            for v in range (-1,2):
                average += disp[y+u,x+v]
        average=average/9
        Distance= -593.97*average**(3) + 1506.8*average**(2) - 1373.1*average + 522.06
        Distance= np.around(Distance*0.01,decimals=2)
        print('Distance: '+ str(Distance)+' m')
        
# This section has to be uncommented if you want to take mesurements and store them in the excel
        ws.append([counterdist, average])
        print('Measure at '+str(counterdist)+' cm, the dispasrity is ' + str(average))
        if (counterdist <= 85):
           counterdist += 3
        elif(counterdist <= 120):
           counterdist += 5
        else:
           counterdist += 10
        print('Next distance to measure: '+str(counterdist)+'cm')
#将测量结果写入excel
# Mouseclick callback
wb=Workbook()
ws=wb.active  

#*************************************************
#***** Parameters for Distortion Calibration *****
#*************************************************

# Termination criteria
#设置了相机校准的参数,误差小于0.001或者迭代30次就停止
criteria =(cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
criteria_stereo= (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

# Prepare object points棋盘格座标点初始化
objp = np.zeros((9*6,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:6].T.reshape(-1,2)

# Arrays to store object points and image points from all images
objpoints= []   # 3d points in real world space
imgpointsR= []   # 2d points in image plane
imgpointsL= []

# Start calibration from the camera
print('Starting calibration for the 2 cameras... ')
# Call all saved images
#重新检测棋盘格角点
print(time.localtime())
for i in range(0,60):   # Put the amount of pictures you have taken for the calibration inbetween range(0,?) wenn starting from the image number 0
    t= str(i)
    ChessImaR= cv2.imread('/home/nvidia/Desktop/gf-learning/Stereo-Vision-master/save_image/chessboard-R'+t+'.png',0)    # Right side
    ChessImaL= cv2.imread('/home/nvidia/Desktop/gf-learning/Stereo-Vision-master/save_image/chessboard-L'+t+'.png',0)    # Left side
    retR, cornersR = cv2.findChessboardCorners(ChessImaR,
                                               (9,6),None)  # Define the number of chees corners we are looking for
    retL, cornersL = cv2.findChessboardCorners(ChessImaL,
                                               (9,6),None)  # Left side
    if (True == retR) & (True == retL):
        objpoints.append(objp)
        cv2.cornerSubPix(ChessImaR,cornersR,(11,11),(-1,-1),criteria)
        cv2.cornerSubPix(ChessImaL,cornersL,(11,11),(-1,-1),criteria)
        imgpointsR.append(cornersR)
        imgpointsL.append(cornersL)#保存角点信息
print(time.localtime())

# Determine the new values for different parameters
#   Right Side
#右相机标定,ret:标定结果的精度,mtx内参矩阵,dist畸变系数;外参数:rvecs旋转向量:描述相机坐标系相对于世界坐标系的旋转关系,tvecs平移向量:描述相机坐标系相对于世界坐标系的平移关系。
retR, mtxR, distR, rvecsR, tvecsR = cv2.calibrateCamera(objpoints,
                                                        imgpointsR,
                                                        ChessImaR.shape[::-1],None,None)
hR,wR= ChessImaR.shape[:2]
#畸变矫正,motx经过优化后相机内参,roi优化后有效区域的ROI坐标
OmtxR, roiR= cv2.getOptimalNewCameraMatrix(mtxR,distR,
                                                   (wR,hR),1,(wR,hR))

#   Left Side
retL, mtxL, distL, rvecsL, tvecsL = cv2.calibrateCamera(objpoints,
                                                        imgpointsL,
                                                        ChessImaL.shape[::-1],None,None)
hL,wL= ChessImaL.shape[:2]
OmtxL, roiL= cv2.getOptimalNewCameraMatrix(mtxL,distL,(wL,hL),1,(wL,hL))

print('Cameras Ready to use')

#********************************************
#***** Calibrate the Cameras for Stereo *****
#********************************************

# StereoCalibrate function
flags = 0
flags |= cv2.CALIB_FIX_INTRINSIC

#相机立体校准,stereoCalibrate计算立体校准所需要的参数,R两个相机间旋转矩阵,T平移矩阵,E本质矩阵,F基本矩阵
retS, MLS, dLS, MRS, dRS, R, T, E, F= cv2.stereoCalibrate(objpoints,#真实世界下坐标
                                                          imgpointsL,#相机下投影角点左边
                                                          imgpointsR,
                                                          mtxL,#内参
                                                          distL,#畸变系数
                                                          mtxR,
                                                          distR,
                                                          ChessImaR.shape[::-1],
                                                          criteria = criteria_stereo,
                                                          flags = cv2.CALIB_FIX_INTRINSIC)

# StereoRectify function
rectify_scale= 0 # if 0 image croped, if 1 image nor croped
#R用于校正图像的旋转矩阵、P用于校正图像的投影矩阵、Q重投影矩阵,可以用于计算深度信息
RL, RR, PL, PR, Q, roiL, roiR= cv2.stereoRectify(MLS, dLS, MRS, dRS,
                                                 ChessImaR.shape[::-1], R, T,
                                                 rectify_scale,(0,0))  # last paramater is alpha, if 0= croped, if 1= not croped
# initUndistortRectifyMap function
#初始化图像校正映射
Left_Stereo_Map= cv2.initUndistortRectifyMap(MLS, dLS, RL, PL,
                                             ChessImaR.shape[::-1], cv2.CV_16SC2)   # cv2.CV_16SC2 this format enables us the programme to work faster
Right_Stereo_Map= cv2.initUndistortRectifyMap(MRS, dRS, RR, PR,
                                              ChessImaR.shape[::-1], cv2.CV_16SC2)
#*******************************************
#***** Parameters for the StereoVision *****
#*******************************************

# Create StereoSGBM and prepare all parameters
window_size = 3
min_disp = 2
num_disp = 130-min_disp
stereo = cv2.StereoSGBM_create(minDisparity = min_disp,#最小视差值
    numDisparities = num_disp,#视差范围
    blockSize = window_size,
    uniquenessRatio = 10,
    speckleWindowSize = 100,
    speckleRange = 32,
    disp12MaxDiff = 5,
    P1 = 8*3*window_size**2,
    P2 = 32*3*window_size**2)

# Used for the filtered image
#计算右视角图像中每个像素与左视角图像中对应像素的差异,视差计算
stereoR=cv2.ximgproc.createRightMatcher(stereo) # Create another stereo for right this time

# WLS FILTER Parameters
lmbda = 80000# 平滑度惩罚系数
sigma = 1.8
visual_multiplier = 1.0
 #提高深度估计的精度
wls_filter = cv2.ximgproc.createDisparityWLSFilter(matcher_left=stereo)
wls_filter.setLambda(lmbda)
wls_filter.setSigmaColor(sigma)

#*************************************
#***** Starting the StereoVision *****
#*************************************

# Call the two cameras
CamR= cv2.VideoCapture(6)   # Wenn 6 then Right Cam and wenn 7 Left Cam
CamL= cv2.VideoCapture(7)

CamR.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
CamR.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
CamR.set(cv2.CAP_PROP_FPS, 30)

CamL.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
CamL.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
CamL.set(cv2.CAP_PROP_FPS, 30)

while True:
    # Start Reading Camera images
    retR, frameR= CamR.read()
    retL, frameL= CamL.read()

    # Rectify the images on rotation and alignement重映射是一种将图像从一个坐标系统映射到另一个坐标系统的过程
    Left_nice= cv2.remap(frameL,Left_Stereo_Map[0],Left_Stereo_Map[1], interpolation = cv2.INTER_LANCZOS4, borderMode = cv2.BORDER_CONSTANT)  # Rectify the image using the kalibration parameters founds during the initialisation
    Right_nice= cv2.remap(frameR,Right_Stereo_Map[0],Right_Stereo_Map[1], interpolation = cv2.INTER_LANCZOS4, borderMode = cv2.BORDER_CONSTANT)



    # Convert from color(BGR) to gray
    grayR= cv2.cvtColor(Right_nice,cv2.COLOR_BGR2GRAY)
    grayL= cv2.cvtColor(Left_nice,cv2.COLOR_BGR2GRAY)

    # Compute the 2 images for the Depth_image计算深度信息
    disp= stereo.compute(grayL,grayR)#.astype(np.float32)/ 16
    dispL= disp
    dispR= stereoR.compute(grayR,grayL)
    dispL= np.int16(dispL)
    dispR= np.int16(dispR)

    # Using the WLS filter加权最小二乘滤波平滑
    filteredImg= wls_filter.filter(dispL,grayL,None,dispR)
    filteredImg = cv2.normalize(src=filteredImg, dst=filteredImg, beta=0, alpha=255, norm_type=cv2.NORM_MINMAX);
    filteredImg = np.uint8(filteredImg)
    #cv2.imshow('Disparity Map', filteredImg)
    #归一化:这个操作的目的是将原始的视差值映射到一定范围内的浮点数。通常,在计算机视觉中,视差图的像素值表示了对应像素点的深度信息,而视差值的范围通常较大。为了方便处理和可视化,我们需要将其缩放到一个合适的范围,以便更好地表达深度差异。
    disp= ((disp.astype(np.float32)/ 16)-min_disp)/num_disp # Calculation allowing us to have 0 for the most distant object able to detect

##    # Resize the image for faster executions
##    dispR= cv2.resize(disp,None,fx=0.7, fy=0.7, interpolation = cv2.INTER_AREA)

    # Filtering the Results with a closing filter
    #用一矩阵去噪
    closing= cv2.morphologyEx(disp,cv2.MORPH_CLOSE, kernel) # Apply an morphological filter for closing little "black" holes in the picture(Remove noise) 

    # Colors map
    dispc= (closing-closing.min())*255
    dispC= dispc.astype(np.uint8)                                   # Convert the type of the matrix from float32 to uint8, this way you can show the results with the function cv2.imshow()
    disp_Color= cv2.applyColorMap(dispC,cv2.COLORMAP_OCEAN)         # Change the Color of the Picture into an Ocean Color_Map
    filt_Color= cv2.applyColorMap(filteredImg,cv2.COLORMAP_OCEAN) 

    # Show the result for the Depth_image
    #cv2.imshow('Disparity', disp)
    #cv2.imshow('Closing',closing)
    #cv2.imshow('Color Depth',disp_Color)
    cv2.imshow('Filtered Color Depth',filt_Color)

    # Mouse clicks
    cv2.setMouseCallback("Filtered Color Depth",coords_mouse_disp,filt_Color)
    
    # End the Programme
    if cv2.waitKey(1) & 0xFF == ord(' '):
        break
    
# Save excel
wb.save("data4.xlsx")

# Release the Cameras
CamR.release()
CamL.release()
cv2.destroyAllWindows()


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

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

相关文章

RK3568驱动指南|第十二篇 GPIO子系统-第128章 GPIO入门实验

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

【期末考试】网络综合复习宝典

相关链接 网络复习思维导图&#xff08;HCIP&#xff09;https://www.edrawsoft.cn/viewer/public/s/038e2370897928 详述循环冗余校验CRC码https://blog.csdn.net/liht_1634/article/details/124328005?app_version6.2.6&codeapp_1562916241&csdn_share_tail%7B%22…

用通俗易懂的方式讲解:十分钟读懂 Stable Diffusion 运行原理

AIGC 热潮正猛烈地席卷开来&#xff0c;可以说 Stable Diffusion 开源发布把 AI 图像生成提高了全新高度&#xff0c;特别是 ControlNet 和 T2I-Adapter 控制模块的提出进一步提高生成可控性&#xff0c;也在逐渐改变一部分行业的生产模式。惊艳其出色表现&#xff0c;也不禁好…

windows server 2012、2019服务器定时重启

手动设置定时任务 1.开始菜单&#xff0c;找到“计划任务程序”; 如果无法创建基本任务的话&#xff0c;可能是系统中的“Task Scheduler”服务没有启动&#xff0c;你可在运行中键入“ services.msc”&#xff0c;查看“Task Scheduler”服务是否被设置成了“已禁用”&#x…

2024 年 8 款最好的PDF阅读和编辑软件

写出好的内容本身就是一门艺术。写作中的错误会让你看起来粗心大意或无能为力——这两种情况都不利于你的职业形象。没有任何软件能够取代现实生活中可以指出您写作错误的编辑器。幸运的是&#xff0c;有些软件已经接近并仍在改进它们的服务以帮助您清理工作。 编辑PDF很昂贵&…

k8s集群配置NodeLocal DNSCache

一、简介 当集群规模较大时&#xff0c;运行的服务非常多&#xff0c;服务之间的频繁进行大量域名解析&#xff0c;CoreDNS将会承受更大的压力&#xff0c;可能会导致如下影响&#xff1a; 延迟增加&#xff1a;有限的coredns服务在解析大量的域名时&#xff0c;会导致解析结果…

【Maven】002-Maven 安装和配置

【Maven】002-Maven 安装和配置 文章目录 【Maven】002-Maven 安装和配置一、官网1、官网2、历史版本列表 二、下载 Maven 3.8.8 版本1、进入 Maven 3.8.8 版本发行说明页2、进入下载页3、下载4、下载得到 apache-maven-3.8.8-bin.zip 三、Maven 安装1、将安装包解压到想放置的…

牛客周赛 Round 1 解题报告 | 珂学家 | 分类计数 + 同余DP

前言 生于生时&#xff0c;亡于亡刻。遵从自心&#xff0c;尽人之事。 整体评价 终于等来了侧重面试的比赛&#xff0c;而且题量刚刚好&#xff0c;不超纲&#xff0c;不涉及算法竞赛。 第一场的比赛&#xff0c;感觉题目出的比较典&#xff0c;A是简单模拟&#xff0c;B则是…

NX二次开发PK获取对象类型

PK_ENTITY_ask_class(),获取对象类型建议用这个函数&#xff0c;比较通用&#xff0c;包含所有对象类型&#xff0c;可以替代UF_MODL_ask_edge_type(),UF_MODL_ask_body_type(),UF_MODL_ask_face_type()等函数 PK_ENTITY_t entity; PK_CLASS_t PK_TYPE; PK_ENTITY_ask_class(e…

玩转Mysql 六(MySQL数据存储结构)

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 一、MySQL数据存储结构解析 1、mysql数据存储结构的组成 ​ 从 InnoDB 逻辑存储结构来看&a…

HandlerInterceptor拦截器 postHandle执行addHeader无效,postHandle执行setStatus无效的解决方案

问题描述 想在postHandle方法里执行addHeader方法来补充一些Header信息&#xff08;如分页信息&#xff09;&#xff0c;但是最后执行却未如期显示 拦截器源码 import com.zhangziwa.practisesvr.utils.response.ResponseContext; import jakarta.servlet.http.HttpServletR…

【降龙算法】基于QT插件机制实现一个机器视觉算法小框架

机器视觉行业有各种各样的拖拉拽框架&#xff0c;也叫做低代码平台&#xff0c;例如国内海康的VisionMaster&#xff1a; 一个机器视觉框架需要包含各种算法模块&#xff0c;日志窗口&#xff0c;图像显示窗口等等&#xff0c;【降龙算法】就是做了一个入门级的机器视觉算法框…

What does `HandlerInterceptor` do?

HandlerInterceptor 是 SpringMVC 中的一个接口&#xff0c;在SpringMVC应用中它提供了一种实现应用级拦截器的机制。 第1步&#xff1a;引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web<…

自动化测试框架pytest系列之8个常用的装饰器函数

自动化测试框架pytest系列之基础概念介绍(一)-CSDN博客 自动化测试框架pytest系列之21个命令行参数介绍(二)-CSDN博客 自动化测试框架pytest系列之强大的fixture功能&#xff0c;为什么fixture强大&#xff1f;一文拆解它的功能参数。(三)-CSDN博客 接上文 3.5 pytest的8…

当一堆数据差异过大如何选?

我们通过采样获得一对数组&#xff0c;但是他们差异过大&#xff0c;所以我们选择进行一次滤除。 先看基本概念&#xff1a; 移动平均&#xff08;Moving Average&#xff09;&#xff1a; 计算每个数据点及其相邻数据点的平均值&#xff0c;可以使用不同的窗口大小。这有助于…

获取 Dll 模块的加载字符串资源

概要 获取 Dll 模块中加载的字符串资源&#xff0c;可以通过 LoadString 实现。这个函数可以用于在不同版本索引系统字符串&#xff0c;对于一些根据名称操作系统菜单的功能&#xff0c;可以使用这种方法动态获取系统模块当前的加载字符串。 LoadStringW 从与指定模块关联的可…

Linux中断 -- 中断应答、嵌套、

接上文&#xff0c;本文继续介绍Linux软件部分逻辑。 参考内核版本&#xff1a;kernel-4.19 目录 1.中断信号在各级中断控制器中的应答 2.supports_deactivate_key意义 3.中断嵌套 1.中断信号在各级中断控制器中的应答 本章主要从内核软件层面来看各中断控制器对中断信号处…

反敏捷宣言

很多时候&#xff0c;敏捷在实践中并没有帮助团队更好的完成工作&#xff0c;而是成为了某种障碍以及僵化的流程。原文: The Anti-Agile Manifesto 警告1: 如果你是敏捷教练或Scrum Master或任何其他形式的敏捷支持者&#xff0c;本文的很多内容都会让你不爽。本文的目的之一是…

Spring MVC组件

1.DispatcherServlet前端控制器 用户请求到达前端控制器&#xff0c;它就相当于mvc模式中的c&#xff0c;dispatcherServlet 是整个流程控制的中心&#xff0c;由它调用其它组件处理用户的请求&#xff0c;dispatcherServlet 的存在降低了组件之间的耦合性。 2.HandlerMappin…

poi解析word取参数方法${参数名}获取参数异常处理(2024-01-12)

poi 读取word模板&#xff0c;确保 ${参数名} 在一个XWPFRun XWPFDocument读取word模板&#xff0c;经常遇到 ${参数名} 没有被识别在一个XWPFRun中&#xff0c;导致参数解析异常如法实现参数替换。 这里只是介绍word模板参数解析问题&#xff0c;让word格式如何转换为可以正常…