zoukankan      html  css  js  c++  java
  • 虹软人脸识别

    虹软人脸识别 - 人脸特征数据的存取

    一、简介

    人脸识别在社会中应用越来越多,提供人脸识别的 API 的公司也很多,如百度、商汤、Face++、虹软、微软等。在简单了解了这些不同企业提供的产品后,发现只有虹软是提供免费离线 SDK 的。使用在线 API,因为网络延迟实时性跟不上。刚开始用的时候还是 2.0 版本,现在已经 3.0 了,实测效果确实不错。在 3.0 后还可以在视频中追踪人脸,避免后重复识别提高了性能。

    在网上关于 ArcSoft人脸识别 SDK --- ArcFace 的开发教程已经很多了,而且 SDK 自带的官方文档也非常简单易懂,就不再重复介绍了。本文的主要内容是怎么使用 SDK 并结合数据库(可选 SQLite 和 MySQL)来保存人脸特征数据以及怎么使用这些特征,中间还包含了和人脸特征相关的部分 API 的使用。

    在本文末提供了使用 ArcFace、Qt 编写的示例代码。

    二、数据库应用

    将数据库的操作封装为一个类,下面介绍封装类的具体实现。

    1. 连接数据库

    使用封装好的数据库对象连接数据库,具体的细节如下:

    • 使用 SQLite

    连接数据库的接口(构造函数)

    DatabaseSQLite(QString const & database_name);
    

    实现

    database_ = QSqlDatabase::addDatabase("QSQLITE", database_name);
    database_.setDatabaseName(database_name);
    database_.open();
    
    • 使用 MySQL

    连接数据库的接口(构造函数)

    DatabaseMySQL::DatabaseMySQL(
        QString const & host_name,
        QString const & user_name,
        QString const & password,
        QString const & database_name
    );
    

    实现

    database_ = QSqlDatabase::addDatabase("QMYSQL", database_name);
    database_.setHostName(host_name);
    database_.setUserName(user_name);
    database_.setPassword(password);
    database_.setDatabaseName(database_name);
    database_.open();
    

    2. 建表

    在连接上数据库后,如果数据库中不存在相应的表结构,需要立即创建相应的表来存放数据。

    • 使用 SQLite

    建表

    auto query = QSqlQuery(database_);
    query.exec(
        "CREATE TABLE IF NOT EXISTS features("           "
    "
        "    id      INTEGER PRIMARY KEY AUTOINCREMENT," "
    "
        "    name    VARCHAR(32),"                       "
    "
        "    feature BLOB"                               "
    "
        ");"
    );
    
    • 使用 MySQL

    建表

    auto query = QSqlQuery(database_);
    query.exec(
        "CREATE TABLE IF NOT EXISTS features("            "
    "
        "    id      INTEGER PRIMARY KEY AUTO_INCREMENT," "
    "
        "    name    VARCHAR(32),"                        "
    "
        "    feature BLOB"                                "
    "
        ");"
    );
    

    SQLite 和 MySQL 的建表操作区别如下:

    SQLite MySQL
    AUTOINCREMENT AUTO_INCREMENT

    存放特征值的字段 feature 使用 BLOB,因为 ArcFace SDK 提取的特征是一串定长的二进制数据(目前 3.0 为 1032 字节)。
    这里的表结构很简单,实际中可以根据业务需要扩展表结构。

    3. 注册人脸并保存其特征值到数据库

    注册流程图

    将人名(文件名)和人脸特征值(通过 ArcFace SDK 获取)绑定加入到数据库和内存缓存。

    获取人脸特征值的相关代码

    // 读取本地文件并转为 B8G8R8 的格式
    QImage image = load(filename);
    
    // 将图片数据转换为 ArcFace SDK 图像的接口(这里用的使新版的接口)
    auto asf_image = ASVLOFFSCREEN();
    asf_image.u32PixelArrayFormat = ASVL_PAF_RGB24_B8G8R8;
    asf_image.i32Width = image.width();
    asf_image.i32Height = image.height();
    asf_image.pi32Pitch[0] = 3 * image.width();
    asf_image.ppu8Plane[0] = const_cast<uint8_t *>(image.bits());
    
    auto faces_info = ASF_MultiFaceInfo();
    
    // 检测人脸,检测到的人脸位置信息存放在 faces_info
    ASFDetectFacesEx(handle, &asf_image, &faces_info);
    
    if (faces_info.faceNum == 0){ return; }
    
    // 仅处理第一个人脸(因为文件名只有一个,无法和多个人脸对应)
    auto face_info = ASF_SingleFaceInfo();
    face_info.faceRect = faces_info.faceRect[0];
    face_info.faceOrient = faces_info.faceOrient[0];
    
    auto asf_feature = ASF_FaceFeature();
    
    // 提取特征到 asf_feature
    ASFFaceFeatureExtractEx(handle, &asf_image, &face_info, &asf_feature);
    
    // 如果提取处理的特征值数据还在 ArcFace SDK 中,且下次再提取会被覆盖,所以对特征值数据进行复制
    auto feature = std::vector<uint8_t>(static_cast<size_t>(asf_feat.featureSize));
    std::copy_n(asf_feat.feature, asf_feat.featureSize, feature.begin());
    
    // 添加到本地数据库和内存缓存,具体细节在下面详细说明
    database_->add(QFileInfo(filename).baseName(), std::move(feature));
    

    保存单人脸特征到数据库的接口

    auto add(
        QString name,
        Feature feature
    ) -> bool;
    

    实现

    这里 SQLite 和 MySQL 的操作是一样的。

    先将数据插入到数据库:

    auto query = QSqlQuery(database_);
    query.prepare(
        "INSERT INTO features(name, feature)VALUES(:name, :feature);"
    );
    
    auto feature_bytes = QByteArray(
        reinterpret_cast<char *>(&feature[0]),
        static_cast<int>(feature.size())
    );
    
    query.bindValue(":name", name);
    query.bindValue(":feature", feature_bytes);
    query.exec();
    

    在实际开发过程中,可能会同时插入多条人脸特征,这时使用事务可以提升性能。

    在数据库插入成功后再把数据复制到内存中数据库数据的副本中,保证内存中人脸特征数据库和数据库中的一致:

    features_.emplace_back(std::move(feature), std::move(name));
    

    4. 获取人脸特征数据库进行人脸识别

    该模块 SQLite 和 MySQL 的操作是一样的。

    识别流程图

    在程序启动时,将数据库中的人脸特征预加载到缓存中。

    加载数据到缓存的实现

    auto query = QSqlQuery(database_);
    query.exec(
        "SELECT name, feature FROM features"
    );
    while (query.next())
    {
        auto name = query.value(QStringLiteral(u"name")).toString();
        auto feature = query.value(QStringLiteral(u"feature")).toByteArray();
        features_.emplace_back(
            Feature(feature.cbegin(), feature.cend()),
            std::move(name)
        );
    }
    

    上面代码中 features_ 的类型是 std::vector<std::pair<std::vector<uint8_t>, QString>>,可根据具体的需求调整。

    人脸比对的实现

    // 读取本地文件并转为 B8G8R8 的格式
    QImage image = load(filename);
    
    // 将 QImage 图像数据转换为 ASVLOFFSCREEN(3.0 的新接口)
    auto asf_image = ASVLOFFSCREEN();
    asf_image.u32PixelArrayFormat = ASVL_PAF_RGB24_B8G8R8;
    asf_image.i32Width = image.width();
    asf_image.i32Height = image.height();
    asf_image.pi32Pitch[0] = 3 * image.width();
    asf_image.ppu8Plane[0] = const_cast<uint8_t *>(image.bits());
    
    auto faces_info = ASF_MultiFaceInfo();
    
    // 检测人脸,检测到的人脸信息存放在 faces_info
    ASFDetectFacesEx(handle, &asf_image, &faces_info);
    
    if (faces_info.faceNum == 0){ return; }
    
    // 相似度最高且高于阈值就认为是同一个人
    constexpr auto threshold = 0.8f;
    
    for (auto i = 0; i != faces_info.faceNum; ++i)
    {
        auto face_info = ASF_SingleFaceInfo();
        face_info.faceRect = faces_info.faceRect[i];
        face_info.faceOrient = faces_info.faceOrient[i];
    
        auto asf_feat = ASF_FaceFeature();
        ASFFaceFeatureExtractEx(handle, &asf_image, &face_info, &asf_feat);
    
        auto name = QString("?");
        auto max_similarity = 0.0f;
    
        // database_->features() 得到的是人脸特征数据库在内存中的缓存
        // 类型是 std::vector<std::pair<std::vector<uint8_t>, QString>>
        for (auto const & feat_name: database_->features())
        {
            auto asf_feat2 = ASF_FaceFeature();
            asf_feat2.feature = feat_name.first.data();
            asf_feat2.featureSize = static_cast<int>(feat_name.first.size());
    
            auto similarity = 0.0f;
            ASFFaceFeatureCompare(face_engine_, &asf_feat, &asf_feat2, &similarity);
    
            if (threshold <= similarity && max_similarity < similarity)
            {
                name = feat_name.second;
                max_similarity = similarity;
            }
        }
    
        // 这里有个绘制人脸框的操作,因为与识别逻辑无关所以没有给出代码
    }
    

    三、工程配置

    1. 编译前准备

    • profile.ini 文件中填好在官网下载的 ArcFace SDK 的 APP_IDSDK_KEY

    注意,因为使用的是相对路径,使用不同的启动方式文件放置的路径不同:
    如果使用 Visual Studio,那么这个文件应该放到项目根目录下;
    如果使用 Qt Creator,那个这个文件应该放到构建的二进制目录的父目录下(即 build- 开头的目录下);
    如果是直接双击运行,那么这个文件应该放到程序所在目录的同级目录下。

    2. 依赖说明

    • ArcFace SDK: 3.0。
    • Qt: 5.12.0。
      • 不同的版本 Qt 可能没有内置 MySQL 的动态库。

    如果需要查看数据表,可以使用 Sqlite Expert。

    示例代码下载路径(https://github.com/tz-byte/arcface-with-database)

    四、功能界面展示

    1. 主界面预览

    界面

    2. 注册人脸

    点击注册按钮,选取一张人脸图片,仅取第一张人脸进行特征提取,将文件名和人脸特征绑定存入数据库。

    具体使用的是 SQLite 还是 MySQL 数据库,请在 widget.cpp 文件中搜索 database_.reset(new,默认是 SQLite。

    3. 识别人脸

    点击识别按钮,选取一张人脸的图片。

    效果图:

    识别效果图

    绿色框表示识别成功,识别出来的结果在框内左下角。
    红色框表示识别失败,可能是没有注册,或人脸相似度低于阈值。

  • 相关阅读:
    企业微信应用授权
    exec存储过程示例
    jquery判断对象是否存在
    IScroll5要防止重复加载
    transitionEnd不起作用解决方法
    微信接口 output {"errMsg":"translateVoice:fail, the permission value is offline verifying"}
    javascript保留两位小数
    html取消回车刷新提交
    企业微信后台登录
    企业微信开启开发者工具
  • 原文地址:https://www.cnblogs.com/feishixin123/p/12794418.html
Copyright © 2011-2022 走看看