人脸识别

  • 一、相关概念
  • 人脸
  • 人脸库
  • 人脸特征标识(face_token)
  • 并发 (QPS)
  • API Key
  • API Secret
  • confidence
  • thresholds
  • 人脸比对/人脸搜索(控制台分析)
  • 人脸检测
  • 人脸搜索
  • 人脸对比
  • 二、Web API
  • face++里注册账号
  • 创建API Key
  • postman调用API接口
  • 人脸检测
  • 人脸对比
  • 使用face_token参数进行对比
  • 人脸搜索
  • 创建人脸库
  • 添加人脸
  • 获取人脸信息:
  • 删除人脸
  • 三、刷脸登录
  • 代码


一、相关概念

人脸

人脸(Face)在人脸识别技术中特指图像中发现的人脸,当对一张图片进行人脸检测时,会将检测到的人脸记录下来,包括人脸在图片中的位置,用一个系统标识 face_token 来表示。注意:对同一张图片进行多次人脸检测,对同一个人脸会得到不同的 face_token。

人脸库

人脸库(FaceSet)是用来存储检测到人脸的存储对象。一个 FaceSet 内的 face_token 是不重复的。

人脸特征标识(face_token)

  • Face_token 是系统为人脸分配的唯一标识。当对一张图片进行人脸检测后,检测到的人脸以及人脸在图片中的位置会用一个用一个人脸特征标识 face_token 来表示。在进行人脸比对或人脸关键点检测时必须指定 face_token。
  • 针对同一张图片进行多次人脸检测,同一个人脸会得到不同的人脸特征标识 (face_token) 。

并发 (QPS)

并发 (QPS) 指每秒可以发起的 API 请求次数。调用同一个功能模块下的各个 API ,会统一计算 QPS。例如人脸识别并发 (QPS) 为 10 个,人脸识别包括人脸检测 API、人脸比对 API、人脸搜索API、人脸库管理 API 组、获取人脸信息 API 和自定义人脸信息 API,则每秒可以发起 10 次 API 调用请求,不限制具体调用了哪一个 API。超过 10 次请求,将返回 403 并发超限错误码 (CONCURRENCY_LIMIT_EXCEEDED)。

API Key

  • API Key 是使用公有云 API 和联网授权 SDK 服务的凭证。注册账号后,需要先创建一个 API Key,才能进行接口调用服务。
  • API Key 分为试用(免费)与正式(服务)两种类型,试用 API Key 在创建数量、使用的服务类型和并发 (QPS) 保障上均有限制。

API Secret

API Secret 是在创建 API Key 时随机生成的一串密钥,需要和 API Key 搭配,获取使用 API 的权限,请您妥善保管记录。

confidence

比对结果置信度,范围 [0,100],小数点后3位有效数字,数字越大表示两个人脸越可能是同一个人

thresholds

  • 一组用于参考的置信度阈值,包含以下三个字段。每个字段的值为一个 [0,100] 的浮点数,小数点后 3 位有效数字。
  • 1e-3:误识率为千分之一的置信度阈值;
  • 1e-4:误识率为万分之一的置信度阈值;
  • 1e-5:误识率为十万分之一的置信度阈值;
  • 如果置信值低于“千分之一”阈值则不建议认为是同一个人;如果置信值超过“十万分之一”阈值,则是同一个人的几率非常高。
  • 阈值不是静态的,每次比对返回的阈值不保证相同,所以没有持久化保存阈值的必要,更不要将当前调用返回的 confidence 与之前调用返回的阈值比较。
  • 如果传入图片但图片中未检测到人脸,则无法进行比对,本字段不返回。

人脸比对/人脸搜索(控制台分析)

  • 计算机检测到图片中一个人脸之后,通过人脸判断人身份的过程被称为人脸比对/人脸搜索。
  • 人脸比对指采集新的人脸,与一个已知身份用户的人脸进行比对,判断新的人脸是否属于该已知身份用户。人脸比对需要调用人脸比对 API。
  • 人脸搜索是指采集用户新的人脸,在多个已知身份用户的人脸集合中进行搜索,找出新的人脸属于哪一个已知身份用户。人脸搜索需要调用人脸搜索API。

人脸检测

检测到了人脸会产生一个"face_token"

java调用摄像头人脸识别 java如何实现人脸识别_字段

并且产生"face_num",这里有两个人脸,因此为2

java调用摄像头人脸识别 java如何实现人脸识别_字段_02

