【OpenCV实战】单目相机 + 条纹结构光三维重建:从条纹图到点云
前言单目相机本身只能获取二维图像无法直接得到真实深度信息。如果想用单个相机做三维重建常见做法是引入主动光源比如投影仪投射条纹图案。这种方法通常称为结构光三维重建其中投影仪投射编码条纹单目相机采集物体表面的条纹变形图像。通过分析条纹相位变化再结合相机和投影仪的标定参数就可以恢复物体表面的三维坐标。本文主要介绍单目相机 条纹图重建的基本流程包括单目相机标定条纹图生成条纹图采集相位计算相位展开投影仪坐标恢复三维点云重建Python 和 C 核心代码一、单目相机 条纹图重建是什么普通单目相机只能看到图像中的像素点(u, v)但是无法知道这个像素点对应的真实空间深度。加入条纹投影后投影仪会向物体表面投射一组有规律的条纹。物体表面的高度变化会导致条纹发生弯曲或偏移。相机拍摄到这些变形条纹后可以根据条纹相位反推出物体表面的位置。可以简单理解为相机负责拍摄 投影仪负责给物体表面编码 条纹相位负责建立像素对应关系 标定参数负责恢复三维坐标在结构光系统中投影仪通常可以看作一个“反向相机”。因此单目相机 投影仪实际上可以形成一个类似双目的几何系统。二、整体重建流程完整流程如下1. 标定单目相机 2. 标定投影仪参数 3. 标定相机和投影仪之间的外参 4. 生成正弦条纹图 5. 投影条纹到物体表面 6. 相机采集条纹图 7. 计算包裹相位 8. 相位展开得到绝对相位 9. 根据相位恢复投影仪像素坐标 10. 通过三角测量恢复三维点云如果只想看物体表面的相对形变可以不做完整投影仪标定。但如果要得到真实毫米级三维坐标就必须完成相机、投影仪以及二者外参的标定。三、正弦条纹图原理常用的条纹图是正弦条纹I(x, y) A B * cos(phase delta)其中A背景亮度B条纹对比度phase相位delta相移量常见方法是四步相移法分别投影四张相位不同的条纹图0 π / 2 π 3π / 2相机采集到四张图后可以计算包裹相位phase atan2(I4 - I2, I1 - I3)这个相位范围通常在[-π, π]所以它叫包裹相位。要进行三维重建还需要进一步做相位展开。四、Python 生成条纹图下面代码生成一组竖直方向的正弦条纹图。import cv2 import numpy as np import os width 1280 height 720 period 64 output_dir fringe_patterns os.makedirs(output_dir, exist_okTrue) phase_shifts [0, np.pi / 2, np.pi, 3 * np.pi / 2] x np.arange(width) xx np.tile(x, (height, 1)) for i, shift in enumerate(phase_shifts): img 127.5 127.5 * np.cos(2 * np.pi * xx / period shift) img img.astype(np.uint8) cv2.imwrite(f{output_dir}/vertical_{i}.png, img)生成后的图片可以通过投影仪依次投射到物体表面然后用相机同步采集。如果要得到完整的投影仪二维坐标一般还需要生成水平方向条纹y np.arange(height) yy np.tile(y.reshape(-1, 1), (1, width)) for i, shift in enumerate(phase_shifts): img 127.5 127.5 * np.cos(2 * np.pi * yy / period shift) img img.astype(np.uint8) cv2.imwrite(f{output_dir}/horizontal_{i}.png, img)竖直条纹主要用于恢复投影仪的x坐标水平条纹主要用于恢复投影仪的y坐标。五、Python 计算包裹相位假设相机已经采集到四张竖直条纹图capture_vertical_0.png capture_vertical_1.png capture_vertical_2.png capture_vertical_3.png计算相位代码如下import cv2 import numpy as np imgs [] for i in range(4): img cv2.imread(fcapture_vertical_{i}.png, cv2.IMREAD_GRAYSCALE) imgs.append(img.astype(np.float32)) I1, I2, I3, I4 imgs wrapped_phase np.arctan2(I4 - I2, I1 - I3) phase_show cv2.normalize( wrapped_phase, None, 0, 255, cv2.NORM_MINMAX ).astype(np.uint8) cv2.imwrite(wrapped_phase.png, phase_show)这一步得到的是包裹相位图。从图像上看相位会呈现周期性跳变这属于正常现象。六、相位展开包裹相位只能表示一个周期内的相位无法区分当前点位于第几个条纹周期。因此需要进行相位展开。简单场景下可以使用numpy.unwrap()做一维展开unwrapped_phase np.unwrap(wrapped_phase, axis1) phase_unwrap_show cv2.normalize( unwrapped_phase, None, 0, 255, cv2.NORM_MINMAX ).astype(np.uint8) cv2.imwrite(unwrapped_phase.png, phase_unwrap_show)不过在真实项目中单纯unwrap()很容易受到噪声、阴影、反光和断裂区域影响。更稳定的方式是格雷码 相移法 多频相移法 时间相位展开 质量引导相位展开工程里比较常用的是“格雷码 四步相移”。格雷码负责确定条纹周期编号相移法负责提供高精度亚像素相位。七、由相位恢复投影仪坐标假设投影条纹周期为period展开后的相位为unwrapped_phase则可以近似恢复投影仪横坐标projector_x unwrapped_phase * period / (2 * np.pi)如果同时采集了水平条纹也可以恢复投影仪纵坐标projector_y unwrapped_phase_y * period / (2 * np.pi)最终可以建立这样的对应关系相机像素点: (camera_x, camera_y) 投影仪像素点: (projector_x, projector_y)有了这组对应关系就可以把相机和投影仪当作一个双目系统进行三角测量。八、Python 三角测量生成点云假设已经得到camera_matrix 相机内参 projector_matrix 投影仪内参 R 投影仪相对于相机的旋转矩阵 T 投影仪相对于相机的平移向量可以构造两个投影矩阵import cv2 import numpy as np P_camera camera_matrix np.hstack((np.eye(3), np.zeros((3, 1)))) P_projector projector_matrix np.hstack((R, T))然后对相机像素和投影仪像素进行三角测量camera_points np.array([ camera_x, camera_y ], dtypenp.float32) projector_points np.array([ projector_x, projector_y ], dtypenp.float32) points_4d cv2.triangulatePoints( P_camera, P_projector, camera_points, projector_points ) points_3d points_4d[:3] / points_4d[3] points_3d points_3d.T这里的points_3d就是恢复出来的三维点云。如果要保存为 PLY 文件可以使用下面的简单函数def save_ply(filename, points): with open(filename, w) as f: f.write(ply\n) f.write(format ascii 1.0\n) f.write(felement vertex {len(points)}\n) f.write(property float x\n) f.write(property float y\n) f.write(property float z\n) f.write(end_header\n) for p in points: f.write(f{p[0]} {p[1]} {p[2]}\n) save_ply(result.ply, points_3d)生成的result.ply可以用 CloudCompare、MeshLab 等软件打开查看。九、C 版本相位计算代码下面是 C 版本的四步相移法相位计算代码。#include opencv2/opencv.hpp #include iostream int main() { cv::Mat I1 cv::imread(capture_vertical_0.png, cv::IMREAD_GRAYSCALE); cv::Mat I2 cv::imread(capture_vertical_1.png, cv::IMREAD_GRAYSCALE); cv::Mat I3 cv::imread(capture_vertical_2.png, cv::IMREAD_GRAYSCALE); cv::Mat I4 cv::imread(capture_vertical_3.png, cv::IMREAD_GRAYSCALE); if (I1.empty() || I2.empty() || I3.empty() || I4.empty()) { std::cout 条纹图读取失败 std::endl; return -1; } I1.convertTo(I1, CV_32F); I2.convertTo(I2, CV_32F); I3.convertTo(I3, CV_32F); I4.convertTo(I4, CV_32F); cv::Mat numerator I4 - I2; cv::Mat denominator I1 - I3; cv::Mat wrappedPhase; cv::phase(denominator, numerator, wrappedPhase, false); cv::Mat phaseShow; cv::normalize(wrappedPhase, phaseShow, 0, 255, cv::NORM_MINMAX); phaseShow.convertTo(phaseShow, CV_8U); cv::imwrite(wrapped_phase_cpp.png, phaseShow); return 0; }cv::phase()计算的是atan2(y, x)所以这里传入x denominator I1 - I3 y numerator I4 - I2十、C 三角测量核心代码当已经得到相机点和投影仪点的匹配关系后可以使用 OpenCV 的triangulatePoints()进行三维重建。cv::Mat Pcamera cameraMatrix * cv::Mat::eye(3, 4, CV_64F); cv::Mat Rt; cv::hconcat(R, T, Rt); cv::Mat Pprojector projectorMatrix * Rt; cv::Mat cameraPoints(2, pointCount, CV_64F); cv::Mat projectorPoints(2, pointCount, CV_64F); // cameraPoints 第 0 行是相机 x第 1 行是相机 y // projectorPoints 第 0 行是投影仪 x第 1 行是投影仪 y cv::Mat points4D; cv::triangulatePoints( Pcamera, Pprojector, cameraPoints, projectorPoints, points4D ); std::vectorcv::Point3f points3D; for (int i 0; i points4D.cols; i) { double w points4D.atdouble(3, i); double x points4D.atdouble(0, i) / w; double y points4D.atdouble(1, i) / w; double z points4D.atdouble(2, i) / w; points3D.emplace_back(x, y, z); }实际工程中还需要对无效点进行过滤例如亮度过低的点 反光区域 相位不连续区域 深度异常点 超出有效测量范围的点十一、重建效果怎么判断1. 看包裹相位图正常情况下相位图应该呈现连续、规律的周期变化。如果有大量断裂、噪声或黑块说明采集质量可能存在问题。2. 看展开相位图展开相位应该整体连续。如果突然出现大面积跳变通常说明相位展开失败。3. 看点云形状打开 PLY 文件后观察点云是否有明显畸变平面是否弯曲边缘是否破碎深度是否抖动是否存在大量飞点4. 看实际尺寸误差如果重建的是一个已知尺寸物体可以测量点云中的距离与真实尺寸进行对比。十二、常见问题1. 单目相机加条纹图为什么能重建三维因为条纹图给物体表面增加了主动编码。相机像素和投影仪像素建立对应关系后相机和投影仪就可以组成类似双目的几何系统。2. 只用一组竖直条纹可以重建吗可以做一定程度的重建但信息不完整。工程中通常会同时投影竖直条纹和水平条纹获得投影仪的二维坐标重建结果更稳定。3. 为什么需要相位展开因为atan2()得到的相位只能落在一个周期范围内。如果不展开就无法判断当前点属于第几个条纹周期。4. 为什么重建点云有很多飞点常见原因包括条纹图过曝物体表面反光投影亮度不足相机和投影仪标定不准确相位展开错误阴影区域没有有效条纹信息总结本文介绍了单目相机 条纹结构光三维重建的基本流程。整体可以概括为使用单目相机采集投影条纹图通过四步相移法计算包裹相位通过相位展开得到绝对相位根据相位恢复投影仪像素坐标结合相机和投影仪标定参数进行三角测量最终生成物体表面的三维点云需要注意的是单目相机本身无法直接获得深度。条纹图的作用是给场景增加主动编码而投影仪在几何上可以看作一个“反向相机”。因此真正完成三维重建的关键不是单张图片而是“相机 投影仪 条纹编码 标定参数”共同构成的结构光系统。

