65.9K
CodeProject 正在变化。 阅读更多。
Home

使用认知服务查找您的《权力的游戏》长相相似的人

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (5投票s)

2018年3月12日

CPOL

11分钟阅读

viewsIcon

10544

downloadIcon

163

使用 Face API 查找人脸之间的相似性

引言

您是《权力的游戏》的粉丝吗?您支持兰尼斯特家族、史塔克家族还是坦格利安家族?在本文中,我们将构建一个应用程序,利用人工智能检测和比较面部,帮助您识别您或他人与剧中哪个角色最相似。

作为快速预览,我们在此构建的应用程序根据它所查看的图像集,发现我的个人资料图片中的脸与史坦尼斯·拜拉席恩的相似度为42%。

认知服务

Microsoft Azure 中提供的一项服务是认知服务。这是一系列通过可访问的 API 作为服务提供的机器学习和人工智能算法,使您能够轻松地将智能功能添加到您的应用程序中。这些服务分为五类:

  • 视觉:提供图像处理算法,用于检测、识别、分析和组织照片中的人脸,检测情感,自动审核图像和视频,以及其他功能。
  • 语音:根据语音识别和验证人员,进行实时语音翻译,或将文本转换为语音并进行反向转换以理解用户意图的 API。
  • 知识:从文本中提取问题和答案,查找学术资源等。
  • 语言:提供评估情感和主题、理解用户命令、检测语言、进行实时机器语言翻译等功能的 API。
  • 搜索:提供由 Bing 提供支持的搜索功能,例如搜索新闻、图像、视频、网页文档、实体等。

您可以在此处阅读有关认知服务的更多信息。我们将在本文中使用的服务称为“人脸 API”,属于“视觉”类别。

人脸 API

这项服务本身实际上是几个功能的集合:

  • 检测图片中的人脸,以及年龄、性别、情绪、姿势、微笑、面部毛发等属性,以及一组人脸标志(嘴巴、鼻子、眼睛的位置)。
  • 从多达一百万张人脸的私人存储库中识别人脸。
  • 在给定的人脸和预先记录的人脸集合之间查找相似的人脸。
  • 根据视觉相似性对人脸进行分组。

在本文中,我们将使用用于检测和识别人脸的 API。您实际上可以在线尝试其中一些 API。

这是它如何在我当前的个人资料图片中检测我的脸以及一些属性的方式。尽管它在年龄上有一些偏差(确切地说是三年),但它在分析脸部和表情方面做得相当好。

人脸 API 入门

要使用任何 Azure 认知服务,您需要拥有一个帐户。人脸 API 有几个价格层级:免费层级允许每月 30000 次事务,但每分钟只能 20 次;付费层级允许每月不同价格的各种事务配额,每秒最多 10 次事务。当然,这些都可能在未来发生变化,如果您想在实际应用程序中使用这些服务,应查看定价详情。但是,还提供 30 天免费试用,因此您可以试用这些服务,以决定它们是否能满足您的需求。

需要注意的一点是,事务基本上代表一个 API 调用。例如,为了找到相似的人脸,我们必须首先检测图片中的人脸,然后找到相似性。这是通过两次不同的 API 调用完成的,代表两次事务。如果您使用免费层级,则每分钟只能进行 20 次 API 调用。

开始所需的唯一内容是一组 API 密钥。我假设您有一个 Azure 帐户。您需要做的是:

  • 登录到 Azure 门户以创建新资源。
  • 搜索人脸 API 并选择创建一个。

  • 填写包含名称、订阅、位置、定价层级、资源组的表单,然后点击创建。对于价格层级,您可以选择 F0 免费,在下图中是 S0,因为我已经有一个免费资源,并且您不能创建多个免费资源。

  • 转到创建的资源,在资源管理下,您会找到密钥。您需要复制其中一个以在应用程序中使用。

  • 您还需要 API 的终结点。这可以从概览面板中获取。

理解 API

人脸 API 参考文档可在此处获得。您可以在其中找到有关每个 API 调用的详细信息,例如功能、参数、结果、可能的错误等。