如图:人脸数为3,小狗不会被识别到

java调用摄像头人脸识别 java如何实现人脸识别_java_03

人脸搜索

java调用摄像头人脸识别 java如何实现人脸识别_java调用摄像头人脸识别_04


搜索结果:

java调用摄像头人脸识别 java如何实现人脸识别_java调用摄像头人脸识别_05

人脸对比

如图:

java调用摄像头人脸识别 java如何实现人脸识别_字段_06

产生了"confidence"和"le-5"
因为此时"confidence"的值大于"le-5"的值,所以分析结果如下图:

java调用摄像头人脸识别 java如何实现人脸识别_java_07

再比如:

java调用摄像头人脸识别 java如何实现人脸识别_API_08

因为此时"confidence"的值小于"le-5"的值,所以分析结果如下图:

java调用摄像头人脸识别 java如何实现人脸识别_字段_09

二、Web API

face++里注册账号

  • 首先:搜索face++
  • 官网: face++
  • java调用摄像头人脸识别 java如何实现人脸识别_java调用摄像头人脸识别_10


  • 登录或注册
  • 点击进入控制台

    如图:

创建API Key

  • 要调用 API,需要先创建一个 API Key(API 密钥),它是使用 API 和 SDK 的凭证注册验证成功后,可以在欢迎页面点击“创建我的第一个应用”,一个免费API Key会被自动生成,可以直接使用
  • 免费API key 可依据免费规则调用API,如果希望使用付费版服务,需创建正式API Key

java调用摄像头人脸识别 java如何实现人脸识别_字段_11

  • 如图:创建完成
  • 后续代码要用到这两个值
  • java调用摄像头人脸识别 java如何实现人脸识别_字段_12


postman调用API接口

人脸检测

  • 概述:
    传入图片进行人脸检测和人脸分析。
    可以检测图片内的所有人脸,对于每个检测出的人脸,会给出其唯一标识 face_token,可用于后续的人脸分析、人脸比对等操作。对于正式 API Key,支持指定图片的某一区域进行人脸检测。

  • 本 API 支持对检测到的人脸直接进行分析,获得人脸的关键点和各类属性信息。对于试用 API Key,最多只对人脸框面积最大的 5 个人脸进行分析,其他检测到的人脸可以使用 Face Analyze API 进行分析。对于正式 API Key,支持分析所有检测到的人脸。

  • 关于 face_token
    如果需要将检测出的人脸用于后续的分析、比对等操作,建议将对应的 face_token 添加到 FaceSet 中。如果一个 face_token 在 72 小时内没有存放在任一 FaceSet 中,则该 face_token 将会失效。如果对同一张图片进行多次人脸检测,同一个人脸得到的 face_token 是不同的。

  • 调用URL
    https://api-cn.faceplusplus.com/facepp/v3/detect

  • 调用方法
    POST

  • 请求体格式
    multipart/form-data

请求参数(必选):

参数名

参数说明

api_key

调用此API的API Key

api_secret

调用此API的API Secret

以下参数三选一(必选):

参数名

参数说明

image_url

图片的 URL

image_file

一个图片,二进制文件,需要用post multipart/form-data的方式上传

image_base64

base64 编码的二进制图片数据如果同时传入了 image_url、image_file 和 image_base64 参数,本API使用顺序为 image_file 优先,image_url 最低

如下图:

选择的参数为:
api_key  
api_secret 
image_file

产生了face_token,face_num等..

选择的图片参数为:

java调用摄像头人脸识别 java如何实现人脸识别_java调用摄像头人脸识别_13


结果:

检测到一张人脸

java调用摄像头人脸识别 java如何实现人脸识别_人工智能_14

再换张图:

java调用摄像头人脸识别 java如何实现人脸识别_java调用摄像头人脸识别_15


结果检测到3张脸:

java调用摄像头人脸识别 java如何实现人脸识别_API_16

返回值说明

字段

说明

request_id

用于区分每一次请求的唯一的字符串。

faces

被检测出的人脸数组

image_id

被检测的图片在系统中的标识。

time_used

整个请求所花费的时间,单位为毫秒。

error_message

当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在

face_num

检测出的人脸个数

faces 数组中单个元素的结构

字段

说明

face_token

人脸的标识

face_rectangle

人脸矩形框的位置,包括以下属性。每个属性的值都是整数:1.top:矩形框左上角像素点的纵坐标 2.left:矩形框左上角像素点的横坐标 3.width:矩形框的宽度 4.height:矩形框的高度

