文章目录

  • 1、简介
    • 1.1 双目测距
    • 1.2 三维重建
  • 2、双目测距
    • 2.1、双目测距原理
    • 2.2、双目相机标定和校准
      • 2.2.1 双目相机选择
      • 2.2.2 采集标定板的左右视图
      • 2.2.3 相机标定和校准
    • 2.3、双目图像校正
    • 2.4、双目图像立体匹配
    • 2.5、计算深度图
  • 3、三维重建
    • 3.1 构建点云
    • 3.2 显示点云
      • 3.2.1 Open3D

1、简介

1.1 双目测距

双目相机实现双目测距主要分为4个步骤:相机标定、双目校正、双目匹配、计算深度信息。

(1)相机标定:需要对双目相机进行标定,得到两个相机的内外参数、单应矩阵。
(2) 双目校正:根据标定结果对原始图像进行校正,校正后的两张图像位于同一平面且互相平行。
(3)双目匹配:对校正后的两张图像进行像素点匹配。
(4)计算深度图:根据匹配结果计算每个像素的三维坐标,从而获得深度图。

1.2 三维重建

双目相机实现三维重建主要分为2个步骤:构建点云、显示点云。目前主要使用Open3D和PCL这两个库来进行三维重建,选用的库不同,构建点云和显示点云的方式也不同。


2、双目测距

2.1、双目测距原理


原理很简单,利用了相似三角形计算目标与基线的距离,所以 **双目测距的主要任务在于前期摄像头的定标、双目图像点的特征匹配上。 **


2.2、双目相机标定和校准

2.2.1 双目相机选择

双目相机根据图像来分主要有RGB+RGB、RGB+IR、IR+IR三种,根据数据输出来分主要有单USB接口、双USB接口两种。

基线不太建议太小,作为测试,一般baseline在3~9cm就可以满足需求。
从双目三维重建原理中可知,左右摄像头的成像平面尽可能在一个平面内,成像平面不在同一个平面的,尽管可以立体矫正,其效果也差很多。

本项目的双目相机为RGB+RGB,基线是固定的5cm,单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示)

2.2.2 采集标定板的左右视图

  • 采集数据前,调节相机焦距,尽可能保证左右视图清晰度一致(目的是为了让左右相机的焦距尽量一致)
  • 采集棋盘格图像时,标定板一般占视图1/2到1/3左右
  • 一般采集15~30张左右
width=9
height=5
left_video=0
right_video=-1
save_dir="data/"python get_stereo_images.py \--left_video 0 \--right_video -1 \--width 9  \--height 5  \--save_dir "data/" \

参数说明:

1.参数width指的是棋盘格宽方向黑白格子相交点个数
2.参数height指的是棋盘格长方向黑白格子相交点个数
3.参数left_video是左路相机ID,一般就是相机连接主板的USB接口号
4.参数right_video是右路相机ID,一般就是相机连接主板的USB接口号
5.如果你的双目相机是单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示),则设置left_video=相机ID,而right_video=-1,
6.参数detect建议设置True,这样可实时检测棋盘格,方面调整角度
7.按键盘s或者c保存左右视图图片


下面是采集双目摄像头标定板左右视图的Python代码:Get_Stereo_Imgs.py,仅需依赖OpenCV。

import os
import argparse
import cv2class StereoCamera(object):"""采集双目标定图片,按键盘【c】或【s】保存图片"""def __init__(self, chess_width, chess_height, detect=False):""":param chess_width: chessboard width size,即棋盘格宽方向黑白格子相交点个数,:param chess_height: chessboard height size,即棋盘格长方向黑白格子相交点个数:param detect: 是否实时检测棋盘格,方便采集数据"""self.chess_width = chess_widthself.chess_height = chess_heightself.detect = detectself.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)def detect_chessboard(self, image):"""检测棋盘格并显示"""gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)ret, corners = cv2.findChessboardCorners(gray, (self.chess_width, self.chess_height), None)if ret:# 角点精检测corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), self.criteria)# Draw and display the cornersimage = cv2.drawChessboardCorners(image, (self.chess_width, self.chess_height), corners2, ret)return imagedef capture2(self, left_video, right_video, save_dir):"""用于采集双USB连接线的双目摄像头:param left_video:int or str,左路视频路径或者摄像头ID:param right_video:int or str,右视频路径或者摄像头ID:param save_dir: str,保存左右图片的路径:return:"""self.create_file(save_dir)capL = cv2.VideoCapture(left_video)capR = cv2.VideoCapture(right_video)widthL, heightL, numFramesL, fpsL = self.get_video_info(capL)widthR, heightR, numFramesR, fpsR = self.get_video_info(capR)print("capL:\n", widthL, heightL, numFramesL, fpsL)print("capR:\n", widthR, heightR, numFramesR, fpsR)save_videoL = self.create_file(save_dir, "video", "left_video.avi")save_videoR = self.create_file(save_dir, "video", "right_video.avi")writerL = self.get_video_writer(save_videoL, widthL, heightL, fpsL)writerR = self.get_video_writer(save_videoR, widthR, heightR, fpsR)i = 0while True:isuccessL, frameL = capL.read()isuccessR, frameR = capR.read()if not (isuccessL and isuccessR):print("No more frames")breakif self.detect:l = self.detect_chessboard(frameL.copy())r = self.detect_chessboard(frameR.copy())else:l = frameL.copy()r = frameR.copy()cv2.imshow('left', l)cv2.imshow('right', r)key = cv2.waitKey(10)if key == ord('q'):breakelif key == ord('c') or key == ord('s'):print("save image:{:0=3d}".format(i))cv2.imwrite(os.path.join(save_dir, "left_{:0=3d}.png".format(i)), frameL)cv2.imwrite(os.path.join(save_dir, "right_{:0=3d}.png".format(i)), frameR)i += 1writerL.write(frameL)writerR.write(frameR)capL.release()capR.release()cv2.destroyAllWindows()def capture1(self, video, save_dir):"""用于采集单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示):param video:int or str,视频路径或者摄像头ID:param save_dir: str,保存左右图片的路径"""self.create_file(save_dir)cap = cv2.VideoCapture(video)width, height, numFrames, fps = self.get_video_info(cap)print("capL:\n", width, height, numFrames, fps)save_videoL = self.create_file(save_dir, "video", "left_video.avi")save_videoR = self.create_file(save_dir, "video", "right_video.avi")writerL = self.get_video_writer(save_videoL, int(width / 2), height, fps)writerR = self.get_video_writer(save_videoR, int(width / 2), height, fps)i = 0while True:isuccess, frame = cap.read()if not isuccess:print("No more frames")break# 分离左右摄像头frameL = frame[:, :int(width / 2), :]frameR = frame[:, int(width / 2):, :]if self.detect:l = self.detect_chessboard(frameL.copy())r = self.detect_chessboard(frameR.copy())else:l = frameL.copy()r = frameR.copy()cv2.imshow('left', l)cv2.imshow('right', r)key = cv2.waitKey(10)if key == ord('q'):breakelif key == ord('c') or key == ord('s'):print("save image:{:0=3d}".format(i))cv2.imwrite(os.path.join(save_dir, "left_{:0=3d}.png".format(i)), frameL)cv2.imwrite(os.path.join(save_dir, "right_{:0=3d}.png".format(i)), frameR)i += 1writerL.write(frameL)writerR.write(frameR)cap.release()cv2.destroyAllWindows()@staticmethoddef get_video_info(video_cap):width = int(video_cap.get(cv2.CAP_PROP_FRAME_WIDTH))height = int(video_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))numFrames = int(video_cap.get(cv2.CAP_PROP_FRAME_COUNT))fps = int(video_cap.get(cv2.CAP_PROP_FPS))return width, height, numFrames, fps@staticmethoddef get_video_writer(save_path, width, height, fps):if not os.path.exists(os.path.dirname(save_path)):os.makedirs(os.path.dirname(save_path))fourcc = cv2.VideoWriter_fourcc(*'XVID')frameSize = (int(width), int(height))video_writer = cv2.VideoWriter(save_path, fourcc, fps, frameSize)print("video:width:{},height:{},fps:{}".format(width, height, fps))return video_writer@staticmethoddef create_file(parent_dir, dir1=None, filename=None):out_path = parent_dirif dir1:out_path = os.path.join(parent_dir, dir1)if not os.path.exists(out_path):os.makedirs(out_path)if filename:out_path = os.path.join(out_path, filename)return out_pathdef get_parser():width = 9height = 5left_video = 0right_video = -1save_dir = "data/"parser = argparse.ArgumentParser(description='Camera calibration')parser.add_argument('--width', type=int, default=width, help='chessboard width size')parser.add_argument('--height', type=int, default=height, help='chessboard height size')parser.add_argument('--left_video', type=int, default=left_video, help='left video file or camera ID')parser.add_argument('--right_video', type=int, default=right_video, help='right video file or camera ID')parser.add_argument('--save_dir', type=str, default=save_dir, help='YML file to save calibrate matrices')return parserif __name__ == '__main__':args = get_parser().parse_args()stereo = StereoCamera(args.width, args.height, detect=args.detect)if args.left_video > -1 and args.right_video > -1:# 双USB连接线的双目摄像头stereo.capture2(left_video=args.left_video, right_video=args.right_video, save_dir=args.save_dir)elif args.left_video > -1:# 单USB连接线的双目摄像头(左右摄像头被拼接在同一个视频中显示)stereo.capture1(video=args.left_video, save_dir=args.save_dir)else:raise Exception("Error: Check your camera{}".format(args.left_video, args.right_video))

