Kinect for Windows SDK v2.0 开发笔记 (十三) 高清面部帧(4) 面部模型构建器



(转载请注明出处)

使用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文件上即可。


范例下载地址:点击这里

这次我学精了,提前上传,不过错别字




没有更多推荐了,返回首页