landmark

人脸的关键点坐标数组。当传入的 landmark 参数值为 1 时,返回 83 个关键点坐标数组。当传入的 landmark 参数值为 2 时,返回 106 个关键点坐标数组。

人脸对比

  • 描述:
    将两个人脸进行比对,来判断是否为同一个人,返回比对结果置信度和不同误识率下的阈值。

  • 支持传入图片或 face_token 进行比对。使用图片时会自动选取图片中检测到人脸尺寸最大的一个人脸。

  • 调用 URL
    https://api-cn.faceplusplus.com/facepp/v3/compare

  • 调用方法
    POST

  • 请求体格式
    multipart/form-data

请求参数(必选):

参数名

参数说明

api_key

调用此API的API Key

api_secret

调用此API的API Secret

以下参数四选一(必选):

参数名

参数说明

face_token1

第一个人脸标识 face_token,优先使用该参数

image_url1

第一张图片的 URL

image_file1

第一张图片,二进制文件,需要用 post multipart/form-data 的方式上传。

image_base64_1

base64 编码的二进制图片数据如果同时传入了 image_url1、image_file1 和 image_base64_1 参数,本 API 使用顺序为image_file1 优先,image_url1 最低。

以下参数四选一(必选):

参数名

参数说明

face_token2

第二个人脸标识 face_token,优先使用该参数

image_url2

第二张图片的 URL

image_file2

第二张图片,二进制文件,需要用 post multipart/form-data 的方式上传。

image_base64_2

base64 编码的二进制图片数据如果同时传入了 image_url2、image_file2 和 image_base64_2 参数,本API 使用顺序为 image_file2优先,image_url2 最低。

如图:对比这两张图:

java调用摄像头人脸识别 java如何实现人脸识别_java调用摄像头人脸识别_17

选择的参数为:
api_key  
api_secret 
image_file1
image_file2

产生了confidence,thresholds等..

结果:

因为此时"confidence"的值小于"le-5"的值,所以分析结果很大概率为不是同一个人:

java调用摄像头人脸识别 java如何实现人脸识别_字段_18

再比如这两张图:

java调用摄像头人脸识别 java如何实现人脸识别_人工智能_19


结果:

因为此时"confidence"的值大于"le-5"的值,所以分析结果很大概率为同一个人:

java调用摄像头人脸识别 java如何实现人脸识别_字段_20

使用face_token参数进行对比

  • 对比上两张人脸
    先检索需对比的第一张人脸得到face_token:

java调用摄像头人脸识别 java如何实现人脸识别_java调用摄像头人脸识别_21

  • 再检索需对比的第二张人脸得到face_token:

java调用摄像头人脸识别 java如何实现人脸识别_API_22

  • 结果:
    因为此时"confidence"的值大于"le-5"的值,所以分析结果很大概率为同一个人:

java调用摄像头人脸识别 java如何实现人脸识别_java_23

返回值说明

字段

说明

request_id

用于区分每一次请求的唯一的字符串。

confidence

比对结果置信度,范围 [0,100],小数点后3位有效数字,数字越大表示两个人脸越可能是同一个人。注:如果传入图片但图片中未检测到人脸,则无法进行比对,本字段不返回。

thresholds

一组用于参考的置信度阈值,包含以下三个字段。每个字段的值为一个 [0,100] 的浮点数,小数点后 3 位有效数字。1e-3:误识率为千分之一的置信度阈值;1e-4:误识率为万分之一的置信度阈值;1e-5:误识率为十万分之一的置信度阈值;如果置信值低于“千分之一”阈值则不建议认为是同一个人;如果置信值超过“十万分之一”阈值,则是同一个人的几率非常高。请注意:阈值不是静态的,每次比对返回的阈值不保证相同,所以没有持久化保存阈值的必要,更不要将当前调用返回的 confidence 与之前调用返回的阈值比较。注:如果传入图片但图片中未检测到人脸,则无法进行比对,本字段不返回。

image_id1

通过 image_url1、image_file1 或 image_base64_1 传入的图片在系统中的标识。注:如果未传入图片,本字段不返回。

image_id2

通过 image_url2、image_file2 或 image_base64_2 传入的图片在系统中的标识。注:如果未传入图片,本字段不返回。

faces1

