首页
/ OpenCV 深度相机实战:用 Orbbec Astra 通过 OpenNI2 API 同步采集深度与彩色双路视频

OpenCV 深度相机实战:用 Orbbec Astra 通过 OpenNI2 API 同步采集深度与彩色双路视频

2026-09-06 15:33:06作者:段琳惟

本文围绕 OpenCV 官方教程 orbbec_astra_openni.markdown 展开,完整讲解 Orbbec Astra 系列 3D 相机在 OpenCV 中的接入流程:从 Orbbec OpenNI SDK 的环境安装、-DWITH_OPENNI2=ON 的 CMake 配置,到用 cv::VideoCapture 双线程读取深度流与彩色流,以及基于时间戳的后同步配对算法。读完本篇,你将能够独立完成 Astra 相机的深度传感器开发,理解 OpenNI2 后端在 OpenCV videoio 模块中的调用关系与关键参数语义。

Astra 相机彩色帧样例:同一场景中彩色图像难以区分真实树叶与墙上的图案

Astra 相机深度帧样例:深度数据使真实树叶与墙面图案清晰可分

一、Astra 相机与 OpenNI2 的接入架构

Orbbec Astra 系列相机在普通彩色传感器之外,还集成了深度传感器。其数据接入 OpenCV 的路径是双通道的:

  • 深度流:通过开源 OpenNI API(OpenNI2)读取,OpenCV 中由 cv::VideoCapture 配合 CAP_OPENNI2_ASTRA 接口标识打开。在头文件中该接口定义为 videoio.hpp 中的枚举值 CAP_OPENNI2_ASTRA = 1620(注释即 OpenNI2 (for Orbbec Astra))。
  • 彩色流走 OpenNI 接口,只通过普通相机接口(Linux 上为 V4L2)提供。这一点在 OpenNI2 后端源码 cap_openni2.cpp 中也有明确注释:Orbbec Astra cameras don't provide OpenNI interface for color stream reading

因此,要同时获得深度与彩色帧,必须创建两个 cv::VideoCapture 对象:一个走 OpenNI2 读深度,一个走 V4L2 读彩色。官方示例代码位于 openni_orbbec_astra.cpp,完整实现了这一模式。

二、安装 Orbbec OpenNI SDK

要使用 Astra 的深度传感器,需要先安装 Orbbec OpenNI SDK(从 Orbbec 官方 OpenNI SDK 下载页获取),解压后按操作系统选择对应构建,并遵循压缩包内 Readme 的安装步骤。

2.1 64 位 Linux 上的标准安装(以 2.3.0.63 为例)

$ cd Linux/OpenNI-Linux-x64-2.3.0.63/
$ sudo ./install.sh

安装完成后:

  1. 重新插拔设备,使 udev 规则生效,相机将作为普通相机设备出现;
  2. 当前用户必须属于 video 组才能访问相机;
  3. 加载环境变量文件:
$ source OpenNIDevEnvironment

验证环境变量已正确加载(若输出为空,需重新 source):

$ echo $OPENNI2_INCLUDE
/home/user/OpenNI_2.3.0.63/Linux/OpenNI-Linux-x64-2.3.0.63/Include
$ echo $OPENNI2_REDIST
/home/user/OpenNI_2.3.0.63/Linux/OpenNI-Linux-x64-2.3.0.63/Redist

OPENNI2_INCLUDEOPENNI2_REDIST 两个变量分别是 OpenCV 构建时查找 OpenNI 头文件与运行时库的关键路径。

2.2 新版 SDK(2.3.0.86 及以上)的环境初始化脚本

注意:Orbbec OpenNI SDK 2.3.0.86 及更新版本不再提供 install.sh。可改用以下脚本完成 udev 规则安装与环境文件生成(教程原文提供):

# Check if user is root/running with sudo
if [ `whoami` != root ]; then
    echo Please run this script with sudo
    exit
fi