2.2.3 相机标定和校准

双目标定的目标是获得左右两个相机的内参、外参和畸变系数,其中内参包括左右相机的fx,fy,cx,cy,外参包括左相机相对于右相机的旋转矩阵和平移向量,畸变系数包括径向畸变系数(k1, k2,k3)和切向畸变系数(p1,p2)。

双目标定工具最常用的莫过于是MATLAB的工具箱: Stereo Camera Calibrator App。但该项目中使用OpenCV来实现双目相机的标定。

(1)单目相机标定(获取畸变校正和立体校正的矩阵)
参数说明:

width=9                     # 横向网格数
height=5                    # 纵向网格数
image_dir="data/"           # 棋盘格图片所在文件夹路径
save_dir="config/"          # 标定结果文件保存路径
file_name="camera_params"   # 标定结果保存文件名python Stereo_Camera_Calibration.py \--width 9  \--height 5  \--image_dir "data/" \--save_dir "data/" \--file_name "camera_params"\

代码如下:

#-*- coding:utf-8 -*-
import os
import numpy as np
import cv2
import glob
import argparseimport json
import pickleclass Stereo_Camera_Calibration(object):def __init__(self, width, height):""":param width: chessboard width size,即棋盘格宽方向黑白格子相交点个数,:param height: chessboard height size,即棋盘格长方向黑白格子相交点个数"""self.width       = widthself.height      = height# 设置迭代终止条件self.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)self.criteria_stereo = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)# =========================== 双目标定 =============================== #def stereo_calibration(self, file_L, file_R):# 设置 object points, 形式为 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)objp = np.zeros((self.width * self.height, 3), np.float32)  #我用的是6×7的棋盘格,可根据自己棋盘格自行修改相关参数objp[:, :2] = np.mgrid[0:self.width, 0:self.height].T.reshape(-1, 2)# 用arrays存储所有图片的object points 和 image pointsobjpoints = []  # 3d points in real world spaceimgpointsR = []  # 2d points in image planeimgpointsL = []for i in range(len(file_L)):                   ChessImaR = cv2.imread(file_L[i], 0)  # 右视图ChessImaL = cv2.imread(file_R[i], 0)  # 左视图retR, cornersR = cv2.findChessboardCorners(ChessImaR,(self.width, self.height), None)  # 提取右图每一张图片的角点retL, cornersL = cv2.findChessboardCorners(ChessImaL,(self.width, self.height), None)  # # 提取左图每一张图片的角点if (True == retR) & (True == retL):objpoints.append(objp)cv2.cornerSubPix(ChessImaR, cornersR, (11, 11), (-1, -1), self.criteria)  # 亚像素精确化,对粗提取的角点进行精确化cv2.cornerSubPix(ChessImaL, cornersL, (11, 11), (-1, -1), self.criteria)  # 亚像素精确化,对粗提取的角点进行精确化imgpointsR.append(cornersR)imgpointsL.append(cornersL)# 相机的单双目标定、及校正#   右侧相机单独标定retR, mtxR, distR, rvecsR, tvecsR = cv2.calibrateCamera(objpoints,imgpointsR,ChessImaR.shape[::-1], None, None)#   获取新的相机矩阵后续传递给initUndistortRectifyMap,以用remap生成映射关系hR, wR = ChessImaR.shape[:2]OmtxR, roiR = cv2.getOptimalNewCameraMatrix(mtxR, distR,(wR, hR), 1, (wR, hR))#   左侧相机单独标定retL, mtxL, distL, rvecsL, tvecsL = cv2.calibrateCamera(objpoints,imgpointsL,ChessImaL.shape[::-1], None, None)#   获取新的相机矩阵后续传递给initUndistortRectifyMap,以用remap生成映射关系hL, wL = ChessImaL.shape[:2]OmtxL, roiL = cv2.getOptimalNewCameraMatrix(mtxL, distL, (wL, hL), 1, (wL, hL))# --------- 双目相机的标定 ----------## 设置标志位为cv2.CALIB_FIX_INTRINSIC,这样就会固定输入的cameraMatrix和distCoeffs不变,只求解												