通过 image_url1、image_file1 或 image_base64_1 传入的图片中检测出的人脸数组,采用数组中的第一个人脸进行人脸比对。注:如果未传入图片,本字段不返回。如果没有检测出人脸则为空数组

faces2

通过 image_url2、image_file2 或 image_base64_2 传入的图片中检测出的人脸数组,采用数组中的第一个人脸进行人脸比对。注:如果未传入图片,本字段不返回。如果没有检测出人脸则为空数组

time_used

整个请求所花费的时间,单位为毫秒。

error_message

当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在

人脸搜索

  • 描述:
    在一个已有的 FaceSet 中找出与目标人脸最相似的一张或多张人脸,返回置信度和不同误识率下的阈值。

  • 支持传入图片或 face_token 进行人脸搜索。使用图片进行搜索时会选取图片中检测到人脸尺寸最大的一个人脸。

  • 调用URL
    https://api-cn.faceplusplus.com/facepp/v3/search

  • 调用方法
    POST

  • 请求体格式
    multipart/form-data

请求参数(必选):

参数名

参数说明

api_key

调用此API的API Key

api_secret

调用此API的API Secret

以下参数四选一(必选):

参数名

参数说明

face_token

进行搜索的目标人脸的 face_token,优先使用该参数

image_url

图片的 URL

image_file

一个图片,二进制文件,需要用post multipart/form-data的方式上传

image_base64

base64 编码的二进制图片数据如果同时传入了 image_url、image_file 和 image_base64 参数,本API使用顺序为 image_file 优先,image_url 最低

以下参数二选一(必选):

参数名

参数说明

faceset_token

用来搜索的 FaceSet 的标识

outer_id

用户自定义的 FaceSet 标识

返回值说明

字段

说明

request_id

用于区分每一次请求的唯一的字符串。

results

搜索结果对象数组 注:如果传入图片但图片中未检测到人脸,则无法进行人脸搜索,本字段不返回。

thresholds

一组用于参考的置信度阈值,包含以下三个字段。每个字段的值为一个 [0,100] 的浮点数,小数点后 3 位有效数字。1e-3:误识率为千分之一的置信度阈值;1e-4:误识率为万分之一的置信度阈值;1e-5:误识率为十万分之一的置信度阈值;如果置信值低于“千分之一”阈值则不建议认为是同一个人;如果置信值超过“十万分之一”阈值,则是同一个人的几率非常高。请注意:阈值不是静态的,每次比对返回的阈值不保证相同,所以没有持久化保存阈值的必要,更不要将当前调用返回的 confidence 与之前调用返回的阈值比较。注:如果传入图片但图片中未检测到人脸,则无法进行比对,本字段不返回。

image_id

传入的图片在系统中的标识。注:如果未传入图片,本字段不返回。

faces

传入的图片中检测出的人脸数组,采用数组中的第一个人脸进行人脸搜索。注:如果未传入图片,本字段不返回。如果没有检测出人脸则为空数组

time_used

整个请求所花费的时间,单位为毫秒。

error_message

当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在

results 数组中单个元素的结构

字段

说明

face_token

从 FaceSet 中搜索出的一个人脸标识 face_token。

confidence

比对结果置信度,范围 [0,100],小数点后3位有效数字,数字越大表示两个人脸越可能是同一个人。

user_id

用户提供的人脸标识,如果未提供则为空。

创建人脸库

  • 描述:
    创建一个人脸的集合 FaceSet,用于存储人脸标识 face_token。一个 FaceSet 能够存储10000个 face_token。

  • 试用API Key可以创建1000个FaceSet,正式API Key可以创建10000个FaceSet。

  • 调用URL
    https://api-cn.faceplusplus.com/facepp/v3/faceset/create

  • 调用方法
    POST

请求参数(必选):

参数名

参数说明

api_key

调用此API的API Key

api_secret

调用此API的API Secret

请求参数(非必选):

参数名

参数说明

outer_id

账号下全局唯一的 FaceSet 自定义标识,可以用来管理 FaceSet 对象。最长255个字符,不能包括字符^@,&=*'"

选择的参数为:
api_key  
api_secret 
outer_id

产生了outer_id等..

如图:

java调用摄像头人脸识别 java如何实现人脸识别_java_24

返回值说明

字段

说明

request_id

用于区分每一次请求的唯一的字符串。除非发生404(API_NOT_FOUND ) 或403 (AUTHORIZATION_ERROR)错误,此字段必定返回。