ORIG_PATH=`pwd`
cd `dirname $0`
SCRIPT_PATH=`pwd`
cd $ORIG_PATH

if [ "`uname -s`" != "Darwin" ]; then
    # Install UDEV rules for USB device
    cp ${SCRIPT_PATH}/orbbec-usb.rules /etc/udev/rules.d/558-orbbec-usb.rules
    echo "usb rules file install at /etc/udev/rules.d/558-orbbec-usb.rules"
fi

OUT_FILE="$SCRIPT_PATH/OpenNIDevEnvironment"
echo "export OPENNI2_INCLUDE=$SCRIPT_PATH/../sdk/Include" > $OUT_FILE
echo "export OPENNI2_REDIST=$SCRIPT_PATH/../sdk/libs" >> $OUT_FILE
chmod a+r $OUT_FILE
echo "exit"

脚本逻辑分两步:在非 macOS 系统上把 orbbec-usb.rules 复制到 /etc/udev/rules.d/558-orbbec-usb.rules(USB 设备权限规则),然后生成 OpenNIDevEnvironment 文件,其中 OPENNI2_INCLUDE 指向 ../sdk/IncludeOPENNI2_REDIST 指向 ../sdk/libs,供后续 source 使用。

2.3 版本选择建议

教程明确提示:最后一次实测可用的 SDK 版本是 2.3.0.63(在 Ubuntu 18.04 amd64 上验证)。较新的 2.3.0.86_202210111154_4c8f5aa4_beta6 在现代 Linux 上工作不正常——即使按照 Orbbec 的说明重建 libusb 也无法解决。2.3.0.63 不再由官方下载页直接提供,需要从 Orbbec 社区论坛的技术支持帖中获取。

三、配置并构建带 OpenNI2 支持的 OpenCV

SDK 就绪后,用 CMake 的 WITH_OPENNI2 开关启用 OpenNI 支持。在 OpenCV 源码根目录执行:

$ mkdir build
$ cd build
$ cmake -DWITH_OPENNI2=ON ..
$ make
  • WITH_OPENNI2:控制"Include OpenNI2 support",默认 OFF。该选项定义在根目录 CMakeLists.txtOCV_OPTION(WITH_OPENNI2 "Include OpenNI2 support" OFF ...)),且在第 1634 行参与平台依赖判定(if(WITH_OPENNI2 OR HAVE_OPENNI2))。若系统已安装 OpenNI2 开发库,OpenCV 会自动以 OpenNI2 支持构建。
  • BUILD_EXAMPLES:建议同时打开,以便编译出可与 Astra 相机配合运行的官方示例 openni_orbbec_astra

配置成功时,CMake 日志的 Video I/O 段落会出现 OpenNI2 一行(教程给出的实际输出片段):

--   Video I/O:
--     DC1394:                      YES (2.2.6)
--     FFMPEG:                      YES
--       avcodec:                   YES (58.91.100)
--       avformat:                   YES (58.45.100)
--       avutil:                     YES (56.51.100)
--       swscale:                   YES (5.7.100)
--       avresample:                 NO
--     GStreamer:                   YES (1.18.1)
--     OpenNI2:                     YES (2.3.0)
--     v4l/v4l2:                    YES (linux/videodev2.h)

看到 OpenNI2: YES (2.3.0) 即代表后端可用,随后 make 完成编译安装。

四、示例代码详解:双路打开、参数设置与属性读取

官方示例 openni_orbbec_astra.cppHAVE_THREADS 宏未定义时会直接退出(提示未启用线程支持),因此示例依赖编译时的线程支持。

4.1 打开两路流(Open streams)

// Open depth stream
VideoCapture depthStream(CAP_OPENNI2_ASTRA);
// Open color stream
VideoCapture colorStream(0, CAP_V4L2);

