使用百度云AI实现刷脸登录

前言

刷脸登录是基于人工智能、生物识别、3D传感、大数据风控技术,最新实现的登录形式。用户在无需输入用户名密码的前提下,凭借“刷脸”完成登录过程。实现刷脸登录的核心是人脸处理,在人脸处理中有两个概念:

  • 人脸检测:检测图中的人脸,并为人脸标记出边框。检测出人脸后,可对人脸进行分析,获得眼、口、鼻轮廓等72个关键点定位准确识别多种人脸属性,如性别,年龄,表情等信息
  • 人脸识别(对比):通过提取人脸的特征,计算两张人脸的相似度,从而判断是否同一个人,并给出相似度评分。

作为中小型企业,可以采取世面上流行的人工智能产品快速的实现刷脸登录需求。目前比较流行人脸检测产品如下:

  • Face++
  • 腾讯优图
  • 科大讯飞
  • 百度云AI (本文使用)

1. 百度云AI

1.1 概述

百度人脸识别基于深度学习的人脸识别方案,准确识别图片中的人脸信息,提供如下功能:

  • 人脸检测:精准定位图中人脸,获得眼、口、鼻等72个关键点位置,分析性别、年龄、表情等多种人脸属性
  • 人脸对比:对比两张人脸的相似度,并给出相似度评分,从而判断是否同一个人
  • 人脸搜索:针对一张人脸照片,在指定人脸集合中搜索,找出最相似的一张脸或多张人脸,并给出相似度分值
  • 活体检测:提供离线/在线方式的活体检测能力,判断操作用户是否为真人,有效抵御照片、视频、模具等作弊攻击
  • 视频流人脸采集:设备端离线实时监测视频流中的人脸,同时支持处理静态图片或者视频流,输出人脸图片并进行图片质量控制

1.2 注册百度云AI并创建应用

(1)打开百度云平台进行账号注册

在这里插入图片描述
(2)激活人脸识别,并创建应用

找到产品-人工智能-人脸识别创建应用

在这里插入图片描述
应用创建完成之后,进入刚刚创建的应用获取开发所需的AppID,API Key,Secret Key。

建议(一定要看):创建完应用后,打开百度云人脸识别官方的帮助文档, 查看JAVA-SDK的具体用法,如人脸注册、人脸检测、人脸查找、人脸更新

2. 刷脸登录实现

2.1 需求分析

由于考虑到有些电脑没有摄像头,所以使用二维码
在这里插入图片描述

  • 用户在登录页面触发刷脸登录功能
  • 在该页面中弹出一个二维码,此二维码是后台即时生成,包含特殊标志(但本质上是一个URL链接),后续登录流程将会使用此标志。用户对该二维码进行扫描,并在扫描端(手机或PC,注:此处不建议使用微信扫描)浏览器打开落地页。
  • 打开落地页时,授权使用摄像头,并进行人脸识别,识别成功后,关闭落地页。
  • 识别成功后,登录页面自动检测到成功标识,并获取相关信息,进入系统主页。
  • 技术点
    • 二维码生成
    • 百度云AI
    • Redis
    • 前端摄像头调用
详细流程图

在这里插入图片描述

2.2 搭建环境

(1) 引入坐标

    <dependencies>
        <!--百度云AI的SDK-->
        <dependency>
            <groupId>com.baidu.aip</groupId>
            <artifactId>java-sdk</artifactId>
            <version>4.8.0</version>
        </dependency>
        <!--二维码-->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.3.0</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
    </dependencies>

(2)添加配置

ai:
  appId: 自己的
  apiKey: 自己的
  secretKey: 自己的
  imageType: BASE64
  groupId: nsx(自定义)
qr:
  url: https://localhost:8080/#/facelogin

(3)创建二维码工具类

配置二维码创建的工具类,生成 Data-url 形式的二维码