faceset_token

FaceSet 的标识

outer_id

用户自定义的 FaceSet 标识,如果未定义则返回值为空

face_added

本次操作成功加入 FaceSet的face_token 数量

face_count

操作结束后 FaceSet 中的 face_token 总数量

failure_detail

无法被加入 FaceSet 的 face_token 以及原因face_token:人脸标识reason:不能被添加的原因,包括 INVALID_FACE_TOKEN 人脸表示不存在 ,QUOTA_EXCEEDED 已达到 FaceSet 存储上限

time_used

整个请求所花费的时间,单位为毫秒。

error_message

当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在

添加人脸

  • 描述
    为一个已经创建的 FaceSet 添加人脸标识 face_token。一个 FaceSet 最多存储1,000个 face_token。

  • 调用 URL
    https://api-cn.faceplusplus.com/facepp/v3/faceset/addface

  • 调用方法
    POST

请求参数(必选):

参数名

参数说明

api_key

调用此API的API Key

api_secret

调用此API的API Secret

以下参数二选一(必选):

参数名

参数说明

faceset_token

用来搜索的 FaceSet 的标识

outer_id

用户自定义的 FaceSet 标识

请求参数(必选):

参数名

参数说明

face_tokens

人脸标识 face_token 组成的字符串,可以是一个或者多个,用逗号分隔。最多不超过5个face_token

1.先进行人脸检测
选择的参数为:
api_key  
api_secret 
image_file

产生了face_token,face_num等..

如图:

java调用摄像头人脸识别 java如何实现人脸识别_字段_25

2.将人脸检测后的face_token存储到faceset
选择的参数为:
api_key  
api_secret 
outer_id
face_token

产生了face_count等..

如图:

java调用摄像头人脸识别 java如何实现人脸识别_人工智能_26

返回值说明

字段

说明

request_id

用于区分每一次请求的唯一的字符串。除非发生404(API_NOT_FOUND ) 或403 (AUTHORIZATION_ERROR)错误,此字段必定返回。

faceset_token

FaceSet 的标识

outer_id

用户自定义的 FaceSet 标识,如果未定义则返回值为空

face_added

本次操作成功加入 FaceSet的face_token 数量

face_count

操作结束后 FaceSet 中的 face_token 总数量

failure_detail

无法被加入 FaceSet 的 face_token 以及原因face_token:人脸标识reason:不能被添加的原因,包括 INVALID_FACE_TOKEN 人脸表示不存在 ,QUOTA_EXCEEDED 已达到 FaceSet 存储上限

time_used

整个请求所花费的时间,单位为毫秒。

error_message

当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在

获取人脸信息:

描述
获取一个 FaceSet 的所有信息,包括此 FaceSet 的 faceset_token, outer_id, display_name 的信息,以及此 FaceSet 中存放的 face_token 数量与列表。

调用URL
https://api-cn.faceplusplus.com/facepp/v3/faceset/getdetail

调用方法
POST
请求参数(必选):

参数名

参数说明

api_key

调用此API的API Key

api_secret

调用此API的API Secret

以下参数二选一(必选):

参数名

参数说明

faceset_token

用来搜索的 FaceSet 的标识

outer_id

用户自定义的 FaceSet 标识

选择的参数为:
api_key  
api_secret 
outer_id

产生了face_tokens等..

face_tokens是一个数组,里面装了faceset里的face_token
  • 如图:
  • java调用摄像头人脸识别 java如何实现人脸识别_java调用摄像头人脸识别_27


  • 再往faceset里存一张人脸:
  • java调用摄像头人脸识别 java如何实现人脸识别_java调用摄像头人脸识别_28


  • java调用摄像头人脸识别 java如何实现人脸识别_字段_29


  • 如图:检测到了两张人脸
  • java调用摄像头人脸识别 java如何实现人脸识别_字段_30


返回值说明

字段

说明

request_id

用于区分每一次请求的唯一的字符串。除非发生404(API_NOT_FOUND ) 或403 (AUTHORIZATION_ERROR)错误,此字段必定返回。

faceset_token

FaceSet 的标识

outer_id

用户自定义的 FaceSet 标识,如果未定义则返回值为空

display_name

人脸集合的名字

user_data

自定义用户信息

tags

自定义标签

face_count

操作结束后 FaceSet 中的 face_token 总数量

face_tokens

face_token的数组 注:如果该 FaceSet 下没有 face_token,则返回空数组。