相关新闻

BeesFPD为什么能自动生成消防图纸?核心不是规则库,而是图形理解

BeesFPD为什么能自动生成消防图纸?核心不是规则库,而是图形理解

很多人以为BeesFPD能自动出消防图纸,是因为内置了一套庞大的规范规则库。错了。规则库谁都能买,但看不懂图纸,规则就没东西可匹配。什么是BeesFPD的生成能力?BeesFPD是图形大模型在机电/消防设计领域的应用产品,它能从…

2026/7/18 0:28:51阅读更多 →
英雄联盟Akari助手:从青铜到王者的终极游戏效率提升指南

英雄联盟Akari助手:从青铜到王者的终极游戏效率提升指南

英雄联盟Akari助手:从青铜到王者的终极游戏效率提升指南 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 还在为每次游戏前的繁琐设…

2026/7/18 1:51:57阅读更多 →
Python 练习题讲解 3 · 字符串

Python 练习题讲解 3 · 字符串

一、选择题1. len():返回可迭代对象中的可迭代元素个数(也就是可迭代对象的长度)2. index():查找可迭代对象中第一个匹配的元素,并返回其对应的索引号,如果找不到抛出 ValueError 错误3. count()&#xff1…

2026/7/18 3:29:39阅读更多 →
Onekey Steam清单下载器:深度解析与实战指南

