结构光三维重建(二)线结构光三维重建

本文介绍了近景三维重建中线结构光技术的原理,包括基于三角测量的三维重建过程,步骤涵盖了相机标定、激光线提取和光平面标定。特别关注了双目线激光三维重建的实现,强调了其在复杂环境中的优势与误匹配问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在近景三维重建领域,结构光技术可以说是应用最广泛的,尤其在工业领域。该技术目前具有高精度和无视弱纹理等优点,但复杂的室外环境还是会对该技术造成一定程度的干扰。目前用的比较多的结构光分别两大类:线结构光以及条纹结构光。接下来我来简单介绍一下线结构光三维重建。


1 原理

线结构光三维重建测量系统,是基于三角测量的原理进行三维重建。整个成像系统示意图如下图所示,我们可以看到主要分为两个子系统,即激光发射器构成的激光系统和相机构成的成像系统。首先我们假定所有点的关系基于相机坐标系,成像系统基于相机标定模型,相机标定完成后得到相机内参,基于相机内参可以得到目标物体点P在成像面P’点的位置坐标(Xc,Yc,Zc)。当P’点的位置坐标已知后,Oc坐标为(0,0,0),因为空间直线OcP’直线方程可以得到。空间直线方程得到后,联立激光发射器投射的光平面方程,直线与平面的交点P(Xw,Yw,Zw)即可通过求解得到,但这一步得到的坐标为相机坐标系下的坐标,将该点坐标乘以相机标定过程中得到的相机外参即可得到该点世界坐标。

2 步骤

结构光三维重建通常分为三步:1)相机标定,标定出相机的内外参数;2)结构光条纹中心的提取即激光线的提取;3)结构光标定即光平面的标定;即将世界坐标系下的三维点转换为相机坐标系下的三维点,通过多个相机下的三维坐标点拟合出光平面,然后拟合的光平面可以用来完成三维重建。

整个过程的详细路线图如下图所示:
请添加图片描述
激光线提取算法如下所示

光平面标定
有直接标定和间接标定两种办法。直接标定法就是直接获取光条上特征点的世界坐标(Xw,Yw,Zw),进而求得光平面,空间平面方程(光平面方程)可以表示如下AX+BY+CZ = 1,有多个特征点的世界点坐标,通过最小二乘即可拟合出光平面方程。
间接标定法并不是在光条上直接获得特征点的世界坐标,而是首先对摄像机进行标定,通过提取标定靶上的光斑中心和对应的世界坐标,由摄像机模型确定出投影矩阵M,然后拍摄光条图像,通过图像处理和计算提取光条上各点的像素坐标,经投影矩阵进行坐标变换后,获取与该像素坐标对应的世界坐标,进而构造方程AX+BY+CZ = 1,并通过最小二乘解求出A、B、C。

3 双目线激光三维重建

是一种基于线激光扫描和双目立体视觉相结合的三维测量系统,该种线激光扫描系统可以避免传统单相机扫描技术中标定激光平面的过程,提高三维测量精度。该测量系统由两个相机、一个先激光投射器和一个旋转电机组成,如图1所示,测量时通过向被测物体投射单条线激光条纹,然后由旋转电机带动激光条纹扫描整个被测物体,扫描的同时立体视觉系统以指定的帧率拍摄被测物体;根据所拍摄的含有激光条纹的立体图像对,由立体视觉的极线约束和激光条纹特征计算出立体视觉系统之间的对应性关系;最后根据预先标定好的系统参数按照双目立体视觉的方法计算出物体的三维点云数据。简单来说,就是通过相机标定、立体校正得到行(或者列)对齐的图像,然后提取出激光线,由于每一行(或每一列)在左右图像上仅有唯一的点相对应,所以行号(或列号)相同的激光点即可认为是对应的匹配点,然后根据视差,求得三维坐标。

当然了,这是对于单条激光线而言的,如果是双目多线激光,则每一行(或每一列)在左右图像上没有唯一的点相对应,这时可以把我们的激光平面再捡回来了,即通过激光平面来约束得到唯一的匹配点。具体措施就是,左相机提取出的激光点像素坐标通过激光平面得到3D坐标,通过左右相机的旋转平移矩阵,将其转换到右相机坐标系下,然后转换到右影像的像素坐标,判断右影像上的激光点的像素坐标是否有与之重合的,重合的则为对应的匹配点,然后根据视差得到三维坐标。
在这里插入图片描述

4 总结

最后值得一提的是,双目多线激光还存在着一点小问题,比如,有一部分多的误匹配点难以剔除。目前论文里说到的剔除误匹配点的方法,都有很大的局限性,且效果不会很明显。
理论比较简单,这里就不放源码了。

以下是一个基于Python的单目线结构光三角测量代码示例: ```python import numpy as np import cv2 # 读入图像 img = cv2.imread('image.png') # 将图像转化为灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 计算图像的梯度 gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0) gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1) # 计算图像的深度信息 depth = np.zeros_like(gray, dtype=np.float32) for i in range(gray.shape[0]): for j in range(gray.shape[1]): depth[i][j] = abs(gx[i][j]) + abs(gy[i][j]) # 构建相机矩阵 f = 500 # 焦距 cx = gray.shape[1] / 2 # 光心x坐标 cy = gray.shape[0] / 2 # 光心y坐标 K = np.array([[f, 0, cx], [0, f, cy], [0, 0, 1]]) # 计算三维坐标 points = np.zeros((gray.shape[0], gray.shape[1], 3), dtype=np.float32) for i in range(gray.shape[0]): for j in range(gray.shape[1]): points[i][j][0] = (j - cx) * depth[i][j] / f points[i][j][1] = (i - cy) * depth[i][j] / f points[i][j][2] = depth[i][j] # 选择三个点进行三角测量 p1 = np.array([0, 0, 0]) p2 = np.array([gray.shape[1], 0, 0]) p3 = np.array([0, gray.shape[0], 0]) # 计算相应的图像坐标 p1u, p1v = cv2.projectPoints(p1, np.zeros((3,)), np.zeros((3,)), K, np.zeros((4,))) [0][0] p2u, p2v = cv2.projectPoints(p2, np.zeros((3,)), np.zeros((3,)), K, np.zeros((4,))) [0][0] p3u, p3v = cv2.projectPoints(p3, np.zeros((3,)), np.zeros((3,)), K, np.zeros((4,))) [0][0] # 利用三角测量法计算三维坐标 A = np.array([[p1u, p1v, 1], [p2u, p2v, 1], [p3u, p3v, 1]]) b = np.array([p1[0], p2[0], p3[0]]) x = np.linalg.solve(A, b) y = np.array([p1[1], p2[1], p3[1]]) z = np.array([p1[2], p2[2], p3[2]]) # 显示三维点云 from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(points[:, :, 0].ravel(), points[:, :, 1].ravel(), points[:, :, 2].ravel()) ax.scatter(x, y, z, c='r') plt.show() ``` 代码中使用了OpenCV库和Matplotlib库,首先读入一张图像,然后将其转化为灰度图像。接着使用Sobel算子计算图像的梯度信息,并将其加起来作为深度信息。然后构建相机矩阵,并根据相机矩阵和深度信息计算每个像素点的三维坐标。接着选择三个点作为三角测量的参考点,并计算它们在图像中的坐标。最后使用三角测量法计算出参考点的三维坐标,并使用Matplotlib库显示三维点云。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值