(转载请注明出处)
使用SDK: Kinect for Windows SDK v2.0 public preview1409
同前面,因为SDK未完成,不附上函数/方法/接口的超链接。
这次让我们让面部捕捉更加稳定/精确吧!
自从一直8月有了高清面部帧的例子后,觉得IFaceModel::GetFaceShapeDeformations它不干正事,一直返回0.0f的数据。
与其他开发者简单交流后,发现了问题的根本所在,需要面部模型构建器(IFaceModelBuilder)。
好了,那么这是开始。还记得上节的CreateFaceModel函数没有?这个函数需要传递一个数组,称为
FaceShapeDeformations数组,在这里简单翻译为面形吧。这个储存了面部的特征点的模拟数据,可能的数据
比如说鼻子的高度模拟值,详细的还需官方SDK更新,现在啥也没有。
那么,如果已经储存了面形信息,CreateFaceModel时直接导入即可。没有就需要捕捉并构建了。
于是使用IFaceModelBuilder吧!
IHighDefinitionFaceFrameSource::OpenModelBuilder打开面部模型构建器
参数1: 构建属性:
enum _FaceModelBuilderAttributes { FaceModelBuilderAttributes_None = 0, FaceModelBuilderAttributes_SkinColor = 0x1, FaceModelBuilderAttributes_HairColor = 0x2 } ;
比较简单,需要肤色那就0x1,需要发色就0x2,都需要就做按位或运算。我们这里仅需面形数据信息,就
FaceModelBuilderAttributes_None即可.
对了,我们这次需要支持简单的数据导入,所以可能会这样:
<span style="font-size:14px;"> // 创建高清面部帧源 if (SUCCEEDED(hr)){ hr = CreateHighDefinitionFaceFrameSource(m_pKinect, &m_pHDFaceFrameSource); } // 创建面部模型构建器 之前常试从文件中读取数据 if (SUCCEEDED(hr) && !read_fsdfile_data()){ hr = m_pHDFaceFrameSource->OpenModelBuilder(FaceModelBuilderAttributes_None, &m_pFaceModelBuilder); } // 开始数据收集 if (SUCCEEDED(hr) && m_pFaceModelBuilder){ hr = m_pFaceModelBuilder->BeginFaceDataCollection(); }</span>
IFaceModelBuilder::BeginFaceDataCollection
开始数据搜集
也是简单易懂, read_fsdfile_data()方法常试读取数据。
我是这样实现的,果然IO还是纯C好点
<span style="font-size:14px;">// 常试FSD读取文件 bool ThisApp::read_fsdfile_data(){ FILE* file = nullptr;; size_t ok = reinterpret_cast<size_t>(m_pFSDFileName); // 打开文件 if (ok){ file = _wfopen(m_pFSDFileName, L"rb"); size_t ok = reinterpret_cast<size_t>(file); } // 读取信息 if (ok){ auto length = fread(m_ImagaRenderer.data.sd, 1, lengthof(m_ImagaRenderer.data.sd), file); ok = length == lengthof(m_ImagaRenderer.data.sd); } // 设置已获取 if (ok){ m_bProduced = TRUE; m_ImagaRenderer.data.co_status = FaceModelBuilderCollectionStatus_Complete; } // 关闭文件 if (file){ fclose(file); #ifdef _DEBUG file = nullptr; #endif } return ok != 0; }</span>
IFaceModelBuilder::get_CollectionStatus
获取搜集状态,搜集状态有
<span style="font-size:14px;">enum _FaceModelBuilderCollectionStatus { FaceModelBuilderCollectionStatus_Complete = 0, FaceModelBuilderCollectionStatus_MoreFramesNeeded = 0x1, FaceModelBuilderCollectionStatus_FrontViewFramesNeeded = 0x2, FaceModelBuilderCollectionStatus_LeftViewsNeeded = 0x4, FaceModelBuilderCollectionStatus_RightViewsNeeded = 0x8, FaceModelBuilderCollectionStatus_TiltedUpViewsNeeded = 0x10 } ;</span>
FaceModelBuilderCollectionStatus_Complete
面部信息搜集完毕,状态就是这个
FaceModelBuilderCollectionStatus_MoreFramesNeeded
还需要其他位置信息,接下来4个状态存在任意一个状态,该状态置为1
FaceModelBuilderCollectionStatus_FrontViewFramesNeeded
需要正面帧数据,面部朝着Kinect即可
FaceModelBuilderCollectionStatus_LeftViewsNeeded
需要面向左边(几乎90度)
FaceModelBuilderCollectionStatus_RightViewsNeeded
需要面形右边(几乎90度)
FaceModelBuilderCollectionStatus_TiltedUpViewsNeeded
需要斜向上(目测需要45度),但是笔者的Kinect置于高处,这个需要90度抬头,简直
还需要值得提到的是,有耳机最好摘掉,会提高收集成功的概率
错别字就不要在意了,代码已经上传了,改也没有意义了.
IFaceModelBuilder::get_CaptureStatus
获取采集状态,就是提示用户的姿势是怎样不对的,基本不动就没事
enum _FaceModelBuilderCaptureStatus { FaceModelBuilderCaptureStatus_GoodFrameCapture = 0, FaceModelBuilderCaptureStatus_OtherViewsNeeded = 1, FaceModelBuilderCaptureStatus_LostFaceTrack = 2, FaceModelBuilderCaptureStatus_FaceTooFar = 3, FaceModelBuilderCaptureStatus_FaceTooNear = 4, FaceModelBuilderCaptureStatus_MovingTooFast = 5, FaceModelBuilderCaptureStatus_SystemError = 6 } ;
这个不多说
采集成功后,使用IFaceModelBuilder::GetFaceData可以获取IFaceModelData,
居然不是小写的,微软在想什么╮( ̄▽ ̄)╭
获取之后IFaceModelData::ProduceFaceModel来生成面部模型,记得释放之前的模型哟
代码差不多就是这样,请注意,我试验后发现构建IFaceModelData::ProduceFaceModel模型需要几秒钟,
简直影响用户体验,所以建议保留数据,下次
// 检查面部模型构建器 if (SUCCEEDED(hr) && !m_bProduced){ IFaceModelData* pFaceModelData = nullptr; // 检查收集状态 hr = m_pFaceModelBuilder->get_CollectionStatus(&m_ImagaRenderer.data.co_status); // 检查采集状态 if (SUCCEEDED(hr)){ hr = m_pFaceModelBuilder->get_CaptureStatus(&m_ImagaRenderer.data.ca_status); } // 采集成功 获取数据 if (SUCCEEDED(hr) && m_ImagaRenderer.data.co_status == FaceModelBuilderCollectionStatus_Complete){ hr = m_pFaceModelBuilder->GetFaceData(&pFaceModelData); } // 生成面部模型 if (SUCCEEDED(hr) && pFaceModelData){ SafeRelease(m_pFaceModel); hr = pFaceModelData->ProduceFaceModel(&m_pFaceModel); } // 检查结果 if (SUCCEEDED(hr) && pFaceModelData){ m_bProduced = TRUE; // 顺便输出数据 m_pFaceModel->GetFaceShapeDeformations(lengthof(m_ImagaRenderer.data.sd), m_ImagaRenderer.data.sd); } // 释放掉 SafeRelease(pFaceModelData); }
差不多就这样:
下面再说说采集的信息:
笔者的方法自然简单:
case WM_KEYDOWN: if (wParam == 'S'){ FILE* file = nullptr; BYTE buffer[FaceShapeDeformations_Count * 64]; size_t now_length = 0; if (!_wfopen_s(&file, L"FaceShapeDeformations.txt", L"wb")){ memcpy(buffer, pOurApp->m_ImagaRenderer.data.sd, sizeof(pOurApp->m_ImagaRenderer.data.sd)); char* index = reinterpret_cast<char*>(buffer+sizeof(pOurApp->m_ImagaRenderer.data.sd)); *index = '\r'; ++index; *index = '\n'; ++index; float* data_index = pOurApp->m_ImagaRenderer.data.sd; for (int i = 0; i < FaceShapeDeformations_Count; ++i){ now_length = sprintf(index, "%f\r\n", *data_index); index += now_length; ++data_index; } fwrite(buffer, 1, reinterpret_cast<BYTE*>(index)-buffer, file); } if (file){ fclose(file); file = nullptr; ::SendMessageW(hwnd, WM_CLOSE, 0, 0); } else{ ::MessageBoxW(hwnd, L"储存失败", L"Error", MB_ICONERROR); } } break;
但是简单两次采样的数据大致如下:
可以看出数据波动较大,解决办法只有多次采样抵消了。算法不少,最简单的平均法。
也可以假定服从N(μ, σ^2)的正态分布,利用《概概率论与数理统计》中的方法计算,不多说了(其实是不会)。
调试可以传递一个参数,表示文件的路径:
正式使用更简单了,直接把输出文件拖到exe文件上即可。
范例下载地址:点击这里
这次我学精了,提前上传,不过错别字