当前位置:   article > 正文

OpenCV使用 Orbbec Astra 3D 相机(76)_astra s 相机用opencv打开

astra s 相机用opencv打开
  返回: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,请运行:

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

完成安装后,请确保重新插入设备以使 udev 规则生效。相机现在应该可以用作通用相机设备。请注意,您当前的用户应属于有权访问相机的组。另外,请确保video OpenNIDevEnvironment源文件:

$ source OpenNIDevEnvironment

要验证源命令是否有效,以及是否能找到OpenNI库和头文件,请运行以下命令,您应该会在终端中看到类似的内容:

  1. $ echo $OPENNI2_INCLUDE
  2. /home/user/OpenNI_2.3.0.63/Linux/OpenNI-Linux-x64-2.3.0.63/Include
  3. $ echo $OPENNI2_REDIST
  4. /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脚本初始化环境: 

  1. #使用 sudo 检查用户是否是 root/运行
  2. if [ `whoami` != root ]; then
  3. echo Please run this script with sudo
  4. exit
  5. fi
  6. ORIG_PATH=`pwd`
  7. cd `dirname $0`
  8. SCRIPT_PATH=`pwd`
  9. cd $ORIG_PATH
  10. if [ "`uname -s`" != "Darwin" ]; then
  11. # Install UDEV rules for USB device
  12. cp ${SCRIPT_PATH}/orbbec-usb.rules /etc/udev/rules.d/558-orbbec-usb.rules
  13. echo "usb rules file install at /etc/udev/rules.d/558-orbbec-usb.rules"
  14. fi
  15. OUT_FILE="$SCRIPT_PATH/OpenNIDevEnvironment"
  16. echo "export OPENNI2_INCLUDE=$SCRIPT_PATH/../sdk/Include" > $OUT_FILE
  17. echo "export OPENNI2_REDIST=$SCRIPT_PATH/../sdk/libs" >> $OUT_FILE
  18. chmod a+r $OUT_FILE
  19. echo "exit"

现在,您可以通过在CMake中设置标志来配置启用OpenNI支持的OpenCV。您可能还希望启用该WITH_OPENNI2标志以获取与 Astra 相机配合使用的代码示例。在包含OpenCV源代码的目录中BUILD_EXAMPLES运行以下命令以启用OpenNI支持:

  1. $ mkdir build
  2. $ cd build
  3. $ cmake -DWITH_OPENNI2=ON ..

如果找到 OpenNI 库,则将使用 OpenNI2 支持构建 OpenCV。您可以在 CMake 日志中查看 OpenNI2 支持的状态:

  1. -- Video I/O:
  2. -- DC1394: YES (2.2.6)
  3. -- FFMPEG: YES
  4. -- avcodec: YES (58.91.100)
  5. -- avformat: YES (58.45.100)
  6. -- avutil: YES (56.51.100)
  7. -- swscale: YES (5.7.100)
  8. -- avresample: NO
  9. -- GStreamer: YES (1.18.1)
  10. -- OpenNI2: YES (2.3.0)
  11. -- v4l/v4l2: YES (linux/videodev2.h)

构建 OpenCV:

$ make

代码:

Astra Pro 相机有两个传感器——深度传感器和颜色传感器。可以使用带有cv::VideoCapture类的OpenNI接口读取深度传感器。视频流无法通过OpenNI API获得,只能通过常规的相机接口提供。因此,要同时获取深度和颜色帧,应创建两个 cv::VideoCapture 对象:

  1. // Open depth stream
  2. VideoCapture depthStream(CAP_OPENNI2_ASTRA);
  3. // Open color stream
  4. VideoCapture colorStream(0, CAP_V4L2);

第一个对象将使用 OpenNI2 API 检索深度数据。第二个使用 Video4Linux2 接口访问颜色传感器。请注意,上面的示例假定 Astra 相机是系统中的第一台相机。如果您连接了多个摄像头,则可能需要显式设置正确的摄像头编号。

在使用创建的 VideoCapture 对象之前,您可能需要通过设置对象的属性来设置流参数。最重要的参数是帧宽、帧高和 fps。在此示例中,我们将两个流的宽度和高度配置为 VGA 分辨率,这是两个传感器可用的最大分辨率,并且我们希望两个流参数相同,以便更轻松地进行颜色到深度的数据配准:

  1. // Set color and depth stream parameters
  2. colorStream.set(CAP_PROP_FRAME_WIDTH, 640);
  3. colorStream.set(CAP_PROP_FRAME_HEIGHT, 480);
  4. depthStream.set(CAP_PROP_FRAME_WIDTH, 640);
  5. depthStream.set(CAP_PROP_FRAME_HEIGHT, 480);
  6. depthStream.set(CAP_PROP_OPENNI2_MIRROR, 0);

为了设置和检索传感器数据生成器的某些属性,请分别使用 cv::VideoCapture::set 和 cv::VideoCapture::get 方法,例如:

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

深度发生器支持通过OpenNI接口提供的相机的以下属性:

