[Cognitive] 使用Azure Cognitive Service的Face API進行清單中的人員辨識

微軟在雲端服務中推出了Cognitive Server,其中包含了Face API這個服務功能
主要的效用就是可以透過照片進行人臉的比對與辨識

在本篇文章中,說明了如何建立一個人員清單,像是部門人員或是公司員工的清單,並在這個清單中加入每個人的照片
最後,透過上傳的照片畫面,找出照片中的人是否存在於這個清單中

要建立清單的人員比對功能,會用到蠻多Face API中的功能,不過若是要自己寫Call API的程式碼會太過麻煩,所以我是直接透過nuget加入套件來使用
由於整個功能程式碼的建置步驟較多,所以這篇文章中會以主要的功能進行說明,完整的實作就留待大家自行進行程式碼的撰寫

在開始前,必須要先說明一下在Face API中一些名詞之間的關係從上圖可以看到,在一個PersonGroup中,可以加入多個Person,而一個Person又可以加入多個Face
換句話說,PersonGroup也就是前面所提到的人員清單,裡面的Person就是每一個人員,而每個人又可以提供不同角度的照片作為辨識的依據

1.首先建立一個Azure Face API的服務,在Azure上建立新的服務,並選擇[Cognitive Services APIs] => [Face API]

每一個帳戶只能建立一個免費的Face API

2.在建立完成的Face API中,點選[Key],並將金鑰值記下來,在程式中會使用到

接下來,就開始進行程式的製作

1.建立新的Windows Form專案,並在專案中加入[Microsoft.ProjectOxford.Face]這個套件

2.在畫面中加上幾個控制項,這個畫面主要是用來編輯PersonGroup的清單內容

3.下面的程式碼,分別為新增、刪除以及修改PersonGroup的動作

using Microsoft.ProjectOxford.Face;
using Microsoft.ProjectOxford.Face.Contract;

FaceServiceClient face;

private void frmUsers_Load(object sender, EventArgs e)
{
    face = new FaceServiceClient([Face API Key]);
    this.BindPersonGroups();
}

/// <summary>
/// 加入人員群組的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnAddPersonGroup_Click(object sender, EventArgs e)
{
    await face.CreatePersonGroupAsync([PersonGroup的Id], [PersonGroup的名稱]);
}

/// <summary>
/// 刪除人員群組的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnDeletePersonGroup_Click(object sender, EventArgs e)
{
    await face.DeletePersonGroupAsync([PersonGroup的Id]);
}

/// <summary>
/// 針對人員群組進行訓練的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnPersonGroupTrain_Click(object sender, EventArgs e)
{
    await face.TrainPersonGroupAsync([PersonGroup的Id]);
}

/// <summary>
/// 放入人員群組的動作
/// </summary>
private async void BindPersonGroups()
{
    PersonGroup[] objPersonGroups = await face.ListPersonGroupsAsync();
    for (int i=0; i<objPersonGroups.Length; i++)
    {
        // 依序找出目前的PersonGroup
    }
}

從上面的程式碼可以看到,可以透過[Microsoft.ProjectOxford.Face]這個套件中的FaceServiceClient.ListPersonGroupAsync,取得目前的PersonGroup
而要新增PersonGroup的話,則是可以透過FaceServiceClient.CreatePersonGroupAsync來執行新增的動作,當然刪除的話,就是執行FaceServiceClient.DeletePersonGroupAsync

當PersonGroup中的Person與Face進行修改之後,一定要記得進行TrainPersonGroupAsync的動作進行訓練,不然後面的比對動作會比對不出結果

4.接著在畫面中,一樣加入幾個控制項,這些控制項主要是用來維護PersonGroup中的Person資料的動作在這裡我多加了一個[Face]的按鈕,主要目的是當點選了Person的項目後再點選[Face]的按鈕,可以進行人​員人臉照片的維護

5.接著加入下面的程式碼內容

/// <summary>
/// 加入人員的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnAddPerson_Click(object sender, EventArgs e)
{
    await face.CreatePersonAsync([PersonGroup的Id], [人員姓名]);
    this.BindPersons();
}

/// <summary>
/// 刪除人員的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnDeletePerson_Click(object sender, EventArgs e)
{
    await face.DeletePersonAsync([PersonGroup的Id], Guid.Parse([Person的Id]));
    this.BindPersons();
}

/// <summary>
/// 放入人員清單的動作
/// </summary>
private async void BindPersons()
{
    Person[] objPersons = await face.GetPersonsAsync([PersonGroup的Id]);
    for (int i = 0; i < objPersons.Length; i++)
    {
        // 依序取得在這個PersonGroup裡每一個Person的資料
    }
}

