使用虹软SDK实现离线人脸注册,人脸登录(H5-JS前端,java后台)

简介: 一开始找人脸识别的第三方接口,选择了百度,就是发请求给百度的接口,解析人家返回的数据。

前言:


一开始找人脸识别的第三方接口,选择了百度,就是发请求给百度的接口,解析人家返回的数据。


但是这样的话,如果没有网络,或者没有外网。程序在局域网中使用的时候,自然就gg了。


人脸识别嘛,大家了解的最多的就是现在手机自带的人脸识别,这个肯定不会说在你没有网络的情况下用不了。


然后就找离线人脸识别吧,就发现虹软这个可以把算法下载到本地……


第一步,获取SDK

首先注册开发者账号,创建一个应用,得到两个东西,用于激活SDK引擎的,SDK key 和 AppID。


百度搜索“虹软”,进入官网,点击右上角开放平台下的人脸识别SDK,如图:

然后进入如下页面点击右上角开发者中心新建应用,获取SDK之前,还有不可或缺的一步……实名认证

提交完资料一般两个小时之内就有结果了。

-

创建完应用点击这个下载按钮把SDK下载到本地,我这里选择的是java版本的,我也不会其他的…… 下载完后解压,就是这样一个文件夹

文件夹中的doc文件夹中的一个pdf文档,就是开发者文档了。

文件夹中包括开发者文档、API文档、示例程序、jar包、引擎库dll文件。

文档中有对这个文件夹结构的详细介绍,这里就不重复了。

第二步,写后台

springboot项目,maven管理,项目结构:把文件夹中的jar包复制到项目中的lib(自己创建的)文件夹下,

依赖这样写:

----我这里用的是SQLserver

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.3.RELEASE</version>
</parent>
<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
  <dependency>
         <groupId>org.mybatis.spring.boot</groupId>
         <artifactId>mybatis-spring-boot-starter</artifactId>
         <version>1.3.5</version>
     </dependency>
     <dependency>
    <groupId>com.microsoft.sqlserver</groupId>
    <artifactId>sqljdbc4</artifactId>
    <version>4.0</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
  </dependency>
  <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.5</version>
    </dependency>
  <dependency>
    <groupId>com.arcsoft.face</groupId>
    <artifactId>arcsoft-sdk-face</artifactId>
    <version>2.2.0.1</version>
    <scope>system</scope>
    <systemPath>${basedir}/lib/arcsoft-sdk-face-2.2.0.1.jar</systemPath>
  </dependency>
</dependencies>
<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <configuration>
        <includeSystemScope>true</includeSystemScope>
      </configuration>
    </plugin>
  </plugins>
  <finalName>face</finalName>
</build>

还要创建一个temp文件夹存放临时生成的图片文件。


启动类就不贴了,下面这个是引擎激活类:


我放在了启动包下,这个类要单独运行一遍,用来激活在这个设备的引擎,如果换了设备还要再运行激活一遍


/**
 * 设备引擎激活
* @author zsz
* @version 
* @创建时间:2019年10月29日 下午2:51:24
*/
public class FaceActivation{
  public static void main(String[] args) {
        //填自己的AppID和SDKkey
  String appId = "";
        String sdkKey = "";
        //dll文件所在目录
        FaceEngine faceEngine = new FaceEngine("D:\\人脸识别\\ArcSoft_ArcFace_Java_Windows_x64_V2.2\\libs\\WIN64");
        //激活引擎
        int activeCode = faceEngine.activeOnline(appId, sdkKey);
        if (activeCode != ErrorInfo.MOK.getValue() && activeCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
            System.out.println("引擎激活失败");
        }else {
          System.out.println("引擎激活成功");
        }
  }
}

Utils包里面是一个base64跟图片互转的工具类,反正不是自己写的……