@Component
public class QRCodeUtil {
  /**
  * 生成Base64 二维码
  */
  public String crateQRCode(String content) throws IOException {
  	//创建ByteArrayOutputstream
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    try {
      QRCodeWriter writer = new QRCodeWriter();
      BitMatrix bitMatrix = writer.encode(content,BarcodeFormat.QR_CODE, 200,200);
      //将二维码数据以byte数组的形式保存到ByteArrayOutputstream
      BufferedImage bufferedImage =MatrixToImageWriter.toBufferedImage(bitMatrix);
      /**
         * 1:image对象
         * 2:图片格式
         * 3:Outputstream
         */
      ImageIO.write(bufferedImage, "png", os);
      //使用 Data-url 添加图片标识
      //对byte数组进行base64处理
      return new String("data:image/png;base64," +Base64.encode(os.toByteArray()));
   } catch (Exception e) {
      e.printStackTrace();
   } finally {
      os.close();
   }
    return null;
 }
}

在QRCodeUtil类头添加 @Component 注解,使用时可通过 @Autowired 来自动装配。

(4) 创建基本的工程结构

在系统微服务中构建基本的Controller代码

@RestController
@RequestMapping("/sys/faceLogin")
public class FaceLoginController {
  /**
  * 获取刷脸登录二维码
  *   返回值:QRCode对象(code,image)
  *
  */
  @RequestMapping(value = "/qrcode", method = RequestMethod.GET)
  public Result qrcode() throws Exception {
   return null;
 }
  /**
  * 检查二维码:登录页面轮询调用此方法,根据唯一标识code判断用户登录情况
  *   查询二维码扫描状态
  *     返回值:FaceLoginResult
  *       state :-1,0,1 (userId和token)
  */
  @RequestMapping(value = "/qrcode/{code}", method = RequestMethod.GET)
  public Result qrcodeCeck(@PathVariable(name = "code") String code) throws Exception{
	return null;
 }
  /**
  * 人脸登录:根据落地页随机拍摄的面部头像进行登录
  *     根据拍摄的图片调用百度云AI进行检索查找
  */
  @RequestMapping(value = "/{code}", method = RequestMethod.POST)
  public Result loginByFace(@PathVariable(name = "code") String code,@RequestParam(name = "file") MultipartFile attachment) throws Exception{
	return null;
 }
  /**
  * 图像检测,判断图片中是否存在面部头像
  */
  @RequestMapping(value = "/checkFace", method = RequestMethod.POST)
  public Result checkFace(@RequestParam(name = "file") MultipartFile attachment)throws Exception {
	return null;
 }
}

在系统微服务中构建基本的Service代码

@Service
public class FaceLoginService {
  @Value("${qr.url}")
  private String url;
  //创建二维码
  public QRCode getQRCode() throws Exception {
	return null;
 }
  //根据唯一标识,查询用户是否登录成功
  public FaceLoginResult checkQRCode(String code) {
	return null;
 }
   //扫描二维码之后,使用拍摄照片进行登录
  public String loginByFace(String code, MultipartFile attachment)throws Exception {
	return null;
 }
   //构造缓存key
  private String getCacheKey(String code) {
    return "qrcode_" + code;
 }
}

2.3 封装API

对于百度云AI SDK我们进行一些简单的封装,便于使用时,减少代码冗余。

