zoukankan      html  css  js  c++  java
  • BFM模型介绍及可视化实现(C++)

    BFM模型介绍及可视化实现(C++)

    BFM模型基本介绍

    Basel Face Model是一个开源的人脸数据库,其基本原理是3DMM,因此其便是在PCA的基础上进行存储的。
    目前有两个版本的数据库(2009和2017)。
    官方网站:20092017

    数据内容(以2009版本为例)

    文件内容

    01_MorphableModel.mat(数据主体)

    BFM模型由53490个顶点构成,其shape/texture的数据长度为160470(53490*3),因为其排列方式如下:

    shape: x_1, y_1, z_1, x_2, y_2, z_2, ..., x_{53490}, y_{53490}, z_{53490}
    
    texture: r_1, g_1, b_1, r_2, g_2, b_2, ..., r_{53490}, g_{53490}, b_{53490}
    

    .h5文件与.mat文件对应关系

    [注] .h5文件中的tl数量与.mat数量不同,主成分方差的值也不同,且shape的值是.mat中shape值的0.001倍(见/shape/representer/length-unit)。

    Matlab脚本

    建议阅读script_gen_random_head.m文件,该脚本实现了如何生成随机脸,从中我们可以学习到BFM模型的使用方法。

    2009与2017版本区别

    2009年版本数据集:

    • 提供数据格式:mat(01_MorphableModel.mat)和h5(model2009-publicmm1-bfm.h5);
    • 提供一系列Matlab脚本,有生成随机脸等功能;
    • 提供多种特征点(PublicMM1/11_feature_points);
    • 提供segment的mask(PublicMM1/09_mask);
    • 提供对称点的对应关系(PublicMM1/13_symmetry_indices);
    • 提供属性(PublicMM1/04_attributes.mat
    • 不提供表情;

    2017年版本数据集:

    • 提供数据格式:h5(原版(model2017-1_bfm_nomouth.h5)和裁剪过的版本(model2017-1_face12_nomouth.h5));
    • 不提供Matlab脚本(本身也无mat格式数据);
    • 提供单种特征点(metadata/landmarks/text);
    • 不提供segment、对称点的对应关系和属性;
    • 提供表情(expression);

    基本原理

    目标shape或者texture都可以通过如下式子得到:

    obj = average + pc * (coeficient .* pcVariance)
    

    其中系数(coeficient)是变量,其余均是数据库里的常量,其是一个199维(对应199个PC)的向量。

    C++实现BFM模型可视工具

    数据读取

    我们可以读取.mat文件或者.h文件,因为读取.mat文件需要使用Matlab的库文件,我们暂时不考虑。

    读取.h5格式文件

    .h5文件无法直接通过文本工具打开,需要下载专门的可视工具,此处我使用了HDFView

    通过该文件我们可以了解到HDF5文件的内部格式。
    在C++中使用HDF5读写需要下载官方的库:
    HDF5库下载地址
    官网右上角注册后下载,随后选择对应版本下载。
    [注] 在Windows的Visual Studio使用shared库需要编译过程定义H5_BUILT_AS_DYNAMIC_LIB。(若出现LINK2001错误可以添加这个来解决)
    [注] static库命名前面以lib开头,例如hdf5.lib是shared库,libhdf5.lib是static库。
    在VS的包含目录和库目录中添加对应的inlcude和lib目录。
    在链接器的输入中增加szip.lib;zlib.lib;hdf5.lib;hdf5_cpp.lib;,并将对应的.dll文件放置到Windows/System32Windows/SysWOW64

    我们只需要用到HDF5中的读取功能,步骤是打开文件->打开数据库->读取数据->关闭数据库->关闭文件。我们以shape平均值为例:

    float *shape_mu_raw = new float[N_VERTICE * 3];
    H5File file(bfm_h5_path, H5F_ACC_RDONLY);
    DataSet shape_mu_data = file.openDataSet("/shape/model/mean"); 
    shape_mu_data.read(shape_mu_raw, PredType::NATIVE_FLOAT); 
    raw2vector(shape_mu, shape_mu_raw);   // 自行将数组转换成想要存放的格式
    shape_mu_data.close(); 
    file.close();
    

    shape平均值读取后需要再乘以1000才等同于.mat格式的读取。
    需要注意的是数据的读取类型一定要根数据库中的类型一致。shape/tex的类型均为float,对应PredType::NATIVE_FLOAT,tl的类型为unsigned int,对应PredType::NATIVE_UINT32
    [注] 因为缺少pdb文件,HDF5中的代码如果报错可能无法进行调试,需要逐行进行错误的排除,常见错误就是类型不匹配或者长度不匹配。

    其他读取方式

    在最开始不了解.h5格式的时候,我便使用一些笨方法进行读取,例如先将.mat格式数据转换成二进制文件/文本文件再进行读取。
    例如这样一个matlab脚本:

    function mat2binary(filename, mat, type)
        fid=fopen(filename, 'wb');
        matrix = mat;                        
        [m,n]=size(matrix);
         for i=1:1:m
           for j=1:1:n
                fwrite(fid, matrix(i,j), type);
           end
        end
        fclose(fid);
    end
    

    这些脚本能够简单地将mat格式进行转换,成为容易被C++进行读取的格式。但是弊端也很明显,在C++中的读写速度非常慢。.h5格式读写1s左右完成,二进制文件读写1分钟左右完成,文本文件读写5分钟左右完成。且在存储大小上,.h5文件(249MB)≈ 二进制文件 < 文本文件(超过710M)。

    生成人脸

    即按照上述基本原理中的式子进行实现。

    OpenGL进行显示

    这里使用了Qt5内置的OpenGL模块,通过最简单的glBegin()glEnd()即可绘出人脸。

    double sint = sin(theta), cost = cos(theta);
    for (auto t = tl.begin(); t != tl.end(); t++) {
           glBegin(GL_TRIANGLES);
           vec3 tmp = *t;
           glColor3f(tex[tmp.x].x / 255.0, tex[tmp.x].y / 255.0, tex[tmp.x].z / 255.0);
           glVertex3f(shape[tmp.x].x * scale * cost - shape[tmp.x].z * scale * sint, shape[tmp.x].y * scale, shape[tmp.x].x * scale * sint + shape[tmp.x].z * scale * cost);
           glColor3f(tex[tmp.y].x / 255.0, tex[tmp.y].y / 255.0, tex[tmp.y].z / 255.0);
           glVertex3f(shape[tmp.y].x * scale * cost - shape[tmp.y].z * scale * sint, shape[tmp.y].y * scale, shape[tmp.y].x * scale * sint + shape[tmp.y].z * scale * cost);
           glColor3f(tex[tmp.z].x / 255.0, tex[tmp.z].y / 255.0, tex[tmp.z].z / 255.0);
           glVertex3f(shape[tmp.z].x * scale * cost - shape[tmp.z].z * scale * sint, shape[tmp.z].y * scale, shape[tmp.z].x * scale * sint + shape[tmp.z].z * scale * cost);
           glEnd();
    }
    

    使用thetascale参数用于实现鼠标和键盘对模型方向的控制。
    根据模型大小,我们设置相应的视角:

    void OpenGLWidget::resizeGL(int width, int height) {
            glViewport(0, 0, width, height);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            gluPerspective(60.0, (GLfloat)width / (GLfloat)height, 1.0, 600000.0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            gluLookAt(0, 0, 300000.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    }
    

    结果展示

    初始界面(左侧显示一个彩色三角形):

    当随机性设置为0(即coeficient设为[0, ..., 0]),生成平均脸:

    随机生成人脸,或随机设置PC值:

    源代码

    GitHub:https://github.com/Great-Keith/bfm-visual-tool

  • 相关阅读:
    【转】win8.1下安装ubuntu
    Codeforces 1025G Company Acquisitions (概率期望)
    Codeforces 997D Cycles in Product (点分治、DP计数)
    Codeforces 997E Good Subsegments (线段树)
    Codeforces 1188E Problem from Red Panda (计数)
    Codeforces 1284E New Year and Castle Building (计算几何)
    Codeforces 1322D Reality Show (DP)
    AtCoder AGC043C Giant Graph (图论、SG函数、FWT)
    Codeforces 1305F Kuroni and the Punishment (随机化)
    AtCoder AGC022E Median Replace (字符串、自动机、贪心、计数)
  • 原文地址:https://www.cnblogs.com/bemfoo/p/11788638.html
Copyright © 2011-2022 走看看