微軟在雲端服務中推出了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]
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
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中的人了
人臉辨識的用途可以說是越來越廣泛了,透過這樣的機制,未來即使要作到臉部辨識進行上下班的打卡,或是簡易的門禁系統也就不會是難事了
參考資料
Face API - V1.0