這部份的程式碼,則是說明了,若是要建立一個新的Person到PersonGroup中,可以使用FaceServiceClient.CreatePersonAsync進行新增,查詢的動作則是使用FaceServiceClient.GetPersonsAsync,刪除可以使用FaceServiceClient.DeletePersonAsync
在這裡要注意的是,不論是取得資料,建立或是刪除Person,都會需要帶入PersonGroup的Id才能進行動作

6.接著再建立一個新表單,放上幾個控制項,這個畫面是用來維護人員臉部照片的畫面

7.接著將下面的程式碼加入到程式之中

/// <summary>
/// 加入人臉的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnAddFace_Click(object sender, EventArgs e)
{
    byte[] data = File.ReadAllBytes("[照片檔案路徑]");
    MemoryStream stream = new MemoryStream(data);
    AddPersistedFaceResult result = await face.AddPersonFaceAsync([PersonGroup的Id], Guid.Parse([Person的Id]), stream);
    // resule可以取得此次上傳臉部照片的FaceId
}

/// <summary>
/// 刪除人臉的動作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnDeleteFace_Click(object sender, EventArgs e)
{
    await face.DeletePersonFaceAsync([PersonGroup的Id], Guid.Parse([Person的Id]), Guid.Parse([Face的Id]));
}

/// <summary>
/// 放入人員人臉照片的動作
/// </summary>
private async void BindFace()
{
    Person objPer = await face.GetPersonAsync([PersonGroup的Id], Guid.Parse([Person的Id]));
    for (int i = 0; i < objPer.PersistedFaceIds.Length; i++)
    {
        // 依序取得Person的Face清單
    }
}

上面的程式碼說明了,若是要在指定的Person中加入臉部照片,可以透過FaceServiceClient.AddPersonFaceAsync的方法,並帶入照片的二進位檔,就可以進行照片上傳與取得該臉部照片的Guid值
若是要刪除,則是可以使用FaceServiceClient.DeletePersonFaceAsync,要取得指定Person的所有Face清單,則是可以透過FaceServiceClient.GetPersonAsync來進行

到這裡,我們已經將PersonGroup、Person、Face的維護功能作完了,接著就是上傳照片來進行比對,比對的功能一共會需要兩個動作,先Detect照片中的人臉,再Identify這個人臉屬於PersonGroup中哪一個Person

很重要,所以要重覆講三次

Detect照片中的人臉,再Identify這個人臉屬於PersonGroup中哪一個Person
Detect照片中的人臉,再Identify這個人臉屬於PersonGroup中哪一個Person
Detect照片中的人臉,再Identify這個人臉屬於PersonGroup中哪一個Person

8.要進行比對的動作,只要透過下面的程式碼就可以完成

// 取得照片,並將該照片放入至MemoryStream之中
var ms = new MemoryStream();
objImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);

// 呼叫Detect,找出在這張照片中所有的人臉,並回傳照片中人臉的FaceId
Face[] faces = await face.DetectAsync(ms);

// 將照片中的臉,與指定的PersonGroup進行比對
if (faces != null)
{
    // 將這張照片回傳的人臉,取出每一張臉的FaceId並進行轉換成Guid
    Guid[] faceGuids = faces.Select(x => x.FaceId).ToArray();

    if (faceGuids.Length > 0)
    {
        // 透過Identify,找出在這張照片中,所有辨識出的人臉,是否有包含在PersonGroup中的所有人員
        IdentifyResult[] result = await face.IdentifyAsync(strPersonGroup, faceGuids);

        // 取得照片中在PersonGroup裡的人
        for (int i = 0; i < result.Length; i++)
        {
            for (int p = 0; p < result[i].Candidates.Length; p++)
            {
                // 取出的strPersonId,就是在PersonGroup中的PersonId
                string strPersonId = result[i].Candidates[p].PersonId.ToString();
            }
        }
    }
}

上面的程式碼,說明了如何將照片透過DetectAsync辨識出照片中的每一個人臉,再將這些人臉的FaceId透過IdentifyAsync進行與PersonGroup中的所有人臉進行驗證與比對
最後我們就可以得到,這張照片有誰是包含在PersonGroup中的人了

人臉辨識的用途可以說是越來越廣泛了,透過這樣的機制,未來即使要作到臉部辨識進行上下班的打卡,或是簡易的門禁系統也就不會是難事了

前提是:網路不能斷 and 當天上班沒有結屎面

參考資料
Face API - V1.0