赞
踩
本教程专门介绍 Astra 系列 Orbbec 3D 相机 (Products - ORBBEC - 3D Vision for a 3D World)。除了常见的颜色传感器外,相机还具有深度传感器。可以使用带有cv::VideoCapture类的开源OpenNI API读取深度传感器。视频流通过常规摄像头接口提供。
要将 Astra 相机的深度传感器与 OpenCV 一起使用,您应该执行以下步骤:
例如,如果您使用 64 位 GNU/Linux,请运行:
- $ cd Linux/OpenNI-Linux-x64-2.3.0.63/
- $ sudo ./install.sh
完成安装后,请确保重新插入设备以使 udev 规则生效。相机现在应该可以用作通用相机设备。请注意,您当前的用户应属于有权访问相机的组。另外,请确保video
OpenNIDevEnvironment
源文件:
$ source OpenNIDevEnvironment
要验证源命令是否有效,以及是否能找到OpenNI库和头文件,请运行以下命令,您应该会在终端中看到类似的内容:
- $ 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
如果以上OpenNIDevEnvironment
两个变量为空,则需要重新获取。
注意
Orbbec OpenNI SDK 2.3.0.86 及更高版本不再提供任何功能。您可以使用以下install.sh
脚本初始化环境:
- #使用 sudo 检查用户是否是 root/运行
- 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"
现在,您可以通过在CMake中设置标志来配置启用OpenNI支持的OpenCV。您可能还希望启用该WITH_OPENNI2
标志以获取与 Astra 相机配合使用的代码示例。在包含OpenCV源代码的目录中BUILD_EXAMPLES
运行以下命令以启用OpenNI支持:
- $ mkdir build
- $ cd build
- $ cmake -DWITH_OPENNI2=ON ..
如果找到 OpenNI 库,则将使用 OpenNI2 支持构建 OpenCV。您可以在 CMake 日志中查看 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)
构建 OpenCV:
$ make
Astra Pro 相机有两个传感器——深度传感器和颜色传感器。可以使用带有cv::VideoCapture类的OpenNI接口读取深度传感器。视频流无法通过OpenNI API获得,只能通过常规的相机接口提供。因此,要同时获取深度和颜色帧,应创建两个 cv::VideoCapture 对象:
- // Open depth stream
- VideoCapture depthStream(CAP_OPENNI2_ASTRA);
- // Open color stream
- VideoCapture colorStream(0, CAP_V4L2);
第一个对象将使用 OpenNI2 API 检索深度数据。第二个使用 Video4Linux2 接口访问颜色传感器。请注意,上面的示例假定 Astra 相机是系统中的第一台相机。如果您连接了多个摄像头,则可能需要显式设置正确的摄像头编号。
在使用创建的 VideoCapture 对象之前,您可能需要通过设置对象的属性来设置流参数。最重要的参数是帧宽、帧高和 fps。在此示例中,我们将两个流的宽度和高度配置为 VGA 分辨率,这是两个传感器可用的最大分辨率,并且我们希望两个流参数相同,以便更轻松地进行颜色到深度的数据配准:
- // 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);
为了设置和检索传感器数据生成器的某些属性,请分别使用 cv::VideoCapture::set 和 cv::VideoCapture::get 方法,例如:
- // 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;
深度发生器支持通过OpenNI接口提供的相机的以下属性:
cv::CAP_PROP_OPENNI2_MIRROR – 用于启用或禁用此流的镜像的标志。设置为 0 以禁用镜像
接下来的属性仅供获取:
设置 VideoCapture 对象后,您可以开始从中读取帧。
注意
OpenCV 的 VideoCapture 提供同步 API,因此您必须在新线程中抓取帧,以避免在读取另一个流时阻塞另一个流。VideoCapture 不是一个线程安全类,因此您需要小心避免任何可能的死锁或数据争用。
由于应同时读取两个视频源,因此有必要创建两个线程以避免阻塞。示例实现,用于从新线程中的每个传感器获取帧,并将它们及其时间戳存储在列表中:
- // Create two lists to store frames
- std::list<Frame> depthFrames, colorFrames;
- const std::size_t maxFrames = 64;
-
- // Synchronization objects
- std::mutex mtx;
- std::condition_variable dataReady;
- std::atomic<bool> isFinish;
-
- isFinish = false;
-
- // Start depth reading thread
- 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();
- }
- }
- });
-
- // Start color reading thread
- std::thread colorReader([&]
- {
- while (!isFinish)
- {
- // Grab and decode new frame
- if (colorStream.grab())
- {
- Frame f;
- f.timestamp = cv::getTickCount();
- colorStream.retrieve(f.frame);
- if (f.frame.empty())
- {
- cerr << "ERROR: Failed to decode frame from color stream" << endl;
- break;
- }
-
- {
- std::lock_guard<std::mutex> lk(mtx);
- if (colorFrames.size() >= maxFrames)
- colorFrames.pop_front();
- colorFrames.push_back(f);
- }
- dataReady.notify_one();
- }
- }
- });
VideoCapture 可以检索以下数据:
当新数据可用时,每个读取线程都使用条件变量通知主线程。帧存储在有序列表中 – 列表中的第一帧是最早捕获的帧,最后一帧是最新捕获的帧。由于深度和颜色帧是从独立来源读取的,因此即使将两个视频流设置为相同的帧速率,两个视频流也可能不同步。可以将后同步过程应用于流,以将深度和颜色帧组合成对。下面的示例代码演示了此过程:
- // Pair depth and color frames
- while (!isFinish)
- {
- std::unique_lock<std::mutex> lk(mtx);
- while (!isFinish && (depthFrames.empty() || colorFrames.empty()))
- dataReady.wait(lk);
-
- while (!depthFrames.empty() && !colorFrames.empty())
- {
- if (!lk.owns_lock())
- lk.lock();
-
- // Get a frame from the list
- Frame depthFrame = depthFrames.front();
- int64 depthT = depthFrame.timestamp;
-
- // Get a frame from the list
- Frame colorFrame = colorFrames.front();
- int64 colorT = colorFrame.timestamp;
-
- // 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;
- }
- depthFrames.pop_front();
- colorFrames.pop_front();
- lk.unlock();
-
- // Show depth frame
- Mat d8, dColor;
- depthFrame.frame.convertTo(d8, CV_8U, 255.0 / 2500);
- applyColorMap(d8, dColor, COLORMAP_OCEAN);
- imshow("Depth (colored)", dColor);
-
- // Show color frame
- imshow("Color", colorFrame.frame);
-
- // Exit on Esc key press
- int key = waitKey(1);
- if (key == 27) // ESC
- {
- isFinish = true;
- break;
- }
- }
- }
在上面的代码片段中,执行被阻止,直到两个帧列表中都有一些帧。当有新帧时,会检查它们的时间戳——如果它们相差超过帧周期的一半,则其中一个帧将被丢弃。如果时间戳足够接近,则两个帧配对。现在,我们有两个框架:一个包含颜色信息,另一个包含深度信息。在上面的示例中,检索到的帧仅使用 cv::imshow 函数显示,但您可以在此处插入任何其他处理代码。
在下面的示例图像中,您可以看到表示同一场景的色框和深度框。从色框上看,很难区分植物叶子和画在墙上的叶子,但深度数据很容易。
完整的实现可以在samples/cpp/tutorial_code/videoio
目录中的orbbec_astra.cpp中找到。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。