设置 VideoCapture 对象后,您可以开始从中读取帧。

注意

OpenCV 的 VideoCapture 提供同步 API,因此您必须在新线程中抓取帧,以避免在读取另一个流时阻塞另一个流。VideoCapture 不是一个线程安全类,因此您需要小心避免任何可能的死锁或数据争用。

由于应同时读取两个视频源,因此有必要创建两个线程以避免阻塞。示例实现,用于从新线程中的每个传感器获取帧,并将它们及其时间戳存储在列表中:

  1. // Create two lists to store frames
  2. std::list<Frame> depthFrames, colorFrames;
  3. const std::size_t maxFrames = 64;
  4. // Synchronization objects
  5. std::mutex mtx;
  6. std::condition_variable dataReady;
  7. std::atomic<bool> isFinish;
  8. isFinish = false;
  9. // Start depth reading thread
  10. std::thread depthReader([&]
  11. {
  12. while (!isFinish)
  13. {
  14. // Grab and decode new frame
  15. if (depthStream.grab())
  16. {
  17. Frame f;
  18. f.timestamp = cv::getTickCount();
  19. depthStream.retrieve(f.frame, CAP_OPENNI_DEPTH_MAP);
  20. if (f.frame.empty())
  21. {
  22. cerr << "ERROR: Failed to decode frame from depth stream" << endl;
  23. break;
  24. }
  25. {
  26. std::lock_guard<std::mutex> lk(mtx);
  27. if (depthFrames.size() >= maxFrames)
  28. depthFrames.pop_front();
  29. depthFrames.push_back(f);
  30. }
  31. dataReady.notify_one();
  32. }
  33. }
  34. });
  35. // Start color reading thread
  36. std::thread colorReader([&]
  37. {
  38. while (!isFinish)
  39. {
  40. // Grab and decode new frame
  41. if (colorStream.grab())
  42. {
  43. Frame f;
  44. f.timestamp = cv::getTickCount();
  45. colorStream.retrieve(f.frame);
  46. if (f.frame.empty())
  47. {
  48. cerr << "ERROR: Failed to decode frame from color stream" << endl;
  49. break;
  50. }
  51. {
  52. std::lock_guard<std::mutex> lk(mtx);
  53. if (colorFrames.size() >= maxFrames)
  54. colorFrames.pop_front();
  55. colorFrames.push_back(f);
  56. }
  57. dataReady.notify_one();
  58. }
  59. }
  60. });

VideoCapture 可以检索以下数据:

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

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

  1. // Pair depth and color frames
  2. while (!isFinish)
  3. {
  4. std::unique_lock<std::mutex> lk(mtx);
  5. while (!isFinish && (depthFrames.empty() || colorFrames.empty()))
  6. dataReady.wait(lk);
  7. while (!depthFrames.empty() && !colorFrames.empty())
  8. {
  9. if (!lk.owns_lock())
  10. lk.lock();
  11. // Get a frame from the list
  12. Frame depthFrame = depthFrames.front();
  13. int64 depthT = depthFrame.timestamp;
  14. // Get a frame from the list
  15. Frame colorFrame = colorFrames.front();
  16. int64 colorT = colorFrame.timestamp;
  17. // Half of frame period is a maximum time diff between frames
  18. const int64 maxTdiff = int64(1000000000 / (2 * colorStream.get(CAP_PROP_FPS)));
  19. if (depthT + maxTdiff < colorT)
  20. {
  21. depthFrames.pop_front();
  22. continue;
  23. }
  24. else if (colorT + maxTdiff < depthT)
  25. {
  26. colorFrames.pop_front();
  27. continue;
  28. }
  29. depthFrames.pop_front();
  30. colorFrames.pop_front();
  31. lk.unlock();
  32. // Show depth frame
  33. Mat d8, dColor;
  34. depthFrame.frame.convertTo(d8, CV_8U, 255.0 / 2500);
  35. applyColorMap(d8, dColor, COLORMAP_OCEAN);
  36. imshow("Depth (colored)", dColor);
  37. // Show color frame
  38. imshow("Color", colorFrame.frame);
  39. // Exit on Esc key press
  40. int key = waitKey(1);
  41. if (key == 27) // ESC
  42. {
  43. isFinish = true;
  44. break;
  45. }
  46. }
  47. }

在上面的代码片段中,执行被阻止,直到两个帧列表中都有一些帧。当有新帧时,会检查它们的时间戳——如果它们相差超过帧周期的一半,则其中一个帧将被丢弃。如果时间戳足够接近,则两个帧配对。现在,我们有两个框架:一个包含颜色信息,另一个包含深度信息。在上面的示例中,检索到的帧仅使用 cv::imshow 函数显示,但您可以在此处插入任何其他处理代码。

在下面的示例图像中,您可以看到表示同一场景的色框和深度框。从色框上看,很难区分植物叶子和画在墙上的叶子,但深度数据很容易。

完整的实现可以在samples/cpp/tutorial_code/videoio目录中的orbbec_astra.cpp中找到。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号