next

用于进行下一次请求。返回值表示排在此次返回的所有 face_token 之后的下一个 face_token 的序号。如果返回此字段,则说明未返回完此 FaceSet 下的所有 face_token。可以将此字段的返回值,在下一次调用时传入 start 字段中,获取接下来的 face_token。如果没有返回该字段,则说明已经返回此 FaceSet 下的所有 face_token。

time_used

整个请求所花费的时间,单位为毫秒。

error_message

当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在

删除人脸

  • 描述
    移除一个FaceSet中的某些或者全部face_token

  • 调用URL
    https://api-cn.faceplusplus.com/facepp/v3/faceset/removeface

  • 调用方法
    POST

请求参数(必选):

参数名

参数说明

api_key

调用此API的API Key

api_secret

调用此API的API Secret

以下参数二选一(必选):

参数名

参数说明

faceset_token

用来搜索的 FaceSet 的标识

outer_id

用户自定义的 FaceSet 标识

请求参数(必选):

参数名

参数说明

face_tokens

需要移除的人脸标识字符串,可以是一个或者多个face_token组成,用逗号分隔。最多不能超过1,000个face_token注:face_tokens字符串传入“RemoveAllFaceTokens”则会移除FaceSet内所有的face_token

如图:

此时搜索到有两张人脸

java调用摄像头人脸识别 java如何实现人脸识别_字段_31

删除人脸

选择的参数为:
api_key  
api_secret 
outer_id
face_tokens

face_tokens可以在获取人脸里得到
产生了face_count等..

java调用摄像头人脸识别 java如何实现人脸识别_API_32


结果:

删除一张人脸后再获取一次

只剩一张人脸了

java调用摄像头人脸识别 java如何实现人脸识别_人工智能_33

返回值说明

字段

说明

request_id

用于区分每一次请求的唯一的字符串。除非发生404(API_NOT_FOUND ) 或403 (AUTHORIZATION_ERROR)错误,此字段必定返回。

faceset_token

FaceSet 的标识

outer_id

用户自定义的 FaceSet 标识,如果未定义则返回值为空

face_removed

成功从FaceSet中移除的face_token数量

face_count

操作结束后 FaceSet 中的 face_token 总数量

failure_detail

无法从FaceSet中移除的face_token以及原因face_token:人脸标识reason:不能被移除的原因,包括 INVALID_FACE_TOKEN 人脸标识不存在

time_used

整个请求所花费的时间,单位为毫秒。除非发生404(API_NOT_FOUND )或403 (AUTHORIZATION_ERROR)错误,此字段必定返回。

error_message

当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在

三、刷脸登录
  • 录入人脸信息,通过人脸检测接口得到face_token,存入outer_id=travel_faceset的faceset中当用户在登录的时候,得到用户当前登录图片,然后用人脸比对接口用当前登录图片和之前在
    faceset中保存的人脸信息进行比对。
  • 比对后的返回信息有我们设置的阈值(thresholds),可以根据自身的需求选择其中一个阈值,当相似度(confidence)高于这个阈值时就可以认为是同一个人,即可以登录成功,反之,不能登录成功。

代码

  • 添加依赖:
<dependency>
	<groupId>org.reactivestreams</groupId>
	<artifactId>reactive-streams</artifactId>
	<version>1.0.3</version>
</dependency>
  • yml配置文件:
face:
  config:
    api-key: jlTzV7I-pWQoqHFXWUUgv312jTpS17FC
    api-secret: eDULPcfETWuoRT9I4q51mvhlVpj_ECBp
    outer-id: link_faceset
  • 配置启动类:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class TravelApplication {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(TravelApplication.class, args);
    }
}
  • FaceDao
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.nio.file.Paths;
import java.util.List;

/**
 * 用于访问face++人脸系统
 */