为了在人脸和《权力的游戏》角色之间找到相似之处,我们必须首先建立一个人脸列表,以便稍后进行搜索。REST API 提供了创建此类列表、更新和删除,以及从列表中添加和删除人脸的资源。应用程序至少应该:

  • 创建列表:您需要向 [endpoint]/facelists/{faceListId} 发送一个 PUT HTTP 请求,其中 endpoint 是您从概览面板复制的,faceListId 是一个必需参数,表示列表的标识符(有效字符是小写字母或数字或 '-' 或 '_',最大长度为 64)。这将创建一个空列表,您可以稍后向其中添加人脸。
  • 向列表添加人脸:您需要向 [endpoint]/facelists/{faceListId}/persistedFaces[?userData][&targetFace] 发送一个 POST HTTP 请求。faceListId 是您创建列表时使用的列表标识符,userData 是一个最大 1KB 的可选字符串,可用于任何应用程序定义的目的,targetFace 是一个可选参数,用于指示图像中应添加哪张脸。在图像中有多张脸的情况下,这是必要的。在这种情况下,如果缺少此参数,则调用失败。如果使用此参数,它应该是人脸检测 API 返回的值。实际图像可以通过两种方式传递:作为 JSON 对象中的 URL,使用 application/json 作为 content-type,或作为二进制数据,使用 application/octet-stream 作为 content-type

在本文随附的应用程序中,所有这些功能,包括检索现有列表、创建新列表、删除现有列表、从列表中添加和删除人脸,都已实现。您可以在 FaceApi 命名空间中找到它们。

在构建完要搜索的人脸列表后,我们就可以实际寻找相似之处了。为此,我们必须按此顺序调用以下 API:

  • 检测:是一个 POST HTTP 请求,发送到 [endpoint]/detect[?returnFaceId][&returnFaceLandmarks][&returnFaceAttributes]。图像可以作为 URL 或二进制流传递,就像将人脸添加到列表的 API 一样。当调用成功时,结果包含一个人脸数组,每个新人脸都有一个 ID、矩形、标志和属性。下一次调用所需的是 ID。此标识符在服务器上存储 24 小时后过期。
  • 查找相似:是一个 POST HTTP 请求,发送到 [endpoint]/findsimilars。内容是一个 JSON 对象,其中包含由 Detect 返回的人脸 ID,以及一个人脸列表 ID 或一个候选人脸 ID 数组,所有这些都由 Detect 返回。您还可以选择指定要返回的匹配项数量以及搜索应如何执行。成功时,返回包含找到的相似的持久人脸 ID 和 0(无相似性)到 1(相同)之间的置信度分数。

请注意,实际上可以创建两种类型的人脸列表:

  • 标准列表(此处描述并在演示应用程序中使用)。这些列表可以包含多达 1000 张人脸。每个订阅最多可以有 64 个这样的列表。
  • 可以包含多达 1000000 张人脸的大型列表。在一个订阅中,您可以拥有多达 64 个大型人脸列表。然而,这些列表在使用“查找相似”等算法之前需要进行训练。这是一个异步操作,可能需要一些时间。启动训练后,您可以查询其状态,以确保在使用列表之前已完成训练。

需要人脸列表的算法无论您指定标准人脸列表还是大人脸列表,工作方式都相同。

还有一个更重要的方面需要记住:创建人脸列表并向其中添加人脸实际上并不会在服务器上保留图像,而只会保留有关人脸的信息。如果您以后需要在应用程序中显示图像,则需要将它们保存在一个以后可以检索的地方。演示应用程序就是这种情况,它显示了与特定人脸最匹配的《权力的游戏》角色的图像。

从 C# 使用 API