import com.baidu.aip.face.AipFace;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
@Component
public class BaiduAiUtil {
  @Value("${ai.appId}")
  private String APP_ID;
  @Value("${ai.apiKey}")
  private String API_KEY;
  @Value("${ai.secretKey}")
  private String SECRET_KEY;
  @Value("${ai.imageType}")
  private String IMAGE_TYPE;
  @Value("${ai.groupId}")
  private String groupId;
  private AipFace client;
  private HashMap<String, String> options = new HashMap<String, String>();
  public BaiduAiUtil() {
    options.put("quality_control", "NORMAL");
    options.put("liveness_control", "LOW");
 }
  @PostConstruct
  public void init() {
    client = new AipFace(APP_ID, API_KEY, SECRET_KEY);
 }
  /**
  * 人脸注册 :将用户照片存入人脸库中
  */
  public Boolean faceRegister(String userId, String image) {
    // 人脸注册
    JSONObject res = client.addUser(image, IMAGE_TYPE, groupId, userId, options);
    Integer errorCode = res.getInt("error_code");
    return errorCode == 0 ? true : false;
 }
  /**
  * 人脸更新 :更新人脸库中的用户照片
  */
  public Boolean faceUpdate(String userId, String image) {
    // 人脸更新
    JSONObject res = client.updateUser(image, IMAGE_TYPE, groupId, userId,
options);
    Integer errorCode = res.getInt("error_code");
    return errorCode == 0 ? true : false;
 }
  /**
  * 人脸检测:判断上传图片中是否具有面部头像
  */
  public Boolean faceCheck(String image) {
    JSONObject res = client.detect(image, IMAGE_TYPE, options);
    if (res.has("error_code") && res.getInt("error_code") == 0) {
      JSONObject resultObject = res.getJSONObject("result");
      Integer faceNum = resultObject.getInt("face_num");
      return faceNum == 1?true:false;
   }else{
      return false;
   }
 }
  /**
  * 人脸查找:查找人脸库中最相似的人脸并返回数据
  *     处理:用户的匹配得分(score)大于80分,即可认为是同一个用户
  */
  public String faceSearch(String image) {
    JSONObject res = client.search(image, IMAGE_TYPE, groupId, options);
    if (res.has("error_code") && res.getInt("error_code") == 0) {
      JSONObject result = res.getJSONObject("result");
      JSONArray userList = result.getJSONArray("user_list");
      if (userList.length() > 0) {
        JSONObject user = userList.getJSONObject(0);
        double score = user.getDouble("score");
        if(score > 80) {
          return user.getString("user_id");
       }
     }
   }
    return null;
 }
}
  • 在构造方法中,实例化client。通过client,可以调用SDK中包含的各种API。
  • APP_ID, API_KEY, SECRET_KEY在文中第一段中所述位置获取,如没有正确配置,会直接导致API调用失败。
  • 根据官方文档所示,我们大致创建了faceRegister()、faceUpdate()、faceCheck()、faceSearch()四个方法。
    • 人脸注册 faceRegister(groupId, userId, image)
      • groupId:用于人脸库区分人群标识,自定义即可,人脸库会根据提交的groupId,将用户分组
      • userId:人脸库中的用户标识,同组不可重复,自定义即可(通常为系统中用户的唯一标识)
      • image:Base64 用户图片
    • 人脸更新 faceUpdate(groupId, userId, image) 参数解释同人脸注册, 该方法用于发生变化时,更新人脸信息
    • 人脸检测 faceCheck(image), image:Base64 用户图片, 该方法用于人脸注册、人脸更新和人脸登录前使用, 目前采用的方案是检测出人脸数大于0即可,如需深化需求,可按需扩展
    • 人脸登录 faceSearch(image),image:Base64 用户图片,该方法使用的是百度云AI 人脸搜索方法,目前采用的方式是匹配度最高的结果,即要登录的用户

同样的,在BaiduAiUtil类头添加 @Component 注解,使用时可通过 @Autowired 来自动装配。在API调用后返回值处理上,进行了简单的解析,如需深化解析,可按需扩展。

2.4 功能实现

完成刷脸登录一共需要我们解决如下5个问题:

  • 人脸注册/人脸更新
    在刷脸登录之前,我们首先需要对系统中的用户进行人脸注册,将相关信息提交至人脸库,才可通过人脸识别的相关接口进行刷脸登录操作。当用户相貌变更较大时,可通过人脸更新进行人脸信息更换。
  • 二维码生成
    获取验证码。通过工具生成相关信息后,如特殊标志,将特殊标志写入Redis缓存,并将标记值设为”-1“,我们认定值为”-1“,即为当前标记尚未使用。调用QRCodeUtil.crateQRCode()生成二维码。
  • 二维码检测
    前端获取二维码后,对二维码进行展现,并且前台启动定时器,定时检测特殊标记状态值。当状态值为“1”时,表明登录成功。
  • 人脸检测
    当用户扫码进入落地页,通过落地页打开摄像头,并且定时成像。将成像图片,通过接口提交给后端进行人脸检测。
  • 人脸登录
    检测成功后,即进行人脸登录,人脸登录后,改变特殊标记状态值,成功为“1”,失败为“0”。当登录成功时,进行自动登录操作,将token和userId存入到redis中。
