资讯动态

Gemini2深度相机Python SDK图像采集与保存实战

发布时间:2026/10/21 9:24:13 来源:尧图企业网站定制
最近在几个开发者社群里隔三差五就能看到类似的问题Gemini2拿到手系统是Ubuntu20.04第一步只想拍一张彩色图像存到本地目录看看效果结果从插上USB线到代码跑通硬是折腾了整整一个下午。这个需求听起来确实简单但里头的环节一点都不少——SDK怎么安装、设备权限怎么给、取到的帧到底是个什么格式、怎么转成OpenCV能认的BGR矩阵、保存路径该写在哪每一步都可能把人卡住。Gemini2是一台双目结构光3D深度相机USB Type-C接口同一时刻能输出彩色图流和深度图流。对做机器人、视觉测量、三维重建的开发者来说先学会“拍一张图并保存”等于拿到了3D视觉开发的第一把钥匙后面做批量采集、做深度转点云、做彩色深度对齐全是从这条链路往上扩展的。这篇博文我就完完整整走一遍流程从环境准备、SDK调用、像素格式转换到最终在本地目录里拿到可用的PNG/JPG文件全部讲透。适合刚拿到相机的新手也适合想从查看器工具切换到代码控制的进阶开发者。我个人最推荐的方式是用官方SDK的Python接口。原因很直接上手快、报错信息直观、几十行代码就能打通全流程而且后面要做自动化采集或多相机同步改起来非常方便。接下来的内容就按这条主线展开。1. 方案选型与整体思路1.1 先想清楚Gemini2到底能给你哪几张“图”Gemini2本质上是一个多模态图像设备。正常工作时它同时输出两类数据流彩色流分辨率最高可以到1920x108030fps像素格式常见为YUYV、NV12或其他编码这和我们平时理解的普通相机照片维度一致深度流默认通常是640x40030fps每个像素是16位整数数值代表该点到相机平面的距离单位是毫米。深度流是普通RGB相机给不了的信息。所以“拍一张图像”如果只是要彩色图那问题范围比较小但如果你还想要深度图就得多考虑一个环节——16bit数据怎么保存、怎么转成适合人眼看的8bit伪彩图。这些细节我在后面会专门展开。1.2 三条技术路线怎么选路线一官方查看器工具。打开图形界面点一下截图保存按钮就能出图适合刚收到硬件时验证设备是否正常、快速看效果缺点是没法自动化也没法写进自己的程序里。路线二官方SDK的Python接口。代码控制在几十行能完成打开设备、取流、格式转换、保存全流程这也是本文选择。路线三ROS驱动。适合机器人系统集成场景但如果你只是“保存一张图”引入ROS一套东西就太重了光环境依赖都能折腾半天。从这也能看出标题这个需求看起来小背后其实是一次“设备控制链路”的完整走通底层流配置、缓冲读取、像素格式理解、应用层文件写入全都要碰一遍。走通这一遍之后你再去看官方示例里的彩色深度对齐、点云生成会轻松很多。1.3 环境前提先别让硬件坑了你Ubuntu20.04对USB3.0设备的兼容性很好但有几个硬件层面的坑我得先说在前头一定要用USB3.0接口。不要插在机箱前面板的USB2.0口上否则带宽不够图像会卡顿甚至掉帧虽然能识别设备但不稳定。注意供电。部分台式机前面板USB口供电不足设备会时断时连。我习惯插主板后置的USB口现场调试更省心。插上之后可以用lsusb快速确认设备是否被发现。如果什么记录都没有先换线、换口、重启相机而不是急着改代码。2. 环境准备依赖与权限一次弄干净2.1 安装Python依赖这个需求里Python侧的依赖其实只有三个OpenCV、NumPy、官方SDK的Python绑定包。sudo apt update sudo apt install -y python3-pip pip3 install opencv-python numpy pyorbbecsdkOpenCV负责颜色空间转换和最终写文件NumPy负责把SDK返回的字节串变成矩阵pyorbbecsdk则是官方SDK的Python封装。Ubuntu20.04默认自带Python3.8这几个库都有对应的预编译二进制安装过程一般很顺利。如果网络条件不好导致下载慢可以配置国内pip镜像源但我见过的大部分情况直接装就行。2.2 设置udev规则避免权限拒绝刚插上设备就直接跑代码最常见的错误是设备枚举数量为0或者打开设备时报类似“Failed to open device”的权限错误。原因通常是当前用户没有访问/dev下USB设备的权限。解决方法是安装udev规则。如果你手头有SDK的完整压缩包里面通常自带scripts/install_udev_rules.sh执行一下再重插设备cd ~/OrbbecSDK/scripts sudo bash ./install_udev_rules.sh sudo udevadm control --reload如果没有脚本也可以手动创建规则文件核心就是把USB设备节点权限放开。规则写完后记得重新插拔相机让内核重新加载设备节点。这一步做完大部分“设备打开失败”的问题就消失了。2.3 跑一个最小设备枚举脚本验证环境环境弄干净没先别急着写完整采集程序跑一个最小脚本验证from pyorbbecsdk import Context ctx Context() device_count ctx.query_devices().get_count() print(device count:, device_count)如果打印出device count: 1说明SDK安装、udev权限、USB连接全部正常。如果一直打印0先看lsusb里有没有设备再看udev规则是否生效最后检查是不是被其他进程占用了。注意不同版本的SDK在初始化接口上可能有细微差异遇到属性或方法不存在的报错时优先看当前安装版本自带的examples以你手头版本为准。3. 核心实操从取流到保存一张彩色图3.1 初始化Pipeline并配置彩色流Gemini2的SDK把数据流调度抽象得很干净核心对象就三个Context管理设备上下文Pipeline负责数据流转发StreamProfile描述分辨率、帧率、格式的组合。下面这段是初始化并配置彩色流的标准写法from pyorbbecsdk import * import cv2 import numpy as np import os def main(): ctx Context() device_list ctx.query_devices() if device_list.get_count() 0: print(没有发现设备) return device device_list.get_device_by_index(0) pipeline Pipeline(device) config PipelineConfig() color_profile_list pipeline.get_stream_profile_list(SensorType.COLOR) color_profile color_profile_list.get_default_video_stream_profile() config.enable_stream(color_profile) pipeline.start(config) print(彩色流已启动)关键一步是get_stream_profile_list(SensorType.COLOR)它会把相机支持的所有彩色流配置分辨率、帧率、格式的组合列出来get_default_video_stream_profile()返回一个默认组合。对“拍一张图”这个需求默认配置完全够用。如果你以后想要更高分辨率可以用按参数筛选的方法取特定Profile但高分辨率会带来更大的带宽压力和更强的光照要求新手不建议一开始就上最大规格。3.2 等待帧并转换为BGRPipeline启动之后数据就在内部缓冲里持续流转。程序里用wait_for_frames()阻塞等待一帧数据准备好。这里有一个很重要的经验相机刚启动时自动曝光和自动白平衡还没有稳定前几帧往往是暗的、偏色的不能直接拿来保存。所以取帧循环里一般会跳过前面若干帧再取稳定帧。彩色帧的像素格式默认不一定是BGR常见的有三种情况YUYVYUV422格式OpenCV需要用cv2.cvtColor(data, cv2.COLOR_YUV2BGR_YUYV)转换NV12YUV420sp格式亮度全分辨率、色度隔行存放转换前要先reshape成(height * 3 // 2, width)MJPG压缩编码直接用cv2.imdecode解压成BGR矩阵。def color_frame_to_bgr(frame): w frame.get_width() h frame.get_height() fmt frame.get_format() data np.asanyarray(frame.get_data(), dtypenp.uint8) if fmt OBFormat.YUYV: yuyv data.reshape(h, w * 2) return cv2.cvtColor(yuyv, cv2.COLOR_YUV2BGR_YUYV) elif fmt OBFormat.NV12: nv12 data.reshape(h * 3 // 2, w) return cv2.cvtColor(nv12, cv2.COLOR_YUV2BGR_NV12) elif fmt OBFormat.MJPG: return cv2.imdecode(data, cv2.IMREAD_COLOR) return None这个转换函数是我实际项目里一直在用的模板。新手最容易在这里翻车直接把frame.get_data()当成RGB矩阵塞给OpenCV结果要么花屏要么颜色诡异折腾半天还以为是相机坏了其实只是格式没转对。3.3 保存到本地目录保存这一步走OpenCV的imwrite就行但要注意目录创建和路径处理save_dir os.path.expanduser(~/workspace/gemini2_shots) os.makedirs(save_dir, exist_okTrue) save_path os.path.join(save_dir, shot_0001.png) saved False for i in range(30): frames pipeline.wait_for_frames(100) if frames is None: continue color_frame frames.get_color_frame() if color_frame is None: continue if i 10: continue bgr color_frame_to_bgr(color_frame) if bgr is not None: cv2.imwrite(save_path, bgr) print(图像已保存:, save_path) saved True break pipeline.stop() if not saved: print(未能获取有效帧)os.path.expanduser(~)会把家目录展开避免硬编码用户名这样代码换机器也能跑。os.makedirs(..., exist_okTrue)这行不能省因为cv2.imwrite在目录不存在时不会自动创建。完整代码我一般会放在一个capture_one.py文件里运行成功后终端会输出保存好的绝对路径直接用图片查看器打开就能验证。4. 顺带把深度图也拿下16bit数据的保存与可视化4.1 同时开启深度流彩色和深度并不强制同时开启但在Gemini2上我推荐两个都开。原因有两个一是后面做彩色深度对齐时同时开启的流往往带有帧同步关系数据更好处理二是配置方法几乎一模一样多几行代码而已。depth_profile_list pipeline.get_stream_profile_list(SensorType.DEPTH) depth_profile depth_profile_list.get_default_video_stream_profile() config.enable_stream(depth_profile)一个PipelineConfig可以同时启用多个流不需要像某些老方案那样开两个Pipeline实例这是SDK设计得比较方便的地方。4.2 深度帧转成可保存的图像深度帧的get_data()拿到的是一个16位无符号整数数组单位是毫米。这个数据用cv2.imwrite直接存PNG是可行的因为PNG格式本身支持16bit单通道。但问题是普通图片查看器打开这种16bit PNG你会看到黑乎乎一片因为深度数值范围很大而显示默认只映射了低动态范围。正确的做法是分成两条路一条路保存原始16bit数据用于算法处理另一路做归一化转成8bit伪彩图用于人眼查看。depth_data np.asanyarray(depth_frame.get_data(), dtypenp.uint16) depth_img depth_data.reshape(depth_frame.get_height(), depth_frame.get_width()) # 原始深度保留毫米精度 cv2.imwrite(os.path.join(save_dir, depth_raw.png), depth_img) # 可视化截断到 0~3000mm然后映射到8bit vis np.clip(depth_img, 0, 3000).astype(np.float32) / 3000.0 * 255.0 vis vis.astype(np.uint8) vis_color cv2.applyColorMap(vis, cv2.COLORMAP_JET) cv2.imwrite(os.path.join(save_dir, depth_vis.png), vis_color)实测下来这组代码能同时得到一张16bit的真实深度图和一张适合人眼看的伪彩图。这个区分对后续调试特别重要——深度图不是照片它是数据只有理解这一点你在处理点云、做距离测量时才不会犯方向性错误。4.3 常见误解深度图和彩色图的像素不是一一对应很多新手拿到两张图第一反应是直接叠加在一起看结果发现边缘对不上误以为设备有问题。其实这不是故障而是深度图和彩色图的视场角、分辨率、光学中心都不一样。深度图要叠加到彩色图上需要经过相机内参和外参做对齐计算。SDK里提供坐标系变换工具保存单张图这个需求用不到但如果你要“深度图与彩色图严格对齐”一定要去调用对齐接口不要自己用像素坐标硬算否则边缘错位会一直存在。5. 常见问题排查与避坑实录5.1 设备打不开 / 枚举数量为0按照顺序排查先用lsusb确认设备有没有被系统识别再检查udev规则是否生效重插设备后是否生成新的设备节点最后看是不是有别的进程占用了相机。特别常见的一个场景是用户开着一个官方查看器窗口再跑自己的脚本设备被查看器独占脚本自然打不开。关掉查看器再跑脚本一般就好了。5.2 图像发黑、过曝、偏色图像发黑通常有两个原因一是取帧循环跳过的帧数不够自动曝光还没收敛我把跳过数从5调到20之后问题就消失了二是环境光太暗需要补光。图像过曝则相反可以减小曝光量或者等更多帧让自动曝光回调。颜色偏黄或偏蓝是自动白平衡还没稳定的表现同样的策略多等几帧。花屏或颜色完全错乱基本可以肯定是格式转换问题先打印frame.get_format()看看到底是什么格式再对应选cvtColor的转换参数。现象常见原因处理思路枚举设备数为0USB线/口问题、udev未生效换USB3.0口重装udev规则打开设备失败被其他进程占用关闭查看器或重启相机图像全黑自动曝光未稳定、环境光不足跳过更多帧增加补光图像过曝曝光参数过大等待收敛或手动调低曝光花屏、颜色错乱YUV转BGR参数不匹配打印format按格式转移5.3 保存失败 / 图片大小异常如果cv2.imwrite返回False但没有报错最可能是数据矩阵的维度、通道数或数据类型不符合预期。保存前先打印bgr.shape和bgr.dtype确认是(height, width, 3)的uint8数组。另外路径问题也值得注意目录不存在要用os.makedirs创建文件名建议一律用英文和数字不要带中文或特殊符号部分图像库对Unicode路径支持不稳定实测容易出奇怪问题。5.4 帧率上不去 / 卡顿如果只是保存单张图卡顿问题不明显。但做连续采集时帧率上不去通常会指向三个原因USB带宽不足建议直接插主板后置USB3.0口不要经过HUB分辨率或帧率配置过高深度和彩色同时跑最大规格会很快占满带宽需要按场景取舍每帧处理耗时过长比如在取帧循环里做复杂的图像处理处理时间大于帧间隔帧率自然掉下去。我在连续采集项目里常用的配置是彩色开720p深度保持默认640x400实测输出能稳定在30fps附近。这个组合对大多数视觉任务都够用又不会给USB带宽和CPU带来太大压力。5.5 多帧取平均的小技巧对质量要求更高的静态场景我一般不会只取一帧而是连续取5到10帧稳定帧做平均降噪。具体做法是把多帧BGR矩阵累加后除以帧数深度图同理。这个操作能明显降低传感器噪声对后续测量精度有提升。代价是会多花几十毫秒但如果你的场景是静态物体这个性价比非常高。把上面这些跑通之后你会发现后来很多工作都建在这条链路上批量采集、多帧平均、深度转点云、彩色深度对齐、不同型号相机的替换等等。你完全可以把它当成一个“相机启动模板”换用同系列其他型号时大部分代码都不用动只需要根据设备能力调整Profile配置就行。最后分享一个我自己踩过好几次的小技巧正式保存第一张图之前先打印一帧图像的shape和format确认数据格式再写保存逻辑。看起来只是多了两行调试代码实际上它能帮你区分“相机问题”和“代码问题”——是先确认拿到了一帧正常数据再去做格式转换和文件写入而不是等存出来的图全黑才开始怀疑相机坏了。这个习惯帮我省了无数次“保存出来全黑但不知道是哪一步出错”的折腾。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