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



(转载请注明出处)

使用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将收集的数据慢慢"融合"起来

Kinect for Windows SDK v2.0 开发笔记 (十八) Fusion 基本2D显示_第1张图片


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

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


Fusion工作管线:

Kinect for Windows SDK v2.0 开发笔记 (十八) Fusion 基本2D显示_第3张图片



需要的额外文件:

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

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

复制过来即可,头文件与库文件:
#include
#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是基于深度帧的,所以应该在深度图像中处理,

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

Kinect for Windows SDK v2.0 开发笔记 (十八) Fusion 基本2D显示_第4张图片

转换为浮点型深度

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消息以重建容积等等.

这就是成果图了:

Kinect for Windows SDK v2.0 开发笔记 (十八) Fusion 基本2D显示_第5张图片

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


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



你可能感兴趣的:(C++,Direct2D,Kinect,Fusion,Kinect,C++,Direct2D,Kinect,for,Windows,SDK,v2.0,开发笔记)