专栏首页CSDN博客双目摄像头测量距离
原创

双目摄像头测量距离

原文博客:Doi技术团队 链接地址:https://blog.doiduoyi.com/authors/1584446358138 初心:记录优秀的Doi技术团队学习经历 本文链接:双目摄像头测量距离

前言

在计算机视觉中,可以通过双目摄像头实现,常用的有BM 算法和SGBM 算法等,双目测距跟激光不同,双目测距不需要激光光源,是人眼安全的,只需要摄像头,成本非常底,也用于应用到大多数的项目中。本章我们就来介绍如何使用双目摄像头和SGBM 算法实现距离测量。

相机标定

每个种双目摄像头都不一样,他们之间的距离,畸变等其他的原因,这些都会导致他们定位算法参数的差异,所以我们通常是通过相机标定来得到他们的算法参数。标定的目的是为了消除畸变以及得到内外参数矩阵,内参数矩阵可以理解为焦距相关,它是一个从平面到像素的转换,焦距不变它就不变,所以确定以后就可以重复使用,而外参数矩阵反映的是摄像机坐标系与世界坐标系的转换,至于畸变参数,一般也包含在内参数矩阵中。从作用上来看,内参数矩阵是为了得到镜头的信息,并消除畸变,使得到的图像更为准确,外参数矩阵是为了得到相机相对于世界坐标的联系,是为了最终的测距。

拍摄标定图像

我们需要通过摄像头拍摄标定图片,拍摄得到的是左目摄像头和右目摄像头的图像,笔者一般是拍摄16张左右。通常双目摄像头拍摄得到的图像是左目摄像头拍摄的在第一位,然后是右目摄像头,使用OpenCV拍摄的图像,可以通过裁剪的方式把他们分开分别存储。以下是笔者提供的拍摄标定图像的Python代码,通过按回车键保存图像。注意在拍摄前需要调整好摄像头的焦距,调整之后就不要再动了。

import cv2

imageWidth = 1280
imageHeight = 720

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, imageWidth * 2)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, imageHeight)
i = 0

while True:
    # 从摄像头读取图片
    success, img = cap.read()
    if success:
        # 获取左右摄像头的图像
        rgbImageL = img[:, 0:imageWidth, :]
        rgbImageR = img[:, imageWidth:imageWidth * 2, :]
        cv2.imshow('Left', rgbImageL)
        cv2.imshow('Right', rgbImageR)
        # 按“回车”保存图片
        c = cv2.waitKey(1) & 0xff
        if c == 13:
            cv2.imwrite('Left%d.bmp' % i, rgbImageL)
            cv2.imwrite('Right%d.bmp' % i, rgbImageR)
            print("Save %d image" % i)
            i += 1

cap.release()

以下图像是相机标定是所需的棋盘,可以使用A3纸打印出来,使用木板等固定好,不要弯曲,最好是使用专业棋盘。摄像头拍摄的棋盘应该占拍摄区域的三分之一以上。

在这里插入图片描述

图像标定

拍摄完成图像之后,使用MATLAB对其进行标定。笔者使用的是MATLAB R2016a,其他的版本应该也可以。

打开MATLAB R2016a,添加TOOLBOX_calib的路径,TOOLBOX_calib下载地址:https://resource.doiduoyi.com/#w0w0sko,下载之后把它解压到D盘的根目录,如下下图所示:

在这里插入图片描述

然后在MATLAB R2016a命令区输入:cd d:\calib_example打开标定图片的文件夹,如果读者保存的图片是其他路径,就打开对应的路径。

在这里插入图片描述

还是在刚才的命令区,输入命令:calib_gui,打开标定工具,即可启动标定工具,界面如下,然后点击Standard(all the images are stored in memory)按钮。

在这里插入图片描述

执行上一步之后会弹出以下界面,然后点击Image names按钮,这个主要是为了通过图像的名称来列出所需的标定图像。

在这里插入图片描述

点击Image names按钮之后,会列出当前目录的图像,首先我们标定左目摄像头拍摄的图像。我们在拍摄图像保存时,保存的名字变化主要在名字最后的数字,这样我们就可以通过固定图像名称的前半部分和后缀名来列出将要标定的图像。

在这里插入图片描述

输入如下,指定名称为Left,后缀名为bmp,这就可以把左目摄像头的图像都加载进来了。

在这里插入图片描述

加载完成之后会弹以下窗口,这些就是将要标定的图像。

在这里插入图片描述

然后回到标定工具的功能选择界面,点击Extract grid corners按钮,开始标定。

在这里插入图片描述

执行上一步之后,需要回到命令区,需要对接下来的标定做好配置。其他基本可以默认,直接回车就好,有两个参数需要更加推荐输入,读者可能跟我的不一样。

在这里插入图片描述

执行完上面之后,会弹出一个标定图像的窗口,标定时需要按照左上、右上、右下、左下顺序点击 4 个边界角点,如图所示。

在这里插入图片描述

回到命令区,标定第一张图像时,需要输入棋盘中每个格子的大小,笔者使用A3纸打印的,每个格子大概是28.8mm,这个需要读者去测量自己其他每个格子的大小。

在这里插入图片描述

输入上面的数据之后,会在每个格子的对角都标注,这样对畸变的图像进一步调整的,如果使用的是无畸变的图像,那到这一步基本上是完成了。

在这里插入图片描述

如果是无畸变的摄像头,在命令去看到以下输出时,直接回车就可以开始标注下一张图像了。如果是有畸变的摄像头,需要输入1,然后根据提示输入调整参数,参数范围在-1, 1,通过调整使得红色的标记都在每个格子的对角上。

在这里插入图片描述

全部标记完成之后,再次回到标定功能选择菜单上,然后点击Calibration按钮。

在这里插入图片描述

点击上面按钮之后,会输入类似以下的信息。

在这里插入图片描述

最后点击保存标注信息,文件会被保存为Calib_Results.mat,我们需要将标定结果文件重命名为: Calib_Results_left.mat,这是为了修改成下一步默认路径。左目摄像头标定完成后,按照同样的方法标定右目摄像头,将标定结果文件重命名为:Calib_Results_right.mat,之后可以进行双目标定。

在这里插入图片描述

上面都完成之后,应该有Calib_Results_left.matCalib_Results_right.mat这两个文件。然后在命令区输入命令:stereo_gui,会弹出以下界面,点击Load left and right calibration files按钮。

在这里插入图片描述

执行上一步之后会输入以下信息,这次产生就是我们测距算法所需的参数,不同我们还可以对这些参数进行优化。

在这里插入图片描述

点击Run stereo calibration按钮对结果进行优化。

在这里插入图片描述

最后输出的是经过优化的参数,输出如下,这些参数非常重要。

在这里插入图片描述

以下为每个参数值对应的双目测距的参数。他们的介绍如下:

fc_left:左目镜头像素级焦距值,fc_left × 像元尺寸 = 左目镜头物理焦距值,像

元尺寸见双目相机产品参数表。

cc_left:左目光心位置坐标。

kc_left:左目镜头畸变系数。

alpha_c_left: 偏斜系数。

om:旋转向量。

T:平移向量。T_01,双目间距(即:双目基线)

Focal Length:          fc_left = [ 781.69191   781.93358 ]  [ 3.14543   3.14792 ]
                                            【fc_left_x     fc_left_y】      【误差1,  误差2】
Principal point:       cc_left = [ 319.50000   239.50000 ]   [ 0.00000   0.00000 ]
                                            【cc_left_x     cc_left_y】     【误差1     误差2】
Skew:             alpha_c_left = [ 0.00000 ]   [ 0.00000  ]   => angle of pixel axes = 90.00000   0.00000 degrees
Distortion:            kc_left = [ 0.02704   0.10758   -0.00408   -0.01769  0.00000 ]   [ 0.01402   0.07915   0.00047   0.00072  0.00000 ]
                                           【kc_left_01,  kc_left_02,  kc_left_03,  kc_left_04,   kc_left_05】【误差1 误差2 误差3 误差4 误差5】


Intrinsic parameters of right camera:

Focal Length:          fc_right = [ 781.98159   784.43429 ]   [ 3.23167   3.20482 ]
                                            【fc_right_x     fc_right_y】      【误差1,  误差2】