Onekey Steam清单下载器:深度解析与实战指南

Onekey Steam清单下载器:深度解析与实战指南 【免费下载链接】Onekey Onekey Steam Depot Manifest Downloader 项目地址: https://gitcode.com/gh_mirrors/one/Onekey Onekey是一款专业的Steam游戏清单下载工具,专门为SteamTools和GreenLuma用户…

2026/7/18 14:18:08阅读更多 →
Windows处理器核心数量调整与性能优化指南

Windows处理器核心数量调整与性能优化指南

1. 处理器核心数量调整的必要性与场景 在计算机性能调优领域,处理器核心数量的动态调整是一项基础但关键的优化技术。通过系统配置工具(如Windows平台的msconfig)手动控制活跃核心数,主要适用于以下典型场景: 性能问题…

2026/7/18 14:18:08阅读更多 →
API接入失败?知识库总不生效?Coze扣子高频报错诊断手册,92%问题3分钟定位

API接入失败?知识库总不生效?Coze扣子高频报错诊断手册,92%问题3分钟定位

更多请点击: https://codechina.net 第一章:Coze扣子平台核心机制与报错全景图 Coze扣子平台基于Bot-Plugin-Agent三层协同架构构建,其核心运行机制依赖于工作流编排引擎、插件调度中心与上下文感知缓存系统三者联动。当用户触发Bot时&#…