@ConfigurationProperties("face.config")
@Component
@Getter
@Setter
public class FaceDao {
    @Autowired
    private RestTemplate restTemplate;
    private String apiKey;
    private String apiSecret;
    private String outerId;
    /**
     * 人脸检测
     */
    public DetectResponseEntity detect(String filePath) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 多部件表单体
        MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
        // ----------------- 表单 part
        multipartBodyBuilder.part("api_key", apiKey);
        multipartBodyBuilder.part("api_secret", apiSecret);
        // ----------------- 文件 part
        // 从磁盘读取文件
        multipartBodyBuilder.part("image_file", new FileSystemResource(Paths.get(filePath)), MediaType.IMAGE_PNG);
        // build完整的消息体
        MultiValueMap<String, HttpEntity<?>> multipartBody = multipartBodyBuilder.build();
        ResponseEntity<DetectResponseEntity> responseEntity = restTemplate.postForEntity("https://api-cn.faceplusplus.com/facepp/v3/detect", multipartBody, DetectResponseEntity.class);
        return responseEntity.getBody();
    }


    /**
     * 创建faceset
     */
    public void faceSetCreate() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
        map.add("api_key", apiKey);
        map.add("api_secret", apiSecret);
        map.add("outer_id", outerId);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>
                (map, headers);
        restTemplate.postForEntity("https://apicn.faceplusplus.com/facepp/v3/faceset/create", request, String.class);
    }
    /**
     * 得到outer_id=trave_faceset的信息
     */
    public FaceSetResponseEntity getFaceSetDetail() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
        map.add("api_key", apiKey);
        map.add("api_secret", apiSecret);
        map.add("outer_id", outerId);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
        ResponseEntity<FaceSetResponseEntity> responseEntity = restTemplate.postForEntity("https://apicn.faceplusplus.com/facepp/v3/faceset/getdetail", request, FaceSetResponseEntity.class);
        return responseEntity.getBody();
    }
    /**
     * 添加faceToken到FaceSet
     * 人脸标识 faceTokens 组成的字符串,可以是一个或者多个,用逗号分隔。最多不超过5个
     face_token
     * @param faceTokens
     */
    public void addFaceToFaceSet(String faceTokens) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
        map.add("api_key", apiKey);
        map.add("api_secret", apiSecret);
        map.add("outer_id", outerId);
        map.add("face_tokens", faceTokens);
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>
                (map, headers);
        restTemplate.postForEntity("https://apicn.faceplusplus.com/facepp/v3/faceset/addface", request, String.class);
    }
    /**
     * 人脸比对
     * @param faceToken1
     * @param faceToken2
     * @return
     */
    public boolean compareFace(String faceToken1, String faceToken2) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA); // 多部件表单体
        MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
        // ----------------- 表单 part
        multipartBodyBuilder.part("api_key", apiKey);
        multipartBodyBuilder.part("api_secret", apiSecret);
        multipartBodyBuilder.part("face_token1", faceToken1);
        multipartBodyBuilder.part("face_token2", faceToken2);
         // ----------------- 文件 part
         // 从磁盘读取文件
        // multipartBodyBuilder.part("image_file", new FileSystemResource(Paths.get(filePath)), MediaType.IMAGE_PNG);
        // build完整的消息体
        MultiValueMap<String, HttpEntity<?>> multipartBody = multipartBodyBuilder.build();
        ResponseEntity<CompareResponseEntity> responseEntity = restTemplate.postForEntity("https://api-cn.faceplusplus.com/facepp/v3/compare", multipartBody, CompareResponseEntity.class);
        System.out.println(responseEntity);
        CompareResponseEntity e = responseEntity.getBody();
        if (e.getConfidence() >= e.getThresholds().e5) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * 人脸比对返回实体类
     */
    @Data
    public static class CompareResponseEntity {
        private Double confidence;
        private ThresholdsResponseEntity thresholds;
    }
    /**
     * 人脸对比置信度阈值返回实体类
     */
    @Data
    public static class ThresholdsResponseEntity {
        @JsonProperty("1e-5")
        private Double e5;
    }

    /**
     * FaceSet返回实体类
     */
    @Data
    public static class FaceSetResponseEntity{
        private String faceset_token;
        private String outer_id;
        private Integer face_count;
        private List<String> face_tokens;
    }
    @Data
/**
 * 人脸检测返回数据实体类
 */
    public static class DetectResponseEntity {
        private String request_id;
        private Integer face_num;
        private List<FaceEntity> faces;
    }
    @Data
    /**
    * 人脸实体类
     */
    public static class FaceEntity {
        private String face_token;
    }
}
  • FaceDaoTest
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.*;
@SpringBootTest
@RunWith(SpringRunner.class)
public class FaceDaoTest {

    @Autowired
    private FaceDao faceDao;