(对应 openni_orbbec_astra.cpp

  • depthStream 通过 OpenNI2 API 获取深度数据;
  • colorStream 使用 V4L2 接口访问彩色传感器,0 表示系统第一路相机。示例假设 Astra 是第一台相机——如果接了多路相机,需要显式指定正确的相机编号。

打开后应立即检查 isOpened(),失败则报 Unable to open ... stream 并退出(第 46-57 行)。

4.2 设置流参数(Setup streams)

// Set color and depth stream parameters
colorStream.set(CAP_PROP_FRAME_WIDTH,  640);
colorStream.set(CAP_PROP_FRAME_HEIGHT, 480);
depthStream.set(CAP_PROP_FRAME_WIDTH,  640);
depthStream.set(CAP_PROP_FRAME_HEIGHT, 480);
depthStream.set(CAP_PROP_OPENNI2_MIRROR, 0);

第 59-66 行

两路流统一配置为 VGA(640×480)分辨率——这是 Astra 两个传感器都支持的最大公共分辨率;保持两路参数一致,能简化彩色到深度的数据配准(registration)。同时用 CAP_PROP_OPENNI2_MIRROR 设为 0 关闭深度流镜像。

4.3 读取流属性(Get properties)

// Print depth stream parameters
cout << "Depth stream: "
     << depthStream.get(CAP_PROP_FRAME_WIDTH) << "x" << depthStream.get(CAP_PROP_FRAME_HEIGHT)
     << " @" << depthStream.get(CAP_PROP_FPS) << " fps" << endl;

第 73-78 行)。属性通过 cv::VideoCapture::set / cv::VideoCapture::get 设置与读取。

深度生成器(OpenNI 接口)支持的属性(教程原文列出的完整清单):

可设置属性:

属性 含义
cv::CAP_PROP_FRAME_WIDTH 帧宽(像素)
cv::CAP_PROP_FRAME_HEIGHT 帧高(像素)
cv::CAP_PROP_FPS 帧率(FPS)
cv::CAP_PROP_OPENNI_REGISTRATION 深度图重映射开关:置"on"时改变深度生成器的视点,把深度图重映射到彩色图像平面;置"off"时恢复常规视点。开启后得到的图像是像素对齐的(pixel-aligned),即图像中每个像素都与深度图像中的一个像素对齐
cv::CAP_PROP_OPENNI2_MIRROR 是否镜像该流;设为 0 关闭镜像

仅可读属性:

属性 含义
cv::CAP_PROP_OPENNI_FRAME_MAX_DEPTH 相机支持的最大深度(单位 mm)
cv::CAP_PROP_OPENNI_BASELINE 双目基线值(单位 mm)

4.4 VideoCapture 可获取的数据类型

  • 深度生成器提供的数据:
    • cv::CAP_OPENNI_DEPTH_MAP:深度值,单位 mm,CV_16UC1
    • cv::CAP_OPENNI_POINT_CLOUD_MAP:XYZ 点云,单位米,CV_32FC3
    • cv::CAP_OPENNI_DISPARITY_MAP:视差,单位像素,CV_8UC1
    • cv::CAP_OPENNI_DISPARITY_MAP_32F:视差,单位像素,CV_32FC1
    • cv::CAP_OPENNI_VALID_DEPTH_MASK:有效像素掩膜(排除被遮挡、阴影等无效像素),CV_8UC1
  • 彩色传感器提供常规 BGR 图像(CV_8UC3)。

五、双线程读流与后同步配对

重要注意(教程原文强调):OpenCV 的 VideoCapture同步 API,因此必须在新线程中抓帧,否则读取一路流时会阻塞另一路。同时 VideoCapture 不是线程安全类,使用时要避免死锁与数据竞争。

5.1 读流线程(Read streams)

示例为每路流创建独立读取线程,帧连同时间戳存入有序 std::list<Frame>(上限 maxFrames = 64,超出则丢弃最旧帧),并通过条件变量通知主线程(第 80-147 行):

struct Frame
{
    int64 timestamp;
    Mat frame;
};