2.4.1 后端实现

(1)人脸注册/人脸更新:在刷脸登录之前,我们首先需要对系统中的用户进行人脸注册,将相关信息提交至人脸库,才可通过人脸识别的相关接口进行刷脸登录操作。当用户相貌变更较大时,可通过人脸更新进行人脸信息更换。

BaiduAiUtil

	//判断用户是否已经注册了面部信息
    public Boolean faceExist(String userId) {
        //返回error_code:0 (存在),非0:不存在
        JSONObject res = client.getUser(userId, groupId, null);
        Integer errorCode = res.getInt("error_code");
        return errorCode == 0 ? true : false;
    }

UserService

	@Autowired
    private BaiduAiUtil baiduAiUtil;

    /**
     * 上传到七牛云存储
     * 注册到百度云AI人脸库
     *      1.调用百度云接口,判断当前用户是否已经注册
     *      2.已注册,更新
     *      3.未注册,注册
     */
    public String uploadImage(String id, MultipartFile file) throws IOException {
        //1.根据id查询用户
        User user = userDao.findById(id).get();
        //2.将图片上传到七牛云存储,获取请求路径
        String imgUrl = new QiniuUploadUtil().upload(user.getId(), file.getBytes());//上传图片名,图片的byte数组
        //3.更新用户头像地址
        user.setStaffPhoto(imgUrl);
        userDao.save(user);

        //判断是否已经注册面部信息
        Boolean aBoolean = baiduAiUtil.faceExist(id);
        String imgBase64 = Base64.encode(file.getBytes());
        if (aBoolean) {
            //更新
            baiduAiUtil.faceUpdate(id,imgBase64);
        }else{
            //注册
            baiduAiUtil.faceRegister(id,imgBase64);
        }
        //4.返回
        return imgUrl;
    }

QiniuUploadUtil (这里使用七牛云将图片存储起来你可以不用这步)

import com.google.gson.Gson;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;

import java.util.Date;

public class QiniuUploadUtil {

    private static final String accessKey = "COuoDRVa7JLsuurzIvQSI_pEDceHDw3yGfJEmvwv";
    private static final String secretKey = "3RWpTjB5Jxg3QosUFr4mxbHXJ5JR2m6AHQqYsSlr";
    private static final String bucket = "ihrm-bucket";
    private static final String prix = "http://pkbivgfrm.bkt.clouddn.com/";
    private UploadManager manager;

    public QiniuUploadUtil() {
        //初始化基本配置
        Configuration cfg = new Configuration(Zone.zone0());
        //创建上传管理器
        manager = new UploadManager(cfg);
    }

