• java使用face++简单实现人脸识别注册登录


    java使用face++简单实现人脸识别注册登录

    前言

    人脸识别,好高大上!!!

    理解之后很简单。

    支付宝使用的就是face++,

    至于face++账号信息,apikey…..,本文不做讲述,网上很多.

    一.设计思想

    1.    先想一想,如果让你实现人脸识别,你会怎么做?

    寻找图片上的关键点,制作一套算法,分析脸部信息,将得到的数据存入数据库,

    登录的时候,通过同样的算法,分析数据,和数据库中存入的信息进行比对……

    工作量好大!!!

    face++有着它独有的非常优秀的算法,我们可以将我们的图片上传到face++服务器来获取对应的图片数据,剩下的事情就很简单了

    2.    四个face++  api简介:

    这里只介绍用到的api,

    2.1 使用api肯定需要先注册一下信息,获取api_key和api_secret,可以注册试用的进行获取

     

    2.2 create

    作用: 创建一个FaceSet,创建一个脸部信息集合,引用官网的描述:

     

    url:https://api-cn.faceplusplus.com/facepp/v3/faceset/create

    参数:api_key,api_secret,……

     

    返回值: faceset_token, outer_id……,这里写的两个返回值需要记住,是这个脸部信息集合的唯一标识,具体返回值信息如下图:

     

    2.3   addFace

    作用:向脸部信息集合faceSet添加一条或多条脸部信息,便于后期搜索

    url: https://api-cn.faceplusplus.com/facepp/v3/faceset/addface

    参数: 不用说,肯定需要,api_key,api_secret,faceSet_token或outer_id(脸部信息唯一标识),还有图片信息,官网截图:

     

    返回值:可以获取插入的结果信息

     

    .

    2.4 Search

    作用: 传入一张图片信息到face++服务器,会返回最相似的face_token

    url: https://api-cn.faceplusplus.com/facepp/v3/search

    参数:api_key,api_secret,image_url或image_file或image_base64或face_token,详细参数列表如下

    是否必选

    参数名

    类型

    参数说明

    必选

    api_key

    String

    调用此 API 的 API Key

    必选

    api_secret

    String

    调用此 API 的 API Secret

    必选(四选一)

    face_token

    String

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

    image_url

    String

    目标人脸所在的图片的 URL

    image_file

    File

    目标人脸所在的图片,二进制文件,需要用 post multipart/form-data 的方式上传。

    image_base64

    String

    base64 编码的二进制图片数据

    如果同时传入了 image_url、image_file 和 image_base64 参数,本 API 使用顺序为 image_file 优先,image_url 最低。

    必选(二选一)

    faceset_token

    String

    用来搜索的 FaceSet 的标识

    outer_id

    String

    用户自定义的 FaceSet 标识

    可选

    return_result_count

    Int

    控制返回比对置信度最高的结果的数量。合法值为一个范围 [1,5] 的整数。默认值为 1

    可选(仅正式 API Key 可以使用)

    face_rectangle

    String

    当传入图片进行人脸检测时,是否指定人脸框位置进行检测。

    如果此参数传入值为空,或不传入此参数,则不使用此功能。本 API 会自动检测图片内所有区域的所有人脸。

    如果使用正式 API Key 对此参数传入符合格式要求的值,则使用此功能。需要传入一个字符串代表人脸框位置,系统会根据此坐标对框内的图像进行人脸检测,以及人脸关键点和人脸属性等后续操作。系统返回的人脸矩形框位置会与传入的 face_rectangle 完全一致。对于此人脸框之外的区域,系统不会进行人脸检测,也不会返回任何其他的人脸信息。

    参数规格:四个正整数,用逗号分隔,依次代表人脸框左上角纵坐标(top),左上角横坐标(left),人脸框宽度(width),人脸框高度(height)。例如:70,80,100,100

    注:只有在传入 image_url、image_file 和 image_base64 三个参数中任意一个时,本参数才生效。

    返回值:返回值包含和你传入图片信息最像的图片的face_token,(可以再和数据库中对应的信息进行比较)

     

    2.4   Detect

    作用:

    传入一张图片信息,获取这张图片的face_token,注意,一张相同图片获取多次的face_token不同

    url: https://api-cn.faceplusplus.com/facepp/v3/detect

    参数:api_key,api_secret, image_url或image_file或image_base64,

    是否必选

    参数名

    类型

    参数说明

    必选

    api_key

    String

    调用此API的API Key

    必选

    api_secret

    String

    调用此API的API Secret

    必选(三选一)

    image_url

    String

    图片的 URL。

    注:在下载图片时可能由于网络等原因导致下载图片时间过长,建议使用 image_file 或 image_base64 参数直接上传图片。

    image_file

    File

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

    image_base64

    String

    base64 编码的二进制图片数据

    如果同时传入了 image_url、image_file 和 image_base64 参数,本API使用顺序为 image_file 优先,image_url 最低。

    可选

    return_landmark

    Int

    是否检测并返回人脸关键点。合法值为:

    检测。返回 106 个人脸关键点。

    1

    检测。返回 83 个人脸关键点。

    0

    不检测

    注:本参数默认值为 0

    可选

    return_attributes

    String

    是否检测并返回根据人脸特征判断出的年龄、性别、情绪等属性。合法值为:

    none

    不检测属性

    • gender
    • age
    • smiling
    • headpose
    • facequality
    • blur
    • eyestatus
    • emotion
    • ethnicity
    • beauty
    • mouthstatus
    • eyegaze
    • skinstatus

    希望检测并返回的属性。

    需要将属性组成一个用逗号分隔的字符串,属性之间的顺序没有要求。

    关于各属性的详细描述,参见下文“返回值”说明的 "attributes" 部分。

    注:在此参数中的传入参数smiling,对应在返回值的attributes中参数名为smile。在使用时请注意。

    注:本参数默认值为 none

    可选(仅正式 API Key 可以使用)

    calculate_all

    Int

    是否检测并返回所有人脸的人脸关键点和人脸属性。如果不使用此功能,则本 API 只会对人脸面积最大的五个人脸分析人脸关键点和人脸属性。合法值为:

    1

    0

    注:本参数默认值为 0

    可选(仅正式 API Key 可以使用)

    face_rectangle

    String

    是否指定人脸框位置进行人脸检测。

    如果此参数传入值为空,或不传入此参数,则不使用此功能。本 API 会自动检测图片内所有区域的所有人脸。

    如果使用正式 API Key 对此参数传入符合格式要求的值,则使用此功能。需要传入一个字符串代表人脸框位置,系统会根据此坐标对框内的图像进行人脸检测,以及人脸关键点和人脸属性等后续操作。系统返回的人脸矩形框位置会与传入的 face_rectangle 完全一致。对于此人脸框之外的区域,系统不会进行人脸检测,也不会返回任何其他的人脸信息。

    参数规格:四个正整数,用逗号分隔,依次代表人脸框左上角纵坐标(top),左上角横坐标(left),人脸框宽度(width),人脸框高度(height)。例如:70,80,100,100

    可选

    beauty_score_min

    Int

    颜值评分分数区间的最小值。默认为0

    注:默认颜值评分分数区间为0-100.可通过beauty_score_min和beauty_score_max来调节分数区间,满足您的场景需求。

    可选

    beauty_score_max

    Int

    颜值评分分数区间的最大值。默认为100

    返回值:图片对应的face_token

    字段

    类型

    说明

    request_id

    String

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

    faces

    Array

    被检测出的人脸数组,具体包含内容见下文。

    注:如果没有检测出人脸则为空数组

    image_id

    String

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

    time_used

    Int

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

    error_message

    String

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

    在faces中包含face_token

    3.    设计分析

    1. 创建调用create api创建faceSet,取得faceSet_token,对应你的一张用户信息表
    2. 注册时:调用detect api传入用户注册的图片信息,获取face_token,

    将face_token存入faceSet,(调用addFace api存入)

    将face_token存入数据库

    1. 登录: 从前端获取用户图片,将图片编码为base64作为参数image_base64调用search api

    返回值为在faceSet中,和传入图片相似度高的face_token

    通过返回的face_token,在数据库中进行查询,实现登陆

    二.用到的技术

    有了上面的分析,即使使用javaweb也能实现了

    本案例使用

    maven

    java的ssm框架

    配上Druid连接池

    前端使用了jquery,(不懂前端,通过参考和自己设计写的很low)

    三.实现

    3.1前端界面:

    实体类:

    User{

             String username;

             String password;

             String other;      //在本案例中没有作用

             String faceToken;

    }

             技术不高,自己写的一个简单的界面

    注册界面:register.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="js/jquery-3.3.1.js"></script>
        <style>
            * {
                margin: 0;
                padding: 0;
            }

            html, body {
                width: 100%;
                height: 100%;
            }

            body {
                background: url(img/bg.jpg) no-repeat center;
            }

            h1 {
                color: #fff;
                text-align: center;
                line-height: 80px;
            }

            .media {
                width: 534px;
                height: 400px;
                margin: 40px auto 0;
                transform-style: preserve-3d;
                transform: perspective(600px) rotateY(25deg);
            }

            #register {
                width: 200px;
                height: 50px;
                background: #0089ff;
                margin: 60px auto 0;
                text-align: center;
                line-height: 50px;
                color: #fff;
                /*      color: red;*/
               
    border-radius: 16px;
                transform-style: preserve-3d;
                transform: perspective(600px) rotateY(25deg) rotateZ(-10deg);
                cursor: pointer;

            }
        </style>
    </head>
    <body>
    <!--autoplay="true"-->
    <div class="media">
        <video id="myVideo" height="534" width="400" src="" autoplay></video>
        <canvas id="myCanvas" height="534" width="400"></canvas>
    </div>

    <!--创建一个注册的按钮-->
    <form action="register.do" id="registerForm">
        用户名: <input type="text" name="username"><br>
        密码 :<input type="text" name="password"><br>
        <input type="hidden" name="faceToken" id="faceToken" value=""><br>
        备注字段:<input type="text" name="other"><br>
        <input type="button" id="toUpPic" value="上传图片">
        <input type="button" id="register" value="注册">
    </form>
    <!--<div id="login">注册</div>-->
    <script>
        //这里写的是网页脚本
        //调用摄像头获取媒体视频流
        /***
         * 默认的写法:navigator.getUserMedia
         * 火狐:navigator.mozGetUserMedia
         * 微软:navigator.msGetUserMedia
         * 谷歌:navigator.webkitGetUserMedia
         *
         * @type {((constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback) => void) | *}
         */
       
    var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);

        var video = document.getElementById("myVideo");
        /***
         *
    四个参数 getUserMedia.call(navigator,{video:true,audio:false},function(){},function(){});
         *  1.要调用的对象
         *  2.约束对象:eg:只调用视频
         *  3.调用成功的方法
         *  4.调用失败的方法
         */
       
    getUserMedia.call(navigator, {video: true, audio: false}, function (localMediaStream) {
            //这里是调用成功的方法,如果调用成功,将视频流对象传到myVideo,localMediaStream是传入的视频流对象

            /*document.getElementById("myVideo").src= window.URL.createObjectURL(localMediaStream);
            * 上一行的代码已经过时了
            * */
           
    try {
                video.src = window.URL.createObjectURL(localMediaStream);
            } catch (e) {
                //执行的是这段代码
               
    video.srcObject = localMediaStream;
            }
            /***
             *
    下面三行代码可以代替了video的autoplay属性
             */
            /*        video.onloadedmetadata = function () {
                        video.play();
                    }*/

       
    }, function (e) {
            console.log("获取摄像头失败", e);//通过控制台将我们的错误信息打印
       
    });

        //获取登陆按钮
       
    var btn_register = document.getElementById("register");
        var toUpPic = document.getElementById("toUpPic");
        //获取canvas对象
       
    var canvas = document.getElementById("myCanvas");
        //获取上下文对象
       
    var context = canvas.getContext("2d");
        //登陆按钮绑定点击事件
       
    toUpPic.onclick = function () {
            //点击登录按钮获取面部信息,(点击登录按钮的时候将图像画到)
            // context.drawImage(video,x轴开始位置,y轴开始位置,x轴结束位置,y轴结束位置);
           
    context.drawImage(video, 0, 0, 534, 400);
            //image/png 表示画成什么格式
           
    var imgSrc = document.getElementById("myCanvas").toDataURL("image/png");
            alert(imgSrc);
            // var Baseimg=imgSrc.split(",")[1];
           
    $.post("getFaceToken.do", {imgSrc: imgSrc}, function (faceToken) {
                alert(faceToken);
                if (faceToken) {
                    $("#faceToken").val(faceToken);
                } else {
                    alert("登录失败,请重新扫描");
                }
            });


        }
        btn_register.onclick=function () {
            $("#registerForm").submit();
        }

    </script>
    </body>
    </html>

    登录界面:login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="js/jquery-3.3.1.js"></script>
        <style>
            * {
                margin: 0;
                padding: 0;
            }

            html, body {
                width: 100%;
                height: 100%;
            }

            body {
                background: url(img/bg.jpg) no-repeat center;
            }

            h1 {
                color: #fff;
                text-align: center;
                line-height: 80px;
            }

            .media {
                width: 534px;
                height: 400px;
                margin: 40px auto 0;
                transform-style: preserve-3d;
                transform: perspective(600px) rotateY(25deg);
            }

            #login {
                width: 200px;
                height: 50px;
                background: #0089ff;
                margin: 60px auto 0;
                text-align: center;
                line-height: 50px;
                color: #fff;
                /*      color: red;*/
               
    border-radius: 16px;
                transform-style: preserve-3d;
                transform: perspective(600px) rotateY(25deg) rotateZ(-10deg);
                cursor: pointer;

            }
        </style>
    </head>
    <body>
    <!--autoplay="true"-->
    <div class="media">
        <video id="myVideo" height="534" width="400" src="" autoplay></video>
        <canvas id="myCanvas" height="534" width="400"></canvas>
    </div>

    <!--创建一个登陆的按钮-->
    <div id="login">登陆</div>
    <script>
        //这里写的是网页脚本
        //调用摄像头获取媒体视频流
        /***
         * 默认的写法:navigator.getUserMedia
         * 火狐:navigator.mozGetUserMedia
         * 微软:navigator.msGetUserMedia
         * 谷歌:navigator.webkitGetUserMedia
         *
         * @type {((constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback) => void) | *}
         */
       
    var getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);

        var video = document.getElementById("myVideo");
        /***
         *
    四个参数 getUserMedia.call(navigator,{video:true,audio:false},function(){},function(){});
         *  1.要调用的对象
         *  2.约束对象:eg:只调用视频
         *  3.调用成功的方法
         *  4.调用失败的方法
         */
       
    getUserMedia.call(navigator, {video: true, audio: false}, function (localMediaStream) {
            //这里是调用成功的方法,如果调用成功,将视频流对象传到myVideo,localMediaStream是传入的视频流对象

            /*document.getElementById("myVideo").src= window.URL.createObjectURL(localMediaStream);
            * 上一行的代码已经过时了
            * */
           
    try {
                video.src = window.URL.createObjectURL(localMediaStream);
            } catch (e) {
                //执行的是这段代码
               
    video.srcObject = localMediaStream;
            }
            /***
             *
    下面三行代码可以代替了video的autoplay属性
             */
            /*        video.onloadedmetadata = function () {
                        video.play();
                    }*/

       
    }, function (e) {
            console.log("获取摄像头失败", e);//通过控制台将我们的错误信息打印
       
    });

        //获取登陆按钮
       
    var btn_login = document.getElementById("login");
        //获取canvas对象
       
    var canvas=document.getElementById("myCanvas");
        //获取上下文对象
       
    var context = canvas.getContext("2d");
        //登陆按钮绑定点击事件
       
    btn_login.onclick = function () {
            //点击登录按钮获取面部信息,(点击登录按钮的时候将图像画到)
            // context.drawImage(video,x轴开始位置,y轴开始位置,x轴结束位置,y轴结束位置);
            
    context.drawImage(video, 0, 0, 534, 400);
            //image/png 表示画成什么格式
           
    var imgSrc = document.getElementById("myCanvas").toDataURL("image/png");
            alert(imgSrc);
            // var Baseimg=imgSrc.split(",")[1];
           
    $.post("login.do",{imgSrc:imgSrc},function (result) {
                if(result){
                    location.href="success.jsp";
                }else{
                    alert("登录失败,请重新扫描");
                }
            })

        }

    </script>
    </body>
    </html>

    3.2后端界面

    face相关类,通过face++官网查到一个demo,本案例修改demo并封装了自己的信息,打成实现功能

     

    获取到的demo:

    import java.io.ByteArrayOutputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.net.URLEncoder;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Random;
    import javax.net.ssl.SSLException;
    public class FaceTest {
             
             public static void main(String[] args) throws Exception{
                     
            File file = new File("你的本地图片路径");
                     byte[] buff = getBytesFromFile(file);
                     String url = "https://api-cn.faceplusplus.com/facepp/v3/detect";
            HashMap<String, String> map = new HashMap<>();
            HashMap<String, byte[]> byteMap = new HashMap<>();
            map.put("api_key", "你的KEY");
            map.put("api_secret", "你的SECRET");
                     map.put("return_landmark", "1");
            map.put("return_attributes", "gender,age,smiling,headpose,facequality,blur,eyestatus,emotion,ethnicity,beauty,mouthstatus,eyegaze,skinstatus");
            byteMap.put("image_file", buff);
            try{
                byte[] bacd = post(url, map, byteMap);
                String str = new String(bacd);
                System.out.println(str);
            }catch (Exception e) {
               e.printStackTrace();
                     }
             }
             
             private final static int CONNECT_TIME_OUT = 30000;
        private final static int READ_OUT_TIME = 50000;
        private static String boundaryString = getBoundary();
        protected static byte[] post(String url, HashMap<String, String> map, HashMap<String, byte[]> fileMap) throws Exception {
            HttpURLConnection conne;
            URL url1 = new URL(url);
            conne = (HttpURLConnection) url1.openConnection();
            conne.setDoOutput(true);
            conne.setUseCaches(false);
            conne.setRequestMethod("POST");
            conne.setConnectTimeout(CONNECT_TIME_OUT);
            conne.setReadTimeout(READ_OUT_TIME);
            conne.setRequestProperty("accept", "*/*");
            conne.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundaryString);
            conne.setRequestProperty("connection", "Keep-Alive");
            conne.setRequestProperty("user-agent", "Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1)");
            DataOutputStream obos = new DataOutputStream(conne.getOutputStream());
            Iterator iter = map.entrySet().iterator();
            while(iter.hasNext()){
                Map.Entry<String, String> entry = (Map.Entry) iter.next();
                String key = entry.getKey();
                String value = entry.getValue();
                obos.writeBytes("--" + boundaryString + "
    ");
                obos.writeBytes("Content-Disposition: form-data; name="" + key
                        + ""
    ");
                obos.writeBytes("
    ");
                obos.writeBytes(value + "
    ");
            }
            if(fileMap != null && fileMap.size() > 0){
                Iterator fileIter = fileMap.entrySet().iterator();
                while(fileIter.hasNext()){
                    Map.Entry<String, byte[]> fileEntry = (Map.Entry<String, byte[]>) fileIter.next();
                    obos.writeBytes("--" + boundaryString + "
    ");
                    obos.writeBytes("Content-Disposition: form-data; name="" + fileEntry.getKey()
                            + ""; filename="" + encode(" ") + ""
    ");
                    obos.writeBytes("
    ");
                    obos.write(fileEntry.getValue());
                    obos.writeBytes("
    ");
                }
            }
            obos.writeBytes("--" + boundaryString + "--" + "
    ");
            obos.writeBytes("
    ");
            obos.flush();
            obos.close();
            InputStream ins = null;
            int code = conne.getResponseCode();
            try{
                if(code == 200){
                    ins = conne.getInputStream();
                }else{
                    ins = conne.getErrorStream();
                }
            }catch (SSLException e){
                e.printStackTrace();
                return new byte[0];
            }
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buff = new byte[4096];
            int len;
            while((len = ins.read(buff)) != -1){
                baos.write(buff, 0, len);
            }
            byte[] bytes = baos.toByteArray();
            ins.close();
            return bytes;
        }
        private static String getBoundary() {
            StringBuilder sb = new StringBuilder();
            Random random = new Random();
            for(int i = 0; i < 32; ++i) {
                sb.append("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".charAt(random.nextInt("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_".length())));
            }
            return sb.toString();
        }
        private static String encode(String value) throws Exception{
            return URLEncoder.encode(value, "UTF-8");
        }
        
        public static byte[] getBytesFromFile(File f) {
            if (f == null) {
                return null;
            }
            try {
                FileInputStream stream = new FileInputStream(f);
                ByteArrayOutputStream out = new ByteArrayOutputStream(1000);
                byte[] b = new byte[1000];
                int n;
                while ((n = stream.read(b)) != -1)
                    out.write(b, 0, n);
                stream.close();
                out.close();
                return out.toByteArray();
            } catch (IOException e) {
            }
            return null;
        }
    }

    哪里需要改?

     

    四.总结

    人无我有,人有我优

    思路很清晰,具体实现很难!!!

    实现后感觉很简单

  • 相关阅读:
    网易企业免费邮箱
    168. Excel Sheet Column Title
    167.Two Sum II-Input array is sorted
    166. Fraction to Recurring Decimal
    165 Compare Version Numbers
    164. Maximum Gap
    163.Missing Ranges
    162.Find Peak Element
    161.One Edit Distance
    160. Intersection of Two Linked Lists
  • 原文地址:https://www.cnblogs.com/aihuadung/p/11592967.html
Copyright © 2020-2023  润新知