一、相关概念
人脸
人脸(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"
并且产生"face_num",这里有两个人脸,因此为2
如图:人脸数为3,小狗不会被识别到
人脸搜索
搜索结果:
人脸对比
如图:
产生了"confidence"和"le-5" 因为此时"confidence"的值大于"le-5"的值,所以分析结果如下图:
再比如:
因为此时"confidence"的值小于"le-5"的值,所以分析结果如下图:
二、Web API
face++里注册账号
- 首先:搜索face++
- 官网: face++
- 登录或注册
- 点击进入控制台
如图:
创建API Key
- 要调用 API,需要先创建一个 API Key(API 密钥),它是使用 API 和 SDK 的凭证注册验证成功后,可以在欢迎页面点击“创建我的第一个应用”,一个免费API Key会被自动生成,可以直接使用
- 免费API key 可依据免费规则调用API,如果希望使用付费版服务,需创建正式API Key
- 如图:创建完成
- 后续代码要用到这两个值
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等..
选择的图片参数为: 结果: 检测到一张人脸
再换张图:
结果检测到3张脸:
返回值说明
字段 | 说明 |
---|
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 最低。 |
如图:对比这两张图:
选择的参数为:
api_key
api_secret
image_file1
image_file2
产生了confidence,thresholds等..
结果: 因为此时"confidence"的值小于"le-5"的值,所以分析结果很大概率为不是同一个人:
再比如这两张图:
结果: 因为此时"confidence"的值大于"le-5"的值,所以分析结果很大概率为同一个人:
使用face_token参数进行对比
- 对比上两张人脸
先检索需对比的第一张人脸得到face_token:
- 再检索需对比的第二张人脸得到face_token:
- 结果:
因为此时"confidence"的值大于"le-5"的值,所以分析结果很大概率为同一个人:
返回值说明
字段 | 说明 |
---|
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等..
如图:
返回值说明
字段 | 说明 |
---|
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等..
如图:
2.将人脸检测后的face_token存储到faceset
选择的参数为:
api_key
api_secret
outer_id
face_token
产生了face_count等..
如图:
返回值说明
字段 | 说明 |
---|
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
- 如图:
- 再往faceset里存一张人脸:
- 如图:检测到了两张人脸
返回值说明
字段 | 说明 |
---|
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 | 当请求失败时才会返回此字符串,具体返回内容见后续错误信息章节。否则此字段不存在 |
删除人脸
请求参数(必选):
参数名 | 参数说明 |
---|
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 |
如图: 此时搜索到有两张人脸
删除人脸
选择的参数为:
api_key
api_secret
outer_id
face_tokens
face_tokens可以在获取人脸里得到
产生了face_count等..
结果: 删除一张人脸后再获取一次 只剩一张人脸了
返回值说明
字段 | 说明 |
---|
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>
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);
}
}
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;
@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();
multipartBodyBuilder.part("api_key", apiKey);
multipartBodyBuilder.part("api_secret", apiSecret);
multipartBodyBuilder.part("image_file", new FileSystemResource(Paths.get(filePath)), MediaType.IMAGE_PNG);
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();
}
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);
}
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();
}
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);
}
public boolean compareFace(String faceToken1, String faceToken2) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
multipartBodyBuilder.part("api_key", apiKey);
multipartBodyBuilder.part("api_secret", apiSecret);
multipartBodyBuilder.part("face_token1", faceToken1);
multipartBodyBuilder.part("face_token2", faceToken2);
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;
}
@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;
}
}
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);
}
}
public interface FaceService {
public void addFace(String filePath);
public boolean loginByFace(String filePath);
}
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) {
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) {
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;
}
}
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;
@RequestMapping("/toinput")
public String toInput() {
return "/face/input";
}
@RequestMapping("/tologin")
public String toLogin() {
return "/face/login";
}
@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\"}");
}
@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}");
}
}
}
|