import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class Base64Utils {
     /**
      * 图片转化成base64字符串
      * @param imgPath
      * @return
      */
     public static String GetImageStr(String imgPath) {// 将图片文件转化为字节数组字符串,并对其进行Base64编码处理
         String imgFile = imgPath;// 待处理的图片
         InputStream in = null;
         byte[] data = null;
         String encode = null; // 返回Base64编码过的字节数组字符串
         // 对字节数组Base64编码
         BASE64Encoder encoder = new BASE64Encoder();
         try {
             // 读取图片字节数组
             in = new FileInputStream(imgFile);
             data = new byte[in.available()];
             in.read(data);
             encode = encoder.encode(data);
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 in.close();
             } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
         }
         return encode;
     }
     /**
      * base64字符串转化成图片
      * 
      * @param imgData
      *            图片编码
      * @param imgFilePath
      *            存放到本地路径
      * @return
      * @throws IOException
      */
     @SuppressWarnings("finally")
     public static boolean GenerateImage(String imgData, String imgFilePath) throws IOException { // 对字节数组字符串进行Base64解码并生成图片
         if (imgData == null) // 图像数据为空
             return false;
         BASE64Decoder decoder = new BASE64Decoder();
         OutputStream out = null;
         try {
             out = new FileOutputStream(imgFilePath);
             // Base64解码
             byte[] b = decoder.decodeBuffer(imgData);
             for (int i = 0; i < b.length; ++i) {
                 if (b[i] < 0) {// 调整异常数据
                     b[i] += 256;
                 }
             }
             out.write(b);
         } catch (FileNotFoundException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         } finally {
             out.flush();
             out.close();
             return true;
         }
     }
  }

实体类包pojo,一个User类,跟数据库的表是对应的,


在SQLserver中的表设计是这样的


public class User {
  //用户名
  private String username;
  //用户人脸特征
  private byte[] extract;
  public User(String username, byte[] extract) {
  super();
  this.username = username;
  this.extract = extract;
  }
  public String getUsername() {
  return username;
  }
  public void setUsername(String username) {
  this.username = username;
  }
  public byte[] getExtract() {
  return extract;
  }
  public void setExtract(byte[] extract) {
  this.extract = extract;
  }
}

face包里面有两个类,一个获取人脸特征的,一个人脸特征对比的。


获取人脸特征:


public class MyFaceFeature {
  public byte[] extract(File file,FaceEngine faceEngine) {
  //人脸检测
        ImageInfo imageInfo = getRGBData(file);
        List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
        int detectCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList);
        if(faceInfoList == null || faceInfoList.size()==0) {
          return null;
        }
        //人脸属性检测
        FunctionConfiguration configuration = new FunctionConfiguration();
        configuration.setSupportAge(true);
        configuration.setSupportFace3dAngle(true);
        configuration.setSupportGender(true);
        configuration.setSupportLiveness(true);
        int processCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList, configuration);
        //3D信息检测
        List<Face3DAngle> face3DAngleList = new ArrayList<Face3DAngle>();
        int face3dCode = faceEngine.getFace3DAngle(face3DAngleList);
        if(face3DAngleList == null || face3DAngleList.size() == 0) {
          return null;
        }
        System.out.println("3D角度:" + face3DAngleList.get(0).getPitch() + "," + face3DAngleList.get(0).getRoll() + "," + face3DAngleList.get(0).getYaw());
        //活体检测
        List<LivenessInfo> livenessInfoList = new ArrayList<LivenessInfo>();
        int livenessCode = faceEngine.getLiveness(livenessInfoList);
        System.out.println("活体:" + livenessInfoList.get(0).getLiveness());
        //只有等于1才是活体,-1是没有人脸,0是非活体(照片之类的),2是多张人脸
        if(livenessInfoList.get(0).getLiveness()!=1) {
          return null;
        }
        //特征提取
        FaceFeature faceFeature = new FaceFeature();
        int extractCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList.get(0), faceFeature);
        //返回特征值
  return faceFeature.getFeatureData();
  }
}

人脸特征对比:

public class FaceContrast {
  public float contrast(byte[] face1,byte[] face2,FaceEngine faceEngine) {
   //特征比对
        FaceFeature targetFaceFeature = new FaceFeature();
        targetFaceFeature.setFeatureData(face1);
        FaceFeature sourceFaceFeature = new FaceFeature();
        sourceFaceFeature.setFeatureData(face2);
        FaceSimilar faceSimilar = new FaceSimilar();
        int compareCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
        //返回相似度
        return faceSimilar.getScore();
  }
}

到这里就剩下controller层service层和dao层了。


controller层:


一个注册一个登录


@RestController
@RequestMapping("face")
@CrossOrigin
public class FaceController {
  FaceEngine faceEngine = new FaceEngine();
  {
  // 引擎配置
  EngineConfiguration engineConfiguration = new EngineConfiguration();
  //检测模式 图片/视频,这里选择的是图片模式
  engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
  //人脸检测角度
  engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);
  // 功能配置
  FunctionConfiguration functionConfiguration = new FunctionConfiguration();
  //是否支持年龄检测
  functionConfiguration.setSupportAge(true);
  //是否支持3D人脸检测
  functionConfiguration.setSupportFace3dAngle(true);
  //是否支持人脸检测
  functionConfiguration.setSupportFaceDetect(true);
  //是否支持人脸识别
  functionConfiguration.setSupportFaceRecognition(true);
  //是否支持性别检测
  functionConfiguration.setSupportGender(true);
  //是否支持RGB活体检测
  functionConfiguration.setSupportLiveness(true);
  //是否支持RGB活体检测
  functionConfiguration.setSupportIRLiveness(true);
  //设置引擎功能配置
  engineConfiguration.setFunctionConfiguration(functionConfiguration);
  // 初始化引擎
  int initCode = faceEngine.init(engineConfiguration);
  if (initCode != ErrorInfo.MOK.getValue()) {
    System.out.println("初始化引擎失败");
  }
  }
  @Autowired
  private FaceService service;
  @RequestMapping("add")
  public String faceAdd(String user_id, String base) {
  // 去掉base64编码中的图片头信息
  String base64 = base.split(",")[1];
  try {
    //base64转图片
    Base64Utils.GenerateImage(base64, "./temp/temp.jpg");
  } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
  //获取人脸特征
  byte[] extract = new MyFaceFeature().extract(new File("./temp/"+user_id+"temp.jpg"), faceEngine);
  if(extract == null) {
    return "{\"msg\":\"检测人脸失败\"}";
  }
  //把人脸特征报错到数据库
  return service.faceAdd(new User(user_id,extract))?"{\"msg\":\"注册成功\"}":"{\"msg\":\"注册失败\"}";
  }
  @RequestMapping("login")
  public String login(String base) {
  // 去掉base64编码中的图片头信息
  String base64 = base.split(",")[1];
  try {
    Base64Utils.GenerateImage(base64, "./temp/temp.jpg");
  } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
  //获取当前人脸特征
  byte[] extract = new MyFaceFeature().extract(new File("./temp/temp.jpg"), faceEngine);
  if(extract==null) {
    return null;
  }

 //查询所有人脸 一一对比

 人脸对比对象

 FaceContrast faceContrast = new FaceContrast();

 获取所有注册过的用户

 List<User> userList = service.getAllUserFace();

 循环对比

for(User user : userList) {
    //如果大于0.8就表示是同一人
    if(faceContrast.contrast(extract, user.getExtract(), faceEngine)>0.8) {
    System.out.println("成功");
    return "{\"user_id\":\""+user.getUsername()+"\"}";
    }
  }
  System.out.println("失败");
  return null;
  }
}

server包 ,service层:


没有业务,直接传给下一层


@Service
public class FaceService {
  @Autowired
  private FaceMapper mapper;
  //注册人脸
  public Boolean faceAdd(User user) {
  return mapper.add(user);
  }
  //查找人脸
  public List<User> getAllUserFace() {
  return mapper.getAllUser();
  }
}
mapper包,dao层
@Mapper
public interface FaceMapper {
  @Insert("insert into user_face values(#{username},#{extract})")
  Boolean add(User user);
  @Select("select * from user_face")
  List<User> getAllUser();
}

后台就是这些,注释写的比较详细了


在启动项目的时候会报错,no libarcsoft_face_engine_jni in java.library.path


见另一个博客:使用虹软SDK的 时候启动项目出现no libarcsoft_face_engine_jni in java.library.path_阿演的博客-CSDN博客


接下来就是前端


一个index页面和,两个按钮,跳转到注册和登录


页面:


<!DOCTYPE html>
<html>
  <head>
  <meta charset="UTF-8">
  <title>主页</title>
  </head>
  <body>
  <button id="register" style="width: 700px;height: 100px;">注册</button>
  <button id="login" style="width: 700px;height: 100px;">登录</button>
  </body>
  <script src="js/index.js"></script>
</html>
JS:
document.getElementById("register").onclick = function(){
  window.location.href = "register.html";
}
document.getElementById("login").onclick = function(){
  window.location.href = "login.html";
}
 注册页面register
页面:
<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8" />
  <title>人脸注册</title>
  </head>
  <body>
  <video id="video" src="" autoplay="autoplay" loop="loop" muted="muted"></video>
  <canvas id="canvas" style="border:1px solid #d3d3d3;"></canvas>
  <br />
  <input type="text" id="user_id" placeholder="请输入账号" style="width: 700px; height: 100px; font-size: 40px;"/>
  <div id="support"></div>
  <button id="ok" style="width: 700px;height: 100px; font-size: 40px;">确认</button>
  <div id="msg" style="font-size: 50px;"></div>
  </body>
  <script src="js/register.js"></script>
  <script src="js/jquery-1.8.2.min.js"></script>
</html>
JS:
//判断浏览器是否支持HTML5 Canvas
window.onload = function() {
  try {
  //动态创建一个canvas元 ,并获取他2Dcontext。如果出现异常则表示不支持 
  document.createElement("canvas").getContext("2d");
  } catch(e) {
  document.getElementByIdx("support").innerHTML = "浏览器不支持HTML5 CANVAS";
  }
}
//这段代 主要是获取摄像头的视频流并显示在Video 签中
window.addEventListener("DOMContentLoaded", function() {
  var canvas = document.getElementById("canvas"),
  context = canvas.getContext("2d"),
  img = document.getElementById("img"),
  video = document.getElementById("video"),
  videoObj = {
    "video": true
  },
  errBack = function(error) {
    console.log("Video capture error: ", error.code);
    alert("不支持");
  };
  canvas.width = 450;
  canvas.height = 600;
  var button = document.getElementById("ok");
  button.onclick = function(){
  context.drawImage(video, 0, 0, 450, 600);
  // 把画布的内容转换为base64编码格式的图片
     var data = canvas.toDataURL( 'image/png', 1 );  //1表示质量(无损压缩)
  var user_id = document.getElementById("user_id").value;
  document.getElementById("msg").innerText = "注册中……";
     $.ajax({
            url: 'http://172.16.2.207:8080/face/add',
            cache:false,
            type: 'POST',
            data: {
    base : data,
    user_id : user_id
      },
            dataType: 'json',
            success : function(rs){
    document.getElementById("msg").innerText = rs.msg;
            },
            error: function(e){
              console.log(e);
            }
        });
  }
  //请求摄像头权限
  navigator.mediaDevices.getUserMedia(videoObj)
  .then(function(stream){
    //成功回调函数,把流给video标签
    video.srcObject = stream;
    video.play();
  })
  .catch(errBack);
}, false);
登录页面login
页面:
<!DOCTYPE html>
<html>
  <head>
  <meta charset="UTF-8">
  <title></title>
  </head>
  <body>
  <video id="video" src="" autoplay="autoplay" loop="loop" muted="muted"></video>
  <canvas id="canvas" style="border:1px solid #d3d3d3;"></canvas>
  <div id="support"></div>
  <div id="msg" style="font-size: 50px;"></div>
  </body>
  <script type="text/javascript" src="js/login.js" ></script>
  <script src="js/jquery-1.8.2.min.js"></script>
