PersonDirectory データ構造を使用する (プレビュー)
注意事項
Microsoft の責任ある AI の原則をサポートするために、Face サービスの利用は、適格性と使用基準に基づいて制限されています。 Face サービスは、Microsoft が管理する顧客とパートナーのみが利用できます。 顔認識受付フォームを使用して利用申請を行ってください。 詳細については、「Face の制限付きアクセス」ページを参照してください。
類似の識別や検索といった顔認識操作を実行するために、Face API のお客様は、Person オブジェクトの類別された一覧を作成する必要があります。 PersonDirectory は、ディレクトリに追加された Person の ID ごとに、一意の ID、オプションの名前文字列、オプションのユーザー メタデータ文字列を含むパブリック プレビュー段階のデータ構造です。 PersonDirectory で基本的なタスクを実行する方法については、このガイドに従ってください。
PersonDirectory の利点
Face API にも同様の機能を持つ LargePersonGroup 構造が用意されていますが、100 万件の ID に制限されています。 PersonDirectory 構造は、最大 2,000 万の ID までスケールアップできます。
PersonDirectory と以前のデータ構造とのもう 1 つの違いは、顔を Person オブジェクトに追加した後に Train API の呼び出しを行う必要がないことです。更新プロセスは自動的に実行されます。
前提条件
- Azure サブスクリプション - 無料アカウントを作成します。
- Azure サブスクリプションを入手したら、Azure portal で Face リソースを作成し、キーとエンドポイントを取得します。 デプロイされたら、 [リソースに移動] を選択します。
- 対象のアプリケーションを Face API に接続するには、作成したリソースのキーとエンドポイントが必要です。 下のコードに、自分のキーとエンドポイントを貼り付けます。
- Free 価格レベル (F0) を使用してサービスを試用し、後から運用環境用の有料レベルにアップグレードすることができます。
Persons を PersonDirectory に追加する
Persons は、PersonDirectory の基本登録単位です。 ディレクトリに Person を追加すると、認識モデルあたり最大 248 の顔画像をその Person に追加できます。 その後、さまざまなスコープを使用して、それらに対して顔を識別できます。
Person を作成する
Person を作成するには、CreatePerson API を呼び出して、name または userData プロパティ値を指定する必要があります。
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
var client = new HttpClient();
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
var addPersonUri = "https://{endpoint}/face/v1.0-preview/persons";
HttpResponseMessage response;
// Request body
var body = new Dictionary<string, object>();
body.Add("name", "Example Person");
body.Add("userData", "User defined data");
byte[] byteData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(body));
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PostAsync(addPersonUri, content);
}
CreatePerson 呼び出しは、Person に対して生成された ID と操作の場所を返します。 Person データは非同期で処理されるため、操作の場所を使用して結果をフェッチします。
非同期操作の完了を待機する
進行状況を確認するには、返された操作の場所の文字列を使用して非同期操作の状態を照会する必要があります。
最初に、状態応答を処理するため、次のようなデータ モデルを定義する必要があります。
[Serializable]
public class AsyncStatus
{
[DataMember(Name = "status")]
public string Status { get; set; }
[DataMember(Name = "createdTime")]
public DateTime CreatedTime { get; set; }
[DataMember(Name = "lastActionTime")]
public DateTime? LastActionTime { get; set; }
[DataMember(Name = "finishedTime", EmitDefaultValue = false)]
public DateTime? FinishedTime { get; set; }
[DataMember(Name = "resourceLocation", EmitDefaultValue = false)]
public string ResourceLocation { get; set; }
[DataMember(Name = "message", EmitDefaultValue = false)]
public string Message { get; set; }
}
次に、上記から得た HttpResponseMessage
を使用して、URL をポーリングし、結果を待機できます。
string operationLocation = response.Headers.GetValues("Operation-Location").FirstOrDefault();
Stopwatch s = Stopwatch.StartNew();
string status = "notstarted";
do
{
await Task.Delay(500);
var operationResponseMessage = await client.GetAsync(operationLocation);
var asyncOperationObj = JsonConvert.DeserializeObject<AsyncStatus>(await operationResponseMessage.Content.ReadAsStringAsync());
status = asyncOperationObj.Status;
} while ((status == "running" || status == "notstarted") && s.Elapsed < TimeSpan.FromSeconds(30));
"succeeded" という状態が返されると、Person オブジェクトがディレクトリに追加されたと見なされます。
Note
Person 作成呼び出しの非同期操作は、顔を追加するときは "succeeded" 状態になっていなくてもかまいませんが、Person を DynamicPersonGroup に追加したり (後の「DynamicPersonGroup の作成と更新」を参照)、Identify 呼び出しの間に比較したりするには、完了している必要があります。 顔が Person に正常に追加された直後に呼び出しが機能することを確認します。
顔を Persons に追加する
Person 作成呼び出しから Person ID を取得したら、認識モデルごとに最大 248 の顔画像を Person に追加できます。 各認識モデルのデータは PersonDirectory 内で個別に処理されるため、使用する認識モデル (および必要に応じて検出モデル) を呼び出しで指定します。
現在サポートされている認識モデルは次のとおりです。
Recognition_02
Recognition_03
Recognition_04
また、画像に複数の顔が含まれている場合は、対象の顔を四角形の境界ボックスで指定する必要があります。 次のコードを使用すると、Person オブジェクトに顔が追加されます。
var client = new HttpClient();
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
// Optional query strings for more fine grained face control
var queryString = "userData={userDefinedData}&targetFace={left,top,width,height}&detectionModel={detectionModel}";
var uri = "https://{endpoint}/face/v1.0-preview/persons/{personId}/recognitionModels/{recognitionModel}/persistedFaces?" + queryString;
HttpResponseMessage response;
// Request body
var body = new Dictionary<string, object>();
body.Add("url", "{image url}");
byte[] byteData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(body));
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PostAsync(uri, content);
}
顔追加呼び出しの後、顔データは非同期的に処理されるため、前と同じ方法で操作が成功するまで待機する必要があります。
顔の追加操作が完了すると、Identify 呼び出しでデータを使用できるようになっています。
DynamicPersonGroup の作成と更新
DynamicPersonGroups は、PersonDirectory 内の Person オブジェクトへの参照のコレクションです。これらは、ディレクトリのサブセットを作成するために使用されます。 一般的に使用されるのは、一致すると予想される Person オブジェクトだけにスコープを制限することにより、擬陽性を減らし、Identify 操作の精度を上げる場合です。 実際のユース ケースには、大規模なキャンパスや組織での特定の建物へのアクセスのためのディレクトリが含まれます。 組織ディレクトリには 500 万人の個人が含まれていても、特定のビルについては特定の 800 人だけを検索する必要がある場合、それらの特定の個人を含む DynamicPersonGroup を作成します。
前に PersonGroup を使用したことがある場合は、2 つの大きな違いに注意してください。
- DynamicPersonGroup 内の各 Person は、PersonDirectory 内の実際の Person への参照です。つまり、グループごとに Person を再作成する必要はありません。
- 前のセクションで説明したように、顔データはディレクトリ レベルで自動的に処理されるため、トレーニング呼び出しを行う必要はありません。
グループを作成する
DynamicPersonGroup を作成するには、英数字またはダッシュ文字でグループ ID を指定する必要があります。 この ID は、グループのすべての使用目的に対して一意の識別子として機能します。
グループ コレクションを初期化するには、2 つの方法があります。 最初に空のグループを作成し、後で設定することができます。
var client = new HttpClient();
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
var uri = "https://{endpoint}/face/v1.0-preview/dynamicpersongroups/{dynamicPersonGroupId}";
HttpResponseMessage response;
// Request body
var body = new Dictionary<string, object>();
body.Add("name", "Example DynamicPersonGroup");
body.Add("userData", "User defined data");
byte[] byteData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(body));
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PutAsync(uri, content);
}
このプロセスはすぐに行われるので、非同期操作が成功するまで待つ必要はありません。
または、Person ID のセットを AddPersonIds 引数で提供することにより、最初からそれらの参照を含めることができます。
var client = new HttpClient();
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
var uri = "https://{endpoint}/face/v1.0-preview/dynamicpersongroups/{dynamicPersonGroupId}";
HttpResponseMessage response;
// Request body
var body = new Dictionary<string, object>();
body.Add("name", "Example DynamicPersonGroup");
body.Add("userData", "User defined data");
body.Add("addPersonIds", new List<string>{"{guid1}", "{guid2}", …});
byte[] byteData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(body));
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PutAsync(uri, content);
// Async operation location to query the completion status from
var operationLocation = response.Headers.Get("Operation-Location");
}
Note
呼び出しから戻ったら、任意の Person 参照をプロセスで指定して、作成された DynamicPersonGroup を Identify 呼び出しで使用できるようになっています。 一方、返された操作 ID の完了状態は、動的人物グループ参照を取得する検索呼び出しの更新状態のみを示します。
DynamicPersonGroup を更新する
初期作成の後、Update Dynamic Person Group API を使用して、DynamicPersonGroup の Person 参照を追加および削除できます。 グループに Person オブジェクトを追加するには、addPersonsIds 引数で Person ID のリストを指定します。 Person オブジェクトを削除するには、removePersonIds 引数でそのリストを指定します。 1 回の呼び出しで、追加と削除の両方を実行することができます。
var client = new HttpClient();
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
var uri = "https://{endpoint}/face/v1.0-preview/dynamicpersongroups/{dynamicPersonGroupId}";
HttpResponseMessage response;
// Request body
var body = new Dictionary<string, object>();
body.Add("name", "Example Dynamic Person Group updated");
body.Add("userData", "User defined data updated");
body.Add("addPersonIds", new List<string>{"{guid1}", "{guid2}", …});
body.Add("removePersonIds", new List<string>{"{guid1}", "{guid2}", …});
byte[] byteData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(body));
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PatchAsync(uri, content);
// Async operation location to query the completion status from
var operationLocation = response.Headers.Get("Operation-Location");
}
呼び出しから戻った後、グループを照会すると、コレクションに対する更新が反映されています。 作成 API と同様に、返された操作では、更新に関係するすべての Person に関する人とグループの関係の更新状態が示されています。 グループに対する更新呼び出しをさらに行う前に、操作の完了を待つ必要はありません。
PersonDirectory 内の顔を識別する
PersonDirectory 内の顔データを使用する最も一般的な方法は、登録されている Person オブジェクトを特定の顔と比較し、それが属している可能性が最も高い候補を特定することです。 要求では複数の顔を指定することができ、それぞれについて応答で独自の比較結果セットを受け取ります。
PersonDirectory には、それぞれの顔を識別できる 3 種類のスコープがあります。
シナリオ 1: DynamicPersonGroup に対して識別する
要求で dynamicPersonGroupId プロパティを指定すると、グループ内で参照されているすべての Person に対して顔が比較されます。 1 回の呼び出しでは、1 つの DynamicPersonGroup についてのみ識別できます。
var client = new HttpClient();
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
// Optional query strings for more fine grained face control
var uri = "https://{endpoint}/face/v1.0-preview/identify";
HttpResponseMessage response;
// Request body
var body = new Dictionary<string, object>();
body.Add("faceIds", new List<string>{"{guid1}", "{guid2}", …});
body.Add("dynamicPersonGroupId", "{dynamicPersonGroupIdToIdentifyIn}");
byte[] byteData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(body));
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PostAsync(uri, content);
}
シナリオ 2: 人の特定のリストに対して識別する
personIds プロパティで Person の ID の一覧を指定して、それぞれと顔を比較することもできます。
var client = new HttpClient();
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
var uri = "https://{endpoint}/face/v1.0-preview/identify";
HttpResponseMessage response;
// Request body
var body = new Dictionary<string, object>();
body.Add("faceIds", new List<string>{"{guid1}", "{guid2}", …});
body.Add("personIds", new List<string>{"{guid1}", "{guid2}", …});
byte[] byteData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(body));
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PostAsync(uri, content);
}
シナリオ 3: PersonDirectory 全体を識別する
要求の personIds プロパティで 1 つのアスタリスクを指定すると、PersonDirectory に登録されているすべての Person と顔が比較されます。
var client = new HttpClient();
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
var uri = "https://{endpoint}/face/v1.0-preview/identify";
HttpResponseMessage response;
// Request body
var body = new Dictionary<string, object>();
body.Add("faceIds", new List<string>{"{guid1}", "{guid2}", …});
body.Add("personIds", new List<string>{"*"});
byte[] byteData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(body));
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PostAsync(uri, content);
}
3 つのシナリオすべてについて、識別では、入力された顔と、AddPersonFace の呼び出しで "succeeded" 応答が返された顔とが比較されます。
PersonDirectory 内の人に対して顔を確認する
検出呼び出しから返された顔 ID を使用して、その顔が PersonDirectory 内に登録されている特定の Person に属しているかどうかを確認できます。 personId プロパティを使用して Person を指定します。
var client = new HttpClient();
// Request headers
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "{subscription key}");
var uri = "https://{endpoint}/face/v1.0-preview/verify";
HttpResponseMessage response;
// Request body
var body = new Dictionary<string, object>();
body.Add("faceId", "{guid1}");
body.Add("personId", "{guid1}");
byte[] byteData = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(body));
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response = await client.PostAsync(uri, content);
}
応答には、新しい顔が同じ Person に属しているとサービスがみなすかどうかを示すブール値と、予測の信頼度スコアが含まれます。
非同期操作の概要
次の表は、PersonDirectory 管理呼び出しが非同期的に処理される実行時間の長い操作 (LRO) であるか、即時かつ同期的に完了するかをまとめたものです。
Person/顔の管理 | URI | LRO? |
---|---|---|
Persons を取得する | /persons | |
Person を作成する | /persons | ✅ |
Person を取得する | /persons/{personId} | |
Person を更新する | /persons/{personId} | |
Person を削除する | /persons/{personId} | ✅ |
複数の Person の顔を取得する | /persons/{personId}/recognitionModels/{model}/persistedfaces | |
Person の顔を追加する | /persons/{personId}/recognitionModels/{model}/persistedfaces | ✅ |
URL から Person の顔を追加する | /persons/{personId}/recognitionModels/{model}/persistedfaces | ✅ |
Person の顔を取得する | /persons/{personId}/recognitionModels/{model}/persistedfaces/{persistedFaceId} | |
Person の顔を更新する | /persons/{personId}/recognitionModels/{model}/persistedfaces/{persistedFaceId} | |
Person の顔を削除する | /persons/{personId}/recognitionModels/{model}/persistedfaces/{persistedFaceId} | ✅ |
グループ管理 | URI | LRO? |
---|---|---|
複数の動的人物グループを取得する | /dynamicpersongroups | |
動的人物グループを作成する | /dynamicpersongroups/{dynamicPersonGroupId} | |
Person で動的人物グループを作成する | /dynamicpersongroups/{dynamicPersonGroupId} | ✅ |
動的人物グループを取得する | /dynamicpersongroups/{dynamicPersonGroupId} | |
動的人物グループを更新する | /dynamicpersongroups/{dynamicPersonGroupId} | |
Person の変更で動的人物グループを更新する | /dynamicpersongroups/{dynamicPersonGroupId} | ✅ |
動的人物グループを削除する | /dynamicpersongroups/{dynamicPersonGroupId} | ✅ |
動的人物グループの Persons を取得する | /dynamicpersongroups/{dynamicPersonGroupId}/persons | |
動的人物グループ参照を取得する | /persons/{personId}/dynamicPersonGroupReferences |
次のコードは、これらの非同期操作の依存関係を示しています。
var createPersonResponse = await CreatePersonAsync();
var personId = createPersonResponse.PersonId;
// faces can be added once the person creation HTTP call returns, even if it's still processing
var addPersonFaceResponse = await AddPersonFaceAsync(personId);
var addPersonFaceFromUrlResponse = await AddPersonFaceFromUrlAsync(personId);
switch (scenario)
{
case Scenario.Verify:
// only need to ensure the addition of face data has propagated
await WaitForLongRunningOperationsAsync(new[]
{
addPersonFaceResponse.OperationLocation,
addPersonFaceFromUrlResponse.OperationLocation
});
await VerifyFromPersonDirectoryAsync(queryFaceId, personId);
break;
case Scenario.IdentifyInPersonDirectory:
// ensure person creation and face data enrollment finish successfully
await WaitForLongRunningOperationsAsync(new[]
{
createPersonResponse.OperationLocation,
addPersonFaceResponse.OperationLocation,
addPersonFaceFromUrlResponse.OperationLocation
});
await IdentifyFromPersonDirectoryAsync(queryFaceId);
break;
case Scenario.IdentifyAgainstDynamicPersonGroup:
// person creation needs to be completed before it can be added to a group
await WaitForLongRunningOperationsAsync(new[]
{
createPersonResponse.OperationLocation
});
var groupResponse = await CreateOrUpdateDynamicPersonGroupWithPersonChangesAsync(groupId, [personId]);
// and make sure face data are ready before identification
await WaitForLongRunningOperationsAsync(new[]
{
addPersonFaceResponse.OperationLocation,
addPersonFaceFromUrlResponse.OperationLocation
});
await IdentifyFromDynamicPersonGroupAsync(queryFaceId, groupId);
// optionally check the person-to-group relationship, which requires the completion of the group's creation or updating call
await WaitForLongRunningOperationsAsync(new[]
{
groupResponse.OperationLocation
});
var groupIds = await GetDynamicPersonGroupReferencesAsync(personId);
break;
default:
break;
}
次のステップ
このガイドでは、PersonDirectory 構造を使用して、Face アプリの顔データと個人データを格納する方法について説明しました。 次に、ユーザーの顔データを追加するためのベスト プラクティスについて説明します。