	//文件名 = key
	//文件的byte数组
    public String upload(String imgName , byte [] bytes) {
        Auth auth = Auth.create(accessKey, secretKey);
        //构造覆盖上传token
        String upToken = auth.uploadToken(bucket,imgName);
        try {
            Response response = manager.put(bytes, imgName, upToken);
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            //返回请求地址
            return prix+putRet.key+"?t="+new Date().getTime();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
}

ps:有关七牛云存储的用法有空再写一篇

(2)二维码生成:获取验证码。通过工具生成相关信息后,如特殊标志,将特殊标志写入Redis缓存,并将标记值设为”-1“,我们认定值为”-1“,即为当前标记尚未使用。调用QRCodeUtil.crateQRCode()生成二维码。

** FaceLoginController**

/**
  * 获取刷脸登录二维码
  */
  @RequestMapping(value = "/qrcode", method = RequestMethod.GET)
  public Result qrcode() throws Exception {
    return new Result(ResultCode.SUCCESS, faceLoginService.getQRCode());
 }

FaceLoginService

public QRCode getQRCode() throws Exception {
    String code = idWorker.nextId() + "";
    FaceLoginResult result = new FaceLoginResult("-1");   
    redisTemplate.boundValueOps(getCacheKey(code)).set(result,30,TimeUnit.MINUTES);
    String strFile = qrCodeUtil.crateQRCode(url + "?code=" + code);
    return new QRCode(code, strFile);
 }

(3)二维码检测:前端获取二维码后,对二维码进行展现,并且前台启动定时器,定时检测特殊标记状态值。当状态值为“1”时,表明登录成功。

FaceLoginController

/**
     * 检查二维码:登录页面轮询调用此方法,根据唯一标识code判断用户登录情况
     *      查询二维码扫描状态
     *          返回值:FaceLoginResult
     *              state :-1,0,1 (userId和token)
     */
    @RequestMapping(value = "/qrcode/{code}", method = RequestMethod.GET)
    public Result qrcodeCeck(@PathVariable(name = "code") String code) throws Exception {
        FaceLoginResult checkQRCode = faceLoginService.checkQRCode(code);
        return new Result(ResultCode.SUCCESS,checkQRCode);
    }

FaceLoginService

	//根据唯一标识,查询用户是否登录成功
    public FaceLoginResult checkQRCode(String code) {
        String key = getCacheKey(code);
        return (FaceLoginResult) redisTemplate.opsForValue().get(key);
    }

(4)人脸检测/人脸登录:当用户扫码进入落地页,通过落地页打开摄像头,并且定时成像。将成像图片,通过接口提交给后端进行人脸检测。

/**
     * 图像检测,判断图片中是否存在面部头像
     */
    @RequestMapping(value = "/checkFace", method = RequestMethod.POST)
    public Result checkFace(@RequestParam(name = "file") MultipartFile attachment) throws Exception {
        String image = Base64Util.encode(attachment.getBytes());
        Boolean aBoolean = baiduAiUtil.faceCheck(image);
        if (aBoolean) {
            return new Result(ResultCode.SUCCESS);
        }else{
            return new Result(ResultCode.FAIL);
        }
    }

(5)检测成功后,即进行人脸登录,人脸登录后,改变特殊标记状态值,成功为“1”,失败为“0”。当登录成功时,进行自动登录操作,将token和userId存入到redis中。
FaceLoginController

 /**
     * 人脸登录:根据落地页随机拍摄的面部头像进行登录
     *          根据拍摄的图片调用百度云AI进行检索查找
     */
    @RequestMapping(value = "/{code}", method = RequestMethod.POST)
    public Result loginByFace(@PathVariable(name = "code") String code, @RequestParam(name = "file") MultipartFile attachment) throws Exception {
        //人脸登录获取用户id(不为null登录成功)
        String userId = faceLoginService.loginByFace(code, attachment);
        if(userId != null) {
            return new Result(ResultCode.SUCCESS);
        }else{
            return new Result(ResultCode.FAIL);
        }
    }

FaceLoginService

	//扫描二维码之后,使用拍摄照片进行登录
    //返回值:登录成功之后返回的用户id
    //登录失败:null
    public String loginByFace(String code, MultipartFile attachment) throws Exception {
        //1.调用百度云AI查询当前的用户
        String userId = baiduAiUtil.faceSearch(Base64Util.encode(attachment.getBytes()));
        //2.自动登录(token)
        FaceLoginResult result = new FaceLoginResult("0");
        if(userId != null) {
            //自己模拟登录
            //查询用户对象
            User user = userDao.findById(userId).get();
            if(user != null) {
                //获取subject
                Subject subject = SecurityUtils.getSubject();
                //调用login方法登录
                subject.login(new UsernamePasswordToken(user.getMobile(),user.getPassword()));
                //获取token(sessionId)
                String token = subject.getSession().getId()+"";
                result = new FaceLoginResult("1",token,userId);
            }
        }
        //3.修改二维码的状态
        redisTemplate.boundValueOps(getCacheKey(code)).set(result,10,TimeUnit.MINUTES);
		return userId;
    }

由于代码比较多,若想继续了解,打开以下资源和视频

资源:
链接:https://pan.baidu.com/s/1ltZarFu-tw2F0JJ7Tnjbog
提取码:1234
复制这段内容后打开百度网盘手机App,操作更方便哦

视频:
https://www.bilibili.com/video/BV1QJ411S7c4?p=217

转载https://www.bilibili.com/video/BV1QJ411S7c4?p=217

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值