    @Test
    public void detect() {
        FaceDao.DetectResponseEntity e = faceDao.detect("C:\\Users\\Administrator\\Desktop\\29.jpg");
        System.out.println(e);
    }
    @Test
    public void getFaceSetDetail() {
        FaceDao.FaceSetResponseEntity e = faceDao.getFaceSetDetail();
        System.out.println(e);
    }
    @Test
    public void createFaceSet() {
        faceDao.faceSetCreate();
    }
    @Test
    public void addFaceToFaceSet() {
        faceDao.addFaceToFaceSet("fb9929c6da2f035f6cf647cc78ab7675");
    }
    @Test
    public void campareFace() {
        boolean b = faceDao.compareFace("d64dc956a6be7c07ebaa3093923a66fa", "10447d93dd700e3f98c0a981c879c99d");
        System.out.println(b);
    }

}
  • FaceService
public interface FaceService {
    public void addFace(String filePath);
    public boolean loginByFace(String filePath);
}
  • FaceServiceImpl
import com.lrk.travel.dao.FaceDao;
import com.lrk.travel.service.FaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
@Service
public class FaceServiceImpl implements FaceService {
    @Autowired
    private FaceDao faceDao;

    @Override
    public void addFace(String filePath) {
            FaceDao.FaceSetResponseEntity fs = null;
            try {
                fs = faceDao.getFaceSetDetail();
            } catch (Exception e) {
            }
            if (fs == null) { //faceset不存在
                faceDao.faceSetCreate();
            }
            FaceDao.DetectResponseEntity dr = faceDao.detect(filePath); //检视人脸
            for (FaceDao.FaceEntity f : dr.getFaces()) {
                faceDao.addFaceToFaceSet(f.getFace_token());
            }
    }

    @Override
    public boolean loginByFace(String filePath) {
        boolean result = false;
        FaceDao.FaceSetResponseEntity fs = null;
        try {
            fs = faceDao.getFaceSetDetail();
        } catch (Exception e) {
        }
        if (fs == null) { //faceset不存在
            faceDao.faceSetCreate();
            fs = faceDao.getFaceSetDetail();
        }
        FaceDao.DetectResponseEntity dr = faceDao.detect(filePath); //检视人脸
        String ft1 = null;
        if (dr.getFace_num() >=1) {
            ft1 = dr.getFaces().get(0).getFace_token();
        } else {
            return false;
        }
        for (String ft2: fs.getFace_tokens()) {
            if (faceDao.compareFace(ft1, ft2)) {
                result = true;
            }
        }
        new File(filePath).delete(); //删除登录人脸
        return result;
    }

}
  • FaceController
import com.lrk.travel.service.FaceService;
import com.lrk.travel.utils.ImageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
@Controller
@RequestMapping("/admin/face")
public class FaceController {

    @Autowired
    private FaceService faceService;
    /**
     * 调到录入人脸页面
     * @return
     */
    @RequestMapping("/toinput")
    public String toInput() {
        return "/face/input";
    }
    @RequestMapping("/tologin")
    public String toLogin() {
        return "/face/login";
    }
    /**
     * 录入人脸
     * @param imgData
     * @param request
     * @return
     * @throws IOException
     */
    @ResponseBody
    @RequestMapping("/upload")
    public ResponseEntity doAdd(@RequestParam("imgData") String imgData, HttpServletRequest request) throws IOException {
        String savePath = request.getServletContext().getRealPath("img/face/");
        String fileName = UUID.randomUUID().toString().replaceAll("-", "") + ".png";
        System.out.println(savePath);
        ImageUtils.generateImage(imgData.substring(22), savePath, fileName);
        faceService.addFace(savePath + fileName);
        return ResponseEntity.ok("{\"success\": \"true\"}");
    }
    /**
     * 人脸识别登录
     * @param imgData
     * @param request
     * @return
     * @throws IOException
     */
    @ResponseBody
    @RequestMapping("/login")
    public ResponseEntity login(@RequestParam("imgData") String imgData,
                                HttpServletRequest request) throws IOException {
        String savePath =
                request.getServletContext().getRealPath("img/face/login/");
        String fileName = UUID.randomUUID().toString().replaceAll("-", "") +
                ".png";
        System.out.println(savePath);
        ImageUtils.generateImage(imgData.substring(22), savePath, fileName);
        boolean b = faceService.loginByFace(savePath + fileName);
        if (b) {
            System.out.println("登录成功");
            return ResponseEntity.ok("{\"success\": true}");
        } else {
            System.out.println("登录失败");
            return ResponseEntity.ok("{\"success\": false}");
        }
    }

}