从 C# 调用前面提到的 API 非常简单。以下是实现(您可以在源代码中的 FaceApiUtils 类中找到它们):

  • 创建人脸列表,带用户定义的人脸列表 ID、名称和描述。如果调用失败,此函数将抛出异常。
    public static async Task<bool> CreateFaceList
        (string faceListId, string name, string description)
    {
       using (var client = new HttpClient())
       {
          client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", AppSettings.Key1);
    
          var uri = $"{AppSettings.Endpoint}/facelists/{faceListId}";
    
          var request = new FaceListCreateRequest()
          {
             Name = name,
             UserData = description
          };
    
          var json = JsonConvert.SerializeObject(request);
    
          var content = new StringContent(json, Encoding.UTF8, "application/json");
    
          var response = await client.PutAsync(uri, content);
          if(!response.IsSuccessStatusCode)
          {
             var body = await response.Content.ReadAsStringAsync();
             var error = JsonConvert.DeserializeObject<FaceApiErrorResponse>(body);
             throw new FaceApiException(error.Error.Code, error.Error.Message);
          }
    
          return true;
       }
    }
  • 向人脸列表添加人脸。人脸从作为二进制流上传的图像中识别。如果调用成功,该函数返回人脸的持久 ID,否则抛出异常。
    public static async Task<string> AddFaceToFaceList(string faceListId, byte[] image)
    {
       using (var client = new HttpClient())
       {
          client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", AppSettings.Key1);
    
          var uri = $"{AppSettings.Endpoint}/facelists/{faceListId}/persistedFaces";
    
          var request = new ByteArrayContent(image);
          request.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    
          var response = await client.PostAsync(uri, request);
          if (!response.IsSuccessStatusCode)
          {
             var body = await response.Content.ReadAsStringAsync();
             var error = JsonConvert.DeserializeObject<FaceApiErrorResponse>(body);
             throw new FaceApiException(error.Error.Code, error.Error.Message);
          }
          else
          {
             var body = await response.Content.ReadAsStringAsync();
             var result = JsonConvert.DeserializeObject<FaceAddResponse>(body);
             return result.PersistedFaceId;
          }
       }
    }

以下是这些函数中使用的辅助类型:

class FaceListCreateRequest
{
  public string Name { get; set; }
  public string UserData { get; set; }
}

class FaceApiError
{
  public string Code { get; set; }
  public string Message { get; set; }
}

class FaceApiErrorResponse
{
  public FaceApiError Error { get; set; }
}   

class FaceAddResponse
{
  public string PersistedFaceId { get; set; }
}
  
class FaceApiException : Exception
{
  public string Code { get; private set; }

  public FaceApiException(string code, string message) : base(message)
  {
     Code = code;
  }
}   

另外两个要实现的人脸 API 调用是 Detect 和 Face Similar。它们如下所示:

  • 检测人脸以图像作为二进制内容,并将其发送到服务器。成功时,它返回检测到的人脸列表。每张人脸都有各种信息,但唯一必要的是 ID。
    public static async Task<List<FaceDetectResponse>> DetectFace(byte[] image)
    {
       using (var client = new HttpClient())
       {
          client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", AppSettings.Key1);
    
          var uri = $"{AppSettings.Endpoint}/detect";
          var content = new ByteArrayContent(image);
          content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
          var response = await client.PostAsync(uri, content);
          if (response.IsSuccessStatusCode)
          {
             var responseBody = await response.Content.ReadAsStringAsync();
             var result = 
                 JsonConvert.DeserializeObject<List<FaceDetectResponse>>(responseBody);
             return result;
          }
          else
          {
             var errorText = await response.Content.ReadAsStringAsync();
             var errorResponse = 
                 JsonConvert.DeserializeObject<FaceApiErrorResponse>(errorText);
             throw new FaceApiException
                   (errorResponse.Error.Code, errorResponse.Error.Message);
          }
       }
    }
  • 查找相似项使用 Detect 返回的临时人脸 ID、人脸列表 ID 和匹配项数量。成功时,它返回一个相似项列表,每个相似项都包含永久人脸 ID 及其置信度分数。
    public static async Task<List<FaceFindSimilarResponse> 
           FindSimilar(string faceId, string faceListId, int count)
    {
       using (var client = new HttpClient())
       {
          client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", AppSettings.Key1);
    
          var uri = $"{AppSettings.Endpoint}/findsimilars";
    
          var body = new FaceFindSimilarRequest()
          {
             FaceId = faceId,
             FaceListId = faceListId,
             MaxNumOfCandidatesReturned = count,
             Mode = FaceFindSimilarRequestMode.MatchFace
          };
          var bodyText = JsonConvert.SerializeObject(body);
    
          var httpContent = new StringContent(bodyText, Encoding.UTF8, "application/json");
    
          var response = await client.PostAsync(uri, httpContent);
          if (response.IsSuccessStatusCode)
          {
             var responseBody = await response.Content.ReadAsStringAsync();
             var result = 
                 JsonConvert.DeserializeObject<List<FaceFindSimilarResponse>(responseBody);
             return result;
          }
          else
          {
             var errorText = await response.Content.ReadAsStringAsync();
             var errorResponse = 
                  JsonConvert.DeserializeObject<FaceApiErrorResponse>(errorText);
             throw new FaceApiException(errorResponse.Error.Code, errorResponse.Error.Message);
          }
       }
    }

