Kinect for Windows SDK v2.0 开发笔记 (十八) Fusion 基本2D显示

来源:互联网 发布:java 给js传值 双引号 编辑:程序博客网 时间:2024/04/29 22:48


(转载请注明出处)

使用SDK: Kinect for Windows SDK v2.0 1409

在9月更新中, x86的Fusion终于可以使用了,这次说Fusion吧.

Fusion不再是所谓的“Fusion帧”了, 而是向一代靠齐,以NUI开头,可见算法与一代一样。

还是仅仅是降低代码移植成本?


所以找了找一代的说明:

Kinect Fusion能够基于深度图像,实时对场景进行重建。

设备能够使用GPU或者CPU进行计算:

enum _NUI_FUSION_RECONSTRUCTION_PROCESSOR_TYPE    {        NUI_FUSION_RECONSTRUCTION_PROCESSOR_TYPE_CPU= 1,        NUI_FUSION_RECONSTRUCTION_PROCESSOR_TYPE_AMP= 2    } NUI_FUSION_RECONSTRUCTION_PROCESSOR_TYPE;

可见GPU加速使用的是C++AMP技术。

1代说明地址:http://msdn.microsoft.com/us-en/library/dn188670.aspx

Fusion将收集的数据慢慢"融合"起来


这是因为期间发动了“再融合”,从而使物品表面光滑(误)


Fusion工作管线:



需要的额外文件:

即Fusion的dll文件,使用命令:

xcopy /C /Y "$(KINECTSDK20_DIR)Redist\Fusion\x86\Kinect20.Fusion.dll" "$(OutDir)"

复制过来即可,头文件与库文件:
#include <NuiKinectFusionApi.h>
#pragma comment ( lib, "Kinect20.Fusion.lib" )

请注意以后SDK更新后,文件名的改变


基本Fusion步骤:

0. 设备检查:

    Fusion对图形设备是有要求的,需要DX11的支持,可以进行设备检测:

    // 获取设备信息:: 可以用于debug    WCHAR description[MAX_PATH];    WCHAR instancePath[MAX_PATH];    UINT memorySize = 0;    if (SUCCEEDED(hr)){        hr = NuiFusionGetDeviceInfo(            m_processorType,            m_deviceIndex,            description,            lengthof(description),            instancePath,            lengthof(instancePath),            &memorySize            );        if (hr == E_NUI_BADINDEX){            ::MessageBoxW(nullptr, L"需要DX11支持", L"错误", MB_ICONERROR);        }    }

NuiFusionGetDeviceInfo第一个参数是设备类型,就是 CPU还是GPU,

第二个是设备索引,标记-1使用默认设备。这次就-1,使用默认设备,下次枚举设备。

其他参数就不说了。

1. 创建Fusion 容积重建INuiFusionReconstruction

  核心函数NuiFusionCreateReconstruction:

参数1: 容积重建参数

参数2: 设备类型

参数3: 设备索引

参数4: 世界到相机 坐标转换矩阵

参数5: 返回指针

参数1中:

typedef struct _NUI_FUSION_RECONSTRUCTION_PARAMETERS    {    FLOAT voxelsPerMeter;    UINT voxelCountX;    UINT voxelCountY;    UINT voxelCountZ;    } NUI_FUSION_RECONSTRUCTION_PARAMETERS;


从上到下依次是

    每米体素(体积元素或者体积像素)数量

    重建X轴体素数量

    重建Y轴体素数量

    重建Z轴体素数量

SDK中使用的数据为:

    m_reconstructionParams.voxelsPerMeter = 256.f;    m_reconstructionParams.voxelCountX = 384;    m_reconstructionParams.voxelCountY = 384;     m_reconstructionParams.voxelCountZ = 384; 

即每米有256个体素,每两个临近的体素距离约为4mm.

建立了1.5m*1.5m*1.5m的容积,消耗内存384*384*384*4,200+MB(可能还有内存对齐消耗的内存)

GPU加速即显卡内存,CPU加速即系统内存,这可是一笔大买卖啊。

目前主流显存在1G~2G,差强人意。


创建后,可能会进行检错:

        if (hr == E_NUI_GPU_FAIL){            ::MessageBoxW(nullptr, L"显卡不支持Fusion计算!\n或者 初始化失败", L"错误", MB_ICONERROR);        }        else if (hr == E_NUI_GPU_OUTOFMEMORY){            ::MessageBoxW(nullptr, L"显存不足", L"错误", MB_ICONERROR);        }


1.5. 查看世界到容积坐标转换矩阵:

之前成功后,我们可以看看创建后的世界到容积坐标转换矩阵

if (SUCCEEDED(hr)){        hr = m_pReconstruction->GetCurrentWorldToVolumeTransform(&m_defaultWorldToVolumeTransform);    }

设置断点后我们可以查看其值如下:

256   0     0     0

0     256   0     0

0       0    256  0

192 192   0     1

(0.75, 0.75, 1.5)经过转换后即为(384, 384, 384),即容积的边界点.

