基于OpenNI2(win64) 的Structure Sensor应用开发完整教程

来源:互联网 发布:中准会计师事务所 知乎 编辑:程序博客网 时间:2024/06/14 06:44

工具准备

1.从http://www.structure.io/openni 下载OpenNI-Windows-x64-2.2.0.33.zip
2.打开zip,运行其中的msi文件。安装完毕注销windows并重新登入(安装完毕后,将有两个新的环境变量OPENNI2_INCLUDE64,OPENNI2_LIB64被添加到系统环境变量中)
3.准备好visual studio 2010 或更高版本

创建第一个工程

新建win32控制台应用程序

这里写图片描述

配置工程依赖项

配置库的输入目录
配置输入的库
配置头文件包含

编写代码

以下代码是对OpenNI2提供的范例进行的归纳,任何与OpenNI2及深度传感器无关的代码都被略去。

启动OpenNI2和设备

以下是常规的读取深度帧的上下文初始化过程,其梗概是:
启动OpenNI2—>打开一个传感器设备—>通过设备创建一个视频流(这个流可以是深度图像流,颜色图像流或其他设备支持的流)

    openni::Status rc = openni::STATUS_OK;    rc = openni::OpenNI::initialize();    printf("After initialization:\n%s\n", openni::OpenNI::getExtendedError());    openni::Device device;    rc = device.open(openni::ANY_DEVICE);    if (rc != openni::STATUS_OK)    {        printf("DepthSensor Viewer: Device open failed:\n%s\n", openni::OpenNI::getExtendedError());        openni::OpenNI::shutdown();        return 1;    }    openni::VideoStream depth;    rc = depth.create(device, openni::SENSOR_DEPTH);    auto videomode = new openni::VideoMode();    videomode->setFps(30);    videomode->setPixelFormat(openni::PIXEL_FORMAT_DEPTH_100_UM);    videomode->setResolution(160, 120);    depth.setVideoMode(*videomode);    if (rc == openni::STATUS_OK)    {        rc = depth.start();        if (rc != openni::STATUS_OK)        {            printf("DepthSensor Viewer: Couldn't start depth stream:\n%s\n", openni::OpenNI::getExtendedError());            depth.destroy();        }    }    else    {        printf("DepthSensor Viewer: Couldn't find depth stream:\n%s\n", openni::OpenNI::getExtendedError());    }    // m_streams 将在后面的代码片段中被用到,我们需要一个指针的数组作为参数    m_streams = new VideoStream*[1];    m_streams[0] = depth;

在自定义的上下文中读取深度帧并渲染

你可以将下面的代码放置在渲染线程的循环中,比如通过glut窗口系统的glutDisplayFunc()注册的回调函数中

    int changedIndex;    openni::Status rc = openni::OpenNI::waitForAnyStream(m_streams, 2, &changedIndex);    openni::VideoFrameRef       depthFrame;    m_streams[changedIndex]->readFrame(&depthFrame);    /// 我们已经拿到了数据,现在可以用你想要的方式进行绘制了, 典型的绘制如下:将数据转为RGB纹理图片    const openni::DepthPixel* pDepthRow = (const openni::DepthPixel*)depthFrame.getData();    openni::RGB888Pixel* pTexRow = m_pTexMap + m_depthFrame.getCropOriginY() * m_nTexMapX;    int rowSize = depthFrame.getStrideInBytes() / sizeof(openni::DepthPixel);    for (int y = 0; y < depthFrame.getHeight(); ++y)    {        const openni::DepthPixel* pDepth = pDepthRow;        openni::RGB888Pixel* pTex = pTexRow + depthFrame.getCropOriginX();        for (int x = 0; x < depthFrame.getWidth(); ++x, ++pDepth, ++pTex)        {            if (*pDepth != 0)            {                char value = 255 - ((float)(*pDepth) / (float)256);       ///DepthPixel是两个字节的,需要将其压缩为1个字节;       ///这种转换效果也许不是最好的,关于这可以查看OpenNI2的例子SimpleViewer                pTex->r = value;                pTex->g = value;                pTex->b = 0;                }            }            pDepthRow += rowSize;            pTexRow += m_nTexMapX;        }    }     

当然,OpenNI2 也提供了将深度值转换为世界坐标系下的3维坐标的API:

void depthPixelToWorld3d(const openni::VideoStream& depthStream, int depthX, int depthY,openni::DepthPixel depthZ, float world3d[3]){    openni::CoordinateConverter::convertDepthToWorld(depthStream, depthX, depthY,depthZ, &world3d[0],        &world3d[1], &world3d[2]);}
    const openni::DepthPixel* pDepthRow = (const openni::DepthPixel*)depthFrame.getData();    int depthYStart = depthFrame.getCropOriginY();    int depthXStart = depthFrame.getCropOriginX();    int rowSize = depthFrame.getStrideInBytes() / sizeof(openni::DepthPixel);       auto points= new float[depthFrame.getWidth()* depthFrame.getHeight()][3];    int pointCounts = 0;    for (int y = 0; y < depthFrame.getHeight(); ++y)    {        const openni::DepthPixel* pDepth = pDepthRow;        for (int x = 0; x < depthFrame.getWidth(); ++x, ++pDepth)        {            if (*pDepth != 0)            {                depthPixelToWorld3d(depthStream, depthXStart + x,                    depthYStart + y, *pDepth, points[pointCounts]);                ++pointCounts;            }        }        pDepthRow += rowSize;    }

运行程序

用USB连接StructureSensor,并将OpenNI2/Redist目录下的所有文件和文件夹拷贝到应用程序的同一目录下,运行程序。如果程序不能找到设备,请抽出USB数据线并重新连接。

可能存在的问题

openni::CoordinateConverter::convertDepthToWorld()所输出的世界坐标的XYZ分量的值并不是以毫米为单位

这是OpenNI2社区最近才修复的bug:
https://github.com/occipital/OpenNI2/commit/1fce8edffab43c4a4cf201cff86f415b07a2d37f
要想解决这个问题,只能自己编译OpenNI2。从https://github.com/occipital/OpenNI2下载最新的zip源码。无须下载该页面所指出的其他内容,直接运行源码包中的Visual Studio sln文件,生成其中的OpenNI工程,将生成的OpenNI2.dll拷贝到应用程序所在目录,替换旧的OpenNI2.dll

自己编译完整的OpenNI2安装包

如果需要生成完整的OpenNI2安装包,请使用Visual Studio 2010,并安装
WIX 3.5 http://wix.codeplex.com/releases/view/60102 。运行OpenNI.sln 并生成解决方案下的Install工程

0 0
原创粉丝点击