// ...
std::thread depthReader([&]
{
    while (!isFinish)
    {
        // Grab and decode new frame
        if (depthStream.grab())
        {
            Frame f;
            f.timestamp = cv::getTickCount();
            depthStream.retrieve(f.frame, CAP_OPENNI_DEPTH_MAP);
            if (f.frame.empty())
            {
                cerr << "ERROR: Failed to decode frame from depth stream" << endl;
                break;
            }
            {
                std::lock_guard<std::mutex> lk(mtx);
                if (depthFrames.size() >= maxFrames)
                    depthFrames.pop_front();
                depthFrames.push_back(f);
            }
            dataReady.notify_one();
        }
    }
});

彩色线程结构相同,只是调用 colorStream.retrieve(f.frame) 获取默认 BGR 帧。同步原语包括 std::mutex mtxstd::condition_variable dataReadystd::atomic<bool> isFinish

5.2 时间戳配对算法(Pair frames)

由于深度与彩色来自两个独立硬件源,即使两路设置相同帧率,流之间仍会失步。示例采用后同步策略(第 149-206 行):

  1. 主线程阻塞等待,直到深度、彩色两个列表都非空;
  2. 取出各自队首帧的时间戳 depthTcolorT
  3. 计算最大允许时差:半个帧周期 maxTdiff = 1000000000 / (2 * fps)getTickCount() 以纳秒计,fps 从 CAP_PROP_FPS 读取);
  4. depthT + maxTdiff < colorT,说明深度帧过旧——丢弃深度帧,继续;若 colorT + maxTdiff < depthT,则丢弃彩色帧;
  5. 时间戳足够接近时,两帧配对弹出,供后续处理。
// Half of frame period is a maximum time diff between frames
const int64 maxTdiff = int64(1000000000 / (2 * colorStream.get(CAP_PROP_FPS)));
if (depthT + maxTdiff < colorT)
{
    depthFrames.pop_front();
    continue;
}
else if (colorT + maxTdiff < depthT)
{
    colorFrames.pop_front();
    continue;
}

5.3 显示与退出(Show frames)

配对后,深度图(mm,CV_16UC1)先按比例转 8 位灰度(convertTo(d8, CV_8U, 255.0 / 2500),即 2500 mm 映射到 255),再用 applyColorMapCOLORMAP_OCEAN)伪彩色显示;彩色帧直接 imshow。按下 Esc(waitKey(1) 返回 27)置 isFinish = true 退出循环,最后 notify_one 唤醒并 join 两个读流线程(第 186-210 行)。示例中两帧仅做显示,此处可替换为任意业务处理逻辑。

六、效果:深度数据揭示彩色图像无法区分的结构

对同一场景,彩色帧中很难区分真实的植物叶片和墙面上绘制的树叶图案;而深度帧利用深度差异使二者清晰可分。这正是"彩色 + 深度"双路数据的典型价值(即上文两张样例图展示的对比效果)。

七、参考索引

内容 仓库路径
本教程原文 doc/tutorials/app/orbbec_astra_openni.markdown
完整示例代码 samples/cpp/tutorial_code/videoio/openni_orbbec_astra/openni_orbbec_astra.cpp
CAP_OPENNI2_ASTRA 枚举定义 modules/videoio/include/opencv2/videoio.hpp
OpenNI2 后端实现(Astra 彩色流限制注释) modules/videoio/src/cap_openni2.cpp
WITH_OPENNI2 CMake 选项 CMakeLists.txt
彩色帧样例 doc/tutorials/app/images/astra_color.jpg
深度帧样例 doc/tutorials/app/images/astra_depth.png

适用前提小结:本文流程针对 Orbbec Astra 系列(含 Astra Pro)相机、OpenNI2 SDK(实测推荐 2.3.0.63)、Linux 下 UDEV + video 组权限环境;彩色流依赖 V4L2,OpenNI2 后端需在构建时以 -DWITH_OPENNI2=ON 显式启用。

登录后查看全文
热门项目推荐
相关项目推荐