(转载请注明出处)
使用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
#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,代码下载地址:点击这里