</html>
JS:
//判断浏览器是否支持HTML5 Canvas
window.onload = function() {
  try {
  //动态创建一个canvas元 ,并获取他2Dcontext。如果出现异常则表示不支持 
  document.createElement("canvas").getContext("2d");
  } catch(e) {
  document.getElementByIdx("support").innerHTML = "浏览器不支持HTML5 CANVAS";
  }
}
//这段代 主要是获取摄像头的视频流并显示在Video 签中
window.addEventListener("DOMContentLoaded", function() {
  var canvas = document.getElementById("canvas"),
  context = canvas.getContext("2d"),
  video = document.getElementById("video"),
  videoObj = {
    "video": true
  },
  errBack = function(error) {
    console.log("Video capture error: ", error.code);
    alert("不支持");
  };
  canvas.width = 450;
  canvas.height = 600;
  //请求摄像头权限
  this.navigator.mediaDevices.getUserMedia(videoObj)
  .then(function(stream){
  video.srcObject = stream;
  video.play();
  //拍照按钮,每0.5秒canvas从video中画到画布,转成base64发给后台
  var login = setInterval(function() {
    context.drawImage(video, 0, 0, 450, 600);
    // 把画布的内容转换为base64编码格式的图片
      var data = canvas.toDataURL( 'image/png', 1 );  //1表示质量(无损压缩)
    $.ajax({
             url: 'http://172.16.2.207:8080/face/login',
             cache:false,
             type: 'POST',
             data: {
      base: data
        },
             dataType: 'json',
             success : function(rs){
              if(rs!=null){
                document.getElementById("msg").innerText = "用户"+rs.user_id+"登录成功";
                //隐藏控件
                $("#video").hide();
        $("#canvas").hide();
      //停止定时发送
                clearInterval(login);
              }
             },
             error: function(e){
             }
         });
  }, 500);
  })
  .catch(errBack);
}, false);

前端就是这些了,注释也加的很详细了。


后台代码码云地址:face: 虹软人脸识别


如果本文帮助到了你,别忘了点赞加关注哦


你点的每个赞,我都认真当成了喜欢


推荐阅读:做了个springboot接口参数解密的工具,我给它命名为万能钥匙(已上传maven中央仓库,附详细使用说明)


相关文章
|
20天前
|
前端开发 小程序 Java
uniapp上传图片 前端以及java后端代码实现
uniapp上传图片 前端以及java后端代码实现
33 0
|
22天前
|
前端开发 JavaScript 网络协议
前端最常见的JS面试题大全
【4月更文挑战第3天】前端最常见的JS面试题大全
45 5
|
1月前
|
JavaScript 前端开发 Java
纯前端JS实现人脸识别眨眨眼张张嘴案例
纯前端JS实现人脸识别眨眨眼张张嘴案例
52 0
|
1月前
|
前端开发 JavaScript 数据安全/隐私保护
推荐6款超级好看的开源中后台前端框架
推荐6款超级好看的开源中后台前端框架
64 0
|
1月前
|
文字识别 算法 小程序
视觉智能平台常见问题之人脸人体的sdk返回报错如何解决
视觉智能平台是利用机器学习和图像处理技术,提供图像识别、视频分析等智能视觉服务的平台;本合集针对该平台在使用中遇到的常见问题进行了收集和解答,以帮助开发者和企业用户在整合和部署视觉智能解决方案时,能够更快地定位问题并找到有效的解决策略。
33 4
|
1月前
|
前端开发 Java 数据库连接
Java代码打造的注册系统
Java代码打造的注册系统
19 0
|
1月前
|
弹性计算 前端开发 小程序
微信小程序上传文件至阿里云OSS直传(java后端签名+前端直传)
当前的通用文件上传方式是通过前端上传到服务器,再由服务器转存至对象存储。这种方式在处理小文件时效率尚可,但大文件上传因受限于服务器带宽,速度较慢。例如,一个100MB的文件在5Mbps带宽的阿里云ECS上上传至服务器需160秒。为解决此问题,可以采用后端签名的方式,使微信小程序直接上传文件到阿里云OSS,绕过服务器中转。具体操作包括在JAVA后端引入相关依赖,生成签名,并在微信小程序前端使用这个签名进行文件上传,注意设置正确的请求头和formData参数。这样能提高大文件上传的速度。
|
1天前
|
JavaScript 前端开发 API
Node.js在前端的妙用:打造更出色的Web体验
Node.js在前端的妙用:打造更出色的Web体验
16 5
|
1月前
|
JavaScript 前端开发 算法
深入探讨前端框架Vue.js中的虚拟DOM机制
本文将深入探讨前端框架Vue.js中的虚拟DOM机制,分析其原理、优势以及在实际开发中的应用场景,帮助读者更好地理解Vue.js框架的核心特性。
|
1月前
|
JavaScript
当当网新用户注册界面——JS代码
当当网新用户注册界面——JS代码
7 0