OpenCV使用 Orbbec Astra 3D 相机(76)

79 篇文章 0 订阅
78 篇文章 0 订阅
  返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV使用 Kinect 和其他兼容 OpenNI 的深度传感器(75)
下一篇 :OpenCV系列文章目录(持续更新中......)

介绍

本教程专门介绍 Astra 系列 Orbbec 3D 相机 (Products - ORBBEC - 3D Vision for a 3D World)。除了常见的颜色传感器外,相机还具有深度传感器。可以使用带有cv::VideoCapture类的开源OpenNI API读取深度传感器。视频流通过常规摄像头接口提供。

安装说明

要将 Astra 相机的深度传感器与 OpenCV 一起使用,您应该执行以下步骤:

  1. 下载最新版本的Orbbec OpenNI SDK(从这里 https://orbbec3d.com/index/download.html)。解压缩存档,根据您的操作系统选择内部版本,然后按照自述文件中提供的安装步骤进行操作。

例如,如果您使用 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接口提供的相机的以下属性:

设置 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 可以检索以下数据:

  1. 深度生成器给出的数据:
  2. 颜色传感器给出的数据是常规的 BGR 图像 (CV_8UC3)。

当新数据可用时,每个读取线程都使用条件变量通知主线程。帧存储在有序列表中 – 列表中的第一帧是最早捕获的帧,最后一帧是最新捕获的帧。由于深度和颜色帧是从独立来源读取的,因此即使将两个视频流设置为相同的帧速率,两个视频流也可能不同步。可以将后同步过程应用于流,以将深度和颜色帧组合成对。下面的示例代码演示了此过程:

// 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中找到。

  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚梦者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值