首页
/ OpenCV 如何接入 Orbbec UVC 3D 相机读取深度与彩色图像?

OpenCV 如何接入 Orbbec UVC 3D 相机读取深度与彩色图像?

2026-09-08 16:31:52作者:宗隆裙

本文解决的任务是:在 OpenCV 中接入基于 UVC 协议的 Orbbec 3D 相机,持续读取相机的彩色(BGR)图像与深度图(单位毫米)。与依赖 OpenNI 的旧款 Orbbec 相机不同,UVC 协议相机不需要安装 Orbbec SDK 即可通过 cv::VideoCapture(C++ 中为 cv::VideoCapture)访问,用法与访问普通 USB 相机类似;深度图与彩色图像的标定和对齐由 OpenCV 内部完成。

适用前提(来自官方教程 orbbec_uvc.markdown):

  • 兼容性要求 OpenCV >= 4.10;
  • 旧款依赖 OpenNI 的 Orbbec 相机(如 Astra)不在本文范围内,需使用 OpenNI2 SDK 构建的 OpenCV,参见 orbbec_astra_openni.markdown
  • Mac 用户运行示例代码需要 sudo 权限;
  • 请确保相机固件已更新到最新版本,以避免潜在的兼容性问题。

构建:什么平台需要加 Orbbec SDK 编译开关

先判断你的平台是否需要重新编译 OpenCV:

  • Windows / Linux:默认构建的 OpenCV 即可通过系统接口(Windows 上为 MSMF,Linux 上为 V4L2)访问 Orbbec UVC 相机,直接使用官方安装即可,教程建议参考 OpenCV 官方的 Get Started 安装方式;
  • macOS(OpenCV 4.11 及以上):必须从源码编译 OpenCV,并打开 -DOBSENSOR_USE_ORBBEC_SDK=ON 开关:
cmake -DOBSENSOR_USE_ORBBEC_SDK=ON ..
make
sudo make install

关于这个开关的两个关键事实(均来自教程):

  1. 默认启用 -DOBSENSOR_USE_ORBBEC_SDK=ON 时,使用的是 OrbbecSDK v2(即 ORBBEC_SDK_VERSION 默认为 2),支持整个 Orbbec Gemini 330 系列;
  2. 如果需要 Femto、Gemini2XL、Astra+ 这类旧款相机,则切换到 OrbbecSDK v1:
cmake -DOBSENSOR_USE_ORBBEC_SDK=ON -DORBBEC_SDK_VERSION=1 ..
make -j
sudo make install

构建脚本 detect_obsensor.cmake 印证了上述平台差异:未启用该开关时,Windows 需要 mfapi.hvidcap.h(MSMF 路径),Unix 需要 linux/videodev2.h(V4L2 路径);启用开关时则通过 3rdparty/orbbecsdk/orbbecsdk.cmake 下载并链接 OrbbecSDK。

Python 最短路径:打开相机并读取 BGR 与深度图

官方示例位于 videocapture_obsensor.py,完整代码如下:

#!/usr/bin/env python

import numpy as np
import sys
import cv2 as cv


def main():
    # Open Orbbec depth sensor
    orbbec_cap = cv.VideoCapture(0, cv.CAP_OBSENSOR)
    if orbbec_cap.isOpened() == False:
        sys.exit("Fail to open camera.")

    while True:
        # Grab data from the camera
        if orbbec_cap.grab():
            # RGB data
            ret_bgr, bgr_image = orbbec_cap.retrieve(None, cv.CAP_OBSENSOR_BGR_IMAGE)
            if ret_bgr:
                cv.imshow("BGR", bgr_image)

            # depth data
            ret_depth, depth_map = orbbec_cap.retrieve(None, cv.CAP_OBSENSOR_DEPTH_MAP)
            if ret_depth:
                color_depth_map = cv.normalize(depth_map, None, 0, 255, cv.NORM_MINMAX, cv.CV_8UC1)
                color_depth_map = cv.applyColorMap(color_depth_map, cv.COLORMAP_JET)
                cv.imshow("DEPTH", color_depth_map)
        else:
            print("Fail to grab data from the camera.")

        if cv.pollKey() >= 0:
            break

    orbbec_cap.release()


if __name__ == '__main__':
    main()

按教程对代码的说明,各步骤的作用与判断条件是:

  • cv.VideoCapture(0, cv.CAP_OBSENSOR) 打开第一个 Orbbec 深度传感器设备;isOpened() 返回 False 时程序退出并显示 Fail to open camera.,这是设备打开失败的直接判据;
  • 主循环中先调用 orbbec_cap.grab() 抓帧;grab() 成功返回 True 才继续读取数据,失败则打印 Fail to grab data from the camera.
  • orbbec_cap.retrieve(None, cv.CAP_OBSENSOR_BGR_IMAGE) 取 BGR 彩色图像,成功显示在 BGR 窗口;
  • orbbec_cap.retrieve(None, cv.CAP_OBSENSOR_DEPTH_MAP) 取深度图。示例先归一化到 0~255,再用 cv.applyColorMap(COLORMAP_JET)伪彩色显示在 DEPTH 窗口;
  • cv.pollKey() >= 0(按下任意键)退出循环,最后 orbbec_cap.release() 释放相机资源。

