目录
1.1 成像原理简介
1.1.1 结构光
1.1.2 双目视觉
1.1.3 光飞行时间TOF
2.使用手册
参考网址
2.1 产品集成设计
2.2 SDK介绍与使用
2.3 常用API介绍
OPENNI API
2 OpenNI类(OpenNI.h)
1.1 成像原理简介
1.1.1 结构光
结构光,英文叫做 Structured light,通常采用特定波长的不可见的红外激光作为光源,它发射出来的光经过
一定的编码投影在物体上,通过一定算法来计算返回的编码图案的畸变来得到物体的位置和深度信息。根
据编码图案不同一般有条纹结构光---enshape ,编码结构光---Mantis Vision, Realsense(F200), 散斑结构光
---apple(primesense), 奥比中光。下图是一个典型的结构光相机的示意图:
结构光(散斑)的优点主要有:
1)方案成熟,相机基线可以做的比较小,方便小型化。
2)资源消耗较低,单帧 IR 图就可计算出深度图,功耗低。
3)主动光源,夜晚也可使用。
4)在一定范围内精度高,分辨率高,分辨率可达 1280x1024,帧率可达 60FPS。
散斑结构光的缺点与结构光类似:
1)容易受环境光干扰,室外体验差。
2)随检测距离增加,精度会变差。
1.1.2 双目视觉
双目相机的主要优点有:
1)硬件要求低,成本也低。普通 CMOS 相机即可。
2)室内外都适用。只要光线合适,不要太昏暗。
但是双目的缺点也是非常明显:
1)对环境光照非常敏感。光线变化导致图像偏差大,进而会导致匹配失败或精度低
2)不适用单调缺乏纹理的场景。双目视觉根据视觉特征进行图像匹配,没有特征会导致匹配失败。
3)计算复杂度高。该方法是纯视觉的方法,对算法要求高,计算量较大。
4)基线限制了测量范围。测量范围和基线(两个摄像头间距)成正比,导致无法小型化。
1.1.3 光飞行时间TOF
TOF 的优点主要有:
1)检测距离远。在激光能量够的情况下可达几十米。
2)受环境光干扰比较小。
但是 TOF 也有一些显而易见的问题:
1)对设备要求高,特别是时间测量模块。
2)资源消耗大。 该方案在检测相位偏移时需要多次采样积分,运算量大。
3)边缘精度低。
4)限于资源消耗和滤波,帧率和分辨率都没办法做到较高。目前消费类最大也就 VGA。
双目具有良好的户外表现,但不适用纹理变化不明显的场合,如白墙;
TOF在远距离的精度更佳,但目前受限于图像分辨率低、功耗大;
结构光综合表现目前来看,在三种3D传感技术中最优,但是户外强光干扰明显和远距离处精度差
奥比中光推出了940nm波段的二代产品P1、Deeyea和双目结构光结合的产品苍龙一 号,尽可能改善结构光的固有缺陷。
2.使用手册
参考网址
https://developer.orbbec.com.cn/develop_details.html?id=1 很全
https://developer.orbbec.com.cn/technical_library.html?id=68 代码实例
https://developer.orbbec.com.cn/technical_library.html?id=70 API
SDK支持
https://developer.orbbec.com.cn/download.html?id=75&menushow=1
官网教程
https://developer.orbbec.com.cn/technical_library.html?id=22
应用例程
https://developer.orbbec.com.cn/develop_details.html?id=3
2.1 产品集成设计
深度图
不同于RGB图像,深度图中每个像素点保存的是视场范围内物体距离相机平面的深度值数据。深
度原始数据通常为16-bit unsigned int类型,单位可通过SDK指定,通常为1mm,即深度图中每个
像素点保存着16-bit无符号整型数据,单位为1mm。
为了将深度数据可视化的显示出来,通常将其转换为灰度图显示,如下图所示,不同灰度级表示
不同的深度值大小。
2.2 SDK介绍与使用
奥比中光提供的OpenNI2.3系列SDK基于OpenNI2开发,OpenNI2(开放自然交互)是一个多
语言、跨平台的框架,它定义了应用程序、中间件和3D传感设备之间的接口。
windows上位机使用前需先安装对应的驱动,具体步骤为:
1.双击【SensorDriver_V4.3.0.17.exe】进行驱动安装
2.解压OrbbecViewer上位机安装包
3.进入解压后文件夹,双击【OrbbecViewer.exe】运行上位机
Windows工程环境配置
1)适用范围
本公司SDK适用X86/X64 Windows7及以上平台
2)开发平台搭建
推荐的Windows开发平台为Visual Studio 2013或以上版本。对于Visual Studio平台,可通过以
下步骤搭建开发环境:
新建或打开C++工程
选择“项目——属性”,将\SDK\windows\Include目录添加到头文件依赖路径下,注意属性
配置(x86/x64)与运行时一致
在“链接器——输入”目录下新增OpenNI.lib项,完成环境配置,可以开始编辑自己的代码
了。
注意编译运行工程时,把x64-release或者x86-release文件夹里面OpenNI文件夹以及ini和dll
文件拷贝到exe文件所在目录下,否则会闪退或报错。
2.3 常用API介绍
https://zhuanlan.zhihu.com/p/608879068
-
OPENNI API
主要包含4个大类:
(1)openni::OpenNI
提供了一个静态的API进入点。提供访问设备,设备相关事件,版本和错误信息。首先确保连接了设备。
(2)openni::Device
提供与连接到系统的单个传感器设备的接口。需要在创建OpenNI之前先初始化它。提供了对流的访问权限。
(3)openni::VideoStream
从一个设备(Device)里提取一个视频流,需要获取视频帧引用(VideoFrameRefs)。
(4)openni::VideoFrameRef
从相关元数据中提取单个视频。从特定流获取。
除了这些主要类之外,还提供了各种支持类和结构来保存特定类型的数据。提供了一个用于将OpenNI视频流存储到文件中的记录器类。还有一些为OpenNI和Stream类可以生成的事件提供的监听器类。视频流可以使用两种基本方法之一来读取:基于循环的和基于事件的。这两种方法将在本指南的后面详细介绍
(5) PlaybackControl 类
有些操作只有在处理已记录的文件时才可能执行。这些操作包括在流中查找、确定录音的长度、循环录音以及改变播放速度。这个功能已经被播放控制类封装了。
2 OpenNI类(OpenNI.h)
-
2.1 对设备的基本访问
(1)OpenNI::initialize() 初始化所有可用的传感器驱动程序,并扫描系统中可用的设备
(2)OpenNI::enumerateDevices() 返回已连接到系统的所有可用设备的列表
(3) OpenNI::shutdown() 关闭所有驱动程序并正确地清理
//1.初始化
Status rc = OpenNI::initialize();
if (rc != STATUS_OK)
{
printf("Initialize failed\n%s\n", OpenNI::getExtendedError());
return 1;
}
// 2.设备对象
Device device;
rc = device.open(ANY_DEVICE);
if (rc != STATUS_OK)
{
printf("Couldn't open device\n%s\n", OpenNI::getExtendedError());
return 2;
}
// 3.视频流对象
VideoStream depth;
if (device.getSensorInfo(SENSOR_DEPTH) != NULL)
{
rc = depth.create(device, SENSOR_DEPTH);
if (rc != STATUS_OK)
{
printf("Couldn't create depth stream\n%s\n", OpenNI::getExtendedError());
return 3;
}
}
rc = depth.start();
if (rc != STATUS_OK)
{
printf("Couldn't start the depth stream\n%s\n", OpenNI::getExtendedError());
return 4;
}
// 4.循环读取一帧,getData
int changedStreamDummy;
VideoStream* pStream = &depth;
VideoFrameRef frame; // 先定义再声明,最后使用
const int maxFramesToProcess = 100;
//Sentinel to count the number of frames that we've processed
int count = 0;
//The frame processing loop
do {
rc = OpenNI::waitForAnyStream(&pStream, 1, &changedStreamDummy, TIMEOUT_FOREVER);
if (rc != STATUS_OK)
{
printf("Wait failed! (timeout is %d ms)\n%s\n", TIMEOUT_FOREVER, OpenNI::getExtendedError());
continue;
}
rc = depth.readFrame(&frame);
if (rc != STATUS_OK)
{
printf("Read failed!\n%s\n", OpenNI::getExtendedError());
continue;
}
if (frame.getVideoMode().getPixelFormat() != PIXEL_FORMAT_DEPTH_1_MM && frame.getVideoMode().getPixelFormat() != PIXEL_FORMAT_DEPTH_100_UM)
{
printf("Unexpected frame format\n");
continue;
}
DepthPixel* pDepth = (DepthPixel*)frame.getData(); //转换帧数据为DepthPixel类型的指针
printf("[%08llu] %8d\n ", (long long)frame.getTimestamp(), pDepth[0]);
int middleIndex = (frame.getHeight() + 1) * frame.getWidth() / 2;
printf("[%08llu] %8d\n %8d\n %8d\n", (long long)frame.getTimestamp(), pDepth[middleIndex], frame.getHeight(), frame.getWidth());
count++;
} while (count < maxFramesToProcess);
// 释放资源
depth.stop();
depth.destroy();
device.close();
OpenNI::shutdown();
std::cout << "hit enter to exit program" << std::endl;
system("pause");