Principal point:       cc_right = [ 319.50000   239.50000 ]   [ 0.00000   0.00000 ]
                                            【cc_right_x     cc_right_y】     【误差1     误差2】
Skew:             alpha_c_right = [ 0.00000 ]   [ 0.00000  ]   => angle of pixel axes = 90.00000   0.00000 degrees
Distortion:            kc_right = [ 0.00679   0.18063   -0.00633   -0.00190  0.00000 ]   [ 0.01585   0.12209   0.00054   0.00056  0.00000 ]
                                             【kc_right_01,  kc_right_02,  kc_right_03,  kc_right_04,   kc_right_05】【误差1 误差2 误差3 误差4 误差5】


Extrinsic parameters (position of right camera wrt left camera):

                                     【rec旋转向量】
Rotation vector:             om = [ -0.01044   -0.04553  -0.00143 ]   [ 0.00026   0.00033  0.00018 ]

                                     【T平移向量】
Translation vector:           T = [ -60.87137   0.15622  0.01502 ]   [ 0.17816   0.16723  1.12660 ]

距离测量

本章教程我们使用的是SGBM算法,SGBM算法作为一种全局匹配算法,立体匹配的效果明显好于局部匹配算法,但是同时复杂度上也要远远大于局部匹配算法。

SGBM算法在OpenCV中已经开源,该算法的函数为cv2.StereoSGBM_create。下面我们就使用Python实现这个双目测距的程序,为了简单,该程序只是使用本地保存的左目图像和右目图像,如何读者想使用摄像头拍摄,可以参考文章开头提供的拍照代码,两者结合,实时检测距离。

这个SGBM算法的实现和相关函数都可以通过OpenCV完成,imageWidth是单目摄像头拍摄的宽度。

import cv2
import numpy as np

imageWidth = 1280
imageHeight = 720
imageSize = (imageWidth, imageHeight)

以下是相机标定的参数,按照相机标定生成的参数对应修改这些参数值。

'''左目相机标定参数
fc_left_x   0            cc_left_x
0           fc_left_y    cc_left_y
0           0            1
'''
cameraMatrixL = np.array([[849.38718, 0, 720.28472],
                          [0, 850.60613, 373.88887],
                          [0, 0, 1]])
# [kc_left_01,  kc_left_02,  kc_left_03,  kc_left_04,   kc_left_05]
distCoeffL = np.array([0.01053, 0.02881, 0.00144, 0.00192, 0.00000])

'''右目相机标定参数
fc_right_x   0              cc_right_x
0            fc_right_y     cc_right_y
0            0              1
'''
cameraMatrixR = np.array([[847.54814, 0, 664.36648],
                          [0, 847.75828, 368.46946],
                          [0, 0, 1]])

# kc_right_01,  kc_right_02,  kc_right_03,  kc_right_04,   kc_right_05
distCoeffR = np.array([0.00905, 0.02094, 0.00082, 0.00183, 0.00000])

# T平移向量
T = np.array([-59.32102, 0.27563, -0.79807])

# rec旋转向量
rec = np.array([-0.00927, -0.00228, -0.00070])

以下就是使用左右目摄像头拍摄到的两张图像,利用SGBM算法技术图像中物体距离摄像头的距离。最后输出的xyz是图像中的三维坐标,通过这个结果可以获取图像中每个点的三维坐标。

# 立体校正
R = cv2.Rodrigues(rec)[0]
Rl, Rr, Pl, Pr, Q, validROIL, validROIR = cv2.stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR,
                                                            imageSize, R, T, flags=cv2.CALIB_ZERO_DISPARITY, alpha=0,
                                                            newImageSize=imageSize)

# 计算更正map
mapLx, mapLy = cv2.initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl, imageSize, cv2.CV_32FC1)
mapRx, mapRy = cv2.initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize, cv2.CV_32FC1)

# 读取图片
rgbImageL = cv2.imread("Left3.bmp")
grayImageL = cv2.cvtColor(rgbImageL, cv2.COLOR_BGR2GRAY)
rgbImageR = cv2.imread("Right3.bmp")
grayImageR = cv2.cvtColor(rgbImageR, cv2.COLOR_BGR2GRAY)