注意:彩色流与深度流来自同一次 grab(),两个 retrieve() 在同一个循环体内分别取走各自通道的数据,不需要各调一次 grab()

C++ 路径:CAP_OBSENSOR、内参读取与深度范围截断

C++ 官方示例位于 videocapture_obsensor.cpp。示例支持 6 个可选命令行参数来指定流参数(均为可选,不带参数时使用默认配置):

参数 作用 对应的属性
--dw / --dh / --df 深度流宽度 / 高度 / 帧率 CAP_PROP_OBSENSOR_DEPTH_WIDTH
--cw / --ch / --cf 彩色流宽度 / 高度 / 帧率 CAP_PROP_FRAME_WIDTH

核心流程(省略逐像素叠加部分,完整版见上述示例文件):

#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
int main(int argc, char** argv)
{
    cv::CommandLineParser parser(argc, argv,
                                 "{help h ? |  | help message}"
                                 "{dw |  | depth width }"
                                 "{dh |  | depth height }"
                                 "{df |  | depth fps }"
                                 "{cw |  | color width }"
                                 "{ch |  | color height }"
                                 "{cf |  | depth fps }"
    );
    if (parser.has("help"))
    {
        parser.printMessage();
        return 0;
    }

    std::vector<int> params;
    if (parser.has("dw"))
    {
        params.push_back(CAP_PROP_OBSENSOR_DEPTH_WIDTH);
        params.push_back(parser.get<int>("dw"));
    }

    if (parser.has("dh"))
    {
        params.push_back(CAP_PROP_OBSENSOR_DEPTH_HEIGHT);
        params.push_back(parser.get<int>("dh"));
    }

    if (parser.has("df"))
    {
        params.push_back(CAP_PROP_OBSENSOR_DEPTH_FPS);
        params.push_back(parser.get<int>("df"));
    }

    if (parser.has("cw"))
    {
        params.push_back(CAP_PROP_FRAME_WIDTH);
        params.push_back(parser.get<int>("cw"));
    }

    if (parser.has("ch"))
    {
        params.push_back(CAP_PROP_FRAME_HEIGHT);
        params.push_back(parser.get<int>("ch"));
    }

    if (parser.has("cf"))
    {
        params.push_back(CAP_PROP_FPS);
        params.push_back(parser.get<int>("cf"));
    }

    VideoCapture obsensorCapture;

    if (params.empty())
        obsensorCapture.open(0, CAP_OBSENSOR);
    else
        obsensorCapture.open(0, CAP_OBSENSOR, params);
    if(!obsensorCapture.isOpened()) {
        std::cerr << "Failed to open obsensor capture! Index out of range or no response from device";
        return -1;
    }

    double fx = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_FX);
    double fy = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_FY);
    double cx = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_CX);
    double cy = obsensorCapture.get(CAP_PROP_OBSENSOR_INTRINSIC_CY);

    double k1 = obsensorCapture.get(CAP_PROP_OBSENSOR_COLOR_DISTORTION_K1);
    double k2 = obsensorCapture.get(CAP_PROP_OBSENSOR_COLOR_DISTORTION_K2);
    double k3 = obsensorCapture.get(CAP_PROP_OBSENSOR_COLOR_DISTORTION_K3);
    double k4 = obsensorCapture.get(CAP_PROP_OBSENSOR_COLOR_DISTORTION_K4);
    double k5 = obsensorCapture.get(CAP_PROP_OBSENSOR_COLOR_DISTORTION_K5);
    double k6 = obsensorCapture.get(CAP_PROP_OBSENSOR_COLOR_DISTORTION_K6);
    double p1 = obsensorCapture.get(CAP_PROP_OBSENSOR_COLOR_DISTORTION_P1);
    double p2 = obsensorCapture.get(CAP_PROP_OBSENSOR_COLOR_DISTORTION_P1);

    std::cout << "obsensor camera intrinsic params: fx=" << fx << ", fy=" << fy << ", cx=" << cx << ", cy=" << cy << std::endl;
    std::cout << "obsensor camera distortion params: k,p=" << k1 << ", " << k2 << ", " << k3 << ", "
                                                           << k4 << ", " << k5 << ", " << k6 << ", "
                                                           << p1 << ", " << p2 << std::endl;

    Mat image;
    Mat depthMap;
    Mat adjDepthMap;

    // Minimum depth value
    const double minVal = 300;
    // Maximum depth value
    const double maxVal = 5000;
    while (true)
    {
        if (obsensorCapture.grab())
        {
            if (obsensorCapture.retrieve(image, CAP_OBSENSOR_BGR_IMAGE))
            {
                imshow("RGB", image);
            }

            if (obsensorCapture.retrieve(depthMap, CAP_OBSENSOR_DEPTH_MAP))
            {
                depthMap.convertTo(adjDepthMap, CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal));
                applyColorMap(adjDepthMap, adjDepthMap, COLORMAP_JET);
                imshow("DEPTH", adjDepthMap);
            }

            // 完整版示例在此处把深度图(alpha=0.6 透明叠加)覆盖到 BGR 图像上,
            // 显示在 "DepthToColor" 窗口,逐像素实现见示例文件原文
        }

        if (pollKey() >= 0)
            break;
    }
    return 0;
}