上述函数使用的其他辅助类定义如下:

class FaceDetectResponse
{
  public string FaceId { get; set; }

  public Rectangle FaceRectangle { get; set; }

  public FaceLandmarks FaceLandmarks { get; set; }

  public FaceAttributes FaceAttributes { get; set; }
}

class Rectangle
{
  public int Width { get; set; }
  public int Height { get; set; }
  public int Left { get; set; }
  public int Top { get; set; }
}

class Point
{
  public double X { get; set; }
  public double Y { get; set; }
}

class FaceFindSimilarRequest
{
  public string FaceId { get; set; }
  public string FaceListId { get; set; }
  public List<string> FaceIds { get; set; }
  public int MaxNumOfCandidatesReturned { get; set; }
  public string Mode { get; set; }
}

class FaceFindSimilarRequestMode
{
  public const string MatchPerson = "matchPerson";
  public const string MatchFace = "matchFace";
}   

class FaceFindSimilarResponse
{
  public string PersistedFaceId { get; set; }
  public string FaceId { get; set; }
  public double Confidence { get; set; }
}

注意:在所有这些代码示例中,AppSettings.Key1AppSettings.Endpoint 是变量,其值从应用程序配置文件中读取,代表您的人脸 API Azure 资源的密钥和终结点。

构建应用程序

尽管拥有一个移动应用程序来检测《权力的游戏》中最相似的角色会更有趣,但我们将构建一个您可以在 PC 上运行的简单 WPF 应用程序。如果您运行本文随附的应用程序,您将不得不使用您自己的应用程序密钥,构建您自己的列表,并向列表中添加您自己的图像,因为这些在订阅之间不可共享。

WPF 应用程序有三个主窗口:

  • 启动窗口,使您能够选择一个操作:管理人脸列表或查找相似人脸。

  • 人脸列表管理窗口。在这里,您可以查看现有的人脸列表(此应用程序中使用的是标准列表),添加新的,或删除现有的。对于每个面部列表,您可以查看用于向列表添加人脸的图像,添加更多人脸(从文件或整个文件夹)并删除现有的人脸。因为服务器不保留图像本身,所以这些图像存储在工作目录的子文件夹中。工作目录的名称在应用程序配置文件中指定。此文件夹中每个图像的名称都是服务器返回的持久人脸 ID。

  • 用于查找相似人脸的窗口。这允许您从磁盘中选择图像和目标人脸列表。成功检测后,它会显示最相似的匹配项以及置信度分数。

app.config 文件包含多个应用程序设置:人脸 API 资源的终结点、访问密钥,以及保存已将人脸添加到人脸列表的图像的文件夹名称。请注意,对于每个列表,将创建一个以列表 ID 为名称的子文件夹。图像存储在与列表关联的子文件夹中。

<appSettings>
  <add key="Endpoint" value="https://westeurope.api.cognitive.microsoft.com/face/v1.0" />
  <add key="Key1" value="...(insert your key)..." />
  <add key="Key2" value="...(insert your key)..." />
  <add key="FaceListBaseFolder" value="facelists" />
</appSettings>

为了让应用程序运行并获得合理的结果,您应该构建一个包含数百张图像的列表。我使用的列表有 400 多张,但您最多可以使用 1000 张。如果您想构建一个更大的列表,那么您必须使用大型人脸列表而不是标准列表。在这种情况下,有一组不同的人脸列表 API,尽管它们与标准 API 大致相似,不同之处在于它们在使用前需要进行训练。

正如您从最后一张图片中看到的那样,当我将我的个人资料照片中的脸与我构建的《权力的游戏》角色图片列表进行比较时,应用程序检测到与史坦尼斯·拜拉席恩有 42% 的相似度。我尝试寻找不同名人与《权力的游戏》角色之间的相似之处,玩得挺开心的。以下是当前 ATP 和 WTA 排名领先者罗杰·费德勒和西蒙娜·哈勒普在我《权力的游戏》人脸列表中的得分。

历史

  • 2018年3月12日:初始版本
使用认知服务查找你的《权力的游戏》相似者 - CodeProject - 代码之家
© . All rights reserved.