# 经过remap之后,左右相机的图像已经共面并且行对齐
rectifyImageL = cv2.remap(grayImageL, mapLx, mapLy, cv2.INTER_LINEAR)
rectifyImageR = cv2.remap(grayImageR, mapRx, mapRy, cv2.INTER_LINEAR)

# SGBM算法重要的参数
mindisparity = 32
SADWindowSize = 16
ndisparities = 176
# 惩罚系数
P1 = 4 * 1 * SADWindowSize * SADWindowSize
P2 = 32 * 1 * SADWindowSize * SADWindowSize

# BM算法
sgbm = cv2.StereoSGBM_create(mindisparity, ndisparities, SADWindowSize)
sgbm.setP1(P1)
sgbm.setP2(P2)

sgbm.setPreFilterCap(60)
sgbm.setUniquenessRatio(30)
sgbm.setSpeckleRange(2)
sgbm.setSpeckleWindowSize(200)
sgbm.setDisp12MaxDiff(1)
disp = sgbm.compute(rectifyImageL, rectifyImageR)

# 在实际求距离时,ReprojectTo3D出来的X / W, Y / W, Z / W都要乘以16
xyz = cv2.reprojectImageTo3D(disp, Q, handleMissingValues=True)
xyz = xyz * 16

xyz是一个矩阵,不能直观体现图像中物体的距离,所以我们可以把他们转换成一张灰度图,更加颜色的深浅就知道他们的大概情况了。然后我们还可以添加一个鼠标点击事件,这样通过点击图像上的点,直接输出该点的三维坐标。