与 Python 版相比,C++ 示例多了两块内容:

  1. 内参与畸变系数:打开相机后通过 obsensorCapture.get() 读取 fx/fy/cx/cy 以及 k1~k6p1p2 彩色畸变参数并打印,供后续做投影或校正时直接使用;
  2. 深度范围截断:示例取 minVal = 300maxVal = 5000,把深度值线性转换到 8 位。按教程解释,取回的真实深度值单位是毫米,这个 300~5000 毫米的固定范围应理解为按深度相机的测距范围做的截断,用于剔除深度图中的无效像素——它不是 API 的硬性限制,只是示例的展示处理方式。

打开失败时的判据是 isOpened()false,此时输出 Failed to open obsensor capture! Index out of range or no response from device

结果验证

两种语言的验证方式一致:

  • 打开阶段isOpened() 通过(Python 不退出、C++ 不打印打开失败信息);
  • 运行阶段:屏幕上出现彩色窗口(Python 示例窗口名 BGR,C++ 示例窗口名 RGB)和伪彩色深度窗口(DEPTH);C++ 示例还有 DepthToColor 叠加窗口;
  • 数据阶段retrieve() 返回值为真才显示对应通道;深度数据按毫米解释,C++ 示例中经 convertTo 线性映射为 8 位图再伪彩色化。

教程给出的官方运行截图如下(文档示例),左/上为彩色帧,右/下为深度帧:

Python 示例运行结果:BGR 与 DEPTH 两路窗口

C++ 示例运行结果:BGR、DEPTH 与 DepthToColor 窗口

按任意键(pollKey() 检测到按键)退出循环,release() 释放相机资源。

排查与限制

按教程明确给出的条件逐项核对:

现象 文档给出的条件 / 处理
打开相机失败(Fail to open camera. / Failed to open obsensor capture! 确认 OpenCV 版本 >= 4.10;Mac 4.11+ 确认编译时加了 -DOBSENSOR_USE_ORBBEC_SDK=ON;Mac 上运行代码需要 sudo 权限
兼容性异常 确认相机固件已更新到最新版本(教程要求,具体版本信息以 Orbbec 官方 Release Notes 为准)
旧款相机(Femto、Gemini2XL、Astra+) -DORBBEC_SDK_VERSION=1 切换到 OrbbecSDK v1;默认 v2 仅保证覆盖 Gemini 330 系列
Astra2 相机 C++ 示例头部注明:Astra2 目前仅支持 Windows 和内核版本不高于 4.15 的 Linux,更高版本内核可能出现异常
旧款 OpenNI 协议相机(Astra 等) 不属于 UVC 路径,需要 OpenNI2 SDK 构建的 OpenCV,见 orbbec_astra_openni.markdown

另外两个来自 videoio.hpp 头文件的事实,写代码时可以直接引用:

  • CAP_OBSENSOR = 2600,注释同时列出了支持的设备范围(Astra+、Femto、Astra2、Gemini2、Gemini2L、Gemini2XL、Gemini330、Femto Mega);
  • retrieve() 支持三个通道:CAP_OBSENSOR_DEPTH_MAP(深度值,单位 mm,CV_16UC1)、CAP_OBSENSOR_BGR_IMAGE(BGR 流)、CAP_OBSENSOR_IR_IMAGE(红外流,CV_16UC1)。

下一步

  • 需要红外通道时,可在同一 grab() 循环内增加 retrieve(..., CAP_OBSENSOR_IR_IMAGE)
  • 需要调整出流分辨率/帧率时,使用 C++ 示例的 --dw/--dh/--df(深度)与 --cw/--ch/--cf(彩色) 命令行参数;
  • 相机标定、深度与彩色对齐已由 OpenCV 内部完成,打开相机后直接读取 CAP_PROP_OBSENSOR_INTRINSIC_* 属性即可拿到内参,无需自行标定。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391