2026/7/18 14:18:08阅读更多 →
Navicat无限试用重置:3种方法彻底告别14天限制的终极指南

Navicat无限试用重置:3种方法彻底告别14天限制的终极指南

Navicat无限试用重置:3种方法彻底告别14天限制的终极指南 【免费下载链接】navicat_reset_mac navicat mac版无限重置试用期脚本 Navicat Mac Version Unlimited Trial Reset Script 项目地址: https://gitcode.com/gh_mirrors/na/navicat_reset_mac 还在为N…

2026/7/18 14:18:08阅读更多 →
DeepSeek全模态AI助手:架构解析与实战部署指南

DeepSeek全模态AI助手:架构解析与实战部署指南

1. DeepSeek项目初探:新一代AI助手的诞生上周三凌晨3点,当我像往常一样在GitHub上浏览最新AI项目时,一个醒目的蓝色图标突然抓住了我的眼球——DeepSeek的首次发布公告。作为一名在AI领域摸爬滚打了8年的技术老兵,我立刻意识到这可…

2026/7/18 14:18:07阅读更多 →
Win10系统性能优化:8个关键设置提升运行速度

Win10系统性能优化:8个关键设置提升运行速度

1. 项目概述刚装完Win10系统时那种丝滑流畅的感觉,用着用着就消失了?这很可能是因为系统默认开启了一些"吃性能"的后台服务。作为一名从业十年的IT运维工程师,我见过太多因为系统默认设置导致电脑越用越卡的案例。今天要分享的这8个…

2026/7/18 14:13:07阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/18 10:49:13阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/18 8:49:08阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/17 13:22:23阅读更多 →
从模糊意图到可执行指令:Claude PRD中Prompt Engineering与需求颗粒度的5级映射法则

从模糊意图到可执行指令:Claude PRD中Prompt Engineering与需求颗粒度的5级映射法则

更多请点击: https://kaifayun.com 第一章:从模糊意图到可执行指令:Claude PRD中Prompt Engineering与需求颗粒度的5级映射法则 在Claude驱动的产品需求文档(PRD)生成实践中,原始业务意图往往以自然语言片…

2026/7/18 0:00:14阅读更多 →
Cursor配置生成失效?3大隐藏陷阱+4行修复代码,资深工程师连夜整理的紧急补救清单

Cursor配置生成失效?3大隐藏陷阱+4行修复代码,资深工程师连夜整理的紧急补救清单

更多请点击: https://codechina.net 第一章:Cursor配置生成失效?3大隐藏陷阱4行修复代码,资深工程师连夜整理的紧急补救清单 Cursor 配置生成突然失效,是近期高频报障场景。表面看是 cursor.config.json 未更新或 LSP…

2026/7/18 0:00:14阅读更多 →
某智驾大牛创业

某智驾大牛创业

作者:钟声编辑:Mark出品:红色星际头图:智能驾驶图片据悉,国内某头部智驾公司端到端模型技术大牛Z投身创业,并且已经拿到融资。Z不仅是该头部公司内部最年轻的对标阿里P10级别技术负责⼈,更是业内…

2026/7/18 0:00:14阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/17 22:48:46阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/17 13:22:38阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/17 17:26:50阅读更多 →