8、双目测距及3D重建python相关推荐

  1. 双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python

    双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python 目录 双目三维重建系统(双目标定+立体校正+双目测距+点云显示)Python 1.项目结构 2. Environment 3.双 ...

  2. Android双目三维重建:Android双目摄像头实现双目测距

    Android双目三维重建:Android双目摄像头实现双目测距 目录 Android双目三维重建:Android双目摄像头实现双目测距 1.开发版本 2.Android双目摄像头 3.双目相机标定 ...

  3. 双目测距代码 python opencv 利用双目摄像头拍照,测距

    环境:ubuntu14.04 软件:opencv3.3 +pycharm 好久没有写博客了,手都感觉好生疏,最近一直在研究工控机,水下机器人等,好不容易闲下来,没办法呀,继续前行吧,前面一直说双目测距 ...

  4. 研电赛项目之双目测距,涉及matlab相机标定,opencv多线程编程,摄像头读取,行人检测、BM立体匹配等等

    1 前言 今年参加了十五届研电赛,前天刚提交了作品,还有几天就答辩了,趁这几天总结一下这一个多月的收获. 本次研电赛作品为汽车行驶防碰撞系统,主要面向大型汽车在低速行驶场景下的防碰撞问题,通过双目相机 ...

  5. 双目测距+点云——使用MiddleBurry数据集的图片

    效果 输入: 左图 右图 输出: 视差图 深度图 实现了鼠标点击图片中的位置,显示其深度. 点云 其他例子点云: bicycle motorcycle 使用自己的双目摄像头拍摄的图片: bottle ...

  6. 双目立体视觉案例源代码 基于HALCCN的双目立体视觉系统实现 基于openev的双目测距 双目测距-opency 通用化视堂系线板架 Halcon视觉例子程序

    双目立体视觉案例源代码 双目测距 双目摄像头图像获取 标定代码 1.Halcoa 10三椎视党 2.Halcon视觉例子程序 wa2013 3.OpenCV+OpencL.双目立体视觉三幢重建 vs2 ...

  7. Mobile3DRecon:手机上的实时单眼3D重建

    今天为大家带来的文章是Mobile3DRecon: Real-time Monocular 3D Reconstruction on a Mobile Phone.在手机上实现实时的单眼3D重建. 此 ...

  8. 仅需一部摄像机即可实现基于AI的3D重建

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 Magic Leap研究人员提出了一种基于AI的方法,只需一个RGB相机即可捕获3D场景. 该方法称为 ...

  9. 3D重建传统算法对比深度学习,SFU谭平:更需要的是二者的融合

    点击我爱计算机视觉标星,更快获取CVML新技术 机器之心原创 作者:一鸣 近年来,深度学习在计算机视觉的重要领域--三维重建中取得了一系列成果.然而,最近有论文指出,深度学习的 3D 重建表现甚至不如 ...

最新文章

  1. 这是我见过最卡通的 Python 算法了,通俗易懂
  2. python课程设计总结1000-编程小白学习python总结文章(一)
  3. python用什么运行环境_使用SAE部署Python运行环境的教程
  4. ubuntu opencv c++ 读取摄像头
  5. 电脑键盘下划线怎么打_电脑键盘右边的数字键无法使用怎么办?
  6. ubuntu安装pr_在Ubuntu 16.04服务器上安装Zabbix 3.2
  7. 使用Server 2008新GPO做驱动器映射
  8. 浅析数据库设计三范式
  9. 证书服务器web注册,无法通过 Web 注册请求证书 - Windows Server | Microsoft Docs
  10. java holder_Java DataHolder.supports方法代码示例
  11. PHP 解决session 死锁
  12. 关于cannot deserialize from Object value (no delegate- or property-based Creator)报错的解决
  13. WAP网站制作(WAP网站建设)全攻略教程一
  14. Everything本地文件检索 快速搜索/共享神器
  15. 链接了GitHub的文件,在Pycharm不同颜色的不同含义
  16. 继昨天的猜拳游戏,升级为老虎机版
  17. 【线性代数】四、二次型
  18. 极域电子教室操作技巧
  19. php小数点问题,php--小数点问题 - osc_m4jd02jn的个人空间 - OSCHINA - 中文开源技术交流社区...
  20. ublox8 M8协议介绍

热门文章

  1. Edge浏览器开启下载提速
  2. opengl android 纹理贴图 代码,Android 使用opengl es的纹理贴图白屏问题请教。
  3. win10定时关机c语言,win10定时关机在哪?win10设置定时关机的三种方法
  4. IDEA 中 使用 git 进行上传和下载项目
  5. java数组:排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。`
  6. # 公有云?私有云?混合云?多云?行业云?傻傻分不清楚(下篇)
  7. XCTF final noxss
  8. 汇编程序设计-11-AX、BX、CX、DX寄存器
  9. Oracle查询出第N高薪水的值
  10. 全网最便宜的OpenHarmony开发板和模组Neptune问世(基于联盛德W800的SoC),9.9元带蓝牙和wifi功能还包邮