Face Service(四)考生考試人臉驗證的實現。

來源:互聯網
上載者:User

需求: 考生需要在考試前將自己的人臉錄入到系統當中。(拍照錄入或者直接匯入,方法二選一) 考生在考試時,需要進行Face Service,通過人臉進行身分識別驗證,驗證成功後,登入成功。

上周的部落格中我講到了如何拍照將人臉錄入系統中,這次我把剩下的講完。
先講簡單的吧,用上傳照片將人臉匯入系統當中。

實現效果是這個樣子的:

還是之前的介面,我改了一下,上面的輸入框是用來輸入帳號的,點擊選擇檔案的按鈕,選好要上傳的圖片後,點擊圖片上傳按鈕。將圖片提交到後台,上傳給face++解析,得到傳回值處理過後返回給使用者,這裡返回的是上傳的照片不對是因為我上傳了一張貓的照片,不是人像。好了,簡單看一下代碼吧,前端代碼:

<!DOCTYPE html>    <html>        <head>            <meta charset="utf-8">            <title>假裝這是註冊頁面</title>            <style>          video,canvas{              border:1px solid gray;              width:400px;              height:400px;              border-radius:50%;          }             </style>      </head>        <body>           <video autoplay style="position: absolute;left: 1%;display: none;"></video>          <canvas id="myCanvas" style="position: absolute;left: 50%;"></canvas>        <form id="pic" action="/face/picture"  enctype="multipart/form-data">        <input type="text" id="name" style="position:  absolute;left: 30%;top: 15%" placeholder="請填入您的帳號">           <input type="file" name="file"  style="position: absolute;left: 30%;top: 35%" >          <button type="button" style="position: absolute;left: 30%;top: 45%" onclick="uploadPic()">圖片上傳</button>          </form>      <!-- <button id="capture" style="position: absolute;left: 30%;top: 25%">拍照上傳</button> -->       <script src="http://code.jquery.com/jquery-latest.js"></script>        <script type="text/javascript">        function uploadPic(){            var formData = new FormData($( "#pic" )[0]);              $.ajax({                    url: '/face/picture' ,  /*這是處理檔案上傳的servlet*/                  type: 'POST',                    data: formData,                    async: false,                    cache: false,                    contentType: false,                    processData: false,                    success: function (returndata) {                        alert(returndata.message)                },                    error: function (returndata) {                        alert(returndata);                    }               });            }        function hasUserMedia(){//判斷是否支援調用裝置api,因為瀏覽器不同所以判斷方式不同哦                return !!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);            }            if(hasUserMedia()){                //alert(navigator.mozGetUserMedia)                navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;                var video=document.querySelector("video");                var canvas=document.querySelector("canvas");              var streaming = false;              navigator.getUserMedia({                    video:true,//開啟視頻                    audio:false//先關閉音頻,因為會有迴響,以後兩台電腦通訊不會有響聲                },function(stream){//將視頻流交給video                    video.src=window.URL.createObjectURL(stream);                     streaming = true;              },function(err){                    console.log("capturing",err)                });                document.querySelector("#capture").addEventListener("click",function(event){                  if(streaming){                      //alert(video.clientHeight)                      //canvas.width = video.clientWidth;                      //canvas.height= video.clientHeight;                      canvas.width = 800;                      canvas.height = 800;                      var context = canvas.getContext('2d');                      imgString = canvas.toDataURL("image/png")                    context.drawImage(video,20,20)                      var info = {                        name: $("#name").val(),                        imgString: canvas.toDataURL("image/png")                      }                    $.post("/face/photograph",info,function(data){                            alert(data.message)                  },"json")                 }              })          }else{                alert("瀏覽器暫不支援")            }            </script>       </body>    </html>    

我加了一些代碼在之前拍照上傳的html裡面,其實就是ajax提交帶著上傳標籤的表單而已,沒什麼好說的。

後台代碼:

@RequestMapping(value="/picture")    public JsonResult picture(MultipartFile file,String name) throws IOException {        if(file == null || "".equals(file.getOriginalFilename())) {            return new JsonResult("0",  "上傳的照片為空白", null);        }        String str = FaceUtil.check(file.getBytes());         JSONObject json = JSONObject.fromObject(str);         try {             String faces = json.getString("faces");             if("[]".equals(faces)) {                 return new JsonResult("0", "對不起,您上傳的不是帳戶圖片或者照片品質不佳,請重新上傳。", null);             }             JSONObject josnToken = JSONObject.fromObject(faces.substring(1, faces.length()-1));             String token = josnToken.getString("face_token");             FaceUser user = new FaceUser();             user.setName(name);             user.setFaceToken(token);             faceService.add(user);        } catch (Exception e) {            // TODO: handle exception                e.printStackTrace();             return new JsonResult("0", "系統繁忙,請稍後重試。", null);        }       return new JsonResult("1", "上傳成功,請登入。", null);    }

我把圖片上傳到face++並得到傳回值的過程封裝到了FaceUtil裡面,代碼如下:

package com.avie.ltd.util;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.URLEncoder;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Random;import javax.net.ssl.SSLException;import sun.misc.BASE64Decoder;public class FaceUtil {    static String url = "https://api-cn.faceplusplus.com/facepp/v3/detect";    public static String checkFace(String imgString) throws IOException {        byte[] buff = getStringImage(imgString.substring(imgString.indexOf(",")+1));        return check( buff);    }    public static String check(byte[] buff) {        HashMap<String, String> map = new HashMap<>();        HashMap<String, byte[]> byteMap = new HashMap<>();        map.put("api_key", "your api key");        map.put("api_secret", "your api 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);        String str =null;        try{            byte[] bacd = post(url, map, byteMap);           str = new String(bacd);            System.out.println(str);        }catch (Exception e) {            e.printStackTrace();        }        return str;    }    /**     * Base64字串轉 二進位流     *     * @param base64String Base64     * @return base64String     * @throws IOException 異常     */    @SuppressWarnings("restriction")    public static byte[] getStringImage(String base64String) throws IOException {        BASE64Decoder decoder = new sun.misc.BASE64Decoder();        return base64String != null ? decoder.decodeBuffer(base64String) : null;    }    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 + "\r\n");            obos.writeBytes("Content-Disposition: form-data; name=\"" + key                    + "\"\r\n");            obos.writeBytes("\r\n");            obos.writeBytes(value + "\r\n");        }        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 + "\r\n");                obos.writeBytes("Content-Disposition: form-data; name=\"" + fileEntry.getKey()                        + "\"; filename=\"" + encode(" ") + "\"\r\n");                obos.writeBytes("\r\n");                obos.write(fileEntry.getValue());                obos.writeBytes("\r\n");            }        }        obos.writeBytes("--" + boundaryString + "--" + "\r\n");        obos.writeBytes("\r\n");        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;    }}