# 用于显示处理
disp = disp.astype(np.float32) / 16.0
disp8U = cv2.normalize(disp, disp, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
disp8U = cv2.medianBlur(disp8U, 9)


# 鼠标点击事件
def onMouse(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN:
        print('点 (%d, %d) 的三维坐标 (%f, %f, %f)' % (x, y, xyz[y, x, 0], xyz[y, x, 1], xyz[y, x, 2]))


# 显示图片
cv2.imshow("disparity", disp8U)
cv2.setMouseCallback("disparity", onMouse, 0)

cv2.waitKey(0)
cv2.destroyAllWindows()

通过xyz最终生成的图像类似如下,全黑的区域是无法测量的距离或者过远的距离。直接使用鼠标点击图像的位置就可以输出该点的三维坐标了。

在这里插入图片描述
点 (777, 331) 的三维坐标 (57.852905, -23.240721, 519.846985)
点 (553, 383) 的三维坐标 (-70.191200, 6.370971, 525.825806)
点 (573, 305) 的三维坐标 (-58.206013, -38.124424, 521.407104)
点 (1012, 694) 的三维坐标 (96.540405, 92.311638, 262.277863)

以上的源码笔者提供了下载,包括测试图像。

源码下载地址: https://resource.doiduoyi.com/#ckg1m1u

更好阅读体验可以访问Kesci Lab: https://www.kesci.com/home/project/5ebfe43f0a6bbb002de8aa58

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一篇文章认识《双目立体视觉》

    双目立体视觉,由两个摄像头组成,像人的眼睛能看到三维的物体,获取物体长度、宽度信息,和深度的信息;单目视觉获取二维的物体信息,即长度、宽度。

    一颗小树x
  • 基于 FPGA 的社交距离检测装置

    2019 年末,新冠疫情爆发, “保持人与人之间距离”的倡议也逐渐成为大家的共识。但事实上,在人流密集场合中,社交距离的保持时常被大家忽视。如何实时监测社交场合...

    FPGA技术江湖
  • 基于 FPGA 的社交距离检测装置

    2019 年末,新冠疫情爆发, “保持人与人之间距离”的倡议也逐渐成为大家的共识。但事实上,在人流密集场合中,社交距离的保持时常被大家忽视。如何实时监测社交场合...

    FPGA技术江湖
  • 剖析自动驾驶双目视觉解决方案

    未来十年,为了完成从感知+预警到决策+执行的进化之路,高级辅助驾驶系统(ADAS)将接入更多的传感器,实现更为复杂的计算,同时具备更高的安全性。 双目视觉简介 ...

    刘盼
  • 自动驾驶传感器那点事之摄像头传感器分类

    车载上一般使用的是数字摄像头,它可以将视频采集设备产生的模拟视频信号转换成数字信号,进而将其储存在计算机里。

    点云PCL博主
  • opencv双目测距实现

    来自: http://blog.csdn.net/sunanger_wang/article/details/7744015 虽然最近注意力已经不可遏制地被神经...

    智能算法
  • 在Android实现双目测距

    在上一章我们介绍了《双目摄像头测量距离》,在这个基础上,我们来了解如何在Android上使用双目测距算法。通过本教程,你不仅掌握如何在Android中使用SBM...

    夜雨飘零
  • 三种主流深度相机介绍

    深度相机又称之为3D相机,顾名思义,就是通过该相机能检测出拍摄空间的景深距离,这也是与普通摄像头最大的区别。

    点云PCL博主
  • 双目视觉测距系统软硬件设计

    随着计算机技术和光电技术的发展,机器视觉技术应运而生。在图像处理技术领域中,有一种采用 CCD摄像机作为图像传感器采集数据的非接触式测量方法,这种方法具有精度高...

    苏州程序大白
  • 视觉三维建模

      既能准确计算进出人数,又能有效过滤掉干扰物体(如推车、行李箱、人员徘徊、拥挤及躯体重叠、人员经过未进入等,另外用户也需要有特殊应用,比如身高低于1.2米的儿...

    JNingWei
  • 在Android实现双目测距

    我们来了解如何在Android上使用双目测距算法。通过本教程,你不仅掌握如何在Android中使用SBM等双目测距算法,顺便也了解到如何在Android Stu...

    夜雨飘零
  • 3D成像方法 汇总(原理解析)— 双目视觉、激光三角、结构光、ToF、光场、全息

    这里要介绍的是真正的3D成像,得到物体三维的图形,是立体的图像。而不是利用人眼视觉差异的特点,错误感知到的假三维信息。

    3D视觉工坊
  • 工业镜头和民用镜头的区别在哪里?

    光学镜头一般称为摄像镜头或摄影镜头,简称镜头,其功能就是光学成像。镜头是机器视觉系统中的重要组件,对成像质量有着关键性的作用,它对成像质量的几个最主要指标都有影...

    小白学视觉
  • 智能手机双摄像头工作原理详解:RBG +RGB, RGB + Mono

    前一篇介绍了为什么会出现双摄像头(简称双摄)手机以及它的典型应用,下面来分析一下双摄的工作原理。 由于双摄技术的快速发展,目前已经衍生出了几种不同的双摄硬件和...

    用户1150922
  • 【深度】机器究竟是如何“看见”世界的?

    GAIR 今年夏天,雷锋网将在深圳举办一场盛况空前的“全球人工智能与机器人创新大会”(简称GAIR)。大会现场,雷锋网将发布“人工智能&机器人Top25创新企...

    AI科技评论
  • 谷歌 Pixel 4 人像模式拍照算法揭秘

    人像模式是 Pixel 系列手机中相机的一个功能,任何人均能通过此功能拍摄出专业的浅景深图像。人像模式最初发布于 Pixel 2,并在 Pixel 3 上得以改...

    CV君
  • 伪激光雷达:无人驾驶的立体视觉

    激光雷达成本高,用廉价的立体视觉替代它可行吗? 作者:Jeremy Cohen 编译:McGL

    McGL
  • 智能手机双摄像头原理解析:RGB +Depth

    本篇来探讨一下智能手机摄像头中:普通彩色相机(RGB) + 深度相机(Depth)的技术原理。 首先来解释一下什么是深度相机吧。 深度相机 顾名思义,深度相机就...

    用户1150922
  • PoE级联摄像机有哪些?能传输多远、级联多少级?

    首先我们都知道的是,监控工程摄像机多了的情况需要级联。那么几个点位安装在一根杆上要级联吗?多个摄像头要串联?室外监控摄像机要传输400米远?这些都是困扰新手的问...

    网络技术联盟站

扫码关注云+社区

领取腾讯云代金券