通过修改世界到容积转换矩阵可以控制重建范围.

2. 创建需要的Fusion图像帧:

  Fusion图像帧储存了需要的数据,类型有:

typedef enum _NUI_FUSION_IMAGE_TYPE    {        NUI_FUSION_IMAGE_TYPE_INVALID= 0,        NUI_FUSION_IMAGE_TYPE_COLOR= 1,        NUI_FUSION_IMAGE_TYPE_FLOAT= 2,        NUI_FUSION_IMAGE_TYPE_POINT_CLOUD= 3    } NUI_FUSION_IMAGE_TYPE;

即色彩、浮点与点云。

我们需要将收集的深度数据浮点化,再平滑化(可选),计算出点云后,输出表面(可选)与法线图像(可选)。

即需要5张Fusion图像帧

    // 创建浮点深度帧    if (SUCCEEDED(hr)){        hr = NuiFusionCreateImageFrame(            NUI_FUSION_IMAGE_TYPE_FLOAT,             m_cDepthWidth,             m_cDepthHeight,             nullptr,            &m_pDepthFloatImage            );    }    // 创建平滑浮点深度帧    if (SUCCEEDED(hr)){        hr = NuiFusionCreateImageFrame(            NUI_FUSION_IMAGE_TYPE_FLOAT,             m_cDepthWidth,             m_cDepthHeight,            nullptr,            &m_pSmoothDepthFloatImage            );    }    // 创建点云帧    if (SUCCEEDED(hr)){        hr = NuiFusionCreateImageFrame(            NUI_FUSION_IMAGE_TYPE_POINT_CLOUD,            m_cDepthWidth,            m_cDepthHeight,            nullptr,            &m_pPointCloud            );    }    // 创建Fusion图像帧    if (SUCCEEDED(hr)){        hr = NuiFusionCreateImageFrame(            NUI_FUSION_IMAGE_TYPE_COLOR,            m_cDepthWidth,            m_cDepthHeight,            nullptr,            &m_pSurfaceImageFrame            );    }    // 创建Fusion法线帧    if (SUCCEEDED(hr)){        hr = NuiFusionCreateImageFrame(            NUI_FUSION_IMAGE_TYPE_COLOR,            m_cDepthWidth,            m_cDepthHeight,            nullptr,            &m_pNormalImageFrame            );    }

这样初始化基本完成了,不过需要重置一下:

INuiFusionReconstruction::ResetReconstruction
参数一: 新的目标 世界到相机 坐标转换矩阵

参数二: 新的目标 世界到容积 坐标转换矩阵

好了,初始化完成:


3. Fusion处理

Fusion是基于深度帧的,所以应该在深度图像中处理,

获取到深度数据后(前略):

转换为浮点型深度

INuiFusionReconstruction::DepthToDepthFloatFrame


之前说了,可以平滑化数据:

INuiFusionReconstruction::SmoothDepthFloatFrame


接下来处理该帧

INuiFusionReconstruction::ProcessFrame

这是一个较为高级的方法, 原文说了:

This is usually the camera pose result from the most recent call to the AlignPointClouds or AlignDepthFloatToReconstruction method.

可以调用这两个较低级的方法.


可能会处理错误:

    // 检查错误    if (hr4f == E_NUI_FUSION_TRACKING_ERROR){        m_ImagaRenderer.error_info = L"Fusion跟踪失败, 请保证目标是静态的,\n请常试重建(单击窗口)";    }    else if(SUCCEEDED(hr)){        m_ImagaRenderer.error_info = L"Fusion跟踪正常";    }    else{        m_ImagaRenderer.error_info = L"Fusion跟踪失败";    }


计算出点云帧:

INuiFusionReconstruction::CalculatePointCloud


为点云帧着色(Shade)

NuiFusionShadePointCloud

输出表面与法线, 这将是我们显示的图像.

值得注意的是参数三:世界到BGR转换矩阵,

即将坐标XYZ转换为颜色BGR,Kinect中,XY可正可负,Z则一直为正,所以,我们这里

的转换矩阵是可以这样:

        Matrix4 worldToBGRTransform = { 0.0f };        worldToBGRTransform.M11 = m_reconstructionParams.voxelsPerMeter / m_reconstructionParams.voxelCountX;        worldToBGRTransform.M22 = m_reconstructionParams.voxelsPerMeter / m_reconstructionParams.voxelCountY;        worldToBGRTransform.M33 = m_reconstructionParams.voxelsPerMeter / m_reconstructionParams.voxelCountZ;        worldToBGRTransform.M41 = 0.5f;        worldToBGRTransform.M42 = 0.5f;        worldToBGRTransform.M43 = 0.0f;        worldToBGRTransform.M44 = 1.0f;


我们还顺带计算了各个方法的耗时, 响应WM_LBUTTONUP消息以重建容积等等.

这就是成果图了:


可以看出各个都是吃时间的大东西。。。


这节就是说是基本的Fusion,代码下载地址:点击这里



0 0
原创粉丝点击