由於我在之前的部落格中講過這些代碼,所以這裡不再詳述,不懂的去看我之前的部落格。那麼到這裡我們就把第一個需求完全實現了嗎。沒有。在我之前講人臉對比的部落格中,提到過人臉對比的傳參列表如下:

要實現人臉對比,至少要傳兩張照片,這兩張照片,可以是二進位流,可以是圖片的url,也可以是之前上傳到face++的照片的face_token。在這裡面,最好的方式應該是傳face_token,這種方式不用再上傳整張圖片,face++那邊也不用再解析你的圖片,直接調用你之前上傳的圖片即可。那麼要想讓face++永久的保留我們現在上傳的圖片以供以後使用,我們就還需要將得到的圖片的face_token存到我們在之前建的face_set中去(對face_set不清楚的同學可以去這裡看我之前的介紹:調用face++api實現人臉對比)。具體做法來看如下代碼:

    ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,            new ArrayBlockingQueue<Runnable>(5));    @Autowired    private FaceUserService faceService;    @RequestMapping(value="/photograph")    public JsonResult getFace(String imgString,String name) throws IOException {        String str = FaceUtil.checkFace(imgString);        String token = "";         JSONObject json = JSONObject.fromObject(str);         try {             String faces = json.getString("faces");             if("[]".equals(faces)) {                 return new JsonResult("0", "對不起,您上傳的不是帳戶圖片或者照片品質不佳,請重新上傳。", null);             }             JSONObject josnToken = JSONObject.fromObject(faces.substring(1, faces.length()-1));             token = josnToken.getString("face_token");             FaceUser user = new FaceUser();             user.setName(name);             user.setFaceToken(token);             faceService.add(user);        } catch (Exception e) {            // TODO: handle exception                e.printStackTrace();             return new JsonResult("0", "系統繁忙,請稍後重試。", null);        }         executor.execute(new AddFace(token));       return new JsonResult("1", "上傳成功,請登入。", null);    }

這裡我對之前的拍照上傳的代碼做了些修改。最開始先new了一個線程池,進入getFace方法,大體和之前部落格的一樣,就是提交照片給face++檢測然後拿到傳回值處理,不同的是,當我檢測成功之後,往線程池提交了一個AddFace()的線程,並在構造方法裡傳入臉部偵測完返回的face_token的值。那我們再來看看這個線程的代碼:

package com.avie.ltd.controller;import java.util.ArrayList;import java.util.List;import org.apache.http.message.BasicNameValuePair;import com.avie.ltd.util.PostUtil;public class AddFace implements Runnable {    String addUrl = "https://api-cn.faceplusplus.com/facepp/v3/faceset/addface";    String faceToken = "";    public AddFace(String faceToken) {        this.faceToken = faceToken;    }    @Override    public void run() {        // TODO Auto-generated method stub        while (true) {            // 建立參數隊列            List<BasicNameValuePair> formparams = new ArrayList<>();            formparams.add(new BasicNameValuePair("api_key", "your api key"));            formparams.add(new BasicNameValuePair("api_secret", "your api secret"));            formparams.add(new BasicNameValuePair("outer_id", "myface_1"));

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.