虹軟人臉識別 - 人臉特征數據的存取


虹軟人臉識別 - 人臉特征數據的存取

一、簡介

人臉識別在社會中應用越來越多,提供人臉識別的 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("           "\n"
    "    id      INTEGER PRIMARY KEY AUTOINCREMENT," "\n"
    "    name    VARCHAR(32),"                       "\n"
    "    feature BLOB"                               "\n"
    ");"
);
  • 使用 MySQL

建表

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

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. 識別人臉

點擊識別按鈕,選取一張人臉的圖片。

效果圖:

識別效果圖

綠色框表示識別成功,識別出來的結果在框內左下角。
紅色框表示識別失敗,可能是沒有注冊,或人臉相似度低於閾值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM